@dmitryvim/form-builder 0.2.32 → 0.2.34

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/esm/index.js CHANGED
@@ -398,6 +398,167 @@ function deepEqual(a, b) {
398
398
  return a === b;
399
399
  }
400
400
 
401
+ // src/utils/styles.ts
402
+ function mountCounterInLabel(wrapper, counter) {
403
+ const labelRow = wrapper.querySelector(
404
+ ":scope > [data-fb-label-row]"
405
+ );
406
+ if (labelRow) labelRow.appendChild(counter);
407
+ }
408
+ function createAddItemRow(classNameSuffix, onClick, options = {}) {
409
+ const label = options.label ?? "";
410
+ const showCounter = options.showCounter !== false;
411
+ const row = document.createElement("div");
412
+ row.className = "fb-add-row mt-2";
413
+ row.style.cssText = "display:flex;align-items:stretch;width:100%;";
414
+ const button = document.createElement("button");
415
+ button.type = "button";
416
+ button.className = `add-${classNameSuffix}-btn`;
417
+ button.style.cssText = `
418
+ flex: 1 1 auto;
419
+ display: inline-flex;
420
+ align-items: center;
421
+ justify-content: center;
422
+ gap: 6px;
423
+ padding: 6px 10px;
424
+ border: 1px dashed var(--fb-primary-color);
425
+ border-radius: var(--fb-border-radius);
426
+ background: transparent;
427
+ color: var(--fb-primary-color);
428
+ font-size: var(--fb-font-size-small, var(--fb-font-size));
429
+ font-weight: 500;
430
+ font-family: var(--fb-font-family);
431
+ cursor: pointer;
432
+ transition: border-color var(--fb-transition-duration), color var(--fb-transition-duration), background-color var(--fb-transition-duration);
433
+ `;
434
+ button.textContent = label ? `+ ${label}` : "+";
435
+ button.addEventListener("mouseenter", () => {
436
+ if (button.disabled) return;
437
+ button.style.borderStyle = "solid";
438
+ button.style.backgroundColor = "var(--fb-background-hover-color)";
439
+ });
440
+ button.addEventListener("mouseleave", () => {
441
+ button.style.borderStyle = "dashed";
442
+ button.style.backgroundColor = "transparent";
443
+ });
444
+ button.onclick = onClick;
445
+ const counter = document.createElement("span");
446
+ counter.className = "fb-add-counter";
447
+ counter.style.cssText = `
448
+ margin-left: auto;
449
+ font-size: var(--fb-font-size-small, 0.875rem);
450
+ color: var(--fb-text-secondary-color);
451
+ font-weight: 400;
452
+ `;
453
+ if (!showCounter) counter.style.display = "none";
454
+ row.appendChild(button);
455
+ const update = (current, max) => {
456
+ const reached = current >= max;
457
+ row.style.display = reached ? "none" : "flex";
458
+ button.style.display = reached ? "none" : "inline-flex";
459
+ button.disabled = reached;
460
+ if (showCounter) {
461
+ counter.textContent = `${current}/${max === Infinity ? "\u221E" : max}`;
462
+ }
463
+ };
464
+ return { row, button, counter, update };
465
+ }
466
+ function createSlideAddTile(onClick, options = {}) {
467
+ const label = options.label ?? "";
468
+ const tile = document.createElement("button");
469
+ tile.type = "button";
470
+ tile.className = "add-container-btn fb-slide-add";
471
+ tile.style.cssText = `
472
+ display: flex;
473
+ flex-direction: column;
474
+ align-items: center;
475
+ justify-content: center;
476
+ gap: 12px;
477
+ width: 100%;
478
+ min-height: 180px;
479
+ align-self: stretch;
480
+ padding: 24px 16px;
481
+ border: 1.5px dashed var(--fb-primary-color);
482
+ border-radius: var(--fb-border-radius);
483
+ background: transparent;
484
+ color: var(--fb-primary-color);
485
+ font-size: var(--fb-font-size-small, var(--fb-font-size));
486
+ font-weight: 500;
487
+ font-family: var(--fb-font-family);
488
+ cursor: pointer;
489
+ transition: border-color var(--fb-transition-duration), color var(--fb-transition-duration), background-color var(--fb-transition-duration);
490
+ `;
491
+ const circle = document.createElement("span");
492
+ circle.className = "fb-slide-add-circle";
493
+ circle.style.cssText = `
494
+ display: inline-flex;
495
+ align-items: center;
496
+ justify-content: center;
497
+ width: 36px;
498
+ height: 36px;
499
+ border: 1px solid var(--fb-primary-color);
500
+ border-radius: 50%;
501
+ background: var(--fb-background-color);
502
+ font-size: 20px;
503
+ line-height: 1;
504
+ color: inherit;
505
+ transition: inherit;
506
+ `;
507
+ circle.textContent = "+";
508
+ tile.appendChild(circle);
509
+ if (label) {
510
+ const text = document.createElement("span");
511
+ text.textContent = label;
512
+ tile.appendChild(text);
513
+ }
514
+ tile.addEventListener("mouseenter", () => {
515
+ if (tile.disabled) return;
516
+ tile.style.borderStyle = "solid";
517
+ tile.style.backgroundColor = "var(--fb-background-hover-color)";
518
+ });
519
+ tile.addEventListener("mouseleave", () => {
520
+ tile.style.borderStyle = "dashed";
521
+ tile.style.backgroundColor = "transparent";
522
+ });
523
+ tile.onclick = onClick;
524
+ const counter = document.createElement("span");
525
+ counter.className = "fb-add-counter";
526
+ counter.style.cssText = `
527
+ margin-left: auto;
528
+ font-size: var(--fb-font-size-small, 0.875rem);
529
+ color: var(--fb-text-secondary-color);
530
+ font-weight: 400;
531
+ `;
532
+ const update = (current, max) => {
533
+ const reached = current >= max;
534
+ tile.style.display = reached ? "none" : "flex";
535
+ tile.disabled = reached;
536
+ counter.textContent = `${current}/${max === Infinity ? "\u221E" : max}`;
537
+ };
538
+ return { tile, counter, update };
539
+ }
540
+ function applyActionButtonStyles(button, isFormLevel = false) {
541
+ button.style.cssText = `
542
+ background-color: var(--fb-action-bg-color);
543
+ color: var(--fb-action-text-color);
544
+ border: var(--fb-border-width) solid var(--fb-action-border-color);
545
+ padding: ${isFormLevel ? "0.5rem 1rem" : "0.5rem 0.75rem"};
546
+ font-size: var(--fb-font-size);
547
+ font-weight: var(--fb-font-weight-medium);
548
+ border-radius: var(--fb-border-radius);
549
+ transition: all var(--fb-transition-duration);
550
+ box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
551
+ `;
552
+ button.addEventListener("mouseenter", () => {
553
+ button.style.backgroundColor = "var(--fb-action-hover-bg-color)";
554
+ button.style.borderColor = "var(--fb-action-hover-border-color)";
555
+ });
556
+ button.addEventListener("mouseleave", () => {
557
+ button.style.backgroundColor = "var(--fb-action-bg-color)";
558
+ button.style.borderColor = "var(--fb-action-border-color)";
559
+ });
560
+ }
561
+
401
562
  // src/components/text.ts
