Add Dynamic Island

This commit is contained in:
codetorso 2024-06-08 06:25:25 +05:30
parent 0a282da5b1
commit 004c952dc3
8 changed files with 285 additions and 36 deletions

View file

@ -0,0 +1,194 @@
"use client";
import { AddIcon } from "@repo/ui/icons";
import Image from "next/image";
import { AnimatePresence, useMotionValueEvent, useScroll } from "framer-motion";
import { useState } from "react";
import { motion } from "framer-motion";
import { Label } from "@repo/ui/shadcn/label";
import { Input } from "@repo/ui/shadcn/input";
import { Textarea } from "@repo/ui/shadcn/textarea";
export function DynamicIsland() {
const { scrollYProgress } = useScroll();
const [visible, setVisible] = useState(true);
useMotionValueEvent(scrollYProgress, "change", (current) => {
// Check if current is not undefined and is a number
if (typeof current === "number") {
let direction = current! - scrollYProgress.getPrevious()!;
if (direction < 0 || direction === 1) {
setVisible(true);
} else {
setVisible(false);
}
}
});
return (
<div className="fixed z-40 left-1/2 -translate-x-1/2 top-12">
<AnimatePresence mode="wait">
<motion.div
initial={{
opacity: 1,
y: -150,
}}
animate={{
y: visible ? 0 : -150,
opacity: visible ? 1 : 0,
}}
transition={{
duration: 0.2,
}}
className="flex flex-col items-center"
>
<DynamicIslandContent />
</motion.div>
</AnimatePresence>
</div>
);
}
export default DynamicIsland;
function DynamicIslandContent() {
const [show, setshow] = useState(true);
function cancelfn(){
setshow(true);
}
return (
<>
{show ? (
<div
onClick={() => setshow(!show)}
className="bg-[#1F2428] p-2 rounded-3xl"
>
<Image src={AddIcon} alt="Add icon" />
</div>
) : (
<div>
<ToolBar cancelfn={cancelfn} />
</div>
)}
</>
);
}
const fakeitems = ["spaces", "page", "note"];
function ToolBar({cancelfn}: {cancelfn: ()=> void}) {
const [index, setIndex] = useState(0);
return (
<div className="flex flex-col items-center">
<div className="bg-[#1F2428] py-[.35rem] px-[.6rem] rounded-2xl">
<HoverEffect
items={fakeitems}
index={index}
indexFn={(i) => setIndex(i)}
/>
</div>
{ index === 0 ?
<SpaceForm cancelfn={cancelfn} /> :
index === 1 ?
<PageForm cancelfn={cancelfn} /> :
<NoteForm cancelfn={cancelfn} />
}
</div>
);
}
export const HoverEffect = ({
items,
index,
indexFn,
}: {
items: string[];
index: number;
indexFn: (i: number) => void;
}) => {
return (
<div className={"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3"}>
{items.map((item, idx) => (
<button
key={idx}
className="relative block h-full w-full px-2 py-1"
onClick={() => indexFn(idx)}
>
<AnimatePresence>
{index === idx && (
<motion.span
className="absolute inset-0 block h-full w-full rounded-xl bg-[#2B3237]"
layoutId="hoverBackground"
initial={{ opacity: 0 }}
animate={{
opacity: 1,
transition: { duration: 0.15 },
}}
exit={{
opacity: 0,
transition: { duration: 0.15, delay: 0.2 },
}}
/>
)}
</AnimatePresence>
<h3 className="text-[#858B92] z-50 relative">{item}</h3>
</button>
))}
</div>
);
};
function SpaceForm({cancelfn}: {cancelfn: ()=> void}) {
return (
<div className="bg-[#1F2428] px-4 py-3 rounded-2xl mt-2 flex flex-col gap-3">
<div >
<Label className="text-[#858B92]" htmlFor="name">Name</Label>
<Input className="bg-[#2B3237] outline-none border-none focus:border-none focus:outline-none" id="name"/>
</div>
<div className="flex justify-between">
<a className="text-blue-500" href="">
pull from store
</a>
<div onClick={cancelfn} className="bg-[#2B3237] px-2 py-1 rounded-xl cursor-pointer">cancel</div>
</div>
</div>
);
}
function PageForm({cancelfn}: {cancelfn: ()=> void}) {
return (
<div className="bg-[#1F2428] px-4 py-3 rounded-2xl mt-2 flex flex-col gap-3">
<div >
<Label className="text-[#858B92]" htmlFor="name">Space</Label>
<Input className="bg-[#2B3237] outline-none border-none focus:border-none focus:outline-none" id="name"/>
</div>
<div >
<Label className="text-[#858B92]" htmlFor="name">Page Url</Label>
<Input className="bg-[#2B3237] outline-none border-none focus:border-none focus:outline-none" id="name"/>
</div>
<div className="flex justify-end">
<div onClick={cancelfn} className="bg-[#2B3237] px-2 py-1 rounded-xl cursor-pointer">cancel</div>
</div>
</div>
);
}
function NoteForm({cancelfn}: {cancelfn: ()=> void}) {
return (
<div className="bg-[#1F2428] px-4 py-3 rounded-2xl mt-2 flex flex-col gap-3">
<div >
<Label className="text-[#858B92]" htmlFor="name">Space</Label>
<Input className="bg-[#2B3237] outline-none border-none focus:border-none focus:outline-none" id="name"/>
</div>
<div >
<Label className="text-[#858B92]" htmlFor="name">Note</Label>
<Textarea cols={4} className="bg-[#2B3237] outline-none border-none resize-none" id="name"/>
</div>
<div className="flex justify-end">
<div onClick={cancelfn} className="bg-[#2B3237] px-2 py-1 rounded-xl cursor-pointer">cancel</div>
</div>
</div>
);
}

