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 useWorkspacePageOrder from '@/features/page/tree/hooks/use-workspace-page-order'; import { useNavigate, useParams } from 'react-router-dom'; import { convertToTree, updateTreeNodeIcon } from '@/features/page/tree/utils'; import { useGetPagesQuery, useUpdatePageMutation } from '@/features/page/queries/page-query'; import EmojiPicker from '@/components/emoji-picker'; import { treeDataAtom } from '@/features/page/tree/atoms/tree-data-atom'; export default function PageTree() { const { data, setData, controllers } = usePersistence>(); const [tree, setTree] = useAtom>(treeApiAtom); const { data: pageOrderData } = useWorkspacePageOrder(); const { data: pagesData, isLoading } = useGetPagesQuery(); const rootElement = useRef(); const { pageId } = useParams(); const fetchAndSetTreeData = async () => { if (pageOrderData?.childrenIds) { try { if (!isLoading) { const treeData = convertToTree(pagesData, pageOrderData.childrenIds); setData(treeData); } } catch (err) { console.error('Error fetching tree data: ', err); } } }; useEffect(() => { fetchAndSetTreeData(); }, [pageOrderData?.childrenIds, isLoading]); 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 updatePageMutation = useUpdatePageMutation(); const [treeData, setTreeData] = useAtom(treeDataAtom); const handleClick = () => { navigate(`/p/${node.id}`); }; const handleUpdateNodeIcon = (nodeId, newIcon) => { const updatedTreeData = updateTreeNodeIcon(treeData, nodeId, newIcon); setTreeData(updatedTreeData); }; const handleEmojiIconClick = (e) => { e.preventDefault(); e.stopPropagation(); } const handleEmojiSelect = (emoji) => { handleUpdateNodeIcon(node.id, emoji.native); updatePageMutation.mutateAsync({ id: node.id, icon: emoji.native }); }; 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); }} /> ); }