@pipelex/mthds-form 0.20.0 → 0.22.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.
@@ -2,7 +2,7 @@ import { planFileSave, planStuffSave, splitFileName, typedFileStem } from './chu
2
2
  import { seedInputs, closeOptionalStructures, openOptionalStructures } from './chunk-VH5CDQDR.js';
3
3
  import { sourceFileValue, roomForChosenFiles, addChosenFiles, chosenFilesAdded } from './chunk-EQJBM3NS.js';
4
4
  import { readStuffFile } from './chunk-RRTI7AIR.js';
5
- import { setValueAtPath, apiInputsFromRunValues, sameRunValues, runValuesFromInputs } from './chunk-ZX52CQJ4.js';
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';
7
7
  import { buildRunFields } from './chunk-ZB4RYUKV.js';
8
8
  import { conceptCategory } from './chunk-FABY3DEY.js';
@@ -566,11 +566,20 @@ function FieldShell({
566
566
  ] });
567
567
  }
568
568
 
569
+ // src/react/brand-ink.ts
570
+ var brandInkClass = "text-[color:var(--brand,var(--primary,hsl(240_5.9%_10%)))]";
571
+ var brandFillInkClass = "text-[color:var(--brand-muted-foreground,var(--brand,var(--primary,hsl(240_5.9%_10%))))]";
572
+ var brandFillClass = "bg-[color:var(--brand-muted,color-mix(in_oklab,var(--primary,hsl(240_5.9%_10%))_10%,transparent))]";
573
+ var brandFillSelectedClass = "data-[state=on]:bg-[color:var(--brand-muted,color-mix(in_oklab,var(--primary,hsl(240_5.9%_10%))_10%,transparent))]";
574
+ var brandFillStrongClass = "bg-[color:var(--brand-muted,color-mix(in_oklab,var(--primary,hsl(240_5.9%_10%))_15%,transparent))]";
575
+ var brandFocusClass = "focus-visible:border-[color:var(--brand,var(--primary,hsl(240_5.9%_10%)))] focus-visible:ring-[color:color-mix(in_oklab,var(--brand,var(--primary,hsl(240_5.9%_10%)))_40%,transparent)]";
576
+
569
577
  // src/react/field-styles.ts
570
578
  var fieldControlClass = cn(
571
579
  "w-full rounded-md border border-border bg-input text-sm text-foreground",
572
580
  "placeholder:text-muted-foreground/70 transition-colors",
573
- "focus-visible:outline-hidden focus-visible:border-primary focus-visible:ring-1 focus-visible:ring-primary/40",
581
+ "focus-visible:outline-hidden focus-visible:ring-1",
582
+ brandFocusClass,
574
583
  "disabled:cursor-not-allowed disabled:opacity-50",
575
584
  "aria-invalid:border-destructive aria-invalid:ring-destructive/30"
576
585
  );
