@pipelex/mthds-form 0.23.2 → 0.24.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.
@@ -24,6 +24,29 @@ function addChosenFiles(held, chosen) {
24
24
  }
25
25
  return items;
26
26
  }
27
+ function placeDroppedFiles(held, count, maxItemCount, isFree = () => true) {
28
+ const items = [...held ?? []];
29
+ const targets = [];
30
+ for (let index = 0; index < items.length && targets.length < count; index += 1) {
31
+ if (heldUrl(items[index]) === void 0 && isFree(index)) targets.push(index);
32
+ }
33
+ let appended = 0;
34
+ while (targets.length < count && (maxItemCount === void 0 || items.length < maxItemCount)) {
35
+ targets.push(items.length);
36
+ items.push(void 0);
37
+ appended += 1;
38
+ }
39
+ return { items, appended, targets, leftOut: count - targets.length };
40
+ }
41
+ function roomForDroppedFiles(held, maxItemCount, isFree = () => true) {
42
+ if (maxItemCount === void 0) return void 0;
43
+ const items = held ?? [];
44
+ let free = 0;
45
+ for (let index = 0; index < items.length; index += 1) {
46
+ if (heldUrl(items[index]) === void 0 && isFree(index)) free += 1;
47
+ }
48
+ return free + Math.max(0, maxItemCount - items.length);
49
+ }
27
50
  function chosenFilesAdded(held, chosen) {
28
51
  const urls = heldUrls(held ?? []);
29
52
  const before = urls.size;
@@ -36,6 +59,6 @@ function roomForChosenFiles(held, maxItemCount) {
36
59
  return Math.max(0, maxItemCount - files);
37
60
  }
38
61
 
39
- export { addChosenFiles, chosenFilesAdded, roomForChosenFiles, sourceFileValue };
40
- //# sourceMappingURL=chunk-O2VIVDHO.js.map
41
- //# sourceMappingURL=chunk-O2VIVDHO.js.map
62
+ export { addChosenFiles, chosenFilesAdded, placeDroppedFiles, roomForChosenFiles, roomForDroppedFiles, sourceFileValue };
63
+ //# sourceMappingURL=chunk-NSHGLM6A.js.map
64
+ //# sourceMappingURL=chunk-NSHGLM6A.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/sources.ts"],"names":[],"mappings":"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","file":"chunk-NSHGLM6A.js","sourcesContent":["/**\n * The two optional host seams beside the upload: a **file source**, which lets\n * a file field take a file the host already stores, and an **example source**,\n * which lets a whole form take a set of values someone saved for the method.\n *\n * The kernel states the shapes and applies the answers; the host answers. It\n * knows nothing of any platform's routes, records or permissions: a host maps\n * its own list onto these shapes, and a host that gives neither seam gets the\n * form it had before. The controls that use them are `./react`'s\n * (`FilePicker`, `FillWithExample`), and docs/sources.md is the contract.\n *\n * Shapes and pure functions only, so a host's server can import the types it\n * answers with, and the rules that write an answer into a value are tested in\n * node. No React, no fetch. What a row SAYS is data here (`SourceFile`'s\n * `typeLabel` and `origin` are the host's words); how a row DRAWS is a\n * rendering, so the glyph a host draws on a row is `./react`'s\n * (`FilePickerSource.renderGlyph`), never a field of these shapes.\n */\n\n/** Whose files a file source lists: the person's own, or the whole team's. */\nexport type FileSourceScope = 'mine' | 'everyone';\n\n/** One page of a file source's list, as the picker asks for it. */\nexport interface FileSourceQuery {\n /** What the person searched for, trimmed; empty when they searched for nothing. */\n search: string;\n /** Whose files to list. */\n scope: FileSourceScope;\n /**\n * The MIME types the field accepts, after any narrowing a host applied\n * (`narrowFileFormats`), in the field's own order. The list must hold only\n * files of these types: the picker writes what is chosen straight into the\n * field, and a field that refuses a dropped file of another type must not\n * be handed one this way.\n */\n mediaTypes: readonly string[];\n /** Where to continue: absent for the first page, else the previous page's `nextCursor`. */\n cursor?: string;\n /** Aborted when the answer is no longer wanted: a newer search, a closed picker. */\n signal: AbortSignal;\n}\n\n/** One row of a file source's list: a file the host already stores. */\nexport interface SourceFile {\n /** The row's identity in the host's list, which a choice is kept by. */\n id: string;\n /**\n * The stored file's address, written into the field as its `url`, exactly as\n * an upload's result is. Several rows may share one address.\n */\n url: string;\n /** The file's name, shown on the row and written into the field as its `filename`. */\n name: string;\n /**\n * The file's MIME type, which picks the field's format the row's file is:\n * the row names that format's label (\"PNG\") unless `typeLabel` words the\n * type, and draws an image's glyph or a document's from it unless the\n * host draws its own.\n */\n mediaType?: string;\n /**\n * The file's type in the host's own words (\"Picture\", \"PDF document\"), said\n * on the row's second line in place of the format's label, so a row reads\n * as the host's own list of files does. An empty string counts as none.\n */\n typeLabel?: string;\n /** The file's size in bytes. */\n size?: number;\n /** How the file came to be. A `test` file is marked \"Test file\" at the head of its row's second line. */\n kind?: 'uploaded' | 'generated' | 'test';\n /** When the file was stored, as an ISO 8601 timestamp; the row says it as a date. */\n createdAt?: string;\n /**\n * Where the file came from, in the host's own words (\"Uploaded by Ana\",\n * \"Made by Invoice extraction\"), shown on the row's second line as it is.\n */\n origin?: string;\n}\n\n/** One page of a file source's answer. */\nexport interface FileSourcePage {\n /** The page's files, newest first. A page may be empty and still carry a cursor. */\n files: readonly SourceFile[];\n /** Where the next page starts, or nothing when this page is the last. */\n nextCursor?: string | null;\n}\n\n/**\n * Where a file field can take a file the host already stores. When a form has\n * one, every file field offers \"Choose from your team's files\", and a list of\n * files offers it for several at once.\n */\nexport interface FileSource {\n /** Lists one page of files for a query. Rejecting shows the failure with Try again. */\n list: (query: FileSourceQuery) => Promise<FileSourcePage>;\n /** Which of Mine and Everyone the picker opens on. */\n initialScope: FileSourceScope;\n}\n\n/** One example of a method: a name and the inputs a run of it sends. */\nexport interface FormExample {\n /** The example's identity in the host's list. */\n id: string;\n /** The example's name, shown on its row. */\n name: string;\n /**\n * The inputs as a run takes them: each input `{ concept, content }`, an\n * optional input left out, an empty plural input a bare `[]`. It is the\n * shape `apiInputsFromRunValues` produces, so a host saves an example from\n * `form.inputs` and hands it back as it is.\n */\n inputs: Readonly<Record<string, unknown>>;\n}\n\n/** One page of an example source's list. */\nexport interface ExampleSourceQuery {\n /** Where to continue: absent for the first page, else the previous page's `nextCursor`. */\n cursor?: string;\n /** Aborted when the answer is no longer wanted. */\n signal: AbortSignal;\n}\n\n/** One page of an example source's answer. */\nexport interface ExampleSourcePage {\n /** The page's examples, in the order the list shows them. */\n examples: readonly FormExample[];\n /** Where the next page starts, or nothing when this page is the last. */\n nextCursor?: string | null;\n}\n\n/**\n * Where a form can take a whole set of values. When a form has one, it offers\n * \"Fill with an example\". `rename` and `delete` are capabilities: each is\n * offered on a row's menu only when given, so a host gives them only to a\n * person allowed to change examples.\n */\nexport interface ExampleSource {\n /** Lists one page of the method's examples. Rejecting shows the failure with Try again. */\n list: (query: ExampleSourceQuery) => Promise<ExampleSourcePage>;\n /**\n * Renames an example. Resolving to the example as stored replaces the row;\n * resolving to nothing keeps the row under the new name.\n */\n rename?: (example: FormExample, name: string) => Promise<FormExample | void>;\n /** Deletes an example. Asked for only after the person confirmed it. */\n delete?: (example: FormExample) => Promise<void>;\n /**\n * Whether \"Fill with an example\" is left out while the method has no\n * example. `false` by default, when the empty list says how to make one.\n * A host sets it for a person who cannot save an example, to whom an empty\n * list has nothing to offer; the form then asks for the first page as it\n * mounts, to know whether to offer the control.\n */\n hideWhenEmpty?: boolean;\n}\n\n/** What a chosen file writes into its field: the same `{ url, filename }` an upload writes. */\nexport function sourceFileValue(file: SourceFile): { url: string; filename: string } {\n return { url: file.url, filename: file.name };\n}\n\n/** The `url` a list item holds, when it is a file value holding one. */\nfunction heldUrl(item: unknown): string | undefined {\n if (!item || typeof item !== 'object') return undefined;\n const url = (item as { url?: unknown }).url;\n return typeof url === 'string' && url !== '' ? url : undefined;\n}\n\n/** The addresses a list's items hold, each once. */\nfunction heldUrls(items: readonly unknown[]): Set<string> {\n return new Set(items.map(heldUrl).filter((url): url is string => url !== undefined));\n}\n\n/**\n * A list of files with the chosen files written into it, in the order they\n * were chosen: first into the rows that hold no file yet (a row the person\n * added and left empty), then as new rows after the last. A file whose address\n * the list already holds is not written again, and neither is a second choice\n * of one address: two rows of a source may name the same stored file. Returns\n * a new array and leaves `held` as it was.\n */\nexport function addChosenFiles(\n held: readonly unknown[] | undefined,\n chosen: readonly SourceFile[],\n): unknown[] {\n const items = [...(held ?? [])];\n const urls = heldUrls(items);\n let free = 0;\n for (const file of chosen) {\n if (urls.has(file.url)) continue;\n urls.add(file.url);\n while (free < items.length && heldUrl(items[free]) !== undefined) free += 1;\n if (free < items.length) items[free] = sourceFileValue(file);\n else items.push(sourceFileValue(file));\n free += 1;\n }\n return items;\n}\n\n/** Where `placeDroppedFiles` puts a batch of files, and what it leaves out. */\nexport interface DroppedFilesPlacement {\n /**\n * The list once the rows the batch needs are appended: `held` as it was,\n * then one empty row (`undefined`) per file that did not go into a free row.\n * A new array whenever rows are appended, and a copy of `held` otherwise.\n */\n items: unknown[];\n /** How many rows were appended, so a caller writes the list only when it grew. */\n appended: number;\n /** The row each placed file goes to, in the order the files were given. */\n targets: number[];\n /** How many files, from the end of the batch, found no room. */\n leftOut: number;\n}\n\n/**\n * Where a batch of `count` files dropped on a list of files goes: first into\n * the rows that are free, in index order, then into new rows after the last,\n * as far as `maxItemCount` allows. The files keep the order they were given,\n * and those beyond the room are left out from the end of the batch.\n *\n * A row is free when it holds no file (its value carries no `url`) and\n * `isFree` says so too. The caller's predicate is where it adds what only it\n * knows of a row: that a file is still arriving there, whose write-back the row\n * must be kept for, or that the host reports a failure there, the only record\n * that one of the person's files did not arrive. Without one, every row holding\n * no file is free.\n *\n * It mirrors `addChosenFiles`, so the two ways several files enter a list fill\n * it alike, and it knows nothing of `File`: the caller judges each file and\n * hands over how many it accepted.\n */\nexport function placeDroppedFiles(\n held: readonly unknown[] | undefined,\n count: number,\n maxItemCount: number | undefined,\n isFree: (index: number) => boolean = () => true,\n): DroppedFilesPlacement {\n const items = [...(held ?? [])];\n const targets: number[] = [];\n for (let index = 0; index < items.length && targets.length < count; index += 1) {\n if (heldUrl(items[index]) === undefined && isFree(index)) targets.push(index);\n }\n let appended = 0;\n while (targets.length < count && (maxItemCount === undefined || items.length < maxItemCount)) {\n targets.push(items.length);\n items.push(undefined);\n appended += 1;\n }\n return { items, appended, targets, leftOut: count - targets.length };\n}\n\n/**\n * How many files `placeDroppedFiles` can place in `held`: its free rows, by the\n * same test, and the rows its bound still allows after the last. `undefined`\n * when the list has no bound, since appending then never runs out.\n */\nexport function roomForDroppedFiles(\n held: readonly unknown[] | undefined,\n maxItemCount: number | undefined,\n isFree: (index: number) => boolean = () => true,\n): number | undefined {\n if (maxItemCount === undefined) return undefined;\n const items = held ?? [];\n let free = 0;\n for (let index = 0; index < items.length; index += 1) {\n if (heldUrl(items[index]) === undefined && isFree(index)) free += 1;\n }\n return free + Math.max(0, maxItemCount - items.length);\n}\n\n/**\n * How many files `addChosenFiles` writes for `chosen` into `held`: the choices\n * whose address the list does not hold yet, each address once. This is what a\n * choice spends of `roomForChosenFiles`, so a held file chosen again, or a\n * second row naming a chosen file, takes no room.\n */\nexport function chosenFilesAdded(\n held: readonly unknown[] | undefined,\n chosen: readonly SourceFile[],\n): number {\n const urls = heldUrls(held ?? []);\n const before = urls.size;\n for (const file of chosen) urls.add(file.url);\n return urls.size - before;\n}\n\n/**\n * How many files a list may still take from a source: its bound less the files\n * it holds, since `addChosenFiles` writes into its empty rows before adding\n * any. `undefined` when the list has no bound.\n */\nexport function roomForChosenFiles(\n held: readonly unknown[] | undefined,\n maxItemCount: number | undefined,\n): number | undefined {\n if (maxItemCount === undefined) return undefined;\n const files = (held ?? []).filter((item) => heldUrl(item) !== undefined).length;\n return Math.max(0, maxItemCount - files);\n}\n"]}
@@ -1,6 +1,6 @@
1
1
  import { planFileSave, planStuffSave, splitFileName, typedFileStem } from './chunk-ICUEHNEH.js';
2
2
  import { seedInputs, closeOptionalStructures, openOptionalStructures } from './chunk-VH5CDQDR.js';
3
- import { sourceFileValue, roomForChosenFiles, addChosenFiles, chosenFilesAdded } from './chunk-O2VIVDHO.js';
3
+ import { sourceFileValue, roomForChosenFiles, roomForDroppedFiles, addChosenFiles, placeDroppedFiles, chosenFilesAdded } from './chunk-NSHGLM6A.js';
4
4
  import { readStuffFile } from './chunk-RRTI7AIR.js';
5
5
  import { apiInputsFromRunValues, sameRunValues, runValuesFromInputs, setValueAtPath } from './chunk-ZX52CQJ4.js';
6
6
  import { viewableUrl, isNativeDateNode, isNativeHtmlNode, readHtmlContent, isNativeCompositeNode, readCompositeContent, isNativeValueNode, readDateContent, formatDateContent, readImageContent, readDataUrl, readDocumentContent } from './chunk-CM4KVG53.js';
@@ -31,6 +31,9 @@ import { marked } from 'marked';
31
31
  function cn(...inputs) {
32
32
  return twMerge(clsx(inputs));
33
33
  }
34
+ function isThenable(answer) {
35
+ return (typeof answer === "object" || typeof answer === "function") && answer !== null && typeof answer.then === "function";
36
+ }
34
37
  var CATEGORY_ICON = {
35
38
  text: Type,
36
39
  date: CalendarDays,
@@ -222,6 +225,9 @@ function formatFileDate(date) {
222
225
  if (days === 1) return "Yesterday";
223
226
  return inCurrentYear(date) ? DAY.format(date) : DAY_YEAR.format(date);
224
227
  }
228
+ function someNames(names, more) {
229
+ return AND.format(more > 0 ? [...names, `${more} more`] : names);
230
+ }
225
231
  var EN_FIELD_STRINGS = {
226
232
  optionalBadge: "optional",
227
233
  selectPlaceholder: "Select\u2026",
@@ -276,6 +282,18 @@ var EN_FIELD_STRINGS = {
276
282
  yes: "Yes",
277
283
  no: "No",
278
284
  unsupportedFileType: (formats) => `That file type is not supported. Accepted formats: ${formats}.`,
285
+ dropFilesOrBrowse: "Drop files or click to browse",
286
+ dropFilesToAdd: "Drop to add the files",
287
+ addFilesAria: (label) => label ? `Add files to ${label}` : "Add files",
288
+ filesAdded: (count) => count === 1 ? "1 file added" : `${count} files added`,
289
+ filesRefused: (names, more, formats) => `${names.length + more === 1 ? "Not added, because its" : "Not added, because their"} file type is not supported: ${someNames(names, more)}. Accepted formats: ${formats}.`,
290
+ filesLeftOut: (names, more, fitted) => {
291
+ const these = names.length + more === 1 ? "this file was" : "these files were";
292
+ const room = fitted === 0 ? "The list is full" : `The list had room for ${fitted === 1 ? "1 more file" : `${fitted} more files`}`;
293
+ return `${room}, so ${these} not added: ${someNames(names, more)}.`;
294
+ },
295
+ oneFileOnly: "This field takes one file.",
296
+ oneFileOnlyInList: "This field takes one file. Drop several files on the list's drop area.",
279
297
  hideOptionalFields: "Hide optional fields",
280
298
  hideOptionalInputs: "Hide optional inputs",
281
299
  optionalFieldsCount: (count) => count === 1 ? "1 optional field" : `${count} optional fields`,
@@ -377,6 +395,9 @@ function formatBytes2(bytes) {
377
395
  if (bytes < 1024 * 1024) return KILOBYTES.format(bytes / 1024);
378
396
  return MEGABYTES.format(bytes / (1024 * 1024));
379
397
  }
398
+ function someNames2(names, more) {
399
+ return AND2.format(more > 0 ? [...names, `${more} ${plural(more, "autre", "autres")}`] : names);
400
+ }
380
401
  var FR_FIELD_STRINGS = {
381
402
  optionalBadge: "facultatif",
382
403
  selectPlaceholder: "Choisir\u2026",
@@ -431,6 +452,18 @@ var FR_FIELD_STRINGS = {
431
452
  yes: "Oui",
432
453
  no: "Non",
433
454
  unsupportedFileType: (formats) => `Ce type de fichier n\u2019est pas pris en charge. Formats accept\xE9s\xA0: ${formats}.`,
455
+ dropFilesOrBrowse: "D\xE9posez des fichiers ou cliquez pour parcourir",
456
+ dropFilesToAdd: "D\xE9posez pour ajouter les fichiers",
457
+ addFilesAria: (label) => label ? `Ajouter des fichiers \xE0 ${label}` : "Ajouter des fichiers",
458
+ filesAdded: (count) => `${count} ${plural(count, "fichier ajout\xE9", "fichiers ajout\xE9s")}`,
459
+ filesRefused: (names, more, formats) => `${names.length + more === 1 ? "Non ajout\xE9, car son type n\u2019est pas pris en charge" : "Non ajout\xE9s, car leur type n\u2019est pas pris en charge"}\xA0: ${someNames2(names, more)}. Formats accept\xE9s\xA0: ${formats}.`,
460
+ filesLeftOut: (names, more, fitted) => {
461
+ const these = names.length + more === 1 ? "ce fichier n\u2019a donc pas \xE9t\xE9 ajout\xE9" : "ces fichiers n\u2019ont donc pas \xE9t\xE9 ajout\xE9s";
462
+ const room = fitted === 0 ? "La liste est pleine" : `La liste n\u2019avait de place que pour ${fitted} ${plural(fitted, "fichier", "fichiers")} de plus`;
463
+ return `${room}, ${these}\xA0: ${someNames2(names, more)}.`;
464
+ },
465
+ oneFileOnly: "Ce champ accepte un seul fichier.",
466
+ oneFileOnlyInList: "Ce champ accepte un seul fichier. D\xE9posez-en plusieurs dans la zone de d\xE9p\xF4t de la liste.",
434
467
  hideOptionalFields: "Masquer les champs facultatifs",
435
468
  hideOptionalInputs: "Masquer les entr\xE9es facultatives",
436
469
  optionalFieldsCount: (count) => `${count} ${plural(count, "champ facultatif", "champs facultatifs")}`,
@@ -1267,6 +1300,10 @@ function FileRow({
1267
1300
  }
1268
1301
  );
1269
1302
  }
1303
+ var ListDropAreaContext = createContext(null);
1304
+ function useListDropArea() {
1305
+ return useContext(ListDropAreaContext);
1306
+ }
1270
1307
  var IMAGE_EXT_RE = /\.(jpg|jpeg|png|gif|webp|bmp|svg|avif)(\?|$)/i;
1271
1308
  var PDF_EXT_RE = /\.pdf(\?|$)/i;
1272
1309
  var DATA_URL_MIME_RE = /^data:([^;,]+)/i;
@@ -1278,9 +1315,6 @@ function isReadableLink(url) {
1278
1315
  function dataUrlMime(url) {
1279
1316
  return DATA_URL_MIME_RE.exec(url)?.[1]?.toLowerCase();
1280
1317
  }
1281
- function isThenable(answer) {
1282
- return (typeof answer === "object" || typeof answer === "function") && answer !== null && typeof answer.then === "function";
1283
- }
1284
1318
  function storedUrlOf(stored) {
1285
1319
  if (typeof stored === "string") return stored === "" ? void 0 : stored;
1286
1320
  if (stored === null || typeof stored !== "object") return void 0;
@@ -1336,6 +1370,7 @@ function FileField({
1336
1370
  }, []);
1337
1371
  useEffect(() => () => setLocal(null), [setLocal]);
1338
1372
  const [rejected, setRejected] = useState(null);
1373
+ const dropArea = useListDropArea();
1339
1374
  const [dismissedUploadError, setDismissedUploadError] = useState(null);
1340
1375
  useEffect(() => {
1341
1376
  if (uploadError === void 0) setDismissedUploadError(null);
@@ -1349,19 +1384,11 @@ function FileField({
1349
1384
  const hint = acceptLabel(formats);
1350
1385
  const accept = useMemo(() => acceptMap(formats), [formats]);
1351
1386
  const canUpload = onDropFile !== void 0;
1352
- const handleFile = useCallback(
1353
- (file) => {
1354
- if (!onDropFile) return;
1355
- if (!isAcceptedFile(formats, file)) {
1356
- setRejected(file.name);
1357
- return;
1358
- }
1359
- setRejected(null);
1360
- setDismissedUploadError(null);
1361
- const upload = onDropFile(file);
1387
+ const track = useCallback(
1388
+ (file, upload, heldUrl) => {
1362
1389
  const awaitingUpload = isThenable(upload);
1363
1390
  setLocal({ objectUrl: URL.createObjectURL(file), type: file.type, awaitingUpload });
1364
- urlInFlight.current = value?.url;
1391
+ urlInFlight.current = heldUrl;
1365
1392
  flightSeen.current = false;
1366
1393
  if (!awaitingUpload) return;
1367
1394
  const generation = localGeneration.current;
@@ -1378,8 +1405,28 @@ function FileField({
1378
1405
  }
1379
1406
  );
1380
1407
  },
1381
- [formats, setLocal, onDropFile, value?.url]
1408
+ [setLocal]
1409
+ );
1410
+ const handleFile = useCallback(
1411
+ (file) => {
1412
+ if (!onDropFile) return;
1413
+ if (!isAcceptedFile(formats, file)) {
1414
+ setRejected({ kind: "type", name: file.name, over: value?.url });
1415
+ return;
1416
+ }
1417
+ setRejected(null);
1418
+ setDismissedUploadError(null);
1419
+ track(file, onDropFile(file), value?.url);
1420
+ },
1421
+ [formats, onDropFile, track, value?.url]
1382
1422
  );
1423
+ useEffect(() => {
1424
+ const placed = dropArea?.takePlaced(id);
1425
+ if (!placed) return;
1426
+ setRejected(null);
1427
+ setDismissedUploadError(null);
1428
+ track(placed.file, placed.upload, void 0);
1429
+ }, [dropArea, id, track]);
1383
1430
  const busy = disabled || uploading;
1384
1431
  const {
1385
1432
  getRootProps,
@@ -1403,17 +1450,22 @@ function FileField({
1403
1450
  // handlers on a `role="presentation"` div, and the element a keyboard or
1404
1451
  // voice-control user lands on is a generic with no role and no name.
1405
1452
  noKeyboard: true,
1406
- onDrop: (files) => {
1407
- const file = files[0];
1408
- if (file) handleFile(file);
1409
- },
1410
- // react-dropzone rejects a file its own matcher refuses without ever calling
1411
- // `onDrop`, so without this a wrong drop is silently swallowed - the control
1412
- // simply does nothing, which reads as a broken dropzone rather than a
1453
+ // Every drop and pick lands here, the files react-dropzone's own matcher
1454
+ // refused included. Those must be answered: left alone, a wrong drop is
1455
+ // silently swallowed, which reads as a broken dropzone rather than a
1413
1456
  // refused file.
1414
- onDropRejected: (rejections) => {
1457
+ onDrop: (accepted, rejections) => {
1458
+ if (accepted.length + rejections.length > 1) {
1459
+ setRejected({ kind: "batch", over: value?.url });
1460
+ return;
1461
+ }
1462
+ const file = accepted[0];
1463
+ if (file) {
1464
+ handleFile(file);
1465
+ return;
1466
+ }
1415
1467
  const name = rejections[0]?.file.name;
1416
- if (name) setRejected(name);
1468
+ if (name) setRejected({ kind: "type", name, over: value?.url });
1417
1469
  }
1418
1470
  });
1419
1471
  const clear = useCallback(() => {
@@ -1439,11 +1491,13 @@ function FileField({
1439
1491
  if (!localPreview || localPreview.boundUrl !== void 0) return;
1440
1492
  if (failure !== null) setLocal(null);
1441
1493
  }, [failure, localPreview, setLocal]);
1494
+ const refusal = rejected !== null && rejected.over === value?.url ? rejected : null;
1442
1495
  const lastUrl = useRef(value?.url);
1443
1496
  useEffect(() => {
1444
1497
  if (lastUrl.current === value?.url) return;
1445
1498
  lastUrl.current = value?.url;
1446
1499
  if (uploadError !== void 0) setDismissedUploadError(uploadError);
1500
+ setRejected(null);
1447
1501
  }, [value?.url, uploadError]);
1448
1502
  useEffect(() => {
1449
1503
  if (!localPreview) return;
@@ -1600,11 +1654,15 @@ function FileField({
1600
1654
  }
1601
1655
  )
1602
1656
  ),
1603
- !busy && (rejected ? /* @__PURE__ */ jsxs("p", { role: "alert", className: "text-[12px] text-destructive", children: [
1604
- /* @__PURE__ */ jsx("span", { className: "font-mono", children: rejected }),
1657
+ !busy && (refusal ? /* @__PURE__ */ jsx("p", { role: "alert", className: "text-[12px] text-destructive", children: refusal.kind === "type" ? /* @__PURE__ */ jsxs(Fragment, { children: [
1658
+ /* @__PURE__ */ jsx("span", { className: "font-mono", children: refusal.name }),
1605
1659
  " \u2014 ",
1606
1660
  s.unsupportedFileType(hint)
1607
- ] }) : failure && /* @__PURE__ */ jsx("p", { role: "alert", className: "text-[12px] text-destructive", children: failure })),
1661
+ ] }) : dropArea?.open ? (
1662
+ // Pointed to only while it takes files: a full list's area is
1663
+ // shut, and would refuse the very drop this sends there.
1664
+ s.oneFileOnlyInList
1665
+ ) : s.oneFileOnly }) : failure && /* @__PURE__ */ jsx("p", { role: "alert", className: "text-[12px] text-destructive", children: failure })),
1608
1666
  linkOpen ? /* @__PURE__ */ jsx(
1609
1667
  "input",
1610
1668
  {
@@ -2199,6 +2257,76 @@ function firstFocusable(within) {
2199
2257
  }
2200
2258
  return null;
2201
2259
  }
2260
+ var NAMED = 3;
2261
+ function ListFileDrop({ label, formats, shut, onPlace }) {
2262
+ const s = useFieldStrings();
2263
+ const hint = acceptLabel(formats);
2264
+ const accept = useMemo(() => acceptMap(formats), [formats]);
2265
+ const [notice, setNotice] = useState(null);
2266
+ const [added, setAdded] = useState({ count: 0, seq: 0 });
2267
+ const { getRootProps, getInputProps, isDragActive } = useDropzone({
2268
+ multiple: true,
2269
+ disabled: shut,
2270
+ // Filters the OS picker; `isAcceptedFile` below is the enforcement, as on a
2271
+ // single field, because a drop bypasses the picker entirely.
2272
+ accept,
2273
+ noKeyboard: true,
2274
+ onDrop: (accepted, rejections) => {
2275
+ const refused = rejections.map((rejection) => rejection.file);
2276
+ const taken = [];
2277
+ for (const file of accepted) (isAcceptedFile(formats, file) ? taken : refused).push(file);
2278
+ const fitted = taken.length > 0 ? onPlace(taken) : 0;
2279
+ setNotice({
2280
+ refused: refused.map((file) => file.name),
2281
+ leftOut: taken.slice(fitted).map((file) => file.name),
2282
+ fitted
2283
+ });
2284
+ if (fitted > 0) setAdded((prev) => ({ count: fitted, seq: prev.seq + 1 }));
2285
+ }
2286
+ });
2287
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
2288
+ /* @__PURE__ */ jsxs(
2289
+ "div",
2290
+ {
2291
+ ...getRootProps(),
2292
+ "data-list-file-drop": true,
2293
+ className: cn(
2294
+ "flex flex-col items-center justify-center gap-1 rounded-md border border-dashed px-4 py-4 text-center max-sm:min-h-11",
2295
+ "cursor-pointer select-none transition-colors",
2296
+ "focus-within:ring-2 focus-within:ring-ring focus-within:ring-offset-2 focus-within:ring-offset-background",
2297
+ isDragActive ? "border-primary/60 bg-primary/5" : "border-border bg-input hover:border-border hover:bg-muted",
2298
+ shut && "cursor-not-allowed opacity-60"
2299
+ ),
2300
+ children: [
2301
+ /* @__PURE__ */ jsx(
2302
+ "input",
2303
+ {
2304
+ ...getInputProps({ tabIndex: 0 }),
2305
+ disabled: shut,
2306
+ "aria-label": s.addFilesAria(label)
2307
+ }
2308
+ ),
2309
+ /* @__PURE__ */ jsx(Upload, { className: "h-4 w-4 text-muted-foreground", strokeWidth: 1.75 }),
2310
+ /* @__PURE__ */ jsx("p", { className: "text-[13px] text-foreground", children: isDragActive ? s.dropFilesToAdd : s.dropFilesOrBrowse }),
2311
+ hint && /* @__PURE__ */ jsx("p", { className: "font-mono text-[10.5px] text-muted-foreground", children: hint })
2312
+ ]
2313
+ }
2314
+ ),
2315
+ notice && (notice.refused.length > 0 || notice.leftOut.length > 0) && /* @__PURE__ */ jsxs("div", { role: "alert", className: "space-y-1 text-[12px] text-destructive", children: [
2316
+ notice.refused.length > 0 && /* @__PURE__ */ jsx("p", { children: s.filesRefused(
2317
+ notice.refused.slice(0, NAMED),
2318
+ Math.max(0, notice.refused.length - NAMED),
2319
+ hint
2320
+ ) }),
2321
+ notice.leftOut.length > 0 && /* @__PURE__ */ jsx("p", { children: s.filesLeftOut(
2322
+ notice.leftOut.slice(0, NAMED),
2323
+ Math.max(0, notice.leftOut.length - NAMED),
2324
+ notice.fitted
2325
+ ) })
2326
+ ] }),
2327
+ /* @__PURE__ */ jsx("p", { role: "status", className: "sr-only", children: /* @__PURE__ */ jsx("span", { children: added.count > 0 ? s.filesAdded(added.count) : "" }, added.seq) })
2328
+ ] });
2329
+ }
2202
2330
 
2203
2331
  // src/react/row-moves.ts
2204
2332
  function destinationOf(order) {
@@ -2226,6 +2354,10 @@ function ListField({ field, value, onChange, id, error, env }) {
2226
2354
  const rowsRef = useRef(null);
2227
2355
  const landedRow = useRef(null);
2228
2356
  const [staleErrors, setStaleErrors] = useState(NO_ERRORS);
2357
+ const [pending, setPending] = useState(NO_UPLOADS);
2358
+ const nextUpload = useRef(0);
2359
+ const rowMoves = env?.rowMoves;
2360
+ const arriving = useMemo(() => arrivingRows(pending, rowMoves), [pending, rowMoves]);
2229
2361
  const setItem = (index, itemValue) => {
2230
2362
  const next = items.slice();
2231
2363
  next[index] = itemValue;
@@ -2242,7 +2374,7 @@ function ListField({ field, value, onChange, id, error, env }) {
2242
2374
  onChange(items.filter((_, i) => i !== index));
2243
2375
  };
2244
2376
  const addItem = () => onChange([...items, emptyValue(field.item)]);
2245
- const busy = listIsBusy(id, env?.uploadingIds);
2377
+ const busy = listIsBusy(id, env?.uploadingIds) || arriving.size > 0;
2246
2378
  const hostErrors = env?.uploadErrors;
2247
2379
  useEffect(() => {
2248
2380
  setStaleErrors((prev) => {
@@ -2251,16 +2383,19 @@ function ListField({ field, value, onChange, id, error, env }) {
2251
2383
  return next.size === prev.size ? prev : next;
2252
2384
  });
2253
2385
  }, [hostErrors]);
2386
+ const fileItem = field.item.kind === "document" || field.item.kind === "image" ? field.item : null;
2387
+ const tracksUploads = fileItem !== null;
2254
2388
  const rowEnv = useMemo(() => {
2255
- if (!env || staleErrors.size === 0) return env;
2389
+ if (!env || staleErrors.size === 0 && !tracksUploads) return env;
2256
2390
  const drop = env.onDropFile;
2391
+ const uploadErrors = staleErrors.size === 0 ? env.uploadErrors : new Map(
2392
+ [...env.uploadErrors ?? []].filter(
2393
+ ([key, message]) => staleErrors.get(key) !== message
2394
+ )
2395
+ );
2257
2396
  return {
2258
2397
  ...env,
2259
- uploadErrors: new Map(
2260
- [...env.uploadErrors ?? []].filter(([key, message]) => staleErrors.get(key) !== message)
2261
- ),
2262
- // A new file at a position is a new attempt there, so what the host says
2263
- // about that position from now on is about the file that is there.
2398
+ uploadErrors,
2264
2399
  onDropFile: drop ? (dropId, file) => {
2265
2400
  setStaleErrors((prev) => {
2266
2401
  if (!prev.has(dropId)) return prev;
@@ -2268,14 +2403,70 @@ function ListField({ field, value, onChange, id, error, env }) {
2268
2403
  next.delete(dropId);
2269
2404
  return next;
2270
2405
  });
2271
- return drop(dropId, file);
2406
+ const upload = drop(dropId, file);
2407
+ if (tracksUploads && isThenable(upload)) {
2408
+ const token = nextUpload.current++;
2409
+ const seq = env.rowMoves?.[env.rowMoves.length - 1]?.seq ?? 0;
2410
+ setPending((prev) => new Map(prev).set(token, { id: dropId, seq }));
2411
+ const answered = () => setPending((prev) => withoutUpload(prev, token));
2412
+ void Promise.resolve(upload).then(answered, answered);
2413
+ }
2414
+ return upload;
2272
2415
  } : void 0
2273
2416
  };
2274
- }, [env, staleErrors]);
2417
+ }, [env, staleErrors, tracksUploads]);
2275
2418
  const isFull = field.maxItemCount !== void 0 && items.length >= field.maxItemCount;
2276
2419
  const fileSource = env?.fileSource;
2277
- const fileItem = field.item.kind === "document" || field.item.kind === "image" ? field.item : null;
2278
2420
  const room = roomForChosenFiles(items, field.maxItemCount);
2421
+ const dropFile = rowEnv?.onDropFile;
2422
+ const dropOffered = fileItem !== null && dropFile !== void 0;
2423
+ const isFreeRow = (index) => {
2424
+ const rowId = `${id}.${index}`;
2425
+ return !arriving.has(rowId) && !env?.uploadingIds?.has(rowId) && !rowEnv?.uploadErrors?.has(rowId);
2426
+ };
2427
+ const dropRoom = dropOffered ? roomForDroppedFiles(items, field.maxItemCount, isFreeRow) : 0;
2428
+ const dropOpen = dropOffered && !env?.disabled && dropRoom !== 0;
2429
+ const placed = useRef(/* @__PURE__ */ new Map());
2430
+ const [placements, setPlacements] = useState(0);
2431
+ const dropArea = useMemo(() => {
2432
+ if (!dropOffered) return null;
2433
+ return {
2434
+ generation: placements,
2435
+ open: dropOpen,
2436
+ takePlaced: (rowId) => {
2437
+ const file = placed.current.get(rowId);
2438
+ placed.current.delete(rowId);
2439
+ return file;
2440
+ }
2441
+ };
2442
+ }, [dropOffered, dropOpen, placements]);
2443
+ const dropRef = useRef(null);
2444
+ const landing = useRef(null);
2445
+ useEffect(() => {
2446
+ const landed = landing.current;
2447
+ if (landed === null) return;
2448
+ landing.current = null;
2449
+ const rows = rowsRef.current?.querySelectorAll(":scope > [data-list-row]");
2450
+ const target = firstFocusable(rows?.[landed]) ?? followingFocusable(dropRef.current, dropRef.current);
2451
+ target?.focus();
2452
+ });
2453
+ const placeFiles = (files) => {
2454
+ if (!dropFile) return 0;
2455
+ const placement = placeDroppedFiles(items, files.length, field.maxItemCount, isFreeRow);
2456
+ if (placement.appended > 0) onChange(placement.items);
2457
+ placement.targets.forEach((index, order) => {
2458
+ const file = files[order];
2459
+ if (!file) return;
2460
+ const rowId = `${id}.${index}`;
2461
+ placed.current.set(rowId, { file, upload: handOver(dropFile, rowId, file) });
2462
+ });
2463
+ const count = placement.targets.length;
2464
+ if (count > 0) setPlacements((generation) => generation + 1);
2465
+ if (count > 0 && count === dropRoom && dropRef.current?.contains(document.activeElement)) {
2466
+ landing.current = placement.targets[count - 1] ?? null;
2467
+ }
2468
+ return count;
2469
+ };
2279
2470
  return /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
2280
2471
  /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-baseline gap-x-2 gap-y-1", children: [
2281
2472
  /* @__PURE__ */ jsx(
@@ -2294,8 +2485,8 @@ function ListField({ field, value, onChange, id, error, env }) {
2294
2485
  ] }),
2295
2486
  field.description && /* @__PURE__ */ jsx("p", { className: "text-[12px] leading-relaxed text-muted-foreground", children: field.description }),
2296
2487
  /* @__PURE__ */ jsxs("div", { ref: rowsRef, className: "space-y-2", children: [
2297
- items.length === 0 && /* @__PURE__ */ jsx("p", { className: "rounded-md border border-dashed border-border bg-input/50 px-3 py-4 text-center text-[12px] text-muted-foreground", children: s.noItemsYet }),
2298
- items.map((item, index) => /* @__PURE__ */ jsxs(
2488
+ items.length === 0 && !dropOffered && /* @__PURE__ */ jsx("p", { className: "rounded-md border border-dashed border-border bg-input/50 px-3 py-4 text-center text-[12px] text-muted-foreground", children: s.noItemsYet }),
2489
+ /* @__PURE__ */ jsx(ListDropAreaContext.Provider, { value: dropArea, children: items.map((item, index) => /* @__PURE__ */ jsxs(
2299
2490
  "div",
2300
2491
  {
2301
2492
  "data-list-row": true,
@@ -2334,7 +2525,16 @@ function ListField({ field, value, onChange, id, error, env }) {
2334
2525
  ]
2335
2526
  },
2336
2527
  rowKeys[index] ?? index
2337
- )),
2528
+ )) }),
2529
+ dropOffered && fileItem && /* @__PURE__ */ jsx("div", { ref: dropRef, children: /* @__PURE__ */ jsx(
2530
+ ListFileDrop,
2531
+ {
2532
+ label: fieldLabel(field.title, field.name, presentation),
2533
+ formats: fileItem.formats,
2534
+ shut: !dropOpen,
2535
+ onPlace: placeFiles
2536
+ }
2537
+ ) }),
2338
2538
  /* @__PURE__ */ jsxs(
2339
2539
  "button",
2340
2540
  {
@@ -2411,6 +2611,42 @@ function listIsBusy(id, uploadingIds) {
2411
2611
  return false;
2412
2612
  }
2413
2613
  var NO_ERRORS = /* @__PURE__ */ new Map();
2614
+ var NO_IDS = /* @__PURE__ */ new Set();
2615
+ var NO_UPLOADS = /* @__PURE__ */ new Map();
2616
+ function withoutUpload(uploads, token) {
2617
+ if (!uploads.has(token)) return uploads;
2618
+ const next = new Map(uploads);
2619
+ next.delete(token);
2620
+ return next;
2621
+ }
2622
+ function arrivingRows(uploads, moves) {
2623
+ if (uploads.size === 0) return NO_IDS;
2624
+ const rows = /* @__PURE__ */ new Set();
2625
+ for (const upload of uploads.values()) {
2626
+ let at = upload.id;
2627
+ for (const move of moves ?? []) {
2628
+ if (at === void 0) break;
2629
+ if (move.seq > upload.seq) at = movedId(at, move.listId, destinationOf(move.order));
2630
+ }
2631
+ if (at !== void 0) rows.add(at);
2632
+ }
2633
+ return rows;
2634
+ }
2635
+ function handOver(drop, rowId, file) {
2636
+ try {
2637
+ return drop(rowId, file);
2638
+ } catch (error) {
2639
+ reportHostError(error);
2640
+ const failed = Promise.reject(error);
2641
+ failed.catch(() => {
2642
+ });
2643
+ return failed;
2644
+ }
2645
+ }
2646
+ function reportHostError(error) {
2647
+ if (typeof globalThis.reportError === "function") globalThis.reportError(error);
2648
+ else console.error(error);
2649
+ }
2414
2650
  function withRenumberedErrors(stale, listId, removed, hostErrors) {
2415
2651
  if (!hostErrors?.size) return stale;
2416
2652
  const prefix = `${listId}.`;
@@ -3132,12 +3368,12 @@ function withoutId(ids, id) {
3132
3368
  next.delete(id);
3133
3369
  return next;
3134
3370
  }
3135
- var NO_UPLOADS = /* @__PURE__ */ new Map();
3371
+ var NO_UPLOADS2 = /* @__PURE__ */ new Map();
3136
3372
  var NO_MOVES = [];
3137
3373
  var NOTHING_HELD = {
3138
3374
  edited: null,
3139
3375
  failed: EMPTY_IDS,
3140
- uploads: NO_UPLOADS,
3376
+ uploads: NO_UPLOADS2,
3141
3377
  moves: NO_MOVES
3142
3378
  };
3143
3379
  function withoutEntry(entries, id) {
@@ -3317,7 +3553,7 @@ function useMethodForm({
3317
3553
  );
3318
3554
  const reset = useCallback(
3319
3555
  (next) => {
3320
- commit({ edited: next ?? null, failed: EMPTY_IDS, uploads: NO_UPLOADS, moves: NO_MOVES });
3556
+ commit({ edited: next ?? null, failed: EMPTY_IDS, uploads: NO_UPLOADS2, moves: NO_MOVES });
3321
3557
  setGiven(next ?? null);
3322
3558
  setAttempted(false);
3323
3559
  setResets((count) => count + 1);
@@ -6646,5 +6882,5 @@ function pdfFileName(field, value, baseName) {
6646
6882
  }
6647
6883
 
6648
6884
  export { BooleanField, ConceptPill, DEFAULT_FIELD_STRINGS, DateField, DocumentField, EN_FIELD_STRINGS, EnumField, FIELD_STRINGS_BY_LOCALE, FR_FIELD_STRINGS, FieldDomIdProvider, FieldPresentationProvider, FieldRenderer, FieldShell, FieldStringsProvider, FilePicker, FilePickerEntry, FillWithExample, HtmlPreview, ImageField, JsonView, ListField, Markdown, MethodForm, NumberField, ObjectField, OptionalToggle, PdfDownloadButton, ProseField, ResultEnvProvider, ResultField, ResultRoot, SUPPORTED_LOCALES, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, StuffViewer, Switch, TextField, ToggleGroup, ToggleGroupItem, UnknownField, checkedAccentClass, cn, fieldControlClass, fieldStringsFor, focusOutlineClass, humanizeFieldName, linkTextClass, renderHtmlPdf, resolveHtmlStorageUrls, saveInBrowser, selectedBorderClass, selectedFillClass, selectedFillStrongClass, useFieldDomId, useFieldPresentation, useFieldStrings, useMethodForm, useProseImages, useResolveShareUrl, useResolveUrl, useResolvedUrl };
6649
- //# sourceMappingURL=chunk-W5MYXDLO.js.map
6650
- //# sourceMappingURL=chunk-W5MYXDLO.js.map
6885
+ //# sourceMappingURL=chunk-UGKFWX7L.js.map
6886
+ //# sourceMappingURL=chunk-UGKFWX7L.js.map