From e0999f701cd1834c3d940113cd8dd5247c5fe95f Mon Sep 17 00:00:00 2001
From: Mohamed Bassem <me@mbassem.com>
Date: Fri, 19 Apr 2024 00:09:27 +0100
Subject: feature: Nested lists (#110). Fixes #62

* feature: Add support for nested lists

* prevent moving the parent to a subtree
---
 .../components/dashboard/lists/AllListsView.tsx    | 113 ++++++++++++++-------
 1 file changed, 74 insertions(+), 39 deletions(-)

(limited to 'apps/web/components/dashboard/lists/AllListsView.tsx')

diff --git a/apps/web/components/dashboard/lists/AllListsView.tsx b/apps/web/components/dashboard/lists/AllListsView.tsx
index 00e76a23..308af5db 100644
--- a/apps/web/components/dashboard/lists/AllListsView.tsx
+++ b/apps/web/components/dashboard/lists/AllListsView.tsx
@@ -1,31 +1,59 @@
 "use client";
 
 import Link from "next/link";
-import { useNewListModal } from "@/components/dashboard/sidebar/NewListModal";
+import { EditListModal } from "@/components/dashboard/lists/EditListModal";
 import { Button } from "@/components/ui/button";
-import { api } from "@/lib/trpc";
-import { keepPreviousData } from "@tanstack/react-query";
-import { Plus } from "lucide-react";
+import { CollapsibleTriggerChevron } from "@/components/ui/collapsible";
+import { MoreHorizontal, Plus } from "lucide-react";
 
 import type { ZBookmarkList } from "@hoarder/shared/types/lists";
 
+import { CollapsibleBookmarkLists } from "./CollapsibleBookmarkLists";
+import { ListOptions } from "./ListOptions";
+
 function ListItem({
   name,
   icon,
   path,
+  style,
+  list,
+  open,
+  collapsible,
 }: {
   name: string;
   icon: string;
   path: string;
+  style?: React.CSSProperties;
+  list?: ZBookmarkList;
+  open?: boolean;
+  collapsible: boolean;
 }) {
   return (
-    <Link href={path}>
-      <div className="rounded-md border border-border bg-background px-4 py-2 text-lg">
-        <p className="text-nowrap">
-          {icon} {name}
-        </p>
-      </div>
-    </Link>
+    <li
+      className="my-2 flex items-center justify-between rounded-md border border-border p-2 hover:bg-accent/50"
+      style={style}
+    >
+      <span className="flex flex-1 items-center gap-1">
+        {collapsible && (
+          <CollapsibleTriggerChevron className="size-5" open={open ?? false} />
+        )}
+        <Link href={path} className="flex flex-1 gap-1">
+          <p className="text-nowrap text-lg">
+            {icon} {name}
+          </p>
+        </Link>
+      </span>
+      {list && (
+        <ListOptions list={list}>
+          <Button
+            className="flex h-full items-center justify-end"
+            variant="ghost"
+          >
+            <MoreHorizontal />
+          </Button>
+        </ListOptions>
+      )}
+    </li>
   );
 }
 
@@ -34,34 +62,41 @@ export default function AllListsView({
 }: {
   initialData: ZBookmarkList[];
 }) {
-  const { setOpen: setIsNewListModalOpen } = useNewListModal();
-  let { data: lists } = api.lists.list.useQuery(undefined, {
-    initialData: { lists: initialData },
-    placeholderData: keepPreviousData,
-  });
-
-  // TODO: This seems to be a bug in react query
-  lists ||= { lists: initialData };
-
   return (
-    <div className="flex flex-col flex-wrap gap-2 md:flex-row">
-      <Button
-        className="my-auto flex h-full"
-        onClick={() => setIsNewListModalOpen(true)}
-      >
-        <Plus />
-        <span className="my-auto">New List</span>
-      </Button>
-      <ListItem name="Favourites" icon="⭐️" path={`/dashboard/favourites`} />
-      <ListItem name="Archive" icon="🗄️" path={`/dashboard/archive`} />
-      {lists.lists.map((l) => (
-        <ListItem
-          key={l.id}
-          name={l.name}
-          icon={l.icon}
-          path={`/dashboard/lists/${l.id}`}
-        />
-      ))}
-    </div>
+    <ul>
+      <EditListModal>
+        <Button className="mb-2 flex h-full w-full items-center">
+          <Plus />
+          <span>New List</span>
+        </Button>
+      </EditListModal>
+      <ListItem
+        collapsible={false}
+        name="Favourites"
+        icon="⭐️"
+        path={`/dashboard/favourites`}
+      />
+      <ListItem
+        collapsible={false}
+        name="Archive"
+        icon="🗄️"
+        path={`/dashboard/archive`}
+      />
+      <CollapsibleBookmarkLists
+        initialData={initialData}
+        render={({ item, level, open }) => (
+          <ListItem
+            key={item.item.id}
+            name={item.item.name}
+            icon={item.item.icon}
+            list={item.item}
+            path={`/dashboard/lists/${item.item.id}`}
+            collapsible={item.children.length > 0}
+            open={open}
+            style={{ marginLeft: `${level * 1}rem` }}
+          />
+        )}
+      />
+    </ul>
   );
 }
-- 
cgit v1.2.3-70-g09d2

