aboutsummaryrefslogtreecommitdiff
path: root/apps/web/src/components
diff options
context:
space:
mode:
authorYash <[email protected]>2024-04-02 10:51:44 +0000
committerYash <[email protected]>2024-04-02 10:51:44 +0000
commit4400648205ea3f6172ed818b5e952556368ca913 (patch)
tree5ac8dcddd3da1c133d866b64fb9f467368a7beca /apps/web/src/components
parentadd wrangler.toml to gitignore (diff)
downloadarchived-supermemory-4400648205ea3f6172ed818b5e952556368ca913.tar.xz
archived-supermemory-4400648205ea3f6172ed818b5e952556368ca913.zip
filter combo box
Diffstat (limited to 'apps/web/src/components')
-rw-r--r--apps/web/src/components/Main.tsx28
-rw-r--r--apps/web/src/components/Sidebar/FilterCombobox.tsx103
-rw-r--r--apps/web/src/components/Sidebar/index.tsx10
-rw-r--r--apps/web/src/components/ui/command.tsx155
-rw-r--r--apps/web/src/components/ui/dialog.tsx122
5 files changed, 399 insertions, 19 deletions
diff --git a/apps/web/src/components/Main.tsx b/apps/web/src/components/Main.tsx
index f5f70bc8..5ec10364 100644
--- a/apps/web/src/components/Main.tsx
+++ b/apps/web/src/components/Main.tsx
@@ -1,8 +1,12 @@
-import { SpaceIcon } from "@/assets/Memories";
+"use client";
+import { useState } from "react";
+import { FilterCombobox } from "./Sidebar/FilterCombobox";
import { Textarea2 } from "./ui/textarea";
-import { ArrowRight, ChevronDown, GlobeIcon } from "lucide-react";
+import { ArrowRight } from "lucide-react";
export default function Main() {
+ const [value, setValue] = useState("");
+
return (
<main className="flex h-screen w-full items-center justify-center px-60">
<Textarea2
@@ -10,20 +14,16 @@ export default function Main() {
textAreaProps={{
placeholder: "Ask your SuperMemory...",
className: "text-lg p-2 text-rgray-11",
+ value,
+ onChange: (e) => setValue(e.target.value),
}}
>
- <div className="text-rgray-11/70 flex w-full items-center justify-center p-2">
- <button className="text-rgray-11/70 focus-visible:ring-rgray-8 hover:bg-rgray-3 flex items-center justify-center gap-1 rounded-md px-2 py-1 ring-2 ring-transparent focus-visible:outline-none">
- <SpaceIcon className="mr-1 h-5 w-5" />
- Spaces
- <ChevronDown className="h-4 w-4" />
- </button>
- <button className="text-rgray-11/70 focus-visible:ring-rgray-8 hover:bg-rgray-3 flex items-center justify-center gap-1 rounded-md px-2 py-1 ring-2 ring-transparent focus-visible:outline-none">
- <GlobeIcon className="mr-1 h-4 w-4" />
- Pages
- <ChevronDown className="h-4 w-4" />
- </button>
- <button className="text-rgray-11/70 bg-rgray-3 focus-visible:ring-rgray-8 hover:bg-rgray-4 ml-auto flex items-center justify-center rounded-full p-2 ring-2 ring-transparent focus-visible:outline-none">
+ <div className="text-rgray-11/70 flex w-full items-center justify-center p-2 pl-0">
+ <FilterCombobox />
+ <button
+ disabled={value.trim().length < 1}
+ className="text-rgray-11/70 bg-rgray-3 focus-visible:ring-rgray-8 hover:bg-rgray-4 ml-auto flex items-center justify-center rounded-full p-2 ring-2 ring-transparent focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50"
+ >
<ArrowRight className="h-5 w-5" />
</button>
</div>
diff --git a/apps/web/src/components/Sidebar/FilterCombobox.tsx b/apps/web/src/components/Sidebar/FilterCombobox.tsx
new file mode 100644
index 00000000..50e885e8
--- /dev/null
+++ b/apps/web/src/components/Sidebar/FilterCombobox.tsx
@@ -0,0 +1,103 @@
+"use client";
+
+import * as React from "react";
+import { Check, ChevronsUpDown } from "lucide-react";
+
+import { cn } from "@/lib/utils";
+import { Button } from "@/components/ui/button";
+import {
+ Command,
+ CommandEmpty,
+ CommandGroup,
+ CommandInput,
+ CommandItem,
+ CommandList,
+} from "@/components/ui/command";
+import {
+ Popover,
+ PopoverContent,
+ PopoverTrigger,
+} from "@/components/ui/popover";
+import { SpaceIcon } from "@/assets/Memories";
+
+const spaces = [
+ {
+ value: "1",
+ label: "Cool Tech",
+ },
+ {
+ value: "2",
+ label: "Cool Courses",
+ },
+ {
+ value: "3",
+ label: "Cool Libraries",
+ },
+];
+
+export function FilterCombobox() {
+ const [open, setOpen] = React.useState(false);
+ const [values, setValues] = React.useState<string[]>([]);
+
+ return (
+ <Popover open={open} onOpenChange={setOpen}>
+ <PopoverTrigger asChild>
+ <button
+ data-state-on={open}
+ className="text-rgray-11/70 on:bg-rgray-3 focus-visible:ring-rgray-8 hover:bg-rgray-3 relative flex items-center justify-center gap-1 rounded-md px-3 py-1.5 ring-2 ring-transparent focus-visible:outline-none"
+ >
+ <SpaceIcon className="mr-1 h-5 w-5" />
+ Filter
+ <ChevronsUpDown className="h-4 w-4" />
+ <div
+ data-state-on={values.length > 0}
+ className="on:flex text-rgray-11 border-rgray-6 bg-rgray-2 absolute left-0 top-0 hidden aspect-[1] h-4 w-4 -translate-x-1/3 -translate-y-1/3 items-center justify-center rounded-full border text-center text-[9px]"
+ >
+ {values.length}
+ </div>
+ </button>
+ </PopoverTrigger>
+ <PopoverContent className="w-[200px] p-0">
+ <Command
+ filter={(val, search) =>
+ spaces
+ .find((s) => s.value === val)
+ ?.label.toLowerCase()
+ .includes(search.toLowerCase().trim())
+ ? 1
+ : 0
+ }
+ >
+ <CommandInput placeholder="Filter spaces..." />
+ <CommandList>
+ <CommandEmpty>Nothing found</CommandEmpty>
+ <CommandGroup>
+ {spaces.map((space) => (
+ <CommandItem
+ key={space.value}
+ value={space.value}
+ onSelect={(val) => {
+ console.log(val, "selected");
+ setValues((prev) =>
+ prev.includes(val)
+ ? prev.filter((v) => v !== val)
+ : [...prev, val],
+ );
+ }}
+ >
+ <SpaceIcon className="mr-2 h-4 w-4" />
+ {space.label}
+ {values.includes(space.value)}
+ <Check
+ data-state-on={values.includes(space.value)}
+ className={cn("on:opacity-100 ml-auto h-4 w-4 opacity-0")}
+ />
+ </CommandItem>
+ ))}
+ </CommandGroup>
+ </CommandList>
+ </Command>
+ </PopoverContent>
+ </Popover>
+ );
+}
diff --git a/apps/web/src/components/Sidebar/index.tsx b/apps/web/src/components/Sidebar/index.tsx
index 49146140..dda95907 100644
--- a/apps/web/src/components/Sidebar/index.tsx
+++ b/apps/web/src/components/Sidebar/index.tsx
@@ -35,7 +35,7 @@ export default async function Sidebar() {
// data-state-on="true"
className="on:opacity-100 on:bg-rgray-4 focus-visible:ring-rgray-7 flex w-full flex-col items-center justify-center rounded-md px-3 py-3 opacity-80 ring-2 ring-transparent transition hover:opacity-100 focus-visible:opacity-100 focus-visible:outline-none"
>
- <MemoryIcon className="h-12 w-12" />
+ <MemoryIcon className="h-10 w-10" />
<span className="">Memories</span>
</button>
<button
@@ -46,7 +46,7 @@ export default async function Sidebar() {
<span className="">Trash</span>
</button>
<button
- data-state-on="true"
+ // data-state-on="true"
className="on:opacity-100 on:bg-rgray-3 focus-visible:ring-rgray-7 flex w-full flex-col items-center justify-center gap-1 rounded-md px-3 py-4 opacity-80 ring-2 ring-transparent transition hover:opacity-100 focus-visible:opacity-100 focus-visible:outline-none"
>
<User2 strokeWidth={1.3} className="h-6 w-6" />
@@ -81,12 +81,12 @@ export async function SubSidebar() {
];
return (
- <aside className="bg-rgray-2 border-rgray-6 flex h-screen w-max flex-col items-center border-r px-3 py-5 font-light">
+ <aside className="bg-rgray-2 border-rgray-6 flex h-screen w-[20vw] flex-col items-center border-r px-3 py-5 font-light">
<button
// data-state-on="true"
className="on:opacity-100 on:bg-rgray-3 focus-visible:ring-rgray-7 flex w-full flex-col items-center justify-center rounded-md px-4 py-3 opacity-80 ring-2 ring-transparent transition hover:opacity-100 focus-visible:opacity-100 focus-visible:outline-none"
>
- <MemoryIcon className="h-12 w-12" />
+ <MemoryIcon className="h-10 w-10" />
<span className="">Memories</span>
</button>
<button
@@ -94,7 +94,7 @@ export async function SubSidebar() {
className="on:opacity-100 focus-visible:ring-rgray-7 mt-auto flex w-full flex-col items-center justify-center gap-1 rounded-md bg-black p-4 opacity-80 ring-2 ring-transparent transition hover:opacity-100 focus-visible:opacity-100 focus-visible:outline-none"
>
<Trash2 strokeWidth={1.3} className="h-6 w-6" />
- <span className="">Trash3</span>
+ <span className="">Trash</span>
</button>
<button
// data-state-on="true"
diff --git a/apps/web/src/components/ui/command.tsx b/apps/web/src/components/ui/command.tsx
new file mode 100644
index 00000000..54070776
--- /dev/null
+++ b/apps/web/src/components/ui/command.tsx
@@ -0,0 +1,155 @@
+"use client";
+
+import * as React from "react";
+import { type DialogProps } from "@radix-ui/react-dialog";
+import { Command as CommandPrimitive } from "cmdk";
+import { Search } from "lucide-react";
+
+import { cn } from "@/lib/utils";
+import { Dialog, DialogContent } from "@/components/ui/dialog";
+
+const Command = React.forwardRef<
+ React.ElementRef<typeof CommandPrimitive>,
+ React.ComponentPropsWithoutRef<typeof CommandPrimitive>
+>(({ className, ...props }, ref) => (
+ <CommandPrimitive
+ ref={ref}
+ className={cn(
+ "bg-rgray-3 text-rgray-11 flex h-full w-full flex-col overflow-hidden rounded-md",
+ className,
+ )}
+ {...props}
+ />
+));
+Command.displayName = CommandPrimitive.displayName;
+
+interface CommandDialogProps extends DialogProps {}
+
+const CommandDialog = ({ children, ...props }: CommandDialogProps) => {
+ return (
+ <Dialog {...props}>
+ <DialogContent className="overflow-hidden p-0 shadow-lg">
+ <Command className="[&_[cmdk-group-heading]]:text-rgray-11 [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-group]]:px-2 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5">
+ {children}
+ </Command>
+ </DialogContent>
+ </Dialog>
+ );
+};
+
+const CommandInput = React.forwardRef<
+ React.ElementRef<typeof CommandPrimitive.Input>,
+ React.ComponentPropsWithoutRef<typeof CommandPrimitive.Input>
+>(({ className, ...props }, ref) => (
+ <div
+ className="border-rgray-6 flex items-center border-b px-3"
+ cmdk-input-wrapper=""
+ >
+ <Search className="mr-2 h-4 w-4 shrink-0 opacity-50" />
+ <CommandPrimitive.Input
+ ref={ref}
+ className={cn(
+ "placeholder:text-rgray-11/50 flex h-11 w-full rounded-md bg-transparent py-3 text-sm outline-none disabled:cursor-not-allowed disabled:opacity-50",
+ className,
+ )}
+ {...props}
+ />
+ </div>
+));
+
+CommandInput.displayName = CommandPrimitive.Input.displayName;
+
+const CommandList = React.forwardRef<
+ React.ElementRef<typeof CommandPrimitive.List>,
+ React.ComponentPropsWithoutRef<typeof CommandPrimitive.List>
+>(({ className, ...props }, ref) => (
+ <CommandPrimitive.List
+ ref={ref}
+ className={cn("max-h-[300px] overflow-y-auto overflow-x-hidden", className)}
+ {...props}
+ />
+));
+
+CommandList.displayName = CommandPrimitive.List.displayName;
+
+const CommandEmpty = React.forwardRef<
+ React.ElementRef<typeof CommandPrimitive.Empty>,
+ React.ComponentPropsWithoutRef<typeof CommandPrimitive.Empty>
+>((props, ref) => (
+ <CommandPrimitive.Empty
+ ref={ref}
+ className="py-6 text-center text-sm"
+ {...props}
+ />
+));
+
+CommandEmpty.displayName = CommandPrimitive.Empty.displayName;
+
+const CommandGroup = React.forwardRef<
+ React.ElementRef<typeof CommandPrimitive.Group>,
+ React.ComponentPropsWithoutRef<typeof CommandPrimitive.Group>
+>(({ className, ...props }, ref) => (
+ <CommandPrimitive.Group
+ ref={ref}
+ className={cn(
+ "text-rgray-12 [&_[cmdk-group-heading]]:text-rgray-11 overflow-hidden p-1 [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium",
+ className,
+ )}
+ {...props}
+ />
+));
+
+CommandGroup.displayName = CommandPrimitive.Group.displayName;
+
+const CommandSeparator = React.forwardRef<
+ React.ElementRef<typeof CommandPrimitive.Separator>,
+ React.ComponentPropsWithoutRef<typeof CommandPrimitive.Separator>
+>(({ className, ...props }, ref) => (
+ <CommandPrimitive.Separator
+ ref={ref}
+ className={cn("bg-rgray-3 -mx-1 h-px", className)}
+ {...props}
+ />
+));
+CommandSeparator.displayName = CommandPrimitive.Separator.displayName;
+
+const CommandItem = React.forwardRef<
+ React.ElementRef<typeof CommandPrimitive.Item>,
+ React.ComponentPropsWithoutRef<typeof CommandPrimitive.Item>
+>(({ className, ...props }, ref) => (
+ <CommandPrimitive.Item
+ ref={ref}
+ className={cn(
+ "aria-selected:bg-rgray-5 aria-selected:text-rgray-12 relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
+ className,
+ )}
+ {...props}
+ />
+));
+
+CommandItem.displayName = CommandPrimitive.Item.displayName;
+
+const CommandShortcut = ({
+ className,
+ ...props
+}: React.HTMLAttributes<HTMLSpanElement>) => {
+ return (
+ <span
+ className={cn("text-gray-11 ml-auto text-xs tracking-widest", className)}
+ {...props}
+ />
+ );
+};
+CommandShortcut.displayName = "CommandShortcut";
+
+export {
+ Command,
+ CommandDialog,
+ CommandInput,
+ CommandList,
+ CommandEmpty,
+ CommandGroup,
+ CommandItem,
+ CommandShortcut,
+ CommandSeparator,
+};
diff --git a/apps/web/src/components/ui/dialog.tsx b/apps/web/src/components/ui/dialog.tsx
new file mode 100644
index 00000000..ec19b41a
--- /dev/null
+++ b/apps/web/src/components/ui/dialog.tsx
@@ -0,0 +1,122 @@
+"use client";
+
+import * as React from "react";
+import * as DialogPrimitive from "@radix-ui/react-dialog";
+import { X } from "lucide-react";
+
+import { cn } from "@/lib/utils";
+
+const Dialog = DialogPrimitive.Root;
+
+const DialogTrigger = DialogPrimitive.Trigger;
+
+const DialogPortal = DialogPrimitive.Portal;
+
+const DialogClose = DialogPrimitive.Close;
+
+const DialogOverlay = React.forwardRef<
+ React.ElementRef<typeof DialogPrimitive.Overlay>,
+ React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>
+>(({ className, ...props }, ref) => (
+ <DialogPrimitive.Overlay
+ ref={ref}
+ className={cn(
+ "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/80",
+ className,
+ )}
+ {...props}
+ />
+));
+DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;
+
+const DialogContent = React.forwardRef<
+ React.ElementRef<typeof DialogPrimitive.Content>,
+ React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content>
+>(({ className, children, ...props }, ref) => (
+ <DialogPortal>
+ <DialogOverlay />
+ <DialogPrimitive.Content
+ ref={ref}
+ className={cn(
+ "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] border-rgray-6 bg-rgray-3 fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border p-6 shadow-lg duration-200 sm:rounded-lg",
+ className,
+ )}
+ {...props}
+ >
+ {children}
+ <DialogPrimitive.Close className="ring-offset-rgray-2 focus:ring-rgray-7 data-[state=open]:bg-rgray-3 data-[state=open]:text-rgray-11 absolute right-4 top-4 rounded-sm opacity-70 transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-offset-2 disabled:pointer-events-none">
+ <X className="h-4 w-4" />
+ <span className="sr-only">Close</span>
+ </DialogPrimitive.Close>
+ </DialogPrimitive.Content>
+ </DialogPortal>
+));
+DialogContent.displayName = DialogPrimitive.Content.displayName;
+
+const DialogHeader = ({
+ className,
+ ...props
+}: React.HTMLAttributes<HTMLDivElement>) => (
+ <div
+ className={cn(
+ "flex flex-col space-y-1.5 text-center sm:text-left",
+ className,
+ )}
+ {...props}
+ />
+);
+DialogHeader.displayName = "DialogHeader";
+
+const DialogFooter = ({
+ className,
+ ...props
+}: React.HTMLAttributes<HTMLDivElement>) => (
+ <div
+ className={cn(
+ "flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
+ className,
+ )}
+ {...props}
+ />
+);
+DialogFooter.displayName = "DialogFooter";
+
+const DialogTitle = React.forwardRef<
+ React.ElementRef<typeof DialogPrimitive.Title>,
+ React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title>
+>(({ className, ...props }, ref) => (
+ <DialogPrimitive.Title
+ ref={ref}
+ className={cn(
+ "text-lg font-semibold leading-none tracking-tight",
+ className,
+ )}
+ {...props}
+ />
+));
+DialogTitle.displayName = DialogPrimitive.Title.displayName;
+
+const DialogDescription = React.forwardRef<
+ React.ElementRef<typeof DialogPrimitive.Description>,
+ React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description>
+>(({ className, ...props }, ref) => (
+ <DialogPrimitive.Description
+ ref={ref}
+ className={cn("text-rgray-11 text-sm", className)}
+ {...props}
+ />
+));
+DialogDescription.displayName = DialogPrimitive.Description.displayName;
+
+export {
+ Dialog,
+ DialogPortal,
+ DialogOverlay,
+ DialogClose,
+ DialogTrigger,
+ DialogContent,
+ DialogHeader,
+ DialogFooter,
+ DialogTitle,
+ DialogDescription,
+};