aboutsummaryrefslogtreecommitdiff
path: root/apps/web/src
diff options
context:
space:
mode:
authorYash <[email protected]>2024-04-03 10:48:29 +0000
committerYash <[email protected]>2024-04-03 10:48:29 +0000
commitbed6d65ffa6a3886d77d497463748983c822100d (patch)
treec88c0292adec220659cda32a45cc8cfcd02213ac /apps/web/src
parentremove debug lie (diff)
downloadsupermemory-bed6d65ffa6a3886d77d497463748983c822100d.tar.xz
supermemory-bed6d65ffa6a3886d77d497463748983c822100d.zip
animation with framer motion
Diffstat (limited to 'apps/web/src')
-rw-r--r--apps/web/src/components/Main.tsx10
-rw-r--r--apps/web/src/components/Sidebar/FilterCombobox.tsx266
-rw-r--r--apps/web/src/components/Sidebar/MemoriesBar.tsx2
-rw-r--r--apps/web/src/components/Sidebar/index.tsx81
-rw-r--r--apps/web/src/components/ui/command.tsx4
-rw-r--r--apps/web/src/hooks/useViewport.ts5
6 files changed, 271 insertions, 97 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}
diff --git a/apps/web/src/hooks/useViewport.ts b/apps/web/src/hooks/useViewport.ts
index ca4ec741..f24ed2d2 100644
--- a/apps/web/src/hooks/useViewport.ts
+++ b/apps/web/src/hooks/useViewport.ts
@@ -1,7 +1,10 @@
import { useState, useEffect } from "react";
function getViewport() {
- const { innerWidth: width, innerHeight: height } = window;
+ const { innerWidth: width, innerHeight: height } = window ?? {
+ innerWidth: 0,
+ innerHeight: 0,
+ };
return {
width,
height,