uploaderkit 1.0.0 → 2.0.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.
Files changed (86) hide show
  1. package/LICENSE +21 -0
  2. package/NOTICE +16 -0
  3. package/README.es.md +1140 -0
  4. package/README.md +1135 -0
  5. package/dist/FileViewer-ChTf7-uY.d.cts +49 -0
  6. package/dist/FileViewer-nuD-GEdJ.d.ts +49 -0
  7. package/dist/adapters/gcs.cjs +83 -0
  8. package/dist/adapters/gcs.cjs.map +1 -0
  9. package/dist/adapters/gcs.d.cts +31 -0
  10. package/dist/adapters/gcs.d.ts +31 -0
  11. package/dist/adapters/gcs.js +81 -0
  12. package/dist/adapters/gcs.js.map +1 -0
  13. package/dist/adapters/memory.cjs +27 -0
  14. package/dist/adapters/memory.cjs.map +1 -0
  15. package/dist/adapters/memory.d.cts +18 -0
  16. package/dist/adapters/memory.d.ts +18 -0
  17. package/dist/adapters/memory.js +25 -0
  18. package/dist/adapters/memory.js.map +1 -0
  19. package/dist/adapters/s3.cjs +75 -0
  20. package/dist/adapters/s3.cjs.map +1 -0
  21. package/dist/adapters/s3.d.cts +27 -0
  22. package/dist/adapters/s3.d.ts +27 -0
  23. package/dist/adapters/s3.js +73 -0
  24. package/dist/adapters/s3.js.map +1 -0
  25. package/dist/chunk-3FI44IOW.js +150 -0
  26. package/dist/chunk-3FI44IOW.js.map +1 -0
  27. package/dist/chunk-H7BRW5IY.js +308 -0
  28. package/dist/chunk-H7BRW5IY.js.map +1 -0
  29. package/dist/chunk-PDKAF4GX.js +707 -0
  30. package/dist/chunk-PDKAF4GX.js.map +1 -0
  31. package/dist/chunk-PTEX7F4R.js +309 -0
  32. package/dist/chunk-PTEX7F4R.js.map +1 -0
  33. package/dist/chunk-T5YZG6JW.js +511 -0
  34. package/dist/chunk-T5YZG6JW.js.map +1 -0
  35. package/dist/index.cjs +220 -27
  36. package/dist/index.cjs.map +1 -1
  37. package/dist/index.d.cts +57 -170
  38. package/dist/index.d.ts +57 -170
  39. package/dist/index.js +1 -324
  40. package/dist/index.js.map +1 -1
  41. package/dist/presets.cjs +1665 -0
  42. package/dist/presets.cjs.map +1 -0
  43. package/dist/presets.d.cts +100 -0
  44. package/dist/presets.d.ts +100 -0
  45. package/dist/presets.js +405 -0
  46. package/dist/presets.js.map +1 -0
  47. package/dist/react.cjs +981 -0
  48. package/dist/react.cjs.map +1 -0
  49. package/dist/react.d.cts +106 -0
  50. package/dist/react.d.ts +106 -0
  51. package/dist/react.js +5 -0
  52. package/dist/react.js.map +1 -0
  53. package/dist/server/express.cjs +179 -0
  54. package/dist/server/express.cjs.map +1 -0
  55. package/dist/server/express.d.cts +64 -0
  56. package/dist/server/express.d.ts +64 -0
  57. package/dist/server/express.js +111 -0
  58. package/dist/server/express.js.map +1 -0
  59. package/dist/server/next.cjs +165 -0
  60. package/dist/server/next.cjs.map +1 -0
  61. package/dist/server/next.d.cts +38 -0
  62. package/dist/server/next.d.ts +38 -0
  63. package/dist/server/next.js +107 -0
  64. package/dist/server/next.js.map +1 -0
  65. package/dist/server.cjs +550 -0
  66. package/dist/server.cjs.map +1 -0
  67. package/dist/server.d.cts +19 -0
  68. package/dist/server.d.ts +19 -0
  69. package/dist/server.js +80 -0
  70. package/dist/server.js.map +1 -0
  71. package/dist/storage-CYkSHWZX.d.cts +133 -0
  72. package/dist/storage-Qc9epG0G.d.ts +133 -0
  73. package/dist/types-BSlJJwti.d.cts +341 -0
  74. package/dist/types-BSlJJwti.d.ts +341 -0
  75. package/dist/ui.cjs +2325 -0
  76. package/dist/ui.cjs.map +1 -0
  77. package/dist/ui.d.cts +331 -0
  78. package/dist/ui.d.ts +331 -0
  79. package/dist/ui.js +749 -0
  80. package/dist/ui.js.map +1 -0
  81. package/dist/useSlottedUploader-BzT5jV8c.d.cts +139 -0
  82. package/dist/useSlottedUploader-IkG5RhEO.d.ts +139 -0
  83. package/dist/useUploader-BiBdS-7y.d.cts +117 -0
  84. package/dist/useUploader-CQHpj_oI.d.ts +117 -0
  85. package/package.json +189 -3
  86. package/tailwind.css +80 -0
