feat(deps): Tier 2 UX polish — embla, lightbox, gestures, virtuoso, motion
Installs all five Tier 2 polish deps the audit flagged. Each integrates where it adds concrete value today: - **embla-carousel-react** — shadcn-style `<Carousel>` primitive in `src/components/ui/carousel.tsx`. Available for future berth/yacht photo galleries; no current call site beyond the primitive. - **yet-another-react-lightbox** — wired into the image branch of `file-preview-dialog.tsx`. Clicking the preview image now opens a fullscreen lightbox with zoom/pan/keyboard nav. Lazy-loaded so the ~50kb only ships when a user actually previews an image. - **@use-gesture/react** — `usePinch` on the PdfViewer's content pane for native pinch-zoom on tablets/phones. Clamped to the same [50%, 300%] range as the +/- buttons; desktop wheel still scrolls. - **react-virtuoso** — installed but NOT wired. Inbox is naturally bounded by recent-notifications filter at ~10-20 items; ScrollArea handles it fine. Reserve for actual scale issues (admin audit log archive, etc.). - **motion** — installed but NOT wired. Pipeline kanban uses dnd-kit's own transforms and conflicts with motion's layout animation. @formkit/auto-animate already handles list-mutation animations elsewhere. Available for opportunistic adoption when a polish surface emerges that the existing libraries don't cover. Verified: tsc clean, vitest 1315/1315, next build green. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { Document, Page, pdfjs } from 'react-pdf';
|
||||
import { usePinch } from '@use-gesture/react';
|
||||
import { ChevronLeft, ChevronRight, Loader2, Minus, Plus } from 'lucide-react';
|
||||
|
||||
import { Button } from '@/components/ui/button';
|
||||
@@ -61,6 +62,22 @@ export function PdfViewer({ url, fileName }: PdfViewerProps) {
|
||||
setError(null);
|
||||
}, [url]);
|
||||
|
||||
// Pinch-zoom on touch devices. usePinch's `offset` already maps
|
||||
// gesture distance to a smoothly-changing scalar; we clamp it to
|
||||
// the same [0.5, 3] range as the +/- buttons.
|
||||
const pinchBindings = usePinch(
|
||||
({ offset: [s] }) => {
|
||||
setScale(Math.max(0.5, Math.min(3, s)));
|
||||
},
|
||||
{
|
||||
scaleBounds: { min: 0.5, max: 3 },
|
||||
from: () => [scale, 0],
|
||||
// Don't hijack wheel events — desktop users zoom via buttons,
|
||||
// wheel still scrolls the page.
|
||||
eventOptions: { passive: false },
|
||||
},
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="flex h-full flex-col">
|
||||
<div className="flex items-center justify-between gap-2 border-b bg-muted/40 px-3 py-2 text-sm">
|
||||
@@ -113,7 +130,14 @@ export function PdfViewer({ url, fileName }: PdfViewerProps) {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex-1 overflow-auto bg-muted/30 p-4">
|
||||
<div
|
||||
className="flex-1 overflow-auto bg-muted/30 p-4 touch-pan-y"
|
||||
// Pinch-zoom on touch devices (tablets, phones). On desktop the
|
||||
// +/- buttons stay primary; the gesture handler ignores wheel
|
||||
// events so it doesn't fight scroll-zoom. Range matches the
|
||||
// button zoom (50%–300%).
|
||||
{...pinchBindings()}
|
||||
>
|
||||
{error ? (
|
||||
<div className="flex h-full items-center justify-center text-sm text-destructive">
|
||||
{error}
|
||||
|
||||
Reference in New Issue
Block a user