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.
- package/LICENSE +21 -0
- package/NOTICE +16 -0
- package/README.es.md +1140 -0
- package/README.md +1135 -0
- package/dist/FileViewer-ChTf7-uY.d.cts +49 -0
- package/dist/FileViewer-nuD-GEdJ.d.ts +49 -0
- package/dist/adapters/gcs.cjs +83 -0
- package/dist/adapters/gcs.cjs.map +1 -0
- package/dist/adapters/gcs.d.cts +31 -0
- package/dist/adapters/gcs.d.ts +31 -0
- package/dist/adapters/gcs.js +81 -0
- package/dist/adapters/gcs.js.map +1 -0
- package/dist/adapters/memory.cjs +27 -0
- package/dist/adapters/memory.cjs.map +1 -0
- package/dist/adapters/memory.d.cts +18 -0
- package/dist/adapters/memory.d.ts +18 -0
- package/dist/adapters/memory.js +25 -0
- package/dist/adapters/memory.js.map +1 -0
- package/dist/adapters/s3.cjs +75 -0
- package/dist/adapters/s3.cjs.map +1 -0
- package/dist/adapters/s3.d.cts +27 -0
- package/dist/adapters/s3.d.ts +27 -0
- package/dist/adapters/s3.js +73 -0
- package/dist/adapters/s3.js.map +1 -0
- package/dist/chunk-3FI44IOW.js +150 -0
- package/dist/chunk-3FI44IOW.js.map +1 -0
- package/dist/chunk-H7BRW5IY.js +308 -0
- package/dist/chunk-H7BRW5IY.js.map +1 -0
- package/dist/chunk-PDKAF4GX.js +707 -0
- package/dist/chunk-PDKAF4GX.js.map +1 -0
- package/dist/chunk-PTEX7F4R.js +309 -0
- package/dist/chunk-PTEX7F4R.js.map +1 -0
- package/dist/chunk-T5YZG6JW.js +511 -0
- package/dist/chunk-T5YZG6JW.js.map +1 -0
- package/dist/index.cjs +220 -27
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +57 -170
- package/dist/index.d.ts +57 -170
- package/dist/index.js +1 -324
- package/dist/index.js.map +1 -1
- package/dist/presets.cjs +1665 -0
- package/dist/presets.cjs.map +1 -0
- package/dist/presets.d.cts +100 -0
- package/dist/presets.d.ts +100 -0
- package/dist/presets.js +405 -0
- package/dist/presets.js.map +1 -0
- package/dist/react.cjs +981 -0
- package/dist/react.cjs.map +1 -0
- package/dist/react.d.cts +106 -0
- package/dist/react.d.ts +106 -0
- package/dist/react.js +5 -0
- package/dist/react.js.map +1 -0
- package/dist/server/express.cjs +179 -0
- package/dist/server/express.cjs.map +1 -0
- package/dist/server/express.d.cts +64 -0
- package/dist/server/express.d.ts +64 -0
- package/dist/server/express.js +111 -0
- package/dist/server/express.js.map +1 -0
- package/dist/server/next.cjs +165 -0
- package/dist/server/next.cjs.map +1 -0
- package/dist/server/next.d.cts +38 -0
- package/dist/server/next.d.ts +38 -0
- package/dist/server/next.js +107 -0
- package/dist/server/next.js.map +1 -0
- package/dist/server.cjs +550 -0
- package/dist/server.cjs.map +1 -0
- package/dist/server.d.cts +19 -0
- package/dist/server.d.ts +19 -0
- package/dist/server.js +80 -0
- package/dist/server.js.map +1 -0
- package/dist/storage-CYkSHWZX.d.cts +133 -0
- package/dist/storage-Qc9epG0G.d.ts +133 -0
- package/dist/types-BSlJJwti.d.cts +341 -0
- package/dist/types-BSlJJwti.d.ts +341 -0
- package/dist/ui.cjs +2325 -0
- package/dist/ui.cjs.map +1 -0
- package/dist/ui.d.cts +331 -0
- package/dist/ui.d.ts +331 -0
- package/dist/ui.js +749 -0
- package/dist/ui.js.map +1 -0
- package/dist/useSlottedUploader-BzT5jV8c.d.cts +139 -0
- package/dist/useSlottedUploader-IkG5RhEO.d.ts +139 -0
- package/dist/useUploader-BiBdS-7y.d.cts +117 -0
- package/dist/useUploader-CQHpj_oI.d.ts +117 -0
- package/package.json +189 -3
- package/tailwind.css +80 -0
package/dist/ui.d.cts
ADDED
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
import { f as UseSlottedUploaderOptions } from './useSlottedUploader-BzT5jV8c.cjs';
|
|
2
|
+
export { U as UploaderLanguage, d as UploaderProvider, e as UploaderProviderProps, j as useUploaderLabels } from './useSlottedUploader-BzT5jV8c.cjs';
|
|
3
|
+
import * as react from 'react';
|
|
4
|
+
import { ReactNode } from 'react';
|
|
5
|
+
import { U as UploaderLabels, o as StoredFile, d as ScopeConfig } from './types-BSlJJwti.cjs';
|
|
6
|
+
import { e as UploaderFile, R as RemoveStrategy, U as UseUploaderOptions } from './useUploader-BiBdS-7y.cjs';
|
|
7
|
+
import { V as ViewableFile, F as FileViewerProps } from './FileViewer-ChTf7-uY.cjs';
|
|
8
|
+
export { a as FileViewer } from './FileViewer-ChTf7-uY.cjs';
|
|
9
|
+
|
|
10
|
+
/** Minimal class joiner — the package has no runtime deps to spend on clsx. */
|
|
11
|
+
declare const cn: (...values: (string | false | null | undefined)[]) => string;
|
|
12
|
+
|
|
13
|
+
type ConfirmDialogProps = {
|
|
14
|
+
open: boolean;
|
|
15
|
+
title: string;
|
|
16
|
+
message: ReactNode;
|
|
17
|
+
/** Tone of the affirmative button. @defaultValue 'danger' */
|
|
18
|
+
variant?: 'danger' | 'primary';
|
|
19
|
+
confirmLabel?: string;
|
|
20
|
+
cancelLabel?: string;
|
|
21
|
+
onConfirm: () => void;
|
|
22
|
+
onCancel: () => void;
|
|
23
|
+
labels?: Partial<UploaderLabels>;
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Second-step gate for destructive file actions — remove a stored file,
|
|
27
|
+
* replace a slot. Focus lands on **cancel** so a stray Enter never destroys
|
|
28
|
+
* anything, Escape and the backdrop cancel, and `Tab` stays inside. Exported
|
|
29
|
+
* for app-level use around the uploaders' callbacks.
|
|
30
|
+
*/
|
|
31
|
+
declare const ConfirmDialog: ({ open, title, message, variant, confirmLabel, cancelLabel, onConfirm, onCancel, labels, }: ConfirmDialogProps) => react.ReactPortal | null;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* When a styled uploader sends, as the three flows every product screen falls
|
|
35
|
+
* into — named at the UI level so a call site declares its intent instead of
|
|
36
|
+
* assembling it from `uploadOn` + button + controller by hand:
|
|
37
|
+
*
|
|
38
|
+
* - `'select'` — fires the moment a valid file lands. Avatars, quick
|
|
39
|
+
* replacements: surfaces where the file IS the action and there is nothing
|
|
40
|
+
* else to wait for.
|
|
41
|
+
* - `'submit'` — staged until the surrounding form sends it through
|
|
42
|
+
* `controllerRef`, together with the rest of the fields. The only correct
|
|
43
|
+
* mode when the record the file belongs to does not exist yet, or when a
|
|
44
|
+
* stable-key scope would overwrite what the entity serves before the user
|
|
45
|
+
* confirmed anything.
|
|
46
|
+
* - `'manual'` — staged, and the zone shows its own upload button: the user
|
|
47
|
+
* drops files and decides the moment. Evidence and other punctual flows
|
|
48
|
+
* with no form around them.
|
|
49
|
+
*
|
|
50
|
+
* The hook underneath only knows `'select' | 'manual'` (fire now vs wait for
|
|
51
|
+
* `upload()`): `'submit'` and `'manual'` are the same machine state with a
|
|
52
|
+
* different owner of the send — the form or the zone.
|
|
53
|
+
*/
|
|
54
|
+
type UiUploadTrigger = 'select' | 'submit' | 'manual';
|
|
55
|
+
/**
|
|
56
|
+
* What a surrounding form needs to send the staged files from its own submit —
|
|
57
|
+
* the flow `uploadOn: 'manual'` exists for, where the file travels together
|
|
58
|
+
* with the rest of the fields instead of the moment it is picked.
|
|
59
|
+
*
|
|
60
|
+
* A ref rather than a callback prop: the form reads it inside its submit
|
|
61
|
+
* handler, and a controller passed down would re-render the zone on every
|
|
62
|
+
* keystroke elsewhere in the form.
|
|
63
|
+
*
|
|
64
|
+
* @see AGENTS.md §3 — "Arity lives on the scope", same reasoning one level up
|
|
65
|
+
*/
|
|
66
|
+
type UploaderController<TResult = StoredFile[]> = {
|
|
67
|
+
/** Uploads everything still waiting. Resolves with what the server confirmed. */
|
|
68
|
+
upload: () => Promise<TResult>;
|
|
69
|
+
/** `true` while a file waits for `upload()`. */
|
|
70
|
+
hasPending: boolean;
|
|
71
|
+
isUploading: boolean;
|
|
72
|
+
};
|
|
73
|
+
/** Slots settle through `onChange`, so the batch itself resolves with nothing. */
|
|
74
|
+
type SlottedUploaderController = UploaderController<void>;
|
|
75
|
+
/**
|
|
76
|
+
* Structural rather than React's `RefObject`: that type is read-only under the
|
|
77
|
+
* v18 typings and writable under v19, and the package supports both.
|
|
78
|
+
* `useRef<UploaderController | null>(null)` satisfies it either way.
|
|
79
|
+
*/
|
|
80
|
+
type UploaderControllerRef<TResult = StoredFile[]> = {
|
|
81
|
+
current: UploaderController<TResult> | null;
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
type DropzoneProps = {
|
|
85
|
+
accept: string;
|
|
86
|
+
multiple?: boolean;
|
|
87
|
+
disabled?: boolean;
|
|
88
|
+
/**
|
|
89
|
+
* Mobile camera shortcut: `'environment'` (rear) or `'user'` (front) makes
|
|
90
|
+
* the file picker offer the camera directly on touch devices. Desktop
|
|
91
|
+
* browsers ignore it.
|
|
92
|
+
*/
|
|
93
|
+
capture?: 'user' | 'environment';
|
|
94
|
+
/** @defaultValue 'md' */
|
|
95
|
+
size?: 'sm' | 'md';
|
|
96
|
+
/**
|
|
97
|
+
* Global keyboard shortcut that opens this zone's picker, e.g. `'mod+u'`
|
|
98
|
+
* (⌘U / Ctrl+U). A small key hint renders under the prompt on fine
|
|
99
|
+
* pointers. With several zones on screen, give each its own combo.
|
|
100
|
+
*/
|
|
101
|
+
shortcut?: string;
|
|
102
|
+
onFiles: (files: File[]) => void;
|
|
103
|
+
children: ReactNode;
|
|
104
|
+
className?: string;
|
|
105
|
+
};
|
|
106
|
+
/**
|
|
107
|
+
* The one drag-and-drop surface both uploaders share: drag state, click and
|
|
108
|
+
* keyboard to browse, paste while focused (screenshots land as files), and a
|
|
109
|
+
* hidden input. Presentation comes from `children`.
|
|
110
|
+
*/
|
|
111
|
+
declare const Dropzone: ({ accept, multiple, disabled, capture, size, shortcut, onFiles, children, className, }: DropzoneProps) => react.JSX.Element;
|
|
112
|
+
|
|
113
|
+
type FileItemProps = {
|
|
114
|
+
file: UploaderFile;
|
|
115
|
+
onRemove: () => void;
|
|
116
|
+
onAbort: () => void;
|
|
117
|
+
/** Present on successes when a viewer is available. */
|
|
118
|
+
onView?: () => void;
|
|
119
|
+
/** @defaultValue 'md' */
|
|
120
|
+
size?: 'sm' | 'md';
|
|
121
|
+
labels?: Partial<UploaderLabels>;
|
|
122
|
+
};
|
|
123
|
+
/**
|
|
124
|
+
* One row of upload state: preview/extension badge, name, size, a progress
|
|
125
|
+
* bar while in flight, the error when it failed. Shared by both uploaders so
|
|
126
|
+
* a file always looks the same wherever it is being uploaded.
|
|
127
|
+
*/
|
|
128
|
+
declare const FileItem: ({ file, onRemove, onAbort, onView, size, labels, }: FileItemProps) => react.JSX.Element;
|
|
129
|
+
|
|
130
|
+
type SlottedUploaderProps<T extends Record<string, ScopeConfig>> = Omit<UseSlottedUploaderOptions<T>, 'uploadOn'> & {
|
|
131
|
+
/**
|
|
132
|
+
* When the slots travel. This presentation has no upload button of its
|
|
133
|
+
* own, so `'manual'` is not offered here: staged slots can only leave
|
|
134
|
+
* through a form's `controllerRef`, which is `'submit'`.
|
|
135
|
+
* @defaultValue 'select'
|
|
136
|
+
* @see UiUploadTrigger
|
|
137
|
+
*/
|
|
138
|
+
uploadOn?: Exclude<UiUploadTrigger, 'manual'>;
|
|
139
|
+
title?: string;
|
|
140
|
+
description?: string;
|
|
141
|
+
/** Hide the bulk dropzone and keep only per-row buttons. */
|
|
142
|
+
hideDropzone?: boolean;
|
|
143
|
+
/**
|
|
144
|
+
* Gate removing a filled slot behind a confirmation dialog. `true` uses
|
|
145
|
+
* the label defaults; an object overrides the copy.
|
|
146
|
+
*/
|
|
147
|
+
confirmRemove?: boolean | {
|
|
148
|
+
title?: string;
|
|
149
|
+
message?: string;
|
|
150
|
+
};
|
|
151
|
+
/**
|
|
152
|
+
* Confirm before a picked file replaces what a slot already holds. The
|
|
153
|
+
* dialog names both files, so the user sees what is about to be lost.
|
|
154
|
+
*/
|
|
155
|
+
confirmReplace?: boolean | {
|
|
156
|
+
title?: string;
|
|
157
|
+
message?: string;
|
|
158
|
+
};
|
|
159
|
+
/**
|
|
160
|
+
* Deletion transport. With it, a confirmed slot removal deletes the object
|
|
161
|
+
* from storage BY ITSELF — unless the scope is `keepOnRemove`, the history
|
|
162
|
+
* contract — same wiring as `Uploader`.
|
|
163
|
+
*/
|
|
164
|
+
removeStrategy?: RemoveStrategy;
|
|
165
|
+
/** Fresh URL right before previewing — for private scopes whose signed url expired. */
|
|
166
|
+
resolveViewUrl?: (file: ViewableFile) => Promise<string>;
|
|
167
|
+
/** Compact paddings and glyphs everywhere. @defaultValue 'md' */
|
|
168
|
+
size?: 'sm' | 'md';
|
|
169
|
+
/** Glyph inside the bulk dropzone. `null` removes it. */
|
|
170
|
+
icon?: ReactNode;
|
|
171
|
+
/** Keyboard shortcut that opens the bulk picker, e.g. `'mod+u'`. */
|
|
172
|
+
shortcut?: string;
|
|
173
|
+
/**
|
|
174
|
+
* Filled with the live controller so the surrounding form can send the
|
|
175
|
+
* staged slots from its own submit. Required by `uploadOn: 'submit'`:
|
|
176
|
+
* without it a staged slot has no way to ever leave.
|
|
177
|
+
*/
|
|
178
|
+
controllerRef?: UploaderControllerRef<void>;
|
|
179
|
+
/**
|
|
180
|
+
* Fires when the zone starts or stops holding files that `upload()`
|
|
181
|
+
* would send. The companion of `controllerRef`: a form that defers the
|
|
182
|
+
* send has no other way to know it has unsent work, so its save button
|
|
183
|
+
* would stay disabled on a pristine form the user has just dropped a
|
|
184
|
+
* file into.
|
|
185
|
+
*/
|
|
186
|
+
onPendingChange?: (hasPending: boolean) => void;
|
|
187
|
+
className?: string;
|
|
188
|
+
};
|
|
189
|
+
/**
|
|
190
|
+
* Named-slot presentation over the same machine as `Uploader`: a status row
|
|
191
|
+
* per slot plus one bulk dropzone whose matcher routes each file to its slot.
|
|
192
|
+
*/
|
|
193
|
+
declare const SlottedUploader: <T extends Record<string, ScopeConfig>>({ uploadOn, title, description, hideDropzone, confirmRemove, confirmReplace, removeStrategy, resolveViewUrl, size, icon, shortcut, controllerRef, onPendingChange, className, ...options }: SlottedUploaderProps<T>) => react.JSX.Element;
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* What {@link UploaderProps.renderFiles} is handed.
|
|
197
|
+
*
|
|
198
|
+
* Both the raw state and the default rows already built, so a custom layout can
|
|
199
|
+
* keep the standard item and only change where — and among what — it sits.
|
|
200
|
+
*/
|
|
201
|
+
type UploaderFilesSlot = {
|
|
202
|
+
/** Files already persisted, exactly as passed in `stored`. */
|
|
203
|
+
stored: StoredFile[];
|
|
204
|
+
/** Files staged in this session, each with its live upload status. */
|
|
205
|
+
staged: UploaderFile[];
|
|
206
|
+
/** The default rows. Place them, wrap them, or drop them entirely. */
|
|
207
|
+
nodes: ReactNode;
|
|
208
|
+
/** Nothing persisted and nothing staged. */
|
|
209
|
+
isEmpty: boolean;
|
|
210
|
+
/** Open a persisted file in the built-in viewer. */
|
|
211
|
+
view: (file: StoredFile) => void;
|
|
212
|
+
/** Forget a staged file. */
|
|
213
|
+
remove: (id: string) => void;
|
|
214
|
+
/**
|
|
215
|
+
* Forget a persisted file, honouring `confirmRemove` and `removeStrategy`.
|
|
216
|
+
* `undefined` when this uploader accepts no removals.
|
|
217
|
+
*/
|
|
218
|
+
removeStored?: (file: StoredFile) => void;
|
|
219
|
+
};
|
|
220
|
+
type UploaderProps<T extends Record<string, ScopeConfig>> = Omit<UseUploaderOptions<T>, 'uploadOn'> & {
|
|
221
|
+
/**
|
|
222
|
+
* When the files travel. @defaultValue 'select'
|
|
223
|
+
* @see UiUploadTrigger — which mode fits which screen
|
|
224
|
+
*/
|
|
225
|
+
uploadOn?: UiUploadTrigger;
|
|
226
|
+
label?: string;
|
|
227
|
+
description?: string;
|
|
228
|
+
/** Files already persisted, rendered on the {@link filesPosition} side. */
|
|
229
|
+
stored?: StoredFile[];
|
|
230
|
+
/**
|
|
231
|
+
* Which side of the dropzone the file lists sit on. @defaultValue 'above'
|
|
232
|
+
*
|
|
233
|
+
* `'below'` keeps the drop target anchored: with the lists above it, every
|
|
234
|
+
* added file pushes the zone further down, so the control the user is
|
|
235
|
+
* repeatedly aiming at moves under the cursor.
|
|
236
|
+
*/
|
|
237
|
+
filesPosition?: 'above' | 'below';
|
|
238
|
+
/**
|
|
239
|
+
* Lay the file lists out yourself: a grid of thumbnails, a single summary
|
|
240
|
+
* line, a count folded into your own card — whatever the screen needs.
|
|
241
|
+
*
|
|
242
|
+
* It replaces the rows, not their place: the result still renders on the
|
|
243
|
+
* {@link filesPosition} side. For a layout the zone itself has to be part of
|
|
244
|
+
* (files BESIDE the dropzone, files inside your own frame), skip this skin
|
|
245
|
+
* and compose `useUploader` with the exported `Dropzone` / `FileItem` /
|
|
246
|
+
* `StoredFileItem` — nothing here is unavailable there.
|
|
247
|
+
*/
|
|
248
|
+
renderFiles?: (slot: UploaderFilesSlot) => ReactNode;
|
|
249
|
+
/**
|
|
250
|
+
* Notified when a persisted file is forgotten — the app's bookkeeping
|
|
251
|
+
* (clear the DB reference). Storage deletion belongs to `removeStrategy`.
|
|
252
|
+
*/
|
|
253
|
+
onRemoveStored?: (file: StoredFile) => void;
|
|
254
|
+
/**
|
|
255
|
+
* Deletion transport. With it, a confirmed removal deletes the object from
|
|
256
|
+
* storage BY ITSELF — unless the scope is `keepOnRemove`, the history
|
|
257
|
+
* contract — so no consumer can orphan by forgetting a callback.
|
|
258
|
+
*/
|
|
259
|
+
removeStrategy?: RemoveStrategy;
|
|
260
|
+
/**
|
|
261
|
+
* Gate `onRemoveStored` behind a confirmation dialog. `true` uses the
|
|
262
|
+
* label defaults; an object overrides the copy for this uploader.
|
|
263
|
+
*/
|
|
264
|
+
confirmRemove?: boolean | {
|
|
265
|
+
title?: string;
|
|
266
|
+
message?: string;
|
|
267
|
+
};
|
|
268
|
+
/** Fresh URL right before previewing — for private scopes whose signed url expired. */
|
|
269
|
+
resolveViewUrl?: (file: ViewableFile) => Promise<string>;
|
|
270
|
+
/** Mobile camera shortcut, forwarded to the dropzone's input. */
|
|
271
|
+
capture?: 'user' | 'environment';
|
|
272
|
+
/** Compact paddings and glyphs everywhere. @defaultValue 'md' */
|
|
273
|
+
size?: 'sm' | 'md';
|
|
274
|
+
/** Glyph inside the dropzone. `null` removes it; defaults to an upload cloud. */
|
|
275
|
+
icon?: ReactNode;
|
|
276
|
+
/** Keyboard shortcut that opens the picker, e.g. `'mod+u'`. Hinted in the zone. */
|
|
277
|
+
shortcut?: string;
|
|
278
|
+
/**
|
|
279
|
+
* Filled with the live controller so the surrounding form can send the
|
|
280
|
+
* staged files from its own submit. Required by `uploadOn: 'submit'`,
|
|
281
|
+
* which has no other way to fire; readable under `'manual'` too, where
|
|
282
|
+
* the zone's button stays the sender.
|
|
283
|
+
*/
|
|
284
|
+
controllerRef?: UploaderControllerRef;
|
|
285
|
+
/**
|
|
286
|
+
* Fires when the zone starts or stops holding files that `upload()`
|
|
287
|
+
* would send. The companion of `controllerRef`: a form that defers the
|
|
288
|
+
* send has no other way to know it has unsent work, so its save button
|
|
289
|
+
* would stay disabled on a pristine form the user has just dropped a
|
|
290
|
+
* file into.
|
|
291
|
+
*/
|
|
292
|
+
onPendingChange?: (hasPending: boolean) => void;
|
|
293
|
+
disabled?: boolean;
|
|
294
|
+
className?: string;
|
|
295
|
+
};
|
|
296
|
+
/**
|
|
297
|
+
* The styled single-zone uploader: one dropzone, one or many files in it.
|
|
298
|
+
* Purely a skin over `useUploader` — apps needing custom markup use the hook
|
|
299
|
+
* directly and lose nothing.
|
|
300
|
+
*
|
|
301
|
+
* Defaults `uploadOn` to `'select'`. `'manual'` gives the zone its own upload
|
|
302
|
+
* button; `'submit'` hands the send to the surrounding form via
|
|
303
|
+
* `controllerRef`. @see UiUploadTrigger for which mode fits which screen.
|
|
304
|
+
*/
|
|
305
|
+
declare const Uploader: <T extends Record<string, ScopeConfig>>({ uploadOn, label, description, stored, filesPosition, renderFiles, onRemoveStored, confirmRemove, resolveViewUrl, capture, size, icon, shortcut, controllerRef, onPendingChange, removeStrategy, disabled, className, ...options }: UploaderProps<T>) => react.JSX.Element;
|
|
306
|
+
|
|
307
|
+
type UseFileViewerOptions = {
|
|
308
|
+
/** Forwarded to the viewer — the authenticated resolver for private urls. */
|
|
309
|
+
resolveUrl?: FileViewerProps['resolveUrl'];
|
|
310
|
+
};
|
|
311
|
+
type UseFileViewerReturn = {
|
|
312
|
+
/** Currently open file, `null` when closed. */
|
|
313
|
+
viewing: ViewableFile | null;
|
|
314
|
+
open: (file: ViewableFile) => void;
|
|
315
|
+
close: () => void;
|
|
316
|
+
/** Spread into `<FileViewer {...viewerProps} />` — the whole wiring. */
|
|
317
|
+
viewerProps: Pick<FileViewerProps, 'file' | 'onClose' | 'resolveUrl'>;
|
|
318
|
+
};
|
|
319
|
+
/**
|
|
320
|
+
* The open/close state every screen with a `FileViewer` repeats. One call
|
|
321
|
+
* replaces the `useState` + prop plumbing:
|
|
322
|
+
*
|
|
323
|
+
* ```tsx
|
|
324
|
+
* const viewer = useFileViewer({ resolveUrl })
|
|
325
|
+
* <button onClick={() => viewer.open(stored)}>Ver</button>
|
|
326
|
+
* <FileViewer {...viewer.viewerProps} />
|
|
327
|
+
* ```
|
|
328
|
+
*/
|
|
329
|
+
declare const useFileViewer: ({ resolveUrl, }?: UseFileViewerOptions) => UseFileViewerReturn;
|
|
330
|
+
|
|
331
|
+
export { ConfirmDialog, type ConfirmDialogProps, Dropzone, type DropzoneProps, FileItem, type FileItemProps, FileViewerProps, SlottedUploader, type SlottedUploaderController, type SlottedUploaderProps, type UiUploadTrigger, Uploader, type UploaderController, type UploaderControllerRef, type UploaderFilesSlot, type UploaderProps, type UseFileViewerOptions, type UseFileViewerReturn, ViewableFile, cn, useFileViewer };
|
package/dist/ui.d.ts
ADDED
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
import { f as UseSlottedUploaderOptions } from './useSlottedUploader-IkG5RhEO.js';
|
|
2
|
+
export { U as UploaderLanguage, d as UploaderProvider, e as UploaderProviderProps, j as useUploaderLabels } from './useSlottedUploader-IkG5RhEO.js';
|
|
3
|
+
import * as react from 'react';
|
|
4
|
+
import { ReactNode } from 'react';
|
|
5
|
+
import { U as UploaderLabels, o as StoredFile, d as ScopeConfig } from './types-BSlJJwti.js';
|
|
6
|
+
import { e as UploaderFile, R as RemoveStrategy, U as UseUploaderOptions } from './useUploader-CQHpj_oI.js';
|
|
7
|
+
import { V as ViewableFile, F as FileViewerProps } from './FileViewer-nuD-GEdJ.js';
|
|
8
|
+
export { a as FileViewer } from './FileViewer-nuD-GEdJ.js';
|
|
9
|
+
|
|
10
|
+
/** Minimal class joiner — the package has no runtime deps to spend on clsx. */
|
|
11
|
+
declare const cn: (...values: (string | false | null | undefined)[]) => string;
|
|
12
|
+
|
|
13
|
+
type ConfirmDialogProps = {
|
|
14
|
+
open: boolean;
|
|
15
|
+
title: string;
|
|
16
|
+
message: ReactNode;
|
|
17
|
+
/** Tone of the affirmative button. @defaultValue 'danger' */
|
|
18
|
+
variant?: 'danger' | 'primary';
|
|
19
|
+
confirmLabel?: string;
|
|
20
|
+
cancelLabel?: string;
|
|
21
|
+
onConfirm: () => void;
|
|
22
|
+
onCancel: () => void;
|
|
23
|
+
labels?: Partial<UploaderLabels>;
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Second-step gate for destructive file actions — remove a stored file,
|
|
27
|
+
* replace a slot. Focus lands on **cancel** so a stray Enter never destroys
|
|
28
|
+
* anything, Escape and the backdrop cancel, and `Tab` stays inside. Exported
|
|
29
|
+
* for app-level use around the uploaders' callbacks.
|
|
30
|
+
*/
|
|
31
|
+
declare const ConfirmDialog: ({ open, title, message, variant, confirmLabel, cancelLabel, onConfirm, onCancel, labels, }: ConfirmDialogProps) => react.ReactPortal | null;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* When a styled uploader sends, as the three flows every product screen falls
|
|
35
|
+
* into — named at the UI level so a call site declares its intent instead of
|
|
36
|
+
* assembling it from `uploadOn` + button + controller by hand:
|
|
37
|
+
*
|
|
38
|
+
* - `'select'` — fires the moment a valid file lands. Avatars, quick
|
|
39
|
+
* replacements: surfaces where the file IS the action and there is nothing
|
|
40
|
+
* else to wait for.
|
|
41
|
+
* - `'submit'` — staged until the surrounding form sends it through
|
|
42
|
+
* `controllerRef`, together with the rest of the fields. The only correct
|
|
43
|
+
* mode when the record the file belongs to does not exist yet, or when a
|
|
44
|
+
* stable-key scope would overwrite what the entity serves before the user
|
|
45
|
+
* confirmed anything.
|
|
46
|
+
* - `'manual'` — staged, and the zone shows its own upload button: the user
|
|
47
|
+
* drops files and decides the moment. Evidence and other punctual flows
|
|
48
|
+
* with no form around them.
|
|
49
|
+
*
|
|
50
|
+
* The hook underneath only knows `'select' | 'manual'` (fire now vs wait for
|
|
51
|
+
* `upload()`): `'submit'` and `'manual'` are the same machine state with a
|
|
52
|
+
* different owner of the send — the form or the zone.
|
|
53
|
+
*/
|
|
54
|
+
type UiUploadTrigger = 'select' | 'submit' | 'manual';
|
|
55
|
+
/**
|
|
56
|
+
* What a surrounding form needs to send the staged files from its own submit —
|
|
57
|
+
* the flow `uploadOn: 'manual'` exists for, where the file travels together
|
|
58
|
+
* with the rest of the fields instead of the moment it is picked.
|
|
59
|
+
*
|
|
60
|
+
* A ref rather than a callback prop: the form reads it inside its submit
|
|
61
|
+
* handler, and a controller passed down would re-render the zone on every
|
|
62
|
+
* keystroke elsewhere in the form.
|
|
63
|
+
*
|
|
64
|
+
* @see AGENTS.md §3 — "Arity lives on the scope", same reasoning one level up
|
|
65
|
+
*/
|
|
66
|
+
type UploaderController<TResult = StoredFile[]> = {
|
|
67
|
+
/** Uploads everything still waiting. Resolves with what the server confirmed. */
|
|
68
|
+
upload: () => Promise<TResult>;
|
|
69
|
+
/** `true` while a file waits for `upload()`. */
|
|
70
|
+
hasPending: boolean;
|
|
71
|
+
isUploading: boolean;
|
|
72
|
+
};
|
|
73
|
+
/** Slots settle through `onChange`, so the batch itself resolves with nothing. */
|
|
74
|
+
type SlottedUploaderController = UploaderController<void>;
|
|
75
|
+
/**
|
|
76
|
+
* Structural rather than React's `RefObject`: that type is read-only under the
|
|
77
|
+
* v18 typings and writable under v19, and the package supports both.
|
|
78
|
+
* `useRef<UploaderController | null>(null)` satisfies it either way.
|
|
79
|
+
*/
|
|
80
|
+
type UploaderControllerRef<TResult = StoredFile[]> = {
|
|
81
|
+
current: UploaderController<TResult> | null;
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
type DropzoneProps = {
|
|
85
|
+
accept: string;
|
|
86
|
+
multiple?: boolean;
|
|
87
|
+
disabled?: boolean;
|
|
88
|
+
/**
|
|
89
|
+
* Mobile camera shortcut: `'environment'` (rear) or `'user'` (front) makes
|
|
90
|
+
* the file picker offer the camera directly on touch devices. Desktop
|
|
91
|
+
* browsers ignore it.
|
|
92
|
+
*/
|
|
93
|
+
capture?: 'user' | 'environment';
|
|
94
|
+
/** @defaultValue 'md' */
|
|
95
|
+
size?: 'sm' | 'md';
|
|
96
|
+
/**
|
|
97
|
+
* Global keyboard shortcut that opens this zone's picker, e.g. `'mod+u'`
|
|
98
|
+
* (⌘U / Ctrl+U). A small key hint renders under the prompt on fine
|
|
99
|
+
* pointers. With several zones on screen, give each its own combo.
|
|
100
|
+
*/
|
|
101
|
+
shortcut?: string;
|
|
102
|
+
onFiles: (files: File[]) => void;
|
|
103
|
+
children: ReactNode;
|
|
104
|
+
className?: string;
|
|
105
|
+
};
|
|
106
|
+
/**
|
|
107
|
+
* The one drag-and-drop surface both uploaders share: drag state, click and
|
|
108
|
+
* keyboard to browse, paste while focused (screenshots land as files), and a
|
|
109
|
+
* hidden input. Presentation comes from `children`.
|
|
110
|
+
*/
|
|
111
|
+
declare const Dropzone: ({ accept, multiple, disabled, capture, size, shortcut, onFiles, children, className, }: DropzoneProps) => react.JSX.Element;
|
|
112
|
+
|
|
113
|
+
type FileItemProps = {
|
|
114
|
+
file: UploaderFile;
|
|
115
|
+
onRemove: () => void;
|
|
116
|
+
onAbort: () => void;
|
|
117
|
+
/** Present on successes when a viewer is available. */
|
|
118
|
+
onView?: () => void;
|
|
119
|
+
/** @defaultValue 'md' */
|
|
120
|
+
size?: 'sm' | 'md';
|
|
121
|
+
labels?: Partial<UploaderLabels>;
|
|
122
|
+
};
|
|
123
|
+
/**
|
|
124
|
+
* One row of upload state: preview/extension badge, name, size, a progress
|
|
125
|
+
* bar while in flight, the error when it failed. Shared by both uploaders so
|
|
126
|
+
* a file always looks the same wherever it is being uploaded.
|
|
127
|
+
*/
|
|
128
|
+
declare const FileItem: ({ file, onRemove, onAbort, onView, size, labels, }: FileItemProps) => react.JSX.Element;
|
|
129
|
+
|
|
130
|
+
type SlottedUploaderProps<T extends Record<string, ScopeConfig>> = Omit<UseSlottedUploaderOptions<T>, 'uploadOn'> & {
|
|
131
|
+
/**
|
|
132
|
+
* When the slots travel. This presentation has no upload button of its
|
|
133
|
+
* own, so `'manual'` is not offered here: staged slots can only leave
|
|
134
|
+
* through a form's `controllerRef`, which is `'submit'`.
|
|
135
|
+
* @defaultValue 'select'
|
|
136
|
+
* @see UiUploadTrigger
|
|
137
|
+
*/
|
|
138
|
+
uploadOn?: Exclude<UiUploadTrigger, 'manual'>;
|
|
139
|
+
title?: string;
|
|
140
|
+
description?: string;
|
|
141
|
+
/** Hide the bulk dropzone and keep only per-row buttons. */
|
|
142
|
+
hideDropzone?: boolean;
|
|
143
|
+
/**
|
|
144
|
+
* Gate removing a filled slot behind a confirmation dialog. `true` uses
|
|
145
|
+
* the label defaults; an object overrides the copy.
|
|
146
|
+
*/
|
|
147
|
+
confirmRemove?: boolean | {
|
|
148
|
+
title?: string;
|
|
149
|
+
message?: string;
|
|
150
|
+
};
|
|
151
|
+
/**
|
|
152
|
+
* Confirm before a picked file replaces what a slot already holds. The
|
|
153
|
+
* dialog names both files, so the user sees what is about to be lost.
|
|
154
|
+
*/
|
|
155
|
+
confirmReplace?: boolean | {
|
|
156
|
+
title?: string;
|
|
157
|
+
message?: string;
|
|
158
|
+
};
|
|
159
|
+
/**
|
|
160
|
+
* Deletion transport. With it, a confirmed slot removal deletes the object
|
|
161
|
+
* from storage BY ITSELF — unless the scope is `keepOnRemove`, the history
|
|
162
|
+
* contract — same wiring as `Uploader`.
|
|
163
|
+
*/
|
|
164
|
+
removeStrategy?: RemoveStrategy;
|
|
165
|
+
/** Fresh URL right before previewing — for private scopes whose signed url expired. */
|
|
166
|
+
resolveViewUrl?: (file: ViewableFile) => Promise<string>;
|
|
167
|
+
/** Compact paddings and glyphs everywhere. @defaultValue 'md' */
|
|
168
|
+
size?: 'sm' | 'md';
|
|
169
|
+
/** Glyph inside the bulk dropzone. `null` removes it. */
|
|
170
|
+
icon?: ReactNode;
|
|
171
|
+
/** Keyboard shortcut that opens the bulk picker, e.g. `'mod+u'`. */
|
|
172
|
+
shortcut?: string;
|
|
173
|
+
/**
|
|
174
|
+
* Filled with the live controller so the surrounding form can send the
|
|
175
|
+
* staged slots from its own submit. Required by `uploadOn: 'submit'`:
|
|
176
|
+
* without it a staged slot has no way to ever leave.
|
|
177
|
+
*/
|
|
178
|
+
controllerRef?: UploaderControllerRef<void>;
|
|
179
|
+
/**
|
|
180
|
+
* Fires when the zone starts or stops holding files that `upload()`
|
|
181
|
+
* would send. The companion of `controllerRef`: a form that defers the
|
|
182
|
+
* send has no other way to know it has unsent work, so its save button
|
|
183
|
+
* would stay disabled on a pristine form the user has just dropped a
|
|
184
|
+
* file into.
|
|
185
|
+
*/
|
|
186
|
+
onPendingChange?: (hasPending: boolean) => void;
|
|
187
|
+
className?: string;
|
|
188
|
+
};
|
|
189
|
+
/**
|
|
190
|
+
* Named-slot presentation over the same machine as `Uploader`: a status row
|
|
191
|
+
* per slot plus one bulk dropzone whose matcher routes each file to its slot.
|
|
192
|
+
*/
|
|
193
|
+
declare const SlottedUploader: <T extends Record<string, ScopeConfig>>({ uploadOn, title, description, hideDropzone, confirmRemove, confirmReplace, removeStrategy, resolveViewUrl, size, icon, shortcut, controllerRef, onPendingChange, className, ...options }: SlottedUploaderProps<T>) => react.JSX.Element;
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* What {@link UploaderProps.renderFiles} is handed.
|
|
197
|
+
*
|
|
198
|
+
* Both the raw state and the default rows already built, so a custom layout can
|
|
199
|
+
* keep the standard item and only change where — and among what — it sits.
|
|
200
|
+
*/
|
|
201
|
+
type UploaderFilesSlot = {
|
|
202
|
+
/** Files already persisted, exactly as passed in `stored`. */
|
|
203
|
+
stored: StoredFile[];
|
|
204
|
+
/** Files staged in this session, each with its live upload status. */
|
|
205
|
+
staged: UploaderFile[];
|
|
206
|
+
/** The default rows. Place them, wrap them, or drop them entirely. */
|
|
207
|
+
nodes: ReactNode;
|
|
208
|
+
/** Nothing persisted and nothing staged. */
|
|
209
|
+
isEmpty: boolean;
|
|
210
|
+
/** Open a persisted file in the built-in viewer. */
|
|
211
|
+
view: (file: StoredFile) => void;
|
|
212
|
+
/** Forget a staged file. */
|
|
213
|
+
remove: (id: string) => void;
|
|
214
|
+
/**
|
|
215
|
+
* Forget a persisted file, honouring `confirmRemove` and `removeStrategy`.
|
|
216
|
+
* `undefined` when this uploader accepts no removals.
|
|
217
|
+
*/
|
|
218
|
+
removeStored?: (file: StoredFile) => void;
|
|
219
|
+
};
|
|
220
|
+
type UploaderProps<T extends Record<string, ScopeConfig>> = Omit<UseUploaderOptions<T>, 'uploadOn'> & {
|
|
221
|
+
/**
|
|
222
|
+
* When the files travel. @defaultValue 'select'
|
|
223
|
+
* @see UiUploadTrigger — which mode fits which screen
|
|
224
|
+
*/
|
|
225
|
+
uploadOn?: UiUploadTrigger;
|
|
226
|
+
label?: string;
|
|
227
|
+
description?: string;
|
|
228
|
+
/** Files already persisted, rendered on the {@link filesPosition} side. */
|
|
229
|
+
stored?: StoredFile[];
|
|
230
|
+
/**
|
|
231
|
+
* Which side of the dropzone the file lists sit on. @defaultValue 'above'
|
|
232
|
+
*
|
|
233
|
+
* `'below'` keeps the drop target anchored: with the lists above it, every
|
|
234
|
+
* added file pushes the zone further down, so the control the user is
|
|
235
|
+
* repeatedly aiming at moves under the cursor.
|
|
236
|
+
*/
|
|
237
|
+
filesPosition?: 'above' | 'below';
|
|
238
|
+
/**
|
|
239
|
+
* Lay the file lists out yourself: a grid of thumbnails, a single summary
|
|
240
|
+
* line, a count folded into your own card — whatever the screen needs.
|
|
241
|
+
*
|
|
242
|
+
* It replaces the rows, not their place: the result still renders on the
|
|
243
|
+
* {@link filesPosition} side. For a layout the zone itself has to be part of
|
|
244
|
+
* (files BESIDE the dropzone, files inside your own frame), skip this skin
|
|
245
|
+
* and compose `useUploader` with the exported `Dropzone` / `FileItem` /
|
|
246
|
+
* `StoredFileItem` — nothing here is unavailable there.
|
|
247
|
+
*/
|
|
248
|
+
renderFiles?: (slot: UploaderFilesSlot) => ReactNode;
|
|
249
|
+
/**
|
|
250
|
+
* Notified when a persisted file is forgotten — the app's bookkeeping
|
|
251
|
+
* (clear the DB reference). Storage deletion belongs to `removeStrategy`.
|
|
252
|
+
*/
|
|
253
|
+
onRemoveStored?: (file: StoredFile) => void;
|
|
254
|
+
/**
|
|
255
|
+
* Deletion transport. With it, a confirmed removal deletes the object from
|
|
256
|
+
* storage BY ITSELF — unless the scope is `keepOnRemove`, the history
|
|
257
|
+
* contract — so no consumer can orphan by forgetting a callback.
|
|
258
|
+
*/
|
|
259
|
+
removeStrategy?: RemoveStrategy;
|
|
260
|
+
/**
|
|
261
|
+
* Gate `onRemoveStored` behind a confirmation dialog. `true` uses the
|
|
262
|
+
* label defaults; an object overrides the copy for this uploader.
|
|
263
|
+
*/
|
|
264
|
+
confirmRemove?: boolean | {
|
|
265
|
+
title?: string;
|
|
266
|
+
message?: string;
|
|
267
|
+
};
|
|
268
|
+
/** Fresh URL right before previewing — for private scopes whose signed url expired. */
|
|
269
|
+
resolveViewUrl?: (file: ViewableFile) => Promise<string>;
|
|
270
|
+
/** Mobile camera shortcut, forwarded to the dropzone's input. */
|
|
271
|
+
capture?: 'user' | 'environment';
|
|
272
|
+
/** Compact paddings and glyphs everywhere. @defaultValue 'md' */
|
|
273
|
+
size?: 'sm' | 'md';
|
|
274
|
+
/** Glyph inside the dropzone. `null` removes it; defaults to an upload cloud. */
|
|
275
|
+
icon?: ReactNode;
|
|
276
|
+
/** Keyboard shortcut that opens the picker, e.g. `'mod+u'`. Hinted in the zone. */
|
|
277
|
+
shortcut?: string;
|
|
278
|
+
/**
|
|
279
|
+
* Filled with the live controller so the surrounding form can send the
|
|
280
|
+
* staged files from its own submit. Required by `uploadOn: 'submit'`,
|
|
281
|
+
* which has no other way to fire; readable under `'manual'` too, where
|
|
282
|
+
* the zone's button stays the sender.
|
|
283
|
+
*/
|
|
284
|
+
controllerRef?: UploaderControllerRef;
|
|
285
|
+
/**
|
|
286
|
+
* Fires when the zone starts or stops holding files that `upload()`
|
|
287
|
+
* would send. The companion of `controllerRef`: a form that defers the
|
|
288
|
+
* send has no other way to know it has unsent work, so its save button
|
|
289
|
+
* would stay disabled on a pristine form the user has just dropped a
|
|
290
|
+
* file into.
|
|
291
|
+
*/
|
|
292
|
+
onPendingChange?: (hasPending: boolean) => void;
|
|
293
|
+
disabled?: boolean;
|
|
294
|
+
className?: string;
|
|
295
|
+
};
|
|
296
|
+
/**
|
|
297
|
+
* The styled single-zone uploader: one dropzone, one or many files in it.
|
|
298
|
+
* Purely a skin over `useUploader` — apps needing custom markup use the hook
|
|
299
|
+
* directly and lose nothing.
|
|
300
|
+
*
|
|
301
|
+
* Defaults `uploadOn` to `'select'`. `'manual'` gives the zone its own upload
|
|
302
|
+
* button; `'submit'` hands the send to the surrounding form via
|
|
303
|
+
* `controllerRef`. @see UiUploadTrigger for which mode fits which screen.
|
|
304
|
+
*/
|
|
305
|
+
declare const Uploader: <T extends Record<string, ScopeConfig>>({ uploadOn, label, description, stored, filesPosition, renderFiles, onRemoveStored, confirmRemove, resolveViewUrl, capture, size, icon, shortcut, controllerRef, onPendingChange, removeStrategy, disabled, className, ...options }: UploaderProps<T>) => react.JSX.Element;
|
|
306
|
+
|
|
307
|
+
type UseFileViewerOptions = {
|
|
308
|
+
/** Forwarded to the viewer — the authenticated resolver for private urls. */
|
|
309
|
+
resolveUrl?: FileViewerProps['resolveUrl'];
|
|
310
|
+
};
|
|
311
|
+
type UseFileViewerReturn = {
|
|
312
|
+
/** Currently open file, `null` when closed. */
|
|
313
|
+
viewing: ViewableFile | null;
|
|
314
|
+
open: (file: ViewableFile) => void;
|
|
315
|
+
close: () => void;
|
|
316
|
+
/** Spread into `<FileViewer {...viewerProps} />` — the whole wiring. */
|
|
317
|
+
viewerProps: Pick<FileViewerProps, 'file' | 'onClose' | 'resolveUrl'>;
|
|
318
|
+
};
|
|
319
|
+
/**
|
|
320
|
+
* The open/close state every screen with a `FileViewer` repeats. One call
|
|
321
|
+
* replaces the `useState` + prop plumbing:
|
|
322
|
+
*
|
|
323
|
+
* ```tsx
|
|
324
|
+
* const viewer = useFileViewer({ resolveUrl })
|
|
325
|
+
* <button onClick={() => viewer.open(stored)}>Ver</button>
|
|
326
|
+
* <FileViewer {...viewer.viewerProps} />
|
|
327
|
+
* ```
|
|
328
|
+
*/
|
|
329
|
+
declare const useFileViewer: ({ resolveUrl, }?: UseFileViewerOptions) => UseFileViewerReturn;
|
|
330
|
+
|
|
331
|
+
export { ConfirmDialog, type ConfirmDialogProps, Dropzone, type DropzoneProps, FileItem, type FileItemProps, FileViewerProps, SlottedUploader, type SlottedUploaderController, type SlottedUploaderProps, type UiUploadTrigger, Uploader, type UploaderController, type UploaderControllerRef, type UploaderFilesSlot, type UploaderProps, type UseFileViewerOptions, type UseFileViewerReturn, ViewableFile, cn, useFileViewer };
|