diff options
| author | Yash <[email protected]> | 2024-04-03 10:48:29 +0000 |
|---|---|---|
| committer | Yash <[email protected]> | 2024-04-03 10:48:29 +0000 |
| commit | bed6d65ffa6a3886d77d497463748983c822100d (patch) | |
| tree | c88c0292adec220659cda32a45cc8cfcd02213ac /apps/web/src/components | |
| parent | remove debug lie (diff) | |
| download | supermemory-bed6d65ffa6a3886d77d497463748983c822100d.tar.xz supermemory-bed6d65ffa6a3886d77d497463748983c822100d.zip | |
animation with framer motion
Diffstat (limited to 'apps/web/src/components')
| -rw-r--r-- | apps/web/src/components/Main.tsx | 10 | ||||
| -rw-r--r-- | apps/web/src/components/Sidebar/FilterCombobox.tsx | 266 | ||||
| -rw-r--r-- | apps/web/src/components/Sidebar/MemoriesBar.tsx | 2 | ||||
| -rw-r--r-- | apps/web/src/components/Sidebar/index.tsx | 81 | ||||
| -rw-r--r-- | apps/web/src/components/ui/command.tsx | 4 |
5 files changed, 267 insertions, 96 deletions
diff --git a/apps/web/src/components/Main.tsx b/apps/web/src/components/Main.tsx index b6ad3787..ef505db5 100644 --- a/apps/web/src/components/Main.tsx +++ b/apps/web/src/components/Main.tsx @@ -5,6 +5,7 @@ import { Textarea2 } from "./ui/textarea"; import { ArrowRight } from "lucide-react"; import { MemoryDrawer } from "./MemoryDrawer"; import useViewport from "@/hooks/useViewport"; +import { motion } from "framer-motion"; export default function Main({ sidebarOpen }: { sidebarOpen: boolean }) { const [value, setValue] = useState(""); @@ -24,10 +25,13 @@ export default function Main({ sidebarOpen }: { sidebarOpen: boolean }) { }, []); return ( - <main + <motion.main data-sidebar-open={sidebarOpen} - className="flex h-screen max-h-screen w-full items-end justify-center px-5 pb-[20vh] pt-5 md:items-center md:px-60 md:[&[data-sidebar-open='true']]:px-20" + className="flex h-screen max-h-screen w-full flex-col items-end justify-center gap-5 px-5 pb-[20vh] pt-5 transition-[padding] delay-200 duration-200 md:items-center md:px-72 [&[data-sidebar-open='true']]:pl-[calc(2.5rem+30vw)] [&[data-sidebar-open='true']]:pr-10 [&[data-sidebar-open='true']]:delay-0 " > + <h1 className="text-rgray-11 text-center text-3xl"> + Ask your Second brain + </h1> <Textarea2 ref={textArea} className="h-max max-h-[30em] min-h-[3em] resize-y flex-row items-start justify-center overflow-auto py-5 md:h-[20vh] md:resize-none md:flex-col md:items-center md:justify-center md:p-2 md:pb-2 md:pt-2" @@ -51,6 +55,6 @@ export default function Main({ sidebarOpen }: { sidebarOpen: boolean }) { </div> </Textarea2> {width <= 768 && <MemoryDrawer />} - </main> + </motion.main> ); } diff --git a/apps/web/src/components/Sidebar/FilterCombobox.tsx b/apps/web/src/components/Sidebar/FilterCombobox.tsx index ade54711..d22e8d8c 100644 --- a/apps/web/src/components/Sidebar/FilterCombobox.tsx +++ b/apps/web/src/components/Sidebar/FilterCombobox.tsx @@ -19,6 +19,7 @@ import { PopoverTrigger, } from "@/components/ui/popover"; import { SpaceIcon } from "@/assets/Memories"; +import { AnimatePresence, LayoutGroup, motion } from "framer-motion"; const spaces = [ { @@ -33,6 +34,118 @@ const spaces = [ value: "3", label: "Cool Libraries", }, + { + value: "4", + label: "Cool People", + }, + { + value: "5", + label: "Cool Projects", + }, + { + value: "6", + label: "Cool Tools", + }, + { + value: "7", + label: "Cool Websites", + }, + { + value: "8", + label: "Cool Books", + }, + { + value: "9", + label: "Cool Videos", + }, + { + value: "10", + label: "Cool Podcasts", + }, + { + value: "11", + label: "Cool Articles", + }, + { + value: "12", + label: "Cool Blogs", + }, + { + value: "13", + label: "Cool News", + }, + { + value: "14", + label: "Cool Forums", + }, + { + value: "15", + label: "Cool Communities", + }, + { + value: "16", + label: "Cool Events", + }, + { + value: "17", + label: "Cool Jobs", + }, + { + value: "18", + label: "Cool Companies", + }, + { + value: "19", + label: "Cool Startups", + }, + { + value: "20", + label: "Cool Investors", + }, + { + value: "21", + label: "Cool Funds", + }, + { + value: "22", + label: "Cool Incubators", + }, + { + value: "23", + label: "Cool Accelerators", + }, + { + value: "24", + label: "Cool Hackathons", + }, + { + value: "25", + label: "Cool Conferences", + }, + { + value: "26", + label: "Cool Workshops", + }, + { + value: "27", + label: "Cool Seminars", + }, + { + value: "28", + label: "Cool Webinars", + }, + { + value: "29", + label: "Cool Courses", + }, + { + value: "30", + label: "Cool Bootcamps", + }, + { + value: "31", + label: "Cool Certifications", + }, ]; export interface Props extends React.ButtonHTMLAttributes<HTMLButtonElement> {} @@ -41,69 +154,96 @@ export function FilterCombobox({ className, ...props }: Props) { const [open, setOpen] = React.useState(false); const [values, setValues] = React.useState<string[]>([]); + const sortedSpaces = spaces.sort(({ value: a }, { value: b }) => + values.includes(a) && !values.includes(b) + ? -1 + : values.includes(b) && !values.includes(a) + ? 1 + : 0, + ); + + console.log(sortedSpaces, values); + return ( - <Popover open={open} onOpenChange={setOpen}> - <PopoverTrigger asChild> - <button - data-state-on={open} - className={cn( - "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", - className, - )} - {...props} - > - <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> - {/* bug: doesn't work on clicking with mouse only keyboard, weird */} - <CommandGroup> - {spaces.map((space) => ( - <CommandItem - key={space.value} - value={space.value} - onSelect={(val) => { - 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> + <AnimatePresence mode="popLayout"> + <LayoutGroup> + <Popover open={open} onOpenChange={setOpen}> + <PopoverTrigger asChild> + <button + data-state-on={open} + className={cn( + "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", + className, + )} + {...props} + > + <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 asChild> + <motion.div layoutScroll> + <CommandEmpty>Nothing found</CommandEmpty> + <CommandGroup> + {sortedSpaces.map((space) => ( + <CommandItem + key={space.value} + value={space.value} + onSelect={(val) => { + setValues((prev) => + prev.includes(val) + ? prev.filter((v) => v !== val) + : [...prev, val], + ); + }} + asChild + > + <motion.div + initial={{ opacity: 0 }} + animate={{ opacity: 1, transition: { delay: 0.05 } }} + transition={{ duration: 0.15 }} + layout + layoutId={`space-combobox-${space.value}`} + className="text-rgray-11" + > + <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", + )} + /> + </motion.div> + </CommandItem> + ))} + </CommandGroup> + </motion.div> + </CommandList> + </Command> + </PopoverContent> + </Popover> + </LayoutGroup> + </AnimatePresence> ); } diff --git a/apps/web/src/components/Sidebar/MemoriesBar.tsx b/apps/web/src/components/Sidebar/MemoriesBar.tsx index e2adcd94..e9f675a5 100644 --- a/apps/web/src/components/Sidebar/MemoriesBar.tsx +++ b/apps/web/src/components/Sidebar/MemoriesBar.tsx @@ -115,7 +115,7 @@ export function MemoriesBar() { export function Space({ title, description, content, id }: Space) { console.log(title, content.map((c) => c.image).reverse()); return ( - <button className="hover:bg-rgray-2 focus-visible:bg-rgray-2 focus-visible:ring-rgray-7 flex flex-col items-center justify-center rounded-md p-2 text-center ring-transparent transition focus-visible:outline-none focus-visible:ring-2"> + <button className="hover:bg-rgray-2 focus-visible:bg-rgray-2 focus-visible:ring-rgray-7 flex flex-col items-center justify-center rounded-md p-2 pb-4 text-center font-normal ring-transparent transition focus-visible:outline-none focus-visible:ring-2"> {content.length > 2 ? ( <MemoryWithImages3 className="h-24 w-24" diff --git a/apps/web/src/components/Sidebar/index.tsx b/apps/web/src/components/Sidebar/index.tsx index 1680000b..49ce446a 100644 --- a/apps/web/src/components/Sidebar/index.tsx +++ b/apps/web/src/components/Sidebar/index.tsx @@ -4,6 +4,7 @@ import { MemoryIcon } from "../../assets/Memories"; import { Trash2, User2 } from "lucide-react"; import React, { useState } from "react"; import { MemoriesBar } from "./MemoriesBar"; +import { AnimatePresence, motion } from "framer-motion"; export type MenuItem = { icon: React.ReactNode | React.ReactNode[]; @@ -47,30 +48,34 @@ export default function Sidebar({ return ( <> - <div className="bg-rgray-2 border-r-rgray-6 hidden h-screen max-h-screen w-max flex-col items-center border-r px-2 py-5 text-sm font-light md:flex"> - {menuItemsTop.map((item, index) => ( - <MenuItem - key={index} - item={item} - selectedItem={selectedItem} - setSelectedItem={setSelectedItem} - /> - ))} - <div className="mt-auto" /> - {menuItemsBottom.map((item, index) => ( - <MenuItem - key={index} - item={item} - selectedItem={selectedItem} - setSelectedItem={setSelectedItem} - /> - ))} + <div className="relative hidden h-screen max-h-screen w-max flex-col items-center text-sm font-light md:flex"> + <div className="bg-rgray-2 border-r-rgray-6 relative z-[10000] flex h-full w-full flex-col items-center justify-center border-r px-2 py-5 "> + {menuItemsTop.map((item, index) => ( + <MenuItem + key={index} + item={item} + selectedItem={selectedItem} + setSelectedItem={setSelectedItem} + /> + ))} + <div className="mt-auto" /> + {menuItemsBottom.map((item, index) => ( + <MenuItem + key={index} + item={item} + selectedItem={selectedItem} + setSelectedItem={setSelectedItem} + /> + ))} + </div> + <AnimatePresence> + {selectedItem && ( + <SubSidebar> + <Subbar /> + </SubSidebar> + )} + </AnimatePresence> </div> - {selectedItem && ( - <SubSidebar> - <Subbar /> - </SubSidebar> - )} </> ); } @@ -87,7 +92,7 @@ const MenuItem = ({ <button data-state-on={selectedItem === label} onClick={() => setSelectedItem((prev) => (prev === label ? null : label))} - 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" + className="on:opacity-100 on:bg-rgray-4 focus-visible:ring-rgray-7 relative z-[100] 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" > {icon} <span className="">{label}</span> @@ -96,8 +101,30 @@ const MenuItem = ({ export function SubSidebar({ children }: { children?: React.ReactNode }) { return ( - <div className="bg-rgray-3 border-r-rgray-6 hidden h-screen w-[50vw] flex-col items-center border-r font-light md:flex"> - {children} - </div> + <motion.div + initial={{ opacity: 0, x: "-100%" }} + animate={{ opacity: 1, x: 0 }} + exit={{ + opacity: 0, + x: "-100%", + transition: { delay: 0.2 }, + }} + transition={{ + duration: 0.2, + }} + className="bg-rgray-3 border-r-rgray-6 absolute left-[100%] top-0 z-[10] hidden h-screen w-[30vw] items-start justify-center overflow-x-hidden border-r font-light md:flex" + > + <motion.div + initial={{ opacity: 0 }} + animate={{ opacity: 1 }} + exit={{ opacity: 0, transition: { delay: 0 } }} + transition={{ + delay: 0.2, + }} + className="z-[10] flex h-full w-full min-w-full flex-col items-center opacity-0" + > + {children} + </motion.div> + </motion.div> ); } diff --git a/apps/web/src/components/ui/command.tsx b/apps/web/src/components/ui/command.tsx index 54070776..74b7f2e8 100644 --- a/apps/web/src/components/ui/command.tsx +++ b/apps/web/src/components/ui/command.tsx @@ -15,7 +15,7 @@ const Command = React.forwardRef< <CommandPrimitive ref={ref} className={cn( - "bg-rgray-3 text-rgray-11 flex h-full w-full flex-col overflow-hidden rounded-md", + "bg-rgray-3 text-rgray-11 flex h-full w-full flex-col overflow-hidden rounded-md focus-visible:outline-none [&>[cmdk-list-sizer]]:max-h-[250px] [&>[cmdk-list-sizer]]:overflow-y-scroll", className, )} {...props} @@ -120,7 +120,7 @@ const CommandItem = React.forwardRef< <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", + "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 opacity-70 outline-none data-[disabled='true']:pointer-events-none data-[disabled='true']:opacity-50", className, )} {...props} |