@@ -1237,6 +1246,20 @@ function isReadableLink(url) {
1237
1246
  function dataUrlMime(url) {
1238
1247
  return DATA_URL_MIME_RE.exec(url)?.[1]?.toLowerCase();
1239
1248
  }
1249
+ function isThenable(answer) {
1250
+ return (typeof answer === "object" || typeof answer === "function") && answer !== null && typeof answer.then === "function";
1251
+ }
1252
+ function storedUrlOf(stored) {
1253
+ if (typeof stored === "string") return stored === "" ? void 0 : stored;
1254
+ if (stored === null || typeof stored !== "object") return void 0;
1255
+ const url = stored.url;
1256
+ return typeof url === "string" && url !== "" ? url : void 0;
1257
+ }
1258
+ function describesValue(local, url) {
1259
+ if (local.boundUrl !== void 0) return local.boundUrl === url;
1260
+ if (local.storedUrl !== void 0) return local.storedUrl === url;
1261
+ return !local.awaitingUpload;
1262
+ }
1240
1263
  function DocumentField(props) {
1241
1264
  return /* @__PURE__ */ jsx(FileField, { ...props, category: "document" });
1242
1265
  }
@@ -1265,6 +1288,9 @@ function FileField({
1265
1288
  const [typedUrl, setTypedUrl] = useState(null);
1266
1289
  const [previewOpen, setPreviewOpen] = useState(false);
1267
1290
  const [localPreview, setLocalPreview] = useState(null);
1291
+ const urlInFlight = useRef(void 0);
1292
+ const flightSeen = useRef(false);
1293
+ const localGeneration = useRef(0);
1268
1294
  const [resolved, setResolved] = useState(null);
1269
1295
  const localUrlRef = useRef(null);
1270
1296
  const [pickerOpen, setPickerOpen] = useState(false);
@@ -1273,6 +1299,7 @@ function FileField({
1273
1299
  const setLocal = useCallback((next) => {
1274
1300
  if (localUrlRef.current) URL.revokeObjectURL(localUrlRef.current);
1275
1301
  localUrlRef.current = next?.objectUrl ?? null;
1302
+ localGeneration.current += 1;
1276
1303
  setLocalPreview(next);
1277
1304
  }, []);
1278
1305
  useEffect(() => () => setLocal(null), [setLocal]);
@@ -1299,10 +1326,27 @@ function FileField({
1299
1326
  }
1300
1327
  setRejected(null);
1301
1328
  setDismissedUploadError(null);
1302
- setLocal({ objectUrl: URL.createObjectURL(file), type: file.type });
1303
- onDropFile(file);
1329
+ const upload = onDropFile(file);
1330
+ const awaitingUpload = isThenable(upload);
1331
+ setLocal({ objectUrl: URL.createObjectURL(file), type: file.type, awaitingUpload });
1332
+ urlInFlight.current = value?.url;
1333
+ flightSeen.current = false;
1334
+ if (!awaitingUpload) return;
1335
+ const generation = localGeneration.current;
1336
+ Promise.resolve(upload).then(
1337
+ (stored) => {
1338
+ if (localGeneration.current !== generation) return;
1339
+ const storedUrl = storedUrlOf(stored);
1340
+ setLocalPreview(
1341
+ (current) => current ? { ...current, awaitingUpload: false, storedUrl } : current
1342
+ );
1343
+ },
1344
+ () => {
1345
+ if (localGeneration.current === generation) setLocal(null);
1346
+ }
1347
+ );
1304
1348
  },
1305
- [formats, setLocal, onDropFile]
1349
+ [formats, setLocal, onDropFile, value?.url]
1306
1350
  );
1307
1351
  const busy = disabled || uploading;
1308
1352
  const {
@@ -1358,14 +1402,11 @@ function FileField({
1358
1402
  },
1359
1403
  [setLocal, onChange, dismissUploadError]
1360
1404
  );
1361
- const localIsCurrent = !!localPreview && (localPreview.boundUrl === void 0 || localPreview.boundUrl === value?.url);
1362
- const wasUploading = useRef(!!uploading);
1405
+ const localIsCurrent = !!localPreview && describesValue(localPreview, value?.url);
1363
1406
  useEffect(() => {
1364
- const ended = wasUploading.current && !uploading;
1365
- wasUploading.current = !!uploading;
1366
1407
  if (!localPreview || localPreview.boundUrl !== void 0) return;
1367
- if (failure !== null || ended && !value?.url) setLocal(null);
1368
- }, [uploading, failure, localPreview, value?.url, setLocal]);
1408
+ if (failure !== null) setLocal(null);
1409
+ }, [failure, localPreview, setLocal]);
1369
1410
  const lastUrl = useRef(value?.url);
1370
1411
  useEffect(() => {
1371
1412
  if (lastUrl.current === value?.url) return;
@@ -1374,12 +1415,30 @@ function FileField({
1374
1415
  }, [value?.url, uploadError]);
1375
1416
  useEffect(() => {
1376
1417
  if (!localPreview) return;
1377
- if (localPreview.boundUrl === void 0) {
1378
- if (uploading || !value?.url) return;
1379
- setLocalPreview({ ...localPreview, boundUrl: value.url });
1418
+ if (localPreview.boundUrl !== void 0) {
1419
+ if (value?.url !== localPreview.boundUrl) setLocal(null);
1380
1420
  return;
1381
1421
  }
1382
- if (value?.url !== localPreview.boundUrl) setLocal(null);
1422
+ if (uploading) {
1423
+ urlInFlight.current = value?.url;
1424
+ flightSeen.current = true;
1425
+ }
1426
+ if (localPreview.storedUrl !== void 0) {
1427
+ if (value?.url === localPreview.storedUrl) {
1428
+ setLocalPreview({ ...localPreview, boundUrl: localPreview.storedUrl });
1429
+ }
1430
+ return;
1431
+ }
1432
+ if (uploading || localPreview.awaitingUpload) return;
1433
+ if (!value?.url) {
1434
+ if (flightSeen.current) setLocal(null);
1435
+ return;
1436
+ }
1437
+ if (value.url === urlInFlight.current) {
1438
+ setLocal(null);
1439
+ return;
1440
+ }
1441
+ setLocalPreview({ ...localPreview, boundUrl: value.url });
1383
1442
  }, [localPreview, value?.url, uploading, setLocal]);
1384
1443
  const hasFile = !!value?.url;
1385
1444
  const localType = localIsCurrent ? localPreview?.type : void 0;
@@ -1814,9 +1873,19 @@ function EnumField({ field, value, onChange, id, error, disabled }) {
1814
1873
  {
1815
1874
  value: option,
1816
1875
  variant: "outline",
1817
- className: "h-9 min-w-0 gap-1.5 border-border bg-input px-3 text-[13px] text-muted-foreground hover:bg-muted hover:text-foreground data-[state=on]:border-primary/40 data-[state=on]:bg-primary/10 data-[state=on]:font-medium data-[state=on]:text-foreground",
1876
+ className: cn(
1877
+ "h-9 min-w-0 gap-1.5 border-border bg-input px-3 text-[13px] text-muted-foreground hover:bg-muted hover:text-foreground data-[state=on]:border-primary/40 data-[state=on]:font-medium data-[state=on]:text-foreground",
1878
+ brandFillSelectedClass
1879
+ ),
1818
1880
  children: [
1819
- value === option && /* @__PURE__ */ jsx(Check, { className: "size-3.5 text-primary", strokeWidth: 2.5, "aria-hidden": "true" }),
1881
+ value === option && /* @__PURE__ */ jsx(
1882
+ Check,
1883
+ {
1884
+ className: cn("size-3.5", brandFillInkClass),
1885
+ strokeWidth: 2.5,
1886
+ "aria-hidden": "true"
1887
+ }
1888
+ ),
1820
1889
  labelOf(option)
1821
1890
  ]
1822
1891
  },
@@ -2083,12 +2152,28 @@ function firstFocusable(within) {
2083
2152
  }
2084
2153
  return null;
2085
2154
  }
2155
+
2156
+ // src/react/row-moves.ts
2157
+ function destinationOf(order) {
2158
+ return new Map(order.map((source, index) => [source, index]));
2159
+ }
2160
+ function movedId(id, listId, destination) {
2161
+ const prefix = `${listId}.`;
2162
+ if (!id.startsWith(prefix)) return id;
2163
+ const rest = id.slice(prefix.length);
2164
+ const dot = rest.indexOf(".");
2165
+ const row = dot === -1 ? rest : rest.slice(0, dot);
2166
+ if (!/^\d+$/.test(row)) return id;
2167
+ const index = destination.get(Number(row));
2168
+ if (index === void 0) return void 0;
2169
+ return `${prefix}${index}${dot === -1 ? "" : rest.slice(dot)}`;
2170
+ }
2086
2171
  function ListField({ field, value, onChange, id, error, env }) {
2087
2172
  const s = useFieldStrings();
2088
2173
  const presentation = useFieldPresentation();
2089
2174
  const isApp = presentation === "app";
2090
2175
  const items = value ?? [];
2091
- const [rowKeys, setRowKeys] = useRowKeys(items.length);
2176
+ const [rowKeys, setRowKeys] = useRowKeys(items.length, id, env?.rowMoves);
2092
2177
  const [pickerOpen, setPickerOpen] = useState(false);
2093
2178
  const pickerEntryRef = useRef(null);
2094
2179
  const rowsRef = useRef(null);
@@ -2100,7 +2185,12 @@ function ListField({ field, value, onChange, id, error, env }) {
2100
2185
  onChange(next);
2101
2186
  };
2102
2187
  const removeItem = (index) => {
2103
- setRowKeys((prev) => prev.filter((_, i) => i !== index));
2188
+ if (!env?.rowMoves) setRowKeys((prev) => prev.filter((_, i) => i !== index));
2189
+ const hostRemoves = env?.onRemoveItem;
2190
+ if (hostRemoves) {
2191
+ hostRemoves(id, index);
2192
+ return;
2193
+ }
2104
2194
  setStaleErrors((prev) => withRenumberedErrors(prev, id, index, env?.uploadErrors));
2105
2195
  onChange(items.filter((_, i) => i !== index));
2106
2196
  };
@@ -2131,7 +2221,7 @@ function ListField({ field, value, onChange, id, error, env }) {
2131
2221
  next.delete(dropId);
2132
2222
  return next;
2133
2223
  });
2134
- drop(dropId, file);
2224
+ return drop(dropId, file);
2135
2225
  } : void 0
2136
2226
  };
2137
2227
  }, [env, staleErrors]);
@@ -2278,24 +2368,49 @@ function withRenumberedErrors(stale, listId, removed, hostErrors) {
2278
2368
  }
2279
2369
  return next.size === stale.size && [...next].every(([k, m]) => stale.get(k) === m) ? stale : next;
2280
2370
  }
2281
- function useRowKeys(length) {
2371
+ function useRowKeys(length, id, moves) {
2372
+ const latest = moves?.[moves.length - 1]?.seq ?? 0;
2282
2373
  const [state, setState] = useState(() => ({
2283
2374
  keys: Array.from({ length }, (_, i) => i),
2284
- next: length
2375
+ next: length,
2376
+ seq: latest,
2377
+ id
2285
2378
  }));
2286
- if (state.keys.length !== length) {
2287
- setState((prev) => {
2288
- if (length <= prev.keys.length) return { keys: prev.keys.slice(0, length), next: prev.next };
2289
- const added = length - prev.keys.length;
2290
- return {
2291
- keys: [...prev.keys, ...Array.from({ length: added }, (_, i) => prev.next + i)],
2292
- next: prev.next + added
2293
- };
2294
- });
2379
+ if (state.seq !== latest || state.id !== id || state.keys.length !== length) {
2380
+ setState((prev) => withLength(replayed(prev, moves, latest, id), length));
2295
2381
  }
2296
- const update = (fn) => setState((prev) => ({ keys: fn(prev.keys), next: prev.next }));
2382
+ const update = (fn) => setState((prev) => ({ ...prev, keys: fn(prev.keys) }));
2297
2383
  return [state.keys, update];
2298
2384
  }
2385
+ function replayed(state, moves, latest, id) {
2386
+ let { keys, next } = state;
2387
+ let tracked = state.id;
2388
+ for (const move of moves ?? []) {
2389
+ if (move.seq <= state.seq || tracked === void 0) continue;
2390
+ if (move.listId === tracked) {
2391
+ const before = keys;
2392
+ keys = [];
2393
+ for (const source of move.order) {
2394
+ const key = before[source];
2395
+ if (key === void 0) keys.push(next++);
2396
+ else keys.push(key);
2397
+ }
2398
+ } else {
2399
+ tracked = movedId(tracked, move.listId, destinationOf(move.order));
2400
+ }
2401
+ }
2402
+ return { keys, next, seq: latest, id };
2403
+ }
2404
+ function withLength(state, length) {
2405
+ if (state.keys.length === length) return state;
2406
+ if (length < state.keys.length) return { ...state, keys: state.keys.slice(0, length) };
2407
+ const added = length - state.keys.length;
2408
+ return {
2409
+ ...state,
2410
+ keys: [...state.keys, ...Array.from({ length: added }, (_, i) => state.next + i)],
2411
+ next: state.next + added
2412
+ };
2413
+ }
2299
2414
  function emptyValue(field) {
2300
2415
  switch (field.kind) {
2301
2416
  case "boolean":
@@ -2948,6 +3063,110 @@ function withoutId(ids, id) {
2948
3063
  next.delete(id);
2949
3064
  return next;
2950
3065
  }
3066
+ var NO_UPLOADS = /* @__PURE__ */ new Map();
3067
+ var NO_MOVES = [];
3068
+ var NOTHING_HELD = {
3069
+ edited: null,
3070
+ failed: EMPTY_IDS,
3071
+ uploads: NO_UPLOADS,
3072
+ moves: NO_MOVES
3073
+ };
3074
+ function withoutEntry(entries, id) {
3075
+ if (!entries.has(id)) return entries;
3076
+ const next = new Map(entries);
3077
+ next.delete(id);
3078
+ return next;
3079
+ }
3080
+ function fileUrlAt(values, id) {
3081
+ const node = valueAt(values, id);
3082
+ if (node === null || typeof node !== "object") return void 0;
3083
+ const url = ownProp(node, "url");
3084
+ return typeof url === "string" && url !== "" ? url : void 0;
3085
+ }
3086
+ function valueAt(values, id) {
3087
+ let node = values;
3088
+ for (const key of id.split(".")) {
3089
+ if (node === null || typeof node !== "object") return void 0;
3090
+ node = ownProp(node, key);
3091
+ }
3092
+ return node;
3093
+ }
3094
+ function written(held, initial, update) {
3095
+ const before = held.edited ?? initial;
3096
+ const after = update(before);
3097
+ if (after === held.edited) return held;
3098
+ const replaced = [...held.failed].filter((id) => fileUrlAt(before, id) !== fileUrlAt(after, id));
3099
+ if (replaced.length === 0) return { ...held, edited: after };
3100
+ const failed = new Set(held.failed);
3101
+ for (const id of replaced) failed.delete(id);
3102
+ return { ...held, edited: after, failed };
3103
+ }
3104
+ function uploadDropped(held, initial, id, token) {
3105
+ const uploads = new Map(held.uploads);
3106
+ uploads.set(id, { token, over: fileUrlAt(held.edited ?? initial, id) });
3107
+ return { ...held, failed: withoutId(held.failed, id), uploads };
3108
+ }
3109
+ function placeOf(held, token) {
3110
+ for (const entry of held.uploads) if (entry[1].token === token) return entry;
3111
+ return void 0;
3112
+ }
3113
+ function uploadFailed(held, initial, token) {
3114
+ const place = placeOf(held, token);
3115
+ if (!place) return held;
3116
+ const [id, upload] = place;
3117
+ const stillThere = fileUrlAt(held.edited ?? initial, id) === upload.over;
3118
+ return {
3119
+ ...held,
3120
+ failed: stillThere ? withId(held.failed, id) : held.failed,
3121
+ uploads: withoutEntry(held.uploads, id)
3122
+ };
3123
+ }
3124
+ function uploadLanded(held, initial, token, value) {
3125
+ const place = placeOf(held, token);
3126
+ if (!place) return held;
3127
+ const [id] = place;
3128
+ const next = written(held, initial, (before) => setValueAtPath(before, id.split("."), value));
3129
+ return { ...next, uploads: withoutEntry(next.uploads, id) };
3130
+ }
3131
+ function rowsMoved(held, initial, listId, order, seq) {
3132
+ const before = held.edited ?? initial;
3133
+ const rows = valueAt(before, listId);
3134
+ if (!Array.isArray(rows)) return held;
3135
+ const sources = order(rows);
3136
+ if (!sources) return held;
3137
+ const after = setValueAtPath(
3138
+ before,
3139
+ listId.split("."),
3140
+ sources.map((source) => rows[source])
3141
+ );
3142
+ const destination = destinationOf(sources);
3143
+ const failed = /* @__PURE__ */ new Set();
3144
+ for (const id of held.failed) {
3145
+ const moved = movedId(id, listId, destination);
3146
+ if (moved !== void 0) failed.add(moved);
3147
+ }
3148
+ const uploads = /* @__PURE__ */ new Map();
3149
+ for (const [id, upload] of held.uploads) {
3150
+ const moved = movedId(id, listId, destination);
3151
+ if (moved !== void 0) uploads.set(moved, upload);
3152
+ }
3153
+ const moves = [...held.moves, { seq, listId, order: sources }];
3154
+ return { edited: after, failed, uploads, moves };
3155
+ }
3156
+ function isRow(rows, index) {
3157
+ return Number.isInteger(index) && index >= 0 && index < rows.length;
3158
+ }
3159
+ function withoutRow(index) {
3160
+ return (rows) => isRow(rows, index) ? rows.map((_, i) => i).filter((i) => i !== index) : null;
3161
+ }
3162
+ function rowMovedTo(from, to) {
3163
+ return (rows) => {
3164
+ if (!isRow(rows, from) || !isRow(rows, to) || from === to) return null;
3165
+ const order = rows.map((_, i) => i).filter((i) => i !== from);
3166
+ order.splice(to, 0, from);
3167
+ return order;
3168
+ };
3169
+ }
2951
3170
  function orderFields(fields, order) {
2952
3171
  if (!order || order.length === 0) return fields;
2953
3172
  const rank = (field) => {
@@ -2976,39 +3195,71 @@ function useMethodForm({
2976
3195
  if (typeof initialValues === "function") return initialValues(seedInputs(fields));
2977
3196
  return initialValues ?? seedInputs(fields);
2978
3197
  }, [initialValues, fields]);
2979
- const [edited, setEdited] = useState(null);
3198
+ const store = useRef(NOTHING_HELD);
3199
+ const [held, setHeld] = useState(NOTHING_HELD);
3200
+ const commit = useCallback((next) => {
3201
+ if (next === store.current) return;
3202
+ store.current = next;
3203
+ setHeld(next);
3204
+ }, []);
3205
+ const edited = held.edited;
3206
+ const failedUploadIds = held.failed;
3207
+ const uploadingIds = useMemo(
3208
+ () => held.uploads.size === 0 ? EMPTY_IDS : new Set(held.uploads.keys()),
3209
+ [held.uploads]
3210
+ );
2980
3211
  const [given, setGiven] = useState(null);
2981
- const [uploadingIds, setUploadingIds] = useState(EMPTY_IDS);
2982
- const [failedUploadIds, setFailedUploadIds] = useState(EMPTY_IDS);
2983
3212
  const [attempted, setAttempted] = useState(false);
2984
3213
  const [resets, setResets] = useState(0);
2985
- const pendingUploads = useRef(/* @__PURE__ */ new Map());
2986
3214
  const uploadCounter = useRef(0);
3215
+ const moveCounter = useRef(0);
2987
3216
  const values = edited ?? initial;
2988
3217
  const setValues = useCallback(
2989
3218
  (next) => {
2990
3219
  setGiven((previous) => previous ?? initial);
2991
- setEdited((previous) => typeof next === "function" ? next(previous ?? initial) : next);
3220
+ commit(
3221
+ written(
3222
+ store.current,
3223
+ initial,
3224
+ (before) => typeof next === "function" ? next(before) : next
3225
+ )
3226
+ );
2992
3227
  },
2993
- [initial]
3228
+ [initial, commit]
3229
+ );
3230
+ const moveRows = useCallback(
3231
+ (listId, order2) => {
3232
+ const seq = moveCounter.current + 1;
3233
+ const next = rowsMoved(store.current, initial, listId, order2, seq);
3234
+ if (next === store.current) return;
3235
+ moveCounter.current = seq;
3236
+ setGiven((previous) => previous ?? initial);
3237
+ commit(next);
3238
+ },
3239
+ [initial, commit]
3240
+ );
3241
+ const removeItem = useCallback(
3242
+ (listId, index) => moveRows(listId, withoutRow(index)),
3243
+ [moveRows]
3244
+ );
3245
+ const moveItem = useCallback(
3246
+ (listId, from, to) => moveRows(listId, rowMovedTo(from, to)),
3247
+ [moveRows]
3248
+ );
3249
+ const reset = useCallback(
3250
+ (next) => {
3251
+ commit({ edited: next ?? null, failed: EMPTY_IDS, uploads: NO_UPLOADS, moves: NO_MOVES });
3252
+ setGiven(next ?? null);
3253
+ setAttempted(false);
3254
+ setResets((count) => count + 1);
3255
+ },
3256
+ [commit]
2994
3257
  );
2995
- const reset = useCallback((next) => {
2996
- pendingUploads.current.clear();
2997
- setEdited(next ?? null);
2998
- setGiven(next ?? null);
2999
- setUploadingIds(EMPTY_IDS);
3000
- setFailedUploadIds(EMPTY_IDS);
3001
- setAttempted(false);
3002
- setResets((count) => count + 1);
3003
- }, []);
3004
3258
  const dropFile = useMemo(() => {
3005
3259
  if (!uploadFile) return void 0;
3006
3260
  return (id, file) => {
3007
3261
  const token = ++uploadCounter.current;
3008
- pendingUploads.current.set(id, token);
3009
- const current = () => pendingUploads.current.get(id) === token;
3010
- setUploadingIds((previous) => withId(previous, id));
3011
- setFailedUploadIds((previous) => withoutId(previous, id));
3262
+ commit(uploadDropped(store.current, initial, id, token));
3012
3263
  const path = id.split(".");
3013
3264
  let upload;
3014
3265
  try {
@@ -3016,23 +3267,21 @@ function useMethodForm({
3016
3267
  } catch (error) {
3017
3268
  upload = Promise.reject(error);
3018
3269
  }
3019
- upload.then(
3020
- (stored) => {
3021
- if (!current()) return;
3022
- const value = typeof stored === "string" ? { url: stored, filename: file.name } : { url: stored.url, filename: stored.filename ?? file.name };
3023
- setGiven((previous) => previous ?? initial);
3024
- setEdited((previous) => setValueAtPath(previous ?? initial, path, value));
3025
- },
3026
- () => {
3027
- if (current()) setFailedUploadIds((previous) => withId(previous, id));
3270
+ const landed = upload.then((stored) => {
3271
+ if (!placeOf(store.current, token)) {
3272
+ throw new Error(
3273
+ `A newer file, a reset or the removal of its row replaced the upload dropped at "${id}".`
3274
+ );
3028
3275
  }
3029
- ).finally(() => {
3030
- if (!current()) return;
3031
- pendingUploads.current.delete(id);
3032
- setUploadingIds((previous) => withoutId(previous, id));
3276
+ const value = typeof stored === "string" ? { url: stored, filename: file.name } : { url: stored.url, filename: stored.filename ?? file.name };
3277
+ setGiven((previous) => previous ?? initial);
3278
+ commit(uploadLanded(store.current, initial, token, value));
3279
+ return value;
3033
3280
  });
3281
+ landed.then(void 0, () => commit(uploadFailed(store.current, initial, token)));
3282
+ return landed;
3034
3283
  };
3035
- }, [uploadFile, initial]);
3284
+ }, [uploadFile, initial, commit]);
3036
3285
  const readiness = useMemo(() => computeReadiness(fields, values), [fields, values]);
3037
3286
  const inputs = useMemo(
3038
3287
  () => apiInputsFromRunValues(values, fields, contract.inputs),
@@ -3058,6 +3307,9 @@ function useMethodForm({
3058
3307
  attempted,
3059
3308
  touched,
3060
3309
  setValues,
3310
+ removeItem,
3311
+ moveItem,
3312
+ rowMoves: held.moves,
3061
3313
  reset,
3062
3314
  attempt,
3063
3315
  resets,
@@ -3093,6 +3345,8 @@ function MethodFormFields({
3093
3345
  fields,
3094
3346
  values,
3095
3347
  setValues,
3348
+ removeItem,
3349
+ rowMoves,
3096
3350
  reset,
3097
3351
  attempted,
3098
3352
  touched,
@@ -3113,11 +3367,27 @@ function MethodFormFields({
3113
3367
  onDropFile: dropFile,
3114
3368
  uploadingIds,
3115
3369
  uploadErrors,
3370
+ // A list's remove button goes through the form, which carries a failed
3371
+ // upload, or one in flight, along with the rows it renumbers, and logs
3372
+ // the move for every list to move its rows' identities the same way,
3373
+ // whether the button or the host moved them.
3374
+ onRemoveItem: removeItem,
3375
+ rowMoves,
3116
3376
  allowUrl,
3117
3377
  resolveUrl,
3118
3378
  fileSource
3119
3379
  }),
3120
- [disabled, dropFile, uploadingIds, uploadErrors, allowUrl, resolveUrl, fileSource]
3380
+ [
3381
+ disabled,
3382
+ dropFile,
3383
+ uploadingIds,
3384
+ uploadErrors,
3385
+ removeItem,
3386
+ rowMoves,
3387
+ allowUrl,
3388
+ resolveUrl,
3389
+ fileSource
3390
+ ]
3121
3391
  );
3122
3392
  const folds = (field) => {
3123
3393
  const value = ownProp(values, field.name);
@@ -6197,6 +6467,6 @@ function pdfFileName(field, value, baseName) {
6197
6467
  return name === void 0 ? "" : name.replace(/\.[^.]+$/, "");
6198
6468
  }
6199
6469
 
6200
- 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, cn, fieldControlClass, fieldStringsFor, humanizeFieldName, renderHtmlPdf, resolveHtmlStorageUrls, saveInBrowser, useFieldDomId, useFieldPresentation, useFieldStrings, useMethodForm, useProseImages, useResolveShareUrl, useResolveUrl, useResolvedUrl };
6201
- //# sourceMappingURL=chunk-CSLDIWB6.js.map
6202
- //# sourceMappingURL=chunk-CSLDIWB6.js.map
6470
+ 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, brandFillClass, brandFillInkClass, brandFillStrongClass, brandInkClass, cn, fieldControlClass, fieldStringsFor, humanizeFieldName, renderHtmlPdf, resolveHtmlStorageUrls, saveInBrowser, useFieldDomId, useFieldPresentation, useFieldStrings, useMethodForm, useProseImages, useResolveShareUrl, useResolveUrl, useResolvedUrl };
6471
+ //# sourceMappingURL=chunk-5IZWP2X4.js.map
6472
+ //# sourceMappingURL=chunk-5IZWP2X4.js.map