import { NodeApi, NodeRendererProps, Tree, TreeApi } from 'react-arborist'; import { IconArrowsLeftRight, IconChevronDown, IconChevronRight, IconCornerRightUp, IconDotsVertical, IconEdit, IconFileDescription, IconLink, IconPlus, IconStar, IconTrash, } from '@tabler/icons-react'; import React, { useEffect, useRef } from 'react'; import clsx from 'clsx'; import classes from './styles/tree.module.css'; import { ActionIcon, Menu, rem } from '@mantine/core'; import { useAtom } from 'jotai'; import { FillFlexParent } from './components/fill-flex-parent'; import { TreeNode } from './types'; import { treeApiAtom } from './atoms/tree-api-atom'; import { usePersistence } from '@/features/page/tree/hooks/use-persistence'; import { getPages } from '@/features/page/services/page-service'; import useWorkspacePageOrder from '@/features/page/tree/hooks/use-workspace-page-order'; import { useNavigate, useParams } from 'react-router-dom'; import { convertToTree } from '@/features/page/tree/utils'; export default function PageTree() { const { data, setData, controllers } = usePersistence>(); const [tree, setTree] = useAtom>(treeApiAtom); const { data: pageOrderData } = useWorkspacePageOrder(); const rootElement = useRef(); const { pageId } = useParams(); const fetchAndSetTreeData = async () => { if (pageOrderData?.childrenIds) { try { const pages = await getPages(); const treeData = convertToTree(pages, pageOrderData.childrenIds); setData(treeData); } catch (err) { console.error('Error fetching tree data: ', err); } } }; useEffect(() => { fetchAndSetTreeData(); }, [pageOrderData?.childrenIds]); useEffect(() => { setTimeout(() => { tree?.select(pageId); tree?.scrollTo(pageId, 'center'); }, 200); }, [tree, pageId]); return (
{(dimens) => ( setTree(t)} openByDefault={false} disableMultiSelection={true} className={classes.tree} rowClassName={classes.row} padding={15} rowHeight={30} overscanCount={5} dndRootElement={rootElement.current} > {Node} )}
); } function Node({ node, style, dragHandle }: NodeRendererProps) { const navigate = useNavigate(); const handleClick = () => { navigate(`/p/${node.id}`); }; if (node.willReceiveDrop && node.isClosed) { setTimeout(() => { if (node.state.willReceiveDrop) node.open(); }, 650); } return ( <>
{node.isEditing ? ( ) : ( node.data.name || 'untitled' )}
); } function CreateNode({ node }: { node: NodeApi }) { const [tree] = useAtom(treeApiAtom); function handleCreate() { tree?.create({ type: 'internal', parentId: node.id, index: 0 }); } return ( { e.preventDefault(); e.stopPropagation(); handleCreate(); }}> ); } function NodeMenu({ node }: { node: NodeApi }) { const [tree] = useAtom(treeApiAtom); function handleDelete() { tree?.delete(node); } return ( { e.preventDefault(); e.stopPropagation(); }}> } onClick={(e) => { e.preventDefault(); e.stopPropagation(); node.edit(); }} > Rename } > Favorite } > Copy link } > Move } > Archive } onClick={() => handleDelete()} > Delete ); } function PageArrow({ node }: { node: NodeApi }) { return ( { e.preventDefault(); e.stopPropagation(); node.toggle(); }}> {node.isInternal ? ( node.children && node.children.length > 0 ? ( node.isOpen ? ( ) : ( ) ) : ( ) ) : null} ); } function Input({ node }: { node: NodeApi }) { return ( e.currentTarget.select()} onBlur={() => node.reset()} onKeyDown={(e) => { if (e.key === 'Escape') node.reset(); if (e.key === 'Enter') node.submit(e.currentTarget.value); }} /> ); }