Add button animations

This commit is contained in:
Richard Roberson 2025-01-26 20:43:42 -07:00
parent edb0b962d3
commit f9ab9190c2
6 changed files with 32 additions and 34 deletions

View file

@ -10,20 +10,19 @@ export default function Home() {
const [isSettingsOpen, setIsSettingsOpen] = useState(false); const [isSettingsOpen, setIsSettingsOpen] = useState(false);
return ( return (
<div className='p-4'> <div className='p-3 md:p-5'>
<div className="flex justify-between items-center mb-6"> <div className="flex justify-between items-center mb-6">
<h1 className="text-xl font-bold text-center flex-grow">OpenReader WebUI</h1> <h1 className="text-xl font-bold text-center flex-grow">OpenReader WebUI</h1>
<button <button
onClick={() => setIsSettingsOpen(true)} onClick={() => setIsSettingsOpen(true)}
className="rounded-full p-2.5 text-foreground hover:bg-base focus:bg-base className="rounded-full p-2 text-foreground hover:bg-offbase transform transition-transform duration-200 ease-in-out hover:scale-[1.1] hover:text-accent"
focus:outline-none focus:ring-2 focus:ring-accent transition-colors"
aria-label="Settings" aria-label="Settings"
> >
<SettingsIcon className="w-6 h-6 hover:animate-spin-slow" /> <SettingsIcon className="w-6 h-6 hover:animate-spin-slow" />
</button> </button>
</div> </div>
<div className="flex flex-col items-center gap-6"> <div className="flex flex-col items-center gap-5">
<PDFUploader className="w-full max-w-md" /> <PDFUploader className='max-w-xl' />
<DocumentList /> <DocumentList />
</div> </div>
<SettingsModal isOpen={isSettingsOpen} setIsOpen={setIsSettingsOpen} /> <SettingsModal isOpen={isSettingsOpen} setIsOpen={setIsSettingsOpen} />

View file

@ -79,9 +79,9 @@ export default function PDFViewerPage() {
<Link <Link
href="/" href="/"
onClick={() => {clearCurrDoc(); stop();}} onClick={() => {clearCurrDoc(); stop();}}
className="inline-flex items-center px-3 py-1 bg-base text-foreground rounded-lg hover:bg-offbase transition-colors" className="inline-flex items-center px-3 py-1 bg-base text-foreground rounded-lg hover:bg-offbase transform transition-transform duration-200 ease-in-out hover:scale-[1.02]"
> >
<svg className="w-4 h-4 mr-2 text-muted" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <svg className="w-4 h-4 mr-2" fill="currentColor" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M10 19l-7-7m0 0l7-7m-7 7h18" /> <path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M10 19l-7-7m0 0l7-7m-7 7h18" />
</svg> </svg>
Documents Documents
@ -89,7 +89,7 @@ export default function PDFViewerPage() {
<div className="bg-offbase px-2 py-0.5 rounded-full flex items-center gap-2"> <div className="bg-offbase px-2 py-0.5 rounded-full flex items-center gap-2">
<Button <Button
onClick={handleZoomOut} onClick={handleZoomOut}
className="text-xs hover:text-accent transition-colors" className="text-xs transform transition-transform duration-200 ease-in-out hover:scale-[1.45] hover:font-semibold hover:text-accent"
aria-label="Zoom out" aria-label="Zoom out"
> >
@ -97,7 +97,7 @@ export default function PDFViewerPage() {
<span className="text-xs">{zoomLevel}%</span> <span className="text-xs">{zoomLevel}%</span>
<Button <Button
onClick={handleZoomIn} onClick={handleZoomIn}
className="text-xs hover:text-accent transition-colors" className="text-xs transform transition-transform duration-200 ease-in-out hover:scale-[1.45] hover:font-semibold hover:text-accent"
aria-label="Zoom in" aria-label="Zoom in"
> >
@ -105,8 +105,7 @@ export default function PDFViewerPage() {
</div> </div>
<Button <Button
onClick={() => setIsSettingsOpen(true)} onClick={() => setIsSettingsOpen(true)}
className="rounded-full p-2 text-foreground hover:bg-offbase focus:bg-offbase className="rounded-full p-1 text-foreground hover:bg-offbase transform transition-transform duration-200 ease-in-out hover:scale-[1.1] hover:text-accent"
focus:outline-none focus:ring-2 focus:ring-accent transition-colors"
aria-label="View Settings" aria-label="View Settings"
> >
<SettingsIcon className="w-5 h-5 hover:animate-spin-slow" /> <SettingsIcon className="w-5 h-5 hover:animate-spin-slow" />

View file

@ -1,6 +1,6 @@
import { usePDF } from '@/contexts/PDFContext'; import { usePDF } from '@/contexts/PDFContext';
import Link from 'next/link'; import Link from 'next/link';
import { Dialog } from '@headlessui/react'; import { Button, Dialog } from '@headlessui/react';
import { Transition, TransitionChild, DialogPanel, DialogTitle } from '@headlessui/react'; import { Transition, TransitionChild, DialogPanel, DialogTitle } from '@headlessui/react';
import { Fragment, useState } from 'react'; import { Fragment, useState } from 'react';
@ -38,7 +38,7 @@ export function DocumentList() {
} }
return ( return (
<div className="w-full max-w-3xl mx-auto"> <div className="w-full mx-auto">
<h2 className="text-xl font-semibold mb-4 text-foreground">Your Documents</h2> <h2 className="text-xl font-semibold mb-4 text-foreground">Your Documents</h2>
<div className="bg-background rounded-lg shadow p-2 space-y-2"> <div className="bg-background rounded-lg shadow p-2 space-y-2">
{documents.map((doc) => ( {documents.map((doc) => (
@ -64,14 +64,14 @@ export function DocumentList() {
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M7 21h10a2 2 0 002-2V9.414a1 1 0 00-.293-.707l-5.414-5.414A1 1 0 0012.586 3H7a2 2 0 00-2 2v14a2 2 0 002 2z" /> <path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M7 21h10a2 2 0 002-2V9.414a1 1 0 00-.293-.707l-5.414-5.414A1 1 0 0012.586 3H7a2 2 0 00-2 2v14a2 2 0 002 2z" />
</svg> </svg>
</div> </div>
<div className="flex-1 min-w-0"> <div className="flex-1 min-w-0 transform transition-transform duration-200 ease-in-out hover:scale-[1.02]">
<p className="text-foreground font-medium truncate">{doc.name}</p> <p className="text-foreground font-medium truncate">{doc.name}</p>
<p className="text-sm text-muted truncate"> <p className="text-sm text-muted truncate">
{(doc.size / 1024 / 1024).toFixed(2)} MB {(doc.size / 1024 / 1024).toFixed(2)} MB
</p> </p>
</div> </div>
</Link> </Link>
<button <Button
onClick={() => { onClick={() => {
setDocumentToDelete({ id: doc.id, name: doc.name }); setDocumentToDelete({ id: doc.id, name: doc.name });
setIsDeleteDialogOpen(true); setIsDeleteDialogOpen(true);
@ -82,7 +82,7 @@ export function DocumentList() {
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" /> <path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
</svg> </svg>
</button> </Button>
</div> </div>
</Transition> </Transition>
))} ))}
@ -131,20 +131,20 @@ export function DocumentList() {
</div> </div>
<div className="mt-4 flex justify-end space-x-3"> <div className="mt-4 flex justify-end space-x-3">
<button <Button
type="button" type="button"
className="inline-flex justify-center rounded-md border border-transparent bg-base px-4 py-2 text-sm font-medium text-foreground hover:bg-base/80 focus:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-2" className="inline-flex justify-center rounded-md border border-transparent bg-base px-4 py-2 text-sm font-medium text-foreground hover:bg-base/80 focus:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-2"
onClick={() => setIsDeleteDialogOpen(false)} onClick={() => setIsDeleteDialogOpen(false)}
> >
Cancel Cancel
</button> </Button>
<button <Button
type="button" type="button"
className="inline-flex justify-center rounded-md border border-transparent bg-red-600 px-4 py-2 text-sm font-medium text-white hover:bg-red-700 focus:outline-none focus-visible:ring-2 focus-visible:ring-red-500 focus-visible:ring-offset-2" className="inline-flex justify-center rounded-md border border-transparent bg-red-600 px-4 py-2 text-sm font-medium text-white hover:bg-red-700 focus:outline-none focus-visible:ring-2 focus-visible:ring-red-500 focus-visible:ring-offset-2"
onClick={handleDelete} onClick={handleDelete}
> >
Delete Delete
</button> </Button>
</div> </div>
</DialogPanel> </DialogPanel>
</TransitionChild> </TransitionChild>

View file

@ -45,7 +45,7 @@ export function PDFUploader({ className = '' }: PDFUploaderProps) {
className={` className={`
w-full py-5 px-3 border-2 border-dashed rounded-lg w-full py-5 px-3 border-2 border-dashed rounded-lg
${isDragActive ? 'border-accent bg-base' : 'border-muted'} ${isDragActive ? 'border-accent bg-base' : 'border-muted'}
transition-colors duration-200 ease-in-out transform trasition-transform duration-200 ease-in-out hover:scale-[1.008]
${isUploading ? 'cursor-not-allowed opacity-50' : 'cursor-pointer hover:border-accent hover:bg-base'} ${isUploading ? 'cursor-not-allowed opacity-50' : 'cursor-pointer hover:border-accent hover:bg-base'}
${className} ${className}
`} `}

View file

@ -1,7 +1,7 @@
'use client'; 'use client';
import { Fragment } from 'react'; import { Fragment } from 'react';
import { Dialog, DialogPanel, DialogTitle, Transition, TransitionChild, Listbox, ListboxButton, ListboxOptions, ListboxOption } from '@headlessui/react'; import { Dialog, DialogPanel, DialogTitle, Transition, TransitionChild, Listbox, ListboxButton, ListboxOptions, ListboxOption, Button } from '@headlessui/react';
import { useConfig, ViewType } from '@/contexts/ConfigContext'; import { useConfig, ViewType } from '@/contexts/ConfigContext';
import { ChevronUpDownIcon, CheckIcon } from '@/components/icons/Icons'; import { ChevronUpDownIcon, CheckIcon } from '@/components/icons/Icons';
@ -62,7 +62,7 @@ export function PDFViewSettings({ isOpen, setIsOpen }: PDFViewSettingsProps) {
onChange={(newView) => updateConfigKey('viewType', newView.id as ViewType)} onChange={(newView) => updateConfigKey('viewType', newView.id as ViewType)}
> >
<div className="relative"> <div className="relative">
<ListboxButton className="relative w-full cursor-pointer rounded-lg bg-background py-2 pl-3 pr-10 text-left text-foreground shadow-sm focus:outline-none focus:ring-2 focus:ring-accent"> <ListboxButton className="relative w-full cursor-pointer rounded-lg bg-background py-2 pl-3 pr-10 text-left text-foreground shadow-sm focus:outline-none focus:ring-2 focus:ring-accent transform transition-transform duration-200 ease-in-out hover:scale-[1.01] hover:text-accent">
<span className="block truncate">{selectedView.name}</span> <span className="block truncate">{selectedView.name}</span>
<span className="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2"> <span className="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2">
<ChevronUpDownIcon className="h-5 w-5 text-muted" /> <ChevronUpDownIcon className="h-5 w-5 text-muted" />
@ -113,16 +113,16 @@ export function PDFViewSettings({ isOpen, setIsOpen }: PDFViewSettingsProps) {
</div> </div>
<div className="mt-3 flex justify-end"> <div className="mt-3 flex justify-end">
<button <Button
type="button" type="button"
className="inline-flex justify-center rounded-lg bg-background px-4 py-2 text-sm className="inline-flex justify-center rounded-lg bg-background px-4 py-2 text-sm
font-medium text-foreground hover:bg-background/90 focus:outline-none font-medium text-foreground hover:bg-background/90 focus:outline-none
focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-2 focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-2
transition-colors" transform transition-transform duration-200 ease-in-out hover:scale-[1.04] hover:text-accent"
onClick={() => setIsOpen(false)} onClick={() => setIsOpen(false)}
> >
Close Close
</button> </Button>
</div> </div>
</DialogPanel> </DialogPanel>
</TransitionChild> </TransitionChild>

View file

@ -1,7 +1,7 @@
'use client'; 'use client';
import { Fragment, useState, useEffect } from 'react'; import { Fragment, useState, useEffect } from 'react';
import { Dialog, DialogPanel, DialogTitle, Transition, TransitionChild, Listbox, ListboxButton, ListboxOptions, ListboxOption } from '@headlessui/react'; import { Dialog, DialogPanel, DialogTitle, Transition, TransitionChild, Listbox, ListboxButton, ListboxOptions, ListboxOption, Button } from '@headlessui/react';
import { useTheme } from '@/contexts/ThemeContext'; import { useTheme } from '@/contexts/ThemeContext';
import { useConfig } from '@/contexts/ConfigContext'; import { useConfig } from '@/contexts/ConfigContext';
import { ChevronUpDownIcon, CheckIcon } from './icons/Icons'; import { ChevronUpDownIcon, CheckIcon } from './icons/Icons';
@ -68,7 +68,7 @@ export function SettingsModal({ isOpen, setIsOpen }: SettingsModalProps) {
<label className="block text-sm font-medium text-foreground">Theme</label> <label className="block text-sm font-medium text-foreground">Theme</label>
<Listbox value={selectedTheme} onChange={(newTheme) => setTheme(newTheme.id)}> <Listbox value={selectedTheme} onChange={(newTheme) => setTheme(newTheme.id)}>
<div className="relative"> <div className="relative">
<ListboxButton className="relative w-full cursor-pointer rounded-lg bg-background py-2 pl-3 pr-10 text-left text-foreground shadow-sm focus:outline-none focus:ring-2 focus:ring-accent"> <ListboxButton className="relative w-full cursor-pointer rounded-lg bg-background py-2 pl-3 pr-10 text-left text-foreground shadow-sm focus:outline-none focus:ring-2 focus:ring-accent transform transition-transform duration-200 ease-in-out hover:scale-[1.01] hover:text-accent">
<span className="block truncate">{selectedTheme.name}</span> <span className="block truncate">{selectedTheme.name}</span>
<span className="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2"> <span className="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2">
<ChevronUpDownIcon className="h-5 w-5 text-muted" /> <ChevronUpDownIcon className="h-5 w-5 text-muted" />
@ -134,12 +134,12 @@ export function SettingsModal({ isOpen, setIsOpen }: SettingsModalProps) {
</div> </div>
<div className="mt-6 flex justify-end space-x-3"> <div className="mt-6 flex justify-end space-x-3">
<button <Button
type="button" type="button"
className="inline-flex justify-center rounded-lg bg-accent px-4 py-2 text-sm className="inline-flex justify-center rounded-lg bg-accent px-4 py-2 text-sm
font-medium text-white hover:bg-accent/90 focus:outline-none font-medium text-white hover:bg-accent/90 focus:outline-none
focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-2 focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-2
transition-colors" transform transition-transform duration-200 ease-in-out hover:scale-[1.04] hover:text-background"
onClick={async () => { onClick={async () => {
await updateConfig({ await updateConfig({
apiKey: localApiKey, apiKey: localApiKey,
@ -149,13 +149,13 @@ export function SettingsModal({ isOpen, setIsOpen }: SettingsModalProps) {
}} }}
> >
Save Save
</button> </Button>
<button <Button
type="button" type="button"
className="inline-flex justify-center rounded-lg bg-background px-4 py-2 text-sm className="inline-flex justify-center rounded-lg bg-background px-4 py-2 text-sm
font-medium text-foreground hover:bg-background/90 focus:outline-none font-medium text-foreground hover:bg-background/90 focus:outline-none
focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-2 focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-2
transition-colors" transform transition-transform duration-200 ease-in-out hover:scale-[1.04] hover:text-accent"
onClick={() => { onClick={() => {
setLocalApiKey(apiKey); setLocalApiKey(apiKey);
setLocalBaseUrl(baseUrl); setLocalBaseUrl(baseUrl);
@ -163,7 +163,7 @@ export function SettingsModal({ isOpen, setIsOpen }: SettingsModalProps) {
}} }}
> >
Cancel Cancel
</button> </Button>
</div> </div>
</DialogPanel> </DialogPanel>
</TransitionChild> </TransitionChild>