diff options
| author | Mohamed Bassem <me@mbassem.com> | 2024-04-19 00:09:27 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-04-19 00:09:27 +0100 |
| commit | e0999f701cd1834c3d940113cd8dd5247c5fe95f (patch) | |
| tree | c4169a564ecd3f933e711bcc8ef7db20532174ea /apps/web/components/dashboard/lists/CollapsibleBookmarkLists.tsx | |
| parent | deba31ee010f785a9739fd4df8a64a3056c9593d (diff) | |
| download | karakeep-e0999f701cd1834c3d940113cd8dd5247c5fe95f.tar.zst | |
feature: Nested lists (#110). Fixes #62
* feature: Add support for nested lists
* prevent moving the parent to a subtree
Diffstat (limited to 'apps/web/components/dashboard/lists/CollapsibleBookmarkLists.tsx')
| -rw-r--r-- | apps/web/components/dashboard/lists/CollapsibleBookmarkLists.tsx | 111 |
1 files changed, 111 insertions, 0 deletions
diff --git a/apps/web/components/dashboard/lists/CollapsibleBookmarkLists.tsx b/apps/web/components/dashboard/lists/CollapsibleBookmarkLists.tsx new file mode 100644 index 00000000..da1b7408 --- /dev/null +++ b/apps/web/components/dashboard/lists/CollapsibleBookmarkLists.tsx @@ -0,0 +1,111 @@ +import { useEffect, useState } from "react"; +import { Collapsible, CollapsibleContent } from "@/components/ui/collapsible"; +import { FullPageSpinner } from "@/components/ui/full-page-spinner"; + +import { + augmentBookmarkListsWithInitialData, + useBookmarkLists, +} from "@hoarder/shared-react/hooks/lists"; +import { ZBookmarkList } from "@hoarder/shared/types/lists"; +import { ZBookmarkListTreeNode } from "@hoarder/shared/utils/listUtils"; + +type RenderFunc = (params: { + item: ZBookmarkListTreeNode; + level: number; + open: boolean; +}) => React.ReactNode; + +type IsOpenFunc = (list: ZBookmarkListTreeNode) => boolean; + +function ListItem({ + node, + render, + level, + className, + isOpenFunc, +}: { + node: ZBookmarkListTreeNode; + render: RenderFunc; + isOpenFunc: IsOpenFunc; + level: number; + className?: string; +}) { + // Not the most efficient way to do this, but it works for now + const isAnyChildOpen = ( + node: ZBookmarkListTreeNode, + isOpenFunc: IsOpenFunc, + ): boolean => { + if (isOpenFunc(node)) { + return true; + } + return node.children.some((l) => isAnyChildOpen(l, isOpenFunc)); + }; + const [open, setOpen] = useState(false); + useEffect(() => { + setOpen((curr) => curr || isAnyChildOpen(node, isOpenFunc)); + }, [node, isOpenFunc]); + + return ( + <Collapsible open={open} onOpenChange={setOpen} className={className}> + {render({ + item: node, + level, + open, + })} + <CollapsibleContent> + {node.children.map((l) => ( + <ListItem + isOpenFunc={isOpenFunc} + key={l.item.id} + node={l} + render={render} + level={level + 1} + className={className} + /> + ))} + </CollapsibleContent> + </Collapsible> + ); +} + +export function CollapsibleBookmarkLists({ + render, + initialData, + className, + isOpenFunc, +}: { + initialData?: ZBookmarkList[]; + render: RenderFunc; + isOpenFunc?: IsOpenFunc; + className?: string; +}) { + let { data } = useBookmarkLists(undefined, { + initialData: initialData ? { lists: initialData } : undefined, + }); + + // TODO: This seems to be a bug in react query + if (initialData) { + data = augmentBookmarkListsWithInitialData(data, initialData); + } + + if (!data) { + return <FullPageSpinner />; + } + + const { root } = data; + + return ( + <div> + {Object.values(root).map((l) => ( + <ListItem + key={l.item.id} + node={l} + render={render} + level={0} + className={className} + isOpenFunc={isOpenFunc ?? (() => false)} + /> + ))} + </div> + ); +} |