402
563
  function createCharCounter(element, input, isTextarea = false) {
403
564
  const counter = document.createElement("span");
@@ -450,12 +611,13 @@ function renderTextElement(element, ctx, wrapper, pathKey) {
450
611
  const readonly = isElementReadonly(element, state, ctx);
451
612
  const inputWrapper = document.createElement("div");
452
613
  inputWrapper.style.cssText = "position: relative;";
614
+ const hasCharCounter = !readonly && (element.minLength != null || element.maxLength != null);
453
615
  const textInput = document.createElement("input");
454
616
  textInput.type = "text";
455
617
  textInput.className = "w-full rounded-lg";
456
618
  textInput.style.cssText = `
457
619
  padding: var(--fb-input-padding-y) var(--fb-input-padding-x);
458
- padding-right: 60px;
620
+ ${hasCharCounter ? "padding-right: 60px;" : ""}
459
621
  border: var(--fb-border-width) solid var(--fb-border-color);
460
622
  border-radius: var(--fb-border-radius);
461
623
  background-color: ${readonly ? "var(--fb-background-readonly-color)" : "var(--fb-background-color)"};
@@ -500,7 +662,7 @@ function renderTextElement(element, ctx, wrapper, pathKey) {
500
662
  textInput.addEventListener("input", handleChange);
501
663
  }
502
664
  inputWrapper.appendChild(textInput);
503
- if (!readonly && (element.minLength != null || element.maxLength != null)) {
665
+ if (hasCharCounter) {
504
666
  const counter = createCharCounter(element, textInput, false);
505
667
  inputWrapper.appendChild(counter);
506
668
  }
@@ -511,6 +673,7 @@ function renderMultipleTextElement(element, ctx, wrapper, pathKey) {
511
673
  const readonly = isElementReadonly(element, state, ctx);
512
674
  const prefillValues = ctx.prefill[element.key] || [];
513
675
  const values = Array.isArray(prefillValues) ? [...prefillValues] : [];
676
+ const hasCharCounter = !readonly && (element.minLength != null || element.maxLength != null);
514
677
  const minCount = element.minCount ?? 1;
515
678
  const maxCount = element.maxCount ?? Infinity;
516
679
  while (values.length < minCount) {
@@ -537,7 +700,7 @@ function renderMultipleTextElement(element, ctx, wrapper, pathKey) {
537
700
  textInput.type = "text";
538
701
  textInput.style.cssText = `
539
702
  padding: var(--fb-input-padding-y) var(--fb-input-padding-x);
540
- padding-right: 60px;
703
+ ${hasCharCounter ? "padding-right: 60px;" : ""}
541
704
  border: var(--fb-border-width) solid var(--fb-border-color);
542
705
  border-radius: var(--fb-border-radius);
543
706
  background-color: ${readonly ? "var(--fb-background-readonly-color)" : "var(--fb-background-color)"};
@@ -581,7 +744,7 @@ function renderMultipleTextElement(element, ctx, wrapper, pathKey) {
581
744
  textInput.addEventListener("input", handleChange);
582
745
  }
583
746
  inputContainer.appendChild(textInput);
584
- if (!readonly && (element.minLength != null || element.maxLength != null)) {
747
+ if (hasCharCounter) {
585
748
  const counter = createCharCounter(element, textInput, false);
586
749
  inputContainer.appendChild(counter);
587
750
  }
@@ -638,50 +801,24 @@ function renderMultipleTextElement(element, ctx, wrapper, pathKey) {
638
801
  removeBtn.style.pointerEvents = disabled ? "none" : "auto";
639
802
  });
640
803
  }
641
- let addRow = null;
642
- let countDisplay = null;
804
+ let addUpdate = null;
643
805
  if (!readonly) {
644
- addRow = document.createElement("div");
645
- addRow.className = "flex items-center gap-3 mt-2";
646
- const addBtn = document.createElement("button");
647
- addBtn.type = "button";
648
- addBtn.className = "add-text-btn px-3 py-1 rounded";
649
- addBtn.style.cssText = `
650
- color: var(--fb-primary-color);
651
- border: var(--fb-border-width) solid var(--fb-primary-color);
652
- background-color: transparent;
653
- font-size: var(--fb-font-size);
654
- transition: all var(--fb-transition-duration);
655
- `;
656
- addBtn.textContent = "+";
657
- addBtn.addEventListener("mouseenter", () => {
658
- addBtn.style.backgroundColor = "var(--fb-background-hover-color)";
659
- });
660
- addBtn.addEventListener("mouseleave", () => {
661
- addBtn.style.backgroundColor = "transparent";
662
- });
663
- addBtn.onclick = () => {
664
- values.push(element.default || "");
665
- addTextItem(element.default || "");
666
- updateAddButton();
667
- updateRemoveButtons();
668
- };
669
- countDisplay = document.createElement("span");
670
- countDisplay.className = "text-sm text-gray-500";
671
- addRow.appendChild(addBtn);
672
- addRow.appendChild(countDisplay);
673
- wrapper.appendChild(addRow);
806
+ const handle = createAddItemRow(
807
+ "text",
808
+ () => {
809
+ values.push(element.default || "");
810
+ addTextItem(element.default || "");
811
+ updateAddButton();
812
+ updateRemoveButtons();
813
+ },
814
+ { label: element.addLabel }
815
+ );
816
+ addUpdate = handle.update;
817
+ mountCounterInLabel(wrapper, handle.counter);
818
+ wrapper.appendChild(handle.row);
674
819
  }
675
820
  function updateAddButton() {
676
- if (!addRow || !countDisplay) return;
677
- const addBtn = addRow.querySelector(".add-text-btn");
678
- if (addBtn) {
679
- const disabled = values.length >= maxCount;
680
- addBtn.disabled = disabled;
681
- addBtn.style.opacity = disabled ? "0.5" : "1";
682
- addBtn.style.pointerEvents = disabled ? "none" : "auto";
683
- }
684
- countDisplay.textContent = `${values.length}/${maxCount === Infinity ? "\u221E" : maxCount}`;
821
+ if (addUpdate) addUpdate(values.length, maxCount);
685
822
  }
686
823
  values.forEach((value) => addTextItem(value));
687
824
  updateAddButton();
@@ -977,52 +1114,24 @@ function renderMultipleTextareaElement(element, ctx, wrapper, pathKey) {
977
1114
  removeBtn.style.pointerEvents = disabled ? "none" : "auto";
978
1115
  });
979
1116
  }
980
- let addRow = null;
981
- let countDisplay = null;
1117
+ let addUpdate = null;
982
1118
  if (!readonly) {
983
- addRow = document.createElement("div");
984
- addRow.className = "flex items-center gap-3 mt-2";
985
- const addBtn = document.createElement("button");
986
- addBtn.type = "button";
987
- addBtn.className = "add-textarea-btn px-3 py-1 rounded";
988
- addBtn.style.cssText = `
989
- color: var(--fb-primary-color);
990
- border: var(--fb-border-width) solid var(--fb-primary-color);
991
- background-color: transparent;
992
- font-size: var(--fb-font-size);
993
- transition: all var(--fb-transition-duration);
994
- `;
995
- addBtn.textContent = "+";
996
- addBtn.addEventListener("mouseenter", () => {
997
- addBtn.style.backgroundColor = "var(--fb-background-hover-color)";
998
- });
999
- addBtn.addEventListener("mouseleave", () => {
1000
- addBtn.style.backgroundColor = "transparent";
1001
- });
1002
- addBtn.onclick = () => {
1003
- values.push(element.default || "");
1004
- addTextareaItem(element.default || "");
1005
- updateAddButton();
1006
- updateRemoveButtons();
1007
- };
1008
- countDisplay = document.createElement("span");
1009
- countDisplay.className = "text-sm text-gray-500";
1010
- addRow.appendChild(addBtn);
1011
- addRow.appendChild(countDisplay);
1012
- wrapper.appendChild(addRow);
1119
+ const handle = createAddItemRow(
1120
+ "textarea",
1121
+ () => {
1122
+ values.push(element.default || "");
1123
+ addTextareaItem(element.default || "");
1124
+ updateAddButton();
1125
+ updateRemoveButtons();
1126
+ },
1127
+ { label: element.addLabel }
1128
+ );
1129
+ addUpdate = handle.update;
1130
+ mountCounterInLabel(wrapper, handle.counter);
1131
+ wrapper.appendChild(handle.row);
1013
1132
  }
1014
1133
  function updateAddButton() {
1015
- if (!addRow || !countDisplay) return;
1016
- const addBtn = addRow.querySelector(
1017
- ".add-textarea-btn"
1018
- );
1019
- if (addBtn) {
1020
- const disabled = values.length >= maxCount;
1021
- addBtn.disabled = disabled;
1022
- addBtn.style.opacity = disabled ? "0.5" : "1";
1023
- addBtn.style.pointerEvents = disabled ? "none" : "auto";
1024
- }
1025
- countDisplay.textContent = `${values.length}/${maxCount === Infinity ? "\u221E" : maxCount}`;
1134
+ if (addUpdate) addUpdate(values.length, maxCount);
1026
1135
  }
1027
1136
  values.forEach((value) => addTextareaItem(value));
1028
1137
  updateAddButton();
@@ -1228,50 +1337,24 @@ function renderMultipleNumberElement(element, ctx, wrapper, pathKey) {
1228
1337
  removeBtn.style.pointerEvents = disabled ? "none" : "auto";
1229
1338
  });
1230
1339
  }
1231
- let addRow = null;
1232
- let countDisplay = null;
1340
+ let addUpdate = null;
1233
1341
  if (!readonly) {
1234
- addRow = document.createElement("div");
1235
- addRow.className = "flex items-center gap-3 mt-2";
1236
- const addBtn = document.createElement("button");
1237
- addBtn.type = "button";
1238
- addBtn.className = "add-number-btn px-3 py-1 rounded";
1239
- addBtn.style.cssText = `
1240
- color: var(--fb-primary-color);
1241
- border: var(--fb-border-width) solid var(--fb-primary-color);
1242
- background-color: transparent;
1243
- font-size: var(--fb-font-size);
1244
- transition: all var(--fb-transition-duration);
1245
- `;
1246
- addBtn.textContent = "+";
1247
- addBtn.addEventListener("mouseenter", () => {
1248
- addBtn.style.backgroundColor = "var(--fb-background-hover-color)";
1249
- });
1250
- addBtn.addEventListener("mouseleave", () => {
1251
- addBtn.style.backgroundColor = "transparent";
1252
- });
1253
- addBtn.onclick = () => {
1254
- values.push(element.default || "");
1255
- addNumberItem(element.default || "");
1256
- updateAddButton();
1257
- updateRemoveButtons();
1258
- };
1259
- countDisplay = document.createElement("span");
1260
- countDisplay.className = "text-sm text-gray-500";
1261
- addRow.appendChild(addBtn);
1262
- addRow.appendChild(countDisplay);
1263
- wrapper.appendChild(addRow);
1342
+ const handle = createAddItemRow(
1343
+ "number",
1344
+ () => {
1345
+ values.push(element.default || "");
1346
+ addNumberItem(element.default || "");
1347
+ updateAddButton();
1348
+ updateRemoveButtons();
1349
+ },
1350
+ { label: element.addLabel }
1351
+ );
1352
+ addUpdate = handle.update;
1353
+ mountCounterInLabel(wrapper, handle.counter);
1354
+ wrapper.appendChild(handle.row);
1264
1355
  }
1265
1356
  function updateAddButton() {
1266
- if (!addRow || !countDisplay) return;
1267
- const addBtn = addRow.querySelector(".add-number-btn");
1268
- if (addBtn) {
1269
- const disabled = values.length >= maxCount;
1270
- addBtn.disabled = disabled;
1271
- addBtn.style.opacity = disabled ? "0.5" : "1";
1272
- addBtn.style.pointerEvents = disabled ? "none" : "auto";
1273
- }
1274
- countDisplay.textContent = `${values.length}/${maxCount === Infinity ? "\u221E" : maxCount}`;
1357
+ if (addUpdate) addUpdate(values.length, maxCount);
1275
1358
  }
