import { ISharedPageTree } from "@/features/share/types/share.types.ts"; import { NodeApi, NodeRendererProps, Tree, TreeApi } from "react-arborist"; import { buildSharedPageTree, SharedPageTreeNode, } from "@/features/share/utils.ts"; import { useEffect, useMemo, useRef, useState } from "react"; import { useElementSize, useMergedRef } from "@mantine/hooks"; import { SpaceTreeNode } from "@/features/page/tree/types.ts"; import { Link, useParams } from "react-router-dom"; import { atom, useAtom } from "jotai/index"; import { useTranslation } from "react-i18next"; import { buildSharedPageUrl } from "@/features/page/page.utils.ts"; import clsx from "clsx"; import { IconChevronDown, IconChevronRight, IconFileDescription, IconPointFilled, } from "@tabler/icons-react"; import { ActionIcon, Box } from "@mantine/core"; import { extractPageSlugId } from "@/lib"; import { OpenMap } from "react-arborist/dist/main/state/open-slice"; import classes from "@/features/page/tree/styles/tree.module.css"; import styles from "./share.module.css"; import { mobileSidebarAtom } from "@/components/layouts/global/hooks/atoms/sidebar-atom.ts"; import EmojiPicker from "@/components/ui/emoji-picker.tsx"; interface SharedTree { sharedPageTree: ISharedPageTree; } const openSharedTreeNodesAtom = atom({}); export default function SharedTree({ sharedPageTree }: SharedTree) { const [tree, setTree] = useState< TreeApi | null | undefined >(null); const rootElement = useRef(); const { ref: sizeRef, width, height } = useElementSize(); const mergedRef = useMergedRef(rootElement, sizeRef); const { pageSlug } = useParams(); const [openTreeNodes, setOpenTreeNodes] = useAtom( openSharedTreeNodesAtom, ); const currentNodeId = extractPageSlugId(pageSlug); const treeData: SharedPageTreeNode[] = useMemo(() => { if (!sharedPageTree?.pageTree) return; return buildSharedPageTree(sharedPageTree.pageTree); }, [sharedPageTree?.pageTree]); useEffect(() => { const parentNodeId = treeData?.[0]?.slugId; if (parentNodeId && tree) { const parentNode = tree.get(parentNodeId); setTimeout(() => { if (parentNode) { tree.openSiblings(parentNode); } }); // open direct children of parent node parentNode?.children.forEach((node) => { tree.openSiblings(node); }); } }, [treeData, tree]); useEffect(() => { if (currentNodeId && tree) { setTimeout(() => { // focus on node and open all parents tree?.select(currentNodeId, { align: "auto" }); }, 200); } else { tree?.deselectAll(); } }, [currentNodeId, tree]); if (!sharedPageTree || !sharedPageTree?.pageTree) { return null; } return (
{rootElement.current && ( setTree(t)} openByDefault={false} disableMultiSelection={true} className={classes.tree} rowClassName={classes.row} rowHeight={30} overscanCount={10} dndRootElement={rootElement.current} onToggle={() => { setOpenTreeNodes(tree?.openState); }} initialOpenState={openTreeNodes} onClick={(e) => { if (tree && tree.focusedNode) { tree.select(tree.focusedNode); } }} > {Node} )}
); } function Node({ node, style, tree }: NodeRendererProps) { const { shareId } = useParams(); const { t } = useTranslation(); const [, setMobileSidebarState] = useAtom(mobileSidebarAtom); const pageUrl = buildSharedPageUrl({ shareId: shareId, pageSlugId: node.data.slugId, pageTitle: node.data.name, }); return ( <> { setMobileSidebarState(false); }} >
{}} icon={ node.data.icon ? ( node.data.icon ) : ( ) } readOnly={true} removeEmojiAction={() => {}} />
{node.data.name || t("untitled")}
); } interface PageArrowProps { node: NodeApi; } function PageArrow({ node }: PageArrowProps) { return ( { e.preventDefault(); e.stopPropagation(); node.toggle(); }} > {node.isInternal ? ( node.children && (node.children.length > 0 || node.data.hasChildren) ? ( node.isOpen ? ( ) : ( ) ) : ( ) ) : null} ); }