mirror of
https://github.com/ItzCrazyKns/Perplexica.git
synced 2025-12-16 16:48:14 +00:00
feat(message-input-actions): update to use motion, improve animations
This commit is contained in:
@@ -2,15 +2,11 @@
|
|||||||
|
|
||||||
import { Cpu, Loader2, Search } from 'lucide-react';
|
import { Cpu, Loader2, Search } from 'lucide-react';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import {
|
import { Popover, PopoverButton, PopoverPanel } from '@headlessui/react';
|
||||||
Popover,
|
import { useEffect, useMemo, useState } from 'react';
|
||||||
PopoverButton,
|
|
||||||
PopoverPanel,
|
|
||||||
Transition,
|
|
||||||
} from '@headlessui/react';
|
|
||||||
import { Fragment, useEffect, useMemo, useState } from 'react';
|
|
||||||
import { MinimalProvider } from '@/lib/models/types';
|
import { MinimalProvider } from '@/lib/models/types';
|
||||||
import { useChat } from '@/lib/hooks/useChat';
|
import { useChat } from '@/lib/hooks/useChat';
|
||||||
|
import { AnimatePresence, motion } from 'motion/react';
|
||||||
|
|
||||||
const ModelSelector = () => {
|
const ModelSelector = () => {
|
||||||
const [providers, setProviders] = useState<MinimalProvider[]>([]);
|
const [providers, setProviders] = useState<MinimalProvider[]>([]);
|
||||||
@@ -79,24 +75,28 @@ const ModelSelector = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<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">
|
||||||
|
{({ open }) => (
|
||||||
|
<>
|
||||||
<PopoverButton
|
<PopoverButton
|
||||||
type="button"
|
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"
|
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"
|
||||||
>
|
>
|
||||||
<Cpu size={16} className="text-sky-500" />
|
<Cpu size={16} className="text-sky-500" />
|
||||||
</PopoverButton>
|
</PopoverButton>
|
||||||
<Transition
|
<AnimatePresence>
|
||||||
as={Fragment}
|
{open && (
|
||||||
enter="transition ease-out duration-100"
|
<PopoverPanel
|
||||||
enterFrom="opacity-0 translate-y-1"
|
className="absolute z-10 w-[230px] sm:w-[270px] md:w-[300px] right-0"
|
||||||
enterTo="opacity-100 translate-y-0"
|
static
|
||||||
leave="transition ease-in duration-100"
|
|
||||||
leaveFrom="opacity-100 translate-y-0"
|
|
||||||
leaveTo="opacity-0 translate-y-1"
|
|
||||||
>
|
>
|
||||||
<PopoverPanel className="absolute z-10 w-[230px] sm:w-[270px] md:w-[300px] -right-4">
|
<motion.div
|
||||||
<div className="bg-light-primary dark:bg-dark-primary max-h-[300px] sm:max-w-none border rounded-lg border-light-200 dark:border-dark-200 w-full flex flex-col shadow-lg overflow-hidden">
|
initial={{ opacity: 0, scale: 0.9 }}
|
||||||
<div className="p-4 border-b border-light-200 dark:border-dark-200">
|
animate={{ opacity: 1, scale: 1 }}
|
||||||
|
exit={{ opacity: 0, scale: 0.9 }}
|
||||||
|
transition={{ duration: 0.1, ease: 'easeOut' }}
|
||||||
|
className="origin-top-right bg-light-primary dark:bg-dark-primary max-h-[300px] sm:max-w-none border rounded-lg border-light-200 dark:border-dark-200 w-full flex flex-col shadow-lg overflow-hidden"
|
||||||
|
>
|
||||||
|
<div className="p-2 border-b border-light-200 dark:border-dark-200">
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<Search
|
<Search
|
||||||
size={16}
|
size={16}
|
||||||
@@ -107,7 +107,7 @@ const ModelSelector = () => {
|
|||||||
placeholder="Search models..."
|
placeholder="Search models..."
|
||||||
value={searchQuery}
|
value={searchQuery}
|
||||||
onChange={(e) => setSearchQuery(e.target.value)}
|
onChange={(e) => setSearchQuery(e.target.value)}
|
||||||
className="w-full pl-9 pr-3 py-2 bg-light-secondary dark:bg-dark-secondary rounded-lg placeholder:text-sm text-sm text-black dark:text-white placeholder:text-black/40 dark:placeholder:text-white/40 focus:outline-none focus:ring-2 focus:ring-sky-500/20 border border-transparent focus:border-sky-500/30 transition duration-200"
|
className="w-full pl-8 pr-3 py-2 bg-light-secondary dark:bg-dark-secondary rounded-lg placeholder:text-xs placeholder:-translate-y-[1.5px] text-xs text-black dark:text-white placeholder:text-black/40 dark:placeholder:text-white/40 focus:outline-none border border-transparent transition duration-200"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -146,7 +146,8 @@ const ModelSelector = () => {
|
|||||||
type="button"
|
type="button"
|
||||||
className={cn(
|
className={cn(
|
||||||
'px-3 py-2 flex items-center justify-between text-start duration-200 cursor-pointer transition rounded-lg group',
|
'px-3 py-2 flex items-center justify-between text-start duration-200 cursor-pointer transition rounded-lg group',
|
||||||
chatModelProvider?.providerId === provider.id &&
|
chatModelProvider?.providerId ===
|
||||||
|
provider.id &&
|
||||||
chatModelProvider?.key === model.key
|
chatModelProvider?.key === model.key
|
||||||
? 'bg-light-secondary dark:bg-dark-secondary'
|
? 'bg-light-secondary dark:bg-dark-secondary'
|
||||||
: 'hover:bg-light-secondary dark:hover:bg-dark-secondary',
|
: 'hover:bg-light-secondary dark:hover:bg-dark-secondary',
|
||||||
@@ -166,7 +167,7 @@ const ModelSelector = () => {
|
|||||||
/>
|
/>
|
||||||
<p
|
<p
|
||||||
className={cn(
|
className={cn(
|
||||||
'text-sm truncate',
|
'text-xs truncate',
|
||||||
chatModelProvider?.providerId ===
|
chatModelProvider?.providerId ===
|
||||||
provider.id &&
|
provider.id &&
|
||||||
chatModelProvider?.key === model.key
|
chatModelProvider?.key === model.key
|
||||||
@@ -189,9 +190,12 @@ const ModelSelector = () => {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</motion.div>
|
||||||
</PopoverPanel>
|
</PopoverPanel>
|
||||||
</Transition>
|
)}
|
||||||
|
</AnimatePresence>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</Popover>
|
</Popover>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import {
|
|||||||
} from '@headlessui/react';
|
} from '@headlessui/react';
|
||||||
import { Fragment } from 'react';
|
import { Fragment } from 'react';
|
||||||
import { useChat } from '@/lib/hooks/useChat';
|
import { useChat } from '@/lib/hooks/useChat';
|
||||||
|
import { AnimatePresence, motion } from 'motion/react';
|
||||||
|
|
||||||
const OptimizationModes = [
|
const OptimizationModes = [
|
||||||
{
|
{
|
||||||
@@ -60,17 +61,19 @@ const Optimization = () => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</PopoverButton>
|
</PopoverButton>
|
||||||
<Transition
|
<AnimatePresence>
|
||||||
as={Fragment}
|
{open && (
|
||||||
enter="transition ease-out duration-150"
|
<PopoverPanel
|
||||||
enterFrom="opacity-0 translate-y-1"
|
className="absolute z-10 w-64 md:w-[250px] left-0"
|
||||||
enterTo="opacity-100 translate-y-0"
|
static
|
||||||
leave="transition ease-in duration-150"
|
>
|
||||||
leaveFrom="opacity-100 translate-y-0"
|
<motion.div
|
||||||
leaveTo="opacity-0 translate-y-1"
|
initial={{ opacity: 0, scale: 0.9 }}
|
||||||
|
animate={{ opacity: 1, scale: 1 }}
|
||||||
|
exit={{ opacity: 0, scale: 0.9 }}
|
||||||
|
transition={{ duration: 0.1, ease: 'easeOut' }}
|
||||||
|
className="origin-top-left flex flex-col space-y-2 bg-light-primary dark:bg-dark-primary border rounded-lg border-light-200 dark:border-dark-200 w-full p-2 max-h-[200px] md:max-h-none overflow-y-auto"
|
||||||
>
|
>
|
||||||
<PopoverPanel className="absolute z-10 w-64 md:w-[250px] left-0">
|
|
||||||
<div className="flex flex-col gap-2 bg-light-primary dark:bg-dark-primary border rounded-lg border-light-200 dark:border-dark-200 w-full p-4 max-h-[200px] md:max-h-none overflow-y-auto">
|
|
||||||
{OptimizationModes.map((mode, i) => (
|
{OptimizationModes.map((mode, i) => (
|
||||||
<PopoverButton
|
<PopoverButton
|
||||||
onClick={() => setOptimizationMode(mode.key)}
|
onClick={() => setOptimizationMode(mode.key)}
|
||||||
@@ -84,16 +87,17 @@ const Optimization = () => {
|
|||||||
>
|
>
|
||||||
<div className="flex flex-row items-center space-x-1 text-black dark:text-white">
|
<div className="flex flex-row items-center space-x-1 text-black dark:text-white">
|
||||||
{mode.icon}
|
{mode.icon}
|
||||||
<p className="text-sm font-medium">{mode.title}</p>
|
<p className="text-xs font-medium">{mode.title}</p>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-black/70 dark:text-white/70 text-xs">
|
<p className="text-black/70 dark:text-white/70 text-xs">
|
||||||
{mode.description}
|
{mode.description}
|
||||||
</p>
|
</p>
|
||||||
</PopoverButton>
|
</PopoverButton>
|
||||||
))}
|
))}
|
||||||
</div>
|
</motion.div>
|
||||||
</PopoverPanel>
|
</PopoverPanel>
|
||||||
</Transition>
|
)}
|
||||||
|
</AnimatePresence>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</Popover>
|
</Popover>
|
||||||
|
|||||||
Reference in New Issue
Block a user