@dmitryvim/form-builder 0.3.0 → 0.3.1

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.
@@ -443,6 +443,36 @@ function ensureThemingHooks(doc) {
443
443
  color: var(--fb-error-color);
444
444
  background-color: var(--fb-background-hover-color);
445
445
  }
446
+ /* Prefill-suggestion pills rendered by createPrefillHints. Outline pill at rest,
447
+ soft-fill on hover, solid-fill when selected. All colors flow from the active
448
+ theme \u2014 consumers don't need to ship their own CSS. */
449
+ .fb-prefill-hint {
450
+ padding: 0.25rem 0.625rem;
451
+ border: var(--fb-border-width) solid var(--fb-primary-color);
452
+ border-radius: 9999px;
453
+ background: var(--fb-background-color);
454
+ color: var(--fb-primary-color);
455
+ font-size: var(--fb-font-size-small);
456
+ font-weight: var(--fb-font-weight-medium);
457
+ font-family: var(--fb-font-family);
458
+ cursor: pointer;
459
+ transition: background-color var(--fb-transition-duration), border-color var(--fb-transition-duration), color var(--fb-transition-duration);
460
+ }
461
+ .fb-prefill-hint:hover {
462
+ background: var(--fb-primary-soft-color);
463
+ border-color: var(--fb-primary-hover-color);
464
+ color: var(--fb-primary-hover-color);
465
+ }
466
+ .fb-prefill-hint:focus-visible {
467
+ outline: var(--fb-focus-ring-width) solid var(--fb-focus-ring-color);
468
+ outline-offset: 2px;
469
+ }
470
+ .fb-prefill-hint[aria-pressed="true"],
471
+ .fb-prefill-hint.active {
472
+ background: var(--fb-primary-color);
473
+ color: #ffffff;
474
+ border-color: var(--fb-primary-color);
475
+ }
446
476
  `;
447
477
  doc.head.appendChild(style);
448
478
  }
@@ -454,7 +484,9 @@ function applyAutoExpand(textarea) {
454
484
  const resize = () => {
455
485
  if (!textarea.isConnected) return;
456
486
  textarea.style.height = "0";
457
- textarea.style.height = `${textarea.scrollHeight}px`;
487
+ const cs = getComputedStyle(textarea);
488
+ const borderY = parseFloat(cs.borderTopWidth || "0") + parseFloat(cs.borderBottomWidth || "0");
489
+ textarea.style.height = `${textarea.scrollHeight + borderY}px`;
458
490
  };
459
491
  textarea.addEventListener("input", resize);
460
492
  setTimeout(() => {
@@ -4275,16 +4307,26 @@ function filterAndSlice(allFiles, currentCount, constraints, state) {
4275
4307
  }
4276
4308
  return { accepted, errorMessage: errorParts.join(" \u2022 ") };
4277
4309
  }
4278
- async function uploadBatch(accepted, resourceIds, listEl, state) {
4310
+ async function uploadBatch(opts) {
4279
4311
  var _a;
4280
- if (listEl) {
4312
+ const {
4313
+ accepted,
4314
+ listEl,
4315
+ state,
4316
+ shouldHideAddTile,
4317
+ buildSuccessTile,
4318
+ prepareForUpload
4319
+ } = opts;
4320
+ if (listEl && shouldHideAddTile) {
4281
4321
  const tilesWrap = ensureTilesWrap(listEl);
4282
4322
  const addTile = (_a = tilesWrap.querySelector(".fb-multi-add-tile-js")) != null ? _a : tilesWrap.querySelector(".fb-tile-add");
4283
4323
  if (addTile) addTile.style.display = "none";
4284
4324
  }
4325
+ prepareForUpload == null ? void 0 : prepareForUpload();
4326
+ const orderedIds = new Array(accepted.length).fill(null);
4285
4327
  const failures = [];
4286
4328
  await Promise.allSettled(
4287
- accepted.map(async (file) => {
4329
+ accepted.map(async (file, index) => {
4288
4330
  const placeholder = createUploadingTile(file.name, state);
4289
4331
  if (listEl) {
4290
4332
  const tilesWrap = ensureTilesWrap(listEl);
@@ -4297,20 +4339,24 @@ async function uploadBatch(accepted, resourceIds, listEl, state) {
4297
4339
  type: file.type,
4298
4340
  size: file.size,
4299
4341
  uploadedAt: /* @__PURE__ */ new Date(),
4300
- file: void 0
4342
+ file
4301
4343
  });
4302
- resourceIds.push(rid);
4344
+ orderedIds[index] = rid;
4345
+ if (buildSuccessTile && placeholder.parentNode) {
4346
+ placeholder.replaceWith(buildSuccessTile(rid));
4347
+ } else {
4348
+ placeholder.remove();
4349
+ }
4303
4350
  } catch (err) {
4304
4351
  const wrapped = err instanceof Error ? err : new Error(String(err));
4305
4352
  const cause = wrapped.cause;
4306
4353
  const root = cause instanceof Error ? cause : cause !== void 0 ? new Error(String(cause)) : wrapped;
4307
4354
  failures.push({ file, error: root });
4308
- } finally {
4309
4355
  placeholder.remove();
4310
4356
  }
4311
4357
  })
4312
4358
  );
4313
- return { failures };
4359
+ return { failures, orderedIds };
4314
4360
  }
4315
4361
  function buildBatchErrorMessage(filterError, failures, state) {
4316
4362
  if (failures.length === 0) return filterError;
@@ -4322,69 +4368,72 @@ function buildBatchErrorMessage(filterError, failures, state) {
4322
4368
  ).join(" \u2022 ");
4323
4369
  return filterError ? `${filterError} \u2022 ${uploadMsg}` : uploadMsg;
4324
4370
  }
4325
- function setupFilesDropHandler(filesContainer, resourceIds, state, updateCallback, constraints, pathKey, instance) {
4371
+ async function runMultiFileBatch(opts, files, listEl, errorTarget) {
4372
+ const {
4373
+ resourceIds,
4374
+ state,
4375
+ updateCallback,
4376
+ constraints,
4377
+ pathKey,
4378
+ instance,
4379
+ buildSuccessTile,
4380
+ prepareForUpload,
4381
+ coordinator
4382
+ } = opts;
4383
+ const { accepted, errorMessage } = filterAndSlice(
4384
+ files,
4385
+ coordinator.getOccupiedCount(),
4386
+ constraints,
4387
+ state
4388
+ );
4389
+ if (errorTarget) {
4390
+ if (errorMessage) showFileError(errorTarget, errorMessage);
4391
+ else clearFileError(errorTarget);
4392
+ }
4393
+ const handle = coordinator.beginBatch(accepted.length);
4394
+ const shouldHideAddTile = coordinator.getOccupiedCount() >= constraints.maxCount;
4395
+ const { failures, orderedIds } = await uploadBatch({
4396
+ accepted,
4397
+ listEl,
4398
+ state,
4399
+ shouldHideAddTile,
4400
+ buildSuccessTile,
4401
+ prepareForUpload
4402
+ });
4403
+ handle.setResults(orderedIds);
4404
+ if (instance && pathKey && !state.config.readonly) {
4405
+ instance.triggerOnChange(pathKey, resourceIds);
4406
+ }
4407
+ const { wasLast } = handle.end();
4408
+ if (wasLast) updateCallback();
4409
+ if (errorTarget) {
4410
+ const combined = buildBatchErrorMessage(errorMessage, failures, state);
4411
+ if (combined) showFileError(errorTarget, combined);
4412
+ else clearFileError(errorTarget);
4413
+ }
4414
+ }
4415
+ function setupFilesDropHandler(opts) {
4416
+ const { filesContainer } = opts;
4326
4417
  setupDragAndDrop(filesContainer, async (files) => {
4327
4418
  var _a;
4328
- const { accepted, errorMessage } = filterAndSlice(
4329
- Array.from(files),
4330
- resourceIds.length,
4331
- constraints,
4332
- state
4333
- );
4334
- if (errorMessage) {
4335
- showFileError(filesContainer, errorMessage);
4336
- } else {
4337
- clearFileError(filesContainer);
4338
- }
4339
4419
  const list = (_a = filesContainer.querySelector(".files-list")) != null ? _a : filesContainer;
4340
- const { failures } = await uploadBatch(accepted, resourceIds, list, state);
4341
- const combined = buildBatchErrorMessage(errorMessage, failures, state);
4342
- if (combined) {
4343
- showFileError(filesContainer, combined);
4344
- } else {
4345
- clearFileError(filesContainer);
4346
- }
4347
- updateCallback();
4348
- if (instance && pathKey && !state.config.readonly) {
4349
- instance.triggerOnChange(pathKey, resourceIds);
4350
- }
4420
+ await runMultiFileBatch(opts, Array.from(files), list, filesContainer);
4351
4421
  });
4352
4422
  }
4353
- function setupFilesPickerHandler(filesPicker, resourceIds, state, updateCallback, constraints, pathKey, instance) {
4423
+ function setupFilesPickerHandler(opts) {
4424
+ const { filesPicker } = opts;
4354
4425
  filesPicker.onchange = async () => {
4426
+ var _a, _b;
4355
4427
  if (!filesPicker.files) return;
4356
- const wrapperEl = filesPicker.closest("[data-files-wrapper]") || filesPicker.parentElement;
4357
- const { accepted, errorMessage } = filterAndSlice(
4428
+ const wrapperEl = (_a = filesPicker.closest("[data-files-wrapper]")) != null ? _a : filesPicker.parentElement;
4429
+ const listEl = (_b = wrapperEl == null ? void 0 : wrapperEl.querySelector(".files-list")) != null ? _b : null;
4430
+ await runMultiFileBatch(
4431
+ opts,
4358
4432
  Array.from(filesPicker.files),
4359
- resourceIds.length,
4360
- constraints,
4361
- state
4362
- );
4363
- if (errorMessage && wrapperEl) {
4364
- showFileError(wrapperEl, errorMessage);
4365
- } else if (wrapperEl) {
4366
- clearFileError(wrapperEl);
4367
- }
4368
- const listEl = wrapperEl == null ? void 0 : wrapperEl.querySelector(".files-list");
4369
- const { failures } = await uploadBatch(
4370
- accepted,
4371
- resourceIds,
4372
- listEl != null ? listEl : null,
4373
- state
4433
+ listEl,
4434
+ wrapperEl
4374
4435
  );
4375
- if (wrapperEl) {
4376
- const combined = buildBatchErrorMessage(errorMessage, failures, state);
4377
- if (combined) {
4378
- showFileError(wrapperEl, combined);
4379
- } else {
4380
- clearFileError(wrapperEl);
4381
- }
4382
- }
4383
- updateCallback();
4384
4436
  filesPicker.value = "";
4385
- if (instance && pathKey && !state.config.readonly) {
4386
- instance.triggerOnChange(pathKey, resourceIds);
4387
- }
4388
4437
  };
4389
4438
  }
4390
4439
 
@@ -4426,16 +4475,6 @@ function validatePickedResource(resource, allowedExtensions, allowedMimes, maxSi
4426
4475
  }
4427
4476
  return null;
4428
4477
  }
4429
- function readCurrentResourceIds(wrapper) {
4430
- const raw = wrapper.dataset.resourceIds;
4431
- if (!raw) return [];
4432
- try {
4433
- const parsed = JSON.parse(raw);
4434
- return Array.isArray(parsed) ? parsed : [];
4435
- } catch {
4436
- return [];
4437
- }
4438
- }
4439
4478
  function registerPickedResource(resource, state) {
4440
4479
  var _a;
4441
4480
  const existing = state.resourceIndex.get(resource.resourceId);
@@ -4451,14 +4490,28 @@ function extractPickerError(error, state) {
4451
4490
  if (error instanceof Error && error.message) return error.message;
4452
4491
  return t("pickerError", state);
4453
4492
  }
4454
- async function handleLibraryPickMulti(state, element, wrapper, fieldPath, resourceIds, maxCount, updateCallback, instance) {
4493
+ async function handleLibraryPickMulti(opts) {
4455
4494
  var _a;
4495
+ const {
4496
+ state,
4497
+ element,
4498
+ wrapper,
4499
+ fieldPath,
4500
+ resourceIds,
4501
+ maxCount,
4502
+ updateCallback,
4503
+ instance,
4504
+ coordinator,
4505
+ list,
4506
+ buildSuccessTile
4507
+ } = opts;
4456
4508
  if (!state.config.pickExistingFiles) return;
4457
4509
  const allowedExtensions = getAllowedExtensions(element.accept);
4458
4510
  const allowedMimes = getAllowedMimes(element.accept);
4459
4511
  const maxSizeMB = (_a = element.maxSize) != null ? _a : Infinity;
4460
- const currentIds = readCurrentResourceIds(wrapper);
4461
- const remaining = maxCount === Infinity ? Infinity : Math.max(0, maxCount - currentIds.length);
4512
+ const knownRids = coordinator.getAllKnownRids();
4513
+ const existingSet = new Set(knownRids);
4514
+ const remaining = maxCount === Infinity ? Infinity : Math.max(0, maxCount - coordinator.getOccupiedCount());
4462
4515
  let picked;
4463
4516
  try {
4464
4517
  picked = await state.config.pickExistingFiles({
@@ -4467,14 +4520,15 @@ async function handleLibraryPickMulti(state, element, wrapper, fieldPath, resour
4467
4520
  accept: buildAcceptContext(element),
4468
4521
  maxSizeMB: maxSizeMB === Infinity ? void 0 : maxSizeMB,
4469
4522
  remainingSlots: remaining === Infinity ? void 0 : remaining,
4470
- selectedResourceIds: [...currentIds]
4523
+ // Hand the host every rid that's already selected (committed or
4524
+ // staged) so it can grey them out or filter them.
4525
+ selectedResourceIds: knownRids
4471
4526
  });
4472
4527
  } catch (error) {
4473
4528
  showFileError(wrapper, extractPickerError(error, state));
4474
4529
  return;
4475
4530
  }
4476
4531
  if (picked.length === 0) return;
4477
- const existingSet = new Set(currentIds);
4478
4532
  const seen = /* @__PURE__ */ new Set();
4479
4533
  const deduped = picked.filter((r) => {
4480
4534
  if (existingSet.has(r.resourceId)) return false;
@@ -4492,10 +4546,18 @@ async function handleLibraryPickMulti(state, element, wrapper, fieldPath, resour
4492
4546
  );
4493
4547
  return err === null;
4494
4548
  });
4495
- const freshRemaining = maxCount === Infinity ? validItems.length : Math.max(0, maxCount - resourceIds.length);
4549
+ const freshRemaining = maxCount === Infinity ? validItems.length : Math.max(0, maxCount - coordinator.getOccupiedCount());
4496
4550
  const accepted = validItems.slice(0, freshRemaining);
4497
4551
  const skipped = validItems.length - accepted.length;
4498
- if (accepted.length === 0) return;
4552
+ if (accepted.length === 0) {
4553
+ if (skipped > 0) {
4554
+ showFileError(
4555
+ wrapper,
4556
+ t("filesLimitExceeded", state, { skipped, max: maxCount })
4557
+ );
4558
+ }
4559
+ return;
4560
+ }
4499
4561
  clearFileError(wrapper);
4500
4562
  if (skipped > 0) {
4501
4563
  showFileError(
@@ -4505,13 +4567,22 @@ async function handleLibraryPickMulti(state, element, wrapper, fieldPath, resour
4505
4567
  }
4506
4568
  for (const resource of accepted) {
4507
4569
  registerPickedResource(resource, state);
4508
- resourceIds.push(resource.resourceId);
4509
4570
  }
4510
- wrapper.dataset.resourceIds = JSON.stringify(resourceIds);
4511
- updateCallback();
4571
+ const acceptedIds = accepted.map((r) => r.resourceId);
4572
+ const handle = coordinator.beginBatch(accepted.length);
4573
+ handle.setResults(acceptedIds);
4512
4574
  if (!state.config.readonly) {
4513
4575
  instance.triggerOnChange(fieldPath, resourceIds);
4514
4576
  }
4577
+ const { wasLast } = handle.end();
4578
+ if (wasLast) {
4579
+ updateCallback();
4580
+ } else {
4581
+ const tilesWrap = ensureTilesWrap(list);
4582
+ for (const rid of acceptedIds) {
4583
+ tilesWrap.appendChild(buildSuccessTile(rid));
4584
+ }
4585
+ }
4515
4586
  }
4516
4587
  async function handleLibraryPickSingle(state, element, container, fileWrapper, pathKey, fieldPath, renderCallback, instance) {
4517
4588
  var _a, _b;
@@ -4839,6 +4910,14 @@ function buildMetaDot() {
4839
4910
  return dot;
4840
4911
  }
4841
4912
  var gridResizeObservers = /* @__PURE__ */ new WeakMap();
4913
+ function disposePlaceholdersForUpload(container) {
4914
+ const observer = gridResizeObservers.get(container);
4915
+ if (observer) {
4916
+ observer.disconnect();
4917
+ gridResizeObservers.delete(container);
4918
+ }
4919
+ container.querySelectorAll(".fb-multi-placeholder").forEach((p) => p.remove());
4920
+ }
4842
4921
  function renderResourcePills(opts) {
4843
4922
  var _a;
4844
4923
  const {
@@ -5193,16 +5272,19 @@ function setupMultiFileEditMode(element, ctx, wrapper, pathKey, maxFiles) {
5193
5272
  filesPicker.click();
5194
5273
  };
5195
5274
  const onLibraryPick = state.config.pickExistingFiles && !element.disableLibrary ? () => {
5196
- handleLibraryPickMulti(
5275
+ handleLibraryPickMulti({
5197
5276
  state,
5198
5277
  element,
5199
- filesWrapper,
5200
- pathKey,
5201
- initialFiles,
5202
- maxFiles,
5203
- updateFilesDisplay,
5204
- ctx.instance
5205
- ).catch((err) => {
5278
+ wrapper: filesWrapper,
5279
+ fieldPath: pathKey,
5280
+ resourceIds: initialFiles,
5281
+ maxCount: maxFiles,
5282
+ updateCallback: updateFilesDisplay,
5283
+ instance: ctx.instance,
5284
+ coordinator,
5285
+ list,
5286
+ buildSuccessTile
5287
+ }).catch((err) => {
5206
5288
  console.error("Library pick failed:", err);
5207
5289
  });
5208
5290
  } : null;
@@ -5213,41 +5295,160 @@ function setupMultiFileEditMode(element, ctx, wrapper, pathKey, maxFiles) {
5213
5295
  rids: initialFiles,
5214
5296
  state,
5215
5297
  onRemove: currentlyReadonly ? null : (ridToRemove) => {
5216
- var _a2;
5298
+ var _a2, _b2;
5217
5299
  releaseLocalFileUrl((_a2 = state.resourceIndex.get(ridToRemove)) == null ? void 0 : _a2.file);
5218
5300
  const index = initialFiles.indexOf(ridToRemove);
5219
5301
  if (index > -1) initialFiles.splice(index, 1);
5220
- updateFilesDisplay();
5302
+ if (coordinator.hasInFlightBatches()) {
5303
+ pendingRemovals.add(ridToRemove);
5304
+ (_b2 = list.querySelector(`[data-resource-id="${ridToRemove}"]`)) == null ? void 0 : _b2.remove();
5305
+ filesWrapper.dataset.resourceIds = JSON.stringify(initialFiles);
5306
+ } else {
5307
+ updateFilesDisplay();
5308
+ }
5309
+ if (ctx.instance && pathKey && !state.config.readonly) {
5310
+ ctx.instance.triggerOnChange(pathKey, initialFiles);
5311
+ }
5221
5312
  },
5222
5313
  maxCount: maxFiles < Infinity ? maxFiles : void 0,
5223
5314
  isReadonly: currentlyReadonly,
5224
5315
  onLibraryPick: currentlyReadonly ? null : onLibraryPick,
5225
5316
  element,
5226
5317
  onClearAll: currentlyReadonly ? void 0 : () => {
5318
+ var _a2, _b2;
5319
+ for (const rid of initialFiles) {
5320
+ releaseLocalFileUrl((_a2 = state.resourceIndex.get(rid)) == null ? void 0 : _a2.file);
5321
+ }
5227
5322
  initialFiles.splice(0);
5228
- updateFilesDisplay();
5323
+ if (coordinator.hasInFlightBatches()) {
5324
+ const visibleTiles = list.querySelectorAll("[data-resource-id]");
5325
+ for (const tile of visibleTiles) {
5326
+ const rid = tile.dataset.resourceId;
5327
+ if (rid) {
5328
+ releaseLocalFileUrl((_b2 = state.resourceIndex.get(rid)) == null ? void 0 : _b2.file);
5329
+ pendingRemovals.add(rid);
5330
+ }
5331
+ tile.remove();
5332
+ }
5333
+ filesWrapper.dataset.resourceIds = JSON.stringify(initialFiles);
5334
+ } else {
5335
+ updateFilesDisplay();
5336
+ }
5337
+ if (ctx.instance && pathKey && !state.config.readonly) {
5338
+ ctx.instance.triggerOnChange(pathKey, initialFiles);
5339
+ }
5229
5340
  },
5230
5341
  openPicker
5231
5342
  });
5232
5343
  }
5233
- setupFilesDropHandler(
5234
- filesContainer,
5235
- initialFiles,
5236
- state,
5237
- updateFilesDisplay,
5238
- constraints,
5239
- pathKey,
5240
- ctx.instance
5241
- );
5242
- setupFilesPickerHandler(
5243
- filesPicker,
5244
- initialFiles,
5344
+ let inFlightFiles = 0;
5345
+ let activeBatches = 0;
5346
+ let nextBatchOrdinal = 0;
5347
+ let nextCommitOrdinal = 0;
5348
+ const stagedResults = /* @__PURE__ */ new Map();
5349
+ const batchReservations = /* @__PURE__ */ new Map();
5350
+ const pendingRemovals = /* @__PURE__ */ new Set();
5351
+ const drainContiguousStagedResults = () => {
5352
+ var _a2;
5353
+ while (stagedResults.has(nextCommitOrdinal)) {
5354
+ const ordinal = nextCommitOrdinal;
5355
+ const ids = stagedResults.get(ordinal);
5356
+ stagedResults.delete(ordinal);
5357
+ nextCommitOrdinal += 1;
5358
+ const reservation = (_a2 = batchReservations.get(ordinal)) != null ? _a2 : 0;
5359
+ batchReservations.delete(ordinal);
5360
+ inFlightFiles -= reservation;
5361
+ for (const rid of ids) {
5362
+ if (rid === null) continue;
5363
+ if (pendingRemovals.has(rid)) {
5364
+ pendingRemovals.delete(rid);
5365
+ continue;
5366
+ }
5367
+ initialFiles.push(rid);
5368
+ }
5369
+ }
5370
+ filesWrapper.dataset.resourceIds = JSON.stringify(initialFiles);
5371
+ };
5372
+ const coordinator = {
5373
+ getOccupiedCount: () => initialFiles.length + inFlightFiles,
5374
+ getAllKnownRids: () => {
5375
+ const out = [...initialFiles];
5376
+ for (const ids of stagedResults.values()) {
5377
+ for (const rid of ids) {
5378
+ if (rid !== null) out.push(rid);
5379
+ }
5380
+ }
5381
+ return out;
5382
+ },
5383
+ hasInFlightBatches: () => activeBatches > 0 || batchReservations.size > 0,
5384
+ wasRemovedDuringBatch: (rid) => pendingRemovals.has(rid),
5385
+ beginBatch: (count) => {
5386
+ inFlightFiles += count;
5387
+ activeBatches += 1;
5388
+ const ordinal = nextBatchOrdinal++;
5389
+ batchReservations.set(ordinal, count);
5390
+ return {
5391
+ setResults: (orderedIds) => {
5392
+ stagedResults.set(ordinal, orderedIds);
5393
+ drainContiguousStagedResults();
5394
+ },
5395
+ end: () => {
5396
+ activeBatches -= 1;
5397
+ const wasLast = activeBatches === 0 && batchReservations.size === 0;
5398
+ if (wasLast) {
5399
+ pendingRemovals.clear();
5400
+ }
5401
+ return { wasLast };
5402
+ }
5403
+ };
5404
+ }
5405
+ };
5406
+ const buildSuccessTile = (rid) => {
5407
+ const currentlyReadonly = isElementReadonly(element, state);
5408
+ return buildPreviewTile(
5409
+ rid,
5410
+ state,
5411
+ !currentlyReadonly,
5412
+ currentlyReadonly ? null : () => {
5413
+ var _a2, _b2, _c;
5414
+ releaseLocalFileUrl((_a2 = state.resourceIndex.get(rid)) == null ? void 0 : _a2.file);
5415
+ const idx = initialFiles.indexOf(rid);
5416
+ if (idx > -1) {
5417
+ initialFiles.splice(idx, 1);
5418
+ if (coordinator.hasInFlightBatches()) {
5419
+ pendingRemovals.add(rid);
5420
+ (_b2 = list.querySelector(`[data-resource-id="${rid}"]`)) == null ? void 0 : _b2.remove();
5421
+ filesWrapper.dataset.resourceIds = JSON.stringify(initialFiles);
5422
+ } else {
5423
+ updateFilesDisplay();
5424
+ }
5425
+ if (ctx.instance && pathKey && !state.config.readonly) {
5426
+ ctx.instance.triggerOnChange(pathKey, initialFiles);
5427
+ }
5428
+ return;
5429
+ }
5430
+ pendingRemovals.add(rid);
5431
+ (_c = list.querySelector(`[data-resource-id="${rid}"]`)) == null ? void 0 : _c.remove();
5432
+ if (ctx.instance && pathKey && !state.config.readonly) {
5433
+ ctx.instance.triggerOnChange(pathKey, initialFiles);
5434
+ }
5435
+ }
5436
+ );
5437
+ };
5438
+ const prepareForUpload = () => disposePlaceholdersForUpload(list);
5439
+ const sharedHandlerOpts = {
5440
+ resourceIds: initialFiles,
5245
5441
  state,
5246
- updateFilesDisplay,
5442
+ updateCallback: updateFilesDisplay,
5247
5443
  constraints,
5248
5444
  pathKey,
5249
- ctx.instance
5250
- );
5445
+ instance: ctx.instance,
5446
+ buildSuccessTile,
5447
+ prepareForUpload,
5448
+ coordinator
5449
+ };
5450
+ setupFilesDropHandler({ ...sharedHandlerOpts, filesContainer });
5451
+ setupFilesPickerHandler({ ...sharedHandlerOpts, filesPicker });
5251
5452
  updateFilesDisplay();
5252
5453
  wrapper.appendChild(filesWrapper);
5253
5454
  }
@@ -11032,6 +11233,7 @@ var exampleThemes = {
11032
11233
  },
11033
11234
  // Picaz wizard design tokens — derived from the Picaz Wizard mockups.
11034
11235
  // Pairs with the host-side .card / .section-num / .lede chrome that wraps the form.
11236
+ // Assumes Inter is loaded by the host (e.g. via Google Fonts in index.html).
11035
11237
  picaz: {
11036
11238
  ...defaultTheme,
11037
11239
  primaryColor: "#2f5bea",
@@ -11063,6 +11265,9 @@ var exampleThemes = {
11063
11265
  fileUploadBgColor: "#fafcff",
11064
11266
  fileUploadBorderColor: "#cdd6e3",
11065
11267
  fileUploadHoverBorderColor: "#2f5bea",
11268
+ // Picaz uses roomier inputs (11/14px) than the defaultTheme compact density.
11269
+ inputPaddingX: "14px",
11270
+ inputPaddingY: "11px",
11066
11271
  borderRadius: "12px",
11067
11272
  borderRadiusSmall: "8px",
11068
11273
  borderRadiusLarge: "16px",
@@ -11070,7 +11275,17 @@ var exampleThemes = {
11070
11275
  fontFamily: '"Inter", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif',
11071
11276
  shadowCard: "0 1px 2px rgba(15,23,42,.04), 0 1px 0 rgba(15,23,42,.02)",
11072
11277
  shadowPopover: "0 12px 32px -12px rgba(15,23,42,.18), 0 4px 12px -6px rgba(15,23,42,.08)",
11073
- focusRingColor: "#2f5bea"
11278
+ focusRingColor: "#2f5bea",
11279
+ // Slide cards: subtle gradient lift, no rest-state shadow (mockup adds it
11280
+ // only on hover, which form-builder doesn't yet differentiate).
11281
+ slideCardBg: "linear-gradient(180deg, #f7f9fc 0%, #dde3ee 100%)",
11282
+ slideCardShadow: "none",
11283
+ slideCardRadius: "16px",
11284
+ // Tiny uppercase captions above grouped lists ("ПРЕИМУЩЕСТВА" in the mockup).
11285
+ labelSectionFontSize: "0.625rem",
11286
+ // 10px
11287
+ labelSectionLetterSpacing: "0.07em",
11288
+ labelSectionTextTransform: "uppercase"
11074
11289
  }
11075
11290
  };
11076
11291
 
@@ -11436,6 +11651,13 @@ var FormBuilderInstance = class {
11436
11651
  const value = hintValues[fieldKey];
11437
11652
  this.updateField(fullPath, value);
11438
11653
  }
11654
+ const group = target.closest(".fb-prefill-hints");
11655
+ if (group) {
11656
+ group.querySelectorAll(
11657
+ '.fb-prefill-hint[aria-pressed="true"]'
11658
+ ).forEach((el) => el.removeAttribute("aria-pressed"));
11659
+ }
11660
+ target.setAttribute("aria-pressed", "true");
11439
11661
  } catch (error) {
11440
11662
  console.error("Error parsing prefill hint values:", error);
11441
11663
  }