diff options
| author | Yash <[email protected]> | 2024-04-02 01:13:18 +0000 |
|---|---|---|
| committer | Yash <[email protected]> | 2024-04-02 01:13:18 +0000 |
| commit | 4ca2acd7b16fe6f9a9e5a7c32c6c01499b816d6f (patch) | |
| tree | fae6af6176d8f79c21f68fa9b44077eac6d725d6 /apps/web/src/components | |
| parent | rename to PageItem (diff) | |
| download | archived-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.tsx | 298 | ||||
| -rw-r--r-- | apps/web/src/components/Sidebar/PagesItem.tsx | 4 | ||||
| -rw-r--r-- | apps/web/src/components/Sidebar/index.tsx | 15 |
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> ); } |