From c22751ee68d9fdd4e5bc5147922de32a20abf484 Mon Sep 17 00:00:00 2001 From: Yash Date: Thu, 4 Apr 2024 08:34:48 +0000 Subject: add custom Bin component --- apps/web/src/components/Sidebar/MemoriesBar.tsx | 375 +++++++++++++++++------- apps/web/src/components/Sidebar/index.tsx | 61 ++-- 2 files changed, 309 insertions(+), 127 deletions(-) (limited to 'apps/web/src/components/Sidebar') 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 (
@@ -109,28 +136,129 @@ export function MemoriesBar() { className="bg-rgray-4 mt-2 w-full" />
-
- {spaces.map((space) => ( - +
+ {currentSpaces.map((space) => ( + + setCurrentSpaces((prev) => prev.filter((s) => s.id !== space.id)) + } + key={space.id} + {...space} + /> ))}
); } -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 ( -
+ - + { + 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 ? ( c.image).reverse() as string[]} /> )} -
+ + ); +} + +export function SpaceMoreButton({ onDelete }: { onDelete?: () => void }) { + const [isOpen, setIsOpen] = useState(false); + + return ( + <> + + + + + + + + Open + + {}}> + + Edit + + + + Move to Trash + + + + ); } 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(null); - React.useEffect(() => { - onSelectChange?.(selectedItem); - }, [selectedItem]); - const Subbar = menuItems.find((i) => i.label === selectedItem)?.content ?? (() => <>); + useEffect(() => { + onSelectChange?.(selectedItem); + }, [selectedItem]); + return ( <>
-
- {menuItemsTop.map((item, index) => ( - - ))} +
+ , + content: MemoriesBar, + }} + selectedItem={selectedItem} + setSelectedItem={setSelectedItem} + /> +
- {menuItemsBottom.map((item, index) => ( - - ))} + + , + }} + selectedItem={selectedItem} + id='trash-button' + setSelectedItem={setSelectedItem} + /> + , + }} + selectedItem={selectedItem} + setSelectedItem={setSelectedItem} + />
{selectedItem && ( @@ -84,7 +97,8 @@ const MenuItem = ({ item: { icon, label }, selectedItem, setSelectedItem, -}: { + ...props +}: React.HTMLAttributes & { item: MenuItem; selectedItem: string | null; setSelectedItem: React.Dispatch>; @@ -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} {label} -- cgit v1.2.3