aboutsummaryrefslogtreecommitdiff
path: root/apps/web/src/components/Sidebar
diff options
context:
space:
mode:
authorYash <[email protected]>2024-04-01 02:19:54 +0000
committerYash <[email protected]>2024-04-01 02:19:54 +0000
commitfed4aa58c9de71f5cd02449e482d38373c58277b (patch)
treeef595d7a096885e4115818a52567a8955eeaf4e0 /apps/web/src/components/Sidebar
parentfix delete button on dark mode (diff)
downloadsupermemory-fed4aa58c9de71f5cd02449e482d38373c58277b.tar.xz
supermemory-fed4aa58c9de71f5cd02449e482d38373c58277b.zip
add new page popover
Diffstat (limited to 'apps/web/src/components/Sidebar')
-rw-r--r--apps/web/src/components/Sidebar/ListItem.tsx189
-rw-r--r--apps/web/src/components/Sidebar/index.tsx46
2 files changed, 235 insertions, 0 deletions
diff --git a/apps/web/src/components/Sidebar/ListItem.tsx b/apps/web/src/components/Sidebar/ListItem.tsx
new file mode 100644
index 00000000..e2648e4b
--- /dev/null
+++ b/apps/web/src/components/Sidebar/ListItem.tsx
@@ -0,0 +1,189 @@
+"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,
+ Edit3,
+ Trash2,
+ Save,
+ ChevronRight,
+ Plus,
+} 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";
+
+export const ListItem: React.FC<{ item: StoredContent }> = ({ item }) => {
+ const [isDropdownOpen, setIsDropdownOpen] = useState(false);
+ 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">
+ <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-upright-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>
+ <DropdownMenu open={isDropdownOpen} onOpenChange={setIsDropdownOpen}>
+ <DropdownMenuTrigger asChild>
+ <button className="ml-auto w-4 min-w-4 rounded-[0.15rem] opacity-0 focus-visible:opacity-100 focus-visible:outline-none">
+ <MoreHorizontal className="h-4 w-4 min-w-4" />
+ </button>
+ </DropdownMenuTrigger>
+ <DropdownMenuContent className="w-5">
+ <DropdownMenuItem onClick={() => window.open(item.url)}>
+ <ArrowUpRight
+ className="mr-2 h-4 w-4 scale-125"
+ strokeWidth={1.5}
+ />
+ Open
+ </DropdownMenuItem>
+ <DropdownMenuItem
+ onClick={() => {
+ setIsDropdownOpen(false);
+ setIsEditDrawerOpen(true);
+ }}
+ >
+ <Edit3 className="mr-2 h-4 w-4" strokeWidth={1.5} />
+ Edit
+ </DropdownMenuItem>
+ <DropdownMenuItem className="focus-visible:bg-red-100 focus-visible:text-red-400 dark:focus-visible:bg-red-100/10">
+ <Trash2 className="mr-2 h-4 w-4" strokeWidth={1.5} />
+ Delete
+ </DropdownMenuItem>
+ </DropdownMenuContent>
+ </DropdownMenu>
+ <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>
+ );
+};
+
+export const AddNewPagePopover: React.FC<{
+ addNewUrl?: (url: string) => Promise<void>;
+}> = ({ addNewUrl }) => {
+ const [isOpen, setIsOpen] = useState(false);
+ const [url, setUrl] = useState("");
+
+ return (
+ <Popover open={isOpen} onOpenChange={setIsOpen}>
+ <PopoverTrigger asChild>
+ <button className="focus-visible:ring-rgray-7 ring-offset-rgray-3 ml-auto rounded-sm ring-2 ring-transparent ring-offset-2 focus-visible:outline-none">
+ <Plus className="h-4 w-4 min-w-4" />
+ </button>
+ </PopoverTrigger>
+ <PopoverContent align="start" side="top">
+ <h1 className="mb-2 flex items-center justify-between ">
+ Add a new page
+ <button
+ onClick={() => {
+ setIsOpen(false);
+ addNewUrl?.(url);
+ }}
+ className="hover:bg-rgray-3 focus-visible:bg-rgray-4 focus-visible:ring-rgray-7 ring-offset-rgray-3 rounded-sm ring-2 ring-transparent ring-offset-2 transition focus-visible:outline-none"
+ >
+ <ChevronRight className="h-4 w-4" />
+ </button>
+ </h1>
+ <Input
+ className="w-full"
+ autoFocus
+ onChange={(e) => setUrl(e.target.value)}
+ onKeyDown={(e) => {
+ if (e.key === "Enter") {
+ setIsOpen(false);
+ addNewUrl?.(url);
+ }
+ }}
+ placeholder="Enter the URL of the page"
+ />
+ </PopoverContent>
+ </Popover>
+ );
+};
diff --git a/apps/web/src/components/Sidebar/index.tsx b/apps/web/src/components/Sidebar/index.tsx
new file mode 100644
index 00000000..0a658b77
--- /dev/null
+++ b/apps/web/src/components/Sidebar/index.tsx
@@ -0,0 +1,46 @@
+"use server";
+import { StoredContent } from "@/server/db/schema";
+import { AddNewPagePopover, ListItem } from "./ListItem";
+
+export default async function Sidebar() {
+ 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(),
+ },
+ ];
+
+ return (
+ <aside className="bg-rgray-3 flex h-screen w-[25%] flex-col items-start justify-between py-5 pb-[50vh] 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">
+ <h1 className="mb-1 flex w-full items-center justify-center px-3 font-normal">
+ Pages
+ <AddNewPagePopover />
+ </h1>
+ {pages.map((item) => (
+ <ListItem key={item.id} item={item} />
+ ))}
+ </div>
+ </aside>
+ );
+}