1276
1359
  values.forEach((value) => addNumberItem(value));
1277
1360
  updateAddButton();
@@ -1556,51 +1639,25 @@ function renderMultipleSelectElement(element, ctx, wrapper, pathKey) {
1556
1639
  removeBtn.style.pointerEvents = disabled ? "none" : "auto";
1557
1640
  });
1558
1641
  }
1559
- let addRow = null;
1560
- let countDisplay = null;
1642
+ let addUpdate = null;
1561
1643
  if (!readonly) {
1562
- addRow = document.createElement("div");
1563
- addRow.className = "flex items-center gap-3 mt-2";
1564
- const addBtn = document.createElement("button");
1565
- addBtn.type = "button";
1566
- addBtn.className = "add-select-btn px-3 py-1 rounded";
1567
- addBtn.style.cssText = `
1568
- color: var(--fb-primary-color);
1569
- border: var(--fb-border-width) solid var(--fb-primary-color);
1570
- background-color: transparent;
1571
- font-size: var(--fb-font-size);
1572
- transition: all var(--fb-transition-duration);
1573
- `;
1574
- addBtn.textContent = "+";
1575
- addBtn.addEventListener("mouseenter", () => {
1576
- addBtn.style.backgroundColor = "var(--fb-background-hover-color)";
1577
- });
1578
- addBtn.addEventListener("mouseleave", () => {
1579
- addBtn.style.backgroundColor = "transparent";
1580
- });
1581
- addBtn.onclick = () => {
1582
- const defaultValue = element.default || element.options?.[0]?.value || "";
1583
- values.push(defaultValue);
1584
- addSelectItem(defaultValue);
1585
- updateAddButton();
1586
- updateRemoveButtons();
1587
- };
1588
- countDisplay = document.createElement("span");
1589
- countDisplay.className = "text-sm text-gray-500";
1590
- addRow.appendChild(addBtn);
1591
- addRow.appendChild(countDisplay);
1592
- wrapper.appendChild(addRow);
1644
+ const handle = createAddItemRow(
1645
+ "select",
1646
+ () => {
1647
+ const defaultValue = element.default || element.options?.[0]?.value || "";
1648
+ values.push(defaultValue);
1649
+ addSelectItem(defaultValue);
1650
+ updateAddButton();
1651
+ updateRemoveButtons();
1652
+ },
1653
+ { label: element.addLabel }
1654
+ );
1655
+ addUpdate = handle.update;
1656
+ mountCounterInLabel(wrapper, handle.counter);
1657
+ wrapper.appendChild(handle.row);
1593
1658
  }
1594
1659
  function updateAddButton() {
1595
- if (!addRow || !countDisplay) return;
1596
- const addBtn = addRow.querySelector(".add-select-btn");
1597
- if (addBtn) {
1598
- const disabled = values.length >= maxCount;
1599
- addBtn.disabled = disabled;
1600
- addBtn.style.opacity = disabled ? "0.5" : "1";
1601
- addBtn.style.pointerEvents = disabled ? "none" : "auto";
1602
- }
1603
- countDisplay.textContent = `${values.length}/${maxCount === Infinity ? "\u221E" : maxCount}`;
1660
+ if (addUpdate) addUpdate(values.length, maxCount);
1604
1661
  }
1605
1662
  values.forEach((value) => addSelectItem(value));
1606
1663
  updateAddButton();
@@ -1943,53 +2000,25 @@ function renderMultipleSwitcherElement(element, ctx, wrapper, pathKey) {
1943
2000
  removeBtn.style.pointerEvents = disabled ? "none" : "auto";
1944
2001
  });
1945
2002
  }
1946
- let addRow = null;
1947
- let countDisplay = null;
2003
+ let addUpdate = null;
1948
2004
  if (!readonly) {
1949
- addRow = document.createElement("div");
1950
- addRow.className = "flex items-center gap-3 mt-2";
1951
- const addBtn = document.createElement("button");
1952
- addBtn.type = "button";
1953
- addBtn.className = "add-switcher-btn px-3 py-1 rounded";
1954
- addBtn.style.cssText = `
1955
- color: var(--fb-primary-color);
1956
- border: var(--fb-border-width) solid var(--fb-primary-color);
1957
- background-color: transparent;
1958
- font-size: var(--fb-font-size);
1959
- transition: all var(--fb-transition-duration);
1960
- `;
1961
- addBtn.textContent = "+";
1962
- addBtn.addEventListener("mouseenter", () => {
1963
- addBtn.style.backgroundColor = "var(--fb-background-hover-color)";
1964
- });
1965
- addBtn.addEventListener("mouseleave", () => {
1966
- addBtn.style.backgroundColor = "transparent";
1967
- });
1968
- addBtn.onclick = () => {
1969
- const defaultValue = element.default || element.options?.[0]?.value || "";
1970
- values.push(defaultValue);
1971
- addSwitcherItem(defaultValue);
1972
- updateAddButton();
1973
- updateRemoveButtons();
1974
- };
1975
- countDisplay = document.createElement("span");
1976
- countDisplay.className = "text-sm text-gray-500";
1977
- addRow.appendChild(addBtn);
1978
- addRow.appendChild(countDisplay);
1979
- wrapper.appendChild(addRow);
2005
+ const handle = createAddItemRow(
2006
+ "switcher",
2007
+ () => {
2008
+ const defaultValue = element.default || element.options?.[0]?.value || "";
2009
+ values.push(defaultValue);
2010
+ addSwitcherItem(defaultValue);
2011
+ updateAddButton();
2012
+ updateRemoveButtons();
2013
+ },
2014
+ { label: element.addLabel }
2015
+ );
2016
+ addUpdate = handle.update;
2017
+ mountCounterInLabel(wrapper, handle.counter);
2018
+ wrapper.appendChild(handle.row);
1980
2019
  }
1981
2020
  function updateAddButton() {
1982
- if (!addRow || !countDisplay) return;
1983
- const addBtn = addRow.querySelector(
1984
- ".add-switcher-btn"
1985
- );
1986
- if (addBtn) {
1987
- const disabled = values.length >= maxCount;
1988
- addBtn.disabled = disabled;
1989
- addBtn.style.opacity = disabled ? "0.5" : "1";
1990
- addBtn.style.pointerEvents = disabled ? "none" : "auto";
1991
- }
1992
- countDisplay.textContent = `${values.length}/${maxCount === Infinity ? "\u221E" : maxCount}`;
2021
+ if (addUpdate) addUpdate(values.length, maxCount);
1993
2022
  }
1994
2023
  values.forEach((value) => addSwitcherItem(value));
1995
2024
  updateAddButton();
@@ -2857,24 +2886,40 @@ function createTileActions(options) {
2857
2886
  return btn;
2858
2887
  };
2859
2888
  if (replaceHandler) {
2860
- const replaceBtn = makeBtn(ICON_REPLACE, t("replaceFile", state), "fb-tile-action-replace");
2889
+ const replaceBtn = makeBtn(
2890
+ ICON_REPLACE,
2891
+ t("replaceFile", state),
2892
+ "fb-tile-action-replace"
2893
+ );
2861
2894
  replaceBtn.addEventListener("click", () => replaceHandler());
2862
2895
  group.appendChild(replaceBtn);
2863
2896
  }
2864
2897
  if (libraryHandler) {
2865
- const libBtn = makeBtn(ICON_LIBRARY, t("fromLibrary", state), "fb-tile-action-library");
2898
+ const libBtn = makeBtn(
2899
+ ICON_LIBRARY,
2900
+ t("fromLibrary", state),
2901
+ "fb-tile-action-library"
2902
+ );
2866
2903
  libBtn.addEventListener("click", () => libraryHandler());
2867
2904
  group.appendChild(libBtn);
2868
2905
  }
2869
2906
  if (canDownload(state, meta)) {
2870
- const dlBtn = makeBtn(ICON_DOWNLOAD, t("downloadFile", state), "fb-tile-action-download");
2907
+ const dlBtn = makeBtn(
2908
+ ICON_DOWNLOAD,
2909
+ t("downloadFile", state),
2910
+ "fb-tile-action-download"
2911
+ );
2871
2912
  dlBtn.addEventListener("click", () => {
2872
2913
  triggerTileDownload(resourceId, fileName, state, meta);
2873
2914
  });
2874
2915
  group.appendChild(dlBtn);
2875
2916
  }