package/dist/ui.js.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/ui/ConfirmDialog.tsx","../src/ui/FileTypeBadge.tsx","../src/ui/ProgressBar.tsx","../src/ui/FileItem.tsx","../src/ui/SlotRow.tsx","../src/ui/useFileViewer.ts","../src/ui/SlottedUploader.tsx","../src/ui/StoredFileItem.tsx","../src/ui/Uploader.tsx"],"names":["jsx","jsxs","useRef","useState","useEffect","Fragment"],"mappings":"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","file":"ui.js","sourcesContent":["import { type ReactNode, useEffect, useRef } from 'react'\nimport { createPortal } from 'react-dom'\n\nimport type { UploaderLabels } from '../labels'\nimport { useUploaderLabels } from '../react/UploaderProvider'\nimport { cn } from './cn'\nimport { useOverlayLayer } from './overlayStack'\nimport { lockBodyScroll, unlockBodyScroll } from './scrollLock'\nimport { useFocusTrap } from './useFocusTrap'\nimport { useOverlayTransition } from './useOverlayTransition'\n\nexport type ConfirmDialogProps = {\n\topen: boolean\n\ttitle: string\n\tmessage: ReactNode\n\t/** Tone of the affirmative button. @defaultValue 'danger' */\n\tvariant?: 'danger' | 'primary'\n\tconfirmLabel?: string\n\tcancelLabel?: string\n\tonConfirm: () => void\n\tonCancel: () => void\n\tlabels?: Partial<UploaderLabels>\n}\n\n/**\n * Second-step gate for destructive file actions — remove a stored file,\n * replace a slot. Focus lands on **cancel** so a stray Enter never destroys\n * anything, Escape and the backdrop cancel, and `Tab` stays inside. Exported\n * for app-level use around the uploaders' callbacks.\n */\nexport const ConfirmDialog = ({\n\topen,\n\ttitle,\n\tmessage,\n\tvariant = 'danger',\n\tconfirmLabel,\n\tcancelLabel,\n\tonConfirm,\n\tonCancel,\n\tlabels,\n}: ConfirmDialogProps) => {\n\tconst copy = useUploaderLabels(labels)\n\tconst panelRef = useRef<HTMLDivElement>(null)\n\tconst { mounted, entered } = useOverlayTransition(open)\n\tconst layer = useOverlayLayer(open)\n\tuseFocusTrap(open, panelRef)\n\n\tconst cancelRef = useRef(onCancel)\n\tcancelRef.current = onCancel\n\n\tuseEffect(() => {\n\t\tif (!open) return\n\t\tconst onKey = (event: KeyboardEvent) => {\n\t\t\tif (event.key === 'Escape') {\n\t\t\t\tif (!layer.isTopmost()) return\n\t\t\t\tevent.stopPropagation()\n\t\t\t\tcancelRef.current()\n\t\t\t}\n\t\t}\n\t\t// Capture phase: both overlays claim the key before any host dialog in\n\t\t// bubble. Which of the two answers is decided by the layer stack.\n\t\twindow.addEventListener('keydown', onKey, true)\n\t\tlockBodyScroll()\n\t\treturn () => {\n\t\t\twindow.removeEventListener('keydown', onKey, true)\n\t\t\tunlockBodyScroll()\n\t\t}\n\t}, [open, layer])\n\n\tif (!mounted) return null\n\n\treturn createPortal(\n\t\t<div\n\t\t\tclassName={cn(\n\t\t\t\t'fixed inset-0 z-1000 flex items-end justify-center p-4 sm:items-center',\n\t\t\t\t!open && 'pointer-events-none'\n\t\t\t)}\n\t\t\tonClick={onCancel}\n\t\t>\n\t\t\t<div\n\t\t\t\taria-hidden\n\t\t\t\tclassName={cn(\n\t\t\t\t\t'bg-ui-overlay fixed inset-0 transition-opacity duration-200 ease-out',\n\t\t\t\t\tentered ? 'opacity-100' : 'opacity-0'\n\t\t\t\t)}\n\t\t\t/>\n\t\t\t<div\n\t\t\t\tref={panelRef}\n\t\t\t\trole='alertdialog'\n\t\t\t\taria-modal='true'\n\t\t\t\taria-label={title}\n\t\t\t\ttabIndex={-1}\n\t\t\t\tonClick={event => event.stopPropagation()}\n\t\t\t\tclassName={cn(\n\t\t\t\t\t'bg-ui-surface rounded-ui-xl relative w-full max-w-md p-6 shadow-2xl outline-none',\n\t\t\t\t\t'transition duration-200 ease-out',\n\t\t\t\t\tentered\n\t\t\t\t\t\t? 'translate-y-0 scale-100 opacity-100'\n\t\t\t\t\t\t: 'translate-y-3 scale-[0.97] opacity-0'\n\t\t\t\t)}\n\t\t\t>\n\t\t\t\t<div className='flex items-start gap-4'>\n\t\t\t\t\t<span\n\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t'flex size-11 shrink-0 items-center justify-center rounded-full',\n\t\t\t\t\t\t\tvariant === 'danger' ? 'bg-ui-danger-soft' : 'bg-ui-primary-soft'\n\t\t\t\t\t\t)}\n\t\t\t\t\t>\n\t\t\t\t\t\t<svg\n\t\t\t\t\t\t\tviewBox='0 0 24 24'\n\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t'size-6',\n\t\t\t\t\t\t\t\tvariant === 'danger' ? 'text-ui-danger' : 'text-ui-primary'\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\tfill='none'\n\t\t\t\t\t\t\tstroke='currentColor'\n\t\t\t\t\t\t\tstrokeWidth='2'\n\t\t\t\t\t\t\tstrokeLinecap='round'\n\t\t\t\t\t\t\tstrokeLinejoin='round'\n\t\t\t\t\t\t\taria-hidden\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<path d='M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z' />\n\t\t\t\t\t\t\t<line x1='12' y1='9' x2='12' y2='13' />\n\t\t\t\t\t\t\t<line x1='12' y1='17' x2='12.01' y2='17' />\n\t\t\t\t\t\t</svg>\n\t\t\t\t\t</span>\n\n\t\t\t\t\t<div className='min-w-0 flex-1'>\n\t\t\t\t\t\t<h2 className='text-ui-fg text-base font-semibold'>{title}</h2>\n\t\t\t\t\t\t<div className='text-ui-muted mt-1 text-sm'>{message}</div>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\n\t\t\t\t<div className='mt-6 flex flex-col-reverse gap-2 sm:flex-row sm:justify-end'>\n\t\t\t\t\t<button\n\t\t\t\t\t\ttype='button'\n\t\t\t\t\t\tdata-autofocus\n\t\t\t\t\t\tonClick={onCancel}\n\t\t\t\t\t\tclassName='border-ui-border-strong text-ui-fg hover:bg-ui-surface-muted focus-visible:ring-ui-ring rounded-ui h-10 cursor-pointer border px-4 text-sm font-medium transition-colors focus-visible:ring-2 focus-visible:outline-none sm:w-auto'\n\t\t\t\t\t>\n\t\t\t\t\t\t{cancelLabel ?? copy.cancel}\n\t\t\t\t\t</button>\n\t\t\t\t\t<button\n\t\t\t\t\t\ttype='button'\n\t\t\t\t\t\tonClick={onConfirm}\n\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t'focus-visible:ring-ui-ring rounded-ui h-10 cursor-pointer px-4 text-sm font-medium transition-colors focus-visible:ring-2 focus-visible:outline-none sm:w-auto',\n\t\t\t\t\t\t\tvariant === 'danger'\n\t\t\t\t\t\t\t\t? 'bg-ui-danger text-ui-danger-fg hover:bg-ui-danger-hover'\n\t\t\t\t\t\t\t\t: 'bg-ui-primary text-ui-primary-fg hover:bg-ui-primary-hover'\n\t\t\t\t\t\t)}\n\t\t\t\t\t>\n\t\t\t\t\t\t{confirmLabel ?? copy.confirm}\n\t\t\t\t\t</button>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>,\n\t\tdocument.body\n\t)\n}\n","import { getFileExtension } from '../file'\nimport { cn } from './cn'\n\nexport type FileTypeBadgeProps = {\n\t/** Omit for an empty position — the same square, dashed. */\n\tfileName?: string\n\t/** Local thumbnail for images; falls back to the extension chip. */\n\tpreview?: string\n\t/** @defaultValue 'md' */\n\tsize?: 'sm' | 'md'\n}\n\n/**\n * The 40x40 leading square of a file row: the image thumbnail when there is\n * one, the extension in a grey chip when there is a file, and a dashed\n * placeholder when the position is empty. Same size in all three, so a row does\n * not jump the moment a file lands in it.\n */\nexport const FileTypeBadge = ({\n\tfileName,\n\tpreview,\n\tsize = 'md',\n}: FileTypeBadgeProps) => {\n\tconst box = size === 'sm' ? 'h-8 w-8' : 'h-10 w-10'\n\tif (!fileName) {\n\t\treturn (\n\t\t\t<span\n\t\t\t\taria-hidden='true'\n\t\t\t\tclassName={cn(\n\t\t\t\t\t'border-ui-border rounded-ui shrink-0 border border-dashed',\n\t\t\t\t\tbox\n\t\t\t\t)}\n\t\t\t/>\n\t\t)\n\t}\n\treturn preview ? (\n\t\t<img\n\t\t\tsrc={preview}\n\t\t\talt=''\n\t\t\tclassName={cn('rounded-ui shrink-0 object-cover', box)}\n\t\t/>\n\t) : (\n\t\t<span\n\t\t\tclassName={cn(\n\t\t\t\t'bg-ui-surface-muted text-ui-muted rounded-ui flex shrink-0 items-center justify-center font-medium uppercase',\n\t\t\t\tsize === 'sm' ? 'text-[10px]' : 'text-xs',\n\t\t\t\tbox\n\t\t\t)}\n\t\t>\n\t\t\t{getFileExtension(fileName)}\n\t\t</span>\n\t)\n}\n","import { cn } from './cn'\n\nexport type ProgressBarProps = {\n\t/** 0–100. */\n\tpercent: number\n\tclassName?: string\n}\n\n/**\n * The thin in-flight bar under a file's name — a grey track with a blue fill.\n * Shared by every upload row so progress reads the same wherever it appears.\n */\nexport const ProgressBar = ({ percent, className }: ProgressBarProps) => (\n\t<div\n\t\tclassName={cn(\n\t\t\t'bg-ui-surface-muted mt-1 h-1 w-full overflow-hidden rounded',\n\t\t\tclassName\n\t\t)}\n\t>\n\t\t<div\n\t\t\tclassName='bg-ui-primary h-full transition-all'\n\t\t\tstyle={{ width: `${percent}%` }}\n\t\t/>\n\t</div>\n)\n","import { formatFileSize } from '../file'\nimport type { UploaderLabels } from '../labels'\nimport type { UploaderFile } from '../react'\nimport { useUploaderLabels } from '../react/UploaderProvider'\nimport { cn } from './cn'\nimport { FileTypeBadge } from './FileTypeBadge'\nimport { ProgressBar } from './ProgressBar'\n\nexport type FileItemProps = {\n\tfile: UploaderFile\n\tonRemove: () => void\n\tonAbort: () => void\n\t/** Present on successes when a viewer is available. */\n\tonView?: () => void\n\t/** @defaultValue 'md' */\n\tsize?: 'sm' | 'md'\n\tlabels?: Partial<UploaderLabels>\n}\n\n/**\n * One row of upload state: preview/extension badge, name, size, a progress\n * bar while in flight, the error when it failed. Shared by both uploaders so\n * a file always looks the same wherever it is being uploaded.\n */\nexport const FileItem = ({\n\tfile,\n\tonRemove,\n\tonAbort,\n\tonView,\n\tsize = 'md',\n\tlabels,\n}: FileItemProps) => {\n\tconst copy = useUploaderLabels(labels)\n\tconst uploading = file.status === 'uploading'\n\n\treturn (\n\t\t<div\n\t\t\tclassName={cn(\n\t\t\t\t'rounded-ui animate-ui-fade-in flex items-center gap-3 border text-sm transition-colors',\n\t\t\t\tsize === 'sm' ? 'p-1.5' : 'p-2',\n\t\t\t\tfile.status === 'error'\n\t\t\t\t\t? 'border-ui-danger-soft-fg/20 bg-ui-danger-soft'\n\t\t\t\t\t: 'border-ui-border bg-ui-surface'\n\t\t\t)}\n\t\t>\n\t\t\t<FileTypeBadge\n\t\t\t\tfileName={file.file.name}\n\t\t\t\tpreview={file.preview}\n\t\t\t\tsize={size}\n\t\t\t/>\n\n\t\t\t<div className='min-w-0 flex-1'>\n\t\t\t\t<p className='text-ui-fg truncate font-medium'>{file.file.name}</p>\n\t\t\t\t{file.status === 'error' ? (\n\t\t\t\t\t<p className='text-ui-danger text-xs'>{file.error}</p>\n\t\t\t\t) : (\n\t\t\t\t\t<p className='text-ui-muted text-xs'>\n\t\t\t\t\t\t{formatFileSize(file.file.size)}\n\t\t\t\t\t\t{file.status === 'success' && ` · ${copy.ready}`}\n\t\t\t\t\t\t{file.status === 'idle' && ` · ${copy.staged}`}\n\t\t\t\t\t</p>\n\t\t\t\t)}\n\t\t\t\t{uploading && <ProgressBar percent={file.progress} />}\n\t\t\t</div>\n\n\t\t\t{onView && (\n\t\t\t\t<button\n\t\t\t\t\ttype='button'\n\t\t\t\t\tonClick={onView}\n\t\t\t\t\tclassName='text-ui-primary hover:bg-ui-primary-soft focus-visible:ring-ui-ring rounded-ui shrink-0 cursor-pointer px-2 py-1 text-xs transition-colors focus-visible:ring-2 focus-visible:outline-none'\n\t\t\t\t>\n\t\t\t\t\t{copy.view}\n\t\t\t\t</button>\n\t\t\t)}\n\t\t\t<button\n\t\t\t\ttype='button'\n\t\t\t\tonClick={uploading ? onAbort : onRemove}\n\t\t\t\tclassName='text-ui-muted hover:bg-ui-surface-muted hover:text-ui-fg focus-visible:ring-ui-ring rounded-ui shrink-0 cursor-pointer px-2 py-1 text-xs transition-colors focus-visible:ring-2 focus-visible:outline-none'\n\t\t\t>\n\t\t\t\t{uploading ? copy.cancel : copy.remove}\n\t\t\t</button>\n\t\t</div>\n\t)\n}\n","import { type DragEvent, useRef, useState } from 'react'\n\nimport { formatFileSize } from '../file'\nimport type { UploaderLabels } from '../labels'\nimport type { SlotState } from '../react'\nimport { useUploaderLabels } from '../react/UploaderProvider'\nimport { cn } from './cn'\nimport { FileTypeBadge } from './FileTypeBadge'\nimport { ProgressBar } from './ProgressBar'\n\nexport type SlotRowProps = {\n\tslot: SlotState\n\t/** Fill this one slot from its own browse button. */\n\tonPick: (file: File) => void\n\tonRemove: () => void\n\tonAbort: () => void\n\tonView: () => void\n\t/** @defaultValue 'md' */\n\tsize?: 'sm' | 'md'\n\tlabels?: Partial<UploaderLabels>\n}\n\n/**\n * One named position of a `SlottedUploader`: a status dot, a thumbnail, the\n * slot's label, whatever occupies it, and the actions for that state.\n *\n * @remarks\n * Four occupancies, not two: under `uploadOn: 'submit'` a picked file waits at\n * `idle` in `pending` with no `filled`, and a row branching on `filled` alone\n * rendered it as an empty slot — with no way to undo the pick.\n */\nexport const SlotRow = ({\n\tslot,\n\tonPick,\n\tonRemove,\n\tonAbort,\n\tonView,\n\tsize = 'md',\n\tlabels,\n}: SlotRowProps) => {\n\tconst copy = useUploaderLabels(labels)\n\tconst inputRef = useRef<HTMLInputElement>(null)\n\tconst [dropping, setDropping] = useState(false)\n\t// Depth counter — see Dropzone.\n\tconst depth = useRef(0)\n\tconst { definition, filled, pending } = slot\n\tconst uploading = pending?.status === 'uploading'\n\t/** Picked but not sent yet — the `uploadOn: 'submit'` resting state. */\n\tconst staged = pending?.status === 'idle'\n\tconst occupied = Boolean(filled) || staged\n\tconst droppable = !uploading\n\n\t// The row itself is a drop target: dragging a file over a filled slot\n\t// reads as \"replace this one\", over an empty one as \"fill it\" — no trip\n\t// through the bulk zone and its matcher.\n\tconst onDrop = (event: DragEvent) => {\n\t\tevent.preventDefault()\n\t\tevent.stopPropagation()\n\t\tdepth.current = 0\n\t\tsetDropping(false)\n\t\tif (!droppable) return\n\t\tconst file = event.dataTransfer.files[0]\n\t\tif (file) onPick(file)\n\t}\n\n\tconst action =\n\t\t'focus-visible:ring-ui-ring rounded-ui shrink-0 cursor-pointer px-2 py-1 text-xs transition-colors focus-visible:ring-2 focus-visible:outline-none'\n\n\treturn (\n\t\t<div\n\t\t\tonDragEnter={() => {\n\t\t\t\tdepth.current += 1\n\t\t\t\tif (droppable) setDropping(true)\n\t\t\t}}\n\t\t\tonDragOver={event => {\n\t\t\t\tevent.preventDefault()\n\t\t\t\tevent.stopPropagation()\n\t\t\t}}\n\t\t\tonDragLeave={() => {\n\t\t\t\tdepth.current = Math.max(0, depth.current - 1)\n\t\t\t\tif (depth.current === 0) setDropping(false)\n\t\t\t}}\n\t\t\tonDrop={onDrop}\n\t\t\tclassName={cn(\n\t\t\t\t'rounded-ui animate-ui-fade-in flex items-center gap-3 border text-sm transition-all duration-200',\n\t\t\t\tsize === 'sm' ? 'p-1.5' : 'p-2',\n\t\t\t\tdropping\n\t\t\t\t\t? 'border-ui-primary bg-ui-primary-soft ring-ui-ring scale-[1.01] ring-2'\n\t\t\t\t\t: 'border-ui-border'\n\t\t\t)}\n\t\t>\n\t\t\t<span\n\t\t\t\tclassName={cn(\n\t\t\t\t\t'h-2.5 w-2.5 shrink-0 rounded-full transition-colors',\n\t\t\t\t\tpending?.status === 'error'\n\t\t\t\t\t\t? 'bg-ui-danger'\n\t\t\t\t\t\t: uploading\n\t\t\t\t\t\t\t? 'bg-ui-primary animate-pulse'\n\t\t\t\t\t\t\t: staged\n\t\t\t\t\t\t\t\t? 'bg-ui-warning'\n\t\t\t\t\t\t\t\t: filled\n\t\t\t\t\t\t\t\t\t? 'bg-ui-success'\n\t\t\t\t\t\t\t\t\t: 'bg-ui-border-strong'\n\t\t\t\t)}\n\t\t\t/>\n\n\t\t\t{/* Reserved in every state, so picking a file does not change the\n\t\t\t row's height. */}\n\t\t\t<FileTypeBadge\n\t\t\t\tfileName={pending?.file.name ?? filled?.stored.fileName}\n\t\t\t\tpreview={pending?.preview}\n\t\t\t\tsize={size}\n\t\t\t/>\n\n\t\t\t<div className='min-w-0 flex-1'>\n\t\t\t\t<p className='text-ui-fg truncate font-medium'>{definition.label}</p>\n\t\t\t\t{pending?.status === 'error' ? (\n\t\t\t\t\t<p className='text-ui-danger text-xs'>{pending.error}</p>\n\t\t\t\t) : pending ? (\n\t\t\t\t\t// The name is the STORAGE one: `assign` renames the pick to\n\t\t\t\t\t// `{slot}.{ext}` before it reaches the machine.\n\t\t\t\t\t<p className='text-ui-muted truncate text-xs'>\n\t\t\t\t\t\t{pending.file.name} · {formatFileSize(pending.file.size)}\n\t\t\t\t\t\t{staged && ` · ${copy.staged}`}\n\t\t\t\t\t</p>\n\t\t\t\t) : filled ? (\n\t\t\t\t\t<button\n\t\t\t\t\t\ttype='button'\n\t\t\t\t\t\tonClick={onView}\n\t\t\t\t\t\tclassName='text-ui-muted hover:text-ui-primary block max-w-full cursor-pointer truncate text-left text-xs hover:underline'\n\t\t\t\t\t>\n\t\t\t\t\t\t{filled.stored.fileName}\n\t\t\t\t\t</button>\n\t\t\t\t) : (\n\t\t\t\t\t<p className='text-ui-placeholder text-xs'>\n\t\t\t\t\t\t{definition.hint ??\n\t\t\t\t\t\t\tdefinition.extensions.map(e => `.${e}`).join(' · ')}\n\t\t\t\t\t</p>\n\t\t\t\t)}\n\t\t\t\t{uploading && <ProgressBar percent={pending.progress} />}\n\t\t\t</div>\n\n\t\t\t<input\n\t\t\t\tref={inputRef}\n\t\t\t\ttype='file'\n\t\t\t\taccept={definition.extensions.map(e => `.${e}`).join(',')}\n\t\t\t\tclassName='hidden'\n\t\t\t\tonChange={event => {\n\t\t\t\t\tconst file = event.target.files?.[0]\n\t\t\t\t\tif (file) onPick(file)\n\t\t\t\t\tevent.target.value = ''\n\t\t\t\t}}\n\t\t\t/>\n\t\t\t{dropping && (\n\t\t\t\t<span className='bg-ui-primary text-ui-primary-fg rounded-full px-2.5 py-1 text-[11px] font-medium whitespace-nowrap'>\n\t\t\t\t\t{occupied ? copy.dropToReplace : copy.upload}\n\t\t\t\t</span>\n\t\t\t)}\n\t\t\t{!dropping && filled && !uploading && (\n\t\t\t\t<button\n\t\t\t\t\ttype='button'\n\t\t\t\t\tonClick={onView}\n\t\t\t\t\tclassName={cn(action, 'text-ui-primary hover:bg-ui-primary-soft')}\n\t\t\t\t>\n\t\t\t\t\t{copy.view}\n\t\t\t\t</button>\n\t\t\t)}\n\t\t\t{uploading ? (\n\t\t\t\t<button\n\t\t\t\t\ttype='button'\n\t\t\t\t\tonClick={onAbort}\n\t\t\t\t\tclassName={cn(action, 'text-ui-muted hover:bg-ui-surface-muted')}\n\t\t\t\t>\n\t\t\t\t\t{copy.cancel}\n\t\t\t\t</button>\n\t\t\t) : dropping ? null : (\n\t\t\t\t<>\n\t\t\t\t\t<button\n\t\t\t\t\t\ttype='button'\n\t\t\t\t\t\tonClick={() => inputRef.current?.click()}\n\t\t\t\t\t\tclassName={cn(action, 'text-ui-primary hover:bg-ui-primary-soft')}\n\t\t\t\t\t>\n\t\t\t\t\t\t{occupied ? copy.replace : copy.upload}\n\t\t\t\t\t</button>\n\t\t\t\t\t{occupied && (\n\t\t\t\t\t\t<button\n\t\t\t\t\t\t\ttype='button'\n\t\t\t\t\t\t\tonClick={onRemove}\n\t\t\t\t\t\t\tclassName={cn(action, 'text-ui-muted hover:bg-ui-surface-muted')}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{copy.remove}\n\t\t\t\t\t\t</button>\n\t\t\t\t\t)}\n\t\t\t\t</>\n\t\t\t)}\n\t\t</div>\n\t)\n}\n","import { useCallback, useMemo, useState } from 'react'\n\nimport type { FileViewerProps, ViewableFile } from './FileViewer'\n\nexport type UseFileViewerOptions = {\n\t/** Forwarded to the viewer — the authenticated resolver for private urls. */\n\tresolveUrl?: FileViewerProps['resolveUrl']\n}\n\nexport type UseFileViewerReturn = {\n\t/** Currently open file, `null` when closed. */\n\tviewing: ViewableFile | null\n\topen: (file: ViewableFile) => void\n\tclose: () => void\n\t/** Spread into `<FileViewer {...viewerProps} />` — the whole wiring. */\n\tviewerProps: Pick<FileViewerProps, 'file' | 'onClose' | 'resolveUrl'>\n}\n\n/**\n * The open/close state every screen with a `FileViewer` repeats. One call\n * replaces the `useState` + prop plumbing:\n *\n * ```tsx\n * const viewer = useFileViewer({ resolveUrl })\n * <button onClick={() => viewer.open(stored)}>Ver</button>\n * <FileViewer {...viewer.viewerProps} />\n * ```\n */\nexport const useFileViewer = ({\n\tresolveUrl,\n}: UseFileViewerOptions = {}): UseFileViewerReturn => {\n\tconst [viewing, setViewing] = useState<ViewableFile | null>(null)\n\tconst close = useCallback(() => setViewing(null), [])\n\tconst open = useCallback((file: ViewableFile) => setViewing(file), [])\n\n\treturn {\n\t\tviewing,\n\t\topen,\n\t\tclose,\n\t\tviewerProps: useMemo(\n\t\t\t() => ({ file: viewing, onClose: close, resolveUrl }),\n\t\t\t[viewing, close, resolveUrl]\n\t\t),\n\t}\n}\n","import { type ReactNode, useEffect, useState } from 'react'\n\nimport type { SlotState, UseSlottedUploaderOptions } from '../react'\nimport { useSlottedUploader } from '../react'\nimport type { RemoveStrategy } from '../react/types'\nimport { useUploaderLabels } from '../react/UploaderProvider'\nimport type { ScopeConfig } from '../types'\nimport { warnDev } from '../warn'\nimport { cn } from './cn'\nimport { ConfirmDialog } from './ConfirmDialog'\nimport type { UiUploadTrigger, UploaderControllerRef } from './controller'\nimport { Dropzone } from './Dropzone'\nimport { FileViewer, type ViewableFile } from './FileViewer'\nimport { UploadCloudIcon } from './icons'\nimport { SlotRow } from './SlotRow'\nimport { useCoarsePointer } from './useCoarsePointer'\nimport { useFileViewer } from './useFileViewer'\n\n/** A pending second step: the user asked for it, the dialog decides. */\ntype PendingAction =\n\t| { kind: 'remove'; slot: SlotState }\n\t| { kind: 'replace'; slot: SlotState; file: File }\n\nexport type SlottedUploaderProps<T extends Record<string, ScopeConfig>> = Omit<\n\tUseSlottedUploaderOptions<T>,\n\t'uploadOn'\n> & {\n\t/**\n\t * When the slots travel. This presentation has no upload button of its\n\t * own, so `'manual'` is not offered here: staged slots can only leave\n\t * through a form's `controllerRef`, which is `'submit'`.\n\t * @defaultValue 'select'\n\t * @see UiUploadTrigger\n\t */\n\tuploadOn?: Exclude<UiUploadTrigger, 'manual'>\n\ttitle?: string\n\tdescription?: string\n\t/** Hide the bulk dropzone and keep only per-row buttons. */\n\thideDropzone?: boolean\n\t/**\n\t * Gate removing a filled slot behind a confirmation dialog. `true` uses\n\t * the label defaults; an object overrides the copy.\n\t */\n\tconfirmRemove?: boolean | { title?: string; message?: string }\n\t/**\n\t * Confirm before a picked file replaces what a slot already holds. The\n\t * dialog names both files, so the user sees what is about to be lost.\n\t */\n\tconfirmReplace?: boolean | { title?: string; message?: string }\n\t/**\n\t * Deletion transport. With it, a confirmed slot removal deletes the object\n\t * from storage BY ITSELF — unless the scope is `keepOnRemove`, the history\n\t * contract — same wiring as `Uploader`.\n\t */\n\tremoveStrategy?: RemoveStrategy\n\t/** Fresh URL right before previewing — for private scopes whose signed url expired. */\n\tresolveViewUrl?: (file: ViewableFile) => Promise<string>\n\t/** Compact paddings and glyphs everywhere. @defaultValue 'md' */\n\tsize?: 'sm' | 'md'\n\t/** Glyph inside the bulk dropzone. `null` removes it. */\n\ticon?: ReactNode\n\t/** Keyboard shortcut that opens the bulk picker, e.g. `'mod+u'`. */\n\tshortcut?: string\n\t/**\n\t * Filled with the live controller so the surrounding form can send the\n\t * staged slots from its own submit. Required by `uploadOn: 'submit'`:\n\t * without it a staged slot has no way to ever leave.\n\t */\n\tcontrollerRef?: UploaderControllerRef<void>\n\t/**\n\t * Fires when the zone starts or stops holding files that `upload()`\n\t * would send. The companion of `controllerRef`: a form that defers the\n\t * send has no other way to know it has unsent work, so its save button\n\t * would stay disabled on a pristine form the user has just dropped a\n\t * file into.\n\t */\n\tonPendingChange?: (hasPending: boolean) => void\n\tclassName?: string\n}\n\n/**\n * Named-slot presentation over the same machine as `Uploader`: a status row\n * per slot plus one bulk dropzone whose matcher routes each file to its slot.\n */\nexport const SlottedUploader = <T extends Record<string, ScopeConfig>>({\n\tuploadOn = 'select',\n\ttitle,\n\tdescription,\n\thideDropzone = false,\n\tconfirmRemove,\n\tconfirmReplace,\n\tremoveStrategy,\n\tresolveViewUrl,\n\tsize = 'md',\n\ticon,\n\tshortcut,\n\tcontrollerRef,\n\tonPendingChange,\n\tclassName,\n\t...options\n}: SlottedUploaderProps<T>) => {\n\tconst slotted = useSlottedUploader({\n\t\t...options,\n\t\tuploadOn: uploadOn === 'select' ? 'select' : 'manual',\n\t})\n\n\tif (uploadOn === 'submit' && !controllerRef) {\n\t\twarnDev(\n\t\t\t'slotted-submit-without-controller',\n\t\t\t\"uploadOn: 'submit' has no send of its own here — pass `controllerRef` and call upload() from the form's submit, or staged slots never leave.\"\n\t\t)\n\t}\n\tconst copy = useUploaderLabels(options.labels)\n\tconst coarse = useCoarsePointer()\n\tconst viewer = useFileViewer({ resolveUrl: resolveViewUrl })\n\tconst [pending, setPending] = useState<PendingAction | null>(null)\n\n\tconst { upload, hasPending, isUploading } = slotted\n\tuseEffect(() => {\n\t\tif (!controllerRef) return\n\t\tcontrollerRef.current = { upload, hasPending, isUploading }\n\t\treturn () => {\n\t\t\tcontrollerRef.current = null\n\t\t}\n\t}, [controllerRef, upload, hasPending, isUploading])\n\n\tuseEffect(() => {\n\t\tonPendingChange?.(hasPending)\n\t}, [onPendingChange, hasPending])\n\n\tconst pick = (slot: SlotState, file: File) => {\n\t\tif (slot.filled && confirmReplace) {\n\t\t\tsetPending({ kind: 'replace', slot, file })\n\t\t\treturn\n\t\t}\n\t\tvoid slotted.addToSlot(slot.definition.id, file)\n\t}\n\n\t// Same contract as Uploader's forgetStored: the package deletes, the app\n\t// observes, and the reference is forgotten even when the delete fails.\n\tconst forgetSlot = (slot: SlotState) => {\n\t\tconst stored = slot.filled?.stored\n\t\tif (\n\t\t\tstored &&\n\t\t\tremoveStrategy &&\n\t\t\t!options.scopes.get(options.scope).keepOnRemove\n\t\t) {\n\t\t\tremoveStrategy(stored, options.scope, options.entityId)\n\t\t\t\t.then(deleted => {\n\t\t\t\t\tif (!deleted) options.onError?.(copy.removeFailed)\n\t\t\t\t})\n\t\t\t\t.catch(() => options.onError?.(copy.removeFailed))\n\t\t}\n\t\tslotted.removeSlot(slot.definition.id)\n\t}\n\n\tconst remove = (slot: SlotState) => {\n\t\tif (slot.filled && confirmRemove) {\n\t\t\tsetPending({ kind: 'remove', slot })\n\t\t\treturn\n\t\t}\n\t\tforgetSlot(slot)\n\t}\n\n\tconst override =\n\t\tpending?.kind === 'replace'\n\t\t\t? typeof confirmReplace === 'object'\n\t\t\t\t? confirmReplace\n\t\t\t\t: {}\n\t\t\t: typeof confirmRemove === 'object'\n\t\t\t\t? confirmRemove\n\t\t\t\t: {}\n\n\tconst dialogTitle =\n\t\toverride.title ??\n\t\t(pending?.kind === 'replace'\n\t\t\t? copy.confirmReplaceTitle\n\t\t\t: copy.confirmRemoveTitle)\n\tconst dialogMessage =\n\t\toverride.message ??\n\t\t(pending?.kind === 'replace'\n\t\t\t? copy.confirmReplaceMessage(\n\t\t\t\t\tpending.slot.filled?.stored.fileName ?? '',\n\t\t\t\t\tpending.file.name\n\t\t\t\t)\n\t\t\t: copy.confirmRemoveMessage(pending?.slot.filled?.stored.fileName ?? ''))\n\n\treturn (\n\t\t<div className={cn('space-y-2', className)}>\n\t\t\t{title && <p className='text-ui-fg text-sm font-medium'>{title}</p>}\n\t\t\t{description && <p className='text-ui-muted text-xs'>{description}</p>}\n\n\t\t\t{slotted.slots.map(slot => (\n\t\t\t\t<SlotRow\n\t\t\t\t\tkey={slot.definition.id}\n\t\t\t\t\tslot={slot}\n\t\t\t\t\tonPick={file => pick(slot, file)}\n\t\t\t\t\tonRemove={() => remove(slot)}\n\t\t\t\t\tonAbort={() => slotted.abort(slot.definition.id)}\n\t\t\t\t\tonView={() => slot.filled && viewer.open(slot.filled.stored)}\n\t\t\t\t\tsize={size}\n\t\t\t\t\tlabels={options.labels}\n\t\t\t\t/>\n\t\t\t))}\n\n\t\t\t{!hideDropzone && (\n\t\t\t\t<Dropzone\n\t\t\t\t\taccept={slotted.accept}\n\t\t\t\t\tmultiple\n\t\t\t\t\tdisabled={slotted.isUploading}\n\t\t\t\t\tsize={size}\n\t\t\t\t\tshortcut={shortcut}\n\t\t\t\t\tonFiles={files => void slotted.addFiles(files)}\n\t\t\t\t>\n\t\t\t\t\t{icon !== null && (\n\t\t\t\t\t\t<span className='text-ui-placeholder group-hover/dz:text-ui-primary group-data-dragging/dz:text-ui-primary mb-1.5 inline-flex justify-center transition-all duration-200 group-hover/dz:-translate-y-0.5 group-data-dragging/dz:-translate-y-1 group-data-dragging/dz:scale-110'>\n\t\t\t\t\t\t\t{icon ?? (\n\t\t\t\t\t\t\t\t<UploadCloudIcon\n\t\t\t\t\t\t\t\t\tclassName={size === 'sm' ? 'h-5 w-5' : 'h-7 w-7'}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t)}\n\t\t\t\t\t<p className='text-ui-muted group-hover/dz:text-ui-fg text-sm font-medium transition-colors duration-200'>\n\t\t\t\t\t\t{coarse ? copy.bulkTapPrompt : copy.bulkDropPrompt}\n\t\t\t\t\t</p>\n\t\t\t\t</Dropzone>\n\t\t\t)}\n\n\t\t\t<FileViewer {...viewer.viewerProps} labels={options.labels} />\n\n\t\t\t<ConfirmDialog\n\t\t\t\topen={pending !== null}\n\t\t\t\ttitle={dialogTitle}\n\t\t\t\tmessage={dialogMessage}\n\t\t\t\tonConfirm={() => {\n\t\t\t\t\tif (pending?.kind === 'replace') {\n\t\t\t\t\t\tvoid slotted.addToSlot(pending.slot.definition.id, pending.file)\n\t\t\t\t\t} else if (pending) {\n\t\t\t\t\t\tforgetSlot(pending.slot)\n\t\t\t\t\t}\n\t\t\t\t\tsetPending(null)\n\t\t\t\t}}\n\t\t\t\tonCancel={() => setPending(null)}\n\t\t\t\tlabels={options.labels}\n\t\t\t/>\n\t\t</div>\n\t)\n}\n","import { formatFileSize } from '../file'\nimport type { UploaderLabels } from '../labels'\nimport { useUploaderLabels } from '../react/UploaderProvider'\nimport type { StoredFile } from '../types'\nimport { cn } from './cn'\nimport { FileTypeBadge } from './FileTypeBadge'\n\nexport type StoredFileItemProps = {\n\tfile: StoredFile\n\tonView: () => void\n\t/** Omitted when the screen does not let the user detach a persisted file. */\n\tonRemove?: (file: StoredFile) => void\n\t/** @defaultValue 'md' */\n\tsize?: 'sm' | 'md'\n\tlabels?: Partial<UploaderLabels>\n}\n\n/**\n * One row for a file the app has already persisted: extension chip, name,\n * size, and the actions to preview or forget it. The settled counterpart of\n * `FileItem`, which tracks a file that is still going through the machine.\n */\nexport const StoredFileItem = ({\n\tfile,\n\tonView,\n\tonRemove,\n\tsize = 'md',\n\tlabels,\n}: StoredFileItemProps) => {\n\tconst copy = useUploaderLabels(labels)\n\n\treturn (\n\t\t<div\n\t\t\tclassName={cn(\n\t\t\t\t'border-ui-border bg-ui-surface-subtle rounded-ui animate-ui-fade-in flex items-center gap-3 border text-sm',\n\t\t\t\tsize === 'sm' ? 'p-1.5' : 'p-2'\n\t\t\t)}\n\t\t>\n\t\t\t<FileTypeBadge fileName={file.fileName} size={size} />\n\n\t\t\t<div className='min-w-0 flex-1'>\n\t\t\t\t<p className='text-ui-fg truncate font-medium'>{file.fileName}</p>\n\t\t\t\t<p className='text-ui-muted text-xs'>{formatFileSize(file.size)}</p>\n\t\t\t</div>\n\n\t\t\t<button\n\t\t\t\ttype='button'\n\t\t\t\tonClick={onView}\n\t\t\t\tclassName='text-ui-primary hover:bg-ui-primary-soft focus-visible:ring-ui-ring rounded-ui shrink-0 cursor-pointer px-2 py-1 text-xs transition-colors focus-visible:ring-2 focus-visible:outline-none'\n\t\t\t>\n\t\t\t\t{copy.view}\n\t\t\t</button>\n\t\t\t{onRemove && (\n\t\t\t\t<button\n\t\t\t\t\ttype='button'\n\t\t\t\t\tonClick={() => onRemove(file)}\n\t\t\t\t\tclassName='text-ui-muted hover:bg-ui-surface-muted hover:text-ui-fg focus-visible:ring-ui-ring rounded-ui shrink-0 cursor-pointer px-2 py-1 text-xs transition-colors focus-visible:ring-2 focus-visible:outline-none'\n\t\t\t\t>\n\t\t\t\t\t{copy.remove}\n\t\t\t\t</button>\n\t\t\t)}\n\t\t</div>\n\t)\n}\n","import { type ReactNode, useEffect, useState } from 'react'\n\nimport type { UseUploaderOptions } from '../react'\nimport { useUploader } from '../react'\nimport type { RemoveStrategy, UploaderFile } from '../react/types'\nimport { useUploaderLabels } from '../react/UploaderProvider'\nimport type { ScopeConfig } from '../types'\nimport type { StoredFile } from '../types'\nimport { warnDev } from '../warn'\nimport { cn } from './cn'\nimport { ConfirmDialog } from './ConfirmDialog'\nimport type { UiUploadTrigger, UploaderControllerRef } from './controller'\nimport { Dropzone } from './Dropzone'\nimport { FileItem } from './FileItem'\nimport { FileViewer, type ViewableFile } from './FileViewer'\nimport { UploadCloudIcon } from './icons'\nimport { StoredFileItem } from './StoredFileItem'\nimport { useCoarsePointer } from './useCoarsePointer'\nimport { useFileViewer } from './useFileViewer'\n\n/**\n * What {@link UploaderProps.renderFiles} is handed.\n *\n * Both the raw state and the default rows already built, so a custom layout can\n * keep the standard item and only change where — and among what — it sits.\n */\nexport type UploaderFilesSlot = {\n\t/** Files already persisted, exactly as passed in `stored`. */\n\tstored: StoredFile[]\n\t/** Files staged in this session, each with its live upload status. */\n\tstaged: UploaderFile[]\n\t/** The default rows. Place them, wrap them, or drop them entirely. */\n\tnodes: ReactNode\n\t/** Nothing persisted and nothing staged. */\n\tisEmpty: boolean\n\t/** Open a persisted file in the built-in viewer. */\n\tview: (file: StoredFile) => void\n\t/** Forget a staged file. */\n\tremove: (id: string) => void\n\t/**\n\t * Forget a persisted file, honouring `confirmRemove` and `removeStrategy`.\n\t * `undefined` when this uploader accepts no removals.\n\t */\n\tremoveStored?: (file: StoredFile) => void\n}\n\nexport type UploaderProps<T extends Record<string, ScopeConfig>> = Omit<\n\tUseUploaderOptions<T>,\n\t'uploadOn'\n> & {\n\t/**\n\t * When the files travel. @defaultValue 'select'\n\t * @see UiUploadTrigger — which mode fits which screen\n\t */\n\tuploadOn?: UiUploadTrigger\n\tlabel?: string\n\tdescription?: string\n\t/** Files already persisted, rendered on the {@link filesPosition} side. */\n\tstored?: StoredFile[]\n\t/**\n\t * Which side of the dropzone the file lists sit on. @defaultValue 'above'\n\t *\n\t * `'below'` keeps the drop target anchored: with the lists above it, every\n\t * added file pushes the zone further down, so the control the user is\n\t * repeatedly aiming at moves under the cursor.\n\t */\n\tfilesPosition?: 'above' | 'below'\n\t/**\n\t * Lay the file lists out yourself: a grid of thumbnails, a single summary\n\t * line, a count folded into your own card — whatever the screen needs.\n\t *\n\t * It replaces the rows, not their place: the result still renders on the\n\t * {@link filesPosition} side. For a layout the zone itself has to be part of\n\t * (files BESIDE the dropzone, files inside your own frame), skip this skin\n\t * and compose `useUploader` with the exported `Dropzone` / `FileItem` /\n\t * `StoredFileItem` — nothing here is unavailable there.\n\t */\n\trenderFiles?: (slot: UploaderFilesSlot) => ReactNode\n\t/**\n\t * Notified when a persisted file is forgotten — the app's bookkeeping\n\t * (clear the DB reference). Storage deletion belongs to `removeStrategy`.\n\t */\n\tonRemoveStored?: (file: StoredFile) => void\n\t/**\n\t * Deletion transport. With it, a confirmed removal deletes the object from\n\t * storage BY ITSELF — unless the scope is `keepOnRemove`, the history\n\t * contract — so no consumer can orphan by forgetting a callback.\n\t */\n\tremoveStrategy?: RemoveStrategy\n\t/**\n\t * Gate `onRemoveStored` behind a confirmation dialog. `true` uses the\n\t * label defaults; an object overrides the copy for this uploader.\n\t */\n\tconfirmRemove?: boolean | { title?: string; message?: string }\n\t/** Fresh URL right before previewing — for private scopes whose signed url expired. */\n\tresolveViewUrl?: (file: ViewableFile) => Promise<string>\n\t/** Mobile camera shortcut, forwarded to the dropzone's input. */\n\tcapture?: 'user' | 'environment'\n\t/** Compact paddings and glyphs everywhere. @defaultValue 'md' */\n\tsize?: 'sm' | 'md'\n\t/** Glyph inside the dropzone. `null` removes it; defaults to an upload cloud. */\n\ticon?: ReactNode\n\t/** Keyboard shortcut that opens the picker, e.g. `'mod+u'`. Hinted in the zone. */\n\tshortcut?: string\n\t/**\n\t * Filled with the live controller so the surrounding form can send the\n\t * staged files from its own submit. Required by `uploadOn: 'submit'`,\n\t * which has no other way to fire; readable under `'manual'` too, where\n\t * the zone's button stays the sender.\n\t */\n\tcontrollerRef?: UploaderControllerRef\n\t/**\n\t * Fires when the zone starts or stops holding files that `upload()`\n\t * would send. The companion of `controllerRef`: a form that defers the\n\t * send has no other way to know it has unsent work, so its save button\n\t * would stay disabled on a pristine form the user has just dropped a\n\t * file into.\n\t */\n\tonPendingChange?: (hasPending: boolean) => void\n\tdisabled?: boolean\n\tclassName?: string\n}\n\n/**\n * The styled single-zone uploader: one dropzone, one or many files in it.\n * Purely a skin over `useUploader` — apps needing custom markup use the hook\n * directly and lose nothing.\n *\n * Defaults `uploadOn` to `'select'`. `'manual'` gives the zone its own upload\n * button; `'submit'` hands the send to the surrounding form via\n * `controllerRef`. @see UiUploadTrigger for which mode fits which screen.\n */\nexport const Uploader = <T extends Record<string, ScopeConfig>>({\n\tuploadOn = 'select',\n\tlabel,\n\tdescription,\n\tstored = [],\n\tfilesPosition = 'above',\n\trenderFiles,\n\tonRemoveStored,\n\tconfirmRemove,\n\tresolveViewUrl,\n\tcapture,\n\tsize = 'md',\n\ticon,\n\tshortcut,\n\tcontrollerRef,\n\tonPendingChange,\n\tremoveStrategy,\n\tdisabled = false,\n\tclassName,\n\t...options\n}: UploaderProps<T>) => {\n\t// 'submit' and 'manual' are the same hook state; who calls upload() differs.\n\tconst uploader = useUploader({\n\t\t...options,\n\t\tuploadOn: uploadOn === 'select' ? 'select' : 'manual',\n\t})\n\tconst copy = useUploaderLabels(options.labels)\n\tconst viewer = useFileViewer({ resolveUrl: resolveViewUrl })\n\tconst [removing, setRemoving] = useState<StoredFile | null>(null)\n\tconst { files, removeFile } = uploader\n\t// Only 'manual' renders the zone's button: under 'submit' the form owns the\n\t// send, and a second, unvalidated way to fire the same batch is one too many.\n\tconst manual = uploadOn === 'manual'\n\n\tif (uploadOn === 'submit' && !controllerRef) {\n\t\twarnDev(\n\t\t\t'uploader-submit-without-controller',\n\t\t\t\"uploadOn: 'submit' has no send of its own — pass `controllerRef` and call upload() from the form's submit, or use 'manual' for the zone's button.\"\n\t\t)\n\t}\n\t// Touch-first devices barely drag; the zone reads as a tap target there.\n\tconst coarse = useCoarsePointer()\n\n\t// Once the app echoes an upload back through `stored`, the machine's copy\n\t// would render the file twice. Depends on the two members it reads, never\n\t// on `uploader` — that object is fresh on every render.\n\tuseEffect(() => {\n\t\tif (stored.length === 0) return\n\t\tconst keys = new Set(stored.map(file => file.key))\n\t\tfiles\n\t\t\t.filter(f => f.status === 'success' && f.stored && keys.has(f.stored.key))\n\t\t\t.forEach(f => removeFile(f.id))\n\t}, [stored, files, removeFile])\n\n\tconst { upload, hasPending, isUploading } = uploader\n\tuseEffect(() => {\n\t\tif (!controllerRef) return\n\t\tcontrollerRef.current = { upload, hasPending, isUploading }\n\t\treturn () => {\n\t\t\tcontrollerRef.current = null\n\t\t}\n\t}, [controllerRef, upload, hasPending, isUploading])\n\n\tuseEffect(() => {\n\t\tonPendingChange?.(hasPending)\n\t}, [onPendingChange, hasPending])\n\n\t// One forget path for both branches: the package deletes, the app observes.\n\t// The reference is forgotten regardless — a dangling pointer is worse than\n\t// an orphan — and a refused delete surfaces through `onError`.\n\tconst forgetStored = (file: StoredFile) => {\n\t\tif (removeStrategy && !options.scopes.get(options.scope).keepOnRemove) {\n\t\t\tremoveStrategy(file, options.scope, options.entityId)\n\t\t\t\t.then(deleted => {\n\t\t\t\t\tif (!deleted) options.onError?.(copy.removeFailed)\n\t\t\t\t})\n\t\t\t\t.catch(() => options.onError?.(copy.removeFailed))\n\t\t}\n\t\tonRemoveStored?.(file)\n\t}\n\n\tconst requestRemove =\n\t\tonRemoveStored || removeStrategy\n\t\t\t? (file: StoredFile) =>\n\t\t\t\t\tconfirmRemove ? setRemoving(file) : forgetStored(file)\n\t\t\t: undefined\n\n\tconst confirmCopy = typeof confirmRemove === 'object' ? confirmRemove : {}\n\n\tconst defaultRows = (\n\t\t<>\n\t\t\t{stored.map(file => (\n\t\t\t\t<StoredFileItem\n\t\t\t\t\tkey={file.key}\n\t\t\t\t\tfile={file}\n\t\t\t\t\tonView={() => viewer.open(file)}\n\t\t\t\t\tonRemove={requestRemove}\n\t\t\t\t\tsize={size}\n\t\t\t\t\tlabels={options.labels}\n\t\t\t\t/>\n\t\t\t))}\n\n\t\t\t{files.map(file => {\n\t\t\t\tconst { stored: settled } = file\n\t\t\t\treturn (\n\t\t\t\t\t<FileItem\n\t\t\t\t\t\tkey={file.id}\n\t\t\t\t\t\tfile={file}\n\t\t\t\t\t\tonRemove={() => removeFile(file.id)}\n\t\t\t\t\t\tonAbort={() => uploader.abort(file.id)}\n\t\t\t\t\t\tonView={settled ? () => viewer.open(settled) : undefined}\n\t\t\t\t\t\tsize={size}\n\t\t\t\t\t\tlabels={options.labels}\n\t\t\t\t\t/>\n\t\t\t\t)\n\t\t\t})}\n\t\t</>\n\t)\n\n\tconst fileList = renderFiles\n\t\t? renderFiles({\n\t\t\t\tstored,\n\t\t\t\tstaged: files,\n\t\t\t\tnodes: defaultRows,\n\t\t\t\tisEmpty: stored.length === 0 && files.length === 0,\n\t\t\t\tview: viewer.open,\n\t\t\t\tremove: removeFile,\n\t\t\t\tremoveStored: requestRemove,\n\t\t\t})\n\t\t: defaultRows\n\n\treturn (\n\t\t<div className={cn('space-y-2', className)}>\n\t\t\t{label && <p className='text-ui-fg text-sm font-medium'>{label}</p>}\n\n\t\t\t{filesPosition === 'above' && fileList}\n\n\t\t\t<Dropzone\n\t\t\t\taccept={uploader.accept}\n\t\t\t\tmultiple={options.multiple}\n\t\t\t\tdisabled={disabled || uploader.isUploading}\n\t\t\t\tcapture={capture}\n\t\t\t\tsize={size}\n\t\t\t\tshortcut={shortcut}\n\t\t\t\tonFiles={incoming => void uploader.addFiles(incoming)}\n\t\t\t>\n\t\t\t\t{icon !== null && (\n\t\t\t\t\t<span className='text-ui-placeholder group-hover/dz:text-ui-primary group-data-dragging/dz:text-ui-primary mb-1.5 inline-flex justify-center transition-all duration-200 group-hover/dz:-translate-y-0.5 group-data-dragging/dz:-translate-y-1 group-data-dragging/dz:scale-110'>\n\t\t\t\t\t\t{icon ?? (\n\t\t\t\t\t\t\t<UploadCloudIcon\n\t\t\t\t\t\t\t\tclassName={size === 'sm' ? 'h-5 w-5' : 'h-7 w-7'}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t)}\n\t\t\t\t\t</span>\n\t\t\t\t)}\n\t\t\t\t<p className='text-ui-muted group-hover/dz:text-ui-fg text-sm font-medium transition-colors duration-200'>\n\t\t\t\t\t{coarse ? copy.tapPrompt : copy.dropPrompt}\n\t\t\t\t</p>\n\t\t\t\t<p className='text-ui-placeholder mt-1 text-xs'>\n\t\t\t\t\t{description ?? uploader.accept.replaceAll(',', ' · ')}\n\t\t\t\t</p>\n\t\t\t</Dropzone>\n\n\t\t\t{filesPosition === 'below' && fileList}\n\n\t\t\t{manual && uploader.hasPending && (\n\t\t\t\t<button\n\t\t\t\t\ttype='button'\n\t\t\t\t\tonClick={() => void uploader.upload()}\n\t\t\t\t\tdisabled={disabled || uploader.isUploading}\n\t\t\t\t\tclassName='bg-ui-primary text-ui-primary-fg hover:bg-ui-primary-hover focus-visible:ring-ui-ring rounded-ui h-10 w-full cursor-pointer px-4 text-sm font-medium transition-colors focus-visible:ring-2 focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50'\n\t\t\t\t>\n\t\t\t\t\t{copy.uploadAll}\n\t\t\t\t</button>\n\t\t\t)}\n\n\t\t\t<FileViewer {...viewer.viewerProps} labels={options.labels} />\n\n\t\t\t<ConfirmDialog\n\t\t\t\topen={removing !== null}\n\t\t\t\ttitle={confirmCopy.title ?? copy.confirmRemoveTitle}\n\t\t\t\tmessage={\n\t\t\t\t\tconfirmCopy.message ??\n\t\t\t\t\tcopy.confirmRemoveMessage(removing?.fileName ?? '')\n\t\t\t\t}\n\t\t\t\tonConfirm={() => {\n\t\t\t\t\tif (removing) forgetStored(removing)\n\t\t\t\t\tsetRemoving(null)\n\t\t\t\t}}\n\t\t\t\tonCancel={() => setRemoving(null)}\n\t\t\t\tlabels={options.labels}\n\t\t\t/>\n\t\t</div>\n\t)\n}\n"]}
@@ -0,0 +1,139 @@
1
+ import * as react from 'react';
2
+ import { ReactNode } from 'react';
3
+ import { b as FileExtension, o as StoredFile, U as UploaderLabels, d as ScopeConfig, e as ScopeRegistry } from './types-BSlJJwti.cjs';
4
+ import { e as UploaderFile, a as UploadStrategy, R as RemoveStrategy, d as UploadTrigger, b as RetryOptions } from './useUploader-BiBdS-7y.cjs';
5
+
6
+ /** A named position a single file can fill (letterhead, CSF, acta, …). */
7
+ type SlotDefinition = {
8
+ /** Canonical id — becomes the stored file name (`{id}.{ext}`), so the
9
+ * scope's `path` yields a stable key and re-uploads overwrite. */
10
+ id: string;
11
+ /** Human label shown in the row. */
12
+ label: string;
13
+ /** Lower-case extensions this slot takes. Narrower than the scope, never wider. */
14
+ extensions: FileExtension[];
15
+ hint?: string;
16
+ };
17
+ /** A stored file tagged with the slot it fills. The app persists these. */
18
+ type SlottedFile = {
19
+ slot: string;
20
+ stored: StoredFile;
21
+ };
22
+ /** Resolves which slot an incoming file belongs to, or `null` for none. */
23
+ type SlotMatcher = (slots: SlotDefinition[], fileName: string) => string | null;
24
+ /**
25
+ * Default matcher: the file's extension picks the slot. Prefers an empty slot
26
+ * so dropping three files fills three positions; falls back to the first
27
+ * extension match, which reads as "replace what is there".
28
+ */
29
+ declare const matchSlotByExtension: (filled: ReadonlySet<string>) => SlotMatcher;
30
+ /** Matcher for uploads named after their slot (`letterhead-a4.pdf` → slot `letterhead-a4`). */
31
+ declare const matchSlotByName: SlotMatcher;
32
+ /** The name a file adopts when it fills a slot. */
33
+ declare const slotFileName: (slotId: string, originalName: string) => string;
34
+ /** The slot a stored file fills, recovered from its name. `null` when foreign. */
35
+ declare const slotOfStored: (slots: SlotDefinition[], stored: StoredFile) => string | null;
36
+
37
+ /** The two built-in copy sets. */
38
+ type UploaderLanguage = 'en' | 'es';
39
+ type UploaderProviderProps = {
40
+ children: ReactNode;
41
+ /**
42
+ * Which built-in copy set every uploader under it speaks. English is the
43
+ * default so the kit ships globalized; a Spanish app opts in ONCE here
44
+ * instead of passing `labels` to each component.
45
+ * @defaultValue 'en'
46
+ */
47
+ language?: UploaderLanguage;
48
+ /** Per-key overrides on top of the selected language. */
49
+ labels?: Partial<UploaderLabels>;
50
+ };
51
+ /**
52
+ * Optional app-root provider carrying the copy. Components work without it
53
+ * (they fall back to {@link DEFAULT_LABELS}), and a per-component `labels`
54
+ * prop still wins for one-off rewording.
55
+ */
56
+ declare const UploaderProvider: ({ children, language, labels, }: UploaderProviderProps) => react.JSX.Element;
57
+ /**
58
+ * The active copy, merged: provider base ← per-call `overrides`. What every
59
+ * component and hook in the package reads instead of `resolveLabels` directly,
60
+ * so a single provider re-words all of them at once.
61
+ */
62
+ declare const useUploaderLabels: (overrides?: Partial<UploaderLabels>) => UploaderLabels;
63
+
64
+ type UseSlottedUploaderOptions<T extends Record<string, ScopeConfig>> = {
65
+ scopes: ScopeRegistry<T>;
66
+ scope: keyof T & string;
67
+ entityId: string;
68
+ strategy?: UploadStrategy;
69
+ /**
70
+ * Deletion transport. With it, a confirmed `removeSlot` deletes the object
71
+ * from storage BY ITSELF — unless the scope is `keepOnRemove`, the history
72
+ * contract — so no consumer can orphan by forgetting a callback.
73
+ * `onRemoveStored` still fires for the app's own bookkeeping.
74
+ */
75
+ removeStrategy?: RemoveStrategy;
76
+ slots: SlotDefinition[];
77
+ /** Currently persisted files. The caller owns persistence — controlled. */
78
+ value: SlottedFile[];
79
+ /** Called with the next full set after an upload lands or a slot empties. */
80
+ onChange: (next: SlottedFile[]) => void | Promise<void>;
81
+ /** How dropped files find their slot. @defaultValue 'extension' */
82
+ matchBy?: 'extension' | 'name';
83
+ /** Custom matcher for special rules; overrides `matchBy`. */
84
+ match?: SlotMatcher;
85
+ /** @defaultValue 'select' — a slot names its file, so it usually travels at once. */
86
+ uploadOn?: UploadTrigger;
87
+ /** Fired when a batch starts travelling. Same contract as `useUploader`. */
88
+ onUploadStart?: (files: UploaderFile[]) => void;
89
+ /**
90
+ * Fired with the PERSISTED file a `removeSlot` just forgot, before
91
+ * `onChange` delivers the filtered set. This is where the app deletes the
92
+ * object from storage — without it the consumer had to diff old-vs-new
93
+ * arrays to even learn which key vanished, and every stable-key scope
94
+ * orphaned on remove.
95
+ */
96
+ onRemoveStored?: (stored: SlottedFile) => void;
97
+ onError?: (message: string) => void;
98
+ /** Forwarded to the underlying `useUploader`. */
99
+ retry?: number | RetryOptions;
100
+ /** Forwarded to the underlying `useUploader`. */
101
+ concurrency?: number;
102
+ labels?: Partial<UploaderLabels>;
103
+ };
104
+ type SlotState = {
105
+ definition: SlotDefinition;
106
+ /** Persisted file occupying the slot, if any. */
107
+ filled?: SlottedFile;
108
+ /** In-flight or failed upload for the slot, if any. */
109
+ pending?: UploaderFile;
110
+ };
111
+ type UseSlottedUploaderReturn = {
112
+ slots: SlotState[];
113
+ /** `<input accept>` derived from the union of the slots' extensions. */
114
+ accept: string;
115
+ isUploading: boolean;
116
+ /** Route a dropped/browsed batch to slots via the matcher. */
117
+ addFiles: (incoming: FileList | File[]) => Promise<void>;
118
+ /** Put one file in one specific slot (per-row browse button). */
119
+ addToSlot: (slotId: string, file: File) => Promise<void>;
120
+ /**
121
+ * Forget the persisted file of a slot. Remote deletion stays the app's
122
+ * call — made through `onRemoveStored`, which receives the forgotten file.
123
+ */
124
+ removeSlot: (slotId: string) => void;
125
+ abort: (slotId?: string) => void;
126
+ /** Uploads every slot still holding an `idle` file (`uploadOn: 'manual'`). */
127
+ upload: () => Promise<void>;
128
+ /** `true` while any slot holds an `idle` file waiting for `upload()`. */
129
+ hasPending: boolean;
130
+ };
131
+ /**
132
+ * Named-slot collection over the SAME machine as the plain uploader: this hook
133
+ * only decides *which slot* a file lands in and renames it to `{slot}.{ext}`;
134
+ * validation, compression, progress and abort are `useUploader` verbatim. One
135
+ * behavior, two presentations — a fix in the machine reaches both.
136
+ */
137
+ declare const useSlottedUploader: <T extends Record<string, ScopeConfig>>({ scopes, scope, entityId, strategy, slots, value, onChange, matchBy, match, uploadOn, onUploadStart, removeStrategy, onRemoveStored, onError, retry, concurrency, labels, }: UseSlottedUploaderOptions<T>) => UseSlottedUploaderReturn;
138
+
139
+ export { type SlotDefinition as S, type UploaderLanguage as U, type SlotMatcher as a, type SlotState as b, type SlottedFile as c, UploaderProvider as d, type UploaderProviderProps as e, type UseSlottedUploaderOptions as f, type UseSlottedUploaderReturn as g, matchSlotByName as h, slotOfStored as i, useUploaderLabels as j, matchSlotByExtension as m, slotFileName as s, useSlottedUploader as u };
@@ -0,0 +1,139 @@
1
+ import * as react from 'react';
2
+ import { ReactNode } from 'react';
3
+ import { b as FileExtension, o as StoredFile, U as UploaderLabels, d as ScopeConfig, e as ScopeRegistry } from './types-BSlJJwti.js';
4
+ import { e as UploaderFile, a as UploadStrategy, R as RemoveStrategy, d as UploadTrigger, b as RetryOptions } from './useUploader-CQHpj_oI.js';
5
+
6
+ /** A named position a single file can fill (letterhead, CSF, acta, …). */
7
+ type SlotDefinition = {
8
+ /** Canonical id — becomes the stored file name (`{id}.{ext}`), so the
9
+ * scope's `path` yields a stable key and re-uploads overwrite. */
10
+ id: string;
11
+ /** Human label shown in the row. */
12
+ label: string;
13
+ /** Lower-case extensions this slot takes. Narrower than the scope, never wider. */
14
+ extensions: FileExtension[];
15
+ hint?: string;
16
+ };
17
+ /** A stored file tagged with the slot it fills. The app persists these. */
18
+ type SlottedFile = {
19
+ slot: string;
20
+ stored: StoredFile;
21
+ };
22
+ /** Resolves which slot an incoming file belongs to, or `null` for none. */
23
+ type SlotMatcher = (slots: SlotDefinition[], fileName: string) => string | null;
24
+ /**
25
+ * Default matcher: the file's extension picks the slot. Prefers an empty slot
26
+ * so dropping three files fills three positions; falls back to the first
27
+ * extension match, which reads as "replace what is there".
28
+ */
29
+ declare const matchSlotByExtension: (filled: ReadonlySet<string>) => SlotMatcher;
30
+ /** Matcher for uploads named after their slot (`letterhead-a4.pdf` → slot `letterhead-a4`). */
31
+ declare const matchSlotByName: SlotMatcher;
32
+ /** The name a file adopts when it fills a slot. */
33
+ declare const slotFileName: (slotId: string, originalName: string) => string;
34
+ /** The slot a stored file fills, recovered from its name. `null` when foreign. */
35
+ declare const slotOfStored: (slots: SlotDefinition[], stored: StoredFile) => string | null;
36
+
37
+ /** The two built-in copy sets. */
38
+ type UploaderLanguage = 'en' | 'es';
39
+ type UploaderProviderProps = {
40
+ children: ReactNode;
41
+ /**
42
+ * Which built-in copy set every uploader under it speaks. English is the
43
+ * default so the kit ships globalized; a Spanish app opts in ONCE here
44
+ * instead of passing `labels` to each component.
45
+ * @defaultValue 'en'
46
+ */
47
+ language?: UploaderLanguage;
48
+ /** Per-key overrides on top of the selected language. */
49
+ labels?: Partial<UploaderLabels>;
50
+ };
51
+ /**
52
+ * Optional app-root provider carrying the copy. Components work without it
53
+ * (they fall back to {@link DEFAULT_LABELS}), and a per-component `labels`
54
+ * prop still wins for one-off rewording.
55
+ */
56
+ declare const UploaderProvider: ({ children, language, labels, }: UploaderProviderProps) => react.JSX.Element;
57
+ /**
58
+ * The active copy, merged: provider base ← per-call `overrides`. What every
59
+ * component and hook in the package reads instead of `resolveLabels` directly,
60
+ * so a single provider re-words all of them at once.
61
+ */
62
+ declare const useUploaderLabels: (overrides?: Partial<UploaderLabels>) => UploaderLabels;
63
+
64
+ type UseSlottedUploaderOptions<T extends Record<string, ScopeConfig>> = {
65
+ scopes: ScopeRegistry<T>;
66
+ scope: keyof T & string;
67
+ entityId: string;
68
+ strategy?: UploadStrategy;
69
+ /**
70
+ * Deletion transport. With it, a confirmed `removeSlot` deletes the object
71
+ * from storage BY ITSELF — unless the scope is `keepOnRemove`, the history
72
+ * contract — so no consumer can orphan by forgetting a callback.
73
+ * `onRemoveStored` still fires for the app's own bookkeeping.
74
+ */
75
+ removeStrategy?: RemoveStrategy;
76
+ slots: SlotDefinition[];
77
+ /** Currently persisted files. The caller owns persistence — controlled. */
78
+ value: SlottedFile[];
79
+ /** Called with the next full set after an upload lands or a slot empties. */
80
+ onChange: (next: SlottedFile[]) => void | Promise<void>;
81
+ /** How dropped files find their slot. @defaultValue 'extension' */
82
+ matchBy?: 'extension' | 'name';
83
+ /** Custom matcher for special rules; overrides `matchBy`. */
84
+ match?: SlotMatcher;
85
+ /** @defaultValue 'select' — a slot names its file, so it usually travels at once. */
86
+ uploadOn?: UploadTrigger;
87
+ /** Fired when a batch starts travelling. Same contract as `useUploader`. */
88
+ onUploadStart?: (files: UploaderFile[]) => void;
89
+ /**
90
+ * Fired with the PERSISTED file a `removeSlot` just forgot, before
91
+ * `onChange` delivers the filtered set. This is where the app deletes the
92
+ * object from storage — without it the consumer had to diff old-vs-new
93
+ * arrays to even learn which key vanished, and every stable-key scope
94
+ * orphaned on remove.
95
+ */
96
+ onRemoveStored?: (stored: SlottedFile) => void;
97
+ onError?: (message: string) => void;
98
+ /** Forwarded to the underlying `useUploader`. */
99
+ retry?: number | RetryOptions;
100
+ /** Forwarded to the underlying `useUploader`. */
101
+ concurrency?: number;
102
+ labels?: Partial<UploaderLabels>;
103
+ };
104
+ type SlotState = {
105
+ definition: SlotDefinition;
106
+ /** Persisted file occupying the slot, if any. */
107
+ filled?: SlottedFile;
108
+ /** In-flight or failed upload for the slot, if any. */
109
+ pending?: UploaderFile;
110
+ };
111
+ type UseSlottedUploaderReturn = {
112
+ slots: SlotState[];
113
+ /** `<input accept>` derived from the union of the slots' extensions. */
114
+ accept: string;
115
+ isUploading: boolean;
116
+ /** Route a dropped/browsed batch to slots via the matcher. */
117
+ addFiles: (incoming: FileList | File[]) => Promise<void>;
118
+ /** Put one file in one specific slot (per-row browse button). */
119
+ addToSlot: (slotId: string, file: File) => Promise<void>;
120
+ /**
121
+ * Forget the persisted file of a slot. Remote deletion stays the app's
122
+ * call — made through `onRemoveStored`, which receives the forgotten file.
123
+ */
124
+ removeSlot: (slotId: string) => void;
125
+ abort: (slotId?: string) => void;
126
+ /** Uploads every slot still holding an `idle` file (`uploadOn: 'manual'`). */
127
+ upload: () => Promise<void>;
128
+ /** `true` while any slot holds an `idle` file waiting for `upload()`. */
129
+ hasPending: boolean;
130
+ };
131
+ /**
132
+ * Named-slot collection over the SAME machine as the plain uploader: this hook
133
+ * only decides *which slot* a file lands in and renames it to `{slot}.{ext}`;
134
+ * validation, compression, progress and abort are `useUploader` verbatim. One
135
+ * behavior, two presentations — a fix in the machine reaches both.
136
+ */
137
+ declare const useSlottedUploader: <T extends Record<string, ScopeConfig>>({ scopes, scope, entityId, strategy, slots, value, onChange, matchBy, match, uploadOn, onUploadStart, removeStrategy, onRemoveStored, onError, retry, concurrency, labels, }: UseSlottedUploaderOptions<T>) => UseSlottedUploaderReturn;
138
+
139
+ export { type SlotDefinition as S, type UploaderLanguage as U, type SlotMatcher as a, type SlotState as b, type SlottedFile as c, UploaderProvider as d, type UploaderProviderProps as e, type UseSlottedUploaderOptions as f, type UseSlottedUploaderReturn as g, matchSlotByName as h, slotOfStored as i, useUploaderLabels as j, matchSlotByExtension as m, slotFileName as s, useSlottedUploader as u };
@@ -0,0 +1,117 @@
1
+ import { o as StoredFile, p as UploadStatus, d as ScopeConfig, e as ScopeRegistry, U as UploaderLabels } from './types-BSlJJwti.cjs';
2
+
3
+ /** Signals the strategy receives while a file is in flight. */
4
+ type UploadStrategyOptions = {
5
+ /** 0–100. Driven by the transport when it can measure (XHR can). */
6
+ onProgress: (percent: number) => void;
7
+ /** Aborting must reject the promise with an `AbortError`-named error. */
8
+ signal: AbortSignal;
9
+ };
10
+ /**
11
+ * Deletes one persisted object from storage. The uploaders run it themselves
12
+ * on a confirmed removal — unless the scope is marked `keepOnRemove`, the
13
+ * history contract — so a consumer that wires a strategy can no longer orphan
14
+ * by forgetting a callback. Resolve `false` when the server refused.
15
+ */
16
+ type RemoveStrategy = (stored: StoredFile, scope: string, entityId: string) => Promise<boolean>;
17
+ /**
18
+ * The physical transport for one file. Injected into `useUploader` so the hook
19
+ * owns state and validation while the app owns how bytes travel — swap the
20
+ * endpoint, the auth header or the whole protocol without touching the hook.
21
+ */
22
+ type UploadStrategy = (file: File, scope: string, entityId: string, options: UploadStrategyOptions) => Promise<StoredFile>;
23
+ /** One file tracked by the hook, from selection to stored (or failed). */
24
+ type UploaderFile = {
25
+ id: string;
26
+ file: File;
27
+ status: UploadStatus;
28
+ /** 0–100 while uploading; 100 on success. */
29
+ progress: number;
30
+ /** Human, Spanish — comes from validation or the strategy's failure. */
31
+ error?: string;
32
+ /** Object URL for image files, for a local thumbnail before upload. */
33
+ preview?: string;
34
+ /** Present once the server confirmed the upload. */
35
+ stored?: StoredFile;
36
+ };
37
+
38
+ /**
39
+ * When the machine sends: `'manual'` holds files in `idle` until the app calls
40
+ * `upload()` — the form-submit flow, where the file travels together with the
41
+ * rest of the fields' action. `'select'` fires as soon as a valid file lands.
42
+ */
43
+ type UploadTrigger = 'select' | 'manual';
44
+ type RetryOptions = {
45
+ /** Total attempts per file, the first one included. @defaultValue 1 */
46
+ attempts?: number;
47
+ /** Delay before the first retry; doubles on each further one. @defaultValue 500 */
48
+ backoffMs?: number;
49
+ };
50
+ type UseUploaderOptions<T extends Record<string, ScopeConfig>> = {
51
+ /** The app's registry — the same object the server authorizes against. */
52
+ scopes: ScopeRegistry<T>;
53
+ scope: keyof T & string;
54
+ /** Owner of the uploads (customerId, userId, …), forwarded to the strategy. */
55
+ entityId: string;
56
+ /** Transport. Omit for local-only selection + validation (no `upload`). */
57
+ strategy?: UploadStrategy;
58
+ /**
59
+ * Overrides the arity derived from the scope's `maxFiles`. Rarely needed:
60
+ * the registry is what the server validates against.
61
+ */
62
+ multiple?: boolean;
63
+ /** Overrides the scope's own `maxFiles`, downward. */
64
+ maxFiles?: number;
65
+ /** @defaultValue 'manual' — the styled `Uploader` flips it to `'select'`. */
66
+ uploadOn?: UploadTrigger;
67
+ /**
68
+ * Fired when a batch actually starts travelling — from either trigger — with
69
+ * the files it carries. The place to flip a form into its "sending" state.
70
+ */
71
+ onUploadStart?: (files: UploaderFile[]) => void;
72
+ /** Fired once per batch with the files the server confirmed. */
73
+ onUploaded?: (stored: StoredFile[]) => void;
74
+ onError?: (message: string) => void;
75
+ /**
76
+ * Re-run a failed strategy call before surfacing the error. A number is
77
+ * shorthand for `{ attempts }`. Aborts never retry, and validation failures
78
+ * never reach the strategy in the first place.
79
+ */
80
+ retry?: number | RetryOptions;
81
+ /** Max uploads in flight per batch; the rest queue. @defaultValue Infinity */
82
+ concurrency?: number;
83
+ /**
84
+ * Rename each file before it enters the machine — a client-side input, a
85
+ * folio, a slug. Runs before validation, so a rename that changes the
86
+ * extension is rejected like any other invalid file. The stored key comes
87
+ * from the scope's `path(entityId, file)`, which reads this name.
88
+ */
89
+ rename?: (file: File) => string;
90
+ labels?: Partial<UploaderLabels>;
91
+ };
92
+ type UseUploaderReturn = {
93
+ files: UploaderFile[];
94
+ /** Ready-made value for `<input accept>`, derived from the scope. */
95
+ accept: string;
96
+ isUploading: boolean;
97
+ /** `true` while any file waits in `idle` — a manual trigger has work to do. */
98
+ hasPending: boolean;
99
+ addFiles: (incoming: FileList | File[]) => Promise<void>;
100
+ /** Uploads every file still in `idle`. Resolves with the confirmed ones. */
101
+ upload: () => Promise<StoredFile[]>;
102
+ /** Abort one in-flight upload, or all of them when no id is given. */
103
+ abort: (id?: string) => void;
104
+ removeFile: (id: string) => void;
105
+ clear: () => void;
106
+ };
107
+ /**
108
+ * Headless upload state machine: selection → validation → (compression) →
109
+ * upload with progress and abort. Owns no markup — render `files` however the
110
+ * screen needs and wire `addFiles` to an input or a drop zone.
111
+ *
112
+ * Validation runs here with the same `validateForScope` the server runs, so
113
+ * the user sees the rejection before any byte leaves the machine.
114
+ */
115
+ declare const useUploader: <T extends Record<string, ScopeConfig>>({ scopes, scope, entityId, strategy, multiple: multipleOption, maxFiles: maxFilesOption, uploadOn, onUploadStart, onUploaded, onError, retry, concurrency, rename, labels, }: UseUploaderOptions<T>) => UseUploaderReturn;
116
+
117
+ export { type RemoveStrategy as R, type UseUploaderOptions as U, type UploadStrategy as a, type RetryOptions as b, type UploadStrategyOptions as c, type UploadTrigger as d, type UploaderFile as e, type UseUploaderReturn as f, useUploader as u };