View file

@ -2,49 +2,25 @@ import React from "react";
import Image from "next/image";
import Link from "next/link";
import Logo from "../../public/logo.svg";
import { AddIcon, ChatIcon } from "@repo/ui/icons";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@repo/ui/shadcn/tabs";
import { ChatIcon } from "@repo/ui/icons";
import DynamicIsland from "./dynamicisland";
function Header() {
return (
<div>
<div className="flex items-center justify-between relative z-10">
<Link href="/">
<div className="absolute left-0 w-full flex items-center justify-between z-10">
<Link className="px-5" href="/home">
<Image
src={Logo}
alt="SuperMemory logo"
className="hover:brightness-125 duration-200"
className="hover:brightness-75 brightness-50 duration-200"
/>
</Link>
<Tabs
className="absolute flex flex-col justify-center items-center w-full -z-10 group top-0 transition-transform duration-1000 ease-out"
defaultValue="account"
>
<div className="bg-secondary all-center h-11 rounded-full p-2 min-w-14">
<button className="p-2 group-hover:hidden transition duration-500 ease-in-out">
<Image src={AddIcon} alt="Add icon" />
</button>
<DynamicIsland />
<div className="hidden group-hover:flex inset-0 transition-opacity duration-500 ease-in-out">
<TabsList className="p-2">
<TabsTrigger value="account">Account</TabsTrigger>
<TabsTrigger value="password">Password</TabsTrigger>
</TabsList>
</div>
</div>
<div className="bg-secondary all-center rounded-full p-2 mt-4 min-w-14 hidden group-hover:block">
<TabsContent value="account">
Make changes to your account here.
</TabsContent>
<TabsContent value="password">
Change your password here.
</TabsContent>
</div>
</Tabs>
<button className="flex shrink-0 duration-200 items-center gap-2 px-2 py-1.5 rounded-xl hover:bg-secondary">
<button className="flex shrink-0 duration-200 items-center gap-2 px-5 py-1.5 rounded-xl hover:bg-secondary">
<Image src={ChatIcon} alt="Chat icon" />
Start new chat
</button>

View file

@ -16,7 +16,7 @@ async function Page({
const spaces = await getSpaces();
return (
<div className="max-w-3xl flex mx-auto w-full flex-col">
<div className="max-w-3xl h-full justify-center flex mx-auto w-full flex-col">
{/* all content goes here */}
{/* <div className="">hi {firstTime ? 'first time' : ''}</div> */}

View file

@ -48,7 +48,7 @@ function QueryInput({
return (
<div>
<div className="bg-secondary rounded-t-[24px] w-full mt-40">
<div className="bg-secondary rounded-t-[24px]">
{/* input and action button */}
<form action={async () => push(parseQ())} className="flex gap-4 p-3">
<textarea

View file

@ -24,7 +24,7 @@ function Menu() {
return (
<div className="absolute h-full p-4 flex items-center top-0 left-0">
<div className="">
<div className="hover:rounded-2x group inline-flex w-14 text-foreground-menu text-[15px] font-medium flex-col items-start gap-6 overflow-hidden rounded-[28px] bg-secondary px-3 py-4 duration-200 hover:w-40">
<div className="group inline-flex w-14 text-foreground-menu text-[15px] font-medium flex-col items-start gap-6 overflow-hidden rounded-[28px] bg-secondary px-3 py-4 duration-200 hover:w-40">
{menuItems.map((item) => (
<div
key={item.url}

View file

@ -3,7 +3,7 @@ import ChatIcon from "./chat.svg";
import HistoryIcon from "./history.svg";
import ExploreIcon from "./explore.svg";
import MemoriesIcon from "./memories.svg";
import ArrowRightIcon from "./arrowRight.svg";
import ArrowRightIcon from "./arrowright.svg";
import SelectIcon from "./select.svg";
export {

View file

@ -0,0 +1,55 @@
"use client"
import * as React from "react"
import * as TabsPrimitive from "@radix-ui/react-tabs"
import { cn } from "@repo/ui/lib/utils";
const Tabs = TabsPrimitive.Root
const TabsList = React.forwardRef<
React.ElementRef<typeof TabsPrimitive.List>,
React.ComponentPropsWithoutRef<typeof TabsPrimitive.List>
>(({ className, ...props }, ref) => (
<TabsPrimitive.List
ref={ref}
className={cn(
"inline-flex h-10 items-center justify-center rounded-md bg-muted p-1 text-muted-foreground",
className
)}
{...props}
/>
))
TabsList.displayName = TabsPrimitive.List.displayName
const TabsTrigger = React.forwardRef<
React.ElementRef<typeof TabsPrimitive.Trigger>,
React.ComponentPropsWithoutRef<typeof TabsPrimitive.Trigger>
>(({ className, ...props }, ref) => (
<TabsPrimitive.Trigger
ref={ref}
className={cn(
"inline-flex items-center justify-center whitespace-nowrap rounded-xl px-3 py-1.5 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-[#2B3237] data-[state=active]:text-[#858B92] data-[state=active]:shadow-sm",
className
)}
{...props}
/>
))
TabsTrigger.displayName = TabsPrimitive.Trigger.displayName
const TabsContent = React.forwardRef<
React.ElementRef<typeof TabsPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof TabsPrimitive.Content>
>(({ className, ...props }, ref) => (
<TabsPrimitive.Content
ref={ref}
className={cn(
"mt-2 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
className
)}
{...props}
/>
))
TabsContent.displayName = TabsPrimitive.Content.displayName
export { Tabs, TabsList, TabsTrigger, TabsContent }

View file

@ -0,0 +1,24 @@
import * as React from "react"
import { cn } from "@repo/ui/lib/utils";
export interface TextareaProps
extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {}
const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
({ className, ...props }, ref) => {
return (
<textarea
className={cn(
"flex min-h-[80px] w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
className
)}
ref={ref}
{...props}
/>
)
}
)
Textarea.displayName = "Textarea"
export { Textarea }