diff options
| author | Yash <[email protected]> | 2024-04-02 10:51:44 +0000 |
|---|---|---|
| committer | Yash <[email protected]> | 2024-04-02 10:51:44 +0000 |
| commit | 4400648205ea3f6172ed818b5e952556368ca913 (patch) | |
| tree | 5ac8dcddd3da1c133d866b64fb9f467368a7beca /apps/web/src | |
| parent | add wrangler.toml to gitignore (diff) | |
| download | archived-supermemory-4400648205ea3f6172ed818b5e952556368ca913.tar.xz archived-supermemory-4400648205ea3f6172ed818b5e952556368ca913.zip | |
filter combo box
Diffstat (limited to 'apps/web/src')
| -rw-r--r-- | apps/web/src/components/Main.tsx | 28 | ||||
| -rw-r--r-- | apps/web/src/components/Sidebar/FilterCombobox.tsx | 103 | ||||
| -rw-r--r-- | apps/web/src/components/Sidebar/index.tsx | 10 | ||||
| -rw-r--r-- | apps/web/src/components/ui/command.tsx | 155 | ||||
| -rw-r--r-- | apps/web/src/components/ui/dialog.tsx | 122 |
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, +}; |