feat(app): enhance attach transition

This commit is contained in:
ItzCrazyKns
2025-12-23 18:53:40 +05:30
parent b47f522bf2
commit 24c32ed881
2 changed files with 173 additions and 160 deletions

View File

@@ -16,6 +16,8 @@ import {
} from 'lucide-react'; } from 'lucide-react';
import { Fragment, useRef, useState } from 'react'; import { Fragment, useRef, useState } from 'react';
import { useChat } from '@/lib/hooks/useChat'; import { useChat } from '@/lib/hooks/useChat';
import { AnimatePresence } from 'motion/react';
import { motion } from 'framer-motion';
const Attach = () => { const Attach = () => {
const { files, setFiles, setFileIds, fileIds } = useChat(); const { files, setFiles, setFileIds, fileIds } = useChat();
@@ -53,86 +55,95 @@ const Attach = () => {
return loading ? ( return loading ? (
<div className="active:border-none hover:bg-light-200 hover:dark:bg-dark-200 p-2 rounded-lg focus:outline-none text-black/50 dark:text-white/50 transition duration-200"> <div className="active:border-none hover:bg-light-200 hover:dark:bg-dark-200 p-2 rounded-lg focus:outline-none text-black/50 dark:text-white/50 transition duration-200">
<LoaderCircle size={16} className="text-sky-400 animate-spin" /> <LoaderCircle size={16} className="text-sky-500 animate-spin" />
</div> </div>
) : files.length > 0 ? ( ) : files.length > 0 ? (
<Popover className="relative w-full max-w-[15rem] md:max-w-md lg:max-w-lg"> <Popover className="relative w-full max-w-[15rem] md:max-w-md lg:max-w-lg">
<PopoverButton {({ open }) => (
type="button" <>
className="active:border-none hover:bg-light-200 hover:dark:bg-dark-200 p-2 rounded-lg focus:outline-none headless-open:text-black dark:headless-open:text-white text-black/50 dark:text-white/50 active:scale-95 transition duration-200 hover:text-black dark:hover:text-white" <PopoverButton
> type="button"
<File size={16} className="text-sky-400" /> className="active:border-none hover:bg-light-200 hover:dark:bg-dark-200 p-2 rounded-lg focus:outline-none headless-open:text-black dark:headless-open:text-white text-black/50 dark:text-white/50 active:scale-95 transition duration-200 hover:text-black dark:hover:text-white"
</PopoverButton> >
<Transition <File size={16} className="text-sky-500" />
as={Fragment} </PopoverButton>
enter="transition ease-out duration-150" <AnimatePresence>
enterFrom="opacity-0 translate-y-1" {open && (
enterTo="opacity-100 translate-y-0" <PopoverPanel
leave="transition ease-in duration-150" className="absolute z-10 w-64 md:w-[350px] right-0"
leaveFrom="opacity-100 translate-y-0" static
leaveTo="opacity-0 translate-y-1" >
> <motion.div
<PopoverPanel className="absolute z-10 w-64 md:w-[350px] right-0"> initial={{ opacity: 0, scale: 0.9 }}
<div className="bg-light-primary dark:bg-dark-primary border rounded-md border-light-200 dark:border-dark-200 w-full max-h-[200px] md:max-h-none overflow-y-auto flex flex-col"> animate={{ opacity: 1, scale: 1 }}
<div className="flex flex-row items-center justify-between px-3 py-2"> exit={{ opacity: 0, scale: 0.9 }}
<h4 className="text-black dark:text-white font-medium text-sm"> transition={{ duration: 0.1, ease: 'easeOut' }}
Attached files className="origin-top-right bg-light-primary dark:bg-dark-primary border rounded-md border-light-200 dark:border-dark-200 w-full max-h-[200px] md:max-h-none overflow-y-auto flex flex-col"
</h4>
<div className="flex flex-row items-center space-x-4">
<button
type="button"
onClick={() => fileInputRef.current.click()}
className="flex flex-row items-center space-x-1 text-black/70 dark:text-white/70 hover:text-black hover:dark:text-white transition duration-200 focus:outline-none"
> >
<input <div className="flex flex-row items-center justify-between px-3 py-2">
type="file" <h4 className="text-black/70 dark:text-white/70 text-sm">
onChange={handleChange} Attached files
ref={fileInputRef} </h4>
accept=".pdf,.docx,.txt" <div className="flex flex-row items-center space-x-4">
multiple <button
hidden type="button"
/> onClick={() => fileInputRef.current.click()}
<Plus size={16} /> className="flex flex-row items-center space-x-1 text-black/70 dark:text-white/70 hover:text-black hover:dark:text-white transition duration-200 focus:outline-none"
<p className="text-xs">Add</p> >
</button> <input
<button type="file"
onClick={() => { onChange={handleChange}
setFiles([]); ref={fileInputRef}
setFileIds([]); accept=".pdf,.docx,.txt"
}} multiple
className="flex flex-row items-center space-x-1 text-black/70 dark:text-white/70 hover:text-black hover:dark:text-white transition duration-200 focus:outline-none" hidden
> />
<Trash size={14} /> <Plus size={16} />
<p className="text-xs">Clear</p> <p className="text-xs">Add</p>
</button> </button>
</div> <button
</div> onClick={() => {
<div className="h-[0.5px] mx-2 bg-white/10" /> setFiles([]);
<div className="flex flex-col items-center"> setFileIds([]);
{files.map((file, i) => ( }}
<div className="flex flex-row items-center space-x-1 text-black/70 dark:text-white/70 hover:text-black hover:dark:text-white transition duration-200 focus:outline-none"
key={i} >
className="flex flex-row items-center justify-start w-full space-x-3 p-3" <Trash size={13} />
> <p className="text-xs">Clear</p>
<div className="bg-light-100 dark:bg-dark-100 flex items-center justify-center w-10 h-10 rounded-md"> </button>
<File </div>
size={16}
className="text-black/70 dark:text-white/70"
/>
</div> </div>
<p className="text-black/70 dark:text-white/70 text-sm"> <div className="h-[0.5px] mx-2 bg-white/10" />
{file.fileName.length > 25 <div className="flex flex-col items-center">
? file.fileName.replace(/\.\w+$/, '').substring(0, 25) + {files.map((file, i) => (
'...' + <div
file.fileExtension key={i}
: file.fileName} className="flex flex-row items-center justify-start w-full space-x-3 p-3"
</p> >
</div> <div className="bg-light-100 dark:bg-dark-100 flex items-center justify-center w-9 h-9 rounded-md">
))} <File
</div> size={16}
</div> className="text-black/70 dark:text-white/70"
</PopoverPanel> />
</Transition> </div>
<p className="text-black/70 dark:text-white/70 text-xs">
{file.fileName.length > 25
? file.fileName
.replace(/\.\w+$/, '')
.substring(0, 25) +
'...' +
file.fileExtension
: file.fileName}
</p>
</div>
))}
</div>
</motion.div>
</PopoverPanel>
)}
</AnimatePresence>
</>
)}
</Popover> </Popover>
) : ( ) : (
<button <button

View File

@@ -1,21 +1,14 @@
import { cn } from '@/lib/utils';
import { import {
Popover, Popover,
PopoverButton, PopoverButton,
PopoverPanel, PopoverPanel,
Transition, Transition,
} from '@headlessui/react'; } from '@headlessui/react';
import { import { File, LoaderCircle, Paperclip, Plus, Trash } from 'lucide-react';
CopyPlus,
File,
LoaderCircle,
Paperclip,
Plus,
Trash,
} from 'lucide-react';
import { Fragment, useRef, useState } from 'react'; import { Fragment, useRef, useState } from 'react';
import { File as FileType } from '../ChatWindow';
import { useChat } from '@/lib/hooks/useChat'; import { useChat } from '@/lib/hooks/useChat';
import { AnimatePresence } from 'motion/react';
import { motion } from 'framer-motion';
const AttachSmall = () => { const AttachSmall = () => {
const { files, setFiles, setFileIds, fileIds } = useChat(); const { files, setFiles, setFileIds, fileIds } = useChat();
@@ -53,86 +46,95 @@ const AttachSmall = () => {
return loading ? ( return loading ? (
<div className="flex flex-row items-center justify-between space-x-1 p-1 "> <div className="flex flex-row items-center justify-between space-x-1 p-1 ">
<LoaderCircle size={20} className="text-sky-400 animate-spin" /> <LoaderCircle size={20} className="text-sky-500 animate-spin" />
</div> </div>
) : files.length > 0 ? ( ) : files.length > 0 ? (
<Popover className="max-w-[15rem] md:max-w-md lg:max-w-lg"> <Popover className="max-w-[15rem] md:max-w-md lg:max-w-lg">
<PopoverButton {({ open }) => (
type="button" <>
className="flex flex-row items-center justify-between space-x-1 p-1 text-black/50 dark:text-white/50 rounded-xl hover:bg-light-secondary dark:hover:bg-dark-secondary active:scale-95 transition duration-200 hover:text-black dark:hover:text-white" <PopoverButton
> type="button"
<File size={20} className="text-sky-400" /> className="flex flex-row items-center justify-between space-x-1 p-1 text-black/50 dark:text-white/50 rounded-xl hover:bg-light-secondary dark:hover:bg-dark-secondary active:scale-95 transition duration-200 hover:text-black dark:hover:text-white"
</PopoverButton> >
<Transition <File size={20} className="text-sky-500" />
as={Fragment} </PopoverButton>
enter="transition ease-out duration-150" <AnimatePresence>
enterFrom="opacity-0 translate-y-1" {open && (
enterTo="opacity-100 translate-y-0" <PopoverPanel
leave="transition ease-in duration-150" className="absolute z-10 w-64 md:w-[350px] bottom-14"
leaveFrom="opacity-100 translate-y-0" static
leaveTo="opacity-0 translate-y-1" >
> <motion.div
<PopoverPanel className="absolute z-10 w-64 md:w-[350px] bottom-14 -ml-3"> initial={{ opacity: 0, scale: 0.9 }}
<div className="bg-light-primary dark:bg-dark-primary border rounded-md border-light-200 dark:border-dark-200 w-full max-h-[200px] md:max-h-none overflow-y-auto flex flex-col"> animate={{ opacity: 1, scale: 1 }}
<div className="flex flex-row items-center justify-between px-3 py-2"> exit={{ opacity: 0, scale: 0.9 }}
<h4 className="text-black dark:text-white font-medium text-sm"> transition={{ duration: 0.1, ease: 'easeOut' }}
Attached files className="origin-bottom-left bg-light-primary dark:bg-dark-primary border rounded-md border-light-200 dark:border-dark-200 w-full max-h-[200px] md:max-h-none overflow-y-auto flex flex-col"
</h4>
<div className="flex flex-row items-center space-x-4">
<button
type="button"
onClick={() => fileInputRef.current.click()}
className="flex flex-row items-center space-x-1 text-black/70 dark:text-white/70 hover:text-black hover:dark:text-white transition duration-200"
> >
<input <div className="flex flex-row items-center justify-between px-3 py-2">
type="file" <h4 className="text-black/70 dark:text-white/70 font-medium text-sm">
onChange={handleChange} Attached files
ref={fileInputRef} </h4>
accept=".pdf,.docx,.txt" <div className="flex flex-row items-center space-x-4">
multiple <button
hidden type="button"
/> onClick={() => fileInputRef.current.click()}
<Plus size={18} /> className="flex flex-row items-center space-x-1 text-black/70 dark:text-white/70 hover:text-black hover:dark:text-white transition duration-200"
<p className="text-xs">Add</p> >
</button> <input
<button type="file"
onClick={() => { onChange={handleChange}
setFiles([]); ref={fileInputRef}
setFileIds([]); accept=".pdf,.docx,.txt"
}} multiple
className="flex flex-row items-center space-x-1 text-black/70 dark:text-white/70 hover:text-black hover:dark:text-white transition duration-200" hidden
> />
<Trash size={14} /> <Plus size={16} />
<p className="text-xs">Clear</p> <p className="text-xs">Add</p>
</button> </button>
</div> <button
</div> onClick={() => {
<div className="h-[0.5px] mx-2 bg-white/10" /> setFiles([]);
<div className="flex flex-col items-center"> setFileIds([]);
{files.map((file, i) => ( }}
<div className="flex flex-row items-center space-x-1 text-black/70 dark:text-white/70 hover:text-black hover:dark:text-white transition duration-200"
key={i} >
className="flex flex-row items-center justify-start w-full space-x-3 p-3" <Trash size={13} />
> <p className="text-xs">Clear</p>
<div className="bg-light-100 dark:bg-dark-100 flex items-center justify-center w-10 h-10 rounded-md"> </button>
<File </div>
size={16}
className="text-black/70 dark:text-white/70"
/>
</div> </div>
<p className="text-black/70 dark:text-white/70 text-sm"> <div className="h-[0.5px] mx-2 bg-white/10" />
{file.fileName.length > 25 <div className="flex flex-col items-center">
? file.fileName.replace(/\.\w+$/, '').substring(0, 25) + {files.map((file, i) => (
'...' + <div
file.fileExtension key={i}
: file.fileName} className="flex flex-row items-center justify-start w-full space-x-3 p-3"
</p> >
</div> <div className="bg-light-100 dark:bg-dark-100 flex items-center justify-center w-9 h-9 rounded-md">
))} <File
</div> size={16}
</div> className="text-black/70 dark:text-white/70"
</PopoverPanel> />
</Transition> </div>
<p className="text-black/70 dark:text-white/70 text-xs">
{file.fileName.length > 25
? file.fileName
.replace(/\.\w+$/, '')
.substring(0, 25) +
'...' +
file.fileExtension
: file.fileName}
</p>
</div>
))}
</div>
</motion.div>
</PopoverPanel>
)}
</AnimatePresence>
</>
)}
</Popover> </Popover>
) : ( ) : (
<button <button