2876
2917
  if (canOpenInTab(state, meta)) {
2877
- const openBtn = makeBtn(ICON_OPEN, t("openInNewTab", state), "fb-tile-action-open");
2918
+ const openBtn = makeBtn(
2919
+ ICON_OPEN,
2920
+ t("openInNewTab", state),
2921
+ "fb-tile-action-open"
2922
+ );
2878
2923
  openBtn.addEventListener("click", () => {
2879
2924
  triggerTileOpen(resourceId, state, meta).catch((err) => {
2880
2925
  console.error("Open failed:", err);
@@ -2883,7 +2928,11 @@ function createTileActions(options) {
2883
2928
  group.appendChild(openBtn);
2884
2929
  }
2885
2930
  if (canRemove && removeHandler) {
2886
- const rmBtn = makeBtn(ICON_REMOVE, t("removeElement", state), "fb-tile-action-remove");
2931
+ const rmBtn = makeBtn(
2932
+ ICON_REMOVE,
2933
+ t("removeElement", state),
2934
+ "fb-tile-action-remove"
2935
+ );
2887
2936
  rmBtn.addEventListener("click", () => {
2888
2937
  removeHandler();
2889
2938
  });
@@ -2961,11 +3010,17 @@ function positionZoomPopup(popup, tile) {
2961
3010
  } else if (tileRect.bottom + margin + popupSize + padding <= window.innerHeight) {
2962
3011
  top = tileRect.bottom + margin;
2963
3012
  } else {
2964
- top = Math.max(padding, Math.min(window.innerHeight - popupSize - padding, tileRect.top));
3013
+ top = Math.max(
3014
+ padding,
3015
+ Math.min(window.innerHeight - popupSize - padding, tileRect.top)
3016
+ );
2965
3017
  }
2966
3018
  const tileCenterX = tileRect.left + tileRect.width / 2;
2967
3019
  let left = tileCenterX - popupSize / 2;
2968
- left = Math.max(padding, Math.min(window.innerWidth - popupSize - padding, left));
3020
+ left = Math.max(
3021
+ padding,
3022
+ Math.min(window.innerWidth - popupSize - padding, left)
3023
+ );
2969
3024
  popup.style.top = `${top}px`;
2970
3025
  popup.style.left = `${left}px`;
2971
3026
  }
@@ -3009,7 +3064,9 @@ function attachZoomHover(tile, src, alt, actionsEl) {
3009
3064
  const popup = getOrCreateZoomPopup();
3010
3065
  const existingActions = popup.querySelector(".fb-tile-actions");
3011
3066
  if (existingActions) existingActions.remove();
3012
- const img = popup.querySelector(".fb-tile-zoom-preview-img");
3067
+ const img = popup.querySelector(
3068
+ ".fb-tile-zoom-preview-img"
3069
+ );
3013
3070
  img.src = src;
3014
3071
  img.alt = alt;
3015
3072
  if (actionsEl) {
@@ -3037,7 +3094,9 @@ function attachZoomHover(tile, src, alt, actionsEl) {
3037
3094
  });
3038
3095
  }
3039
3096
  function attachClonedActionListeners(cloned, original) {
3040
- const originalBtns = Array.from(original.querySelectorAll(".fb-tile-action-btn"));
3097
+ const originalBtns = Array.from(
3098
+ original.querySelectorAll(".fb-tile-action-btn")
3099
+ );
3041
3100
  const clonedBtns = Array.from(cloned.querySelectorAll(".fb-tile-action-btn"));
3042
3101
  clonedBtns.forEach((clonedBtn, i) => {
3043
3102
  const origBtn = originalBtns[i];
@@ -3090,14 +3149,18 @@ function renderLocalVideoPreview(container, file, videoType, resourceId, state,
3090
3149
  return newContainer;
3091
3150
  }
3092
3151
  function attachVideoButtonHandlers(container, resourceId, state, deps) {
3093
- const changeBtn = container.querySelector(".change-file-btn");
3152
+ const changeBtn = container.querySelector(
3153
+ ".change-file-btn"
3154
+ );
3094
3155
  if (changeBtn) {
3095
3156
  changeBtn.onclick = (e) => {
3096
3157
  e.stopPropagation();
3097
3158
  deps?.picker?.click();
3098
3159
  };
3099
3160
  }
3100
- const deleteBtn = container.querySelector(".delete-file-btn");
3161
+ const deleteBtn = container.querySelector(
3162
+ ".delete-file-btn"
3163
+ );
3101
3164
  if (deleteBtn) {
3102
3165
  deleteBtn.onclick = (e) => {
3103
3166
  e.stopPropagation();
@@ -3230,7 +3293,13 @@ async function renderFilePreview(container, resourceId, state, options = {}) {
3230
3293
  deps
3231
3294
  );
3232
3295
  } else {
3233
- await renderUploadedFilePreview(container, resourceId, fileName, meta, state);
3296
+ await renderUploadedFilePreview(
3297
+ container,
3298
+ resourceId,
3299
+ fileName,
3300
+ meta,
3301
+ state
3302
+ );
3234
3303
  const isVideo = meta?.type?.startsWith("video/");
3235
3304
  if (!isReadonly && !isVideo) {
3236
3305
  renderDeleteButton(container, resourceId, state);
@@ -3257,7 +3326,8 @@ async function renderFilePreviewReadonly(resourceId, state, fileName, options =
3257
3326
  }
3258
3327
  const localFileUrl = meta?.file instanceof File ? getLocalFileUrl(meta.file) : null;
3259
3328
  const resolveOpenUrl = async () => {
3260
- if (state.config.getDownloadUrl) return state.config.getDownloadUrl(resourceId);
3329
+ if (state.config.getDownloadUrl)
3330
+ return state.config.getDownloadUrl(resourceId);
3261
3331
  if (state.config.getThumbnail) return state.config.getThumbnail(resourceId);
3262
3332
  return localFileUrl;
3263
3333
  };
@@ -3397,7 +3467,8 @@ async function fillTileContent(tile, rid, meta, state, actionsEl) {
3397
3467
  }
3398
3468
  } catch (error) {
3399
3469
  const err = error instanceof Error ? error : new Error(String(error));
3400
- if (state.config.onThumbnailError) state.config.onThumbnailError(err, rid);
3470
+ if (state.config.onThumbnailError)
3471
+ state.config.onThumbnailError(err, rid);
3401
3472
  tile.innerHTML = `<div style="display:flex;align-items:center;justify-content:center;height:100%;font-size:16px;color:var(--fb-error-color,#ef4444);">\u2715</div>`;
3402
3473
  }
3403
3474
  } else {
@@ -3430,7 +3501,8 @@ async function fillTileContent(tile, rid, meta, state, actionsEl) {
3430
3501
  }
3431
3502
  } catch (error) {
3432
3503
  const err = error instanceof Error ? error : new Error(String(error));
3433
- if (state.config.onThumbnailError) state.config.onThumbnailError(err, rid);
3504
+ if (state.config.onThumbnailError)
3505
+ state.config.onThumbnailError(err, rid);
3434
3506
  tile.innerHTML = `<div style="display:flex;align-items:center;justify-content:center;height:100%;font-size:16px;color:var(--fb-error-color,#ef4444);">\u2715</div>`;
3435
3507
  }
3436
3508
  } else {
@@ -3463,7 +3535,8 @@ async function forceDownload(resourceId, fileName, state) {
3463
3535
  if (fileUrl) {
3464
3536
  const finalUrl = fileUrl.startsWith("http") ? fileUrl : new URL(fileUrl, window.location.href).href;
3465
3537
  const response = await fetch(finalUrl);
3466
- if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
3538
+ if (!response.ok)
3539
+ throw new Error(`HTTP error! status: ${response.status}`);
3467
3540
  const blob = await response.blob();
3468
3541
  downloadBlob(blob, fileName);
3469
3542
  } else {
@@ -3512,7 +3585,9 @@ async function uploadSingleFile(file, state) {
3512
3585
  } catch (error) {
3513
3586
  const err = error instanceof Error ? error : new Error(String(error));
3514
3587
  if (state.config.onUploadError) state.config.onUploadError(err, file);
3515
- throw new Error(`File upload failed: ${err.message}`);
3588
+ const wrapped = new Error(`File upload failed: ${err.message}`);
3589
+ wrapped.cause = err;
3590
+ throw wrapped;
3516
3591
  }
3517
3592
  }
3518
3593
  async function handleFileSelect(opts) {
@@ -3624,7 +3699,9 @@ function filterAndSlice(allFiles, currentCount, constraints, state) {
3624
3699
  const rejectedBySize = afterMime.filter(
3625
3700
  (f) => !isFileSizeAllowed(f, constraints.maxSize)
3626
3701
  );
3627
- const valid = afterMime.filter((f) => isFileSizeAllowed(f, constraints.maxSize));
3702
+ const valid = afterMime.filter(
3703
+ (f) => isFileSizeAllowed(f, constraints.maxSize)
3704
+ );
3628
3705
  const remaining = constraints.maxCount === Infinity ? valid.length : Math.max(0, constraints.maxCount - currentCount);
3629
3706
  const accepted = valid.slice(0, remaining);
3630
3707
  const skippedByCount = valid.length - accepted.length;
@@ -3637,7 +3714,13 @@ function filterAndSlice(allFiles, currentCount, constraints, state) {
3637
3714
  if (rejectedByMime.length > 0) {
3638
3715
  const mimes = constraints.allowedMimes.join(", ");
3639
3716
  const names = rejectedByMime.map((f) => f.name).join(", ");
3640
- errorParts.push(t("invalidFileMime", state, { name: names, type: rejectedByMime.map((f) => f.type).join(", "), mimes }));
3717
+ errorParts.push(
3718
+ t("invalidFileMime", state, {
3719
+ name: names,
3720
+ type: rejectedByMime.map((f) => f.type).join(", "),
3721
+ mimes
3722
+ })
3723
+ );
3641
3724
  }
3642
3725
  if (rejectedBySize.length > 0) {
3643
3726
  const names = rejectedBySize.map((f) => f.name).join(", ");
@@ -3661,7 +3744,8 @@ async function uploadBatch(accepted, resourceIds, listEl, state) {
3661
3744
  const addTile = tilesWrap.querySelector(".fb-multi-add-tile-js") ?? tilesWrap.querySelector(".fb-tile-add");
3662
3745
  if (addTile) addTile.style.display = "none";
3663
3746
  }
3664
- await Promise.all(
3747
+ const failures = [];
3748
+ await Promise.allSettled(
3665
3749
  accepted.map(async (file) => {
3666
3750
  const placeholder = createUploadingTile(file.name, state);
3667
3751
  if (listEl) {
@@ -3678,11 +3762,27 @@ async function uploadBatch(accepted, resourceIds, listEl, state) {
3678
3762
  file: void 0
3679
3763
  });
3680
3764
  resourceIds.push(rid);
3765
+ } catch (err) {
3766
+ const wrapped = err instanceof Error ? err : new Error(String(err));
3767
+ const cause = wrapped.cause;
3768
+ const root = cause instanceof Error ? cause : cause !== void 0 ? new Error(String(cause)) : wrapped;
3769
+ failures.push({ file, error: root });
3681
3770
  } finally {
3682
3771
  placeholder.remove();
3683
3772
  }
3684
3773
  })
3685
3774
  );
3775
+ return { failures };
3776
+ }
3777
+ function buildBatchErrorMessage(filterError, failures, state) {
3778
+ if (failures.length === 0) return filterError;
3779
+ const uploadMsg = failures.map(
3780
+ (f) => t("uploadFailed", state, {
3781
+ name: f.file.name,
3782
+ error: f.error.message
3783
+ })
3784
+ ).join(" \u2022 ");
3785
+ return filterError ? `${filterError} \u2022 ${uploadMsg}` : uploadMsg;
3686
3786
  }
3687
3787
  function setupFilesDropHandler(filesContainer, resourceIds, state, updateCallback, constraints, pathKey, instance) {
3688
3788
  setupDragAndDrop(filesContainer, async (files) => {
@@ -3698,7 +3798,13 @@ function setupFilesDropHandler(filesContainer, resourceIds, state, updateCallbac
3698
3798
  clearFileError(filesContainer);
3699
3799
  }
3700
3800
  const list = filesContainer.querySelector(".files-list") ?? filesContainer;
3701
- await uploadBatch(accepted, resourceIds, list, state);
3801
+ const { failures } = await uploadBatch(accepted, resourceIds, list, state);
3802
+ const combined = buildBatchErrorMessage(errorMessage, failures, state);
3803
+ if (combined) {
3804
+ showFileError(filesContainer, combined);
3805
+ } else {
3806
+ clearFileError(filesContainer);
3807
+ }
3702
3808
  updateCallback();
3703
3809
  if (instance && pathKey && !state.config.readonly) {
3704
3810
  instance.triggerOnChange(pathKey, resourceIds);
@@ -3721,7 +3827,20 @@ function setupFilesPickerHandler(filesPicker, resourceIds, state, updateCallback
3721
3827
  clearFileError(wrapperEl);
3722
3828
  }
3723
3829
  const listEl = wrapperEl?.querySelector(".files-list");
3724
- await uploadBatch(accepted, resourceIds, listEl ?? null, state);
3830
+ const { failures } = await uploadBatch(
3831
+ accepted,
3832
+ resourceIds,
3833
+ listEl ?? null,
3834
+ state
3835
+ );
3836
+ if (wrapperEl) {
3837
+ const combined = buildBatchErrorMessage(errorMessage, failures, state);
3838
+ if (combined) {
3839
+ showFileError(wrapperEl, combined);
3840
+ } else {
3841
+ clearFileError(wrapperEl);
3842
+ }
3843
+ }
3725
3844
  updateCallback();
3726
3845
  filesPicker.value = "";
3727
3846
  if (instance && pathKey && !state.config.readonly) {
@@ -3753,10 +3872,17 @@ function validatePickedResource(resource, allowedExtensions, allowedMimes, maxSi
3753
3872
  }
3754
3873
  if (!isMimeAllowed(resource.type, allowedMimes)) {
3755
3874
  const mimes = allowedMimes.join(", ");
3756
- return t("invalidFileMime", state, { name: resource.name, type: resource.type, mimes });
3875
+ return t("invalidFileMime", state, {
3876
+ name: resource.name,
3877
+ type: resource.type,
3878
+ mimes
3879
+ });
3757
3880
  }
3758
3881
  if (!isSizeWithinLimit(resource.size, maxSizeMB)) {
3759
- return t("fileTooLarge", state, { name: resource.name, maxSize: maxSizeMB });
3882
+ return t("fileTooLarge", state, {
3883
+ name: resource.name,
3884
+ maxSize: maxSizeMB
3885
+ });
3760
3886
  }
3761
3887
  return null;
3762
3888
  }
@@ -3815,7 +3941,13 @@ async function handleLibraryPickMulti(state, element, wrapper, fieldPath, resour
3815
3941
  return true;
3816
3942
  });
3817
3943
  const validItems = deduped.filter((r) => {
3818
- const err = validatePickedResource(r, allowedExtensions, allowedMimes, maxSizeMB, state);
3944
+ const err = validatePickedResource(
3945
+ r,
3946
+ allowedExtensions,
3947
+ allowedMimes,
3948
+ maxSizeMB,
3949
+ state
3950
+ );
3819
3951
  return err === null;
3820
3952
  });
3821
3953
  const freshRemaining = maxCount === Infinity ? validItems.length : Math.max(0, maxCount - resourceIds.length);
@@ -3859,14 +3991,22 @@ async function handleLibraryPickSingle(state, element, container, fileWrapper, p
3859
3991
  }
3860
3992
  if (picked.length === 0) return;
3861
3993
  const first = picked[0];
3862
- const validationError = validatePickedResource(first, allowedExtensions, allowedMimes, maxSizeMB, state);
3994
+ const validationError = validatePickedResource(
3995
+ first,
3996
+ allowedExtensions,
3997
+ allowedMimes,
3998
+ maxSizeMB,
3999
+ state
4000
+ );
3863
4001
  if (validationError !== null) {
3864
4002
  showFileError(container, validationError);
3865
4003
  return;
3866
4004
  }
3867
4005
  clearFileError(container);
3868
4006
  registerPickedResource(first, state);
3869
- let hiddenInput = fileWrapper.querySelector('input[type="hidden"]');
4007
+ let hiddenInput = fileWrapper.querySelector(
4008
+ 'input[type="hidden"]'
4009
+ );
3870
4010
  if (!hiddenInput) {
3871
4011
  hiddenInput = document.createElement("input");
3872
4012
  hiddenInput.type = "hidden";
@@ -4025,7 +4165,8 @@ function renderSingleFileFilled(fileContainer, resourceId, state, deps, extras)
4025
4165
  grid.appendChild(tile);
4026
4166
  fileContainer.className = "file-preview-container";
4027
4167
  fileContainer.removeAttribute("style");
4028
- while (fileContainer.firstChild) fileContainer.removeChild(fileContainer.firstChild);
4168
+ while (fileContainer.firstChild)
4169
+ fileContainer.removeChild(fileContainer.firstChild);
4029
4170
  fileContainer.appendChild(outer);
4030
4171
  }
4031
4172
  function buildMultiAddTile(state, hasLibrary, onUploadClick, onLibraryClick, isDragOver = false) {
@@ -4106,7 +4247,9 @@ function buildMetaLine(state, element, ridCount, maxCount, canClearAll, onClearA
4106
4247
  metaText.appendChild(sizeSpan);
4107
4248
  metaText.appendChild(buildMetaDot());
4108
4249
  }
4109
- const exts = getAllowedExtensions(element.accept);
4250
+ const exts = getAllowedExtensions(
4251
+ element.accept
4252
+ );
4110
4253
  if (exts.length > 0) {
4111
4254
  const fmtSpan = document.createElement("span");
4112
4255
  fmtSpan.className = "fb-meta-mono";
@@ -4352,7 +4495,9 @@ function renderFileElementEdit(element, ctx, wrapper, pathKey) {
4352
4495
  setupDragAndDrop(container, handlers.dragHandler);
4353
4496
  },
4354
4497
  onRemove() {
4355
- const hiddenInput = fileWrapper.querySelector('input[type="hidden"]');
4498
+ const hiddenInput = fileWrapper.querySelector(
4499
+ 'input[type="hidden"]'
4500
+ );
4356
4501
  const currentRid = hiddenInput?.value;
4357
4502
  if (currentRid) {
4358
4503
  releaseLocalFileUrl(state.resourceIndex.get(currentRid)?.file);
@@ -4362,7 +4507,9 @@ function renderFileElementEdit(element, ctx, wrapper, pathKey) {
4362
4507
  }
4363
4508
  };
4364
4509
  const buildSingleExtras = () => {
4365
- const hasLibrary = Boolean(state.config.pickExistingFiles && !element.disableLibrary);
4510
+ const hasLibrary = Boolean(
4511
+ state.config.pickExistingFiles && !element.disableLibrary
4512
+ );
4366
4513
  return {
4367
4514
  replaceHandler: state.config.uploadFile ? () => picker.click() : null,
4368
4515
  libraryHandler: hasLibrary ? () => {
@@ -4374,7 +4521,13 @@ function renderFileElementEdit(element, ctx, wrapper, pathKey) {
4374
4521
  pathKey,
4375
4522
  pathKey,
4376
4523
  async (rid) => {
4377
- renderSingleFileFilled(fileContainer, rid, state, buildDeps(), buildSingleExtras());
4524
+ renderSingleFileFilled(
4525
+ fileContainer,
4526
+ rid,
4527
+ state,
4528
+ buildDeps(),
4529
+ buildSingleExtras()
4530
+ );
4378
4531
  },
4379
4532
  ctx.instance
4380
4533
  ).catch((err) => {
@@ -4390,14 +4543,21 @@ function renderFileElementEdit(element, ctx, wrapper, pathKey) {
4390
4543
  setupDrop: handlers.setupDrop,
4391
4544
  onRemove: handlers.onRemove,
4392
4545
  onAfterUpload: (container, rid) => {
4393
- renderSingleFileFilled(container, rid, state, buildDeps(), buildSingleExtras());
4546
+ renderSingleFileFilled(
4547
+ container,
4548
+ rid,
4549
+ state,
4550
+ buildDeps(),
4551
+ buildSingleExtras()
4552
+ );
4394
4553
  }
4395
4554
  });
4396
4555
  const renderEmptySingleState = () => {
4397
4556
  ensureFileStyles();
4398
4557
  fileContainer.className = "file-preview-container";
4399
4558
  fileContainer.removeAttribute("style");
4400
- while (fileContainer.firstChild) fileContainer.removeChild(fileContainer.firstChild);
4559
+ while (fileContainer.firstChild)
4560
+ fileContainer.removeChild(fileContainer.firstChild);
4401
4561
  const onLibraryClick = buildSingleExtras().libraryHandler;
4402
4562
  const wideTile = buildWideTile(
4403
4563
  state,
@@ -4538,7 +4698,13 @@ function renderFilesElementEdit(element, ctx, wrapper, pathKey) {
4538
4698
  setupMultiFileEditMode(element, ctx, wrapper, pathKey, Infinity);
4539
4699
  }
4540
4700
  function renderMultipleFileElementEdit(element, ctx, wrapper, pathKey) {
4541
- setupMultiFileEditMode(element, ctx, wrapper, pathKey, element.maxCount ?? Infinity);
4701
+ setupMultiFileEditMode(
4702
+ element,
4703
+ ctx,
4704
+ wrapper,
4705
+ pathKey,
4706
+ element.maxCount ?? Infinity
4707
+ );
4542
4708
  }
4543
4709
 
4544
4710
  // src/components/file/validate.ts
@@ -4717,7 +4883,11 @@ function renderMultiFileReadonly(rids, state, wrapper, pathKey, _marginTop) {
4717
4883
  const placeholder = placeholders[i];
4718
4884
  const meta = state.resourceIndex.get(resourceId);
4719
4885
  renderFilePreviewReadonly(resourceId, state, meta?.name).then((tile) => {
4720
- tile.classList.add("fb-readonly-tile", "fb-checker", "fb-tile-resource");
4886
+ tile.classList.add(
4887
+ "fb-readonly-tile",
4888
+ "fb-checker",
4889
+ "fb-tile-resource"
4890
+ );
4721
4891
  tile.dataset.resourceId = resourceId;
4722
4892
  placeholder.replaceWith(tile);
4723
4893
  }).catch(() => {
@@ -5067,51 +5237,25 @@ function renderMultipleColourElement(element, ctx, wrapper, pathKey) {
5067
5237
  removeBtn.style.pointerEvents = disabled ? "none" : "auto";
5068
5238
  });
5069
5239
  }
5070
- let addRow = null;
5071
- let countDisplay = null;
5240
+ let addUpdate = null;
5072
5241
  if (!readonly) {
5073
- addRow = document.createElement("div");
5074
- addRow.className = "flex items-center gap-3 mt-2";
5075
- const addBtn = document.createElement("button");
5076
- addBtn.type = "button";
5077
- addBtn.className = "add-colour-btn px-3 py-1 rounded";
5078
- addBtn.style.cssText = `
5079
- color: var(--fb-primary-color);
5080
- border: var(--fb-border-width) solid var(--fb-primary-color);
5081
- background-color: transparent;
5082
- font-size: var(--fb-font-size);
5083
- transition: all var(--fb-transition-duration);
5084
- `;
5085
- addBtn.textContent = "+";
5086
- addBtn.addEventListener("mouseenter", () => {
5087
- addBtn.style.backgroundColor = "var(--fb-background-hover-color)";
5088
- });
5089
- addBtn.addEventListener("mouseleave", () => {
5090
- addBtn.style.backgroundColor = "transparent";
5091
- });
5092
- addBtn.onclick = () => {
5093
- const defaultColour = element.default || "#000000";
5094
- values.push(defaultColour);
5095
- addColourItem(defaultColour);
5096
- updateAddButton();
5097
- updateRemoveButtons();
5098
- };
5099
- countDisplay = document.createElement("span");
5100
- countDisplay.className = "text-sm text-gray-500";
5101
- addRow.appendChild(addBtn);
5102
- addRow.appendChild(countDisplay);
5103
- wrapper.appendChild(addRow);
5242
+ const handle = createAddItemRow(
5243
+ "colour",
5244
+ () => {
5245
+ const defaultColour = element.default || "#000000";
5246
+ values.push(defaultColour);
5247
+ addColourItem(defaultColour);
5248
+ updateAddButton();
5249
+ updateRemoveButtons();
5250
+ },
5251
+ { label: element.addLabel }
5252
+ );
5253
+ addUpdate = handle.update;
5254
+ mountCounterInLabel(wrapper, handle.counter);
5255
+ wrapper.appendChild(handle.row);
5104
5256
  }
5105
5257
  function updateAddButton() {
5106
- if (!addRow || !countDisplay) return;
5107
- const addBtn = addRow.querySelector(".add-colour-btn");
5108
- if (addBtn) {
5109
- const disabled = values.length >= maxCount;
5110
- addBtn.disabled = disabled;
5111
- addBtn.style.opacity = disabled ? "0.5" : "1";
5112
- addBtn.style.pointerEvents = disabled ? "none" : "auto";
5113
- }
5114
- countDisplay.textContent = `${values.length}/${maxCount === Infinity ? "\u221E" : maxCount}`;
5258
+ if (addUpdate) addUpdate(values.length, maxCount);
5115
5259
  }
5116
5260
  values.forEach((value) => addColourItem(value));
5117
5261
  updateAddButton();
@@ -5547,50 +5691,24 @@ function renderMultipleSliderElement(element, ctx, wrapper, pathKey) {
5547
5691
  removeBtn.style.pointerEvents = disabled ? "none" : "auto";
5548
5692
  });
5549
5693
  }
5550
- let addRow = null;
5551
- let countDisplay = null;
5694
+ let addUpdate = null;
5552
5695
  if (!readonly) {
5553
- addRow = document.createElement("div");
5554
- addRow.className = "flex items-center gap-3 mt-2";
5555
- const addBtn = document.createElement("button");
5556
- addBtn.type = "button";
5557
- addBtn.className = "add-slider-btn px-3 py-1 rounded";
5558
- addBtn.style.cssText = `
5559
- color: var(--fb-primary-color);
5560
- border: var(--fb-border-width) solid var(--fb-primary-color);
5561
- background-color: transparent;
5562
- font-size: var(--fb-font-size);
5563
- transition: all var(--fb-transition-duration);
5564
- `;
5565
- addBtn.textContent = "+";
5566
- addBtn.addEventListener("mouseenter", () => {
5567
- addBtn.style.backgroundColor = "var(--fb-background-hover-color)";
5568
- });
5569
- addBtn.addEventListener("mouseleave", () => {
5570
- addBtn.style.backgroundColor = "transparent";
5571
- });
5572
- addBtn.onclick = () => {
5573
- values.push(defaultValue);
5574
- addSliderItem(defaultValue);
5575
- updateAddButton();
5576
- updateRemoveButtons();
5577
- };
5578
- countDisplay = document.createElement("span");
5579
- countDisplay.className = "text-sm text-gray-500";
5580
- addRow.appendChild(addBtn);
5581
- addRow.appendChild(countDisplay);
5582
- wrapper.appendChild(addRow);
5696
+ const handle = createAddItemRow(
5697
+ "slider",
5698
+ () => {
5699
+ values.push(defaultValue);
5700
+ addSliderItem(defaultValue);
5701
+ updateAddButton();
5702
+ updateRemoveButtons();
5703
+ },
5704
+ { label: element.addLabel }
5705
+ );
5706
+ addUpdate = handle.update;
5707
+ mountCounterInLabel(wrapper, handle.counter);
5708
+ wrapper.appendChild(handle.row);
5583
5709
  }
5584
5710
  function updateAddButton() {
5585
- if (!addRow || !countDisplay) return;
5586
- const addBtn = addRow.querySelector(".add-slider-btn");
5587
- if (addBtn) {
5588
- const disabled = values.length >= maxCount;
5589
- addBtn.disabled = disabled;
5590
- addBtn.style.opacity = disabled ? "0.5" : "1";
5591
- addBtn.style.pointerEvents = disabled ? "none" : "auto";
5592
- }
5593
- countDisplay.textContent = `${values.length}/${maxCount === Infinity ? "\u221E" : maxCount}`;
5711
+ if (addUpdate) addUpdate(values.length, maxCount);
5594
5712
  }
5595
5713
  values.forEach((value) => addSliderItem(value));
5596
5714
  updateAddButton();
@@ -5937,14 +6055,53 @@ function getChildWrapperClass(isSlides, columns) {
5937
6055
  const cols = columns || 1;
5938
6056
  return cols === 1 ? "space-y-2" : `grid grid-cols-${cols} gap-2`;
5939
6057
  }
6058
+ function mountRemoveButton(item, onRemove) {
6059
+ const rem = document.createElement("button");
6060
+ rem.type = "button";
6061
+ rem.className = "fb-item-remove";
6062
+ rem.style.cssText = `
6063
+ width: 22px;
6064
+ height: 22px;
6065
+ display: inline-flex;
6066
+ align-items: center;
6067
+ justify-content: center;
6068
+ padding: 0;
6069
+ line-height: 1;
6070
+ font-size: 14px;
6071
+ color: var(--fb-error-color);
6072
+ background-color: transparent;
6073
+ border: 0;
6074
+ border-radius: 4px;
6075
+ cursor: pointer;
6076
+ flex-shrink: 0;
6077
+ transition: background-color var(--fb-transition-duration);
6078
+ `;
6079
+ rem.textContent = "\u2715";
6080
+ rem.addEventListener("mouseenter", () => {
6081
+ rem.style.backgroundColor = "var(--fb-background-hover-color)";
6082
+ });
6083
+ rem.addEventListener("mouseleave", () => {
6084
+ rem.style.backgroundColor = "transparent";
6085
+ });
6086
+ rem.onclick = onRemove;
6087
+ const labelRow = item.querySelector("[data-fb-label-row]");
6088
+ if (labelRow) {
6089
+ rem.style.marginLeft = "auto";
6090
+ labelRow.appendChild(rem);
6091
+ return;
6092
+ }
6093
+ rem.style.position = "absolute";
6094
+ rem.style.top = "8px";
6095
+ rem.style.right = "8px";
6096
+ item.style.position = "relative";
6097
+ item.appendChild(rem);
6098
+ }
5940
6099
  function renderMultipleContainerElement(element, ctx, wrapper, _pathKey) {
5941
6100
  const state = ctx.state;
5942
6101
  const containerIsReadonly = isElementReadonly(element, state, ctx);
5943
6102
  const childInheritedReadonly = containerIsReadonly || ctx.inheritedReadonly;
5944
6103
  const containerWrap = document.createElement("div");
5945
6104
  containerWrap.className = "border border-gray-200 rounded-lg p-2 bg-gray-50";
5946
- const countDisplay = document.createElement("span");
5947
- countDisplay.className = "text-sm text-gray-500";
5948
6105
  const itemsWrap = document.createElement("div");
5949
6106
  const isSlides = element.displayMode === "slides";
5950
6107
  if (isSlides) {
@@ -5966,92 +6123,64 @@ function renderMultipleContainerElement(element, ctx, wrapper, _pathKey) {
5966
6123
  const pre = Array.isArray(ctx.prefill?.[element.key]) ? ctx.prefill[element.key] : null;
5967
6124
  const childDefaults = extractChildDefaults(element.elements);
5968
6125
  const countItems = () => itemsWrap.querySelectorAll(":scope > .containerItem").length;
5969
- const createAddButton = () => {
5970
- const add = document.createElement("button");
5971
- add.type = "button";
5972
- add.className = "add-container-btn px-3 py-1 rounded";
5973
- add.style.cssText = `
5974
- color: var(--fb-primary-color);
5975
- border: var(--fb-border-width) solid var(--fb-primary-color);
5976
- background-color: transparent;
5977
- font-size: var(--fb-font-size);
5978
- transition: all var(--fb-transition-duration);
5979
- `;
5980
- add.textContent = "+";
5981
- add.addEventListener("mouseenter", () => {
5982
- add.style.backgroundColor = "var(--fb-background-hover-color)";
5983
- });
5984
- add.addEventListener("mouseleave", () => {
5985
- add.style.backgroundColor = "transparent";
5986
- });
5987
- add.onclick = () => {
5988
- if (countItems() < max) {
5989
- const idx = countItems();
5990
- const currentFormData = state.formRoot ? extractRootFormData(state.formRoot) : {};
5991
- const subCtx = {
5992
- state: ctx.state,
5993
- path: pathJoin(ctx.path, `${element.key}[${idx}]`),
5994
- prefill: childDefaults,
5995
- // Defaults for enableIf evaluation
5996
- formData: currentFormData,
5997
- // Current root data from DOM for enableIf
5998
- inheritedReadonly: childInheritedReadonly
5999
- };
6000
- const item = document.createElement("div");
6001
- item.className = "containerItem border border-gray-300 rounded-lg p-2 bg-white";
6002
- item.setAttribute("data-container-item", `${element.key}[${idx}]`);
6003
- const childWrapper = document.createElement("div");
6004
- childWrapper.className = getChildWrapperClass(isSlides, element.columns);
6005
- element.elements.forEach((child) => {
6006
- if (child.type !== "markdown" && (child.hidden || child.type === "hidden")) {
6007
- childWrapper.appendChild(
6008
- createHiddenInput(
6009
- pathJoin(subCtx.path, child.key),
6010
- ("default" in child ? child.default : null) ?? null
6011
- )
6012
- );
6013
- } else {
6014
- childWrapper.appendChild(renderElement(child, subCtx));
6015
- }
6016
- });
6017
- item.appendChild(childWrapper);
6018
- if (!containerIsReadonly) {
6019
- const rem = document.createElement("button");
6020
- rem.type = "button";
6021
- rem.className = "absolute top-2 right-2 px-2 py-1 rounded";
6022
- rem.style.cssText = `
6023
- color: var(--fb-error-color);
6024
- background-color: transparent;
6025
- transition: background-color var(--fb-transition-duration);
6026
- `;
6027
- rem.textContent = "\u2715";
6028
- rem.addEventListener("mouseenter", () => {
6029
- rem.style.backgroundColor = "var(--fb-background-hover-color)";
6030
- });
6031
- rem.addEventListener("mouseleave", () => {
6032
- rem.style.backgroundColor = "transparent";
6033
- });
6034
- rem.onclick = () => handleRemoveItem(item);
6035
- item.style.position = "relative";
6036
- item.appendChild(rem);
6037
- }
6038
- itemsWrap.appendChild(item);
6039
- updateAddButton();
6040
- }
6126
+ const handleAddItem = () => {
6127
+ if (countItems() >= max) return;
6128
+ const idx = countItems();
6129
+ const currentFormData = state.formRoot ? extractRootFormData(state.formRoot) : {};
6130
+ const subCtx = {
6131
+ state: ctx.state,
6132
+ path: pathJoin(ctx.path, `${element.key}[${idx}]`),
6133
+ prefill: childDefaults,
6134
+ // Defaults for enableIf evaluation
6135
+ formData: currentFormData,
6136
+ // Current root data from DOM for enableIf
6137
+ inheritedReadonly: childInheritedReadonly
6041
6138
  };
6042
- return add;
6139
+ const item = document.createElement("div");
6140
+ item.className = "containerItem border border-gray-300 rounded-lg p-2 bg-white";
6141
+ item.setAttribute("data-container-item", `${element.key}[${idx}]`);
6142
+ const childWrapper = document.createElement("div");
6143
+ childWrapper.className = getChildWrapperClass(isSlides, element.columns);
6144
+ element.elements.forEach((child) => {
6145
+ if (child.type !== "markdown" && (child.hidden || child.type === "hidden")) {
6146
+ childWrapper.appendChild(
6147
+ createHiddenInput(
6148
+ pathJoin(subCtx.path, child.key),
6149
+ ("default" in child ? child.default : null) ?? null
6150
+ )
6151
+ );
6152
+ } else {
6153
+ childWrapper.appendChild(renderElement(child, subCtx));
6154
+ }
6155
+ });
6156
+ item.appendChild(childWrapper);
6157
+ if (!containerIsReadonly) {
6158
+ mountRemoveButton(item, () => handleRemoveItem(item));
6159
+ }
6160
+ if (slideAddTile && slideAddTile.parentElement === itemsWrap) {
6161
+ itemsWrap.insertBefore(item, slideAddTile);
6162
+ } else {
6163
+ itemsWrap.appendChild(item);
6164
+ }
6165
+ updateAddButton();
6043
6166
  };
6044
- const updateAddButton = () => {
6045
- const currentCount = countItems();
6046
- const existingAddBtn = containerWrap.querySelector(
6047
- ".add-container-btn"
6167
+ let slideAddTile = null;
6168
+ let slideAddUpdate = null;
6169
+ let pillAddUpdate = null;
6170
+ const syncSlideTileSize = () => {
6171
+ if (!slideAddTile) return;
6172
+ const firstSlide = itemsWrap.querySelector(
6173
+ ":scope > .containerItem"
6048
6174
  );
6049
- if (existingAddBtn) {
6050
- existingAddBtn.disabled = currentCount >= max;
6051
- existingAddBtn.style.opacity = currentCount >= max ? "0.5" : "1";
6052
- existingAddBtn.style.pointerEvents = currentCount >= max ? "none" : "auto";
6175
+ if (firstSlide && firstSlide.offsetHeight > 0) {
6176
+ slideAddTile.style.minHeight = `${firstSlide.offsetHeight}px`;
6053
6177
  }
6054
- countDisplay.textContent = `${currentCount}/${max === Infinity ? "\u221E" : max}`;
6178
+ };
6179
+ const updateAddButton = () => {
6180
+ const currentCount = countItems();
6181
+ if (slideAddUpdate) slideAddUpdate(currentCount, max);
6182
+ if (pillAddUpdate) pillAddUpdate(currentCount, max);
6183
+ if (slideAddTile) syncSlideTileSize();
6055
6184
  };
6056
6185
  const handleRemoveItem = (item) => {
6057
6186
  item.remove();
@@ -6095,24 +6224,7 @@ function renderMultipleContainerElement(element, ctx, wrapper, _pathKey) {
6095
6224
  });
6096
6225
  item.appendChild(childWrapper);
6097
6226
  if (!containerIsReadonly) {
6098
- const rem = document.createElement("button");
6099
- rem.type = "button";
6100
- rem.className = "absolute top-2 right-2 px-2 py-1 rounded";
6101
- rem.style.cssText = `
6102
- color: var(--fb-error-color);
6103
- background-color: transparent;
6104
- transition: background-color var(--fb-transition-duration);
6105
- `;
6106
- rem.textContent = "\u2715";
6107
- rem.addEventListener("mouseenter", () => {
6108
- rem.style.backgroundColor = "var(--fb-background-hover-color)";
6109
- });
6110
- rem.addEventListener("mouseleave", () => {
6111
- rem.style.backgroundColor = "transparent";
6112
- });
6113
- rem.onclick = () => handleRemoveItem(item);
6114
- item.style.position = "relative";
6115
- item.appendChild(rem);
6227
+ mountRemoveButton(item, () => handleRemoveItem(item));
6116
6228
  }
6117
6229
  itemsWrap.appendChild(item);
6118
6230
  });
@@ -6156,41 +6268,43 @@ function renderMultipleContainerElement(element, ctx, wrapper, _pathKey) {
6156
6268
  }
6157
6269
  });
6158
6270
  item.appendChild(childWrapper);
6159
- const rem = document.createElement("button");
6160
- rem.type = "button";
6161
- rem.className = "absolute top-2 right-2 px-2 py-1 rounded";
6162
- rem.style.cssText = `
6163
- color: var(--fb-error-color);
6164
- background-color: transparent;
6165
- transition: background-color var(--fb-transition-duration);
6166
- `;
6167
- rem.textContent = "\u2715";
6168
- rem.addEventListener("mouseenter", () => {
6169
- rem.style.backgroundColor = "var(--fb-background-hover-color)";
6170
- });
6171
- rem.addEventListener("mouseleave", () => {
6172
- rem.style.backgroundColor = "transparent";
6173
- });
6174
- rem.onclick = () => {
6271
+ mountRemoveButton(item, () => {
6175
6272
  if (countItems() > min) {
6176
6273
  handleRemoveItem(item);
6177
6274
  }
6178
- };
6179
- item.style.position = "relative";
6180
- item.appendChild(rem);
6275
+ });
6181
6276
  itemsWrap.appendChild(item);
6182
6277
  }
6183
6278
  }
6184
6279
  containerWrap.appendChild(itemsWrap);
6185
6280
  if (!containerIsReadonly) {
6186
- const addRow = document.createElement("div");
6187
- addRow.className = "flex items-center gap-3 mt-2";
6188
- addRow.appendChild(createAddButton());
6189
- addRow.appendChild(countDisplay);
6190
- containerWrap.appendChild(addRow);
6281
+ if (isSlides) {
6282
+ itemsWrap.style.alignItems = "stretch";
6283
+ const handle = createSlideAddTile(handleAddItem, {
6284
+ label: element.addLabel
6285
+ });
6286
+ slideAddTile = handle.tile;
6287
+ slideAddUpdate = handle.update;
6288
+ mountCounterInLabel(wrapper, handle.counter);
6289
+ itemsWrap.appendChild(handle.tile);
6290
+ } else {
6291
+ const handle = createAddItemRow("container", handleAddItem, {
6292
+ label: element.addLabel
6293
+ });
6294
+ pillAddUpdate = handle.update;
6295
+ mountCounterInLabel(wrapper, handle.counter);
6296
+ containerWrap.appendChild(handle.row);
6297
+ }
6191
6298
  }
6192
6299
  updateAddButton();
6193
6300
  wrapper.appendChild(containerWrap);
6301
+ if (slideAddTile) {
6302
+ if (typeof requestAnimationFrame === "function") {
6303
+ requestAnimationFrame(syncSlideTileSize);
6304
+ } else {
6305
+ syncSlideTileSize();
6306
+ }
6307
+ }
6194
6308
  }
6195
6309
  var validateElementFunc = null;
6196
6310
  function setValidateElement(fn) {
@@ -9085,10 +9199,7 @@ var TAGS = {
9085
9199
  "-": ["<hr />"]
9086
9200
  };
9087
9201
  function outdent(str) {
9088
- return str.replace(
9089
- RegExp("^" + (str.match(/^(\t| )+/) || "")[0], "gm"),
9090
- ""
9091
- );
9202
+ return str.replace(RegExp("^" + (str.match(/^(\t| )+/) || "")[0], "gm"), "");
9092
9203
  }
9093
9204
  function encodeAttr(str) {
9094
9205
  return (str + "").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
@@ -9251,12 +9362,7 @@ function ensureMarkdownStyles() {
9251
9362
  `;
9252
9363
  document.head.appendChild(style);
9253
9364
  }
9254
- var ANCHOR_DANGEROUS_SCHEMES = [
9255
- "javascript:",
9256
- "data:",
9257
- "vbscript:",
9258
- "blob:"
9259
- ];
9365
+ var ANCHOR_DANGEROUS_SCHEMES = ["javascript:", "data:", "vbscript:", "blob:"];
9260
9366
  var IMG_DANGEROUS_SCHEMES = ["javascript:", "vbscript:", "blob:"];
9261
9367
  function isImgSrcDangerous(normalized) {
9262
9368
  if (IMG_DANGEROUS_SCHEMES.some((scheme) => normalized.startsWith(scheme))) {
@@ -9577,6 +9683,7 @@ function createInfoButton(element) {
9577
9683
  function createLabelContainer(element) {
9578
9684
  const label = document.createElement("div");
9579
9685
  label.className = "flex items-center mb-1";
9686
+ label.dataset.fbLabelRow = "";
9580
9687
  const title = createFieldLabel(element);
9581
9688
  label.appendChild(title);
9582
9689
  if (element.description || element.hint) {
@@ -9794,6 +9901,7 @@ var defaultConfig = {
9794
9901
  invalidFileExtension: 'File "{name}" has unsupported format. Allowed: {formats}',
9795
9902
  invalidFileMime: 'File "{name}": file type {type} not allowed (allowed: {mimes})',
9796
9903
  fileTooLarge: 'File "{name}" exceeds maximum size of {maxSize}MB',
9904
+ uploadFailed: 'Failed to upload "{name}": {error}',
9797
9905
  filesLimitExceeded: "{skipped} file(s) skipped: maximum {max} files allowed",
9798
9906
  unsupportedFieldType: "Unsupported field type: {type}",
9799
9907
  invalidOption: "Invalid option",
@@ -9869,6 +9977,7 @@ var defaultConfig = {
9869
9977
  invalidFileExtension: '\u0424\u0430\u0439\u043B "{name}" \u0438\u043C\u0435\u0435\u0442 \u043D\u0435\u043F\u043E\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u043C\u044B\u0439 \u0444\u043E\u0440\u043C\u0430\u0442. \u0414\u043E\u043F\u0443\u0441\u0442\u0438\u043C\u044B\u0435: {formats}',
9870
9978
  invalidFileMime: '\u0424\u0430\u0439\u043B "{name}": \u0442\u0438\u043F \u0444\u0430\u0439\u043B\u0430 {type} \u043D\u0435 \u0440\u0430\u0437\u0440\u0435\u0448\u0451\u043D (\u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043D\u044B: {mimes})',
9871
9979
  fileTooLarge: '\u0424\u0430\u0439\u043B "{name}" \u043F\u0440\u0435\u0432\u044B\u0448\u0430\u0435\u0442 \u043C\u0430\u043A\u0441\u0438\u043C\u0430\u043B\u044C\u043D\u044B\u0439 \u0440\u0430\u0437\u043C\u0435\u0440 {maxSize}\u041C\u0411',
9980
+ uploadFailed: '\u041D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u0437\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044C "{name}": {error}',
9872
9981
  filesLimitExceeded: "{skipped} \u0444\u0430\u0439\u043B(\u043E\u0432) \u043F\u0440\u043E\u043F\u0443\u0449\u0435\u043D\u043E: \u043C\u0430\u043A\u0441\u0438\u043C\u0443\u043C {max} \u0444\u0430\u0439\u043B\u043E\u0432",
9873
9982
  unsupportedFieldType: "\u041D\u0435\u043F\u043E\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u043C\u044B\u0439 \u0442\u0438\u043F \u043F\u043E\u043B\u044F: {type}",
9874
9983
  invalidOption: "\u041D\u0435\u0434\u043E\u043F\u0443\u0441\u0442\u0438\u043C\u043E\u0435 \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0435",
@@ -10091,29 +10200,6 @@ var exampleThemes = {
10091
10200
  }
10092
10201
  };
10093
10202
 
10094
- // src/utils/styles.ts
10095
- function applyActionButtonStyles(button, isFormLevel = false) {
10096
- button.style.cssText = `
10097
- background-color: var(--fb-action-bg-color);
10098
- color: var(--fb-action-text-color);
10099
- border: var(--fb-border-width) solid var(--fb-action-border-color);
10100
- padding: ${isFormLevel ? "0.5rem 1rem" : "0.5rem 0.75rem"};
10101
- font-size: var(--fb-font-size);
10102
- font-weight: var(--fb-font-weight-medium);
10103
- border-radius: var(--fb-border-radius);
10104
- transition: all var(--fb-transition-duration);
10105
- box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
10106
- `;
10107
- button.addEventListener("mouseenter", () => {
10108
- button.style.backgroundColor = "var(--fb-action-hover-bg-color)";
10109
- button.style.borderColor = "var(--fb-action-hover-border-color)";
10110
- });
10111
- button.addEventListener("mouseleave", () => {
10112
- button.style.backgroundColor = "var(--fb-action-bg-color)";
10113
- button.style.borderColor = "var(--fb-action-border-color)";
10114
- });
10115
- }
10116
-
10117
10203
  // src/components/registry.ts
10118
10204
  function validateHiddenElement(element, key, context) {
10119
10205
  const { scopeRoot } = context;