import React, { useCallback, useState, FC, useEffect, memo, } from 'react'; import nodePath from 'path'; import { useTranslation } from 'react-i18next'; import { pagePathUtils } from '@growi/core'; import { ItemNode } from './ItemNode'; import { IPageHasId } from '~/interfaces/page'; import { useSWRxPageChildren } from '../../../stores/page-listing'; import ClosableTextInput, { AlertInfo, AlertType } from '../../Common/ClosableTextInput'; import PageItemControl from '../../Common/Dropdown/PageItemControl'; import { IPageForPageDeleteModal } from '~/components/PageDeleteModal'; import TriangleIcon from '~/components/Icons/TriangleIcon'; const { isTopPage } = pagePathUtils; interface ItemProps { isEnableActions: boolean itemNode: ItemNode targetId?: string isOpen?: boolean onClickDeleteByPage?(page: IPageForPageDeleteModal): void } // Utility to mark target const markTarget = (children: ItemNode[], targetId?: string): void => { if (targetId == null) { return; } children.forEach((node) => { if (node.page._id === targetId) { node.page.isTarget = true; } return node; }); }; type ItemControlProps = { page: Partial isEnableActions: boolean isDeletable: boolean onClickDeleteButtonHandler?(): void onClickPlusButtonHandler?(): void } const ItemControl: FC = memo((props: ItemControlProps) => { const onClickPlusButton = () => { if (props.onClickPlusButtonHandler == null) { return; } props.onClickPlusButtonHandler(); }; const onClickDeleteButton = () => { if (props.onClickDeleteButtonHandler == null) { return; } props.onClickDeleteButtonHandler(); }; if (props.page == null) { return <>; } return ( <> ); }); const ItemCount: FC = () => { return ( <> {/* TODO: consider to show the number of children pages */} ); }; const Item: FC = (props: ItemProps) => { const { t } = useTranslation(); const { itemNode, targetId, isOpen: _isOpen = false, onClickDeleteByPage, isEnableActions, } = props; const { page, children } = itemNode; const [currentChildren, setCurrentChildren] = useState(children); const [isOpen, setIsOpen] = useState(_isOpen); const [isNewPageInputShown, setNewPageInputShown] = useState(false); const { data, error } = useSWRxPageChildren(isOpen ? page._id : null); const hasChildren = useCallback((): boolean => { return currentChildren != null && currentChildren.length > 0; }, [currentChildren]); const onClickLoadChildren = useCallback(async() => { setIsOpen(!isOpen); }, [isOpen]); const onClickDeleteButtonHandler = useCallback(() => { if (onClickDeleteByPage == null) { return; } const { _id: pageId, revision: revisionId, path } = page; if (pageId == null || revisionId == null || path == null) { throw Error('Any of _id, revision, and path must not be null.'); } const pageToDelete: IPageForPageDeleteModal = { pageId, revisionId: revisionId as string, path, }; onClickDeleteByPage(pageToDelete); }, [page, onClickDeleteByPage]); const inputValidator = (title: string | null): AlertInfo | null => { if (title == null || title === '') { return { type: AlertType.ERROR, message: t('Page title is required'), }; return; } return null; }; // TODO: go to create page page const onPressEnterHandler = () => { console.log('Enter key was pressed!'); }; // didMount useEffect(() => { if (hasChildren()) setIsOpen(true); }, []); /* * Make sure itemNode.children and currentChildren are synced */ useEffect(() => { if (children.length > currentChildren.length) { markTarget(children, targetId); setCurrentChildren(children); } }, []); /* * When swr fetch succeeded */ useEffect(() => { if (isOpen && error == null && data != null) { const newChildren = ItemNode.generateNodesFromPages(data.children); markTarget(newChildren, targetId); setCurrentChildren(newChildren); } }, [data, isOpen]); return ( <>

{nodePath.basename(page.path as string) || '/'}

{ setNewPageInputShown(true) }} isEnableActions={isEnableActions} isDeletable={!page.isEmpty && !isTopPage(page.path as string)} />
{isEnableActions && ( { setNewPageInputShown(false) }} onPressEnter={onPressEnterHandler} inputValidator={inputValidator} /> )} { isOpen && hasChildren() && currentChildren.map(node => (
)) } ); }; export default Item;