import React, {
FC, useCallback, useEffect, useRef,
} from 'react';
import { useTranslation } from 'react-i18next';
import { DropdownItem } from 'reactstrap';
import { IPageToDeleteWithMeta, IPageWithMeta } from '~/interfaces/page';
import { IPageSearchMeta } from '~/interfaces/search';
import { OnDuplicatedFunction, OnDeletedFunction } from '~/interfaces/ui';
import { usePageTreeTermManager } from '~/stores/page-listing';
import { useFullTextSearchTermManager } from '~/stores/search';
import { useDescendantsPageListForCurrentPathTermManager } from '~/stores/page';
import { exportAsMarkdown } from '~/client/services/page-operation';
import { toastSuccess } from '~/client/util/apiNotification';
import RevisionLoader from '../Page/RevisionLoader';
import AppContainer from '../../client/services/AppContainer';
import { smoothScrollIntoView } from '~/client/util/smooth-scroll';
import { GrowiSubNavigation } from '../Navbar/GrowiSubNavigation';
import { SubNavButtons } from '../Navbar/SubNavButtons';
import { AdditionalMenuItemsRendererProps, ForceHideMenuItems } from '../Common/Dropdown/PageItemControl';
import {
usePageDuplicateModal, usePageRenameModal, usePageDeleteModal,
} from '~/stores/modal';
type AdditionalMenuItemsProps = AdditionalMenuItemsRendererProps & {
pageId: string,
revisionId: string,
}
const AdditionalMenuItems = (props: AdditionalMenuItemsProps): JSX.Element => {
const { t } = useTranslation();
const { pageId, revisionId } = props;
return (
// Export markdown
exportAsMarkdown(pageId, revisionId, 'md')}>
{t('export_bulk.export_page_markdown')}
);
};
const SCROLL_OFFSET_TOP = 175; // approximate height of (navigation + subnavigation)
const MUTATION_OBSERVER_CONFIG = { childList: true, subtree: true };
type Props ={
appContainer: AppContainer,
pageWithMeta : IPageWithMeta,
highlightKeywords?: string[],
showPageControlDropdown?: boolean,
forceHideMenuItems?: ForceHideMenuItems,
}
const scrollTo = (scrollElement:HTMLElement) => {
// use querySelector to intentionally get the first element found
const highlightedKeyword = scrollElement.querySelector('.highlighted-keyword') as HTMLElement | null;
if (highlightedKeyword != null) {
smoothScrollIntoView(highlightedKeyword, SCROLL_OFFSET_TOP, scrollElement);
}
};
const generateObserverCallback = (doScroll: ()=>void) => {
return (mutationRecords:MutationRecord[]) => {
mutationRecords.forEach((record:MutationRecord) => {
const target = record.target as HTMLElement;
const targetId = target.id as string;
if (targetId !== 'wiki') return;
doScroll();
});
};
};
export const SearchResultContent: FC = (props: Props) => {
const scrollElementRef = useRef(null);
// for mutation
const { advance: advancePt } = usePageTreeTermManager();
const { advance: advanceFts } = useFullTextSearchTermManager();
const { advance: advanceDpl } = useDescendantsPageListForCurrentPathTermManager();
// *************************** Auto Scroll ***************************
useEffect(() => {
const scrollElement = scrollElementRef.current as HTMLElement | null;
if (scrollElement == null) return;
const observerCallback = generateObserverCallback(() => {
scrollTo(scrollElement);
});
const observer = new MutationObserver(observerCallback);
observer.observe(scrollElement, MUTATION_OBSERVER_CONFIG);
return () => {
observer.disconnect();
};
});
// ******************************* end *******************************
const {
appContainer,
pageWithMeta,
highlightKeywords,
showPageControlDropdown,
forceHideMenuItems,
} = props;
const { t } = useTranslation();
const page = pageWithMeta?.data;
const { open: openDuplicateModal } = usePageDuplicateModal();
const { open: openRenameModal } = usePageRenameModal();
const { open: openDeleteModal } = usePageDeleteModal();
const growiRenderer = appContainer.getRenderer('searchresult');
const duplicateItemClickedHandler = useCallback(async(pageToDuplicate) => {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const duplicatedHandler: OnDuplicatedFunction = (fromPath, toPath) => {
toastSuccess(t('duplicated_pages', { fromPath }));
advancePt();
advanceFts();
advanceDpl();
};
openDuplicateModal(pageToDuplicate, { onDuplicated: duplicatedHandler });
}, [advanceDpl, advanceFts, advancePt, openDuplicateModal, t]);
const renameItemClickedHandler = useCallback(async(pageToRename) => {
openRenameModal(pageToRename);
}, [openRenameModal]);
const onDeletedHandler: OnDeletedFunction = useCallback((pathOrPathsToDelete, isRecursively, isCompletely) => {
if (typeof pathOrPathsToDelete !== 'string') {
return;
}
const path = pathOrPathsToDelete;
if (isCompletely) {
toastSuccess(t('deleted_pages_completely', { path }));
}
else {
toastSuccess(t('deleted_pages', { path }));
}
advancePt();
advanceFts();
advanceDpl();
}, [advanceDpl, advanceFts, advancePt, t]);
const deleteItemClickedHandler = useCallback((pageToDelete: IPageToDeleteWithMeta) => {
openDeleteModal([pageToDelete], { onDeleted: onDeletedHandler });
}, [onDeletedHandler, openDeleteModal]);
const ControlComponents = useCallback(() => {
if (page == null) {
return <>>;
}
const revisionId = typeof page.revision === 'string'
? page.revision
: page.revision._id;
return (
<>
}
isCompactMode
onClickDuplicateMenuItem={duplicateItemClickedHandler}
onClickRenameMenuItem={renameItemClickedHandler}
onClickDeleteMenuItem={deleteItemClickedHandler}
/>
>
);
}, [page, showPageControlDropdown, forceHideMenuItems, duplicateItemClickedHandler, renameItemClickedHandler, deleteItemClickedHandler]);
// return if page is null
if (page == null) return <>>;
return (
);
};