aboutsummaryrefslogtreecommitdiff
path: root/apps
diff options
context:
space:
mode:
authorYash <[email protected]>2024-04-04 08:34:48 +0000
committerYash <[email protected]>2024-04-04 08:34:48 +0000
commitc22751ee68d9fdd4e5bc5147922de32a20abf484 (patch)
treeff5f4051cb8cd973a18a5431a0300ed8edbe63fb /apps
parentfix content paths (diff)
downloadsupermemory-c22751ee68d9fdd4e5bc5147922de32a20abf484.tar.xz
supermemory-c22751ee68d9fdd4e5bc5147922de32a20abf484.zip
add custom Bin component
Diffstat (limited to 'apps')
-rw-r--r--apps/web/src/assets/Bin.tsx104
-rw-r--r--apps/web/src/assets/Memories.tsx2
-rw-r--r--apps/web/src/components/Main.tsx2
-rw-r--r--apps/web/src/components/Sidebar/MemoriesBar.tsx375
-rw-r--r--apps/web/src/components/Sidebar/index.tsx61
-rw-r--r--apps/web/src/components/ui/dropdown-menu.tsx2
-rw-r--r--apps/web/src/components/ui/popover.tsx39
7 files changed, 440 insertions, 145 deletions
diff --git a/apps/web/src/assets/Bin.tsx b/apps/web/src/assets/Bin.tsx
new file mode 100644
index 00000000..d0793cef
--- /dev/null
+++ b/apps/web/src/assets/Bin.tsx
@@ -0,0 +1,104 @@
+import { cn } from "@/lib/utils";
+import { useEffect, useRef } from "react";
+
+export const Bin: React.FC<React.HTMLAttributes<HTMLDivElement> & {}> = ({
+ className,
+ ...props
+}) => {
+ const icon = useRef<HTMLDivElement>(null);
+
+ useEffect(() => {
+ let timeout: ReturnType<typeof setTimeout> | undefined;
+
+ const observer = new MutationObserver(function (mutations) {
+ mutations.forEach(function (mutation) {
+ if (
+ mutation.type === "attributes" &&
+ mutation.attributeName === "data-open" &&
+ (mutation.oldValue === "false" || mutation.oldValue === null) &&
+ icon.current?.dataset["open"] === "true"
+ ) {
+ if (timeout) clearTimeout(timeout);
+ timeout = setTimeout(() => {
+ icon.current!.dataset["open"] = "false";
+ }, 2000);
+ }
+ });
+ });
+
+ observer.observe(icon.current!, {
+ attributes: true, //configure it to listen to attribute changes
+ });
+
+ return () => {
+ observer.disconnect();
+ };
+ }, []);
+
+ return (
+ <div
+ ref={icon}
+ data-open="false"
+ className={cn(
+ "relative z-[100] flex w-full origin-bottom flex-col items-center justify-center transition-transform delay-500 duration-500 data-[open='true']:-translate-y-2 data-[open='true']:scale-150 data-[open='true']:delay-0 [&[data-open='true']>[data-lid]]:rotate-[150deg] [&[data-open='true']>[data-lid]]:delay-0",
+ className,
+ )}
+ {...props}
+ >
+ <svg
+ data-lid
+ className="w-full origin-[90%_80%] transition-transform delay-500 duration-500 ease-in-out"
+ viewBox="0 0 24 7"
+ fill="none"
+ strokeWidth={1}
+ xmlns="http://www.w3.org/2000/svg"
+ >
+ <path
+ d="M3 6H21"
+ stroke="currentColor"
+ strokeWidth={1}
+ strokeLinecap="round"
+ strokeLinejoin="round"
+ />
+ <path
+ d="M8 6V4C8 3 9 2 10 2H14C15 2 16 3 16 4V6"
+ stroke="currentColor"
+ strokeWidth={1}
+ strokeLinecap="round"
+ strokeLinejoin="round"
+ />
+ </svg>
+
+ <svg
+ className="-mt-[1px] w-full"
+ viewBox="0 0 24 19"
+ fill="none"
+ strokeWidth={1}
+ data-trash-bin
+ xmlns="http://www.w3.org/2000/svg"
+ >
+ <path
+ d="M19 1V15C19 16 18 17 17 17H7C6 17 5 16 5 15V1"
+ stroke="currentColor"
+ strokeWidth={1}
+ strokeLinecap="round"
+ strokeLinejoin="round"
+ />
+ <path
+ d="M10 6V12"
+ stroke="currentColor"
+ strokeWidth={1}
+ strokeLinecap="round"
+ strokeLinejoin="round"
+ />
+ <path
+ d="M14 6V12"
+ stroke="currentColor"
+ strokeWidth={1}
+ strokeLinecap="round"
+ strokeLinejoin="round"
+ />
+ </svg>
+ </div>
+ );
+};
diff --git a/apps/web/src/assets/Memories.tsx b/apps/web/src/assets/Memories.tsx
index f8fd83b8..3b1c177f 100644
--- a/apps/web/src/assets/Memories.tsx
+++ b/apps/web/src/assets/Memories.tsx
@@ -1,5 +1,3 @@
-import { svgId } from "@/lib/utils";
-
export const MemoryIcon: React.FC<React.SVGAttributes<SVGElement>> = (
props,
) => (
diff --git a/apps/web/src/components/Main.tsx b/apps/web/src/components/Main.tsx
index ef505db5..8796c52e 100644
--- a/apps/web/src/components/Main.tsx
+++ b/apps/web/src/components/Main.tsx
@@ -13,6 +13,8 @@ export default function Main({ sidebarOpen }: { sidebarOpen: boolean }) {
const textArea = useRef<HTMLTextAreaElement>(null);
+ console.log("main px", sidebarOpen);
+
useEffect(() => {
function onResize() {
if (!textArea.current || !window.visualViewport) return;
diff --git a/apps/web/src/components/Sidebar/MemoriesBar.tsx b/apps/web/src/components/Sidebar/MemoriesBar.tsx
index 2890af56..367f0173 100644
--- a/apps/web/src/components/Sidebar/MemoriesBar.tsx
+++ b/apps/web/src/components/Sidebar/MemoriesBar.tsx
@@ -1,3 +1,4 @@
+import { useAutoAnimate } from "@formkit/auto-animate/react";
import {
MemoryWithImage,
MemoryWithImages3,
@@ -5,99 +6,125 @@ import {
} from "@/assets/MemoryWithImages";
import { type Space } from "../../../types/memory";
import { InputWithIcon } from "../ui/input";
-import { MoreHorizontal, Search } from "lucide-react";
+import {
+ ArrowUpRight,
+ Edit3,
+ MoreHorizontal,
+ Search,
+ Trash2,
+} from "lucide-react";
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuItem,
+ DropdownMenuTrigger,
+} from "../ui/dropdown-menu";
+import {
+ animate,
+ AnimatePresence,
+ LayoutGroup,
+ motion,
+ useAnimate,
+ Variant,
+} from "framer-motion";
+import { useRef, useState } from "react";
+
+const spaces: Space[] = [
+ {
+ id: 1,
+ title: "Cool Tech",
+ description: "Really cool mind blowing tech",
+ content: [
+ {
+ id: 1,
+ title: "Perplexity",
+ description: "A good ui",
+ content: "",
+ image: "https://perplexity.ai/favicon.ico",
+ url: "https://perplexity.ai",
+ savedAt: new Date(),
+ baseUrl: "https://perplexity.ai",
+ space: "Cool tech",
+ },
+ {
+ id: 2,
+ title: "Pi.ai",
+ description: "A good ui",
+ content: "",
+ image: "https://pi.ai/pi-logo-192.png?v=2",
+ url: "https://pi.ai",
+ savedAt: new Date(),
+ baseUrl: "https://pi.ai",
+ space: "Cool tech",
+ },
+ {
+ id: 3,
+ title: "Visual Studio Code",
+ description: "A good ui",
+ content: "",
+ image: "https://code.visualstudio.com/favicon.ico",
+ url: "https://code.visualstudio.com",
+ savedAt: new Date(),
+ baseUrl: "https://code.visualstudio.com",
+ space: "Cool tech",
+ },
+ ],
+ },
+ {
+ id: 2,
+ title: "Cool Courses",
+ description: "Amazng",
+ content: [
+ {
+ id: 1,
+ title: "Animation on the web",
+ description: "A good ui",
+ content: "",
+ image: "https://animations.dev/favicon.ico",
+ url: "https://animations.dev",
+ savedAt: new Date(),
+ baseUrl: "https://animations.dev",
+ space: "Cool courses",
+ },
+ {
+ id: 2,
+ title: "Tailwind Course",
+ description: "A good ui",
+ content: "",
+ image:
+ "https://tailwindcss.com/_next/static/media/tailwindcss-mark.3c5441fc7a190fb1800d4a5c7f07ba4b1345a9c8.svg",
+ url: "https://tailwindcss.com",
+ savedAt: new Date(),
+ baseUrl: "https://tailwindcss.com",
+ space: "Cool courses",
+ },
+ ],
+ },
+ {
+ id: 3,
+ title: "Cool Libraries",
+ description: "Really cool mind blowing tech",
+ content: [
+ {
+ id: 1,
+ title: "Perplexity",
+ description: "A good ui",
+ content: "",
+ image: "https://yashverma.me/logo.jpg",
+ url: "https://perplexity.ai",
+ savedAt: new Date(),
+ baseUrl: "https://perplexity.ai",
+ space: "Cool libraries",
+ },
+ ],
+ },
+];
export function MemoriesBar() {
- const spaces: Space[] = [
- {
- id: 1,
- title: "Cool Tech",
- description: "Really cool mind blowing tech",
- content: [
- {
- id: 1,
- title: "Perplexity",
- description: "A good ui",
- content: "",
- image: "https://perplexity.ai/favicon.ico",
- url: "https://perplexity.ai",
- savedAt: new Date(),
- baseUrl: "https://perplexity.ai",
- space: "Cool tech",
- },
- {
- id: 2,
- title: "Pi.ai",
- description: "A good ui",
- content: "",
- image: "https://pi.ai/pi-logo-192.png?v=2",
- url: "https://pi.ai",
- savedAt: new Date(),
- baseUrl: "https://pi.ai",
- space: "Cool tech",
- },
- {
- id: 3,
- title: "Visual Studio Code",
- description: "A good ui",
- content: "",
- image: "https://code.visualstudio.com/favicon.ico",
- url: "https://code.visualstudio.com",
- savedAt: new Date(),
- baseUrl: "https://code.visualstudio.com",
- space: "Cool tech",
- },
- ],
- },
- {
- id: 2,
- title: "Cool Courses",
- description: "Amazng",
- content: [
- {
- id: 1,
- title: "Animation on the web",
- description: "A good ui",
- content: "",
- image: "https://animations.dev/favicon.ico",
- url: "https://animations.dev",
- savedAt: new Date(),
- baseUrl: "https://animations.dev",
- space: "Cool courses",
- },
- {
- id: 2,
- title: "Tailwind Course",
- description: "A good ui",
- content: "",
- image:
- "https://tailwindcss.com/_next/static/media/tailwindcss-mark.3c5441fc7a190fb1800d4a5c7f07ba4b1345a9c8.svg",
- url: "https://tailwindcss.com",
- savedAt: new Date(),
- baseUrl: "https://tailwindcss.com",
- space: "Cool courses",
- },
- ],
- },
- {
- id: 3,
- title: "Cool Libraries",
- description: "Really cool mind blowing tech",
- content: [
- {
- id: 1,
- title: "Perplexity",
- description: "A good ui",
- content: "",
- image: "https://yashverma.me/logo.jpg",
- url: "https://perplexity.ai",
- savedAt: new Date(),
- baseUrl: "https://perplexity.ai",
- space: "Cool libraries",
- },
- ],
- },
- ];
+ const [parent, enableAnimations] = useAutoAnimate();
+ const [currentSpaces, setCurrentSpaces] = useState(spaces);
+
+ console.log("currentSpaces: ", currentSpaces);
return (
<div className="text-rgray-11 flex w-full flex-col items-start py-8 text-left">
@@ -109,28 +136,129 @@ export function MemoriesBar() {
className="bg-rgray-4 mt-2 w-full"
/>
</div>
- <div className="grid w-full grid-flow-row grid-cols-3 gap-1 px-2 py-5">
- {spaces.map((space) => (
- <Space key={space.id} {...space} />
+ <div
+ ref={parent}
+ className="grid w-full grid-flow-row grid-cols-3 gap-1 px-2 py-5"
+ >
+ {currentSpaces.map((space) => (
+ <SpaceItem
+ onDelete={() =>
+ setCurrentSpaces((prev) => prev.filter((s) => s.id !== space.id))
+ }
+ key={space.id}
+ {...space}
+ />
))}
</div>
</div>
);
}
-export function Space({ title, description, content, id }: Space) {
- console.log(title, content.map((c) => c.image).reverse());
+const SpaceExitVariant: Variant = {
+ opacity: 0,
+ scale: 0,
+ borderRadius: "50%",
+ background: "var(--gray-1)",
+ transition: {
+ duration: 0.2,
+ },
+};
+
+export function SpaceItem({
+ title,
+ description,
+ content,
+ id,
+ onDelete,
+}: Space & { onDelete: () => void }) {
+ const [itemRef, animateItem] = useAnimate();
+
return (
- <div className="hover:bg-rgray-2 has-[[data-space-text]:focus-visible]:bg-rgray-2 has-[[data-space-text]:focus-visible]:ring-rgray-7 [&:has-[[data-space-text]:focus-visible]>[data-more-button]]:opacity-100 relative flex flex-col-reverse items-center justify-center rounded-md p-2 pb-4 text-center font-normal ring-transparent transition has-[[data-space-text]:focus-visible]:outline-none has-[[data-space-text]:focus-visible]:ring-2 [&:hover>[data-more-button]]:opacity-100">
+ <motion.div
+ ref={itemRef}
+ className="hover:bg-rgray-2 has-[[data-space-text]:focus-visible]:bg-rgray-2 has-[[data-space-text]:focus-visible]:ring-rgray-7 [&:has-[[data-space-text]:focus-visible]>[data-more-button]]:opacity-100 relative flex flex-col-reverse items-center justify-center rounded-md p-2 pb-4 text-center font-normal ring-transparent transition has-[[data-space-text]:focus-visible]:outline-none has-[[data-space-text]:focus-visible]:ring-2 [&:hover>[data-more-button]]:opacity-100"
+ >
<button data-space-text className="focus-visible:outline-none">
{title}
</button>
- <button
- data-more-button
- className="hover:bg-rgray-3 focus-visible:bg-rgray-3 focus-visible:ring-rgray-7 absolute right-2 top-2 rounded-md p-1 opacity-0 ring-transparent transition focus-visible:opacity-100 focus-visible:outline-none focus-visible:ring-2"
- >
- <MoreHorizontal className="text-rgray-11 h-5 w-5" />
- </button>
+ <SpaceMoreButton
+ onDelete={() => {
+ if (!itemRef.current) return;
+ const trash = document.querySelector("#trash")! as HTMLDivElement;
+ const trashBin = document.querySelector("#trash-button")!;
+ const trashRect = trashBin.getBoundingClientRect();
+ const scopeRect = itemRef.current.getBoundingClientRect();
+ const el = document.createElement("div");
+ el.style.position = "fixed";
+ el.style.top = "0";
+ el.style.left = "0";
+ el.style.width = "15px";
+ el.style.height = "15px";
+ el.style.backgroundColor = "var(--gray-7)";
+ el.style.zIndex = "60";
+ el.style.borderRadius = "50%";
+ el.style.transform = "scale(5)";
+ el.style.opacity = "0";
+ trash.dataset["open"] = "true";
+ const initial = {
+ x: scopeRect.left + scopeRect.width / 2,
+ y: scopeRect.top + scopeRect.height / 2,
+ };
+ const delta = {
+ x:
+ trashRect.left +
+ trashRect.width / 2 -
+ scopeRect.left +
+ scopeRect.width / 2,
+ y:
+ trashRect.top +
+ trashRect.height / 4 -
+ scopeRect.top +
+ scopeRect.height / 2,
+ };
+ const end = {
+ x: trashRect.left + trashRect.width / 2,
+ y: trashRect.top + trashRect.height / 4,
+ };
+ el.style.offsetPath = `path('M ${initial.x} ${initial.y} Q ${delta.x * 0.01} ${delta.y * 0.01} ${end.x} ${end.y}`;
+ animateItem(itemRef.current, SpaceExitVariant, {
+ duration: 0.2,
+ }).then(() => {
+ itemRef.current.style.scale = "0";
+ onDelete();
+ });
+ document.body.appendChild(el);
+ el.animate(
+ {
+ transform: ["scale(5)", "scale(1)"],
+ opacity: [0, 0.3, 1],
+ },
+ {
+ duration: 200,
+ easing: "cubic-bezier(0.64, 0.57, 0.67, 1.53)",
+ fill: "forwards",
+ },
+ );
+ el.animate(
+ {
+ offsetDistance: ["0%", "100%"],
+ },
+ {
+ duration: 2000,
+ easing: "cubic-bezier(0.64, 0.57, 0.67, 1.53)",
+ fill: "forwards",
+ delay: 200,
+ },
+ ).onfinish = () => {
+ el.animate(
+ { transform: "scale(0)", opacity: 0 },
+ { duration: 200, fill: "forwards" },
+ ).onfinish = () => {
+ el.remove();
+ };
+ };
+ }}
+ />
{content.length > 2 ? (
<MemoryWithImages3
className="h-24 w-24"
@@ -150,6 +278,45 @@ export function Space({ title, description, content, id }: Space) {
images={content.map((c) => c.image).reverse() as string[]}
/>
)}
- </div>
+ </motion.div>
+ );
+}
+
+export function SpaceMoreButton({ onDelete }: { onDelete?: () => void }) {
+ const [isOpen, setIsOpen] = useState(false);
+
+ return (
+ <>
+ <DropdownMenu open={isOpen} onOpenChange={setIsOpen}>
+ <DropdownMenuTrigger asChild>
+ <button
+ data-more-button
+ className="hover:bg-rgray-3 focus-visible:bg-rgray-3 focus-visible:ring-rgray-7 absolute right-2 top-2 rounded-md p-1 opacity-0 ring-transparent transition focus-visible:opacity-100 focus-visible:outline-none focus-visible:ring-2"
+ >
+ <MoreHorizontal className="text-rgray-11 h-5 w-5" />
+ </button>
+ </DropdownMenuTrigger>
+ <DropdownMenuContent>
+ <DropdownMenuItem>
+ <ArrowUpRight
+ className="mr-2 h-4 w-4 scale-125"
+ strokeWidth={1.5}
+ />
+ Open
+ </DropdownMenuItem>
+ <DropdownMenuItem onClick={() => {}}>
+ <Edit3 className="mr-2 h-4 w-4" strokeWidth={1.5} />
+ Edit
+ </DropdownMenuItem>
+ <DropdownMenuItem
+ onClick={onDelete}
+ className="focus:bg-red-100 focus:text-red-400 dark:focus:bg-red-100/10"
+ >
+ <Trash2 className="mr-2 h-4 w-4" strokeWidth={1.5} />
+ Move to Trash
+ </DropdownMenuItem>
+ </DropdownMenuContent>
+ </DropdownMenu>
+ </>
);
}
diff --git a/apps/web/src/components/Sidebar/index.tsx b/apps/web/src/components/Sidebar/index.tsx
index 49ce446a..fe5fcb0a 100644
--- a/apps/web/src/components/Sidebar/index.tsx
+++ b/apps/web/src/components/Sidebar/index.tsx
@@ -2,9 +2,10 @@
import { StoredContent } from "@/server/db/schema";
import { MemoryIcon } from "../../assets/Memories";
import { Trash2, User2 } from "lucide-react";
-import React, { useState } from "react";
+import React, { useEffect, useState } from "react";
import { MemoriesBar } from "./MemoriesBar";
import { AnimatePresence, motion } from "framer-motion";
+import { Bin } from "@/assets/Bin";
export type MenuItem = {
icon: React.ReactNode | React.ReactNode[];
@@ -39,34 +40,46 @@ export default function Sidebar({
const menuItems = [...menuItemsTop, ...menuItemsBottom];
const [selectedItem, setSelectedItem] = useState<string | null>(null);
- React.useEffect(() => {
- onSelectChange?.(selectedItem);
- }, [selectedItem]);
-
const Subbar =
menuItems.find((i) => i.label === selectedItem)?.content ?? (() => <></>);
+ useEffect(() => {
+ onSelectChange?.(selectedItem);
+ }, [selectedItem]);
+
return (
<>
<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="bg-rgray-2 border-r-rgray-6 relative z-[50] flex h-full w-full flex-col items-center justify-center border-r px-2 py-5 ">
+ <MenuItem
+ item={{
+ label: "Memories",
+ icon: <MemoryIcon className="h-10 w-10" />,
+ content: MemoriesBar,
+ }}
+ selectedItem={selectedItem}
+ setSelectedItem={setSelectedItem}
+ />
+
<div className="mt-auto" />
- {menuItemsBottom.map((item, index) => (
- <MenuItem
- key={index}
- item={item}
- selectedItem={selectedItem}
- setSelectedItem={setSelectedItem}
- />
- ))}
+
+ <MenuItem
+ item={{
+ label: "Trash",
+ icon: <Bin id="trash" className="z-[300] h-7 w-7" />,
+ }}
+ selectedItem={selectedItem}
+ id='trash-button'
+ setSelectedItem={setSelectedItem}
+ />
+ <MenuItem
+ item={{
+ label: "Profile",
+ icon: <User2 strokeWidth={1.3} className="h-7 w-7" />,
+ }}
+ selectedItem={selectedItem}
+ setSelectedItem={setSelectedItem}
+ />
</div>
<AnimatePresence>
{selectedItem && (
@@ -84,7 +97,8 @@ const MenuItem = ({
item: { icon, label },
selectedItem,
setSelectedItem,
-}: {
+ ...props
+}: React.HTMLAttributes<HTMLButtonElement> & {
item: MenuItem;
selectedItem: string | null;
setSelectedItem: React.Dispatch<React.SetStateAction<string | null>>;
@@ -93,6 +107,7 @@ const MenuItem = ({
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 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"
+ {...props}
>
{icon}
<span className="">{label}</span>
diff --git a/apps/web/src/components/ui/dropdown-menu.tsx b/apps/web/src/components/ui/dropdown-menu.tsx
index 375662bb..cbc5cb1e 100644
--- a/apps/web/src/components/ui/dropdown-menu.tsx
+++ b/apps/web/src/components/ui/dropdown-menu.tsx
@@ -65,7 +65,7 @@ const DropdownMenuContent = React.forwardRef<
ref={ref}
sideOffset={sideOffset}
className={cn(
- "data-[state=open]:animate-in bg-rgray-3 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-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 border-rgray-6 text-rgray-11 z-50 min-w-[8rem] overflow-hidden rounded-md border p-1 shadow-md",
+ "data-[state=open]:animate-in bg-rgray-3 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-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 border-rgray-6 text-rgray-11 z-50 min-w-[9rem] overflow-hidden rounded-md border p-1 shadow-md",
className,
)}
{...props}
diff --git a/apps/web/src/components/ui/popover.tsx b/apps/web/src/components/ui/popover.tsx
index 0c4563a8..cabe76a9 100644
--- a/apps/web/src/components/ui/popover.tsx
+++ b/apps/web/src/components/ui/popover.tsx
@@ -11,21 +11,30 @@ const PopoverTrigger = PopoverPrimitive.Trigger;
const PopoverContent = React.forwardRef<
React.ElementRef<typeof PopoverPrimitive.Content>,
- React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Content>
->(({ className, align = "center", sideOffset = 4, ...props }, ref) => (
- <PopoverPrimitive.Portal>
- <PopoverPrimitive.Content
- ref={ref}
- align={align}
- sideOffset={sideOffset}
- className={cn(
- "border-rgray-6 bg-rgray-3 text-rgray-11 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-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-72 rounded-md border p-4 shadow-md outline-none",
- className,
- )}
- {...props}
- />
- </PopoverPrimitive.Portal>
-));
+ React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Content> & {
+ animate?: boolean;
+ }
+>(
+ (
+ { className, align = "center", animate = true, sideOffset = 4, ...props },
+ ref,
+ ) => (
+ <PopoverPrimitive.Portal>
+ <PopoverPrimitive.Content
+ ref={ref}
+ align={align}
+ sideOffset={sideOffset}
+ className={cn(
+ "border-rgray-6 bg-rgray-3 text-rgray-11 z-50 w-72 rounded-md border p-4 shadow-md outline-none",
+ animate &&
+ "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-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
+ className,
+ )}
+ {...props}
+ />
+ </PopoverPrimitive.Portal>
+ ),
+);
PopoverContent.displayName = PopoverPrimitive.Content.displayName;
export { Popover, PopoverTrigger, PopoverContent };