aboutsummaryrefslogtreecommitdiff
path: root/apps/web/src/components
diff options
context:
space:
mode:
authorYash <[email protected]>2024-04-02 01:13:18 +0000
committerYash <[email protected]>2024-04-02 01:13:18 +0000
commit4ca2acd7b16fe6f9a9e5a7c32c6c01499b816d6f (patch)
treefae6af6176d8f79c21f68fa9b44077eac6d725d6 /apps/web/src/components
parentrename to PageItem (diff)
downloadarchived-supermemory-4ca2acd7b16fe6f9a9e5a7c32c6c01499b816d6f.tar.xz
archived-supermemory-4ca2acd7b16fe6f9a9e5a7c32c6c01499b816d6f.zip
git add categoryitem
Diffstat (limited to 'apps/web/src/components')
-rw-r--r--apps/web/src/components/Sidebar/CategoryItem.tsx298
-rw-r--r--apps/web/src/components/Sidebar/PagesItem.tsx4
-rw-r--r--apps/web/src/components/Sidebar/index.tsx15
3 files changed, 313 insertions, 4 deletions
diff --git a/apps/web/src/components/Sidebar/CategoryItem.tsx b/apps/web/src/components/Sidebar/CategoryItem.tsx
new file mode 100644
index 00000000..7fb571b5
--- /dev/null
+++ b/apps/web/src/components/Sidebar/CategoryItem.tsx
@@ -0,0 +1,298 @@
+"use client";
+import { cleanUrl } from "@/lib/utils";
+import { StoredContent } from "@/server/db/schema";
+import {
+ DropdownMenu,
+ DropdownMenuTrigger,
+ DropdownMenuContent,
+ DropdownMenuItem,
+} from "../ui/dropdown-menu";
+import { Label } from "../ui/label";
+import {
+ ArrowUpRight,
+ MoreHorizontal,
+ Tags,
+ ChevronDown,
+ Edit3,
+ Trash2,
+ Save,
+ ChevronRight,
+ Plus,
+ Minus,
+} from "lucide-react";
+import { useState } from "react";
+import {
+ Drawer,
+ DrawerContent,
+ DrawerHeader,
+ DrawerTitle,
+ DrawerDescription,
+ DrawerFooter,
+ DrawerClose,
+} from "../ui/drawer";
+import { Input } from "../ui/input";
+import { Textarea } from "../ui/textarea";
+import { Popover, PopoverContent, PopoverTrigger } from "../ui/popover";
+import {
+ AnimatePresence,
+ motion,
+ Reorder,
+ useMotionValue,
+} from "framer-motion";
+
+const pages: StoredContent[] = [
+ {
+ id: 1,
+ content: "",
+ title: "Visual Studio Code",
+ url: "https://code.visualstudio.com",
+ description: "",
+ image: "https://code.visualstudio.com/favicon.ico",
+ baseUrl: "https://code.visualstudio.com",
+ savedAt: new Date(),
+ },
+ {
+ id: 2,
+ content: "",
+ title: "yxshv/vscode: An unofficial remake of vscode's landing page",
+ url: "https://github.com/yxshv/vscode",
+ description: "",
+ image: "https://github.com/favicon.ico",
+ baseUrl: "https://github.com",
+ savedAt: new Date(),
+ },
+ {
+ id: 3,
+ content: "",
+ title: "yxshv/vscode: An unofficial remake of vscode's landing page",
+ url: "https://github.com/yxshv/vscode",
+ description: "",
+ image: "https://github.com/favicon.ico",
+ baseUrl: "https://github.com",
+ savedAt: new Date(),
+ },
+ {
+ id: 4,
+ content: "",
+ title: "yxshv/vscode: An unofficial remake of vscode's landing page",
+ url: "https://github.com/yxshv/vscode",
+ description: "",
+ image: "https://github.com/favicon.ico",
+ baseUrl: "https://github.com",
+ savedAt: new Date(),
+ },
+ {
+ id: 5,
+ content: "",
+ title: "yxshv/vscode: An unofficial remake of vscode's landing page",
+ url: "https://github.com/yxshv/vscode",
+ description: "",
+ image: "https://github.com/favicon.ico",
+ baseUrl: "https://github.com",
+ savedAt: new Date(),
+ },
+ {
+ id: 6,
+ content: "",
+ title: "yxshv/vscode: An unofficial remake of vscode's landing page",
+ url: "https://github.com/yxshv/vscode",
+ description: "",
+ image: "https://github.com/favicon.ico",
+ baseUrl: "https://github.com",
+ savedAt: new Date(),
+ },
+ {
+ id: 7,
+ content: "",
+ title: "yxshv/vscode: An unofficial remake of vscode's landing page",
+ url: "https://github.com/yxshv/vscode",
+ description: "",
+ image: "https://github.com/favicon.ico",
+ baseUrl: "https://github.com",
+ savedAt: new Date(),
+ },
+ {
+ id: 8,
+ content: "",
+ title: "yxshv/vscode: An unofficial remake of vscode's landing page",
+ url: "https://github.com/yxshv/vscode",
+ description: "",
+ image: "https://github.com/favicon.ico",
+ baseUrl: "https://github.com",
+ savedAt: new Date(),
+ },
+ {
+ id: 9,
+ content: "",
+ title: "yxshv/vscode: An unofficial remake of vscode's landing page",
+ url: "https://github.com/yxshv/vscode",
+ description: "",
+ image: "https://github.com/favicon.ico",
+ baseUrl: "https://github.com",
+ savedAt: new Date(),
+ },
+];
+export const CategoryItem: React.FC<{ item: StoredContent }> = ({ item }) => {
+ const [isExpanded, setIsExpanded] = useState(false);
+ const [isEditDrawerOpen, setIsEditDrawerOpen] = useState(false);
+
+ const [items, setItems] = useState<StoredContent[]>(pages);
+
+ return (
+ <>
+ <div className="hover:bg-rgray-5 has-[button:focus]:bg-rgray-5 flex w-full items-center rounded-full py-1 pl-3 pr-2 transition [&:hover>button>div>[data-down-icon]]:scale-125 [&:hover>button>div>[data-down-icon]]:opacity-100 [&:hover>button>div>[data-down-icon]]:delay-150 [&:hover>button>div>[data-tags-icon]]:scale-75 [&:hover>button>div>[data-tags-icon]]:opacity-0 [&:hover>button>div>[data-tags-icon]]:delay-0 [&:hover>button]:opacity-100">
+ <button
+ onClick={() => setIsExpanded((prev) => !prev)}
+ className="flex w-full items-center gap-2 focus-visible:outline-none"
+ >
+ <div className="relative h-5 min-w-5">
+ <Tags
+ data-tags-icon
+ className="z-1 h-5 w-5 transition-[transform,opacity] delay-150 duration-150"
+ strokeWidth={1.5}
+ />
+ <ChevronDown
+ data-down-icon
+ className={`absolute left-1/2 top-1/2 z-[2] h-4 w-4 min-w-4 -translate-x-1/2 -translate-y-1/2 scale-75 opacity-0 transition-[transform,opacity] duration-150 ${isExpanded ? "rotate-180" : "rotate-0"}`}
+ strokeWidth={1.5}
+ />
+ </div>
+
+ <span className="w-full truncate text-nowrap text-left">
+ {item.title ?? "Untitled website"}
+ </span>
+ </button>
+ <Drawer
+ shouldScaleBackground
+ open={isEditDrawerOpen}
+ onOpenChange={setIsEditDrawerOpen}
+ >
+ <DrawerContent className="pb-10 lg:px-[25vw]">
+ <DrawerHeader className="relative mt-10 px-0">
+ <DrawerTitle className=" flex w-full justify-between">
+ Edit Page Details
+ </DrawerTitle>
+ <DrawerDescription>Change the page details</DrawerDescription>
+ <a
+ target="_blank"
+ href={item.url}
+ className="text-rgray-11/90 bg-rgray-3 text-md absolute right-0 top-0 flex w-min translate-y-1/2 items-center justify-center gap-1 rounded-full px-5 py-1"
+ >
+ <img src={item.image ?? "/brain.png"} className="h-4 w-4" />
+ {cleanUrl(item.url)}
+ </a>
+ </DrawerHeader>
+
+ <div className="mt-5">
+ <Label>Title</Label>
+ <Input
+ className=""
+ required
+ value={item.title ?? ""}
+ placeholder={item.title ?? "Enter the title for the page"}
+ />
+ </div>
+ <div className="mt-5">
+ <Label>Additional Context</Label>
+ <Textarea
+ className=""
+ value={item.content ?? ""}
+ placeholder={"Enter additional context for this page"}
+ />
+ </div>
+ <DrawerFooter className="flex flex-row-reverse items-center justify-end px-0 pt-5">
+ <DrawerClose className="flex items-center justify-center rounded-md px-3 py-2 ring-2 ring-transparent transition hover:bg-blue-100 hover:text-blue-400 focus-visible:bg-blue-100 focus-visible:text-blue-400 focus-visible:outline-none focus-visible:ring-blue-200 dark:hover:bg-blue-100/10 dark:focus-visible:bg-blue-100/10 dark:focus-visible:ring-blue-200/30">
+ <Save className="mr-2 h-4 w-4 " strokeWidth={1.5} />
+ Save
+ </DrawerClose>
+ <DrawerClose className="hover:bg-rgray-3 focus-visible:bg-rgray-4 focus-visible:ring-rgray-7 flex items-center justify-center rounded-md px-3 py-2 ring-2 ring-transparent transition focus-visible:outline-none">
+ Cancel
+ </DrawerClose>
+ <DrawerClose className="mr-auto flex items-center justify-center rounded-md bg-red-100 px-3 py-2 text-red-400 ring-2 ring-transparent transition focus-visible:outline-none focus-visible:ring-red-200 dark:bg-red-100/10 dark:focus-visible:ring-red-200/30">
+ <Trash2 className="mr-2 h-4 w-4 " strokeWidth={1.5} />
+ Delete
+ </DrawerClose>
+ </DrawerFooter>
+ </DrawerContent>
+ </Drawer>
+ </div>
+ <AnimatePresence>
+ {isExpanded && (
+ <Reorder.Group
+ axis="y"
+ values={items}
+ onReorder={setItems}
+ as="div"
+ initial={{ height: 0 }}
+ animate={{ height: "auto" }}
+ exit={{
+ height: 0,
+ transition: {},
+ }}
+ layoutScroll
+ className="flex max-h-32 w-full flex-col items-center overflow-y-auto pl-7"
+ >
+ <AnimatePresence>
+ {items.map((item, i) => (
+ <CategoryPage
+ key={item.id}
+ index={i}
+ item={item}
+ onRemove={() =>
+ setItems((prev) => prev.filter((_, index) => i !== index))
+ }
+ />
+ ))}
+ </AnimatePresence>
+ </Reorder.Group>
+ )}
+ </AnimatePresence>
+ </>
+ );
+};
+
+export const CategoryPage: React.FC<{
+ item: StoredContent;
+ index: number;
+ onRemove?: () => void;
+}> = ({ item, onRemove, index }) => {
+ return (
+ <Reorder.Item
+ value={item}
+ as="div"
+ key={index}
+ exit={{ opacity: 0, scale: 0.8 }}
+ dragListener={false}
+ className="hover:bg-rgray-5 has-[a:focus]:bg-rgray-5 flex w-full items-center rounded-full py-1 pl-3 pr-2 transition [&:hover>a>div>[data-icon]]:scale-125 [&:hover>a>div>[data-icon]]:opacity-100 [&:hover>a>div>[data-icon]]:delay-150 [&:hover>a>div>img]:scale-75 [&:hover>a>div>img]:opacity-0 [&:hover>a>div>img]:delay-0 [&:hover>button]:opacity-100"
+ >
+ <a
+ href={item.url}
+ target="_blank"
+ className="flex w-[90%] items-center gap-2 focus-visible:outline-none"
+ >
+ <div className="relative h-4 min-w-4">
+ <img
+ src={item.image ?? "/brain.png"}
+ alt={item.title ?? "Untitiled website"}
+ className="z-1 h-4 w-4 transition-[transform,opacity] delay-150 duration-150"
+ />
+ <ArrowUpRight
+ data-icon
+ className="absolute left-1/2 top-1/2 z-[2] h-4 w-4 min-w-4 -translate-x-1/2 -translate-y-1/2 scale-75 opacity-0 transition-[transform,opacity] duration-150"
+ strokeWidth={1.5}
+ />
+ </div>
+
+ <span className="w-full truncate text-nowrap">
+ {item.title ?? "Untitled website"}
+ </span>
+ </a>
+ <button
+ onClick={() => onRemove?.()}
+ className="ml-auto w-4 min-w-4 rounded-[0.15rem] opacity-0 focus-visible:opacity-100 focus-visible:outline-none"
+ >
+ <Minus className="h-4 w-4 min-w-4" />
+ </button>
+ </Reorder.Item>
+ );
+};
diff --git a/apps/web/src/components/Sidebar/PagesItem.tsx b/apps/web/src/components/Sidebar/PagesItem.tsx
index ce762ae5..fea8bf33 100644
--- a/apps/web/src/components/Sidebar/PagesItem.tsx
+++ b/apps/web/src/components/Sidebar/PagesItem.tsx
@@ -36,7 +36,7 @@ export const PageItem: React.FC<{ item: StoredContent }> = ({ item }) => {
const [isEditDrawerOpen, setIsEditDrawerOpen] = useState(false);
return (
- <div className="hover:bg-rgray-5 focus-within:bg-rgray-5 flex w-full items-center rounded-full py-1 pl-3 pr-2 transition [&:hover>a>div>[data-upright-icon]]:scale-125 [&:hover>a>div>[data-upright-icon]]:opacity-100 [&:hover>a>div>[data-upright-icon]]:delay-150 [&:hover>a>div>img]:scale-75 [&:hover>a>div>img]:opacity-0 [&:hover>a>div>img]:delay-0 [&:hover>button]:opacity-100">
+ <div className="hover:bg-rgray-5 has-[a:focus]:bg-rgray-5 flex w-full items-center rounded-full py-1 pl-3 pr-2 transition [&:hover>a>div>[data-icon]]:scale-125 [&:hover>a>div>[data-icon]]:opacity-100 [&:hover>a>div>[data-icon]]:delay-150 [&:hover>a>div>img]:scale-75 [&:hover>a>div>img]:opacity-0 [&:hover>a>div>img]:delay-0 [&:hover>button]:opacity-100">
<a
href={item.url}
target="_blank"
@@ -49,7 +49,7 @@ export const PageItem: React.FC<{ item: StoredContent }> = ({ item }) => {
className="z-1 h-4 w-4 transition-[transform,opacity] delay-150 duration-150"
/>
<ArrowUpRight
- data-upright-icon
+ data-icon
className="absolute left-1/2 top-1/2 z-[2] h-4 w-4 min-w-4 -translate-x-1/2 -translate-y-1/2 scale-75 opacity-0 transition-[transform,opacity] duration-150"
strokeWidth={1.5}
/>
diff --git a/apps/web/src/components/Sidebar/index.tsx b/apps/web/src/components/Sidebar/index.tsx
index 80d4beb5..4f295bb3 100644
--- a/apps/web/src/components/Sidebar/index.tsx
+++ b/apps/web/src/components/Sidebar/index.tsx
@@ -1,6 +1,7 @@
"use server";
import { StoredContent } from "@/server/db/schema";
import { AddNewPagePopover, PageItem } from "./PagesItem";
+import { CategoryItem } from "./CategoryItem";
export default async function Sidebar() {
const pages: StoredContent[] = [
@@ -27,12 +28,12 @@ export default async function Sidebar() {
];
return (
- <aside className="bg-rgray-3 flex h-screen w-[25%] flex-col items-start justify-between py-5 pb-[50vh] font-light">
+ <aside className="bg-rgray-3 flex h-screen w-[25%] flex-col items-start py-5 font-light">
<div className="flex items-center justify-center gap-1 px-5 text-xl font-normal">
<img src="/brain.png" alt="logo" className="h-10 w-10" />
SuperMemory
</div>
- <div className="flex w-full flex-col items-start justify-center p-2">
+ <div className="flex h-1/3 w-full flex-col items-start justify-center p-2">
<h1 className="mb-1 flex w-full items-center justify-center px-3 font-normal">
Pages
<AddNewPagePopover />
@@ -41,6 +42,16 @@ export default async function Sidebar() {
<PageItem key={item.id} item={item} />
))}
</div>
+ <div className="flex h-1/2 w-full flex-col items-start p-2">
+ <h1 className="mb-1 flex w-full items-center justify-center px-3 font-normal">
+ Categories
+ <AddNewPagePopover />
+ </h1>
+ {pages.map((item) => (
+ <CategoryItem key={item.id} item={item} />
+ ))}
+ </div>
+ <button>y</button>
</aside>
);
}