@ibanzajoe/uploader 1.4.0 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -3
- package/dist/index.cjs +93 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +38 -5
- package/dist/index.d.ts +38 -5
- package/dist/index.js +91 -5
- package/dist/index.js.map +1 -1
- package/dist/server.cjs +3 -1
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +35 -6
- package/dist/server.d.ts +35 -6
- package/dist/server.js +3 -1
- package/dist/server.js.map +1 -1
- package/dist/styles.css +42 -0
- package/package.json +1 -1
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/react/PickerOverlay.tsx","../src/react/usePicker.ts","../src/react/ImageEditor.tsx","../src/react/editImage.ts","../src/react/Dropzone.tsx","../src/react/CameraCapture.tsx","../src/react/ProgressBar.tsx","../src/react/FileQueueRow.tsx","../src/react/theme.ts","../src/react/DropPane.tsx"],"sourcesContent":["/**\n * PickerOverlay — modal file picker with drag-and-drop.\n *\n * A full-featured modal picker matching filestack's core UX:\n * - Drag-and-drop dropzone + \"Browse files\" button\n * - Multi-file queue with thumbnails/preview, per-file progress bars\n * - Remove and retry controls per file\n * - Aggregate progress bar\n * - Upload / Cancel action buttons\n * - Keyboard: ESC closes; Tab cycles within the modal (focus trap)\n * - ARIA: role=\"dialog\", aria-modal, labelled header, live regions\n * - Themeable via CSS variables (--uploader-accent, etc.)\n *\n * Opens when `open={true}`. Calls `onClose` when the user dismisses via ESC,\n * the backdrop click, or the Cancel button. `onUploadDone` fires after all\n * selected files have been uploaded.\n *\n * Composes Dropzone, FileQueueRow, and ProgressBar primitives from the #194\n * foundation — no hard-coded brand colors.\n *\n * @dsCard PickerOverlay\n */\n\nimport React, {\n useRef,\n useState,\n useCallback,\n useEffect,\n useId,\n} from 'react'\nimport { usePicker } from './usePicker.js'\nimport { ImageEditor } from './ImageEditor.js'\nimport { Dropzone } from './Dropzone.js'\nimport { CameraCapture, shouldOfferCamera } from './CameraCapture.js'\nimport { FileQueueRow } from './FileQueueRow.js'\nimport { ProgressBar } from './ProgressBar.js'\nimport { themeToVars } from './theme.js'\nimport type { UploaderTheme } from './theme.js'\nimport type { SharedPickerProps } from './types.js'\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport type PickerOverlayProps = SharedPickerProps & {\n /** Whether the modal is open. */\n open: boolean\n /** Called when the modal should close (ESC, backdrop, cancel). */\n onClose: () => void\n /**\n * Design tokens to override, scoped to this picker instance. Maps to\n * `--uploader-*` CSS variables applied on the overlay root — see UploaderTheme.\n * Omit to use the shipped defaults (or page-level CSS-variable overrides).\n */\n theme?: UploaderTheme\n /** Additional CSS class(es) applied to the modal panel. */\n className?: string\n /** Inline styles applied to the modal panel. */\n style?: React.CSSProperties\n}\n\n// ─── Helpers ─────────────────────────────────────────────────────────────────\n\n/** Derive the dialog title from current upload state. */\nfunction dialogTitle(\n isUploading: boolean,\n isDone: boolean,\n totalFiles: number,\n doneCount: number,\n errorCount: number,\n editingId: string | null,\n cameraOpen: boolean,\n): string {\n if (editingId) return 'Edit Image'\n if (cameraOpen) return 'Take a photo'\n if (isDone && errorCount === 0) return 'All files uploaded'\n if (isDone && errorCount > 0) return `${doneCount} of ${totalFiles} uploaded`\n if (isUploading) return `Uploading ${totalFiles} file${totalFiles !== 1 ? 's' : ''}`\n return 'Upload files'\n}\n\n// ─── Component ────────────────────────────────────────────────────────────────\n\nexport function PickerOverlay({\n open,\n onClose,\n apikey,\n apiUrl,\n security,\n deliveryProtection,\n allowedOrigins,\n pickerOptions,\n onUploadDone,\n onFileUploadFinished,\n onFileUploadFailed,\n onCancel,\n theme,\n className,\n style,\n}: PickerOverlayProps) {\n const titleId = useId()\n const panelRef = useRef<HTMLDivElement>(null)\n const [isDragOver, setIsDragOver] = useState(false)\n const [editingId, setEditingId] = useState<string | null>(null)\n const [cameraOpen, setCameraOpen] = useState(false)\n\n const { files, addFiles, removeFile, editFile, retryFile, upload, progress, isUploading, isDone } =\n usePicker({\n apikey,\n apiUrl,\n security,\n deliveryProtection,\n allowedOrigins,\n pickerOptions,\n onUploadDone,\n onFileUploadFinished,\n onFileUploadFailed,\n })\n\n // ── ESC key to close ─────────────────────────────────────────────────────\n useEffect(() => {\n if (!open) return\n const handler = (e: KeyboardEvent) => {\n if (e.key === 'Escape') {\n onCancel?.()\n onClose()\n }\n }\n document.addEventListener('keydown', handler)\n return () => document.removeEventListener('keydown', handler)\n }, [open, onClose, onCancel])\n\n // ── Focus trap ───────────────────────────────────────────────────────────\n // When open, constrain Tab/Shift+Tab to the modal panel.\n useEffect(() => {\n if (!open || !panelRef.current) return\n\n const panel = panelRef.current\n const focusable = panel.querySelectorAll<HTMLElement>(\n 'button, [href], input, select, textarea, [tabindex]:not([tabindex=\"-1\"])',\n )\n if (focusable.length === 0) return\n\n const first = focusable[0]!\n const last = focusable[focusable.length - 1]!\n\n // Move initial focus into the panel\n first.focus()\n\n const trap = (e: KeyboardEvent) => {\n if (e.key !== 'Tab') return\n if (e.shiftKey) {\n if (document.activeElement === first) {\n e.preventDefault()\n last.focus()\n }\n } else {\n if (document.activeElement === last) {\n e.preventDefault()\n first.focus()\n }\n }\n }\n\n document.addEventListener('keydown', trap)\n return () => document.removeEventListener('keydown', trap)\n }, [open, files, editingId, cameraOpen]) // re-run when the list or active view changes\n\n // ── Body scroll lock ─────────────────────────────────────────────────────\n useEffect(() => {\n if (!open) return\n const prev = document.body.style.overflow\n document.body.style.overflow = 'hidden'\n return () => {\n document.body.style.overflow = prev\n }\n }, [open])\n\n // ── Drag handlers ────────────────────────────────────────────────────────\n const handleDragEnter = useCallback((e: React.DragEvent) => {\n e.preventDefault()\n setIsDragOver(true)\n }, [])\n\n const handleDragLeave = useCallback((e: React.DragEvent) => {\n e.preventDefault()\n setIsDragOver(false)\n }, [])\n\n const handleDragOver = useCallback((e: React.DragEvent) => {\n e.preventDefault()\n }, [])\n\n const handleDrop = useCallback(\n (e: React.DragEvent) => {\n e.preventDefault()\n setIsDragOver(false)\n if (e.dataTransfer.files.length > 0) {\n addFiles(e.dataTransfer.files)\n }\n },\n [addFiles],\n )\n\n const handleClose = useCallback(() => {\n onCancel?.()\n onClose()\n }, [onCancel, onClose])\n\n const hasPending = files.some((f) => f.state === 'pending')\n const pendingCount = files.filter((f) => f.state === 'pending').length\n const doneCount = files.filter((f) => f.state === 'done').length\n const errorCount = files.filter((f) => f.state === 'error').length\n const accept = pickerOptions?.accept?.join(',')\n const multiple = !pickerOptions?.maxFiles || pickerOptions.maxFiles > 1\n const editingEntry = files.find((f) => f.id === editingId) ?? null\n const offerCamera = shouldOfferCamera(pickerOptions)\n\n const title = dialogTitle(\n isUploading,\n isDone,\n files.length,\n doneCount,\n errorCount,\n editingId,\n cameraOpen,\n )\n\n if (!open) return null\n\n return (\n /* Backdrop — clicking outside the panel dismisses the modal */\n <div\n className=\"uploader-backdrop\"\n data-testid=\"picker-backdrop\"\n style={themeToVars(theme)}\n onClick={handleClose}\n >\n {/* Modal panel — stops backdrop click propagation */}\n <div\n ref={panelRef}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby={titleId}\n data-drag-over={isDragOver && !editingEntry && files.length === 0 ? 'true' : undefined}\n className={['uploader-picker-panel', className ?? ''].filter(Boolean).join(' ')}\n style={style}\n data-testid=\"picker-panel\"\n onClick={(e) => e.stopPropagation()}\n >\n {/* ── Header ─────────────────────────────────────────────────────── */}\n <div className=\"uploader-picker-header\">\n <h2 id={titleId} className=\"uploader-picker-title\">\n {title}\n </h2>\n <button\n type=\"button\"\n className=\"uploader-btn-close\"\n aria-label=\"Close picker\"\n onClick={handleClose}\n >\n ✕\n </button>\n </div>\n\n {/* ── Image editor view ───────────────────────────────────────────── */}\n {editingEntry ? (\n <ImageEditor\n file={editingEntry.file}\n onCancel={() => setEditingId(null)}\n onApply={(edited) => {\n editFile(editingEntry.id, edited)\n setEditingId(null)\n }}\n />\n ) : cameraOpen ? (\n /* ── Camera capture view ───────────────────────────────────────── */\n <CameraCapture\n facingMode={pickerOptions?.cameraFacingMode}\n onCancel={() => setCameraOpen(false)}\n onCapture={(file) => {\n addFiles([file])\n setCameraOpen(false)\n }}\n />\n ) : isDone ? (\n /* ── Done state ─────────────────────────────────────────────────── */\n <div className=\"uploader-done-body\">\n <p className=\"uploader-done-subtitle\">\n {files.length} file{files.length !== 1 ? 's' : ''} delivered to your bucket\n </p>\n <ul className=\"uploader-file-list uploader-done-list\" role=\"list\" aria-label=\"Uploaded files\">\n {files.map((entry) => (\n <FileQueueRow\n key={entry.id}\n entry={entry}\n onRemove={removeFile}\n onRetry={retryFile}\n />\n ))}\n </ul>\n <button\n type=\"button\"\n className=\"uploader-btn-primary uploader-btn-done-full\"\n onClick={onClose}\n >\n Done\n </button>\n </div>\n ) : (\n /* ── Normal (idle / uploading / error) view ─────────────────────── */\n <>\n {/* Dropzone — hidden once files are queued */}\n {files.length === 0 && (\n <Dropzone\n onFiles={addFiles}\n isDragOver={isDragOver}\n onDragEnter={handleDragEnter}\n onDragLeave={handleDragLeave}\n onDragOver={handleDragOver}\n onDrop={handleDrop}\n accept={accept}\n multiple={multiple}\n onCamera={offerCamera ? () => setCameraOpen(true) : undefined}\n hint=\"PNG, JPG, PDF, ZIP · up to 50 MB\"\n />\n )}\n\n {/* File queue */}\n {files.length > 0 && (\n <ul\n className=\"uploader-file-list\"\n role=\"list\"\n aria-label=\"Selected files\"\n aria-live=\"polite\"\n >\n {files.map((entry) => (\n <FileQueueRow\n key={entry.id}\n entry={entry}\n onRemove={removeFile}\n onRetry={retryFile}\n onEdit={setEditingId}\n />\n ))}\n </ul>\n )}\n\n {/* Aggregate progress — shown while uploading */}\n {isUploading && (\n <div\n className=\"uploader-aggregate-progress\"\n role=\"status\"\n aria-live=\"polite\"\n >\n <span className=\"uploader-aggregate-progress-label\">Total progress</span>\n <ProgressBar\n value={progress}\n label=\"Overall upload progress\"\n size=\"lg\"\n />\n <span className=\"uploader-aggregate-progress-pct\">{progress}%</span>\n </div>\n )}\n\n {/* Footer */}\n <div className=\"uploader-picker-footer\">\n <span className=\"uploader-picker-footer-esc\">Esc to close</span>\n <div className=\"uploader-picker-footer-actions\">\n <button\n type=\"button\"\n className=\"uploader-btn-secondary uploader-btn-cancel\"\n disabled={isUploading}\n onClick={handleClose}\n >\n Cancel\n </button>\n <button\n type=\"button\"\n className=\"uploader-btn-primary uploader-btn-upload\"\n disabled={isUploading || !hasPending}\n onClick={() => void upload()}\n >\n {isUploading\n ? 'Uploading…'\n : `Upload${pendingCount > 0 ? ` ${pendingCount} file${pendingCount !== 1 ? 's' : ''}` : ''}`}\n </button>\n </div>\n </div>\n </>\n )}\n </div>\n </div>\n )\n}\n","/**\n * usePicker — headless hook driving UploaderClient for the React picker.\n *\n * Manages the file queue, per-file upload state, progress aggregation, and\n * result collection. Components (PickerOverlay, DropPane) call this hook and\n * render its state; custom UIs can use it directly for fully bespoke pickers.\n *\n * Lifecycle:\n * addFiles() → files are queued (state: \"pending\")\n * upload() → each pending file is passed to client.upload() in order;\n * state transitions pending → uploading → done/error\n * onUploadDone fires once all files have settled\n */\n\nimport { useState, useCallback, useRef } from 'react'\nimport { UploaderClient } from '../core/client.js'\nimport type { UploaderClientOptions } from '../core/types.js'\nimport type { PickerFile, PickerOptions, UsePickerResult } from './types.js'\nimport type { PickerResponse } from '../core/types.js'\n\n// ─── Counter for stable keys ──────────────────────────────────────────────────\n\nlet _idCounter = 0\nfunction nextId(): string {\n return `pf-${++_idCounter}`\n}\n\n// ─── usePicker ────────────────────────────────────────────────────────────────\n\nexport type UsePickerOptions = UploaderClientOptions & {\n pickerOptions?: PickerOptions\n onUploadDone?: (response: PickerResponse) => void\n onFileUploadFinished?: (file: import('../core/types.js').FileResult) => void\n onFileUploadFailed?: (file: File, error: Error) => void\n}\n\nexport function usePicker(opts: UsePickerOptions): UsePickerResult {\n const [files, setFiles] = useState<PickerFile[]>([])\n\n // Client ref — stable across renders; recreated when any client-affecting\n // option changes (apikey/apiUrl/security/deliveryProtection/allowedOrigins).\n const clientRef = useRef<UploaderClient | null>(null)\n const clientSigRef = useRef<string>('')\n const clientSig = JSON.stringify({\n apikey: opts.apikey,\n apiUrl: opts.apiUrl ?? null,\n security: opts.security ?? null,\n deliveryProtection: opts.deliveryProtection ?? null,\n allowedOrigins: opts.allowedOrigins ?? null,\n })\n if (!clientRef.current || clientSigRef.current !== clientSig) {\n clientSigRef.current = clientSig\n clientRef.current = new UploaderClient({\n apikey: opts.apikey,\n apiUrl: opts.apiUrl,\n security: opts.security,\n deliveryProtection: opts.deliveryProtection,\n allowedOrigins: opts.allowedOrigins,\n })\n }\n\n const addFiles = useCallback(\n (incoming: FileList | File[]) => {\n const list = Array.from(incoming)\n const { maxFiles, maxSize } = opts.pickerOptions ?? {}\n\n setFiles((prev) => {\n const remaining = maxFiles != null ? maxFiles - prev.length : Infinity\n const toAdd = list.slice(0, remaining).filter((f) => {\n if (maxSize != null && f.size > maxSize) return false\n return true\n })\n\n const newEntries: PickerFile[] = toAdd.map((f) => ({\n file: f,\n id: nextId(),\n state: 'pending',\n progress: null,\n previewUrl: f.type.startsWith('image/') ? URL.createObjectURL(f) : undefined,\n }))\n\n return [...prev, ...newEntries]\n })\n },\n [opts.pickerOptions],\n )\n\n const removeFile = useCallback((id: string) => {\n setFiles((prev) => {\n const entry = prev.find((f) => f.id === id)\n if (entry?.previewUrl) URL.revokeObjectURL(entry.previewUrl)\n return prev.filter((f) => f.id !== id)\n })\n }, [])\n\n // Replace a queued file's bytes with an edited version (from ImageEditor),\n // regenerating its preview and resetting it to a fresh pending state.\n const editFile = useCallback((id: string, newFile: File) => {\n setFiles((prev) =>\n prev.map((f) => {\n if (f.id !== id) return f\n if (f.previewUrl) URL.revokeObjectURL(f.previewUrl)\n return {\n ...f,\n file: newFile,\n state: 'pending',\n progress: null,\n error: undefined,\n result: undefined,\n previewUrl: newFile.type.startsWith('image/') ? URL.createObjectURL(newFile) : undefined,\n }\n }),\n )\n }, [])\n\n const retryFile = useCallback((id: string) => {\n setFiles((prev) =>\n prev.map((f) =>\n f.id === id ? { ...f, state: 'pending', progress: null, error: undefined } : f,\n ),\n )\n }, [])\n\n const upload = useCallback(async () => {\n const client = clientRef.current!\n const snapshot = files.filter((f) => f.state === 'pending')\n\n const uploaded: import('../core/types.js').FileResult[] = []\n const failed: import('../core/types.js').FileResult[] = []\n\n for (const entry of snapshot) {\n // Mark uploading\n setFiles((prev) =>\n prev.map((f) => (f.id === entry.id ? { ...f, state: 'uploading', progress: 0 } : f)),\n )\n\n try {\n const result = await client.upload(entry.file, {\n onProgress: (pct) => {\n setFiles((prev) =>\n prev.map((f) => (f.id === entry.id ? { ...f, progress: pct } : f)),\n )\n },\n })\n\n setFiles((prev) =>\n prev.map((f) =>\n f.id === entry.id ? { ...f, state: 'done', progress: 100, result } : f,\n ),\n )\n\n opts.onFileUploadFinished?.(result)\n uploaded.push(result)\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err))\n setFiles((prev) =>\n prev.map((f) =>\n f.id === entry.id\n ? { ...f, state: 'error', progress: null, error: error.message }\n : f,\n ),\n )\n opts.onFileUploadFailed?.(entry.file, error)\n // Push a placeholder into failed — no FileResult since upload didn't complete\n failed.push({\n handle: '',\n url: '',\n filename: entry.file.name,\n mimetype: entry.file.type,\n size: entry.file.size,\n status: 'Stored',\n })\n }\n }\n\n if (snapshot.length > 0) {\n opts.onUploadDone?.({ filesUploaded: uploaded, filesFailed: failed })\n }\n }, [files, opts])\n\n // Aggregate progress: mean of uploading files; 100 when all done, 0 when no files\n const activeFiles = files.filter((f) => f.state === 'uploading' || f.state === 'done')\n const progress =\n activeFiles.length === 0\n ? 0\n : Math.round(\n activeFiles.reduce((sum, f) => sum + (f.progress ?? 0), 0) / activeFiles.length,\n )\n\n const isUploading = files.some((f) => f.state === 'uploading')\n const isDone =\n files.length > 0 && files.every((f) => f.state === 'done' || f.state === 'error')\n\n return { files, addFiles, removeFile, editFile, retryFile, upload, progress, isUploading, isDone }\n}\n","/**\n * ImageEditor — in-picker, pre-upload image editor.\n *\n * Interaction model: the crop box is FIXED in the center and only resizable\n * (drag a corner — it grows/shrinks symmetrically). The user positions the\n * IMAGE under it by dragging to pan and scrolling to zoom (zoom toward the\n * cursor). Plus rotate 90° / flip / circular crop / output resize.\n *\n * Hand-rolled (no crop library): the image is transformed by translate+scale in\n * the viewport; on Apply the crop box is inverse-mapped to source pixels and\n * editImage produces the final File. Rotation/flip are baked into a working\n * image first so the crop math stays axis-aligned.\n */\n\nimport { useCallback, useEffect, useRef, useState } from 'react'\nimport type { CSSProperties, PointerEvent as RPointerEvent } from 'react'\nimport { editImage } from './editImage.js'\n\nconst clampNum = (n: number, min: number, max: number) => Math.min(max, Math.max(min, n))\nconst MIN_CROP = 48 // px, in viewport space\nconst MAX_ZOOM = 8 // relative to the fit scale\n\nconst ASPECTS: Array<{ label: string; value: number | undefined }> = [\n { label: 'Free', value: undefined },\n { label: '1:1', value: 1 },\n { label: '4:3', value: 4 / 3 },\n { label: '16:9', value: 16 / 9 },\n { label: '3:2', value: 3 / 2 },\n]\n\ntype Corner = 'nw' | 'ne' | 'sw' | 'se'\nconst HANDLES: Array<{ id: Corner; pos: CSSProperties }> = [\n { id: 'nw', pos: { left: -7, top: -7, cursor: 'nwse-resize' } },\n { id: 'ne', pos: { right: -7, top: -7, cursor: 'nesw-resize' } },\n { id: 'sw', pos: { left: -7, bottom: -7, cursor: 'nesw-resize' } },\n { id: 'se', pos: { right: -7, bottom: -7, cursor: 'nwse-resize' } },\n]\n\nexport type ImageEditorProps = {\n /** The image File to edit. */\n file: File\n /** Called with the edited File when the user clicks Apply. */\n onApply: (edited: File) => void\n /** Called when the user cancels editing. */\n onCancel: () => void\n /** Output MIME type. Defaults to the source type (when re-encodable) else png. */\n outputType?: string\n /** Quality 0–1 for lossy output. Default 0.92. */\n quality?: number\n /** Default \"max longest side\" for the resize control. Default 1920. */\n maxDimensionDefault?: number\n className?: string\n style?: CSSProperties\n}\n\nexport function ImageEditor({\n file,\n onApply,\n onCancel,\n outputType,\n quality,\n maxDimensionDefault = 1920,\n className,\n style,\n}: ImageEditorProps) {\n const [rotation, setRotation] = useState(0)\n const [flip, setFlip] = useState({ horizontal: false, vertical: false })\n const [aspect, setAspect] = useState<number | undefined>(undefined)\n const [cropShape, setCropShape] = useState<'rect' | 'round'>('rect')\n const [resizeEnabled, setResizeEnabled] = useState(false)\n const [maxDimension, setMaxDimension] = useState(maxDimensionDefault)\n const [busy, setBusy] = useState(false)\n const [error, setError] = useState<string | null>(null)\n\n // Working image = original with rotation/flip baked in (axis-aligned).\n const [workingFile, setWorkingFile] = useState<File>(file)\n useEffect(() => {\n let cancelled = false\n if (rotation === 0 && !flip.horizontal && !flip.vertical) {\n setWorkingFile(file)\n return\n }\n editImage(file, { rotation, flip })\n .then((f) => {\n if (!cancelled) setWorkingFile(f)\n })\n .catch(() => {\n if (!cancelled) setError('Failed to render the rotated preview')\n })\n return () => {\n cancelled = true\n }\n }, [file, rotation, flip])\n\n // Object URL for the working image (effect-based for StrictMode safety).\n const [workingUrl, setWorkingUrl] = useState('')\n useEffect(() => {\n const url = URL.createObjectURL(workingFile)\n setWorkingUrl(url)\n return () => URL.revokeObjectURL(url)\n }, [workingFile])\n\n const viewportRef = useRef<HTMLDivElement>(null)\n const [vp, setVp] = useState({ w: 0, h: 0 }) // viewport size, px\n const [nat, setNat] = useState({ w: 0, h: 0 }) // working image natural size, px\n // Image transform: viewportPoint = (x,y) + scale * naturalPoint, origin 0,0.\n const [view, setView] = useState({ scale: 1, x: 0, y: 0 })\n const [crop, setCrop] = useState({ w: 0, h: 0 }) // centered crop box, viewport px\n\n const ready = nat.w > 0 && vp.w > 0\n\n // Measure the viewport.\n useEffect(() => {\n const el = viewportRef.current\n if (!el || typeof ResizeObserver === 'undefined') return\n const measure = () => {\n const r = el.getBoundingClientRect()\n setVp({ w: r.width, h: r.height })\n }\n measure()\n const ro = new ResizeObserver(measure)\n ro.observe(el)\n return () => ro.disconnect()\n }, [])\n\n // Clamp a desired (scale, x, y) for the given crop: image must cover the crop,\n // and scale stays within [coverMin, fit*MAX_ZOOM].\n const normalize = useCallback(\n (scaleRaw: number, xRaw: number, yRaw: number, cw: number, ch: number) => {\n if (!nat.w || !vp.w) return { scale: scaleRaw, x: xRaw, y: yRaw }\n const fitS = Math.min(vp.w / nat.w, vp.h / nat.h)\n const coverMin = Math.max(cw / nat.w, ch / nat.h)\n const scale = clampNum(scaleRaw, coverMin, Math.max(fitS, coverMin) * MAX_ZOOM)\n const cbX = (vp.w - cw) / 2\n const cbY = (vp.h - ch) / 2\n const x = clampNum(xRaw, cbX + cw - scale * nat.w, cbX)\n const y = clampNum(yRaw, cbY + ch - scale * nat.h, cbY)\n return { scale, x, y }\n },\n [nat, vp],\n )\n\n // Initialize/reset view + crop when the image or viewport changes.\n useEffect(() => {\n if (!nat.w || !vp.w) return\n const fitS = Math.min(vp.w / nat.w, vp.h / nat.h)\n const baseline = Math.min(vp.w, vp.h) * 0.7\n let cw = baseline\n let ch = baseline\n if (cropShape === 'round') {\n cw = ch = baseline\n } else if (aspect) {\n if (aspect >= 1) ch = baseline / aspect\n else cw = baseline * aspect\n }\n cw = clampNum(cw, MIN_CROP, vp.w)\n ch = clampNum(ch, MIN_CROP, vp.h)\n setCrop({ w: cw, h: ch })\n setView(normalize(fitS, (vp.w - fitS * nat.w) / 2, (vp.h - fitS * nat.h) / 2, cw, ch))\n // Only re-init on a new image / viewport resize.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [nat, vp])\n\n // Adjust the crop to a new aspect/shape (keep the current view).\n useEffect(() => {\n if (!nat.w || !vp.w) return\n let cw = crop.w\n let ch = crop.h\n if (cropShape === 'round') {\n cw = ch = Math.min(cw, ch)\n } else if (aspect) {\n ch = cw / aspect\n if (ch > vp.h) {\n ch = vp.h\n cw = ch * aspect\n }\n }\n cw = clampNum(cw, MIN_CROP, vp.w)\n ch = clampNum(ch, MIN_CROP, vp.h)\n setCrop({ w: cw, h: ch })\n setView((v) => normalize(v.scale, v.x, v.y, cw, ch))\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [aspect, cropShape])\n\n const onImageLoad = useCallback((e: React.SyntheticEvent<HTMLImageElement>) => {\n setNat({ w: e.currentTarget.naturalWidth, h: e.currentTarget.naturalHeight })\n }, [])\n\n // Wheel zoom toward the cursor (native non-passive listener to preventDefault).\n useEffect(() => {\n const el = viewportRef.current\n if (!el) return\n const onWheel = (e: WheelEvent) => {\n e.preventDefault()\n const rect = el.getBoundingClientRect()\n const fx = e.clientX - rect.left\n const fy = e.clientY - rect.top\n const factor = Math.exp(-e.deltaY * 0.0015)\n setView((v) => {\n const next = normalize(v.scale * factor, v.x, v.y, crop.w, crop.h)\n const ratio = next.scale / v.scale\n // keep the cursor point fixed, then re-clamp pan\n return normalize(next.scale, fx - ratio * (fx - v.x), fy - ratio * (fy - v.y), crop.w, crop.h)\n })\n }\n el.addEventListener('wheel', onWheel, { passive: false })\n return () => el.removeEventListener('wheel', onWheel)\n }, [normalize, crop.w, crop.h])\n\n // Pointer drag: pan the image, or resize the crop from a corner handle.\n const dragRef = useRef<\n | { mode: 'pan'; downX: number; downY: number; startX: number; startY: number }\n | { mode: 'resize'; corner: Corner }\n | null\n >(null)\n\n const onPointerDown = useCallback(\n (e: RPointerEvent<HTMLDivElement>) => {\n if (!ready) return\n const handle = (e.target as HTMLElement).dataset.handle as Corner | undefined\n e.currentTarget.setPointerCapture(e.pointerId)\n if (handle) {\n dragRef.current = { mode: 'resize', corner: handle }\n } else {\n dragRef.current = { mode: 'pan', downX: e.clientX, downY: e.clientY, startX: view.x, startY: view.y }\n }\n },\n [ready, view.x, view.y],\n )\n\n const onPointerMove = useCallback(\n (e: RPointerEvent<HTMLDivElement>) => {\n const d = dragRef.current\n if (!d) return\n const rect = e.currentTarget.getBoundingClientRect()\n if (d.mode === 'pan') {\n setView((v) => normalize(v.scale, d.startX + (e.clientX - d.downX), d.startY + (e.clientY - d.downY), crop.w, crop.h))\n } else {\n const hx = Math.abs(e.clientX - rect.left - rect.width / 2)\n const hy = Math.abs(e.clientY - rect.top - rect.height / 2)\n let cw: number\n let ch: number\n if (cropShape === 'round') {\n cw = ch = 2 * Math.max(hx, hy)\n } else if (aspect) {\n cw = Math.max(2 * hx, 2 * hy * aspect)\n ch = cw / aspect\n } else {\n cw = 2 * hx\n ch = 2 * hy\n }\n cw = clampNum(cw, MIN_CROP, rect.width)\n ch = clampNum(ch, MIN_CROP, rect.height)\n if (cropShape === 'round') cw = ch = Math.min(cw, ch)\n setCrop({ w: cw, h: ch })\n setView((v) => normalize(v.scale, v.x, v.y, cw, ch))\n }\n },\n [normalize, crop.w, crop.h, cropShape, aspect],\n )\n\n const onPointerUp = useCallback((e: RPointerEvent<HTMLDivElement>) => {\n dragRef.current = null\n if (e.currentTarget.hasPointerCapture(e.pointerId)) e.currentTarget.releasePointerCapture(e.pointerId)\n }, [])\n\n const handleApply = useCallback(async () => {\n if (!ready) {\n onApply(workingFile)\n return\n }\n setBusy(true)\n setError(null)\n try {\n const cbX = (vp.w - crop.w) / 2\n const cbY = (vp.h - crop.h) / 2\n // Inverse of viewportPoint = view + scale*natural.\n const region = {\n x: (cbX - view.x) / view.scale,\n y: (cbY - view.y) / view.scale,\n width: crop.w / view.scale,\n height: crop.h / view.scale,\n }\n const edited = await editImage(workingFile, {\n crop: region,\n circle: cropShape === 'round',\n maxDimension: resizeEnabled ? maxDimension : undefined,\n outputType,\n quality,\n })\n onApply(edited)\n } catch (e) {\n setError(e instanceof Error ? e.message : 'Failed to apply edits')\n } finally {\n setBusy(false)\n }\n }, [ready, vp, crop, view, workingFile, cropShape, resizeEnabled, maxDimension, outputType, quality, onApply])\n\n const cbX = (vp.w - crop.w) / 2\n const cbY = (vp.h - crop.h) / 2\n\n return (\n <div\n className={['uploader-editor', className ?? ''].filter(Boolean).join(' ')}\n style={style}\n data-testid=\"image-editor\"\n >\n <div\n ref={viewportRef}\n className=\"uploader-editor-canvas\"\n onPointerDown={onPointerDown}\n onPointerMove={onPointerMove}\n onPointerUp={onPointerUp}\n onPointerCancel={onPointerUp}\n >\n {workingUrl && (\n /* eslint-disable-next-line @next/next/no-img-element */\n <img\n src={workingUrl}\n alt=\"Edit preview\"\n onLoad={onImageLoad}\n draggable={false}\n className=\"uploader-editor-image\"\n style={{\n width: nat.w || undefined,\n height: nat.h || undefined,\n transform: `translate(${view.x}px, ${view.y}px) scale(${view.scale})`,\n opacity: ready ? 1 : 0,\n }}\n />\n )}\n {ready && (\n <div\n className={`uploader-editor-cropbox${cropShape === 'round' ? ' uploader-editor-cropbox-round' : ''}`}\n style={{ left: cbX, top: cbY, width: crop.w, height: crop.h }}\n >\n {HANDLES.map((h) => (\n <span key={h.id} data-handle={h.id} className=\"uploader-editor-handle\" style={h.pos} />\n ))}\n </div>\n )}\n </div>\n <p className=\"uploader-editor-hint\">Drag the image to move it · scroll to zoom · drag a corner to resize the crop.</p>\n\n <div className=\"uploader-editor-controls\">\n {/* Rotate / flip */}\n <div className=\"uploader-editor-row\">\n <button\n type=\"button\"\n className=\"uploader-editor-tool\"\n aria-label=\"Rotate left\"\n onClick={() => setRotation((r) => (r - 90 + 360) % 360)}\n >\n ⟲\n </button>\n <button\n type=\"button\"\n className=\"uploader-editor-tool\"\n aria-label=\"Rotate right\"\n onClick={() => setRotation((r) => (r + 90) % 360)}\n >\n ⟳\n </button>\n <button\n type=\"button\"\n className={`uploader-editor-tool${flip.horizontal ? ' uploader-editor-tool-active' : ''}`}\n aria-pressed={flip.horizontal}\n aria-label=\"Flip horizontal\"\n onClick={() => setFlip((f) => ({ ...f, horizontal: !f.horizontal }))}\n >\n ⇋\n </button>\n <button\n type=\"button\"\n className={`uploader-editor-tool${flip.vertical ? ' uploader-editor-tool-active' : ''}`}\n aria-pressed={flip.vertical}\n aria-label=\"Flip vertical\"\n onClick={() => setFlip((f) => ({ ...f, vertical: !f.vertical }))}\n >\n ⇅\n </button>\n </div>\n\n {/* Shape */}\n <div className=\"uploader-editor-row\">\n <span className=\"uploader-editor-label\">Shape</span>\n <button\n type=\"button\"\n className={`uploader-editor-chip${cropShape === 'rect' ? ' uploader-editor-chip-active' : ''}`}\n onClick={() => setCropShape('rect')}\n >\n Rectangle\n </button>\n <button\n type=\"button\"\n className={`uploader-editor-chip${cropShape === 'round' ? ' uploader-editor-chip-active' : ''}`}\n onClick={() => {\n setCropShape('round')\n setAspect(1)\n }}\n >\n Circle\n </button>\n </div>\n\n {/* Aspect ratio (locked to 1:1 for circles) */}\n <div className=\"uploader-editor-row\">\n <span className=\"uploader-editor-label\">Ratio</span>\n {ASPECTS.map((a) => (\n <button\n key={a.label}\n type=\"button\"\n disabled={cropShape === 'round'}\n className={`uploader-editor-chip${\n cropShape === 'rect' && aspect === a.value ? ' uploader-editor-chip-active' : ''\n }`}\n onClick={() => setAspect(a.value)}\n >\n {a.label}\n </button>\n ))}\n </div>\n\n {/* Resize */}\n <div className=\"uploader-editor-row\">\n <label className=\"uploader-editor-label\">\n <input\n type=\"checkbox\"\n checked={resizeEnabled}\n onChange={(e) => setResizeEnabled(e.target.checked)}\n />{' '}\n Resize to\n </label>\n <input\n type=\"number\"\n min={16}\n value={maxDimension}\n disabled={!resizeEnabled}\n aria-label=\"Maximum longest side in pixels\"\n className=\"uploader-editor-number\"\n onChange={(e) => setMaxDimension(Number(e.target.value))}\n />\n <span className=\"uploader-editor-label\">px (longest side)</span>\n </div>\n\n {error && (\n <div className=\"uploader-editor-error\" role=\"alert\">\n {error}\n </div>\n )}\n </div>\n\n <div className=\"uploader-editor-actions\">\n <button type=\"button\" className=\"uploader-btn-cancel\" onClick={onCancel} disabled={busy}>\n Cancel\n </button>\n <button type=\"button\" className=\"uploader-btn-upload\" onClick={handleApply} disabled={busy}>\n {busy ? 'Applying…' : 'Apply'}\n </button>\n </div>\n </div>\n )\n}\n","/**\n * editImage — bake pre-upload edits (rotate / flip / crop / resize) into an\n * image File using a <canvas>, returning a NEW File. Browser-only.\n *\n * The crop rectangle is in the pixel space of the rotated+flipped image (the\n * order is: rotate, then flip, then crop, then resize). ImageEditor bakes\n * rotation/flip into a working image first and passes a crop with rotation=0, so\n * the crop coordinates come straight from the on-screen selection.\n */\n\nexport type Flip = { horizontal: boolean; vertical: boolean }\nexport type CropArea = { x: number; y: number; width: number; height: number }\n\nexport type EditImageOptions = {\n /** Clockwise rotation in degrees. */\n rotation?: number\n /** Mirror horizontally / vertically. */\n flip?: Flip\n /** Crop rectangle in the rotated+flipped image's natural pixels. */\n crop?: CropArea\n /** If set, scale the result down so its longest side is at most this many pixels. */\n maxDimension?: number\n /** Mask the result to a circle/ellipse (transparent corners). Forces PNG output. */\n circle?: boolean\n /** Output MIME type. Defaults to the source type when re-encodable, else image/png. */\n outputType?: string\n /** Quality 0–1 for lossy formats (jpeg/webp). Default 0.92. */\n quality?: number\n /** Override the output filename. Defaults to the source name with a matching extension. */\n fileName?: string\n}\n\nconst ENCODABLE = new Set(['image/jpeg', 'image/png', 'image/webp'])\n\n/** Map an output MIME type to a file extension. */\nexport function mimeToExtension(mime: string): string {\n switch (mime) {\n case 'image/jpeg':\n return 'jpg'\n case 'image/png':\n return 'png'\n case 'image/webp':\n return 'webp'\n default:\n return 'png'\n }\n}\n\n/** Choose the output MIME: caller override, else the source type if re-encodable, else png. */\nexport function pickOutputType(sourceType: string, override?: string): string {\n if (override) return override\n return ENCODABLE.has(sourceType) ? sourceType : 'image/png'\n}\n\n/** Size of the axis-aligned bounding box after rotating a w×h box by `deg` degrees. */\nexport function rotatedBoundingBox(\n width: number,\n height: number,\n deg: number,\n): { width: number; height: number } {\n const rad = (deg * Math.PI) / 180\n return {\n width: Math.abs(Math.cos(rad) * width) + Math.abs(Math.sin(rad) * height),\n height: Math.abs(Math.sin(rad) * width) + Math.abs(Math.cos(rad) * height),\n }\n}\n\n/** Scale factor to fit (w,h) within `maxDimension` on the longest side; never upscales (≤1). */\nexport function fitScale(width: number, height: number, maxDimension?: number): number {\n if (!maxDimension) return 1\n const longest = Math.max(width, height)\n return longest > maxDimension ? maxDimension / longest : 1\n}\n\n/** Replace a filename's extension (keeps the base name). */\nexport function withExtension(name: string, ext: string): string {\n const dot = name.lastIndexOf('.')\n const base = dot > 0 ? name.slice(0, dot) : name\n return `${base}.${ext}`\n}\n\nfunction loadImage(url: string): Promise<HTMLImageElement> {\n return new Promise((resolve, reject) => {\n const img = new Image()\n img.onload = () => resolve(img)\n img.onerror = () => reject(new Error('Failed to load image for editing'))\n img.src = url\n })\n}\n\nexport async function editImage(file: File, opts: EditImageOptions = {}): Promise<File> {\n const {\n rotation = 0,\n flip = { horizontal: false, vertical: false },\n crop,\n maxDimension,\n } = opts\n\n const url = URL.createObjectURL(file)\n try {\n const image = await loadImage(url)\n const iw = image.naturalWidth || image.width\n const ih = image.naturalHeight || image.height\n\n // 1) Draw the rotated + flipped image onto a bounding-box canvas.\n const box = rotatedBoundingBox(iw, ih, rotation)\n const boxCanvas = document.createElement('canvas')\n boxCanvas.width = Math.max(1, Math.round(box.width))\n boxCanvas.height = Math.max(1, Math.round(box.height))\n const boxCtx = boxCanvas.getContext('2d')\n if (!boxCtx) throw new Error('Canvas 2D context unavailable')\n boxCtx.translate(boxCanvas.width / 2, boxCanvas.height / 2)\n boxCtx.rotate((rotation * Math.PI) / 180)\n boxCtx.scale(flip.horizontal ? -1 : 1, flip.vertical ? -1 : 1)\n boxCtx.drawImage(image, -iw / 2, -ih / 2)\n\n // 2) Crop (default: the whole bounding box).\n const region: CropArea = crop ?? {\n x: 0,\n y: 0,\n width: boxCanvas.width,\n height: boxCanvas.height,\n }\n const scale = fitScale(region.width, region.height, maxDimension)\n const outW = Math.max(1, Math.round(region.width * scale))\n const outH = Math.max(1, Math.round(region.height * scale))\n\n const outCanvas = document.createElement('canvas')\n outCanvas.width = outW\n outCanvas.height = outH\n const outCtx = outCanvas.getContext('2d')\n if (!outCtx) throw new Error('Canvas 2D context unavailable')\n // Circular crop: clip to the inscribed ellipse so the corners stay transparent.\n if (opts.circle) {\n outCtx.beginPath()\n outCtx.ellipse(outW / 2, outH / 2, outW / 2, outH / 2, 0, 0, Math.PI * 2)\n outCtx.clip()\n }\n outCtx.drawImage(boxCanvas, region.x, region.y, region.width, region.height, 0, 0, outW, outH)\n\n // 3) Encode. A circular crop needs an alpha channel, so always PNG.\n const outputType = opts.circle ? 'image/png' : pickOutputType(file.type, opts.outputType)\n const quality = opts.quality ?? 0.92\n const blob: Blob = await new Promise((resolve, reject) => {\n outCanvas.toBlob(\n (b) => (b ? resolve(b) : reject(new Error('Canvas toBlob returned null'))),\n outputType,\n quality,\n )\n })\n\n const name = opts.fileName ?? withExtension(file.name, mimeToExtension(outputType))\n return new File([blob], name, { type: outputType, lastModified: Date.now() })\n } finally {\n URL.revokeObjectURL(url)\n }\n}\n","/**\n * Dropzone — shared drag-and-drop zone primitive for the SDK picker.\n *\n * Renders the upload icon swatch, headline, \"or\" divider, a row of source\n * buttons (Browse files + optional Take photo), and file-type hint text.\n * Drag-over state is reflected via the data-drag-over attribute so parent\n * components can apply accent border/fill via CSS without any JS color logic.\n *\n * All colors resolve from --uploader-* CSS variables — no hard-coded brand\n * colors. The Browse files button receives a glow matching --uploader-accent-ring\n * so it inherits whatever accent the host app sets.\n *\n * Used by PickerOverlay (#203) and DropPane (#204).\n *\n * @dsCard Dropzone\n */\n\nimport React, { useRef, useCallback } from 'react'\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport type DropzoneProps = {\n /** Called when the user drops files or clicks Browse and selects files. */\n onFiles: (files: FileList) => void\n /** Whether a drag is currently in progress over this zone. */\n isDragOver: boolean\n /** Called when a drag enters the zone. */\n onDragEnter: (e: React.DragEvent) => void\n /** Called when a drag leaves the zone. */\n onDragLeave: (e: React.DragEvent) => void\n /** Called on each dragover event (to prevent browser default). */\n onDragOver: (e: React.DragEvent) => void\n /** Called when files are dropped onto the zone. */\n onDrop: (e: React.DragEvent) => void\n /** Accept string forwarded to the hidden <input>. E.g. \"image/*,.pdf\" */\n accept?: string\n /** Whether to allow multiple file selection. Default: true. */\n multiple?: boolean\n /**\n * When provided, renders a \"Take photo\" button beside \"Browse files\" that\n * invokes this callback (the parent swaps in the camera capture view).\n * Omit to hide the camera option entirely.\n */\n onCamera?: () => void\n /**\n * Hint text shown below the Browse button (e.g. \"PNG, JPG, PDF · up to 50 MB\").\n * If omitted, no hint is rendered.\n */\n hint?: string\n /** Additional CSS class(es) on the zone root. */\n className?: string\n /** Inline styles on the zone root. */\n style?: React.CSSProperties\n}\n\n// ─── Component ────────────────────────────────────────────────────────────────\n\nexport function Dropzone({\n onFiles,\n isDragOver,\n onDragEnter,\n onDragLeave,\n onDragOver,\n onDrop,\n accept,\n multiple = true,\n onCamera,\n hint,\n className,\n style,\n}: DropzoneProps) {\n const inputRef = useRef<HTMLInputElement>(null)\n\n const openFileDialog = useCallback(() => inputRef.current?.click(), [])\n\n const handleInputChange = useCallback(\n (e: React.ChangeEvent<HTMLInputElement>) => {\n if (e.target.files && e.target.files.length > 0) {\n onFiles(e.target.files)\n // Reset so the same file can be re-selected after removal\n e.target.value = ''\n }\n },\n [onFiles],\n )\n\n return (\n <div\n className={['uploader-dropzone', className ?? ''].filter(Boolean).join(' ')}\n data-drag-over={isDragOver ? 'true' : undefined}\n role=\"button\"\n tabIndex={0}\n aria-label=\"Drop files here or click to browse\"\n style={style}\n onDragEnter={onDragEnter}\n onDragLeave={onDragLeave}\n onDragOver={onDragOver}\n onDrop={onDrop}\n onClick={openFileDialog}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault()\n openFileDialog()\n }\n }}\n >\n {/* Upload icon swatch */}\n <div className=\"uploader-dropzone-icon-wrap\" aria-hidden=\"true\">\n <div className=\"uploader-dropzone-icon\" />\n </div>\n\n <p className=\"uploader-dropzone-label\">Drag & drop files here</p>\n <p className=\"uploader-dropzone-sublabel\">or</p>\n\n <div className=\"uploader-dropzone-actions\">\n <button\n type=\"button\"\n className=\"uploader-btn-browse\"\n onClick={(e) => {\n // Stop propagation so the zone's own onClick doesn't fire again\n e.stopPropagation()\n openFileDialog()\n }}\n >\n Browse files\n </button>\n\n {onCamera && (\n <button\n type=\"button\"\n className=\"uploader-btn-camera\"\n onClick={(e) => {\n e.stopPropagation()\n onCamera()\n }}\n >\n <svg\n className=\"uploader-btn-camera-icon\"\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n aria-hidden=\"true\"\n >\n <path\n d=\"M4 8.5A2.5 2.5 0 0 1 6.5 6h1.2a1 1 0 0 0 .83-.45l.74-1.1A1 1 0 0 1 10.1 4h3.8a1 1 0 0 1 .83.45l.74 1.1a1 1 0 0 0 .83.45h1.2A2.5 2.5 0 0 1 20 8.5v8A2.5 2.5 0 0 1 17.5 19h-11A2.5 2.5 0 0 1 4 16.5v-8Z\"\n stroke=\"currentColor\"\n strokeWidth=\"1.6\"\n strokeLinejoin=\"round\"\n />\n <circle cx=\"12\" cy=\"12.5\" r=\"3.2\" stroke=\"currentColor\" strokeWidth=\"1.6\" />\n </svg>\n Take photo\n </button>\n )}\n </div>\n\n {hint && <p className=\"uploader-dropzone-hint\">{hint}</p>}\n\n {/* Hidden file input */}\n <input\n ref={inputRef}\n type=\"file\"\n multiple={multiple}\n accept={accept}\n className=\"uploader-file-input\"\n aria-hidden=\"true\"\n tabIndex={-1}\n onChange={handleInputChange}\n />\n </div>\n )\n}\n","/**\n * CameraCapture — in-picker webcam capture.\n *\n * Requests the device camera via getUserMedia, shows a live preview, and lets\n * the user snap a still. The capture is reviewed (Retake / Use photo) before it\n * leaves the component as a regular image `File`, so it flows into the picker\n * queue identically to a dropped or browsed file — preview, edit, and upload.\n *\n * Front ('user') cameras are mirrored in both the preview and the captured\n * still so the selfie matches what the user sees. The MediaStream is stopped on\n * unmount, on capture (to release the camera indicator while reviewing), and on\n * cancel.\n *\n * All colors resolve from --uploader-* CSS variables — no hard-coded brand\n * colors. Used by PickerOverlay and DropPane.\n *\n * @dsCard CameraCapture\n */\n\nimport React, { useCallback, useEffect, useRef, useState } from 'react'\nimport type { PickerOptions } from './types.js'\n\n// ─── Support detection ──────────────────────────────────────────────────────────\n\n/** True when the current environment can open a camera stream. */\nexport function isCameraSupported(): boolean {\n return (\n typeof navigator !== 'undefined' &&\n !!navigator.mediaDevices &&\n typeof navigator.mediaDevices.getUserMedia === 'function' &&\n typeof document !== 'undefined' &&\n typeof HTMLCanvasElement !== 'undefined'\n )\n}\n\nconst IMAGE_EXT = /\\.(png|jpe?g|gif|webp|bmp|svg|avif|heic|heif)$/i\n\n/** Whether an `accept` list permits image files (and thus camera capture). */\nfunction acceptsImages(accept?: string[]): boolean {\n if (!accept || accept.length === 0) return true\n return accept.some((a) => a.startsWith('image/') || a === 'image/*' || IMAGE_EXT.test(a.trim()))\n}\n\n/**\n * Decide whether the picker should offer the camera source, given the picker\n * options and the current environment. The camera is offered when:\n * - the browser supports getUserMedia, AND\n * - the accept filter permits images, AND\n * - fromSources is unset or explicitly includes 'camera'.\n */\nexport function shouldOfferCamera(options?: PickerOptions): boolean {\n if (!isCameraSupported()) return false\n if (!acceptsImages(options?.accept)) return false\n const sources = options?.fromSources\n if (sources && !sources.includes('camera')) return false\n return true\n}\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport type CameraCaptureProps = {\n /** Called with the captured still when the user confirms \"Use photo\". */\n onCapture: (file: File) => void\n /** Called when the user backs out without capturing. */\n onCancel: () => void\n /** Which camera to prefer. 'user' = front (mirrored), 'environment' = rear. */\n facingMode?: 'user' | 'environment'\n /** Output MIME type for the still. Default 'image/jpeg'. */\n outputType?: string\n /** Quality 0–1 for lossy output. Default 0.92. */\n quality?: number\n /** Base name for the produced file (extension is derived). Default 'camera-photo'. */\n fileNamePrefix?: string\n className?: string\n style?: React.CSSProperties\n}\n\n// ─── Helpers ─────────────────────────────────────────────────────────────────\n\nfunction extForType(mime: string): string {\n switch (mime) {\n case 'image/png':\n return 'png'\n case 'image/webp':\n return 'webp'\n default:\n return 'jpg'\n }\n}\n\n/** Map a getUserMedia rejection to a friendly, actionable message. */\nfunction describeCameraError(err: unknown): string {\n const name = err instanceof DOMException ? err.name : ''\n switch (name) {\n case 'NotAllowedError':\n case 'SecurityError':\n return 'Camera access was blocked. Allow camera permission and try again.'\n case 'NotFoundError':\n case 'OverconstrainedError':\n return 'No camera was found on this device.'\n case 'NotReadableError':\n return 'The camera is already in use by another application.'\n default:\n return 'Could not start the camera.'\n }\n}\n\n// ─── Component ────────────────────────────────────────────────────────────────\n\ntype Status = 'initializing' | 'live' | 'captured' | 'error'\n\nexport function CameraCapture({\n onCapture,\n onCancel,\n facingMode = 'user',\n outputType = 'image/jpeg',\n quality = 0.92,\n fileNamePrefix = 'camera-photo',\n className,\n style,\n}: CameraCaptureProps) {\n const videoRef = useRef<HTMLVideoElement>(null)\n const streamRef = useRef<MediaStream | null>(null)\n const blobRef = useRef<Blob | null>(null)\n\n const [status, setStatus] = useState<Status>('initializing')\n const [error, setError] = useState<string | null>(null)\n const [stillUrl, setStillUrl] = useState('')\n\n const mirror = facingMode === 'user'\n\n const stopStream = useCallback(() => {\n streamRef.current?.getTracks().forEach((t) => t.stop())\n streamRef.current = null\n if (videoRef.current) videoRef.current.srcObject = null\n }, [])\n\n // Acquire the camera stream. Reused on mount and on \"Retake\".\n const startStream = useCallback(async () => {\n setError(null)\n setStatus('initializing')\n if (!isCameraSupported()) {\n setError('Camera capture is not supported in this browser.')\n setStatus('error')\n return\n }\n try {\n const stream = await navigator.mediaDevices.getUserMedia({\n video: { facingMode },\n audio: false,\n })\n // Component may have unmounted while awaiting permission.\n if (videoRef.current == null) {\n stream.getTracks().forEach((t) => t.stop())\n return\n }\n streamRef.current = stream\n videoRef.current.srcObject = stream\n setStatus('live')\n } catch (err) {\n setError(describeCameraError(err))\n setStatus('error')\n }\n }, [facingMode])\n\n // Start on mount / when facing changes; always tear the stream down on cleanup.\n useEffect(() => {\n void startStream()\n return () => {\n stopStream()\n }\n }, [startStream, stopStream])\n\n // Revoke the review object URL when it changes or on unmount.\n useEffect(() => {\n if (!stillUrl) return\n return () => URL.revokeObjectURL(stillUrl)\n }, [stillUrl])\n\n const capture = useCallback(() => {\n const video = videoRef.current\n if (!video) return\n const w = video.videoWidth\n const h = video.videoHeight\n if (!w || !h) {\n setError('The camera is still warming up — try again in a moment.')\n return\n }\n const canvas = document.createElement('canvas')\n canvas.width = w\n canvas.height = h\n const ctx = canvas.getContext('2d')\n if (!ctx) {\n setError('Canvas is unavailable for capturing.')\n return\n }\n if (mirror) {\n ctx.translate(w, 0)\n ctx.scale(-1, 1)\n }\n ctx.drawImage(video, 0, 0, w, h)\n canvas.toBlob(\n (blob) => {\n if (!blob) {\n setError('Failed to capture the photo.')\n return\n }\n blobRef.current = blob\n setStillUrl(URL.createObjectURL(blob))\n setStatus('captured')\n // Release the camera light while the user reviews the still.\n stopStream()\n },\n outputType,\n quality,\n )\n }, [mirror, outputType, quality, stopStream])\n\n const retake = useCallback(() => {\n blobRef.current = null\n setStillUrl('')\n void startStream()\n }, [startStream])\n\n const confirm = useCallback(() => {\n const blob = blobRef.current\n if (!blob) return\n const name = `${fileNamePrefix}-${Date.now()}.${extForType(outputType)}`\n const file = new File([blob], name, { type: outputType, lastModified: Date.now() })\n onCapture(file)\n }, [fileNamePrefix, outputType, onCapture])\n\n return (\n <div\n className={['uploader-camera', className ?? ''].filter(Boolean).join(' ')}\n style={style}\n data-testid=\"camera-capture\"\n >\n <div className=\"uploader-camera-stage\">\n {status === 'error' ? (\n <div className=\"uploader-camera-error\" role=\"alert\">\n {error}\n </div>\n ) : status === 'captured' ? (\n /* eslint-disable-next-line @next/next/no-img-element */\n <img src={stillUrl} alt=\"Captured preview\" className=\"uploader-camera-still\" />\n ) : (\n <video\n ref={videoRef}\n className=\"uploader-camera-video\"\n autoPlay\n playsInline\n muted\n data-mirror={mirror ? 'true' : undefined}\n />\n )}\n\n {status === 'initializing' && (\n <div className=\"uploader-camera-status\" aria-live=\"polite\">\n Starting camera…\n </div>\n )}\n </div>\n\n {status === 'live' && (\n <p className=\"uploader-camera-hint\">Frame your shot, then tap the shutter to capture.</p>\n )}\n\n <div className=\"uploader-camera-actions\">\n {status === 'captured' ? (\n <>\n <button type=\"button\" className=\"uploader-btn-secondary\" onClick={retake}>\n Retake\n </button>\n <button type=\"button\" className=\"uploader-btn-primary\" onClick={confirm}>\n Use photo\n </button>\n </>\n ) : status === 'error' ? (\n <>\n <button type=\"button\" className=\"uploader-btn-secondary\" onClick={onCancel}>\n Back\n </button>\n <button type=\"button\" className=\"uploader-btn-primary\" onClick={() => void startStream()}>\n Try again\n </button>\n </>\n ) : (\n <>\n <button type=\"button\" className=\"uploader-btn-secondary\" onClick={onCancel}>\n Back\n </button>\n <button\n type=\"button\"\n className=\"uploader-camera-shutter\"\n aria-label=\"Capture photo\"\n disabled={status !== 'live'}\n onClick={capture}\n >\n <span className=\"uploader-camera-shutter-ring\" aria-hidden=\"true\" />\n </button>\n {/* Invisible twin of the Back button — balances the row so the\n shutter lands dead-center regardless of the label's width. */}\n <div className=\"uploader-camera-actions-spacer\" aria-hidden=\"true\">\n <span className=\"uploader-btn-secondary\">Back</span>\n </div>\n </>\n )}\n </div>\n </div>\n )\n}\n","/**\n * ProgressBar — standalone progress-bar primitive for the SDK picker.\n *\n * Renders a thin filled track using only --uploader-* CSS variables so the\n * color automatically follows the host app's theme override. No external\n * dependencies; no hard-coded brand colors.\n *\n * Usage:\n * <ProgressBar value={62} label=\"invoice.pdf upload progress\" />\n * <ProgressBar value={72} size=\"lg\" label=\"Overall upload progress\" />\n */\n\nimport React from 'react'\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport type ProgressBarSize = 'sm' | 'lg'\n\nexport type ProgressBarProps = {\n /** Progress value 0–100. */\n value: number\n /**\n * Accessible label for screen readers (maps to aria-label on the track).\n * Required — callers must describe what this bar tracks.\n */\n label: string\n /** Visual size variant. 'sm' = 5px (per-file), 'lg' = 8px (aggregate). Default: 'sm'. */\n size?: ProgressBarSize\n /** Additional CSS class(es) on the track element. */\n className?: string\n /** Inline styles on the track element. */\n style?: React.CSSProperties\n}\n\n// ─── Component ────────────────────────────────────────────────────────────────\n\nexport function ProgressBar({ value, label, size = 'sm', className, style }: ProgressBarProps) {\n const clamped = Math.max(0, Math.min(100, value))\n\n return (\n <div\n role=\"progressbar\"\n aria-valuenow={clamped}\n aria-valuemin={0}\n aria-valuemax={100}\n aria-label={label}\n data-size={size}\n className={['uploader-progress-bar', className ?? ''].filter(Boolean).join(' ')}\n style={style}\n >\n <div\n className=\"uploader-progress-bar-fill\"\n style={{ width: `${clamped}%` }}\n />\n </div>\n )\n}\n","/**\n * FileQueueRow — shared file-queue row primitive for the SDK picker.\n *\n * Renders a single file entry: 38×38 thumbnail swatch, file name, upload\n * status/percentage, thin progress bar, and action slots (remove, retry, edit).\n * All colors resolve from --uploader-* CSS variables — no hard-coded brand\n * colors — so the component adapts to any host-app theme.\n *\n * Used by PickerOverlay (#203) and DropPane (#204). Callers supply file state\n * and event handlers; this component is display-only with no internal state.\n */\n\nimport { ProgressBar } from './ProgressBar.js'\nimport type { PickerFile } from './types.js'\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport type FileQueueRowProps = {\n /** The file entry to display. */\n entry: PickerFile\n /** Called when the user clicks the remove (×) button. */\n onRemove: (id: string) => void\n /** Called when the user clicks the retry button (shown on error). */\n onRetry: (id: string) => void\n /**\n * Called when the user clicks the Edit button (shown for image files in\n * pending/error state). If not provided, the Edit button is never rendered.\n */\n onEdit?: (id: string) => void\n}\n\n// ─── Helpers ─────────────────────────────────────────────────────────────────\n\nfunction statusLabel(entry: PickerFile): string {\n switch (entry.state) {\n case 'uploading':\n return entry.progress != null ? `${entry.progress}%` : 'Uploading…'\n case 'done':\n return 'Done'\n case 'error':\n return entry.error ?? 'Failed'\n default:\n return 'Ready'\n }\n}\n\n// ─── Component ────────────────────────────────────────────────────────────────\n\nexport function FileQueueRow({ entry, onRemove, onRetry, onEdit }: FileQueueRowProps) {\n const { id, file, state, progress, previewUrl } = entry\n const isUploading = state === 'uploading'\n\n return (\n <li className={`uploader-file-item uploader-file-${state}`}>\n {/* Thumbnail */}\n {previewUrl ? (\n <img\n src={previewUrl}\n alt=\"\"\n aria-hidden=\"true\"\n className=\"uploader-file-thumb\"\n />\n ) : (\n <div className=\"uploader-file-thumb-placeholder\" aria-hidden=\"true\" />\n )}\n\n {/* File info + progress */}\n <div className=\"uploader-file-info\">\n <div className=\"uploader-file-info-row\">\n <span className=\"uploader-file-name\">{file.name}</span>\n <span\n className=\"uploader-file-status\"\n data-state={state}\n role={state === 'error' ? 'alert' : undefined}\n >\n {statusLabel(entry)}\n </span>\n </div>\n <ProgressBar\n value={isUploading && progress != null ? progress : state === 'done' ? 100 : 0}\n label={`${file.name} upload progress`}\n />\n </div>\n\n {/* Done badge */}\n {state === 'done' && (\n <div className=\"uploader-file-done-badge\" aria-hidden=\"true\">\n {/* Checkmark rendered via SVG to avoid icon-library dependency */}\n <svg width=\"9\" height=\"7\" viewBox=\"0 0 9 7\" fill=\"none\" aria-hidden=\"true\">\n <path\n d=\"M1 3.5L3.5 6L8 1\"\n stroke=\"currentColor\"\n strokeWidth=\"1.8\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n </svg>\n </div>\n )}\n\n {/* Remove / retry / edit actions */}\n {!isUploading && (\n <div className=\"uploader-file-actions\">\n {onEdit && previewUrl && (state === 'pending' || state === 'error') && (\n <button\n type=\"button\"\n className=\"uploader-btn-edit\"\n aria-label={`Edit ${file.name}`}\n onClick={() => onEdit(id)}\n >\n Edit\n </button>\n )}\n {state === 'error' && (\n <button\n type=\"button\"\n className=\"uploader-btn-retry\"\n aria-label={`Retry uploading ${file.name}`}\n onClick={() => onRetry(id)}\n >\n Retry\n </button>\n )}\n <button\n type=\"button\"\n className=\"uploader-btn-remove\"\n aria-label={`Remove ${file.name}`}\n onClick={() => onRemove(id)}\n >\n ×\n </button>\n </div>\n )}\n </li>\n )\n}\n","/**\n * Theming — map a plain token object to scoped CSS custom properties.\n *\n * The SDK is styled entirely from `--uploader-*` CSS custom properties (see\n * styles.css). A host React app can override them in its own stylesheet, but\n * that overrides every instance on the page and requires writing CSS.\n *\n * The `theme` prop on PickerOverlay / DropPane lets a host pass those tokens as\n * a plain object instead. `themeToVars` converts the object into inline CSS\n * variables applied to the component's own root element, so the override is\n * **scoped to that instance** — two pickers on one page can carry different\n * themes, and nothing leaks to the rest of the app.\n *\n * Only the keys you set are emitted; everything else falls back to the shipped\n * defaults. Fully backward compatible — omit `theme` and behaviour is unchanged.\n *\n * @example\n * <PickerOverlay\n * theme={{ accent: '#4f46e5', accentSoft: '#eef2ff', radius: '12px' }}\n * apikey=\"pk_...\"\n * open={open}\n * onClose={close}\n * />\n */\n\nimport type React from 'react'\n\n/**\n * Host-overridable design tokens. Each maps to one `--uploader-*` CSS variable.\n * All optional — set only what you want to change. Values are raw CSS strings\n * (e.g. '#4f46e5', 'rgba(79,70,229,.28)', '12px', '0.2s ease').\n */\nexport type UploaderTheme = {\n /** Primary brand color. → `--uploader-accent` */\n accent?: string\n /** Hover/active shade of the accent. → `--uploader-accent-hover` */\n accentHover?: string\n /** Tinted fill for icon swatches and hover backgrounds. → `--uploader-accent-soft` */\n accentSoft?: string\n /** Focus ring / drag-over glow (≈20–30% opacity of accent). → `--uploader-accent-ring` */\n accentRing?: string\n\n /** Corner radius for panels, buttons, inputs. → `--uploader-radius` */\n radius?: string\n /** Base surface / panel background. → `--uploader-bg` */\n background?: string\n /** Secondary surface (rows, wells). → `--uploader-surface` */\n surface?: string\n /** Hover state for secondary surfaces. → `--uploader-surface-hover` */\n surfaceHover?: string\n /** Default border color. → `--uploader-border` */\n border?: string\n /** Subtle/hairline border color. → `--uploader-border-subtle` */\n borderSubtle?: string\n\n /** Primary text color. → `--uploader-text` */\n text?: string\n /** Muted/secondary text. → `--uploader-text-muted` */\n textMuted?: string\n /** Extra-muted text (hints, captions). → `--uploader-text-xmuted` */\n textXMuted?: string\n\n /** Error color. → `--uploader-error` */\n error?: string\n /** Tinted error background. → `--uploader-error-soft` */\n errorSoft?: string\n /** Success color. → `--uploader-success` */\n success?: string\n\n /** Small elevation shadow. → `--uploader-shadow-sm` */\n shadowSm?: string\n /** Default elevation shadow. → `--uploader-shadow` */\n shadow?: string\n /** Large elevation shadow (modal panel). → `--uploader-shadow-lg` */\n shadowLg?: string\n\n /** Font family. Defaults to `inherit`. → `--uploader-font` */\n font?: string\n /** Transition timing. → `--uploader-transition` */\n transition?: string\n\n /** z-index of the modal backdrop. → `--uploader-z-backdrop` */\n zBackdrop?: number | string\n /** z-index of the modal panel. → `--uploader-z-panel` */\n zPanel?: number | string\n}\n\n/** Token key → CSS custom property name. */\nconst VAR_MAP: Record<keyof UploaderTheme, string> = {\n accent: '--uploader-accent',\n accentHover: '--uploader-accent-hover',\n accentSoft: '--uploader-accent-soft',\n accentRing: '--uploader-accent-ring',\n radius: '--uploader-radius',\n background: '--uploader-bg',\n surface: '--uploader-surface',\n surfaceHover: '--uploader-surface-hover',\n border: '--uploader-border',\n borderSubtle: '--uploader-border-subtle',\n text: '--uploader-text',\n textMuted: '--uploader-text-muted',\n textXMuted: '--uploader-text-xmuted',\n error: '--uploader-error',\n errorSoft: '--uploader-error-soft',\n success: '--uploader-success',\n shadowSm: '--uploader-shadow-sm',\n shadow: '--uploader-shadow',\n shadowLg: '--uploader-shadow-lg',\n font: '--uploader-font',\n transition: '--uploader-transition',\n zBackdrop: '--uploader-z-backdrop',\n zPanel: '--uploader-z-panel',\n}\n\n/**\n * Convert a theme object into a style object of CSS custom properties.\n *\n * Returns an empty object when `theme` is undefined/empty, so it is safe to\n * spread unconditionally. `undefined` token values are skipped (no var emitted),\n * letting the shipped default apply.\n *\n * Merge it ahead of any host `style` so the host can still override per-instance:\n * style={{ ...themeToVars(theme), ...style }}\n */\nexport function themeToVars(theme?: UploaderTheme): React.CSSProperties {\n if (!theme) return {}\n const vars: Record<string, string> = {}\n for (const key of Object.keys(theme) as Array<keyof UploaderTheme>) {\n const value = theme[key]\n if (value === undefined || value === null) continue\n vars[VAR_MAP[key]] = String(value)\n }\n return vars as React.CSSProperties\n}\n","/**\n * DropPane — inline drag-and-drop upload zone.\n *\n * A non-modal file drop target that renders in-place. Suitable for embedding\n * directly in a page (e.g. a form upload field) rather than as a modal overlay.\n *\n * Composes the Dropzone, FileQueueRow, and ProgressBar primitives from the\n * #194 foundation. All colors resolve from --uploader-* CSS variables —\n * no hard-coded brand colors.\n *\n * States:\n * idle — Dropzone shown, queue empty\n * dragging-over — Dropzone fills with accent-soft, label changes to\n * \"Drop to add files\" (handled inside Dropzone via isDragOver)\n * queued — Dropzone collapses; file list + Upload/Cancel shown\n * uploading — File rows show per-file progress; aggregate ProgressBar shown\n * done — Files show done state; onUploadDone fires\n *\n * The host supplies any surrounding form chrome (labels, Skip/Continue buttons)\n * as normal JSX siblings — DropPane does not render those.\n *\n * @dsCard DropPane\n */\n\nimport React, { useState, useCallback } from 'react'\nimport { usePicker } from './usePicker.js'\nimport { Dropzone } from './Dropzone.js'\nimport { CameraCapture, shouldOfferCamera } from './CameraCapture.js'\nimport { FileQueueRow } from './FileQueueRow.js'\nimport { ProgressBar } from './ProgressBar.js'\nimport { themeToVars } from './theme.js'\nimport type { UploaderTheme } from './theme.js'\nimport type { SharedPickerProps } from './types.js'\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport type DropPaneProps = SharedPickerProps & {\n /**\n * Hint text shown below the Browse button in the dropzone.\n * Default: \"or click to browse · max 50 MB\"\n */\n hint?: string\n /**\n * Design tokens to override, scoped to this pane instance. Maps to\n * `--uploader-*` CSS variables applied on the root element — see UploaderTheme.\n * Omit to use the shipped defaults (or page-level CSS-variable overrides).\n */\n theme?: UploaderTheme\n /** Additional CSS class(es) applied to the root element. */\n className?: string\n /** Inline styles applied to the root element. */\n style?: React.CSSProperties\n}\n\n// ─── Component ────────────────────────────────────────────────────────────────\n\nexport function DropPane({\n apikey,\n apiUrl,\n security,\n deliveryProtection,\n allowedOrigins,\n pickerOptions,\n onUploadDone,\n onFileUploadFinished,\n onFileUploadFailed,\n onCancel,\n hint = 'or click to browse · max 50 MB',\n theme,\n className,\n style,\n}: DropPaneProps) {\n const [isDragOver, setIsDragOver] = useState(false)\n const [cameraOpen, setCameraOpen] = useState(false)\n const rootStyle = { ...themeToVars(theme), ...style }\n\n const { files, addFiles, removeFile, retryFile, upload, progress, isUploading, isDone } =\n usePicker({\n apikey,\n apiUrl,\n security,\n deliveryProtection,\n allowedOrigins,\n pickerOptions,\n onUploadDone,\n onFileUploadFinished,\n onFileUploadFailed,\n })\n\n // ── Drag handlers ────────────────────────────────────────────────────────\n const handleDragEnter = useCallback((e: React.DragEvent) => {\n e.preventDefault()\n e.stopPropagation()\n setIsDragOver(true)\n }, [])\n\n const handleDragLeave = useCallback((e: React.DragEvent) => {\n e.preventDefault()\n e.stopPropagation()\n setIsDragOver(false)\n }, [])\n\n const handleDragOver = useCallback((e: React.DragEvent) => {\n e.preventDefault()\n e.stopPropagation()\n }, [])\n\n const handleDrop = useCallback(\n (e: React.DragEvent) => {\n e.preventDefault()\n e.stopPropagation()\n setIsDragOver(false)\n if (e.dataTransfer.files.length > 0) {\n addFiles(e.dataTransfer.files)\n }\n },\n [addFiles],\n )\n\n const hasPending = files.some((f) => f.state === 'pending')\n const pendingCount = files.filter((f) => f.state === 'pending').length\n const accept = pickerOptions?.accept?.join(',')\n const multiple = !pickerOptions?.maxFiles || pickerOptions.maxFiles > 1\n const offerCamera = shouldOfferCamera(pickerOptions)\n\n // Camera capture view replaces the whole pane while active.\n if (cameraOpen) {\n return (\n <div\n className={['uploader-drop-pane', className ?? ''].filter(Boolean).join(' ')}\n data-testid=\"drop-pane\"\n style={rootStyle}\n >\n <CameraCapture\n facingMode={pickerOptions?.cameraFacingMode}\n onCancel={() => setCameraOpen(false)}\n onCapture={(file) => {\n addFiles([file])\n setCameraOpen(false)\n }}\n />\n </div>\n )\n }\n\n return (\n <div\n className={['uploader-drop-pane', className ?? ''].filter(Boolean).join(' ')}\n data-drag-over={isDragOver ? 'true' : undefined}\n data-testid=\"drop-pane\"\n style={rootStyle}\n >\n {/* Dropzone — shown until files are queued */}\n {files.length === 0 && (\n <Dropzone\n onFiles={addFiles}\n isDragOver={isDragOver}\n onDragEnter={handleDragEnter}\n onDragLeave={handleDragLeave}\n onDragOver={handleDragOver}\n onDrop={handleDrop}\n accept={accept}\n multiple={multiple}\n onCamera={offerCamera ? () => setCameraOpen(true) : undefined}\n hint={hint}\n />\n )}\n\n {/* Drag-over overlay on the pane when files already queued */}\n {files.length > 0 && isDragOver && (\n <div\n className=\"uploader-drop-pane-drag-overlay\"\n onDragEnter={handleDragEnter}\n onDragLeave={handleDragLeave}\n onDragOver={handleDragOver}\n onDrop={handleDrop}\n aria-hidden=\"true\"\n >\n <span className=\"uploader-drop-pane-drag-label\">Drop to add files</span>\n </div>\n )}\n\n {/* File queue */}\n {files.length > 0 && (\n <ul\n className=\"uploader-file-list uploader-drop-pane-file-list\"\n role=\"list\"\n aria-label=\"Selected files\"\n aria-live=\"polite\"\n >\n {files.map((entry) => (\n <FileQueueRow\n key={entry.id}\n entry={entry}\n onRemove={removeFile}\n onRetry={retryFile}\n />\n ))}\n </ul>\n )}\n\n {/* Aggregate progress — shown while uploading */}\n {isUploading && (\n <div\n className=\"uploader-aggregate-progress\"\n role=\"status\"\n aria-live=\"polite\"\n >\n <ProgressBar\n value={progress}\n label=\"Overall upload progress\"\n size=\"lg\"\n />\n <span className=\"uploader-aggregate-progress-pct\">{progress}%</span>\n </div>\n )}\n\n {/* Actions — Upload / Cancel (host supplies any surrounding form buttons) */}\n {files.length > 0 && !isDone && (\n <div className=\"uploader-drop-pane-actions\">\n <button\n type=\"button\"\n className=\"uploader-btn-secondary uploader-btn-cancel\"\n disabled={isUploading}\n onClick={onCancel}\n >\n Cancel\n </button>\n {hasPending && (\n <button\n type=\"button\"\n className=\"uploader-btn-primary uploader-btn-upload\"\n disabled={isUploading}\n onClick={() => void upload()}\n >\n {isUploading\n ? 'Uploading…'\n : `Upload${pendingCount > 0 ? ` ${pendingCount} file${pendingCount !== 1 ? 's' : ''}` : ''}`}\n </button>\n )}\n </div>\n )}\n </div>\n )\n}\n"],"mappings":"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","names":["useRef","useState","useCallback","useEffect","useCallback","useRef","useState","flip","crop","quality","quality","useState","flip","useRef","crop","useCallback","cbX","cbY","useRef","useCallback","jsx","jsxs","useCallback","useEffect","useRef","useState","jsx","jsxs","quality","jsx","jsx","jsxs","Fragment","jsx","jsxs","useRef","useState","useEffect","useCallback","useState","useCallback","jsx","jsxs","useState","useCallback"]}
|
|
1
|
+
{"version":3,"sources":["../src/react/PickerOverlay.tsx","../src/react/usePicker.ts","../src/react/ImageEditor.tsx","../src/react/editImage.ts","../src/react/Dropzone.tsx","../src/react/CameraCapture.tsx","../src/react/ProgressBar.tsx","../src/react/FileQueueRow.tsx","../src/react/theme.ts","../src/react/DropPane.tsx"],"sourcesContent":["/**\n * PickerOverlay — modal file picker with drag-and-drop.\n *\n * A full-featured modal picker matching filestack's core UX:\n * - Drag-and-drop dropzone + \"Browse files\" button\n * - Multi-file queue with thumbnails/preview, per-file progress bars\n * - Remove and retry controls per file\n * - Aggregate progress bar\n * - Upload / Cancel action buttons\n * - Keyboard: ESC closes; Tab cycles within the modal (focus trap)\n * - ARIA: role=\"dialog\", aria-modal, labelled header, live regions\n * - Themeable via CSS variables (--uploader-accent, etc.)\n *\n * Opens when `open={true}`. Calls `onClose` when the user dismisses via ESC,\n * the backdrop click, or the Cancel button. `onUploadDone` fires after all\n * selected files have been uploaded.\n *\n * Composes Dropzone, FileQueueRow, and ProgressBar primitives from the #194\n * foundation — no hard-coded brand colors.\n *\n * @dsCard PickerOverlay\n */\n\nimport React, {\n useRef,\n useState,\n useCallback,\n useEffect,\n useId,\n} from 'react'\nimport { usePicker } from './usePicker.js'\nimport { ImageEditor } from './ImageEditor.js'\nimport { Dropzone } from './Dropzone.js'\nimport { CameraCapture, shouldOfferCamera } from './CameraCapture.js'\nimport { FileQueueRow } from './FileQueueRow.js'\nimport { ProgressBar } from './ProgressBar.js'\nimport { themeToVars } from './theme.js'\nimport type { UploaderTheme } from './theme.js'\nimport type { SharedPickerProps } from './types.js'\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport type PickerOverlayProps = SharedPickerProps & {\n /** Whether the modal is open. */\n open: boolean\n /** Called when the modal should close (ESC, backdrop, cancel). */\n onClose: () => void\n /**\n * Design tokens to override, scoped to this picker instance. Maps to\n * `--uploader-*` CSS variables applied on the overlay root — see UploaderTheme.\n * Omit to use the shipped defaults (or page-level CSS-variable overrides).\n */\n theme?: UploaderTheme\n /** Additional CSS class(es) applied to the modal panel. */\n className?: string\n /** Inline styles applied to the modal panel. */\n style?: React.CSSProperties\n}\n\n// ─── Helpers ─────────────────────────────────────────────────────────────────\n\n/** Derive the dialog title from current upload state. */\nfunction dialogTitle(\n isUploading: boolean,\n isDone: boolean,\n totalFiles: number,\n doneCount: number,\n errorCount: number,\n editingId: string | null,\n cameraOpen: boolean,\n): string {\n if (editingId) return 'Edit Image'\n if (cameraOpen) return 'Take a photo'\n if (isDone && errorCount === 0) return 'All files uploaded'\n if (isDone && errorCount > 0) return `${doneCount} of ${totalFiles} uploaded`\n if (isUploading) return `Uploading ${totalFiles} file${totalFiles !== 1 ? 's' : ''}`\n return 'Upload files'\n}\n\n// ─── Component ────────────────────────────────────────────────────────────────\n\nexport function PickerOverlay({\n open,\n onClose,\n apikey,\n apiUrl,\n security,\n deliveryProtection,\n allowedOrigins,\n pickerOptions,\n onUploadDone,\n onFileUploadFinished,\n onFileUploadFailed,\n onCancel,\n theme,\n className,\n style,\n}: PickerOverlayProps) {\n const titleId = useId()\n const panelRef = useRef<HTMLDivElement>(null)\n const [isDragOver, setIsDragOver] = useState(false)\n const [editingId, setEditingId] = useState<string | null>(null)\n const [cameraOpen, setCameraOpen] = useState(false)\n\n const { files, addFiles, removeFile, editFile, retryFile, upload, progress, isUploading, isDone } =\n usePicker({\n apikey,\n apiUrl,\n security,\n deliveryProtection,\n allowedOrigins,\n pickerOptions,\n onUploadDone,\n onFileUploadFinished,\n onFileUploadFailed,\n })\n\n // ── ESC key to close ─────────────────────────────────────────────────────\n useEffect(() => {\n if (!open) return\n const handler = (e: KeyboardEvent) => {\n if (e.key === 'Escape') {\n onCancel?.()\n onClose()\n }\n }\n document.addEventListener('keydown', handler)\n return () => document.removeEventListener('keydown', handler)\n }, [open, onClose, onCancel])\n\n // ── Focus trap ───────────────────────────────────────────────────────────\n // When open, constrain Tab/Shift+Tab to the modal panel.\n useEffect(() => {\n if (!open || !panelRef.current) return\n\n const panel = panelRef.current\n const focusable = panel.querySelectorAll<HTMLElement>(\n 'button, [href], input, select, textarea, [tabindex]:not([tabindex=\"-1\"])',\n )\n if (focusable.length === 0) return\n\n const first = focusable[0]!\n const last = focusable[focusable.length - 1]!\n\n // Move initial focus into the panel\n first.focus()\n\n const trap = (e: KeyboardEvent) => {\n if (e.key !== 'Tab') return\n if (e.shiftKey) {\n if (document.activeElement === first) {\n e.preventDefault()\n last.focus()\n }\n } else {\n if (document.activeElement === last) {\n e.preventDefault()\n first.focus()\n }\n }\n }\n\n document.addEventListener('keydown', trap)\n return () => document.removeEventListener('keydown', trap)\n }, [open, files, editingId, cameraOpen]) // re-run when the list or active view changes\n\n // ── Body scroll lock ─────────────────────────────────────────────────────\n useEffect(() => {\n if (!open) return\n const prev = document.body.style.overflow\n document.body.style.overflow = 'hidden'\n return () => {\n document.body.style.overflow = prev\n }\n }, [open])\n\n // ── Drag handlers ────────────────────────────────────────────────────────\n const handleDragEnter = useCallback((e: React.DragEvent) => {\n e.preventDefault()\n setIsDragOver(true)\n }, [])\n\n const handleDragLeave = useCallback((e: React.DragEvent) => {\n e.preventDefault()\n setIsDragOver(false)\n }, [])\n\n const handleDragOver = useCallback((e: React.DragEvent) => {\n e.preventDefault()\n }, [])\n\n const handleDrop = useCallback(\n (e: React.DragEvent) => {\n e.preventDefault()\n setIsDragOver(false)\n if (e.dataTransfer.files.length > 0) {\n addFiles(e.dataTransfer.files)\n }\n },\n [addFiles],\n )\n\n const handleClose = useCallback(() => {\n onCancel?.()\n onClose()\n }, [onCancel, onClose])\n\n const hasPending = files.some((f) => f.state === 'pending')\n const pendingCount = files.filter((f) => f.state === 'pending').length\n const doneCount = files.filter((f) => f.state === 'done').length\n const errorCount = files.filter((f) => f.state === 'error').length\n const accept = pickerOptions?.accept?.join(',')\n const multiple = !pickerOptions?.maxFiles || pickerOptions.maxFiles > 1\n const editingEntry = files.find((f) => f.id === editingId) ?? null\n const offerCamera = shouldOfferCamera(pickerOptions)\n\n const title = dialogTitle(\n isUploading,\n isDone,\n files.length,\n doneCount,\n errorCount,\n editingId,\n cameraOpen,\n )\n\n if (!open) return null\n\n return (\n /* Backdrop — clicking outside the panel dismisses the modal */\n <div\n className=\"uploader-backdrop\"\n data-testid=\"picker-backdrop\"\n style={themeToVars(theme)}\n onClick={handleClose}\n >\n {/* Modal panel — stops backdrop click propagation */}\n <div\n ref={panelRef}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby={titleId}\n data-drag-over={isDragOver && !editingEntry && files.length === 0 ? 'true' : undefined}\n className={['uploader-picker-panel', className ?? ''].filter(Boolean).join(' ')}\n style={style}\n data-testid=\"picker-panel\"\n onClick={(e) => e.stopPropagation()}\n >\n {/* ── Header ─────────────────────────────────────────────────────── */}\n <div className=\"uploader-picker-header\">\n <h2 id={titleId} className=\"uploader-picker-title\">\n {title}\n </h2>\n <button\n type=\"button\"\n className=\"uploader-btn-close\"\n aria-label=\"Close picker\"\n onClick={handleClose}\n >\n ✕\n </button>\n </div>\n\n {/* ── Image editor view ───────────────────────────────────────────── */}\n {editingEntry ? (\n <ImageEditor\n file={editingEntry.file}\n onCancel={() => setEditingId(null)}\n onApply={(edited) => {\n editFile(editingEntry.id, edited)\n setEditingId(null)\n }}\n />\n ) : cameraOpen ? (\n /* ── Camera capture view ───────────────────────────────────────── */\n <CameraCapture\n facingMode={pickerOptions?.cameraFacingMode}\n onCancel={() => setCameraOpen(false)}\n onCapture={(file) => {\n addFiles([file])\n setCameraOpen(false)\n }}\n />\n ) : isDone ? (\n /* ── Done state ─────────────────────────────────────────────────── */\n <div className=\"uploader-done-body\">\n <p className=\"uploader-done-subtitle\">\n {files.length} file{files.length !== 1 ? 's' : ''} delivered to your bucket\n </p>\n <ul className=\"uploader-file-list uploader-done-list\" role=\"list\" aria-label=\"Uploaded files\">\n {files.map((entry) => (\n <FileQueueRow\n key={entry.id}\n entry={entry}\n onRemove={removeFile}\n onRetry={retryFile}\n />\n ))}\n </ul>\n <button\n type=\"button\"\n className=\"uploader-btn-primary uploader-btn-done-full\"\n onClick={onClose}\n >\n Done\n </button>\n </div>\n ) : (\n /* ── Normal (idle / uploading / error) view ─────────────────────── */\n <>\n {/* Dropzone — hidden once files are queued */}\n {files.length === 0 && (\n <Dropzone\n onFiles={addFiles}\n isDragOver={isDragOver}\n onDragEnter={handleDragEnter}\n onDragLeave={handleDragLeave}\n onDragOver={handleDragOver}\n onDrop={handleDrop}\n accept={accept}\n multiple={multiple}\n onCamera={offerCamera ? () => setCameraOpen(true) : undefined}\n hint=\"PNG, JPG, PDF, ZIP · up to 50 MB\"\n />\n )}\n\n {/* File queue */}\n {files.length > 0 && (\n <ul\n className=\"uploader-file-list\"\n role=\"list\"\n aria-label=\"Selected files\"\n aria-live=\"polite\"\n >\n {files.map((entry) => (\n <FileQueueRow\n key={entry.id}\n entry={entry}\n onRemove={removeFile}\n onRetry={retryFile}\n onEdit={setEditingId}\n />\n ))}\n </ul>\n )}\n\n {/* Aggregate progress — shown while uploading */}\n {isUploading && (\n <div\n className=\"uploader-aggregate-progress\"\n role=\"status\"\n aria-live=\"polite\"\n >\n <span className=\"uploader-aggregate-progress-label\">Total progress</span>\n <ProgressBar\n value={progress}\n label=\"Overall upload progress\"\n size=\"lg\"\n />\n <span className=\"uploader-aggregate-progress-pct\">{progress}%</span>\n </div>\n )}\n\n {/* Footer */}\n <div className=\"uploader-picker-footer\">\n <span className=\"uploader-picker-footer-esc\">Esc to close</span>\n <div className=\"uploader-picker-footer-actions\">\n <button\n type=\"button\"\n className=\"uploader-btn-secondary uploader-btn-cancel\"\n disabled={isUploading}\n onClick={handleClose}\n >\n Cancel\n </button>\n <button\n type=\"button\"\n className=\"uploader-btn-primary uploader-btn-upload\"\n disabled={isUploading || !hasPending}\n onClick={() => void upload()}\n >\n {isUploading\n ? 'Uploading…'\n : `Upload${pendingCount > 0 ? ` ${pendingCount} file${pendingCount !== 1 ? 's' : ''}` : ''}`}\n </button>\n </div>\n </div>\n </>\n )}\n </div>\n </div>\n )\n}\n","/**\n * usePicker — headless hook driving UploaderClient for the React picker.\n *\n * Manages the file queue, per-file upload state, progress aggregation, and\n * result collection. Components (PickerOverlay, DropPane) call this hook and\n * render its state; custom UIs can use it directly for fully bespoke pickers.\n *\n * Lifecycle:\n * addFiles() → files are queued (state: \"pending\")\n * upload() → each pending file is passed to client.upload() in order;\n * state transitions pending → uploading → done/error\n * onUploadDone fires once all files have settled\n */\n\nimport { useState, useCallback, useRef } from 'react'\nimport { UploaderClient } from '../core/client.js'\nimport type { UploaderClientOptions } from '../core/types.js'\nimport type { PickerFile, PickerOptions, UsePickerResult } from './types.js'\nimport type { PickerResponse } from '../core/types.js'\n\n// ─── Counter for stable keys ──────────────────────────────────────────────────\n\nlet _idCounter = 0\nfunction nextId(): string {\n return `pf-${++_idCounter}`\n}\n\n// ─── usePicker ────────────────────────────────────────────────────────────────\n\nexport type UsePickerOptions = UploaderClientOptions & {\n pickerOptions?: PickerOptions\n onUploadDone?: (response: PickerResponse) => void\n onFileUploadFinished?: (file: import('../core/types.js').FileResult) => void\n onFileUploadFailed?: (file: File, error: Error) => void\n}\n\nexport function usePicker(opts: UsePickerOptions): UsePickerResult {\n const [files, setFiles] = useState<PickerFile[]>([])\n\n // Client ref — stable across renders; recreated when any client-affecting\n // option changes (apikey/apiUrl/security/deliveryProtection/allowedOrigins).\n const clientRef = useRef<UploaderClient | null>(null)\n const clientSigRef = useRef<string>('')\n const clientSig = JSON.stringify({\n apikey: opts.apikey,\n apiUrl: opts.apiUrl ?? null,\n security: opts.security ?? null,\n deliveryProtection: opts.deliveryProtection ?? null,\n allowedOrigins: opts.allowedOrigins ?? null,\n })\n if (!clientRef.current || clientSigRef.current !== clientSig) {\n clientSigRef.current = clientSig\n clientRef.current = new UploaderClient({\n apikey: opts.apikey,\n apiUrl: opts.apiUrl,\n security: opts.security,\n deliveryProtection: opts.deliveryProtection,\n allowedOrigins: opts.allowedOrigins,\n })\n }\n\n const addFiles = useCallback(\n (incoming: FileList | File[]) => {\n const list = Array.from(incoming)\n const { maxFiles, maxSize } = opts.pickerOptions ?? {}\n\n setFiles((prev) => {\n const remaining = maxFiles != null ? maxFiles - prev.length : Infinity\n const toAdd = list.slice(0, remaining).filter((f) => {\n if (maxSize != null && f.size > maxSize) return false\n return true\n })\n\n const newEntries: PickerFile[] = toAdd.map((f) => ({\n file: f,\n id: nextId(),\n state: 'pending',\n progress: null,\n previewUrl: f.type.startsWith('image/') ? URL.createObjectURL(f) : undefined,\n }))\n\n return [...prev, ...newEntries]\n })\n },\n [opts.pickerOptions],\n )\n\n const removeFile = useCallback((id: string) => {\n setFiles((prev) => {\n const entry = prev.find((f) => f.id === id)\n if (entry?.previewUrl) URL.revokeObjectURL(entry.previewUrl)\n return prev.filter((f) => f.id !== id)\n })\n }, [])\n\n // Replace a queued file's bytes with an edited version (from ImageEditor),\n // regenerating its preview and resetting it to a fresh pending state.\n const editFile = useCallback((id: string, newFile: File) => {\n setFiles((prev) =>\n prev.map((f) => {\n if (f.id !== id) return f\n if (f.previewUrl) URL.revokeObjectURL(f.previewUrl)\n return {\n ...f,\n file: newFile,\n state: 'pending',\n progress: null,\n error: undefined,\n result: undefined,\n previewUrl: newFile.type.startsWith('image/') ? URL.createObjectURL(newFile) : undefined,\n }\n }),\n )\n }, [])\n\n const retryFile = useCallback((id: string) => {\n setFiles((prev) =>\n prev.map((f) =>\n f.id === id ? { ...f, state: 'pending', progress: null, error: undefined } : f,\n ),\n )\n }, [])\n\n const upload = useCallback(async () => {\n const client = clientRef.current!\n const snapshot = files.filter((f) => f.state === 'pending')\n\n const uploaded: import('../core/types.js').FileResult[] = []\n const failed: import('../core/types.js').FileResult[] = []\n\n for (const entry of snapshot) {\n // Mark uploading\n setFiles((prev) =>\n prev.map((f) => (f.id === entry.id ? { ...f, state: 'uploading', progress: 0 } : f)),\n )\n\n try {\n const result = await client.upload(entry.file, {\n onProgress: (pct) => {\n setFiles((prev) =>\n prev.map((f) => (f.id === entry.id ? { ...f, progress: pct } : f)),\n )\n },\n })\n\n setFiles((prev) =>\n prev.map((f) =>\n f.id === entry.id ? { ...f, state: 'done', progress: 100, result } : f,\n ),\n )\n\n opts.onFileUploadFinished?.(result)\n uploaded.push(result)\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err))\n setFiles((prev) =>\n prev.map((f) =>\n f.id === entry.id\n ? { ...f, state: 'error', progress: null, error: error.message }\n : f,\n ),\n )\n opts.onFileUploadFailed?.(entry.file, error)\n // Push a placeholder into failed — no FileResult since upload didn't complete\n failed.push({\n handle: '',\n url: '',\n filename: entry.file.name,\n mimetype: entry.file.type,\n size: entry.file.size,\n status: 'Stored',\n })\n }\n }\n\n if (snapshot.length > 0) {\n opts.onUploadDone?.({ filesUploaded: uploaded, filesFailed: failed })\n }\n }, [files, opts])\n\n // Aggregate progress: mean of uploading files; 100 when all done, 0 when no files\n const activeFiles = files.filter((f) => f.state === 'uploading' || f.state === 'done')\n const progress =\n activeFiles.length === 0\n ? 0\n : Math.round(\n activeFiles.reduce((sum, f) => sum + (f.progress ?? 0), 0) / activeFiles.length,\n )\n\n const isUploading = files.some((f) => f.state === 'uploading')\n const isDone =\n files.length > 0 && files.every((f) => f.state === 'done' || f.state === 'error')\n\n return { files, addFiles, removeFile, editFile, retryFile, upload, progress, isUploading, isDone }\n}\n","/**\n * ImageEditor — in-picker, pre-upload image editor.\n *\n * Interaction model: the crop box is FIXED in the center and only resizable\n * (drag a corner — it grows/shrinks symmetrically). The user positions the\n * IMAGE under it by dragging to pan and scrolling to zoom (zoom toward the\n * cursor). Plus rotate 90° / flip / circular crop / output resize.\n *\n * Hand-rolled (no crop library): the image is transformed by translate+scale in\n * the viewport; on Apply the crop box is inverse-mapped to source pixels and\n * editImage produces the final File. Rotation/flip are baked into a working\n * image first so the crop math stays axis-aligned.\n */\n\nimport { useCallback, useEffect, useRef, useState } from 'react'\nimport type { CSSProperties, PointerEvent as RPointerEvent } from 'react'\nimport { editImage } from './editImage.js'\n\nconst clampNum = (n: number, min: number, max: number) => Math.min(max, Math.max(min, n))\nconst MIN_CROP = 48 // px, in viewport space\nconst MAX_ZOOM = 8 // relative to the fit scale\n\nconst ASPECTS: Array<{ label: string; value: number | undefined }> = [\n { label: 'Free', value: undefined },\n { label: '1:1', value: 1 },\n { label: '4:3', value: 4 / 3 },\n { label: '16:9', value: 16 / 9 },\n { label: '3:2', value: 3 / 2 },\n]\n\ntype Corner = 'nw' | 'ne' | 'sw' | 'se'\nconst HANDLES: Array<{ id: Corner; pos: CSSProperties }> = [\n { id: 'nw', pos: { left: -7, top: -7, cursor: 'nwse-resize' } },\n { id: 'ne', pos: { right: -7, top: -7, cursor: 'nesw-resize' } },\n { id: 'sw', pos: { left: -7, bottom: -7, cursor: 'nesw-resize' } },\n { id: 'se', pos: { right: -7, bottom: -7, cursor: 'nwse-resize' } },\n]\n\nexport type ImageEditorProps = {\n /** The image File to edit. */\n file: File\n /** Called with the edited File when the user clicks Apply. */\n onApply: (edited: File) => void\n /** Called when the user cancels editing. */\n onCancel: () => void\n /** Output MIME type. Defaults to the source type (when re-encodable) else png. */\n outputType?: string\n /** Quality 0–1 for lossy output. Default 0.92. */\n quality?: number\n /** Default \"max longest side\" for the resize control. Default 1920. */\n maxDimensionDefault?: number\n className?: string\n style?: CSSProperties\n}\n\nexport function ImageEditor({\n file,\n onApply,\n onCancel,\n outputType,\n quality,\n maxDimensionDefault = 1920,\n className,\n style,\n}: ImageEditorProps) {\n const [rotation, setRotation] = useState(0)\n const [flip, setFlip] = useState({ horizontal: false, vertical: false })\n const [aspect, setAspect] = useState<number | undefined>(undefined)\n const [cropShape, setCropShape] = useState<'rect' | 'round'>('rect')\n const [resizeEnabled, setResizeEnabled] = useState(false)\n const [maxDimension, setMaxDimension] = useState(maxDimensionDefault)\n const [busy, setBusy] = useState(false)\n const [error, setError] = useState<string | null>(null)\n\n // Working image = original with rotation/flip baked in (axis-aligned).\n const [workingFile, setWorkingFile] = useState<File>(file)\n useEffect(() => {\n let cancelled = false\n if (rotation === 0 && !flip.horizontal && !flip.vertical) {\n setWorkingFile(file)\n return\n }\n editImage(file, { rotation, flip })\n .then((f) => {\n if (!cancelled) setWorkingFile(f)\n })\n .catch(() => {\n if (!cancelled) setError('Failed to render the rotated preview')\n })\n return () => {\n cancelled = true\n }\n }, [file, rotation, flip])\n\n // Object URL for the working image (effect-based for StrictMode safety).\n const [workingUrl, setWorkingUrl] = useState('')\n useEffect(() => {\n const url = URL.createObjectURL(workingFile)\n setWorkingUrl(url)\n return () => URL.revokeObjectURL(url)\n }, [workingFile])\n\n const viewportRef = useRef<HTMLDivElement>(null)\n const [vp, setVp] = useState({ w: 0, h: 0 }) // viewport size, px\n const [nat, setNat] = useState({ w: 0, h: 0 }) // working image natural size, px\n // Image transform: viewportPoint = (x,y) + scale * naturalPoint, origin 0,0.\n const [view, setView] = useState({ scale: 1, x: 0, y: 0 })\n const [crop, setCrop] = useState({ w: 0, h: 0 }) // centered crop box, viewport px\n\n const ready = nat.w > 0 && vp.w > 0\n\n // Measure the viewport.\n useEffect(() => {\n const el = viewportRef.current\n if (!el || typeof ResizeObserver === 'undefined') return\n const measure = () => {\n const r = el.getBoundingClientRect()\n setVp({ w: r.width, h: r.height })\n }\n measure()\n const ro = new ResizeObserver(measure)\n ro.observe(el)\n return () => ro.disconnect()\n }, [])\n\n // Clamp a desired (scale, x, y) for the given crop: image must cover the crop,\n // and scale stays within [coverMin, fit*MAX_ZOOM].\n const normalize = useCallback(\n (scaleRaw: number, xRaw: number, yRaw: number, cw: number, ch: number) => {\n if (!nat.w || !vp.w) return { scale: scaleRaw, x: xRaw, y: yRaw }\n const fitS = Math.min(vp.w / nat.w, vp.h / nat.h)\n const coverMin = Math.max(cw / nat.w, ch / nat.h)\n const scale = clampNum(scaleRaw, coverMin, Math.max(fitS, coverMin) * MAX_ZOOM)\n const cbX = (vp.w - cw) / 2\n const cbY = (vp.h - ch) / 2\n const x = clampNum(xRaw, cbX + cw - scale * nat.w, cbX)\n const y = clampNum(yRaw, cbY + ch - scale * nat.h, cbY)\n return { scale, x, y }\n },\n [nat, vp],\n )\n\n // Initialize/reset view + crop when the image or viewport changes.\n useEffect(() => {\n if (!nat.w || !vp.w) return\n const fitS = Math.min(vp.w / nat.w, vp.h / nat.h)\n const baseline = Math.min(vp.w, vp.h) * 0.7\n let cw = baseline\n let ch = baseline\n if (cropShape === 'round') {\n cw = ch = baseline\n } else if (aspect) {\n if (aspect >= 1) ch = baseline / aspect\n else cw = baseline * aspect\n }\n cw = clampNum(cw, MIN_CROP, vp.w)\n ch = clampNum(ch, MIN_CROP, vp.h)\n setCrop({ w: cw, h: ch })\n setView(normalize(fitS, (vp.w - fitS * nat.w) / 2, (vp.h - fitS * nat.h) / 2, cw, ch))\n // Only re-init on a new image / viewport resize.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [nat, vp])\n\n // Adjust the crop to a new aspect/shape (keep the current view).\n useEffect(() => {\n if (!nat.w || !vp.w) return\n let cw = crop.w\n let ch = crop.h\n if (cropShape === 'round') {\n cw = ch = Math.min(cw, ch)\n } else if (aspect) {\n ch = cw / aspect\n if (ch > vp.h) {\n ch = vp.h\n cw = ch * aspect\n }\n }\n cw = clampNum(cw, MIN_CROP, vp.w)\n ch = clampNum(ch, MIN_CROP, vp.h)\n setCrop({ w: cw, h: ch })\n setView((v) => normalize(v.scale, v.x, v.y, cw, ch))\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [aspect, cropShape])\n\n const onImageLoad = useCallback((e: React.SyntheticEvent<HTMLImageElement>) => {\n setNat({ w: e.currentTarget.naturalWidth, h: e.currentTarget.naturalHeight })\n }, [])\n\n // Wheel zoom toward the cursor (native non-passive listener to preventDefault).\n useEffect(() => {\n const el = viewportRef.current\n if (!el) return\n const onWheel = (e: WheelEvent) => {\n e.preventDefault()\n const rect = el.getBoundingClientRect()\n const fx = e.clientX - rect.left\n const fy = e.clientY - rect.top\n const factor = Math.exp(-e.deltaY * 0.0015)\n setView((v) => {\n const next = normalize(v.scale * factor, v.x, v.y, crop.w, crop.h)\n const ratio = next.scale / v.scale\n // keep the cursor point fixed, then re-clamp pan\n return normalize(next.scale, fx - ratio * (fx - v.x), fy - ratio * (fy - v.y), crop.w, crop.h)\n })\n }\n el.addEventListener('wheel', onWheel, { passive: false })\n return () => el.removeEventListener('wheel', onWheel)\n }, [normalize, crop.w, crop.h])\n\n // Pointer drag: pan the image, or resize the crop from a corner handle.\n const dragRef = useRef<\n | { mode: 'pan'; downX: number; downY: number; startX: number; startY: number }\n | { mode: 'resize'; corner: Corner }\n | null\n >(null)\n\n const onPointerDown = useCallback(\n (e: RPointerEvent<HTMLDivElement>) => {\n if (!ready) return\n const handle = (e.target as HTMLElement).dataset.handle as Corner | undefined\n e.currentTarget.setPointerCapture(e.pointerId)\n if (handle) {\n dragRef.current = { mode: 'resize', corner: handle }\n } else {\n dragRef.current = { mode: 'pan', downX: e.clientX, downY: e.clientY, startX: view.x, startY: view.y }\n }\n },\n [ready, view.x, view.y],\n )\n\n const onPointerMove = useCallback(\n (e: RPointerEvent<HTMLDivElement>) => {\n const d = dragRef.current\n if (!d) return\n const rect = e.currentTarget.getBoundingClientRect()\n if (d.mode === 'pan') {\n setView((v) => normalize(v.scale, d.startX + (e.clientX - d.downX), d.startY + (e.clientY - d.downY), crop.w, crop.h))\n } else {\n const hx = Math.abs(e.clientX - rect.left - rect.width / 2)\n const hy = Math.abs(e.clientY - rect.top - rect.height / 2)\n let cw: number\n let ch: number\n if (cropShape === 'round') {\n cw = ch = 2 * Math.max(hx, hy)\n } else if (aspect) {\n cw = Math.max(2 * hx, 2 * hy * aspect)\n ch = cw / aspect\n } else {\n cw = 2 * hx\n ch = 2 * hy\n }\n cw = clampNum(cw, MIN_CROP, rect.width)\n ch = clampNum(ch, MIN_CROP, rect.height)\n if (cropShape === 'round') cw = ch = Math.min(cw, ch)\n setCrop({ w: cw, h: ch })\n setView((v) => normalize(v.scale, v.x, v.y, cw, ch))\n }\n },\n [normalize, crop.w, crop.h, cropShape, aspect],\n )\n\n const onPointerUp = useCallback((e: RPointerEvent<HTMLDivElement>) => {\n dragRef.current = null\n if (e.currentTarget.hasPointerCapture(e.pointerId)) e.currentTarget.releasePointerCapture(e.pointerId)\n }, [])\n\n const handleApply = useCallback(async () => {\n if (!ready) {\n onApply(workingFile)\n return\n }\n setBusy(true)\n setError(null)\n try {\n const cbX = (vp.w - crop.w) / 2\n const cbY = (vp.h - crop.h) / 2\n // Inverse of viewportPoint = view + scale*natural.\n const region = {\n x: (cbX - view.x) / view.scale,\n y: (cbY - view.y) / view.scale,\n width: crop.w / view.scale,\n height: crop.h / view.scale,\n }\n const edited = await editImage(workingFile, {\n crop: region,\n circle: cropShape === 'round',\n maxDimension: resizeEnabled ? maxDimension : undefined,\n outputType,\n quality,\n })\n onApply(edited)\n } catch (e) {\n setError(e instanceof Error ? e.message : 'Failed to apply edits')\n } finally {\n setBusy(false)\n }\n }, [ready, vp, crop, view, workingFile, cropShape, resizeEnabled, maxDimension, outputType, quality, onApply])\n\n const cbX = (vp.w - crop.w) / 2\n const cbY = (vp.h - crop.h) / 2\n\n return (\n <div\n className={['uploader-editor', className ?? ''].filter(Boolean).join(' ')}\n style={style}\n data-testid=\"image-editor\"\n >\n <div\n ref={viewportRef}\n className=\"uploader-editor-canvas\"\n onPointerDown={onPointerDown}\n onPointerMove={onPointerMove}\n onPointerUp={onPointerUp}\n onPointerCancel={onPointerUp}\n >\n {workingUrl && (\n /* eslint-disable-next-line @next/next/no-img-element */\n <img\n src={workingUrl}\n alt=\"Edit preview\"\n onLoad={onImageLoad}\n draggable={false}\n className=\"uploader-editor-image\"\n style={{\n width: nat.w || undefined,\n height: nat.h || undefined,\n transform: `translate(${view.x}px, ${view.y}px) scale(${view.scale})`,\n opacity: ready ? 1 : 0,\n }}\n />\n )}\n {ready && (\n <div\n className={`uploader-editor-cropbox${cropShape === 'round' ? ' uploader-editor-cropbox-round' : ''}`}\n style={{ left: cbX, top: cbY, width: crop.w, height: crop.h }}\n >\n {HANDLES.map((h) => (\n <span key={h.id} data-handle={h.id} className=\"uploader-editor-handle\" style={h.pos} />\n ))}\n </div>\n )}\n </div>\n <p className=\"uploader-editor-hint\">Drag the image to move it · scroll to zoom · drag a corner to resize the crop.</p>\n\n <div className=\"uploader-editor-controls\">\n {/* Rotate / flip */}\n <div className=\"uploader-editor-row\">\n <button\n type=\"button\"\n className=\"uploader-editor-tool\"\n aria-label=\"Rotate left\"\n onClick={() => setRotation((r) => (r - 90 + 360) % 360)}\n >\n ⟲\n </button>\n <button\n type=\"button\"\n className=\"uploader-editor-tool\"\n aria-label=\"Rotate right\"\n onClick={() => setRotation((r) => (r + 90) % 360)}\n >\n ⟳\n </button>\n <button\n type=\"button\"\n className={`uploader-editor-tool${flip.horizontal ? ' uploader-editor-tool-active' : ''}`}\n aria-pressed={flip.horizontal}\n aria-label=\"Flip horizontal\"\n onClick={() => setFlip((f) => ({ ...f, horizontal: !f.horizontal }))}\n >\n ⇋\n </button>\n <button\n type=\"button\"\n className={`uploader-editor-tool${flip.vertical ? ' uploader-editor-tool-active' : ''}`}\n aria-pressed={flip.vertical}\n aria-label=\"Flip vertical\"\n onClick={() => setFlip((f) => ({ ...f, vertical: !f.vertical }))}\n >\n ⇅\n </button>\n </div>\n\n {/* Shape */}\n <div className=\"uploader-editor-row\">\n <span className=\"uploader-editor-label\">Shape</span>\n <button\n type=\"button\"\n className={`uploader-editor-chip${cropShape === 'rect' ? ' uploader-editor-chip-active' : ''}`}\n onClick={() => setCropShape('rect')}\n >\n Rectangle\n </button>\n <button\n type=\"button\"\n className={`uploader-editor-chip${cropShape === 'round' ? ' uploader-editor-chip-active' : ''}`}\n onClick={() => {\n setCropShape('round')\n setAspect(1)\n }}\n >\n Circle\n </button>\n </div>\n\n {/* Aspect ratio (locked to 1:1 for circles) */}\n <div className=\"uploader-editor-row\">\n <span className=\"uploader-editor-label\">Ratio</span>\n {ASPECTS.map((a) => (\n <button\n key={a.label}\n type=\"button\"\n disabled={cropShape === 'round'}\n className={`uploader-editor-chip${\n cropShape === 'rect' && aspect === a.value ? ' uploader-editor-chip-active' : ''\n }`}\n onClick={() => setAspect(a.value)}\n >\n {a.label}\n </button>\n ))}\n </div>\n\n {/* Resize */}\n <div className=\"uploader-editor-row\">\n <label className=\"uploader-editor-label\">\n <input\n type=\"checkbox\"\n checked={resizeEnabled}\n onChange={(e) => setResizeEnabled(e.target.checked)}\n />{' '}\n Resize to\n </label>\n <input\n type=\"number\"\n min={16}\n value={maxDimension}\n disabled={!resizeEnabled}\n aria-label=\"Maximum longest side in pixels\"\n className=\"uploader-editor-number\"\n onChange={(e) => setMaxDimension(Number(e.target.value))}\n />\n <span className=\"uploader-editor-label\">px (longest side)</span>\n </div>\n\n {error && (\n <div className=\"uploader-editor-error\" role=\"alert\">\n {error}\n </div>\n )}\n </div>\n\n <div className=\"uploader-editor-actions\">\n <button type=\"button\" className=\"uploader-btn-cancel\" onClick={onCancel} disabled={busy}>\n Cancel\n </button>\n <button type=\"button\" className=\"uploader-btn-upload\" onClick={handleApply} disabled={busy}>\n {busy ? 'Applying…' : 'Apply'}\n </button>\n </div>\n </div>\n )\n}\n","/**\n * editImage — bake pre-upload edits (rotate / flip / crop / resize) into an\n * image File using a <canvas>, returning a NEW File. Browser-only.\n *\n * The crop rectangle is in the pixel space of the rotated+flipped image (the\n * order is: rotate, then flip, then crop, then resize). ImageEditor bakes\n * rotation/flip into a working image first and passes a crop with rotation=0, so\n * the crop coordinates come straight from the on-screen selection.\n */\n\nexport type Flip = { horizontal: boolean; vertical: boolean }\nexport type CropArea = { x: number; y: number; width: number; height: number }\n\nexport type EditImageOptions = {\n /** Clockwise rotation in degrees. */\n rotation?: number\n /** Mirror horizontally / vertically. */\n flip?: Flip\n /** Crop rectangle in the rotated+flipped image's natural pixels. */\n crop?: CropArea\n /** If set, scale the result down so its longest side is at most this many pixels. */\n maxDimension?: number\n /** Mask the result to a circle/ellipse (transparent corners). Forces PNG output. */\n circle?: boolean\n /** Output MIME type. Defaults to the source type when re-encodable, else image/png. */\n outputType?: string\n /** Quality 0–1 for lossy formats (jpeg/webp). Default 0.92. */\n quality?: number\n /** Override the output filename. Defaults to the source name with a matching extension. */\n fileName?: string\n}\n\nconst ENCODABLE = new Set(['image/jpeg', 'image/png', 'image/webp'])\n\n/** Map an output MIME type to a file extension. */\nexport function mimeToExtension(mime: string): string {\n switch (mime) {\n case 'image/jpeg':\n return 'jpg'\n case 'image/png':\n return 'png'\n case 'image/webp':\n return 'webp'\n default:\n return 'png'\n }\n}\n\n/** Choose the output MIME: caller override, else the source type if re-encodable, else png. */\nexport function pickOutputType(sourceType: string, override?: string): string {\n if (override) return override\n return ENCODABLE.has(sourceType) ? sourceType : 'image/png'\n}\n\n/** Size of the axis-aligned bounding box after rotating a w×h box by `deg` degrees. */\nexport function rotatedBoundingBox(\n width: number,\n height: number,\n deg: number,\n): { width: number; height: number } {\n const rad = (deg * Math.PI) / 180\n return {\n width: Math.abs(Math.cos(rad) * width) + Math.abs(Math.sin(rad) * height),\n height: Math.abs(Math.sin(rad) * width) + Math.abs(Math.cos(rad) * height),\n }\n}\n\n/** Scale factor to fit (w,h) within `maxDimension` on the longest side; never upscales (≤1). */\nexport function fitScale(width: number, height: number, maxDimension?: number): number {\n if (!maxDimension) return 1\n const longest = Math.max(width, height)\n return longest > maxDimension ? maxDimension / longest : 1\n}\n\n/** Replace a filename's extension (keeps the base name). */\nexport function withExtension(name: string, ext: string): string {\n const dot = name.lastIndexOf('.')\n const base = dot > 0 ? name.slice(0, dot) : name\n return `${base}.${ext}`\n}\n\nfunction loadImage(url: string): Promise<HTMLImageElement> {\n return new Promise((resolve, reject) => {\n const img = new Image()\n img.onload = () => resolve(img)\n img.onerror = () => reject(new Error('Failed to load image for editing'))\n img.src = url\n })\n}\n\nexport async function editImage(file: File, opts: EditImageOptions = {}): Promise<File> {\n const {\n rotation = 0,\n flip = { horizontal: false, vertical: false },\n crop,\n maxDimension,\n } = opts\n\n const url = URL.createObjectURL(file)\n try {\n const image = await loadImage(url)\n const iw = image.naturalWidth || image.width\n const ih = image.naturalHeight || image.height\n\n // 1) Draw the rotated + flipped image onto a bounding-box canvas.\n const box = rotatedBoundingBox(iw, ih, rotation)\n const boxCanvas = document.createElement('canvas')\n boxCanvas.width = Math.max(1, Math.round(box.width))\n boxCanvas.height = Math.max(1, Math.round(box.height))\n const boxCtx = boxCanvas.getContext('2d')\n if (!boxCtx) throw new Error('Canvas 2D context unavailable')\n boxCtx.translate(boxCanvas.width / 2, boxCanvas.height / 2)\n boxCtx.rotate((rotation * Math.PI) / 180)\n boxCtx.scale(flip.horizontal ? -1 : 1, flip.vertical ? -1 : 1)\n boxCtx.drawImage(image, -iw / 2, -ih / 2)\n\n // 2) Crop (default: the whole bounding box).\n const region: CropArea = crop ?? {\n x: 0,\n y: 0,\n width: boxCanvas.width,\n height: boxCanvas.height,\n }\n const scale = fitScale(region.width, region.height, maxDimension)\n const outW = Math.max(1, Math.round(region.width * scale))\n const outH = Math.max(1, Math.round(region.height * scale))\n\n const outCanvas = document.createElement('canvas')\n outCanvas.width = outW\n outCanvas.height = outH\n const outCtx = outCanvas.getContext('2d')\n if (!outCtx) throw new Error('Canvas 2D context unavailable')\n // Circular crop: clip to the inscribed ellipse so the corners stay transparent.\n if (opts.circle) {\n outCtx.beginPath()\n outCtx.ellipse(outW / 2, outH / 2, outW / 2, outH / 2, 0, 0, Math.PI * 2)\n outCtx.clip()\n }\n outCtx.drawImage(boxCanvas, region.x, region.y, region.width, region.height, 0, 0, outW, outH)\n\n // 3) Encode. A circular crop needs an alpha channel, so always PNG.\n const outputType = opts.circle ? 'image/png' : pickOutputType(file.type, opts.outputType)\n const quality = opts.quality ?? 0.92\n const blob: Blob = await new Promise((resolve, reject) => {\n outCanvas.toBlob(\n (b) => (b ? resolve(b) : reject(new Error('Canvas toBlob returned null'))),\n outputType,\n quality,\n )\n })\n\n const name = opts.fileName ?? withExtension(file.name, mimeToExtension(outputType))\n return new File([blob], name, { type: outputType, lastModified: Date.now() })\n } finally {\n URL.revokeObjectURL(url)\n }\n}\n","/**\n * Dropzone — shared drag-and-drop zone primitive for the SDK picker.\n *\n * Renders the upload icon swatch, headline, \"or\" divider, a row of source\n * buttons (Browse files + optional Take photo), and file-type hint text.\n * Drag-over state is reflected via the data-drag-over attribute so parent\n * components can apply accent border/fill via CSS without any JS color logic.\n *\n * All colors resolve from --uploader-* CSS variables — no hard-coded brand\n * colors. The Browse files button receives a glow matching --uploader-accent-ring\n * so it inherits whatever accent the host app sets.\n *\n * Used by PickerOverlay (#203) and DropPane (#204).\n *\n * @dsCard Dropzone\n */\n\nimport React, { useRef, useCallback } from 'react'\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport type DropzoneProps = {\n /** Called when the user drops files or clicks Browse and selects files. */\n onFiles: (files: FileList) => void\n /** Whether a drag is currently in progress over this zone. */\n isDragOver: boolean\n /** Called when a drag enters the zone. */\n onDragEnter: (e: React.DragEvent) => void\n /** Called when a drag leaves the zone. */\n onDragLeave: (e: React.DragEvent) => void\n /** Called on each dragover event (to prevent browser default). */\n onDragOver: (e: React.DragEvent) => void\n /** Called when files are dropped onto the zone. */\n onDrop: (e: React.DragEvent) => void\n /** Accept string forwarded to the hidden <input>. E.g. \"image/*,.pdf\" */\n accept?: string\n /** Whether to allow multiple file selection. Default: true. */\n multiple?: boolean\n /**\n * When provided, renders a \"Take photo\" button beside \"Browse files\" that\n * invokes this callback (the parent swaps in the camera capture view).\n * Omit to hide the camera option entirely.\n */\n onCamera?: () => void\n /**\n * Hint text shown below the Browse button (e.g. \"PNG, JPG, PDF · up to 50 MB\").\n * If omitted, no hint is rendered.\n */\n hint?: string\n /** Additional CSS class(es) on the zone root. */\n className?: string\n /** Inline styles on the zone root. */\n style?: React.CSSProperties\n}\n\n// ─── Component ────────────────────────────────────────────────────────────────\n\nexport function Dropzone({\n onFiles,\n isDragOver,\n onDragEnter,\n onDragLeave,\n onDragOver,\n onDrop,\n accept,\n multiple = true,\n onCamera,\n hint,\n className,\n style,\n}: DropzoneProps) {\n const inputRef = useRef<HTMLInputElement>(null)\n\n const openFileDialog = useCallback(() => inputRef.current?.click(), [])\n\n const handleInputChange = useCallback(\n (e: React.ChangeEvent<HTMLInputElement>) => {\n if (e.target.files && e.target.files.length > 0) {\n onFiles(e.target.files)\n // Reset so the same file can be re-selected after removal\n e.target.value = ''\n }\n },\n [onFiles],\n )\n\n return (\n <div\n className={['uploader-dropzone', className ?? ''].filter(Boolean).join(' ')}\n data-drag-over={isDragOver ? 'true' : undefined}\n role=\"button\"\n tabIndex={0}\n aria-label=\"Drop files here or click to browse\"\n style={style}\n onDragEnter={onDragEnter}\n onDragLeave={onDragLeave}\n onDragOver={onDragOver}\n onDrop={onDrop}\n onClick={openFileDialog}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault()\n openFileDialog()\n }\n }}\n >\n {/* Upload icon swatch */}\n <div className=\"uploader-dropzone-icon-wrap\" aria-hidden=\"true\">\n <div className=\"uploader-dropzone-icon\" />\n </div>\n\n <p className=\"uploader-dropzone-label\">Drag & drop files here</p>\n <p className=\"uploader-dropzone-sublabel\">or</p>\n\n <div className=\"uploader-dropzone-actions\">\n <button\n type=\"button\"\n className=\"uploader-btn-browse\"\n onClick={(e) => {\n // Stop propagation so the zone's own onClick doesn't fire again\n e.stopPropagation()\n openFileDialog()\n }}\n >\n Browse files\n </button>\n\n {onCamera && (\n <button\n type=\"button\"\n className=\"uploader-btn-camera\"\n onClick={(e) => {\n e.stopPropagation()\n onCamera()\n }}\n >\n <svg\n className=\"uploader-btn-camera-icon\"\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n aria-hidden=\"true\"\n >\n <path\n d=\"M4 8.5A2.5 2.5 0 0 1 6.5 6h1.2a1 1 0 0 0 .83-.45l.74-1.1A1 1 0 0 1 10.1 4h3.8a1 1 0 0 1 .83.45l.74 1.1a1 1 0 0 0 .83.45h1.2A2.5 2.5 0 0 1 20 8.5v8A2.5 2.5 0 0 1 17.5 19h-11A2.5 2.5 0 0 1 4 16.5v-8Z\"\n stroke=\"currentColor\"\n strokeWidth=\"1.6\"\n strokeLinejoin=\"round\"\n />\n <circle cx=\"12\" cy=\"12.5\" r=\"3.2\" stroke=\"currentColor\" strokeWidth=\"1.6\" />\n </svg>\n Take photo\n </button>\n )}\n </div>\n\n {hint && <p className=\"uploader-dropzone-hint\">{hint}</p>}\n\n {/* Hidden file input */}\n <input\n ref={inputRef}\n type=\"file\"\n multiple={multiple}\n accept={accept}\n className=\"uploader-file-input\"\n aria-hidden=\"true\"\n tabIndex={-1}\n onChange={handleInputChange}\n />\n </div>\n )\n}\n","/**\n * CameraCapture — in-picker webcam capture.\n *\n * Requests the device camera via getUserMedia, shows a live preview, and lets\n * the user snap a still. The capture is reviewed (Retake / Use photo) before it\n * leaves the component as a regular image `File`, so it flows into the picker\n * queue identically to a dropped or browsed file — preview, edit, and upload.\n *\n * Front ('user') cameras are mirrored in both the preview and the captured\n * still so the selfie matches what the user sees. The MediaStream is stopped on\n * unmount, on capture (to release the camera indicator while reviewing), and on\n * cancel.\n *\n * Which camera opens first is device-dependent: phones and tablets default to\n * the REAR camera (people photograph the world, not themselves, from an upload\n * form), desktops to the only camera they have. A flip control appears whenever\n * the device actually exposes more than one camera, so the default is a starting\n * point rather than a constraint. `facingMode` overrides the default outright.\n *\n * All colors resolve from --uploader-* CSS variables — no hard-coded brand\n * colors. Used by PickerOverlay and DropPane.\n *\n * @dsCard CameraCapture\n */\n\nimport React, { useCallback, useEffect, useRef, useState } from 'react'\nimport type { PickerOptions } from './types.js'\n\n// ─── Support detection ──────────────────────────────────────────────────────────\n\n/** True when the current environment can open a camera stream. */\nexport function isCameraSupported(): boolean {\n return (\n typeof navigator !== 'undefined' &&\n !!navigator.mediaDevices &&\n typeof navigator.mediaDevices.getUserMedia === 'function' &&\n typeof document !== 'undefined' &&\n typeof HTMLCanvasElement !== 'undefined'\n )\n}\n\n/** Which physical camera to open. Mirrors the getUserMedia `facingMode` values. */\nexport type CameraFacingMode = 'user' | 'environment'\n\n/**\n * Best-effort \"is this a phone or tablet?\" check, used only to pick which camera\n * opens first — never to gate a feature, so a wrong answer costs one tap on the\n * flip control.\n *\n * `userAgentData.mobile` is the reliable signal where it exists (Chromium). The\n * UA regex covers everything else, and the `maxTouchPoints` clause catches\n * iPadOS 13+, which reports a desktop Macintosh UA and would otherwise open the\n * front camera on a tablet.\n */\nexport function isMobileDevice(): boolean {\n if (typeof navigator === 'undefined') return false\n\n const uaData = (navigator as Navigator & { userAgentData?: { mobile?: boolean } }).userAgentData\n if (typeof uaData?.mobile === 'boolean') return uaData.mobile\n\n const ua = navigator.userAgent ?? ''\n if (/Android|iPhone|iPad|iPod|Windows Phone|webOS|BlackBerry|Opera Mini|Mobile/i.test(ua)) {\n return true\n }\n // iPadOS 13+ masquerading as macOS: a real Mac reports maxTouchPoints 0.\n return /Macintosh/.test(ua) && (navigator.maxTouchPoints ?? 0) > 1\n}\n\n/**\n * The camera to open when the caller expresses no preference: rear on mobile,\n * front on everything else.\n */\nexport function defaultCameraFacingMode(): CameraFacingMode {\n return isMobileDevice() ? 'environment' : 'user'\n}\n\nconst IMAGE_EXT = /\\.(png|jpe?g|gif|webp|bmp|svg|avif|heic|heif)$/i\n\n/** Whether an `accept` list permits image files (and thus camera capture). */\nfunction acceptsImages(accept?: string[]): boolean {\n if (!accept || accept.length === 0) return true\n return accept.some((a) => a.startsWith('image/') || a === 'image/*' || IMAGE_EXT.test(a.trim()))\n}\n\n/**\n * Decide whether the picker should offer the camera source, given the picker\n * options and the current environment. The camera is offered when:\n * - the browser supports getUserMedia, AND\n * - the accept filter permits images, AND\n * - fromSources is unset or explicitly includes 'camera'.\n */\nexport function shouldOfferCamera(options?: PickerOptions): boolean {\n if (!isCameraSupported()) return false\n if (!acceptsImages(options?.accept)) return false\n const sources = options?.fromSources\n if (sources && !sources.includes('camera')) return false\n return true\n}\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport type CameraCaptureProps = {\n /** Called with the captured still when the user confirms \"Use photo\". */\n onCapture: (file: File) => void\n /** Called when the user backs out without capturing. */\n onCancel: () => void\n /**\n * Which camera to open first. 'user' = front (mirrored), 'environment' = rear.\n * Omitted → `defaultCameraFacingMode()`: rear on mobile, front elsewhere. The\n * user can still flip when the device has more than one camera.\n */\n facingMode?: CameraFacingMode\n /** Output MIME type for the still. Default 'image/jpeg'. */\n outputType?: string\n /** Quality 0–1 for lossy output. Default 0.92. */\n quality?: number\n /** Base name for the produced file (extension is derived). Default 'camera-photo'. */\n fileNamePrefix?: string\n className?: string\n style?: React.CSSProperties\n}\n\n// ─── Helpers ─────────────────────────────────────────────────────────────────\n\nfunction extForType(mime: string): string {\n switch (mime) {\n case 'image/png':\n return 'png'\n case 'image/webp':\n return 'webp'\n default:\n return 'jpg'\n }\n}\n\n/** Map a getUserMedia rejection to a friendly, actionable message. */\nfunction describeCameraError(err: unknown): string {\n const name = err instanceof DOMException ? err.name : ''\n switch (name) {\n case 'NotAllowedError':\n case 'SecurityError':\n return 'Camera access was blocked. Allow camera permission and try again.'\n case 'NotFoundError':\n case 'OverconstrainedError':\n return 'No camera was found on this device.'\n case 'NotReadableError':\n return 'The camera is already in use by another application.'\n default:\n return 'Could not start the camera.'\n }\n}\n\n// ─── Component ────────────────────────────────────────────────────────────────\n\ntype Status = 'initializing' | 'live' | 'captured' | 'error'\n\nexport function CameraCapture({\n onCapture,\n onCancel,\n facingMode,\n outputType = 'image/jpeg',\n quality = 0.92,\n fileNamePrefix = 'camera-photo',\n className,\n style,\n}: CameraCaptureProps) {\n const videoRef = useRef<HTMLVideoElement>(null)\n const streamRef = useRef<MediaStream | null>(null)\n const blobRef = useRef<Blob | null>(null)\n\n const [status, setStatus] = useState<Status>('initializing')\n const [error, setError] = useState<string | null>(null)\n const [stillUrl, setStillUrl] = useState('')\n // The prop seeds the choice; the flip control owns it from then on.\n const [facing, setFacing] = useState<CameraFacingMode>(\n () => facingMode ?? defaultCameraFacingMode(),\n )\n const [canFlip, setCanFlip] = useState(false)\n\n const mirror = facing === 'user'\n\n // A caller that changes the prop is re-expressing a preference; follow it.\n // Same-value re-renders don't re-run this, so it never fights a user flip.\n useEffect(() => {\n if (facingMode) setFacing(facingMode)\n }, [facingMode])\n\n const stopStream = useCallback(() => {\n streamRef.current?.getTracks().forEach((t) => t.stop())\n streamRef.current = null\n if (videoRef.current) videoRef.current.srcObject = null\n }, [])\n\n /**\n * Decide whether to offer the flip control. Only meaningful once permission\n * has been granted — before that `enumerateDevices()` may report placeholder\n * entries or none at all. Where the API is missing entirely, fall back to the\n * device class: phones have two cameras, desktops usually have one.\n */\n const probeCameraCount = useCallback(async () => {\n const media = typeof navigator !== 'undefined' ? navigator.mediaDevices : undefined\n if (!media || typeof media.enumerateDevices !== 'function') {\n setCanFlip(isMobileDevice())\n return\n }\n try {\n const devices = await media.enumerateDevices()\n setCanFlip(devices.filter((d) => d.kind === 'videoinput').length > 1)\n } catch {\n setCanFlip(isMobileDevice())\n }\n }, [])\n\n // Acquire the camera stream. Reused on mount, on flip, and on \"Retake\".\n const startStream = useCallback(async () => {\n setError(null)\n setStatus('initializing')\n if (!isCameraSupported()) {\n setError('Camera capture is not supported in this browser.')\n setStatus('error')\n return\n }\n // Release any stream we already hold first — some mobile devices refuse to\n // open the second camera while the first one is still live.\n stopStream()\n try {\n const stream = await navigator.mediaDevices.getUserMedia({\n // Non-exact: a device with no camera on the requested side falls back to\n // the one it has rather than throwing OverconstrainedError.\n video: { facingMode: facing },\n audio: false,\n })\n // Component may have unmounted while awaiting permission.\n if (videoRef.current == null) {\n stream.getTracks().forEach((t) => t.stop())\n return\n }\n streamRef.current = stream\n videoRef.current.srcObject = stream\n setStatus('live')\n void probeCameraCount()\n } catch (err) {\n setError(describeCameraError(err))\n setStatus('error')\n }\n }, [facing, probeCameraCount, stopStream])\n\n // Start on mount / when facing changes; always tear the stream down on cleanup.\n useEffect(() => {\n void startStream()\n return () => {\n stopStream()\n }\n }, [startStream, stopStream])\n\n // Revoke the review object URL when it changes or on unmount.\n useEffect(() => {\n if (!stillUrl) return\n return () => URL.revokeObjectURL(stillUrl)\n }, [stillUrl])\n\n const capture = useCallback(() => {\n const video = videoRef.current\n if (!video) return\n const w = video.videoWidth\n const h = video.videoHeight\n if (!w || !h) {\n setError('The camera is still warming up — try again in a moment.')\n return\n }\n const canvas = document.createElement('canvas')\n canvas.width = w\n canvas.height = h\n const ctx = canvas.getContext('2d')\n if (!ctx) {\n setError('Canvas is unavailable for capturing.')\n return\n }\n if (mirror) {\n ctx.translate(w, 0)\n ctx.scale(-1, 1)\n }\n ctx.drawImage(video, 0, 0, w, h)\n canvas.toBlob(\n (blob) => {\n if (!blob) {\n setError('Failed to capture the photo.')\n return\n }\n blobRef.current = blob\n setStillUrl(URL.createObjectURL(blob))\n setStatus('captured')\n // Release the camera light while the user reviews the still.\n stopStream()\n },\n outputType,\n quality,\n )\n }, [mirror, outputType, quality, stopStream])\n\n // Flipping only changes state — the effect above tears the old stream down\n // and opens the new side, so there is one code path for acquiring a camera.\n const flipCamera = useCallback(() => {\n setFacing((current) => (current === 'user' ? 'environment' : 'user'))\n }, [])\n\n const retake = useCallback(() => {\n blobRef.current = null\n setStillUrl('')\n void startStream()\n }, [startStream])\n\n const confirm = useCallback(() => {\n const blob = blobRef.current\n if (!blob) return\n const name = `${fileNamePrefix}-${Date.now()}.${extForType(outputType)}`\n const file = new File([blob], name, { type: outputType, lastModified: Date.now() })\n onCapture(file)\n }, [fileNamePrefix, outputType, onCapture])\n\n return (\n <div\n className={['uploader-camera', className ?? ''].filter(Boolean).join(' ')}\n style={style}\n data-testid=\"camera-capture\"\n data-facing={facing}\n >\n <div className=\"uploader-camera-stage\">\n {status === 'error' ? (\n <div className=\"uploader-camera-error\" role=\"alert\">\n {error}\n </div>\n ) : status === 'captured' ? (\n /* eslint-disable-next-line @next/next/no-img-element */\n <img src={stillUrl} alt=\"Captured preview\" className=\"uploader-camera-still\" />\n ) : (\n <video\n ref={videoRef}\n className=\"uploader-camera-video\"\n autoPlay\n playsInline\n muted\n data-mirror={mirror ? 'true' : undefined}\n />\n )}\n\n {status === 'initializing' && (\n <div className=\"uploader-camera-status\" aria-live=\"polite\">\n Starting camera…\n </div>\n )}\n\n {/* Overlaid on the stage rather than added to the actions row below,\n which balances Back against an invisible twin to centre the shutter —\n a fourth control there would push the shutter off-centre. */}\n {canFlip && (status === 'live' || status === 'initializing') && (\n <button\n type=\"button\"\n className=\"uploader-camera-flip\"\n aria-label={facing === 'user' ? 'Switch to rear camera' : 'Switch to front camera'}\n disabled={status !== 'live'}\n onClick={flipCamera}\n >\n <svg viewBox=\"0 0 24 24\" width=\"20\" height=\"20\" aria-hidden=\"true\" focusable=\"false\">\n <path\n d=\"M4 8.5A2.5 2.5 0 0 1 6.5 6h1.2l1-1.7a1 1 0 0 1 .87-.5h4.86a1 1 0 0 1 .86.5l1 1.7h1.21A2.5 2.5 0 0 1 20 8.5v8A2.5 2.5 0 0 1 17.5 19h-11A2.5 2.5 0 0 1 4 16.5v-8Z\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"1.6\"\n strokeLinejoin=\"round\"\n />\n <path\n d=\"M9.4 12.6a2.6 2.6 0 0 0 4.45 1.6M14.6 11.4a2.6 2.6 0 0 0-4.45-1.6\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"1.6\"\n strokeLinecap=\"round\"\n />\n <path\n d=\"m9.4 10.2-.1 2.1 2.1-.6M14.6 13.8l.1-2.1-2.1.6\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"1.6\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n </svg>\n </button>\n )}\n </div>\n\n {status === 'live' && (\n <p className=\"uploader-camera-hint\">Frame your shot, then tap the shutter to capture.</p>\n )}\n\n <div className=\"uploader-camera-actions\">\n {status === 'captured' ? (\n <>\n <button type=\"button\" className=\"uploader-btn-secondary\" onClick={retake}>\n Retake\n </button>\n <button type=\"button\" className=\"uploader-btn-primary\" onClick={confirm}>\n Use photo\n </button>\n </>\n ) : status === 'error' ? (\n <>\n <button type=\"button\" className=\"uploader-btn-secondary\" onClick={onCancel}>\n Back\n </button>\n <button type=\"button\" className=\"uploader-btn-primary\" onClick={() => void startStream()}>\n Try again\n </button>\n </>\n ) : (\n <>\n <button type=\"button\" className=\"uploader-btn-secondary\" onClick={onCancel}>\n Back\n </button>\n <button\n type=\"button\"\n className=\"uploader-camera-shutter\"\n aria-label=\"Capture photo\"\n disabled={status !== 'live'}\n onClick={capture}\n >\n <span className=\"uploader-camera-shutter-ring\" aria-hidden=\"true\" />\n </button>\n {/* Invisible twin of the Back button — balances the row so the\n shutter lands dead-center regardless of the label's width. */}\n <div className=\"uploader-camera-actions-spacer\" aria-hidden=\"true\">\n <span className=\"uploader-btn-secondary\">Back</span>\n </div>\n </>\n )}\n </div>\n </div>\n )\n}\n","/**\n * ProgressBar — standalone progress-bar primitive for the SDK picker.\n *\n * Renders a thin filled track using only --uploader-* CSS variables so the\n * color automatically follows the host app's theme override. No external\n * dependencies; no hard-coded brand colors.\n *\n * Usage:\n * <ProgressBar value={62} label=\"invoice.pdf upload progress\" />\n * <ProgressBar value={72} size=\"lg\" label=\"Overall upload progress\" />\n */\n\nimport React from 'react'\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport type ProgressBarSize = 'sm' | 'lg'\n\nexport type ProgressBarProps = {\n /** Progress value 0–100. */\n value: number\n /**\n * Accessible label for screen readers (maps to aria-label on the track).\n * Required — callers must describe what this bar tracks.\n */\n label: string\n /** Visual size variant. 'sm' = 5px (per-file), 'lg' = 8px (aggregate). Default: 'sm'. */\n size?: ProgressBarSize\n /** Additional CSS class(es) on the track element. */\n className?: string\n /** Inline styles on the track element. */\n style?: React.CSSProperties\n}\n\n// ─── Component ────────────────────────────────────────────────────────────────\n\nexport function ProgressBar({ value, label, size = 'sm', className, style }: ProgressBarProps) {\n const clamped = Math.max(0, Math.min(100, value))\n\n return (\n <div\n role=\"progressbar\"\n aria-valuenow={clamped}\n aria-valuemin={0}\n aria-valuemax={100}\n aria-label={label}\n data-size={size}\n className={['uploader-progress-bar', className ?? ''].filter(Boolean).join(' ')}\n style={style}\n >\n <div\n className=\"uploader-progress-bar-fill\"\n style={{ width: `${clamped}%` }}\n />\n </div>\n )\n}\n","/**\n * FileQueueRow — shared file-queue row primitive for the SDK picker.\n *\n * Renders a single file entry: 38×38 thumbnail swatch, file name, upload\n * status/percentage, thin progress bar, and action slots (remove, retry, edit).\n * All colors resolve from --uploader-* CSS variables — no hard-coded brand\n * colors — so the component adapts to any host-app theme.\n *\n * Used by PickerOverlay (#203) and DropPane (#204). Callers supply file state\n * and event handlers; this component is display-only with no internal state.\n */\n\nimport { ProgressBar } from './ProgressBar.js'\nimport type { PickerFile } from './types.js'\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport type FileQueueRowProps = {\n /** The file entry to display. */\n entry: PickerFile\n /** Called when the user clicks the remove (×) button. */\n onRemove: (id: string) => void\n /** Called when the user clicks the retry button (shown on error). */\n onRetry: (id: string) => void\n /**\n * Called when the user clicks the Edit button (shown for image files in\n * pending/error state). If not provided, the Edit button is never rendered.\n */\n onEdit?: (id: string) => void\n}\n\n// ─── Helpers ─────────────────────────────────────────────────────────────────\n\nfunction statusLabel(entry: PickerFile): string {\n switch (entry.state) {\n case 'uploading':\n return entry.progress != null ? `${entry.progress}%` : 'Uploading…'\n case 'done':\n return 'Done'\n case 'error':\n return entry.error ?? 'Failed'\n default:\n return 'Ready'\n }\n}\n\n// ─── Component ────────────────────────────────────────────────────────────────\n\nexport function FileQueueRow({ entry, onRemove, onRetry, onEdit }: FileQueueRowProps) {\n const { id, file, state, progress, previewUrl } = entry\n const isUploading = state === 'uploading'\n\n return (\n <li className={`uploader-file-item uploader-file-${state}`}>\n {/* Thumbnail */}\n {previewUrl ? (\n <img\n src={previewUrl}\n alt=\"\"\n aria-hidden=\"true\"\n className=\"uploader-file-thumb\"\n />\n ) : (\n <div className=\"uploader-file-thumb-placeholder\" aria-hidden=\"true\" />\n )}\n\n {/* File info + progress */}\n <div className=\"uploader-file-info\">\n <div className=\"uploader-file-info-row\">\n <span className=\"uploader-file-name\">{file.name}</span>\n <span\n className=\"uploader-file-status\"\n data-state={state}\n role={state === 'error' ? 'alert' : undefined}\n >\n {statusLabel(entry)}\n </span>\n </div>\n <ProgressBar\n value={isUploading && progress != null ? progress : state === 'done' ? 100 : 0}\n label={`${file.name} upload progress`}\n />\n </div>\n\n {/* Done badge */}\n {state === 'done' && (\n <div className=\"uploader-file-done-badge\" aria-hidden=\"true\">\n {/* Checkmark rendered via SVG to avoid icon-library dependency */}\n <svg width=\"9\" height=\"7\" viewBox=\"0 0 9 7\" fill=\"none\" aria-hidden=\"true\">\n <path\n d=\"M1 3.5L3.5 6L8 1\"\n stroke=\"currentColor\"\n strokeWidth=\"1.8\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n </svg>\n </div>\n )}\n\n {/* Remove / retry / edit actions */}\n {!isUploading && (\n <div className=\"uploader-file-actions\">\n {onEdit && previewUrl && (state === 'pending' || state === 'error') && (\n <button\n type=\"button\"\n className=\"uploader-btn-edit\"\n aria-label={`Edit ${file.name}`}\n onClick={() => onEdit(id)}\n >\n Edit\n </button>\n )}\n {state === 'error' && (\n <button\n type=\"button\"\n className=\"uploader-btn-retry\"\n aria-label={`Retry uploading ${file.name}`}\n onClick={() => onRetry(id)}\n >\n Retry\n </button>\n )}\n <button\n type=\"button\"\n className=\"uploader-btn-remove\"\n aria-label={`Remove ${file.name}`}\n onClick={() => onRemove(id)}\n >\n ×\n </button>\n </div>\n )}\n </li>\n )\n}\n","/**\n * Theming — map a plain token object to scoped CSS custom properties.\n *\n * The SDK is styled entirely from `--uploader-*` CSS custom properties (see\n * styles.css). A host React app can override them in its own stylesheet, but\n * that overrides every instance on the page and requires writing CSS.\n *\n * The `theme` prop on PickerOverlay / DropPane lets a host pass those tokens as\n * a plain object instead. `themeToVars` converts the object into inline CSS\n * variables applied to the component's own root element, so the override is\n * **scoped to that instance** — two pickers on one page can carry different\n * themes, and nothing leaks to the rest of the app.\n *\n * Only the keys you set are emitted; everything else falls back to the shipped\n * defaults. Fully backward compatible — omit `theme` and behaviour is unchanged.\n *\n * @example\n * <PickerOverlay\n * theme={{ accent: '#4f46e5', accentSoft: '#eef2ff', radius: '12px' }}\n * apikey=\"pk_...\"\n * open={open}\n * onClose={close}\n * />\n */\n\nimport type React from 'react'\n\n/**\n * Host-overridable design tokens. Each maps to one `--uploader-*` CSS variable.\n * All optional — set only what you want to change. Values are raw CSS strings\n * (e.g. '#4f46e5', 'rgba(79,70,229,.28)', '12px', '0.2s ease').\n */\nexport type UploaderTheme = {\n /** Primary brand color. → `--uploader-accent` */\n accent?: string\n /** Hover/active shade of the accent. → `--uploader-accent-hover` */\n accentHover?: string\n /** Tinted fill for icon swatches and hover backgrounds. → `--uploader-accent-soft` */\n accentSoft?: string\n /** Focus ring / drag-over glow (≈20–30% opacity of accent). → `--uploader-accent-ring` */\n accentRing?: string\n\n /** Corner radius for panels, buttons, inputs. → `--uploader-radius` */\n radius?: string\n /** Base surface / panel background. → `--uploader-bg` */\n background?: string\n /** Secondary surface (rows, wells). → `--uploader-surface` */\n surface?: string\n /** Hover state for secondary surfaces. → `--uploader-surface-hover` */\n surfaceHover?: string\n /** Default border color. → `--uploader-border` */\n border?: string\n /** Subtle/hairline border color. → `--uploader-border-subtle` */\n borderSubtle?: string\n\n /** Primary text color. → `--uploader-text` */\n text?: string\n /** Muted/secondary text. → `--uploader-text-muted` */\n textMuted?: string\n /** Extra-muted text (hints, captions). → `--uploader-text-xmuted` */\n textXMuted?: string\n\n /** Error color. → `--uploader-error` */\n error?: string\n /** Tinted error background. → `--uploader-error-soft` */\n errorSoft?: string\n /** Success color. → `--uploader-success` */\n success?: string\n\n /** Small elevation shadow. → `--uploader-shadow-sm` */\n shadowSm?: string\n /** Default elevation shadow. → `--uploader-shadow` */\n shadow?: string\n /** Large elevation shadow (modal panel). → `--uploader-shadow-lg` */\n shadowLg?: string\n\n /** Font family. Defaults to `inherit`. → `--uploader-font` */\n font?: string\n /** Transition timing. → `--uploader-transition` */\n transition?: string\n\n /** z-index of the modal backdrop. → `--uploader-z-backdrop` */\n zBackdrop?: number | string\n /** z-index of the modal panel. → `--uploader-z-panel` */\n zPanel?: number | string\n}\n\n/** Token key → CSS custom property name. */\nconst VAR_MAP: Record<keyof UploaderTheme, string> = {\n accent: '--uploader-accent',\n accentHover: '--uploader-accent-hover',\n accentSoft: '--uploader-accent-soft',\n accentRing: '--uploader-accent-ring',\n radius: '--uploader-radius',\n background: '--uploader-bg',\n surface: '--uploader-surface',\n surfaceHover: '--uploader-surface-hover',\n border: '--uploader-border',\n borderSubtle: '--uploader-border-subtle',\n text: '--uploader-text',\n textMuted: '--uploader-text-muted',\n textXMuted: '--uploader-text-xmuted',\n error: '--uploader-error',\n errorSoft: '--uploader-error-soft',\n success: '--uploader-success',\n shadowSm: '--uploader-shadow-sm',\n shadow: '--uploader-shadow',\n shadowLg: '--uploader-shadow-lg',\n font: '--uploader-font',\n transition: '--uploader-transition',\n zBackdrop: '--uploader-z-backdrop',\n zPanel: '--uploader-z-panel',\n}\n\n/**\n * Convert a theme object into a style object of CSS custom properties.\n *\n * Returns an empty object when `theme` is undefined/empty, so it is safe to\n * spread unconditionally. `undefined` token values are skipped (no var emitted),\n * letting the shipped default apply.\n *\n * Merge it ahead of any host `style` so the host can still override per-instance:\n * style={{ ...themeToVars(theme), ...style }}\n */\nexport function themeToVars(theme?: UploaderTheme): React.CSSProperties {\n if (!theme) return {}\n const vars: Record<string, string> = {}\n for (const key of Object.keys(theme) as Array<keyof UploaderTheme>) {\n const value = theme[key]\n if (value === undefined || value === null) continue\n vars[VAR_MAP[key]] = String(value)\n }\n return vars as React.CSSProperties\n}\n","/**\n * DropPane — inline drag-and-drop upload zone.\n *\n * A non-modal file drop target that renders in-place. Suitable for embedding\n * directly in a page (e.g. a form upload field) rather than as a modal overlay.\n *\n * Composes the Dropzone, FileQueueRow, and ProgressBar primitives from the\n * #194 foundation. All colors resolve from --uploader-* CSS variables —\n * no hard-coded brand colors.\n *\n * States:\n * idle — Dropzone shown, queue empty\n * dragging-over — Dropzone fills with accent-soft, label changes to\n * \"Drop to add files\" (handled inside Dropzone via isDragOver)\n * queued — Dropzone collapses; file list + Upload/Cancel shown\n * uploading — File rows show per-file progress; aggregate ProgressBar shown\n * done — Files show done state; onUploadDone fires\n *\n * The host supplies any surrounding form chrome (labels, Skip/Continue buttons)\n * as normal JSX siblings — DropPane does not render those.\n *\n * @dsCard DropPane\n */\n\nimport React, { useState, useCallback } from 'react'\nimport { usePicker } from './usePicker.js'\nimport { Dropzone } from './Dropzone.js'\nimport { CameraCapture, shouldOfferCamera } from './CameraCapture.js'\nimport { FileQueueRow } from './FileQueueRow.js'\nimport { ProgressBar } from './ProgressBar.js'\nimport { themeToVars } from './theme.js'\nimport type { UploaderTheme } from './theme.js'\nimport type { SharedPickerProps } from './types.js'\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport type DropPaneProps = SharedPickerProps & {\n /**\n * Hint text shown below the Browse button in the dropzone.\n * Default: \"or click to browse · max 50 MB\"\n */\n hint?: string\n /**\n * Design tokens to override, scoped to this pane instance. Maps to\n * `--uploader-*` CSS variables applied on the root element — see UploaderTheme.\n * Omit to use the shipped defaults (or page-level CSS-variable overrides).\n */\n theme?: UploaderTheme\n /** Additional CSS class(es) applied to the root element. */\n className?: string\n /** Inline styles applied to the root element. */\n style?: React.CSSProperties\n}\n\n// ─── Component ────────────────────────────────────────────────────────────────\n\nexport function DropPane({\n apikey,\n apiUrl,\n security,\n deliveryProtection,\n allowedOrigins,\n pickerOptions,\n onUploadDone,\n onFileUploadFinished,\n onFileUploadFailed,\n onCancel,\n hint = 'or click to browse · max 50 MB',\n theme,\n className,\n style,\n}: DropPaneProps) {\n const [isDragOver, setIsDragOver] = useState(false)\n const [cameraOpen, setCameraOpen] = useState(false)\n const rootStyle = { ...themeToVars(theme), ...style }\n\n const { files, addFiles, removeFile, retryFile, upload, progress, isUploading, isDone } =\n usePicker({\n apikey,\n apiUrl,\n security,\n deliveryProtection,\n allowedOrigins,\n pickerOptions,\n onUploadDone,\n onFileUploadFinished,\n onFileUploadFailed,\n })\n\n // ── Drag handlers ────────────────────────────────────────────────────────\n const handleDragEnter = useCallback((e: React.DragEvent) => {\n e.preventDefault()\n e.stopPropagation()\n setIsDragOver(true)\n }, [])\n\n const handleDragLeave = useCallback((e: React.DragEvent) => {\n e.preventDefault()\n e.stopPropagation()\n setIsDragOver(false)\n }, [])\n\n const handleDragOver = useCallback((e: React.DragEvent) => {\n e.preventDefault()\n e.stopPropagation()\n }, [])\n\n const handleDrop = useCallback(\n (e: React.DragEvent) => {\n e.preventDefault()\n e.stopPropagation()\n setIsDragOver(false)\n if (e.dataTransfer.files.length > 0) {\n addFiles(e.dataTransfer.files)\n }\n },\n [addFiles],\n )\n\n const hasPending = files.some((f) => f.state === 'pending')\n const pendingCount = files.filter((f) => f.state === 'pending').length\n const accept = pickerOptions?.accept?.join(',')\n const multiple = !pickerOptions?.maxFiles || pickerOptions.maxFiles > 1\n const offerCamera = shouldOfferCamera(pickerOptions)\n\n // Camera capture view replaces the whole pane while active.\n if (cameraOpen) {\n return (\n <div\n className={['uploader-drop-pane', className ?? ''].filter(Boolean).join(' ')}\n data-testid=\"drop-pane\"\n style={rootStyle}\n >\n <CameraCapture\n facingMode={pickerOptions?.cameraFacingMode}\n onCancel={() => setCameraOpen(false)}\n onCapture={(file) => {\n addFiles([file])\n setCameraOpen(false)\n }}\n />\n </div>\n )\n }\n\n return (\n <div\n className={['uploader-drop-pane', className ?? ''].filter(Boolean).join(' ')}\n data-drag-over={isDragOver ? 'true' : undefined}\n data-testid=\"drop-pane\"\n style={rootStyle}\n >\n {/* Dropzone — shown until files are queued */}\n {files.length === 0 && (\n <Dropzone\n onFiles={addFiles}\n isDragOver={isDragOver}\n onDragEnter={handleDragEnter}\n onDragLeave={handleDragLeave}\n onDragOver={handleDragOver}\n onDrop={handleDrop}\n accept={accept}\n multiple={multiple}\n onCamera={offerCamera ? () => setCameraOpen(true) : undefined}\n hint={hint}\n />\n )}\n\n {/* Drag-over overlay on the pane when files already queued */}\n {files.length > 0 && isDragOver && (\n <div\n className=\"uploader-drop-pane-drag-overlay\"\n onDragEnter={handleDragEnter}\n onDragLeave={handleDragLeave}\n onDragOver={handleDragOver}\n onDrop={handleDrop}\n aria-hidden=\"true\"\n >\n <span className=\"uploader-drop-pane-drag-label\">Drop to add files</span>\n </div>\n )}\n\n {/* File queue */}\n {files.length > 0 && (\n <ul\n className=\"uploader-file-list uploader-drop-pane-file-list\"\n role=\"list\"\n aria-label=\"Selected files\"\n aria-live=\"polite\"\n >\n {files.map((entry) => (\n <FileQueueRow\n key={entry.id}\n entry={entry}\n onRemove={removeFile}\n onRetry={retryFile}\n />\n ))}\n </ul>\n )}\n\n {/* Aggregate progress — shown while uploading */}\n {isUploading && (\n <div\n className=\"uploader-aggregate-progress\"\n role=\"status\"\n aria-live=\"polite\"\n >\n <ProgressBar\n value={progress}\n label=\"Overall upload progress\"\n size=\"lg\"\n />\n <span className=\"uploader-aggregate-progress-pct\">{progress}%</span>\n </div>\n )}\n\n {/* Actions — Upload / Cancel (host supplies any surrounding form buttons) */}\n {files.length > 0 && !isDone && (\n <div className=\"uploader-drop-pane-actions\">\n <button\n type=\"button\"\n className=\"uploader-btn-secondary uploader-btn-cancel\"\n disabled={isUploading}\n onClick={onCancel}\n >\n Cancel\n </button>\n {hasPending && (\n <button\n type=\"button\"\n className=\"uploader-btn-primary uploader-btn-upload\"\n disabled={isUploading}\n onClick={() => void upload()}\n >\n {isUploading\n ? 'Uploading…'\n : `Upload${pendingCount > 0 ? ` ${pendingCount} file${pendingCount !== 1 ? 's' : ''}` : ''}`}\n </button>\n )}\n </div>\n )}\n </div>\n )\n}\n"],"mappings":"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","names":["useRef","useState","useCallback","useEffect","useCallback","useRef","useState","flip","crop","quality","quality","useState","flip","useRef","crop","useCallback","cbX","cbY","useRef","useCallback","jsx","jsxs","useCallback","useEffect","useRef","useState","jsx","jsxs","quality","jsx","jsx","jsxs","Fragment","jsx","jsxs","useRef","useState","useEffect","useCallback","useState","useCallback","jsx","jsxs","useState","useCallback"]}
|