@dmitryvim/form-builder 0.2.32 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser/formbuilder.min.js +493 -230
- package/dist/browser/formbuilder.v0.3.0.min.js +1411 -0
- package/dist/cjs/index.cjs +1563 -871
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/esm/index.js +1551 -868
- package/dist/esm/index.js.map +1 -1
- package/dist/form-builder.js +493 -230
- package/dist/types/components/boolean.d.ts +4 -0
- package/dist/types/components/colour.d.ts +1 -0
- package/dist/types/components/file/upload.d.ts +4 -0
- package/dist/types/components/file.d.ts +2 -2
- package/dist/types/components/text.d.ts +12 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/instance/FormBuilderInstance.d.ts +14 -1
- package/dist/types/styles/theme.d.ts +47 -0
- package/dist/types/types/component-operations.d.ts +7 -0
- package/dist/types/types/config.d.ts +1 -0
- package/dist/types/types/index.d.ts +1 -1
- package/dist/types/types/schema.d.ts +48 -1
- package/dist/types/utils/styles.d.ts +81 -0
- package/package.json +1 -1
- package/dist/browser/formbuilder.v0.2.32.min.js +0 -1148
package/dist/cjs/index.cjs
CHANGED
|
@@ -406,47 +406,334 @@ function deepEqual(a, b) {
|
|
|
406
406
|
return a === b;
|
|
407
407
|
}
|
|
408
408
|
|
|
409
|
+
// src/utils/styles.ts
|
|
410
|
+
function clearFieldError(input) {
|
|
411
|
+
const name = input.getAttribute("name");
|
|
412
|
+
if (!name) return;
|
|
413
|
+
const doc = input.ownerDocument || document;
|
|
414
|
+
const errorNode = doc.getElementById(`error-${name}`);
|
|
415
|
+
if (errorNode) errorNode.remove();
|
|
416
|
+
}
|
|
417
|
+
var BIN_ICON_SVG = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="3 6 5 6 21 6"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6"/><path d="M10 11v6"/><path d="M14 11v6"/><path d="M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/></svg>';
|
|
418
|
+
function ensureThemingHooks(doc) {
|
|
419
|
+
if (doc.head.querySelector("[data-fb-theming-hooks]")) return;
|
|
420
|
+
const style = doc.createElement("style");
|
|
421
|
+
style.setAttribute("data-fb-theming-hooks", "");
|
|
422
|
+
style.textContent = `
|
|
423
|
+
[data-fb-slide-card] {
|
|
424
|
+
background: var(--fb-slide-card-bg);
|
|
425
|
+
box-shadow: var(--fb-slide-card-shadow);
|
|
426
|
+
border-radius: var(--fb-slide-card-radius);
|
|
427
|
+
min-height: var(--fb-slide-card-min-height);
|
|
428
|
+
padding: var(--fb-slide-card-padding);
|
|
429
|
+
}
|
|
430
|
+
[data-fb-label-row] > label {
|
|
431
|
+
font-size: var(--fb-label-section-font-size);
|
|
432
|
+
letter-spacing: var(--fb-label-section-letter-spacing);
|
|
433
|
+
text-transform: var(--fb-label-section-text-transform);
|
|
434
|
+
}
|
|
435
|
+
/* Per-item remove (trash) button used by multi-container items. Shares the
|
|
436
|
+
same faint-on-rest, error-on-hover palette as .fb-chip-remove. */
|
|
437
|
+
.fb-item-remove {
|
|
438
|
+
color: var(--fb-text-faint-color, #94a3b8);
|
|
439
|
+
background-color: transparent;
|
|
440
|
+
transition: color var(--fb-transition-duration), background-color var(--fb-transition-duration);
|
|
441
|
+
}
|
|
442
|
+
.fb-item-remove:hover {
|
|
443
|
+
color: var(--fb-error-color);
|
|
444
|
+
background-color: var(--fb-background-hover-color);
|
|
445
|
+
}
|
|
446
|
+
`;
|
|
447
|
+
doc.head.appendChild(style);
|
|
448
|
+
}
|
|
449
|
+
function applyAutoExpand(textarea) {
|
|
450
|
+
textarea.style.overflow = "hidden";
|
|
451
|
+
textarea.style.resize = "none";
|
|
452
|
+
const lineCount = (textarea.value.match(/\n/g) || []).length + 1;
|
|
453
|
+
textarea.rows = Math.max(1, lineCount);
|
|
454
|
+
const resize = () => {
|
|
455
|
+
if (!textarea.isConnected) return;
|
|
456
|
+
textarea.style.height = "0";
|
|
457
|
+
textarea.style.height = `${textarea.scrollHeight}px`;
|
|
458
|
+
};
|
|
459
|
+
textarea.addEventListener("input", resize);
|
|
460
|
+
setTimeout(() => {
|
|
461
|
+
if (textarea.isConnected) resize();
|
|
462
|
+
}, 0);
|
|
463
|
+
}
|
|
464
|
+
function applySingleLineMode(textarea) {
|
|
465
|
+
textarea.addEventListener("keydown", (e) => {
|
|
466
|
+
if (e.key === "Enter") {
|
|
467
|
+
e.preventDefault();
|
|
468
|
+
}
|
|
469
|
+
});
|
|
470
|
+
textarea.addEventListener("paste", (e) => {
|
|
471
|
+
var _a, _b, _c, _d;
|
|
472
|
+
const pasted = (_b = (_a = e.clipboardData) == null ? void 0 : _a.getData("text")) != null ? _b : "";
|
|
473
|
+
if (!/[\r\n]/.test(pasted)) return;
|
|
474
|
+
e.preventDefault();
|
|
475
|
+
const cleaned = pasted.replace(/[\r\n]+/g, " ");
|
|
476
|
+
const start = (_c = textarea.selectionStart) != null ? _c : textarea.value.length;
|
|
477
|
+
const end = (_d = textarea.selectionEnd) != null ? _d : textarea.value.length;
|
|
478
|
+
const before = textarea.value.slice(0, start);
|
|
479
|
+
const after = textarea.value.slice(end);
|
|
480
|
+
textarea.value = before + cleaned + after;
|
|
481
|
+
const pos = start + cleaned.length;
|
|
482
|
+
textarea.setSelectionRange(pos, pos);
|
|
483
|
+
textarea.dispatchEvent(new Event("input", { bubbles: true }));
|
|
484
|
+
});
|
|
485
|
+
}
|
|
486
|
+
function mountCounterInLabel(wrapper, counter) {
|
|
487
|
+
const labelRow = wrapper.querySelector(
|
|
488
|
+
":scope > [data-fb-label-row]"
|
|
489
|
+
);
|
|
490
|
+
if (labelRow) labelRow.appendChild(counter);
|
|
491
|
+
}
|
|
492
|
+
function createAddItemRow(classNameSuffix, onClick, options = {}) {
|
|
493
|
+
var _a;
|
|
494
|
+
const label = (_a = options.label) != null ? _a : "";
|
|
495
|
+
const showCounter = options.showCounter !== false;
|
|
496
|
+
const row = document.createElement("div");
|
|
497
|
+
row.className = "fb-add-row mt-2";
|
|
498
|
+
row.style.cssText = "display:flex;align-items:stretch;width:100%;";
|
|
499
|
+
const button = document.createElement("button");
|
|
500
|
+
button.type = "button";
|
|
501
|
+
button.className = `add-${classNameSuffix}-btn`;
|
|
502
|
+
button.style.cssText = `
|
|
503
|
+
flex: 1 1 auto;
|
|
504
|
+
display: inline-flex;
|
|
505
|
+
align-items: center;
|
|
506
|
+
justify-content: center;
|
|
507
|
+
gap: 6px;
|
|
508
|
+
padding: 6px 10px;
|
|
509
|
+
border: 1px dashed var(--fb-primary-color);
|
|
510
|
+
border-radius: var(--fb-border-radius);
|
|
511
|
+
background: transparent;
|
|
512
|
+
color: var(--fb-primary-color);
|
|
513
|
+
font-size: var(--fb-font-size-small, var(--fb-font-size));
|
|
514
|
+
font-weight: 500;
|
|
515
|
+
font-family: var(--fb-font-family);
|
|
516
|
+
cursor: pointer;
|
|
517
|
+
transition: border-color var(--fb-transition-duration), color var(--fb-transition-duration), background-color var(--fb-transition-duration);
|
|
518
|
+
`;
|
|
519
|
+
button.textContent = label ? `+ ${label}` : "+";
|
|
520
|
+
button.addEventListener("mouseenter", () => {
|
|
521
|
+
if (button.disabled) return;
|
|
522
|
+
button.style.borderStyle = "solid";
|
|
523
|
+
button.style.backgroundColor = "var(--fb-background-hover-color)";
|
|
524
|
+
});
|
|
525
|
+
button.addEventListener("mouseleave", () => {
|
|
526
|
+
button.style.borderStyle = "dashed";
|
|
527
|
+
button.style.backgroundColor = "transparent";
|
|
528
|
+
});
|
|
529
|
+
button.onclick = onClick;
|
|
530
|
+
const counter = document.createElement("span");
|
|
531
|
+
counter.className = "fb-add-counter";
|
|
532
|
+
counter.style.cssText = `
|
|
533
|
+
margin-left: auto;
|
|
534
|
+
font-size: var(--fb-font-size-small, 0.875rem);
|
|
535
|
+
color: var(--fb-text-secondary-color);
|
|
536
|
+
font-weight: 400;
|
|
537
|
+
`;
|
|
538
|
+
if (!showCounter) counter.style.display = "none";
|
|
539
|
+
row.appendChild(button);
|
|
540
|
+
const update = (current, max) => {
|
|
541
|
+
const reached = current >= max;
|
|
542
|
+
row.style.display = reached ? "none" : "flex";
|
|
543
|
+
button.style.display = reached ? "none" : "inline-flex";
|
|
544
|
+
button.disabled = reached;
|
|
545
|
+
if (showCounter) {
|
|
546
|
+
counter.textContent = `${current}/${max === Infinity ? "\u221E" : max}`;
|
|
547
|
+
}
|
|
548
|
+
};
|
|
549
|
+
return { row, button, counter, update };
|
|
550
|
+
}
|
|
551
|
+
function createSlideAddTile(onClick, options = {}) {
|
|
552
|
+
var _a;
|
|
553
|
+
const label = (_a = options.label) != null ? _a : "";
|
|
554
|
+
const tile = document.createElement("button");
|
|
555
|
+
tile.type = "button";
|
|
556
|
+
tile.className = "add-container-btn fb-slide-add";
|
|
557
|
+
tile.style.cssText = `
|
|
558
|
+
display: flex;
|
|
559
|
+
flex-direction: column;
|
|
560
|
+
align-items: center;
|
|
561
|
+
justify-content: center;
|
|
562
|
+
gap: 12px;
|
|
563
|
+
width: 100%;
|
|
564
|
+
min-height: 180px;
|
|
565
|
+
align-self: stretch;
|
|
566
|
+
padding: 24px 16px;
|
|
567
|
+
border: 1.5px dashed var(--fb-primary-color);
|
|
568
|
+
border-radius: var(--fb-border-radius);
|
|
569
|
+
background: transparent;
|
|
570
|
+
color: var(--fb-primary-color);
|
|
571
|
+
font-size: var(--fb-font-size-small, var(--fb-font-size));
|
|
572
|
+
font-weight: 500;
|
|
573
|
+
font-family: var(--fb-font-family);
|
|
574
|
+
cursor: pointer;
|
|
575
|
+
transition: border-color var(--fb-transition-duration), color var(--fb-transition-duration), background-color var(--fb-transition-duration);
|
|
576
|
+
`;
|
|
577
|
+
const circle = document.createElement("span");
|
|
578
|
+
circle.className = "fb-slide-add-circle";
|
|
579
|
+
circle.style.cssText = `
|
|
580
|
+
display: inline-flex;
|
|
581
|
+
align-items: center;
|
|
582
|
+
justify-content: center;
|
|
583
|
+
width: 36px;
|
|
584
|
+
height: 36px;
|
|
585
|
+
border: 1px solid var(--fb-primary-color);
|
|
586
|
+
border-radius: 50%;
|
|
587
|
+
background: var(--fb-background-color);
|
|
588
|
+
font-size: 20px;
|
|
589
|
+
line-height: 1;
|
|
590
|
+
color: inherit;
|
|
591
|
+
transition: inherit;
|
|
592
|
+
`;
|
|
593
|
+
circle.textContent = "+";
|
|
594
|
+
tile.appendChild(circle);
|
|
595
|
+
if (label) {
|
|
596
|
+
const text = document.createElement("span");
|
|
597
|
+
text.textContent = label;
|
|
598
|
+
tile.appendChild(text);
|
|
599
|
+
}
|
|
600
|
+
tile.addEventListener("mouseenter", () => {
|
|
601
|
+
if (tile.disabled) return;
|
|
602
|
+
tile.style.borderStyle = "solid";
|
|
603
|
+
tile.style.backgroundColor = "var(--fb-background-hover-color)";
|
|
604
|
+
});
|
|
605
|
+
tile.addEventListener("mouseleave", () => {
|
|
606
|
+
tile.style.borderStyle = "dashed";
|
|
607
|
+
tile.style.backgroundColor = "transparent";
|
|
608
|
+
});
|
|
609
|
+
tile.onclick = onClick;
|
|
610
|
+
const counter = document.createElement("span");
|
|
611
|
+
counter.className = "fb-add-counter";
|
|
612
|
+
counter.style.cssText = `
|
|
613
|
+
margin-left: auto;
|
|
614
|
+
font-size: var(--fb-font-size-small, 0.875rem);
|
|
615
|
+
color: var(--fb-text-secondary-color);
|
|
616
|
+
font-weight: 400;
|
|
617
|
+
`;
|
|
618
|
+
const update = (current, max) => {
|
|
619
|
+
const reached = current >= max;
|
|
620
|
+
tile.style.display = reached ? "none" : "flex";
|
|
621
|
+
tile.disabled = reached;
|
|
622
|
+
counter.textContent = `${current}/${max === Infinity ? "\u221E" : max}`;
|
|
623
|
+
};
|
|
624
|
+
return { tile, counter, update };
|
|
625
|
+
}
|
|
626
|
+
function applyActionButtonStyles(button, isFormLevel = false) {
|
|
627
|
+
button.style.cssText = `
|
|
628
|
+
background-color: var(--fb-action-bg-color);
|
|
629
|
+
color: var(--fb-action-text-color);
|
|
630
|
+
border: var(--fb-border-width) solid var(--fb-action-border-color);
|
|
631
|
+
padding: ${isFormLevel ? "0.5rem 1rem" : "0.5rem 0.75rem"};
|
|
632
|
+
font-size: var(--fb-font-size);
|
|
633
|
+
font-weight: var(--fb-font-weight-medium);
|
|
634
|
+
border-radius: var(--fb-border-radius);
|
|
635
|
+
transition: all var(--fb-transition-duration);
|
|
636
|
+
box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
|
|
637
|
+
`;
|
|
638
|
+
button.addEventListener("mouseenter", () => {
|
|
639
|
+
button.style.backgroundColor = "var(--fb-action-hover-bg-color)";
|
|
640
|
+
button.style.borderColor = "var(--fb-action-hover-border-color)";
|
|
641
|
+
});
|
|
642
|
+
button.addEventListener("mouseleave", () => {
|
|
643
|
+
button.style.backgroundColor = "var(--fb-action-bg-color)";
|
|
644
|
+
button.style.borderColor = "var(--fb-action-border-color)";
|
|
645
|
+
});
|
|
646
|
+
}
|
|
647
|
+
|
|
409
648
|
// src/components/text.ts
|
|
410
|
-
function
|
|
649
|
+
function ensureChipStyles(doc) {
|
|
650
|
+
if (doc.head.querySelector("[data-fb-chip-styles]")) return;
|
|
651
|
+
const style = doc.createElement("style");
|
|
652
|
+
style.setAttribute("data-fb-chip-styles", "");
|
|
653
|
+
style.textContent = `
|
|
654
|
+
.fb-chip-list { display: flex; flex-direction: column; gap: 4px; }
|
|
655
|
+
.fb-chip {
|
|
656
|
+
display: flex;
|
|
657
|
+
align-items: center;
|
|
658
|
+
gap: 8px;
|
|
659
|
+
padding: 5px 6px 5px 10px;
|
|
660
|
+
background: var(--fb-chip-bg, var(--fb-background-color, #fff));
|
|
661
|
+
border: 1px solid var(--fb-chip-border, var(--fb-border-color, #e2e8f0));
|
|
662
|
+
border-radius: 6px;
|
|
663
|
+
position: relative;
|
|
664
|
+
transition: border-color var(--fb-transition-duration, 0.15s);
|
|
665
|
+
}
|
|
666
|
+
.fb-chip:hover { border-color: var(--fb-border-hover-color, var(--fb-border-color, #cbd5e1)); }
|
|
667
|
+
.fb-chip:focus-within { border-color: var(--fb-border-focus-color, var(--fb-primary-color, #2f5bea)); }
|
|
668
|
+
.fb-chip-dot {
|
|
669
|
+
flex: 0 0 6px;
|
|
670
|
+
width: 6px;
|
|
671
|
+
height: 6px;
|
|
672
|
+
border-radius: 50%;
|
|
673
|
+
background: var(--fb-chip-dot, var(--fb-primary-color, #2f5bea));
|
|
674
|
+
}
|
|
675
|
+
.fb-chip-input {
|
|
676
|
+
flex: 1;
|
|
677
|
+
min-width: 0;
|
|
678
|
+
padding: 2px 0;
|
|
679
|
+
border: 0;
|
|
680
|
+
outline: none;
|
|
681
|
+
background: transparent;
|
|
682
|
+
color: var(--fb-chip-text, var(--fb-text-color, inherit));
|
|
683
|
+
font-size: var(--fb-font-size, 14px);
|
|
684
|
+
font-family: var(--fb-font-family, inherit);
|
|
685
|
+
line-height: 1.4;
|
|
686
|
+
}
|
|
687
|
+
.fb-chip-input::placeholder { color: var(--fb-text-placeholder-color, #94a3b8); }
|
|
688
|
+
.fb-chip-input:read-only { color: var(--fb-text-secondary-color, #475569); }
|
|
689
|
+
.fb-chip-remove {
|
|
690
|
+
flex: 0 0 auto;
|
|
691
|
+
width: 22px;
|
|
692
|
+
height: 22px;
|
|
693
|
+
display: inline-flex;
|
|
694
|
+
align-items: center;
|
|
695
|
+
justify-content: center;
|
|
696
|
+
padding: 0;
|
|
697
|
+
border: 0;
|
|
698
|
+
border-radius: 4px;
|
|
699
|
+
background: transparent;
|
|
700
|
+
color: var(--fb-text-faint-color, #94a3b8);
|
|
701
|
+
cursor: pointer;
|
|
702
|
+
opacity: 0;
|
|
703
|
+
transition: opacity 0.12s, color 0.12s, background-color 0.12s;
|
|
704
|
+
}
|
|
705
|
+
.fb-chip:hover .fb-chip-remove,
|
|
706
|
+
.fb-chip-remove:focus-visible { opacity: 1; }
|
|
707
|
+
.fb-chip-remove:hover {
|
|
708
|
+
color: var(--fb-error-color, #dc2626);
|
|
709
|
+
background: var(--fb-background-hover-color, #f1f5f9);
|
|
710
|
+
}
|
|
711
|
+
.fb-chip-remove:disabled { opacity: 0 !important; pointer-events: none; }
|
|
712
|
+
`;
|
|
713
|
+
doc.head.appendChild(style);
|
|
714
|
+
}
|
|
715
|
+
function createCharCounter(element, input) {
|
|
411
716
|
const counter = document.createElement("span");
|
|
412
717
|
counter.className = "char-counter";
|
|
413
718
|
counter.style.cssText = `
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
719
|
+
margin-top: 4px;
|
|
720
|
+
padding-right: 12px;
|
|
721
|
+
text-align: right;
|
|
417
722
|
font-size: var(--fb-font-size-small);
|
|
418
|
-
|
|
723
|
+
line-height: 1;
|
|
724
|
+
color: var(--fb-error-color);
|
|
419
725
|
pointer-events: none;
|
|
420
|
-
|
|
421
|
-
padding: 0 4px;
|
|
726
|
+
display: none;
|
|
422
727
|
`;
|
|
423
728
|
const updateCounter = () => {
|
|
424
729
|
const len = input.value.length;
|
|
425
|
-
const min = element.minLength;
|
|
426
730
|
const max = element.maxLength;
|
|
427
|
-
if (
|
|
428
|
-
counter.textContent = "";
|
|
429
|
-
return;
|
|
430
|
-
}
|
|
431
|
-
if (len === 0 || min != null && len < min) {
|
|
432
|
-
if (min != null && max != null) {
|
|
433
|
-
counter.textContent = `${min}-${max}`;
|
|
434
|
-
} else if (max != null) {
|
|
435
|
-
counter.textContent = `\u2264${max}`;
|
|
436
|
-
} else if (min != null) {
|
|
437
|
-
counter.textContent = `\u2265${min}`;
|
|
438
|
-
}
|
|
439
|
-
counter.style.color = "var(--fb-text-secondary-color)";
|
|
440
|
-
} else if (max != null && len > max) {
|
|
731
|
+
if (max != null && len > max) {
|
|
441
732
|
counter.textContent = `${len}/${max}`;
|
|
442
|
-
counter.style.
|
|
733
|
+
counter.style.display = "block";
|
|
443
734
|
} else {
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
} else {
|
|
447
|
-
counter.textContent = `${len}`;
|
|
448
|
-
}
|
|
449
|
-
counter.style.color = "var(--fb-text-secondary-color)";
|
|
735
|
+
counter.textContent = "";
|
|
736
|
+
counter.style.display = "none";
|
|
450
737
|
}
|
|
451
738
|
};
|
|
452
739
|
input.addEventListener("input", updateCounter);
|
|
@@ -458,26 +745,33 @@ function renderTextElement(element, ctx, wrapper, pathKey) {
|
|
|
458
745
|
const readonly = isElementReadonly(element, state, ctx);
|
|
459
746
|
const inputWrapper = document.createElement("div");
|
|
460
747
|
inputWrapper.style.cssText = "position: relative;";
|
|
461
|
-
const
|
|
462
|
-
textInput
|
|
748
|
+
const hasCharCounter = !readonly && (element.minLength != null || element.maxLength != null);
|
|
749
|
+
const textInput = document.createElement("textarea");
|
|
750
|
+
textInput.rows = 1;
|
|
463
751
|
textInput.className = "w-full rounded-lg";
|
|
464
752
|
textInput.style.cssText = `
|
|
465
753
|
padding: var(--fb-input-padding-y) var(--fb-input-padding-x);
|
|
466
|
-
padding-right: 60px;
|
|
467
754
|
border: var(--fb-border-width) solid var(--fb-border-color);
|
|
468
755
|
border-radius: var(--fb-border-radius);
|
|
469
756
|
background-color: ${readonly ? "var(--fb-background-readonly-color)" : "var(--fb-background-color)"};
|
|
470
757
|
color: var(--fb-text-color);
|
|
471
758
|
font-size: var(--fb-font-size);
|
|
472
759
|
font-family: var(--fb-font-family);
|
|
760
|
+
line-height: var(--fb-line-height, 1.5);
|
|
473
761
|
transition: all var(--fb-transition-duration) ease-in-out;
|
|
474
762
|
width: 100%;
|
|
475
763
|
box-sizing: border-box;
|
|
764
|
+
resize: none;
|
|
765
|
+
overflow: hidden;
|
|
766
|
+
word-break: break-word;
|
|
767
|
+
overflow-wrap: anywhere;
|
|
476
768
|
`;
|
|
477
769
|
textInput.name = pathKey;
|
|
478
770
|
textInput.placeholder = element.placeholder || "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u0442\u0435\u043A\u0441\u0442";
|
|
479
771
|
textInput.value = ctx.prefill[element.key] || element.default || "";
|
|
480
772
|
textInput.readOnly = readonly;
|
|
773
|
+
applySingleLineMode(textInput);
|
|
774
|
+
applyAutoExpand(textInput);
|
|
481
775
|
if (!readonly) {
|
|
482
776
|
textInput.addEventListener("focus", () => {
|
|
483
777
|
textInput.style.borderColor = "var(--fb-border-focus-color)";
|
|
@@ -508,8 +802,8 @@ function renderTextElement(element, ctx, wrapper, pathKey) {
|
|
|
508
802
|
textInput.addEventListener("input", handleChange);
|
|
509
803
|
}
|
|
510
804
|
inputWrapper.appendChild(textInput);
|
|
511
|
-
if (
|
|
512
|
-
const counter = createCharCounter(element, textInput
|
|
805
|
+
if (hasCharCounter) {
|
|
806
|
+
const counter = createCharCounter(element, textInput);
|
|
513
807
|
inputWrapper.appendChild(counter);
|
|
514
808
|
}
|
|
515
809
|
wrapper.appendChild(inputWrapper);
|
|
@@ -525,174 +819,100 @@ function renderMultipleTextElement(element, ctx, wrapper, pathKey) {
|
|
|
525
819
|
while (values.length < minCount) {
|
|
526
820
|
values.push(element.default || "");
|
|
527
821
|
}
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
822
|
+
ensureChipStyles(document);
|
|
823
|
+
const list = document.createElement("div");
|
|
824
|
+
list.className = "fb-chip-list";
|
|
825
|
+
wrapper.appendChild(list);
|
|
531
826
|
function updateIndices() {
|
|
532
|
-
const items =
|
|
533
|
-
items.forEach((
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
827
|
+
const items = list.querySelectorAll(".fb-chip-input");
|
|
828
|
+
items.forEach((input, index) => {
|
|
829
|
+
input.name = `${pathKey}[${index}]`;
|
|
830
|
+
const chip = input.closest(".fb-chip");
|
|
831
|
+
const sib = chip == null ? void 0 : chip.nextElementSibling;
|
|
832
|
+
if (sib && sib.classList.contains("error-message")) {
|
|
833
|
+
sib.id = `error-${input.name}`;
|
|
537
834
|
}
|
|
538
835
|
});
|
|
539
836
|
}
|
|
540
|
-
function
|
|
541
|
-
const
|
|
542
|
-
|
|
543
|
-
const
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
font-size: var(--fb-font-size);
|
|
555
|
-
font-family: var(--fb-font-family);
|
|
556
|
-
transition: all var(--fb-transition-duration) ease-in-out;
|
|
557
|
-
width: 100%;
|
|
558
|
-
box-sizing: border-box;
|
|
559
|
-
`;
|
|
560
|
-
textInput.placeholder = element.placeholder || t("placeholderText", state);
|
|
561
|
-
textInput.value = value;
|
|
562
|
-
textInput.readOnly = readonly;
|
|
563
|
-
if (!readonly) {
|
|
564
|
-
textInput.addEventListener("focus", () => {
|
|
565
|
-
textInput.style.borderColor = "var(--fb-border-focus-color)";
|
|
566
|
-
textInput.style.outline = `var(--fb-focus-ring-width) solid var(--fb-focus-ring-color)`;
|
|
567
|
-
textInput.style.outlineOffset = "0";
|
|
568
|
-
});
|
|
569
|
-
textInput.addEventListener("blur", () => {
|
|
570
|
-
textInput.style.borderColor = "var(--fb-border-color)";
|
|
571
|
-
textInput.style.outline = "none";
|
|
572
|
-
});
|
|
573
|
-
textInput.addEventListener("mouseenter", () => {
|
|
574
|
-
if (document.activeElement !== textInput) {
|
|
575
|
-
textInput.style.borderColor = "var(--fb-border-hover-color)";
|
|
576
|
-
}
|
|
577
|
-
});
|
|
578
|
-
textInput.addEventListener("mouseleave", () => {
|
|
579
|
-
if (document.activeElement !== textInput) {
|
|
580
|
-
textInput.style.borderColor = "var(--fb-border-color)";
|
|
581
|
-
}
|
|
582
|
-
});
|
|
583
|
-
}
|
|
837
|
+
function addChip(value = "") {
|
|
838
|
+
const chip = document.createElement("div");
|
|
839
|
+
chip.className = "fb-chip";
|
|
840
|
+
const dot = document.createElement("span");
|
|
841
|
+
dot.className = "fb-chip-dot";
|
|
842
|
+
dot.setAttribute("aria-hidden", "true");
|
|
843
|
+
chip.appendChild(dot);
|
|
844
|
+
const input = document.createElement("input");
|
|
845
|
+
input.type = "text";
|
|
846
|
+
input.className = "fb-chip-input";
|
|
847
|
+
input.value = value;
|
|
848
|
+
input.placeholder = element.placeholder || t("placeholderText", state);
|
|
849
|
+
input.readOnly = readonly;
|
|
850
|
+
chip.appendChild(input);
|
|
584
851
|
if (!readonly && ctx.instance) {
|
|
585
852
|
const handleChange = () => {
|
|
586
|
-
|
|
587
|
-
|
|
853
|
+
ctx.instance.triggerOnChange(
|
|
854
|
+
input.name,
|
|
855
|
+
input.value === "" ? null : input.value
|
|
856
|
+
);
|
|
588
857
|
};
|
|
589
|
-
|
|
590
|
-
|
|
858
|
+
input.addEventListener("blur", handleChange);
|
|
859
|
+
input.addEventListener("input", handleChange);
|
|
591
860
|
}
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
861
|
+
if (!readonly) {
|
|
862
|
+
const rem = document.createElement("button");
|
|
863
|
+
rem.type = "button";
|
|
864
|
+
rem.className = "fb-chip-remove";
|
|
865
|
+
rem.setAttribute("aria-label", t("removeElement", state));
|
|
866
|
+
rem.innerHTML = BIN_ICON_SVG;
|
|
867
|
+
rem.onclick = () => {
|
|
868
|
+
const chips = list.querySelectorAll(".fb-chip");
|
|
869
|
+
const idx = Array.prototype.indexOf.call(chips, chip);
|
|
870
|
+
if (idx < 0) return;
|
|
871
|
+
if (chips.length <= minCount) return;
|
|
872
|
+
values.splice(idx, 1);
|
|
873
|
+
const trailingError = chip.nextElementSibling;
|
|
874
|
+
if (trailingError && trailingError.classList.contains("error-message")) {
|
|
875
|
+
trailingError.remove();
|
|
876
|
+
}
|
|
877
|
+
chip.remove();
|
|
878
|
+
updateIndices();
|
|
879
|
+
updateAddButton();
|
|
880
|
+
updateRemoveButtons();
|
|
881
|
+
};
|
|
882
|
+
chip.appendChild(rem);
|
|
602
883
|
}
|
|
884
|
+
list.appendChild(chip);
|
|
603
885
|
updateIndices();
|
|
604
|
-
return
|
|
886
|
+
return chip;
|
|
605
887
|
}
|
|
606
888
|
function updateRemoveButtons() {
|
|
607
889
|
if (readonly) return;
|
|
608
|
-
const
|
|
609
|
-
const
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
".remove-item-btn"
|
|
613
|
-
);
|
|
614
|
-
if (!removeBtn) {
|
|
615
|
-
removeBtn = document.createElement("button");
|
|
616
|
-
removeBtn.type = "button";
|
|
617
|
-
removeBtn.className = "remove-item-btn px-2 py-1 rounded";
|
|
618
|
-
removeBtn.style.cssText = `
|
|
619
|
-
color: var(--fb-error-color);
|
|
620
|
-
background-color: transparent;
|
|
621
|
-
transition: background-color var(--fb-transition-duration);
|
|
622
|
-
`;
|
|
623
|
-
removeBtn.innerHTML = "\u2715";
|
|
624
|
-
removeBtn.addEventListener("mouseenter", () => {
|
|
625
|
-
removeBtn.style.backgroundColor = "var(--fb-background-hover-color)";
|
|
626
|
-
});
|
|
627
|
-
removeBtn.addEventListener("mouseleave", () => {
|
|
628
|
-
removeBtn.style.backgroundColor = "transparent";
|
|
629
|
-
});
|
|
630
|
-
removeBtn.onclick = () => {
|
|
631
|
-
const currentIndex = Array.from(container.children).indexOf(
|
|
632
|
-
item
|
|
633
|
-
);
|
|
634
|
-
if (container.children.length > minCount) {
|
|
635
|
-
values.splice(currentIndex, 1);
|
|
636
|
-
item.remove();
|
|
637
|
-
updateIndices();
|
|
638
|
-
updateAddButton();
|
|
639
|
-
updateRemoveButtons();
|
|
640
|
-
}
|
|
641
|
-
};
|
|
642
|
-
item.appendChild(removeBtn);
|
|
643
|
-
}
|
|
644
|
-
const disabled = currentCount <= minCount;
|
|
645
|
-
removeBtn.disabled = disabled;
|
|
646
|
-
removeBtn.style.opacity = disabled ? "0.5" : "1";
|
|
647
|
-
removeBtn.style.pointerEvents = disabled ? "none" : "auto";
|
|
890
|
+
const chipCount = list.querySelectorAll(".fb-chip").length;
|
|
891
|
+
const disabled = chipCount <= minCount;
|
|
892
|
+
list.querySelectorAll(".fb-chip-remove").forEach((btn) => {
|
|
893
|
+
btn.disabled = disabled;
|
|
648
894
|
});
|
|
649
895
|
}
|
|
650
|
-
let
|
|
651
|
-
let countDisplay = null;
|
|
896
|
+
let addUpdate = null;
|
|
652
897
|
if (!readonly) {
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
addBtn.addEventListener("mouseenter", () => {
|
|
667
|
-
addBtn.style.backgroundColor = "var(--fb-background-hover-color)";
|
|
668
|
-
});
|
|
669
|
-
addBtn.addEventListener("mouseleave", () => {
|
|
670
|
-
addBtn.style.backgroundColor = "transparent";
|
|
671
|
-
});
|
|
672
|
-
addBtn.onclick = () => {
|
|
673
|
-
values.push(element.default || "");
|
|
674
|
-
addTextItem(element.default || "");
|
|
675
|
-
updateAddButton();
|
|
676
|
-
updateRemoveButtons();
|
|
677
|
-
};
|
|
678
|
-
countDisplay = document.createElement("span");
|
|
679
|
-
countDisplay.className = "text-sm text-gray-500";
|
|
680
|
-
addRow.appendChild(addBtn);
|
|
681
|
-
addRow.appendChild(countDisplay);
|
|
682
|
-
wrapper.appendChild(addRow);
|
|
898
|
+
const handle = createAddItemRow(
|
|
899
|
+
"text",
|
|
900
|
+
() => {
|
|
901
|
+
values.push(element.default || "");
|
|
902
|
+
addChip(element.default || "");
|
|
903
|
+
updateAddButton();
|
|
904
|
+
updateRemoveButtons();
|
|
905
|
+
},
|
|
906
|
+
{ label: element.addLabel }
|
|
907
|
+
);
|
|
908
|
+
addUpdate = handle.update;
|
|
909
|
+
mountCounterInLabel(wrapper, handle.counter);
|
|
910
|
+
wrapper.appendChild(handle.row);
|
|
683
911
|
}
|
|
684
912
|
function updateAddButton() {
|
|
685
|
-
if (
|
|
686
|
-
const addBtn = addRow.querySelector(".add-text-btn");
|
|
687
|
-
if (addBtn) {
|
|
688
|
-
const disabled = values.length >= maxCount;
|
|
689
|
-
addBtn.disabled = disabled;
|
|
690
|
-
addBtn.style.opacity = disabled ? "0.5" : "1";
|
|
691
|
-
addBtn.style.pointerEvents = disabled ? "none" : "auto";
|
|
692
|
-
}
|
|
693
|
-
countDisplay.textContent = `${values.length}/${maxCount === Infinity ? "\u221E" : maxCount}`;
|
|
913
|
+
if (addUpdate) addUpdate(values.length, maxCount);
|
|
694
914
|
}
|
|
695
|
-
values.forEach((value) =>
|
|
915
|
+
values.forEach((value) => addChip(value));
|
|
696
916
|
updateAddButton();
|
|
697
917
|
updateRemoveButtons();
|
|
698
918
|
}
|
|
@@ -701,7 +921,7 @@ function validateTextElement(element, key, context) {
|
|
|
701
921
|
const errors = [];
|
|
702
922
|
const { scopeRoot, skipValidation } = context;
|
|
703
923
|
const markValidity = (input, errorMessage) => {
|
|
704
|
-
var _a2, _b2;
|
|
924
|
+
var _a2, _b2, _c2;
|
|
705
925
|
if (!input) return;
|
|
706
926
|
const errorId = `error-${input.getAttribute("name") || Math.random().toString(36).substring(7)}`;
|
|
707
927
|
let errorElement = document.getElementById(errorId);
|
|
@@ -717,10 +937,12 @@ function validateTextElement(element, key, context) {
|
|
|
717
937
|
font-size: var(--fb-font-size-small);
|
|
718
938
|
margin-top: 0.25rem;
|
|
719
939
|
`;
|
|
720
|
-
|
|
721
|
-
|
|
940
|
+
const chipAncestor = (_a2 = input.closest) == null ? void 0 : _a2.call(input, ".fb-chip");
|
|
941
|
+
const anchor = chipAncestor || input;
|
|
942
|
+
if (anchor.nextSibling) {
|
|
943
|
+
(_b2 = anchor.parentNode) == null ? void 0 : _b2.insertBefore(errorElement, anchor.nextSibling);
|
|
722
944
|
} else {
|
|
723
|
-
(
|
|
945
|
+
(_c2 = anchor.parentNode) == null ? void 0 : _c2.appendChild(errorElement);
|
|
724
946
|
}
|
|
725
947
|
}
|
|
726
948
|
errorElement.textContent = errorMessage;
|
|
@@ -769,7 +991,7 @@ function validateTextElement(element, key, context) {
|
|
|
769
991
|
}
|
|
770
992
|
};
|
|
771
993
|
if (element.multiple) {
|
|
772
|
-
const inputs = scopeRoot.querySelectorAll(`[name^="${key}["]`);
|
|
994
|
+
const inputs = scopeRoot.querySelectorAll(`[name^="${key}\\["]`);
|
|
773
995
|
const values = [];
|
|
774
996
|
const rawValues = [];
|
|
775
997
|
inputs.forEach((input, index) => {
|
|
@@ -819,12 +1041,14 @@ function updateTextField(element, fieldPath, value, context) {
|
|
|
819
1041
|
);
|
|
820
1042
|
return;
|
|
821
1043
|
}
|
|
822
|
-
const inputs = scopeRoot.querySelectorAll(`[name^="${fieldPath}["]`);
|
|
1044
|
+
const inputs = scopeRoot.querySelectorAll(`[name^="${fieldPath}\\["]`);
|
|
823
1045
|
inputs.forEach((input, index) => {
|
|
824
1046
|
if (index < value.length) {
|
|
825
1047
|
input.value = value[index] != null ? String(value[index]) : "";
|
|
826
1048
|
input.classList.remove("invalid");
|
|
827
1049
|
input.title = "";
|
|
1050
|
+
clearFieldError(input);
|
|
1051
|
+
input.dispatchEvent(new Event("input", { bubbles: true }));
|
|
828
1052
|
}
|
|
829
1053
|
});
|
|
830
1054
|
if (value.length !== inputs.length) {
|
|
@@ -838,26 +1062,15 @@ function updateTextField(element, fieldPath, value, context) {
|
|
|
838
1062
|
input.value = value != null ? String(value) : "";
|
|
839
1063
|
input.classList.remove("invalid");
|
|
840
1064
|
input.title = "";
|
|
1065
|
+
clearFieldError(input);
|
|
1066
|
+
if (input instanceof HTMLTextAreaElement) {
|
|
1067
|
+
input.dispatchEvent(new Event("input", { bubbles: true }));
|
|
1068
|
+
}
|
|
841
1069
|
}
|
|
842
1070
|
}
|
|
843
1071
|
}
|
|
844
1072
|
|
|
845
1073
|
// src/components/textarea.ts
|
|
846
|
-
function applyAutoExpand(textarea) {
|
|
847
|
-
textarea.style.overflow = "hidden";
|
|
848
|
-
textarea.style.resize = "none";
|
|
849
|
-
const lineCount = (textarea.value.match(/\n/g) || []).length + 1;
|
|
850
|
-
textarea.rows = Math.max(1, lineCount);
|
|
851
|
-
const resize = () => {
|
|
852
|
-
if (!textarea.isConnected) return;
|
|
853
|
-
textarea.style.height = "0";
|
|
854
|
-
textarea.style.height = `${textarea.scrollHeight}px`;
|
|
855
|
-
};
|
|
856
|
-
textarea.addEventListener("input", resize);
|
|
857
|
-
setTimeout(() => {
|
|
858
|
-
if (textarea.isConnected) resize();
|
|
859
|
-
}, 0);
|
|
860
|
-
}
|
|
861
1074
|
function renderTextareaElement(element, ctx, wrapper, pathKey) {
|
|
862
1075
|
const state = ctx.state;
|
|
863
1076
|
const readonly = isElementReadonly(element, state, ctx);
|
|
@@ -888,7 +1101,7 @@ function renderTextareaElement(element, ctx, wrapper, pathKey) {
|
|
|
888
1101
|
}
|
|
889
1102
|
textareaWrapper.appendChild(textareaInput);
|
|
890
1103
|
if (!readonly && (element.minLength != null || element.maxLength != null)) {
|
|
891
|
-
const counter = createCharCounter(element, textareaInput
|
|
1104
|
+
const counter = createCharCounter(element, textareaInput);
|
|
892
1105
|
textareaWrapper.appendChild(counter);
|
|
893
1106
|
}
|
|
894
1107
|
wrapper.appendChild(textareaWrapper);
|
|
@@ -945,7 +1158,7 @@ function renderMultipleTextareaElement(element, ctx, wrapper, pathKey) {
|
|
|
945
1158
|
}
|
|
946
1159
|
textareaContainer.appendChild(textareaInput);
|
|
947
1160
|
if (!readonly && (element.minLength != null || element.maxLength != null)) {
|
|
948
|
-
const counter = createCharCounter(element, textareaInput
|
|
1161
|
+
const counter = createCharCounter(element, textareaInput);
|
|
949
1162
|
textareaContainer.appendChild(counter);
|
|
950
1163
|
}
|
|
951
1164
|
itemWrapper.appendChild(textareaContainer);
|
|
@@ -990,52 +1203,24 @@ function renderMultipleTextareaElement(element, ctx, wrapper, pathKey) {
|
|
|
990
1203
|
removeBtn.style.pointerEvents = disabled ? "none" : "auto";
|
|
991
1204
|
});
|
|
992
1205
|
}
|
|
993
|
-
let
|
|
994
|
-
let countDisplay = null;
|
|
1206
|
+
let addUpdate = null;
|
|
995
1207
|
if (!readonly) {
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
addBtn.addEventListener("mouseenter", () => {
|
|
1010
|
-
addBtn.style.backgroundColor = "var(--fb-background-hover-color)";
|
|
1011
|
-
});
|
|
1012
|
-
addBtn.addEventListener("mouseleave", () => {
|
|
1013
|
-
addBtn.style.backgroundColor = "transparent";
|
|
1014
|
-
});
|
|
1015
|
-
addBtn.onclick = () => {
|
|
1016
|
-
values.push(element.default || "");
|
|
1017
|
-
addTextareaItem(element.default || "");
|
|
1018
|
-
updateAddButton();
|
|
1019
|
-
updateRemoveButtons();
|
|
1020
|
-
};
|
|
1021
|
-
countDisplay = document.createElement("span");
|
|
1022
|
-
countDisplay.className = "text-sm text-gray-500";
|
|
1023
|
-
addRow.appendChild(addBtn);
|
|
1024
|
-
addRow.appendChild(countDisplay);
|
|
1025
|
-
wrapper.appendChild(addRow);
|
|
1208
|
+
const handle = createAddItemRow(
|
|
1209
|
+
"textarea",
|
|
1210
|
+
() => {
|
|
1211
|
+
values.push(element.default || "");
|
|
1212
|
+
addTextareaItem(element.default || "");
|
|
1213
|
+
updateAddButton();
|
|
1214
|
+
updateRemoveButtons();
|
|
1215
|
+
},
|
|
1216
|
+
{ label: element.addLabel }
|
|
1217
|
+
);
|
|
1218
|
+
addUpdate = handle.update;
|
|
1219
|
+
mountCounterInLabel(wrapper, handle.counter);
|
|
1220
|
+
wrapper.appendChild(handle.row);
|
|
1026
1221
|
}
|
|
1027
1222
|
function updateAddButton() {
|
|
1028
|
-
if (
|
|
1029
|
-
const addBtn = addRow.querySelector(
|
|
1030
|
-
".add-textarea-btn"
|
|
1031
|
-
);
|
|
1032
|
-
if (addBtn) {
|
|
1033
|
-
const disabled = values.length >= maxCount;
|
|
1034
|
-
addBtn.disabled = disabled;
|
|
1035
|
-
addBtn.style.opacity = disabled ? "0.5" : "1";
|
|
1036
|
-
addBtn.style.pointerEvents = disabled ? "none" : "auto";
|
|
1037
|
-
}
|
|
1038
|
-
countDisplay.textContent = `${values.length}/${maxCount === Infinity ? "\u221E" : maxCount}`;
|
|
1223
|
+
if (addUpdate) addUpdate(values.length, maxCount);
|
|
1039
1224
|
}
|
|
1040
1225
|
values.forEach((value) => addTextareaItem(value));
|
|
1041
1226
|
updateAddButton();
|
|
@@ -1067,6 +1252,91 @@ function updateTextareaField(element, fieldPath, value, context) {
|
|
|
1067
1252
|
}
|
|
1068
1253
|
|
|
1069
1254
|
// src/components/number.ts
|
|
1255
|
+
function ensureStepperStyles(doc) {
|
|
1256
|
+
const ID = "fb-number-stepper-styles";
|
|
1257
|
+
if (doc.getElementById(ID)) return;
|
|
1258
|
+
const style = doc.createElement("style");
|
|
1259
|
+
style.id = ID;
|
|
1260
|
+
style.textContent = `
|
|
1261
|
+
.fb-stepper-input::-webkit-outer-spin-button,
|
|
1262
|
+
.fb-stepper-input::-webkit-inner-spin-button {
|
|
1263
|
+
-webkit-appearance: none;
|
|
1264
|
+
margin: 0;
|
|
1265
|
+
}
|
|
1266
|
+
.fb-stepper-input { -moz-appearance: textfield; }
|
|
1267
|
+
`;
|
|
1268
|
+
doc.head.appendChild(style);
|
|
1269
|
+
}
|
|
1270
|
+
function buildStepper(input, element, readonly) {
|
|
1271
|
+
var _a;
|
|
1272
|
+
ensureStepperStyles(input.ownerDocument);
|
|
1273
|
+
const step = (_a = element.step) != null ? _a : 1;
|
|
1274
|
+
const min = element.min;
|
|
1275
|
+
const max = element.max;
|
|
1276
|
+
const wrap = document.createElement("div");
|
|
1277
|
+
wrap.className = "fb-stepper";
|
|
1278
|
+
wrap.style.cssText = `
|
|
1279
|
+
display: inline-flex;
|
|
1280
|
+
align-items: stretch;
|
|
1281
|
+
border: var(--fb-border-width) solid var(--fb-border-color);
|
|
1282
|
+
border-radius: var(--fb-border-radius);
|
|
1283
|
+
overflow: hidden;
|
|
1284
|
+
background: var(--fb-background-color);
|
|
1285
|
+
`;
|
|
1286
|
+
const makeBtn = (label, delta) => {
|
|
1287
|
+
const b = document.createElement("button");
|
|
1288
|
+
b.type = "button";
|
|
1289
|
+
b.textContent = label;
|
|
1290
|
+
b.tabIndex = -1;
|
|
1291
|
+
b.style.cssText = `
|
|
1292
|
+
width: 32px;
|
|
1293
|
+
border: none;
|
|
1294
|
+
background: transparent;
|
|
1295
|
+
color: var(--fb-text-color);
|
|
1296
|
+
font-size: var(--fb-font-size);
|
|
1297
|
+
font-family: var(--fb-font-family);
|
|
1298
|
+
cursor: ${readonly ? "default" : "pointer"};
|
|
1299
|
+
user-select: none;
|
|
1300
|
+
`;
|
|
1301
|
+
if (readonly) {
|
|
1302
|
+
b.disabled = true;
|
|
1303
|
+
b.style.opacity = "0.5";
|
|
1304
|
+
} else {
|
|
1305
|
+
b.addEventListener("click", (e) => {
|
|
1306
|
+
var _a2;
|
|
1307
|
+
e.preventDefault();
|
|
1308
|
+
const current = parseFloat(input.value);
|
|
1309
|
+
const base = Number.isFinite(current) ? current : (_a2 = min != null ? min : element.default) != null ? _a2 : 0;
|
|
1310
|
+
let next = parseFloat((base + delta * step).toPrecision(12));
|
|
1311
|
+
if (min != null) next = Math.max(min, next);
|
|
1312
|
+
if (max != null) next = Math.min(max, next);
|
|
1313
|
+
input.value = String(next);
|
|
1314
|
+
input.dispatchEvent(new Event("input", { bubbles: true }));
|
|
1315
|
+
input.dispatchEvent(new Event("change", { bubbles: true }));
|
|
1316
|
+
});
|
|
1317
|
+
}
|
|
1318
|
+
return b;
|
|
1319
|
+
};
|
|
1320
|
+
input.classList.add("fb-stepper-input");
|
|
1321
|
+
input.style.cssText = `
|
|
1322
|
+
width: 56px;
|
|
1323
|
+
border: none;
|
|
1324
|
+
border-left: var(--fb-border-width) solid var(--fb-border-color);
|
|
1325
|
+
border-right: var(--fb-border-width) solid var(--fb-border-color);
|
|
1326
|
+
padding: var(--fb-input-padding-y) 0;
|
|
1327
|
+
font-size: var(--fb-font-size);
|
|
1328
|
+
font-family: var(--fb-font-family);
|
|
1329
|
+
text-align: center;
|
|
1330
|
+
background: transparent;
|
|
1331
|
+
color: var(--fb-text-color);
|
|
1332
|
+
-moz-appearance: textfield;
|
|
1333
|
+
box-sizing: border-box;
|
|
1334
|
+
`;
|
|
1335
|
+
wrap.appendChild(makeBtn("\u2212", -1));
|
|
1336
|
+
wrap.appendChild(input);
|
|
1337
|
+
wrap.appendChild(makeBtn("+", 1));
|
|
1338
|
+
return wrap;
|
|
1339
|
+
}
|
|
1070
1340
|
function createNumberRangeHint(element, input) {
|
|
1071
1341
|
const hint = document.createElement("span");
|
|
1072
1342
|
hint.className = "number-range-hint";
|
|
@@ -1113,14 +1383,6 @@ function renderNumberElement(element, ctx, wrapper, pathKey) {
|
|
|
1113
1383
|
inputWrapper.style.cssText = "position: relative;";
|
|
1114
1384
|
const numberInput = document.createElement("input");
|
|
1115
1385
|
numberInput.type = "number";
|
|
1116
|
-
numberInput.className = "w-full border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500";
|
|
1117
|
-
numberInput.style.cssText = `
|
|
1118
|
-
padding: var(--fb-input-padding-y) 60px var(--fb-input-padding-y) var(--fb-input-padding-x);
|
|
1119
|
-
font-size: var(--fb-font-size);
|
|
1120
|
-
font-family: var(--fb-font-family);
|
|
1121
|
-
width: 100%;
|
|
1122
|
-
box-sizing: border-box;
|
|
1123
|
-
`;
|
|
1124
1386
|
numberInput.name = pathKey;
|
|
1125
1387
|
numberInput.placeholder = element.placeholder || "0";
|
|
1126
1388
|
if (element.min !== void 0) numberInput.min = element.min.toString();
|
|
@@ -1128,6 +1390,16 @@ function renderNumberElement(element, ctx, wrapper, pathKey) {
|
|
|
1128
1390
|
if (element.step !== void 0) numberInput.step = element.step.toString();
|
|
1129
1391
|
numberInput.value = ctx.prefill[element.key] || element.default || "";
|
|
1130
1392
|
numberInput.readOnly = readonly;
|
|
1393
|
+
if (!element.stepper) {
|
|
1394
|
+
numberInput.className = "w-full border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500";
|
|
1395
|
+
numberInput.style.cssText = `
|
|
1396
|
+
padding: var(--fb-input-padding-y) 60px var(--fb-input-padding-y) var(--fb-input-padding-x);
|
|
1397
|
+
font-size: var(--fb-font-size);
|
|
1398
|
+
font-family: var(--fb-font-family);
|
|
1399
|
+
width: 100%;
|
|
1400
|
+
box-sizing: border-box;
|
|
1401
|
+
`;
|
|
1402
|
+
}
|
|
1131
1403
|
if (!readonly && ctx.instance) {
|
|
1132
1404
|
const handleChange = () => {
|
|
1133
1405
|
const value = numberInput.value ? parseFloat(numberInput.value) : null;
|
|
@@ -1136,10 +1408,14 @@ function renderNumberElement(element, ctx, wrapper, pathKey) {
|
|
|
1136
1408
|
numberInput.addEventListener("blur", handleChange);
|
|
1137
1409
|
numberInput.addEventListener("input", handleChange);
|
|
1138
1410
|
}
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
inputWrapper.appendChild(
|
|
1411
|
+
if (element.stepper) {
|
|
1412
|
+
inputWrapper.appendChild(buildStepper(numberInput, element, readonly));
|
|
1413
|
+
} else {
|
|
1414
|
+
inputWrapper.appendChild(numberInput);
|
|
1415
|
+
if (!readonly && (element.min != null || element.max != null)) {
|
|
1416
|
+
const counter = createNumberRangeHint(element, numberInput);
|
|
1417
|
+
inputWrapper.appendChild(counter);
|
|
1418
|
+
}
|
|
1143
1419
|
}
|
|
1144
1420
|
wrapper.appendChild(inputWrapper);
|
|
1145
1421
|
}
|
|
@@ -1242,50 +1518,24 @@ function renderMultipleNumberElement(element, ctx, wrapper, pathKey) {
|
|
|
1242
1518
|
removeBtn.style.pointerEvents = disabled ? "none" : "auto";
|
|
1243
1519
|
});
|
|
1244
1520
|
}
|
|
1245
|
-
let
|
|
1246
|
-
let countDisplay = null;
|
|
1521
|
+
let addUpdate = null;
|
|
1247
1522
|
if (!readonly) {
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
addBtn.addEventListener("mouseenter", () => {
|
|
1262
|
-
addBtn.style.backgroundColor = "var(--fb-background-hover-color)";
|
|
1263
|
-
});
|
|
1264
|
-
addBtn.addEventListener("mouseleave", () => {
|
|
1265
|
-
addBtn.style.backgroundColor = "transparent";
|
|
1266
|
-
});
|
|
1267
|
-
addBtn.onclick = () => {
|
|
1268
|
-
values.push(element.default || "");
|
|
1269
|
-
addNumberItem(element.default || "");
|
|
1270
|
-
updateAddButton();
|
|
1271
|
-
updateRemoveButtons();
|
|
1272
|
-
};
|
|
1273
|
-
countDisplay = document.createElement("span");
|
|
1274
|
-
countDisplay.className = "text-sm text-gray-500";
|
|
1275
|
-
addRow.appendChild(addBtn);
|
|
1276
|
-
addRow.appendChild(countDisplay);
|
|
1277
|
-
wrapper.appendChild(addRow);
|
|
1523
|
+
const handle = createAddItemRow(
|
|
1524
|
+
"number",
|
|
1525
|
+
() => {
|
|
1526
|
+
values.push(element.default || "");
|
|
1527
|
+
addNumberItem(element.default || "");
|
|
1528
|
+
updateAddButton();
|
|
1529
|
+
updateRemoveButtons();
|
|
1530
|
+
},
|
|
1531
|
+
{ label: element.addLabel }
|
|
1532
|
+
);
|
|
1533
|
+
addUpdate = handle.update;
|
|
1534
|
+
mountCounterInLabel(wrapper, handle.counter);
|
|
1535
|
+
wrapper.appendChild(handle.row);
|
|
1278
1536
|
}
|
|
1279
1537
|
function updateAddButton() {
|
|
1280
|
-
if (
|
|
1281
|
-
const addBtn = addRow.querySelector(".add-number-btn");
|
|
1282
|
-
if (addBtn) {
|
|
1283
|
-
const disabled = values.length >= maxCount;
|
|
1284
|
-
addBtn.disabled = disabled;
|
|
1285
|
-
addBtn.style.opacity = disabled ? "0.5" : "1";
|
|
1286
|
-
addBtn.style.pointerEvents = disabled ? "none" : "auto";
|
|
1287
|
-
}
|
|
1288
|
-
countDisplay.textContent = `${values.length}/${maxCount === Infinity ? "\u221E" : maxCount}`;
|
|
1538
|
+
if (addUpdate) addUpdate(values.length, maxCount);
|
|
1289
1539
|
}
|
|
1290
1540
|
values.forEach((value) => addNumberItem(value));
|
|
1291
1541
|
updateAddButton();
|
|
@@ -1423,13 +1673,14 @@ function updateNumberField(element, fieldPath, value, context) {
|
|
|
1423
1673
|
return;
|
|
1424
1674
|
}
|
|
1425
1675
|
const inputs = scopeRoot.querySelectorAll(
|
|
1426
|
-
`[name^="${fieldPath}["]`
|
|
1676
|
+
`[name^="${fieldPath}\\["]`
|
|
1427
1677
|
);
|
|
1428
1678
|
inputs.forEach((input, index) => {
|
|
1429
1679
|
if (index < value.length) {
|
|
1430
1680
|
input.value = value[index] != null ? String(value[index]) : "";
|
|
1431
1681
|
input.classList.remove("invalid");
|
|
1432
1682
|
input.title = "";
|
|
1683
|
+
clearFieldError(input);
|
|
1433
1684
|
}
|
|
1434
1685
|
});
|
|
1435
1686
|
if (value.length !== inputs.length) {
|
|
@@ -1445,6 +1696,7 @@ function updateNumberField(element, fieldPath, value, context) {
|
|
|
1445
1696
|
input.value = value != null ? String(value) : "";
|
|
1446
1697
|
input.classList.remove("invalid");
|
|
1447
1698
|
input.title = "";
|
|
1699
|
+
clearFieldError(input);
|
|
1448
1700
|
}
|
|
1449
1701
|
}
|
|
1450
1702
|
}
|
|
@@ -1574,52 +1826,26 @@ function renderMultipleSelectElement(element, ctx, wrapper, pathKey) {
|
|
|
1574
1826
|
removeBtn.style.pointerEvents = disabled ? "none" : "auto";
|
|
1575
1827
|
});
|
|
1576
1828
|
}
|
|
1577
|
-
let
|
|
1578
|
-
let countDisplay = null;
|
|
1829
|
+
let addUpdate = null;
|
|
1579
1830
|
if (!readonly) {
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
});
|
|
1596
|
-
addBtn.addEventListener("mouseleave", () => {
|
|
1597
|
-
addBtn.style.backgroundColor = "transparent";
|
|
1598
|
-
});
|
|
1599
|
-
addBtn.onclick = () => {
|
|
1600
|
-
var _a2, _b2;
|
|
1601
|
-
const defaultValue = element.default || ((_b2 = (_a2 = element.options) == null ? void 0 : _a2[0]) == null ? void 0 : _b2.value) || "";
|
|
1602
|
-
values.push(defaultValue);
|
|
1603
|
-
addSelectItem(defaultValue);
|
|
1604
|
-
updateAddButton();
|
|
1605
|
-
updateRemoveButtons();
|
|
1606
|
-
};
|
|
1607
|
-
countDisplay = document.createElement("span");
|
|
1608
|
-
countDisplay.className = "text-sm text-gray-500";
|
|
1609
|
-
addRow.appendChild(addBtn);
|
|
1610
|
-
addRow.appendChild(countDisplay);
|
|
1611
|
-
wrapper.appendChild(addRow);
|
|
1831
|
+
const handle = createAddItemRow(
|
|
1832
|
+
"select",
|
|
1833
|
+
() => {
|
|
1834
|
+
var _a2, _b2;
|
|
1835
|
+
const defaultValue = element.default || ((_b2 = (_a2 = element.options) == null ? void 0 : _a2[0]) == null ? void 0 : _b2.value) || "";
|
|
1836
|
+
values.push(defaultValue);
|
|
1837
|
+
addSelectItem(defaultValue);
|
|
1838
|
+
updateAddButton();
|
|
1839
|
+
updateRemoveButtons();
|
|
1840
|
+
},
|
|
1841
|
+
{ label: element.addLabel }
|
|
1842
|
+
);
|
|
1843
|
+
addUpdate = handle.update;
|
|
1844
|
+
mountCounterInLabel(wrapper, handle.counter);
|
|
1845
|
+
wrapper.appendChild(handle.row);
|
|
1612
1846
|
}
|
|
1613
1847
|
function updateAddButton() {
|
|
1614
|
-
if (
|
|
1615
|
-
const addBtn = addRow.querySelector(".add-select-btn");
|
|
1616
|
-
if (addBtn) {
|
|
1617
|
-
const disabled = values.length >= maxCount;
|
|
1618
|
-
addBtn.disabled = disabled;
|
|
1619
|
-
addBtn.style.opacity = disabled ? "0.5" : "1";
|
|
1620
|
-
addBtn.style.pointerEvents = disabled ? "none" : "auto";
|
|
1621
|
-
}
|
|
1622
|
-
countDisplay.textContent = `${values.length}/${maxCount === Infinity ? "\u221E" : maxCount}`;
|
|
1848
|
+
if (addUpdate) addUpdate(values.length, maxCount);
|
|
1623
1849
|
}
|
|
1624
1850
|
values.forEach((value) => addSelectItem(value));
|
|
1625
1851
|
updateAddButton();
|
|
@@ -1687,7 +1913,7 @@ function validateSelectElement(element, key, context) {
|
|
|
1687
1913
|
};
|
|
1688
1914
|
if ("multiple" in element && element.multiple) {
|
|
1689
1915
|
const inputs = scopeRoot.querySelectorAll(
|
|
1690
|
-
`[name^="${key}["]`
|
|
1916
|
+
`[name^="${key}\\["]`
|
|
1691
1917
|
);
|
|
1692
1918
|
const values = [];
|
|
1693
1919
|
inputs.forEach((input) => {
|
|
@@ -1724,7 +1950,7 @@ function updateSelectField(element, fieldPath, value, context) {
|
|
|
1724
1950
|
return;
|
|
1725
1951
|
}
|
|
1726
1952
|
const selects = scopeRoot.querySelectorAll(
|
|
1727
|
-
`[name^="${fieldPath}["]`
|
|
1953
|
+
`[name^="${fieldPath}\\["]`
|
|
1728
1954
|
);
|
|
1729
1955
|
selects.forEach((select, index) => {
|
|
1730
1956
|
if (index < value.length) {
|
|
@@ -1735,6 +1961,7 @@ function updateSelectField(element, fieldPath, value, context) {
|
|
|
1735
1961
|
});
|
|
1736
1962
|
select.classList.remove("invalid");
|
|
1737
1963
|
select.title = "";
|
|
1964
|
+
clearFieldError(select);
|
|
1738
1965
|
}
|
|
1739
1966
|
});
|
|
1740
1967
|
if (value.length !== selects.length) {
|
|
@@ -1754,72 +1981,147 @@ function updateSelectField(element, fieldPath, value, context) {
|
|
|
1754
1981
|
});
|
|
1755
1982
|
select.classList.remove("invalid");
|
|
1756
1983
|
select.title = "";
|
|
1984
|
+
clearFieldError(select);
|
|
1757
1985
|
}
|
|
1758
1986
|
}
|
|
1759
1987
|
}
|
|
1760
1988
|
|
|
1761
1989
|
// src/components/switcher.ts
|
|
1762
|
-
function applySelectedStyle(btn) {
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1990
|
+
function applySelectedStyle(btn, isPreset) {
|
|
1991
|
+
if (isPreset) {
|
|
1992
|
+
btn.style.backgroundColor = "var(--fb-primary-soft-color)";
|
|
1993
|
+
btn.style.color = "var(--fb-primary-color)";
|
|
1994
|
+
btn.style.borderColor = "var(--fb-primary-color)";
|
|
1995
|
+
} else {
|
|
1996
|
+
btn.style.backgroundColor = "var(--fb-primary-color)";
|
|
1997
|
+
btn.style.color = "#ffffff";
|
|
1998
|
+
btn.style.borderColor = "var(--fb-primary-color)";
|
|
1999
|
+
}
|
|
1766
2000
|
}
|
|
1767
|
-
function applyUnselectedStyle(btn) {
|
|
1768
|
-
btn.style.backgroundColor = "transparent";
|
|
2001
|
+
function applyUnselectedStyle(btn, isPreset) {
|
|
2002
|
+
btn.style.backgroundColor = isPreset ? "var(--fb-background-color)" : "transparent";
|
|
1769
2003
|
btn.style.color = "var(--fb-text-color)";
|
|
1770
2004
|
btn.style.borderColor = "var(--fb-border-color)";
|
|
1771
2005
|
}
|
|
2006
|
+
function isPresetButton(btn) {
|
|
2007
|
+
return btn.classList.contains("fb-switcher-preset");
|
|
2008
|
+
}
|
|
2009
|
+
function buildPresetCard(option, readonly) {
|
|
2010
|
+
const btn = document.createElement("button");
|
|
2011
|
+
btn.type = "button";
|
|
2012
|
+
btn.className = "fb-switcher-btn fb-switcher-preset";
|
|
2013
|
+
btn.dataset.value = option.value;
|
|
2014
|
+
btn.style.cssText = `
|
|
2015
|
+
display: inline-flex;
|
|
2016
|
+
align-items: center;
|
|
2017
|
+
gap: 8px;
|
|
2018
|
+
padding: 7px 12px 7px 10px;
|
|
2019
|
+
border-width: var(--fb-border-width);
|
|
2020
|
+
border-style: solid;
|
|
2021
|
+
border-radius: 999px;
|
|
2022
|
+
background: var(--fb-background-color);
|
|
2023
|
+
font-size: var(--fb-font-size);
|
|
2024
|
+
font-family: var(--fb-font-family);
|
|
2025
|
+
line-height: 1.25;
|
|
2026
|
+
cursor: ${readonly ? "default" : "pointer"};
|
|
2027
|
+
transition: background-color var(--fb-transition-duration), color var(--fb-transition-duration), border-color var(--fb-transition-duration);
|
|
2028
|
+
outline: none;
|
|
2029
|
+
`;
|
|
2030
|
+
if (option.iconUrl) {
|
|
2031
|
+
const icon = document.createElement("img");
|
|
2032
|
+
icon.className = "fb-switcher-icon";
|
|
2033
|
+
icon.src = option.iconUrl;
|
|
2034
|
+
icon.alt = "";
|
|
2035
|
+
icon.setAttribute("aria-hidden", "true");
|
|
2036
|
+
icon.style.cssText = `
|
|
2037
|
+
display: block;
|
|
2038
|
+
flex: 0 0 auto;
|
|
2039
|
+
width: 20px;
|
|
2040
|
+
height: 20px;
|
|
2041
|
+
object-fit: contain;
|
|
2042
|
+
`;
|
|
2043
|
+
btn.appendChild(icon);
|
|
2044
|
+
}
|
|
2045
|
+
const name = document.createElement("span");
|
|
2046
|
+
name.className = "fb-switcher-name";
|
|
2047
|
+
name.textContent = option.label;
|
|
2048
|
+
name.style.cssText = "font-weight: 600;";
|
|
2049
|
+
btn.appendChild(name);
|
|
2050
|
+
if (option.subtitle) {
|
|
2051
|
+
const sub = document.createElement("span");
|
|
2052
|
+
sub.className = "fb-switcher-subtitle";
|
|
2053
|
+
sub.textContent = option.subtitle;
|
|
2054
|
+
sub.style.cssText = `
|
|
2055
|
+
font-size: var(--fb-font-size-small);
|
|
2056
|
+
opacity: 0.7;
|
|
2057
|
+
font-variant-numeric: tabular-nums;
|
|
2058
|
+
`;
|
|
2059
|
+
btn.appendChild(sub);
|
|
2060
|
+
}
|
|
2061
|
+
return btn;
|
|
2062
|
+
}
|
|
1772
2063
|
function buildSegmentedGroup(element, currentValue, hiddenInput, readonly, onChange) {
|
|
1773
2064
|
const options = element.options || [];
|
|
2065
|
+
const isPresetMode = options.some((o) => o.subtitle || o.iconUrl);
|
|
1774
2066
|
const group = document.createElement("div");
|
|
1775
2067
|
group.className = "fb-switcher-group";
|
|
1776
|
-
group.style.cssText = `
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
2068
|
+
group.style.cssText = isPresetMode ? `
|
|
2069
|
+
display: flex;
|
|
2070
|
+
flex-direction: row;
|
|
2071
|
+
flex-wrap: wrap;
|
|
2072
|
+
gap: 6px;
|
|
2073
|
+
` : `
|
|
2074
|
+
display: inline-flex;
|
|
2075
|
+
flex-direction: row;
|
|
2076
|
+
flex-wrap: nowrap;
|
|
2077
|
+
`;
|
|
1781
2078
|
const buttons = [];
|
|
1782
2079
|
options.forEach((option, index) => {
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
btn.dataset.value = option.value;
|
|
1787
|
-
btn.textContent = option.label;
|
|
1788
|
-
btn.style.cssText = `
|
|
1789
|
-
padding: var(--fb-input-padding-y) var(--fb-input-padding-x);
|
|
1790
|
-
font-size: var(--fb-font-size);
|
|
1791
|
-
border-width: var(--fb-border-width);
|
|
1792
|
-
border-style: solid;
|
|
1793
|
-
cursor: ${readonly ? "default" : "pointer"};
|
|
1794
|
-
transition: background-color var(--fb-transition-duration), color var(--fb-transition-duration), border-color var(--fb-transition-duration);
|
|
1795
|
-
white-space: nowrap;
|
|
1796
|
-
line-height: 1.25;
|
|
1797
|
-
outline: none;
|
|
1798
|
-
`;
|
|
1799
|
-
if (options.length === 1) {
|
|
1800
|
-
btn.style.borderRadius = "var(--fb-border-radius)";
|
|
1801
|
-
} else if (index === 0) {
|
|
1802
|
-
btn.style.borderRadius = "var(--fb-border-radius) 0 0 var(--fb-border-radius)";
|
|
1803
|
-
btn.style.borderRightWidth = "0";
|
|
1804
|
-
} else if (index === options.length - 1) {
|
|
1805
|
-
btn.style.borderRadius = "0 var(--fb-border-radius) var(--fb-border-radius) 0";
|
|
2080
|
+
let btn;
|
|
2081
|
+
if (isPresetMode) {
|
|
2082
|
+
btn = buildPresetCard(option, readonly);
|
|
1806
2083
|
} else {
|
|
1807
|
-
btn
|
|
1808
|
-
btn.
|
|
2084
|
+
btn = document.createElement("button");
|
|
2085
|
+
btn.type = "button";
|
|
2086
|
+
btn.className = "fb-switcher-btn";
|
|
2087
|
+
btn.dataset.value = option.value;
|
|
2088
|
+
btn.textContent = option.label;
|
|
2089
|
+
btn.style.cssText = `
|
|
2090
|
+
padding: var(--fb-input-padding-y) var(--fb-input-padding-x);
|
|
2091
|
+
font-size: var(--fb-font-size);
|
|
2092
|
+
border-width: var(--fb-border-width);
|
|
2093
|
+
border-style: solid;
|
|
2094
|
+
cursor: ${readonly ? "default" : "pointer"};
|
|
2095
|
+
transition: background-color var(--fb-transition-duration), color var(--fb-transition-duration), border-color var(--fb-transition-duration);
|
|
2096
|
+
white-space: nowrap;
|
|
2097
|
+
line-height: 1.25;
|
|
2098
|
+
outline: none;
|
|
2099
|
+
`;
|
|
2100
|
+
if (options.length === 1) {
|
|
2101
|
+
btn.style.borderRadius = "var(--fb-border-radius)";
|
|
2102
|
+
} else if (index === 0) {
|
|
2103
|
+
btn.style.borderRadius = "var(--fb-border-radius) 0 0 var(--fb-border-radius)";
|
|
2104
|
+
btn.style.borderRightWidth = "0";
|
|
2105
|
+
} else if (index === options.length - 1) {
|
|
2106
|
+
btn.style.borderRadius = "0 var(--fb-border-radius) var(--fb-border-radius) 0";
|
|
2107
|
+
} else {
|
|
2108
|
+
btn.style.borderRadius = "0";
|
|
2109
|
+
btn.style.borderRightWidth = "0";
|
|
2110
|
+
}
|
|
1809
2111
|
}
|
|
1810
2112
|
if (option.value === currentValue) {
|
|
1811
|
-
applySelectedStyle(btn);
|
|
2113
|
+
applySelectedStyle(btn, isPresetMode);
|
|
1812
2114
|
} else {
|
|
1813
|
-
applyUnselectedStyle(btn);
|
|
2115
|
+
applyUnselectedStyle(btn, isPresetMode);
|
|
1814
2116
|
}
|
|
1815
2117
|
if (!readonly) {
|
|
1816
2118
|
btn.addEventListener("click", () => {
|
|
1817
2119
|
hiddenInput.value = option.value;
|
|
1818
2120
|
buttons.forEach((b) => {
|
|
1819
2121
|
if (b.dataset.value === option.value) {
|
|
1820
|
-
applySelectedStyle(b);
|
|
2122
|
+
applySelectedStyle(b, isPresetMode);
|
|
1821
2123
|
} else {
|
|
1822
|
-
applyUnselectedStyle(b);
|
|
2124
|
+
applyUnselectedStyle(b, isPresetMode);
|
|
1823
2125
|
}
|
|
1824
2126
|
});
|
|
1825
2127
|
if (onChange) {
|
|
@@ -1833,7 +2135,7 @@ function buildSegmentedGroup(element, currentValue, hiddenInput, readonly, onCha
|
|
|
1833
2135
|
});
|
|
1834
2136
|
btn.addEventListener("mouseleave", () => {
|
|
1835
2137
|
if (hiddenInput.value !== option.value) {
|
|
1836
|
-
btn.style.backgroundColor = "transparent";
|
|
2138
|
+
btn.style.backgroundColor = isPresetMode ? "var(--fb-background-color)" : "transparent";
|
|
1837
2139
|
}
|
|
1838
2140
|
});
|
|
1839
2141
|
}
|
|
@@ -1968,54 +2270,26 @@ function renderMultipleSwitcherElement(element, ctx, wrapper, pathKey) {
|
|
|
1968
2270
|
removeBtn.style.pointerEvents = disabled ? "none" : "auto";
|
|
1969
2271
|
});
|
|
1970
2272
|
}
|
|
1971
|
-
let
|
|
1972
|
-
let countDisplay = null;
|
|
2273
|
+
let addUpdate = null;
|
|
1973
2274
|
if (!readonly) {
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
});
|
|
1990
|
-
addBtn.addEventListener("mouseleave", () => {
|
|
1991
|
-
addBtn.style.backgroundColor = "transparent";
|
|
1992
|
-
});
|
|
1993
|
-
addBtn.onclick = () => {
|
|
1994
|
-
var _a2, _b2;
|
|
1995
|
-
const defaultValue = element.default || ((_b2 = (_a2 = element.options) == null ? void 0 : _a2[0]) == null ? void 0 : _b2.value) || "";
|
|
1996
|
-
values.push(defaultValue);
|
|
1997
|
-
addSwitcherItem(defaultValue);
|
|
1998
|
-
updateAddButton();
|
|
1999
|
-
updateRemoveButtons();
|
|
2000
|
-
};
|
|
2001
|
-
countDisplay = document.createElement("span");
|
|
2002
|
-
countDisplay.className = "text-sm text-gray-500";
|
|
2003
|
-
addRow.appendChild(addBtn);
|
|
2004
|
-
addRow.appendChild(countDisplay);
|
|
2005
|
-
wrapper.appendChild(addRow);
|
|
2275
|
+
const handle = createAddItemRow(
|
|
2276
|
+
"switcher",
|
|
2277
|
+
() => {
|
|
2278
|
+
var _a2, _b2;
|
|
2279
|
+
const defaultValue = element.default || ((_b2 = (_a2 = element.options) == null ? void 0 : _a2[0]) == null ? void 0 : _b2.value) || "";
|
|
2280
|
+
values.push(defaultValue);
|
|
2281
|
+
addSwitcherItem(defaultValue);
|
|
2282
|
+
updateAddButton();
|
|
2283
|
+
updateRemoveButtons();
|
|
2284
|
+
},
|
|
2285
|
+
{ label: element.addLabel }
|
|
2286
|
+
);
|
|
2287
|
+
addUpdate = handle.update;
|
|
2288
|
+
mountCounterInLabel(wrapper, handle.counter);
|
|
2289
|
+
wrapper.appendChild(handle.row);
|
|
2006
2290
|
}
|
|
2007
2291
|
function updateAddButton() {
|
|
2008
|
-
if (
|
|
2009
|
-
const addBtn = addRow.querySelector(
|
|
2010
|
-
".add-switcher-btn"
|
|
2011
|
-
);
|
|
2012
|
-
if (addBtn) {
|
|
2013
|
-
const disabled = values.length >= maxCount;
|
|
2014
|
-
addBtn.disabled = disabled;
|
|
2015
|
-
addBtn.style.opacity = disabled ? "0.5" : "1";
|
|
2016
|
-
addBtn.style.pointerEvents = disabled ? "none" : "auto";
|
|
2017
|
-
}
|
|
2018
|
-
countDisplay.textContent = `${values.length}/${maxCount === Infinity ? "\u221E" : maxCount}`;
|
|
2292
|
+
if (addUpdate) addUpdate(values.length, maxCount);
|
|
2019
2293
|
}
|
|
2020
2294
|
values.forEach((value) => addSwitcherItem(value));
|
|
2021
2295
|
updateAddButton();
|
|
@@ -2086,7 +2360,7 @@ function validateSwitcherElement(element, key, context) {
|
|
|
2086
2360
|
);
|
|
2087
2361
|
if ("multiple" in element && element.multiple) {
|
|
2088
2362
|
const inputs = scopeRoot.querySelectorAll(
|
|
2089
|
-
`input[type="hidden"][name^="${key}["]`
|
|
2363
|
+
`input[type="hidden"][name^="${key}\\["]`
|
|
2090
2364
|
);
|
|
2091
2365
|
const values = [];
|
|
2092
2366
|
inputs.forEach((input) => {
|
|
@@ -2135,7 +2409,7 @@ function updateSwitcherField(element, fieldPath, value, context) {
|
|
|
2135
2409
|
return;
|
|
2136
2410
|
}
|
|
2137
2411
|
const inputs = scopeRoot.querySelectorAll(
|
|
2138
|
-
`input[type="hidden"][name^="${fieldPath}["]`
|
|
2412
|
+
`input[type="hidden"][name^="${fieldPath}\\["]`
|
|
2139
2413
|
);
|
|
2140
2414
|
inputs.forEach((input, index) => {
|
|
2141
2415
|
var _a2;
|
|
@@ -2145,15 +2419,17 @@ function updateSwitcherField(element, fieldPath, value, context) {
|
|
|
2145
2419
|
const group = (_a2 = input.parentElement) == null ? void 0 : _a2.querySelector(".fb-switcher-group");
|
|
2146
2420
|
if (group) {
|
|
2147
2421
|
group.querySelectorAll(".fb-switcher-btn").forEach((btn) => {
|
|
2422
|
+
const isPreset = isPresetButton(btn);
|
|
2148
2423
|
if (btn.dataset.value === newVal) {
|
|
2149
|
-
applySelectedStyle(btn);
|
|
2424
|
+
applySelectedStyle(btn, isPreset);
|
|
2150
2425
|
} else {
|
|
2151
|
-
applyUnselectedStyle(btn);
|
|
2426
|
+
applyUnselectedStyle(btn, isPreset);
|
|
2152
2427
|
}
|
|
2153
2428
|
});
|
|
2154
2429
|
}
|
|
2155
2430
|
input.classList.remove("invalid");
|
|
2156
2431
|
input.title = "";
|
|
2432
|
+
clearFieldError(input);
|
|
2157
2433
|
}
|
|
2158
2434
|
});
|
|
2159
2435
|
if (value.length !== inputs.length) {
|
|
@@ -2171,16 +2447,212 @@ function updateSwitcherField(element, fieldPath, value, context) {
|
|
|
2171
2447
|
const group = (_a = input.parentElement) == null ? void 0 : _a.querySelector(".fb-switcher-group");
|
|
2172
2448
|
if (group) {
|
|
2173
2449
|
group.querySelectorAll(".fb-switcher-btn").forEach((btn) => {
|
|
2450
|
+
const isPreset = isPresetButton(btn);
|
|
2174
2451
|
if (btn.dataset.value === newVal) {
|
|
2175
|
-
applySelectedStyle(btn);
|
|
2452
|
+
applySelectedStyle(btn, isPreset);
|
|
2176
2453
|
} else {
|
|
2177
|
-
applyUnselectedStyle(btn);
|
|
2454
|
+
applyUnselectedStyle(btn, isPreset);
|
|
2178
2455
|
}
|
|
2179
2456
|
});
|
|
2180
2457
|
}
|
|
2181
2458
|
input.classList.remove("invalid");
|
|
2182
2459
|
input.title = "";
|
|
2460
|
+
clearFieldError(input);
|
|
2461
|
+
}
|
|
2462
|
+
}
|
|
2463
|
+
}
|
|
2464
|
+
|
|
2465
|
+
// src/components/boolean.ts
|
|
2466
|
+
var TOGGLE_W = 36;
|
|
2467
|
+
var TOGGLE_H = 20;
|
|
2468
|
+
var KNOB = 16;
|
|
2469
|
+
function ensureStyles(doc) {
|
|
2470
|
+
const ID = "fb-boolean-styles";
|
|
2471
|
+
if (doc.getElementById(ID)) return;
|
|
2472
|
+
const style = doc.createElement("style");
|
|
2473
|
+
style.id = ID;
|
|
2474
|
+
style.textContent = `
|
|
2475
|
+
.fb-toggle {
|
|
2476
|
+
position: relative;
|
|
2477
|
+
display: inline-block;
|
|
2478
|
+
width: ${TOGGLE_W}px;
|
|
2479
|
+
height: ${TOGGLE_H}px;
|
|
2480
|
+
border-radius: ${TOGGLE_H}px;
|
|
2481
|
+
background: var(--fb-border-color);
|
|
2482
|
+
transition: background-color var(--fb-transition-duration);
|
|
2483
|
+
flex-shrink: 0;
|
|
2484
|
+
}
|
|
2485
|
+
.fb-toggle::after {
|
|
2486
|
+
content: "";
|
|
2487
|
+
position: absolute;
|
|
2488
|
+
top: ${(TOGGLE_H - KNOB) / 2}px;
|
|
2489
|
+
left: ${(TOGGLE_H - KNOB) / 2}px;
|
|
2490
|
+
width: ${KNOB}px;
|
|
2491
|
+
height: ${KNOB}px;
|
|
2492
|
+
border-radius: 50%;
|
|
2493
|
+
background: #ffffff;
|
|
2494
|
+
box-shadow: 0 1px 3px rgba(0,0,0,0.2);
|
|
2495
|
+
transition: transform var(--fb-transition-duration);
|
|
2496
|
+
}
|
|
2497
|
+
.fb-toggle.fb-on {
|
|
2498
|
+
background: var(--fb-primary-color);
|
|
2499
|
+
}
|
|
2500
|
+
.fb-toggle.fb-on::after {
|
|
2501
|
+
transform: translateX(${TOGGLE_W - KNOB - (TOGGLE_H - KNOB)}px);
|
|
2502
|
+
}
|
|
2503
|
+
.fb-toggle-row {
|
|
2504
|
+
display: flex;
|
|
2505
|
+
align-items: center;
|
|
2506
|
+
gap: 12px;
|
|
2507
|
+
padding: 12px 14px;
|
|
2508
|
+
background: var(--fb-surface-soft-color);
|
|
2509
|
+
border: var(--fb-border-width) solid var(--fb-border-color);
|
|
2510
|
+
border-radius: var(--fb-border-radius);
|
|
2511
|
+
cursor: pointer;
|
|
2512
|
+
user-select: none;
|
|
2513
|
+
}
|
|
2514
|
+
.fb-toggle-row[aria-disabled="true"] {
|
|
2515
|
+
cursor: default;
|
|
2516
|
+
opacity: 0.7;
|
|
2517
|
+
}
|
|
2518
|
+
.fb-toggle-row:focus-visible {
|
|
2519
|
+
outline: var(--fb-focus-ring-width) solid var(--fb-focus-ring-color);
|
|
2520
|
+
outline-offset: var(--fb-focus-ring-offset);
|
|
2521
|
+
}
|
|
2522
|
+
.fb-toggle-text { flex: 1; min-width: 0; }
|
|
2523
|
+
.fb-toggle-title {
|
|
2524
|
+
display: flex;
|
|
2525
|
+
align-items: center;
|
|
2526
|
+
gap: 4px;
|
|
2527
|
+
font-size: var(--fb-font-size);
|
|
2528
|
+
font-weight: 500;
|
|
2529
|
+
color: var(--fb-text-color);
|
|
2530
|
+
line-height: 1.3;
|
|
2531
|
+
}
|
|
2532
|
+
.fb-toggle-subtitle {
|
|
2533
|
+
font-size: var(--fb-font-size-small);
|
|
2534
|
+
color: var(--fb-text-secondary-color);
|
|
2535
|
+
margin-top: 2px;
|
|
2536
|
+
line-height: 1.35;
|
|
2537
|
+
}
|
|
2538
|
+
.fb-toggle-info {
|
|
2539
|
+
flex: 0 0 14px;
|
|
2540
|
+
display: inline-flex;
|
|
2541
|
+
align-items: center;
|
|
2542
|
+
justify-content: center;
|
|
2543
|
+
width: 14px;
|
|
2544
|
+
height: 14px;
|
|
2545
|
+
border-radius: 50%;
|
|
2546
|
+
background: var(--fb-border-color);
|
|
2547
|
+
color: #fff;
|
|
2548
|
+
font-size: 10px;
|
|
2549
|
+
font-weight: 700;
|
|
2550
|
+
font-style: italic;
|
|
2551
|
+
font-family: serif;
|
|
2552
|
+
cursor: help;
|
|
2183
2553
|
}
|
|
2554
|
+
`;
|
|
2555
|
+
doc.head.appendChild(style);
|
|
2556
|
+
}
|
|
2557
|
+
function parseBool(v) {
|
|
2558
|
+
if (typeof v === "boolean") return v;
|
|
2559
|
+
if (typeof v === "string") return v === "true" || v === "on" || v === "1";
|
|
2560
|
+
return false;
|
|
2561
|
+
}
|
|
2562
|
+
function renderBooleanElement(element, ctx, wrapper, pathKey) {
|
|
2563
|
+
var _a;
|
|
2564
|
+
ensureStyles(document);
|
|
2565
|
+
const state = ctx.state;
|
|
2566
|
+
const readonly = isElementReadonly(element, state, ctx);
|
|
2567
|
+
const prefillRaw = ctx.prefill[element.key];
|
|
2568
|
+
const initial = prefillRaw !== void 0 ? parseBool(prefillRaw) : parseBool(element.default);
|
|
2569
|
+
const hiddenInput = document.createElement("input");
|
|
2570
|
+
hiddenInput.type = "hidden";
|
|
2571
|
+
hiddenInput.name = pathKey;
|
|
2572
|
+
hiddenInput.value = initial ? "true" : "false";
|
|
2573
|
+
const row = document.createElement("div");
|
|
2574
|
+
row.className = "fb-toggle-row";
|
|
2575
|
+
row.setAttribute("role", "switch");
|
|
2576
|
+
row.setAttribute("aria-checked", initial ? "true" : "false");
|
|
2577
|
+
if (readonly) {
|
|
2578
|
+
row.setAttribute("aria-disabled", "true");
|
|
2579
|
+
} else {
|
|
2580
|
+
row.tabIndex = 0;
|
|
2581
|
+
}
|
|
2582
|
+
const pill = document.createElement("span");
|
|
2583
|
+
pill.className = "fb-toggle" + (initial ? " fb-on" : "");
|
|
2584
|
+
pill.setAttribute("aria-hidden", "true");
|
|
2585
|
+
row.appendChild(pill);
|
|
2586
|
+
const textBlock = document.createElement("div");
|
|
2587
|
+
textBlock.className = "fb-toggle-text";
|
|
2588
|
+
const titleEl = document.createElement("div");
|
|
2589
|
+
titleEl.className = "fb-toggle-title";
|
|
2590
|
+
titleEl.appendChild(document.createTextNode((_a = element.label) != null ? _a : ""));
|
|
2591
|
+
if (element.description) {
|
|
2592
|
+
const info = document.createElement("span");
|
|
2593
|
+
info.className = "fb-toggle-info";
|
|
2594
|
+
info.textContent = "i";
|
|
2595
|
+
info.title = element.description;
|
|
2596
|
+
titleEl.appendChild(info);
|
|
2597
|
+
}
|
|
2598
|
+
textBlock.appendChild(titleEl);
|
|
2599
|
+
if (element.hint) {
|
|
2600
|
+
const subtitle = document.createElement("div");
|
|
2601
|
+
subtitle.className = "fb-toggle-subtitle";
|
|
2602
|
+
subtitle.textContent = element.hint;
|
|
2603
|
+
textBlock.appendChild(subtitle);
|
|
2604
|
+
}
|
|
2605
|
+
row.appendChild(textBlock);
|
|
2606
|
+
if (!readonly) {
|
|
2607
|
+
const toggle = () => {
|
|
2608
|
+
const next = hiddenInput.value !== "true";
|
|
2609
|
+
hiddenInput.value = next ? "true" : "false";
|
|
2610
|
+
pill.classList.toggle("fb-on", next);
|
|
2611
|
+
row.setAttribute("aria-checked", next ? "true" : "false");
|
|
2612
|
+
if (ctx.instance) ctx.instance.triggerOnChange(pathKey, next);
|
|
2613
|
+
};
|
|
2614
|
+
row.addEventListener("click", (e) => {
|
|
2615
|
+
var _a2;
|
|
2616
|
+
if ((_a2 = e.target) == null ? void 0 : _a2.classList.contains("fb-toggle-info")) {
|
|
2617
|
+
return;
|
|
2618
|
+
}
|
|
2619
|
+
toggle();
|
|
2620
|
+
});
|
|
2621
|
+
row.addEventListener("keydown", (e) => {
|
|
2622
|
+
if (e.key === " " || e.key === "Enter") {
|
|
2623
|
+
e.preventDefault();
|
|
2624
|
+
toggle();
|
|
2625
|
+
}
|
|
2626
|
+
});
|
|
2627
|
+
}
|
|
2628
|
+
wrapper.appendChild(hiddenInput);
|
|
2629
|
+
wrapper.appendChild(row);
|
|
2630
|
+
}
|
|
2631
|
+
function validateBooleanElement(element, key, context) {
|
|
2632
|
+
var _a;
|
|
2633
|
+
const { scopeRoot } = context;
|
|
2634
|
+
const input = scopeRoot.querySelector(
|
|
2635
|
+
`input[type="hidden"][name="${key}"]`
|
|
2636
|
+
);
|
|
2637
|
+
const raw = (_a = input == null ? void 0 : input.value) != null ? _a : "";
|
|
2638
|
+
const value = parseBool(raw);
|
|
2639
|
+
const errors = [];
|
|
2640
|
+
return { value, errors };
|
|
2641
|
+
}
|
|
2642
|
+
function updateBooleanField(_element, fieldPath, value, context) {
|
|
2643
|
+
var _a;
|
|
2644
|
+
const { scopeRoot } = context;
|
|
2645
|
+
const input = scopeRoot.querySelector(
|
|
2646
|
+
`input[type="hidden"][name="${fieldPath}"]`
|
|
2647
|
+
);
|
|
2648
|
+
if (!input) return;
|
|
2649
|
+
const bool = parseBool(value);
|
|
2650
|
+
input.value = bool ? "true" : "false";
|
|
2651
|
+
const row = (_a = input.parentElement) == null ? void 0 : _a.querySelector(".fb-toggle-row");
|
|
2652
|
+
if (row) {
|
|
2653
|
+
row.setAttribute("aria-checked", bool ? "true" : "false");
|
|
2654
|
+
const pill = row.querySelector(".fb-toggle");
|
|
2655
|
+
if (pill) pill.classList.toggle("fb-on", bool);
|
|
2184
2656
|
}
|
|
2185
2657
|
}
|
|
2186
2658
|
|
|
@@ -2286,14 +2758,20 @@ function ensureFileStyles() {
|
|
|
2286
2758
|
}
|
|
2287
2759
|
|
|
2288
2760
|
/* \u2500\u2500\u2500 Wide single-file add tile (empty state) \u2500\u2500\u2500 */
|
|
2761
|
+
/* Flex-wraps: side-by-side when wide enough, stacks upload/library
|
|
2762
|
+
vertically when narrow (e.g. inside a 50/50 container column). */
|
|
2289
2763
|
.fb-wide-tile {
|
|
2290
2764
|
width: 100%;
|
|
2765
|
+
box-sizing: border-box;
|
|
2291
2766
|
border-radius: 0.75rem;
|
|
2292
2767
|
border: 1px dashed #60a5fa;
|
|
2293
2768
|
background: rgba(239,246,255,0.5);
|
|
2294
2769
|
display: flex;
|
|
2770
|
+
flex-wrap: wrap;
|
|
2771
|
+
align-items: stretch;
|
|
2772
|
+
gap: 0;
|
|
2295
2773
|
overflow: hidden;
|
|
2296
|
-
height: 180px;
|
|
2774
|
+
min-height: 180px;
|
|
2297
2775
|
transition: border-color 150ms, background 150ms, box-shadow 150ms;
|
|
2298
2776
|
cursor: pointer;
|
|
2299
2777
|
}
|
|
@@ -2307,9 +2785,12 @@ function ensureFileStyles() {
|
|
|
2307
2785
|
box-shadow: 0 0 0 4px rgba(191,219,254,0.7);
|
|
2308
2786
|
}
|
|
2309
2787
|
|
|
2310
|
-
/* Upload zone inside wide tile
|
|
2788
|
+
/* Upload zone inside wide tile.
|
|
2789
|
+
flex: 1 1 220px \u2014 wants at least 220px; if the container can't fit
|
|
2790
|
+
upload + library on one row (~220 + 176), library wraps below. */
|
|
2311
2791
|
.fb-wide-tile-upload {
|
|
2312
|
-
flex: 1;
|
|
2792
|
+
flex: 1 1 220px;
|
|
2793
|
+
min-height: 140px;
|
|
2313
2794
|
display: flex;
|
|
2314
2795
|
flex-direction: column;
|
|
2315
2796
|
align-items: center;
|
|
@@ -2322,24 +2803,21 @@ function ensureFileStyles() {
|
|
|
2322
2803
|
background: transparent;
|
|
2323
2804
|
border: none;
|
|
2324
2805
|
font-family: inherit;
|
|
2806
|
+
/* Dashed separator from library: right side when in a row, bottom when
|
|
2807
|
+
wrapped (the line then sits between the two stacked cards). */
|
|
2808
|
+
border-right: 1px dashed rgba(96,165,250,0.5);
|
|
2325
2809
|
}
|
|
2326
2810
|
.fb-wide-tile-upload:hover {
|
|
2327
2811
|
background: rgba(191,219,254,0.25);
|
|
2328
2812
|
}
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
.
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
border-left: 1px dashed rgba(96,165,250,0.5);
|
|
2335
|
-
background: transparent;
|
|
2336
|
-
flex-shrink: 0;
|
|
2337
|
-
}
|
|
2338
|
-
|
|
2339
|
-
/* Library zone inside wide tile */
|
|
2813
|
+
/* Library zone inside wide tile.
|
|
2814
|
+
flex: 0 0 176px \u2014 fixed 176px, never grows. Upload fills the rest in
|
|
2815
|
+
row layout. When the tile wraps to two rows on narrow containers,
|
|
2816
|
+
library stays 176px wide on its own row (left-aligned), preserving the
|
|
2817
|
+
visual hierarchy "upload > library" in both layouts. */
|
|
2340
2818
|
.fb-wide-tile-library {
|
|
2341
|
-
|
|
2342
|
-
|
|
2819
|
+
flex: 0 0 176px;
|
|
2820
|
+
min-height: 120px;
|
|
2343
2821
|
display: flex;
|
|
2344
2822
|
flex-direction: column;
|
|
2345
2823
|
align-items: center;
|
|
@@ -2356,6 +2834,10 @@ function ensureFileStyles() {
|
|
|
2356
2834
|
.fb-wide-tile-library:hover {
|
|
2357
2835
|
background: rgba(191,219,254,0.25);
|
|
2358
2836
|
}
|
|
2837
|
+
/* Narrow-tile mode lives in a separate <style> tag (see below) \u2014 the
|
|
2838
|
+
@container rule is appended only when the runtime actually supports
|
|
2839
|
+
container queries, so jsdom (which doesn't) never sees it and stays
|
|
2840
|
+
quiet in test logs. */
|
|
2359
2841
|
|
|
2360
2842
|
/* \u2500\u2500\u2500 Multi-file outer grid container \u2500\u2500\u2500 */
|
|
2361
2843
|
.fb-multi-outer {
|
|
@@ -2734,6 +3216,39 @@ function ensureFileStyles() {
|
|
|
2734
3216
|
}
|
|
2735
3217
|
`;
|
|
2736
3218
|
document.head.appendChild(style);
|
|
3219
|
+
if (typeof CSS !== "undefined" && typeof CSS.supports === "function" && CSS.supports("container-type", "inline-size")) {
|
|
3220
|
+
const cq = document.createElement("style");
|
|
3221
|
+
cq.setAttribute("data-fb-file-styles-cq", "true");
|
|
3222
|
+
cq.textContent = `
|
|
3223
|
+
.fb-wide-tile { container-type: inline-size; }
|
|
3224
|
+
@container (max-width: 408px) {
|
|
3225
|
+
.fb-wide-tile-upload {
|
|
3226
|
+
border-right: none;
|
|
3227
|
+
border-bottom: 1px dashed rgba(96,165,250,0.5);
|
|
3228
|
+
}
|
|
3229
|
+
.fb-wide-tile-library {
|
|
3230
|
+
flex: 1 0 100%;
|
|
3231
|
+
min-height: 0;
|
|
3232
|
+
flex-direction: row;
|
|
3233
|
+
gap: 6px;
|
|
3234
|
+
padding: 8px 12px;
|
|
3235
|
+
font-size: 12px;
|
|
3236
|
+
}
|
|
3237
|
+
.fb-wide-tile-library .fb-wide-tile-library-icon {
|
|
3238
|
+
width: 16px;
|
|
3239
|
+
height: 16px;
|
|
3240
|
+
}
|
|
3241
|
+
.fb-wide-tile-library .fb-wide-tile-library-label {
|
|
3242
|
+
font-size: 12px;
|
|
3243
|
+
font-weight: 500;
|
|
3244
|
+
}
|
|
3245
|
+
.fb-wide-tile-library .fb-wide-tile-library-hint {
|
|
3246
|
+
display: none;
|
|
3247
|
+
}
|
|
3248
|
+
}
|
|
3249
|
+
`;
|
|
3250
|
+
document.head.appendChild(cq);
|
|
3251
|
+
}
|
|
2737
3252
|
}
|
|
2738
3253
|
|
|
2739
3254
|
// src/components/file/dom.ts
|
|
@@ -2895,24 +3410,40 @@ function createTileActions(options) {
|
|
|
2895
3410
|
return btn;
|
|
2896
3411
|
};
|
|
2897
3412
|
if (replaceHandler) {
|
|
2898
|
-
const replaceBtn = makeBtn(
|
|
3413
|
+
const replaceBtn = makeBtn(
|
|
3414
|
+
ICON_REPLACE,
|
|
3415
|
+
t("replaceFile", state),
|
|
3416
|
+
"fb-tile-action-replace"
|
|
3417
|
+
);
|
|
2899
3418
|
replaceBtn.addEventListener("click", () => replaceHandler());
|
|
2900
3419
|
group.appendChild(replaceBtn);
|
|
2901
3420
|
}
|
|
2902
3421
|
if (libraryHandler) {
|
|
2903
|
-
const libBtn = makeBtn(
|
|
3422
|
+
const libBtn = makeBtn(
|
|
3423
|
+
ICON_LIBRARY,
|
|
3424
|
+
t("fromLibrary", state),
|
|
3425
|
+
"fb-tile-action-library"
|
|
3426
|
+
);
|
|
2904
3427
|
libBtn.addEventListener("click", () => libraryHandler());
|
|
2905
3428
|
group.appendChild(libBtn);
|
|
2906
3429
|
}
|
|
2907
3430
|
if (canDownload(state, meta)) {
|
|
2908
|
-
const dlBtn = makeBtn(
|
|
3431
|
+
const dlBtn = makeBtn(
|
|
3432
|
+
ICON_DOWNLOAD,
|
|
3433
|
+
t("downloadFile", state),
|
|
3434
|
+
"fb-tile-action-download"
|
|
3435
|
+
);
|
|
2909
3436
|
dlBtn.addEventListener("click", () => {
|
|
2910
3437
|
triggerTileDownload(resourceId, fileName, state, meta);
|
|
2911
3438
|
});
|
|
2912
3439
|
group.appendChild(dlBtn);
|
|
2913
3440
|
}
|
|
2914
3441
|
if (canOpenInTab(state, meta)) {
|
|
2915
|
-
const openBtn = makeBtn(
|
|
3442
|
+
const openBtn = makeBtn(
|
|
3443
|
+
ICON_OPEN,
|
|
3444
|
+
t("openInNewTab", state),
|
|
3445
|
+
"fb-tile-action-open"
|
|
3446
|
+
);
|
|
2916
3447
|
openBtn.addEventListener("click", () => {
|
|
2917
3448
|
triggerTileOpen(resourceId, state, meta).catch((err) => {
|
|
2918
3449
|
console.error("Open failed:", err);
|
|
@@ -2921,7 +3452,11 @@ function createTileActions(options) {
|
|
|
2921
3452
|
group.appendChild(openBtn);
|
|
2922
3453
|
}
|
|
2923
3454
|
if (canRemove && removeHandler) {
|
|
2924
|
-
const rmBtn = makeBtn(
|
|
3455
|
+
const rmBtn = makeBtn(
|
|
3456
|
+
ICON_REMOVE,
|
|
3457
|
+
t("removeElement", state),
|
|
3458
|
+
"fb-tile-action-remove"
|
|
3459
|
+
);
|
|
2925
3460
|
rmBtn.addEventListener("click", () => {
|
|
2926
3461
|
removeHandler();
|
|
2927
3462
|
});
|
|
@@ -2999,11 +3534,17 @@ function positionZoomPopup(popup, tile) {
|
|
|
2999
3534
|
} else if (tileRect.bottom + margin + popupSize + padding <= window.innerHeight) {
|
|
3000
3535
|
top = tileRect.bottom + margin;
|
|
3001
3536
|
} else {
|
|
3002
|
-
top = Math.max(
|
|
3537
|
+
top = Math.max(
|
|
3538
|
+
padding,
|
|
3539
|
+
Math.min(window.innerHeight - popupSize - padding, tileRect.top)
|
|
3540
|
+
);
|
|
3003
3541
|
}
|
|
3004
3542
|
const tileCenterX = tileRect.left + tileRect.width / 2;
|
|
3005
3543
|
let left = tileCenterX - popupSize / 2;
|
|
3006
|
-
left = Math.max(
|
|
3544
|
+
left = Math.max(
|
|
3545
|
+
padding,
|
|
3546
|
+
Math.min(window.innerWidth - popupSize - padding, left)
|
|
3547
|
+
);
|
|
3007
3548
|
popup.style.top = `${top}px`;
|
|
3008
3549
|
popup.style.left = `${left}px`;
|
|
3009
3550
|
}
|
|
@@ -3047,7 +3588,9 @@ function attachZoomHover(tile, src, alt, actionsEl) {
|
|
|
3047
3588
|
const popup = getOrCreateZoomPopup();
|
|
3048
3589
|
const existingActions = popup.querySelector(".fb-tile-actions");
|
|
3049
3590
|
if (existingActions) existingActions.remove();
|
|
3050
|
-
const img = popup.querySelector(
|
|
3591
|
+
const img = popup.querySelector(
|
|
3592
|
+
".fb-tile-zoom-preview-img"
|
|
3593
|
+
);
|
|
3051
3594
|
img.src = src;
|
|
3052
3595
|
img.alt = alt;
|
|
3053
3596
|
if (actionsEl) {
|
|
@@ -3075,7 +3618,9 @@ function attachZoomHover(tile, src, alt, actionsEl) {
|
|
|
3075
3618
|
});
|
|
3076
3619
|
}
|
|
3077
3620
|
function attachClonedActionListeners(cloned, original) {
|
|
3078
|
-
const originalBtns = Array.from(
|
|
3621
|
+
const originalBtns = Array.from(
|
|
3622
|
+
original.querySelectorAll(".fb-tile-action-btn")
|
|
3623
|
+
);
|
|
3079
3624
|
const clonedBtns = Array.from(cloned.querySelectorAll(".fb-tile-action-btn"));
|
|
3080
3625
|
clonedBtns.forEach((clonedBtn, i) => {
|
|
3081
3626
|
const origBtn = originalBtns[i];
|
|
@@ -3129,7 +3674,9 @@ function renderLocalVideoPreview(container, file, videoType, resourceId, state,
|
|
|
3129
3674
|
return newContainer;
|
|
3130
3675
|
}
|
|
3131
3676
|
function attachVideoButtonHandlers(container, resourceId, state, deps) {
|
|
3132
|
-
const changeBtn = container.querySelector(
|
|
3677
|
+
const changeBtn = container.querySelector(
|
|
3678
|
+
".change-file-btn"
|
|
3679
|
+
);
|
|
3133
3680
|
if (changeBtn) {
|
|
3134
3681
|
changeBtn.onclick = (e) => {
|
|
3135
3682
|
var _a;
|
|
@@ -3137,7 +3684,9 @@ function attachVideoButtonHandlers(container, resourceId, state, deps) {
|
|
|
3137
3684
|
(_a = deps == null ? void 0 : deps.picker) == null ? void 0 : _a.click();
|
|
3138
3685
|
};
|
|
3139
3686
|
}
|
|
3140
|
-
const deleteBtn = container.querySelector(
|
|
3687
|
+
const deleteBtn = container.querySelector(
|
|
3688
|
+
".delete-file-btn"
|
|
3689
|
+
);
|
|
3141
3690
|
if (deleteBtn) {
|
|
3142
3691
|
deleteBtn.onclick = (e) => {
|
|
3143
3692
|
e.stopPropagation();
|
|
@@ -3275,7 +3824,13 @@ async function renderFilePreview(container, resourceId, state, options = {}) {
|
|
|
3275
3824
|
deps
|
|
3276
3825
|
);
|
|
3277
3826
|
} else {
|
|
3278
|
-
await renderUploadedFilePreview(
|
|
3827
|
+
await renderUploadedFilePreview(
|
|
3828
|
+
container,
|
|
3829
|
+
resourceId,
|
|
3830
|
+
fileName,
|
|
3831
|
+
meta,
|
|
3832
|
+
state
|
|
3833
|
+
);
|
|
3279
3834
|
const isVideo = (_a = meta == null ? void 0 : meta.type) == null ? void 0 : _a.startsWith("video/");
|
|
3280
3835
|
if (!isReadonly && !isVideo) {
|
|
3281
3836
|
renderDeleteButton(container, resourceId, state);
|
|
@@ -3304,7 +3859,8 @@ async function renderFilePreviewReadonly(resourceId, state, fileName, options =
|
|
|
3304
3859
|
}
|
|
3305
3860
|
const localFileUrl = (meta == null ? void 0 : meta.file) instanceof File ? getLocalFileUrl(meta.file) : null;
|
|
3306
3861
|
const resolveOpenUrl = async () => {
|
|
3307
|
-
if (state.config.getDownloadUrl)
|
|
3862
|
+
if (state.config.getDownloadUrl)
|
|
3863
|
+
return state.config.getDownloadUrl(resourceId);
|
|
3308
3864
|
if (state.config.getThumbnail) return state.config.getThumbnail(resourceId);
|
|
3309
3865
|
return localFileUrl;
|
|
3310
3866
|
};
|
|
@@ -3446,7 +4002,8 @@ async function fillTileContent(tile, rid, meta, state, actionsEl) {
|
|
|
3446
4002
|
}
|
|
3447
4003
|
} catch (error) {
|
|
3448
4004
|
const err = error instanceof Error ? error : new Error(String(error));
|
|
3449
|
-
if (state.config.onThumbnailError)
|
|
4005
|
+
if (state.config.onThumbnailError)
|
|
4006
|
+
state.config.onThumbnailError(err, rid);
|
|
3450
4007
|
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>`;
|
|
3451
4008
|
}
|
|
3452
4009
|
} else {
|
|
@@ -3479,7 +4036,8 @@ async function fillTileContent(tile, rid, meta, state, actionsEl) {
|
|
|
3479
4036
|
}
|
|
3480
4037
|
} catch (error) {
|
|
3481
4038
|
const err = error instanceof Error ? error : new Error(String(error));
|
|
3482
|
-
if (state.config.onThumbnailError)
|
|
4039
|
+
if (state.config.onThumbnailError)
|
|
4040
|
+
state.config.onThumbnailError(err, rid);
|
|
3483
4041
|
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>`;
|
|
3484
4042
|
}
|
|
3485
4043
|
} else {
|
|
@@ -3513,7 +4071,8 @@ async function forceDownload(resourceId, fileName, state) {
|
|
|
3513
4071
|
if (fileUrl) {
|
|
3514
4072
|
const finalUrl = fileUrl.startsWith("http") ? fileUrl : new URL(fileUrl, window.location.href).href;
|
|
3515
4073
|
const response = await fetch(finalUrl);
|
|
3516
|
-
if (!response.ok)
|
|
4074
|
+
if (!response.ok)
|
|
4075
|
+
throw new Error(`HTTP error! status: ${response.status}`);
|
|
3517
4076
|
const blob = await response.blob();
|
|
3518
4077
|
downloadBlob(blob, fileName);
|
|
3519
4078
|
} else {
|
|
@@ -3562,7 +4121,9 @@ async function uploadSingleFile(file, state) {
|
|
|
3562
4121
|
} catch (error) {
|
|
3563
4122
|
const err = error instanceof Error ? error : new Error(String(error));
|
|
3564
4123
|
if (state.config.onUploadError) state.config.onUploadError(err, file);
|
|
3565
|
-
|
|
4124
|
+
const wrapped = new Error(`File upload failed: ${err.message}`);
|
|
4125
|
+
wrapped.cause = err;
|
|
4126
|
+
throw wrapped;
|
|
3566
4127
|
}
|
|
3567
4128
|
}
|
|
3568
4129
|
async function handleFileSelect(opts) {
|
|
@@ -3675,7 +4236,9 @@ function filterAndSlice(allFiles, currentCount, constraints, state) {
|
|
|
3675
4236
|
const rejectedBySize = afterMime.filter(
|
|
3676
4237
|
(f) => !isFileSizeAllowed(f, constraints.maxSize)
|
|
3677
4238
|
);
|
|
3678
|
-
const valid = afterMime.filter(
|
|
4239
|
+
const valid = afterMime.filter(
|
|
4240
|
+
(f) => isFileSizeAllowed(f, constraints.maxSize)
|
|
4241
|
+
);
|
|
3679
4242
|
const remaining = constraints.maxCount === Infinity ? valid.length : Math.max(0, constraints.maxCount - currentCount);
|
|
3680
4243
|
const accepted = valid.slice(0, remaining);
|
|
3681
4244
|
const skippedByCount = valid.length - accepted.length;
|
|
@@ -3688,7 +4251,13 @@ function filterAndSlice(allFiles, currentCount, constraints, state) {
|
|
|
3688
4251
|
if (rejectedByMime.length > 0) {
|
|
3689
4252
|
const mimes = constraints.allowedMimes.join(", ");
|
|
3690
4253
|
const names = rejectedByMime.map((f) => f.name).join(", ");
|
|
3691
|
-
errorParts.push(
|
|
4254
|
+
errorParts.push(
|
|
4255
|
+
t("invalidFileMime", state, {
|
|
4256
|
+
name: names,
|
|
4257
|
+
type: rejectedByMime.map((f) => f.type).join(", "),
|
|
4258
|
+
mimes
|
|
4259
|
+
})
|
|
4260
|
+
);
|
|
3692
4261
|
}
|
|
3693
4262
|
if (rejectedBySize.length > 0) {
|
|
3694
4263
|
const names = rejectedBySize.map((f) => f.name).join(", ");
|
|
@@ -3713,7 +4282,8 @@ async function uploadBatch(accepted, resourceIds, listEl, state) {
|
|
|
3713
4282
|
const addTile = (_a = tilesWrap.querySelector(".fb-multi-add-tile-js")) != null ? _a : tilesWrap.querySelector(".fb-tile-add");
|
|
3714
4283
|
if (addTile) addTile.style.display = "none";
|
|
3715
4284
|
}
|
|
3716
|
-
|
|
4285
|
+
const failures = [];
|
|
4286
|
+
await Promise.allSettled(
|
|
3717
4287
|
accepted.map(async (file) => {
|
|
3718
4288
|
const placeholder = createUploadingTile(file.name, state);
|
|
3719
4289
|
if (listEl) {
|
|
@@ -3730,11 +4300,27 @@ async function uploadBatch(accepted, resourceIds, listEl, state) {
|
|
|
3730
4300
|
file: void 0
|
|
3731
4301
|
});
|
|
3732
4302
|
resourceIds.push(rid);
|
|
4303
|
+
} catch (err) {
|
|
4304
|
+
const wrapped = err instanceof Error ? err : new Error(String(err));
|
|
4305
|
+
const cause = wrapped.cause;
|
|
4306
|
+
const root = cause instanceof Error ? cause : cause !== void 0 ? new Error(String(cause)) : wrapped;
|
|
4307
|
+
failures.push({ file, error: root });
|
|
3733
4308
|
} finally {
|
|
3734
4309
|
placeholder.remove();
|
|
3735
4310
|
}
|
|
3736
4311
|
})
|
|
3737
4312
|
);
|
|
4313
|
+
return { failures };
|
|
4314
|
+
}
|
|
4315
|
+
function buildBatchErrorMessage(filterError, failures, state) {
|
|
4316
|
+
if (failures.length === 0) return filterError;
|
|
4317
|
+
const uploadMsg = failures.map(
|
|
4318
|
+
(f) => t("uploadFailed", state, {
|
|
4319
|
+
name: f.file.name,
|
|
4320
|
+
error: f.error.message
|
|
4321
|
+
})
|
|
4322
|
+
).join(" \u2022 ");
|
|
4323
|
+
return filterError ? `${filterError} \u2022 ${uploadMsg}` : uploadMsg;
|
|
3738
4324
|
}
|
|
3739
4325
|
function setupFilesDropHandler(filesContainer, resourceIds, state, updateCallback, constraints, pathKey, instance) {
|
|
3740
4326
|
setupDragAndDrop(filesContainer, async (files) => {
|
|
@@ -3751,7 +4337,13 @@ function setupFilesDropHandler(filesContainer, resourceIds, state, updateCallbac
|
|
|
3751
4337
|
clearFileError(filesContainer);
|
|
3752
4338
|
}
|
|
3753
4339
|
const list = (_a = filesContainer.querySelector(".files-list")) != null ? _a : filesContainer;
|
|
3754
|
-
await uploadBatch(accepted, resourceIds, list, state);
|
|
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
|
+
}
|
|
3755
4347
|
updateCallback();
|
|
3756
4348
|
if (instance && pathKey && !state.config.readonly) {
|
|
3757
4349
|
instance.triggerOnChange(pathKey, resourceIds);
|
|
@@ -3774,7 +4366,20 @@ function setupFilesPickerHandler(filesPicker, resourceIds, state, updateCallback
|
|
|
3774
4366
|
clearFileError(wrapperEl);
|
|
3775
4367
|
}
|
|
3776
4368
|
const listEl = wrapperEl == null ? void 0 : wrapperEl.querySelector(".files-list");
|
|
3777
|
-
|
|
4369
|
+
const { failures } = await uploadBatch(
|
|
4370
|
+
accepted,
|
|
4371
|
+
resourceIds,
|
|
4372
|
+
listEl != null ? listEl : null,
|
|
4373
|
+
state
|
|
4374
|
+
);
|
|
4375
|
+
if (wrapperEl) {
|
|
4376
|
+
const combined = buildBatchErrorMessage(errorMessage, failures, state);
|
|
4377
|
+
if (combined) {
|
|
4378
|
+
showFileError(wrapperEl, combined);
|
|
4379
|
+
} else {
|
|
4380
|
+
clearFileError(wrapperEl);
|
|
4381
|
+
}
|
|
4382
|
+
}
|
|
3778
4383
|
updateCallback();
|
|
3779
4384
|
filesPicker.value = "";
|
|
3780
4385
|
if (instance && pathKey && !state.config.readonly) {
|
|
@@ -3807,10 +4412,17 @@ function validatePickedResource(resource, allowedExtensions, allowedMimes, maxSi
|
|
|
3807
4412
|
}
|
|
3808
4413
|
if (!isMimeAllowed(resource.type, allowedMimes)) {
|
|
3809
4414
|
const mimes = allowedMimes.join(", ");
|
|
3810
|
-
return t("invalidFileMime", state, {
|
|
4415
|
+
return t("invalidFileMime", state, {
|
|
4416
|
+
name: resource.name,
|
|
4417
|
+
type: resource.type,
|
|
4418
|
+
mimes
|
|
4419
|
+
});
|
|
3811
4420
|
}
|
|
3812
4421
|
if (!isSizeWithinLimit(resource.size, maxSizeMB)) {
|
|
3813
|
-
return t("fileTooLarge", state, {
|
|
4422
|
+
return t("fileTooLarge", state, {
|
|
4423
|
+
name: resource.name,
|
|
4424
|
+
maxSize: maxSizeMB
|
|
4425
|
+
});
|
|
3814
4426
|
}
|
|
3815
4427
|
return null;
|
|
3816
4428
|
}
|
|
@@ -3871,7 +4483,13 @@ async function handleLibraryPickMulti(state, element, wrapper, fieldPath, resour
|
|
|
3871
4483
|
return true;
|
|
3872
4484
|
});
|
|
3873
4485
|
const validItems = deduped.filter((r) => {
|
|
3874
|
-
const err = validatePickedResource(
|
|
4486
|
+
const err = validatePickedResource(
|
|
4487
|
+
r,
|
|
4488
|
+
allowedExtensions,
|
|
4489
|
+
allowedMimes,
|
|
4490
|
+
maxSizeMB,
|
|
4491
|
+
state
|
|
4492
|
+
);
|
|
3875
4493
|
return err === null;
|
|
3876
4494
|
});
|
|
3877
4495
|
const freshRemaining = maxCount === Infinity ? validItems.length : Math.max(0, maxCount - resourceIds.length);
|
|
@@ -3916,14 +4534,22 @@ async function handleLibraryPickSingle(state, element, container, fileWrapper, p
|
|
|
3916
4534
|
}
|
|
3917
4535
|
if (picked.length === 0) return;
|
|
3918
4536
|
const first = picked[0];
|
|
3919
|
-
const validationError = validatePickedResource(
|
|
4537
|
+
const validationError = validatePickedResource(
|
|
4538
|
+
first,
|
|
4539
|
+
allowedExtensions,
|
|
4540
|
+
allowedMimes,
|
|
4541
|
+
maxSizeMB,
|
|
4542
|
+
state
|
|
4543
|
+
);
|
|
3920
4544
|
if (validationError !== null) {
|
|
3921
4545
|
showFileError(container, validationError);
|
|
3922
4546
|
return;
|
|
3923
4547
|
}
|
|
3924
4548
|
clearFileError(container);
|
|
3925
4549
|
registerPickedResource(first, state);
|
|
3926
|
-
let hiddenInput = fileWrapper.querySelector(
|
|
4550
|
+
let hiddenInput = fileWrapper.querySelector(
|
|
4551
|
+
'input[type="hidden"]'
|
|
4552
|
+
);
|
|
3927
4553
|
if (!hiddenInput) {
|
|
3928
4554
|
hiddenInput = document.createElement("input");
|
|
3929
4555
|
hiddenInput.type = "hidden";
|
|
@@ -3992,21 +4618,21 @@ function buildWideTile(state, hasLibrary, onUploadClick, onLibraryClick, isDragO
|
|
|
3992
4618
|
};
|
|
3993
4619
|
outer.appendChild(uploadBtn);
|
|
3994
4620
|
if (hasLibrary && onLibraryClick) {
|
|
3995
|
-
const divider = document.createElement("div");
|
|
3996
|
-
divider.className = "fb-wide-tile-divider";
|
|
3997
|
-
outer.appendChild(divider);
|
|
3998
4621
|
const libBtn = document.createElement("button");
|
|
3999
4622
|
libBtn.type = "button";
|
|
4000
4623
|
libBtn.className = "fb-wide-tile-library fb-file-library-card";
|
|
4001
4624
|
const libIcon = document.createElement("span");
|
|
4625
|
+
libIcon.className = "fb-wide-tile-library-icon";
|
|
4002
4626
|
libIcon.style.cssText = "width:28px;height:28px;display:block;flex-shrink:0;";
|
|
4003
4627
|
libIcon.innerHTML = ICON_LIBRARY2;
|
|
4004
4628
|
libBtn.appendChild(libIcon);
|
|
4005
4629
|
const libLabel = document.createElement("div");
|
|
4630
|
+
libLabel.className = "fb-wide-tile-library-label";
|
|
4006
4631
|
libLabel.style.cssText = "font-size:13px;font-weight:600;text-align:center;";
|
|
4007
4632
|
libLabel.textContent = t("fromLibrary", state);
|
|
4008
4633
|
libBtn.appendChild(libLabel);
|
|
4009
4634
|
const libHint = document.createElement("div");
|
|
4635
|
+
libHint.className = "fb-wide-tile-library-hint";
|
|
4010
4636
|
libHint.style.cssText = "font-size:11px;opacity:0.75;text-align:center;";
|
|
4011
4637
|
libHint.textContent = t("libraryHint", state);
|
|
4012
4638
|
libBtn.appendChild(libHint);
|
|
@@ -4087,7 +4713,8 @@ function renderSingleFileFilled(fileContainer, resourceId, state, deps, extras)
|
|
|
4087
4713
|
grid.appendChild(tile);
|
|
4088
4714
|
fileContainer.className = "file-preview-container";
|
|
4089
4715
|
fileContainer.removeAttribute("style");
|
|
4090
|
-
while (fileContainer.firstChild)
|
|
4716
|
+
while (fileContainer.firstChild)
|
|
4717
|
+
fileContainer.removeChild(fileContainer.firstChild);
|
|
4091
4718
|
fileContainer.appendChild(outer);
|
|
4092
4719
|
}
|
|
4093
4720
|
function buildMultiAddTile(state, hasLibrary, onUploadClick, onLibraryClick, isDragOver = false) {
|
|
@@ -4169,7 +4796,9 @@ function buildMetaLine(state, element, ridCount, maxCount, canClearAll, onClearA
|
|
|
4169
4796
|
metaText.appendChild(sizeSpan);
|
|
4170
4797
|
metaText.appendChild(buildMetaDot());
|
|
4171
4798
|
}
|
|
4172
|
-
const exts = getAllowedExtensions(
|
|
4799
|
+
const exts = getAllowedExtensions(
|
|
4800
|
+
element.accept
|
|
4801
|
+
);
|
|
4173
4802
|
if (exts.length > 0) {
|
|
4174
4803
|
const fmtSpan = document.createElement("span");
|
|
4175
4804
|
fmtSpan.className = "fb-meta-mono";
|
|
@@ -4420,7 +5049,9 @@ function renderFileElementEdit(element, ctx, wrapper, pathKey) {
|
|
|
4420
5049
|
},
|
|
4421
5050
|
onRemove() {
|
|
4422
5051
|
var _a2;
|
|
4423
|
-
const hiddenInput = fileWrapper.querySelector(
|
|
5052
|
+
const hiddenInput = fileWrapper.querySelector(
|
|
5053
|
+
'input[type="hidden"]'
|
|
5054
|
+
);
|
|
4424
5055
|
const currentRid = hiddenInput == null ? void 0 : hiddenInput.value;
|
|
4425
5056
|
if (currentRid) {
|
|
4426
5057
|
releaseLocalFileUrl((_a2 = state.resourceIndex.get(currentRid)) == null ? void 0 : _a2.file);
|
|
@@ -4430,7 +5061,9 @@ function renderFileElementEdit(element, ctx, wrapper, pathKey) {
|
|
|
4430
5061
|
}
|
|
4431
5062
|
};
|
|
4432
5063
|
const buildSingleExtras = () => {
|
|
4433
|
-
const hasLibrary = Boolean(
|
|
5064
|
+
const hasLibrary = Boolean(
|
|
5065
|
+
state.config.pickExistingFiles && !element.disableLibrary
|
|
5066
|
+
);
|
|
4434
5067
|
return {
|
|
4435
5068
|
replaceHandler: state.config.uploadFile ? () => picker.click() : null,
|
|
4436
5069
|
libraryHandler: hasLibrary ? () => {
|
|
@@ -4442,7 +5075,13 @@ function renderFileElementEdit(element, ctx, wrapper, pathKey) {
|
|
|
4442
5075
|
pathKey,
|
|
4443
5076
|
pathKey,
|
|
4444
5077
|
async (rid) => {
|
|
4445
|
-
renderSingleFileFilled(
|
|
5078
|
+
renderSingleFileFilled(
|
|
5079
|
+
fileContainer,
|
|
5080
|
+
rid,
|
|
5081
|
+
state,
|
|
5082
|
+
buildDeps(),
|
|
5083
|
+
buildSingleExtras()
|
|
5084
|
+
);
|
|
4446
5085
|
},
|
|
4447
5086
|
ctx.instance
|
|
4448
5087
|
).catch((err) => {
|
|
@@ -4458,14 +5097,21 @@ function renderFileElementEdit(element, ctx, wrapper, pathKey) {
|
|
|
4458
5097
|
setupDrop: handlers.setupDrop,
|
|
4459
5098
|
onRemove: handlers.onRemove,
|
|
4460
5099
|
onAfterUpload: (container, rid) => {
|
|
4461
|
-
renderSingleFileFilled(
|
|
5100
|
+
renderSingleFileFilled(
|
|
5101
|
+
container,
|
|
5102
|
+
rid,
|
|
5103
|
+
state,
|
|
5104
|
+
buildDeps(),
|
|
5105
|
+
buildSingleExtras()
|
|
5106
|
+
);
|
|
4462
5107
|
}
|
|
4463
5108
|
});
|
|
4464
5109
|
const renderEmptySingleState = () => {
|
|
4465
5110
|
ensureFileStyles();
|
|
4466
5111
|
fileContainer.className = "file-preview-container";
|
|
4467
5112
|
fileContainer.removeAttribute("style");
|
|
4468
|
-
while (fileContainer.firstChild)
|
|
5113
|
+
while (fileContainer.firstChild)
|
|
5114
|
+
fileContainer.removeChild(fileContainer.firstChild);
|
|
4469
5115
|
const onLibraryClick = buildSingleExtras().libraryHandler;
|
|
4470
5116
|
const wideTile = buildWideTile(
|
|
4471
5117
|
state,
|
|
@@ -4610,7 +5256,13 @@ function renderFilesElementEdit(element, ctx, wrapper, pathKey) {
|
|
|
4610
5256
|
}
|
|
4611
5257
|
function renderMultipleFileElementEdit(element, ctx, wrapper, pathKey) {
|
|
4612
5258
|
var _a;
|
|
4613
|
-
setupMultiFileEditMode(
|
|
5259
|
+
setupMultiFileEditMode(
|
|
5260
|
+
element,
|
|
5261
|
+
ctx,
|
|
5262
|
+
wrapper,
|
|
5263
|
+
pathKey,
|
|
5264
|
+
(_a = element.maxCount) != null ? _a : Infinity
|
|
5265
|
+
);
|
|
4614
5266
|
}
|
|
4615
5267
|
|
|
4616
5268
|
// src/components/file/validate.ts
|
|
@@ -4793,7 +5445,11 @@ function renderMultiFileReadonly(rids, state, wrapper, pathKey, _marginTop) {
|
|
|
4793
5445
|
const placeholder = placeholders[i];
|
|
4794
5446
|
const meta = state.resourceIndex.get(resourceId);
|
|
4795
5447
|
renderFilePreviewReadonly(resourceId, state, meta == null ? void 0 : meta.name).then((tile) => {
|
|
4796
|
-
tile.classList.add(
|
|
5448
|
+
tile.classList.add(
|
|
5449
|
+
"fb-readonly-tile",
|
|
5450
|
+
"fb-checker",
|
|
5451
|
+
"fb-tile-resource"
|
|
5452
|
+
);
|
|
4797
5453
|
tile.dataset.resourceId = resourceId;
|
|
4798
5454
|
placeholder.replaceWith(tile);
|
|
4799
5455
|
}).catch(() => {
|
|
@@ -5144,51 +5800,25 @@ function renderMultipleColourElement(element, ctx, wrapper, pathKey) {
|
|
|
5144
5800
|
removeBtn.style.pointerEvents = disabled ? "none" : "auto";
|
|
5145
5801
|
});
|
|
5146
5802
|
}
|
|
5147
|
-
let
|
|
5148
|
-
let countDisplay = null;
|
|
5803
|
+
let addUpdate = null;
|
|
5149
5804
|
if (!readonly) {
|
|
5150
|
-
|
|
5151
|
-
|
|
5152
|
-
|
|
5153
|
-
|
|
5154
|
-
|
|
5155
|
-
|
|
5156
|
-
|
|
5157
|
-
|
|
5158
|
-
|
|
5159
|
-
|
|
5160
|
-
|
|
5161
|
-
|
|
5162
|
-
|
|
5163
|
-
|
|
5164
|
-
addBtn.style.backgroundColor = "var(--fb-background-hover-color)";
|
|
5165
|
-
});
|
|
5166
|
-
addBtn.addEventListener("mouseleave", () => {
|
|
5167
|
-
addBtn.style.backgroundColor = "transparent";
|
|
5168
|
-
});
|
|
5169
|
-
addBtn.onclick = () => {
|
|
5170
|
-
const defaultColour = element.default || "#000000";
|
|
5171
|
-
values.push(defaultColour);
|
|
5172
|
-
addColourItem(defaultColour);
|
|
5173
|
-
updateAddButton();
|
|
5174
|
-
updateRemoveButtons();
|
|
5175
|
-
};
|
|
5176
|
-
countDisplay = document.createElement("span");
|
|
5177
|
-
countDisplay.className = "text-sm text-gray-500";
|
|
5178
|
-
addRow.appendChild(addBtn);
|
|
5179
|
-
addRow.appendChild(countDisplay);
|
|
5180
|
-
wrapper.appendChild(addRow);
|
|
5805
|
+
const handle = createAddItemRow(
|
|
5806
|
+
"colour",
|
|
5807
|
+
() => {
|
|
5808
|
+
const defaultColour = element.default || "#000000";
|
|
5809
|
+
values.push(defaultColour);
|
|
5810
|
+
addColourItem(defaultColour);
|
|
5811
|
+
updateAddButton();
|
|
5812
|
+
updateRemoveButtons();
|
|
5813
|
+
},
|
|
5814
|
+
{ label: element.addLabel }
|
|
5815
|
+
);
|
|
5816
|
+
addUpdate = handle.update;
|
|
5817
|
+
mountCounterInLabel(wrapper, handle.counter);
|
|
5818
|
+
wrapper.appendChild(handle.row);
|
|
5181
5819
|
}
|
|
5182
5820
|
function updateAddButton() {
|
|
5183
|
-
if (
|
|
5184
|
-
const addBtn = addRow.querySelector(".add-colour-btn");
|
|
5185
|
-
if (addBtn) {
|
|
5186
|
-
const disabled = values.length >= maxCount;
|
|
5187
|
-
addBtn.disabled = disabled;
|
|
5188
|
-
addBtn.style.opacity = disabled ? "0.5" : "1";
|
|
5189
|
-
addBtn.style.pointerEvents = disabled ? "none" : "auto";
|
|
5190
|
-
}
|
|
5191
|
-
countDisplay.textContent = `${values.length}/${maxCount === Infinity ? "\u221E" : maxCount}`;
|
|
5821
|
+
if (addUpdate) addUpdate(values.length, maxCount);
|
|
5192
5822
|
}
|
|
5193
5823
|
values.forEach((value) => addColourItem(value));
|
|
5194
5824
|
updateAddButton();
|
|
@@ -5265,7 +5895,7 @@ function validateColourElement(element, key, context) {
|
|
|
5265
5895
|
};
|
|
5266
5896
|
if (element.multiple) {
|
|
5267
5897
|
const hexInputs = scopeRoot.querySelectorAll(
|
|
5268
|
-
`[name^="${key}["].colour-hex-input`
|
|
5898
|
+
`[name^="${key}\\["].colour-hex-input`
|
|
5269
5899
|
);
|
|
5270
5900
|
const values = [];
|
|
5271
5901
|
hexInputs.forEach((input, index) => {
|
|
@@ -5315,7 +5945,7 @@ function updateColourField(element, fieldPath, value, context) {
|
|
|
5315
5945
|
return;
|
|
5316
5946
|
}
|
|
5317
5947
|
const hexInputs = scopeRoot.querySelectorAll(
|
|
5318
|
-
`[name^="${fieldPath}["].colour-hex-input`
|
|
5948
|
+
`[name^="${fieldPath}\\["].colour-hex-input`
|
|
5319
5949
|
);
|
|
5320
5950
|
hexInputs.forEach((hexInput, index) => {
|
|
5321
5951
|
if (index < value.length) {
|
|
@@ -5323,6 +5953,7 @@ function updateColourField(element, fieldPath, value, context) {
|
|
|
5323
5953
|
hexInput.value = normalized;
|
|
5324
5954
|
hexInput.classList.remove("invalid");
|
|
5325
5955
|
hexInput.title = "";
|
|
5956
|
+
clearFieldError(hexInput);
|
|
5326
5957
|
const wrapper = hexInput.closest(".colour-picker-wrapper");
|
|
5327
5958
|
if (wrapper) {
|
|
5328
5959
|
const swatch = wrapper.querySelector(".colour-swatch");
|
|
@@ -5352,6 +5983,7 @@ function updateColourField(element, fieldPath, value, context) {
|
|
|
5352
5983
|
hexInput.value = normalized;
|
|
5353
5984
|
hexInput.classList.remove("invalid");
|
|
5354
5985
|
hexInput.title = "";
|
|
5986
|
+
clearFieldError(hexInput);
|
|
5355
5987
|
const wrapper = hexInput.closest(".colour-picker-wrapper");
|
|
5356
5988
|
if (wrapper) {
|
|
5357
5989
|
const swatch = wrapper.querySelector(".colour-swatch");
|
|
@@ -5630,50 +6262,24 @@ function renderMultipleSliderElement(element, ctx, wrapper, pathKey) {
|
|
|
5630
6262
|
removeBtn.style.pointerEvents = disabled ? "none" : "auto";
|
|
5631
6263
|
});
|
|
5632
6264
|
}
|
|
5633
|
-
let
|
|
5634
|
-
let countDisplay = null;
|
|
6265
|
+
let addUpdate = null;
|
|
5635
6266
|
if (!readonly) {
|
|
5636
|
-
|
|
5637
|
-
|
|
5638
|
-
|
|
5639
|
-
|
|
5640
|
-
|
|
5641
|
-
|
|
5642
|
-
|
|
5643
|
-
|
|
5644
|
-
|
|
5645
|
-
|
|
5646
|
-
|
|
5647
|
-
|
|
5648
|
-
|
|
5649
|
-
addBtn.addEventListener("mouseenter", () => {
|
|
5650
|
-
addBtn.style.backgroundColor = "var(--fb-background-hover-color)";
|
|
5651
|
-
});
|
|
5652
|
-
addBtn.addEventListener("mouseleave", () => {
|
|
5653
|
-
addBtn.style.backgroundColor = "transparent";
|
|
5654
|
-
});
|
|
5655
|
-
addBtn.onclick = () => {
|
|
5656
|
-
values.push(defaultValue);
|
|
5657
|
-
addSliderItem(defaultValue);
|
|
5658
|
-
updateAddButton();
|
|
5659
|
-
updateRemoveButtons();
|
|
5660
|
-
};
|
|
5661
|
-
countDisplay = document.createElement("span");
|
|
5662
|
-
countDisplay.className = "text-sm text-gray-500";
|
|
5663
|
-
addRow.appendChild(addBtn);
|
|
5664
|
-
addRow.appendChild(countDisplay);
|
|
5665
|
-
wrapper.appendChild(addRow);
|
|
6267
|
+
const handle = createAddItemRow(
|
|
6268
|
+
"slider",
|
|
6269
|
+
() => {
|
|
6270
|
+
values.push(defaultValue);
|
|
6271
|
+
addSliderItem(defaultValue);
|
|
6272
|
+
updateAddButton();
|
|
6273
|
+
updateRemoveButtons();
|
|
6274
|
+
},
|
|
6275
|
+
{ label: element.addLabel }
|
|
6276
|
+
);
|
|
6277
|
+
addUpdate = handle.update;
|
|
6278
|
+
mountCounterInLabel(wrapper, handle.counter);
|
|
6279
|
+
wrapper.appendChild(handle.row);
|
|
5666
6280
|
}
|
|
5667
6281
|
function updateAddButton() {
|
|
5668
|
-
if (
|
|
5669
|
-
const addBtn = addRow.querySelector(".add-slider-btn");
|
|
5670
|
-
if (addBtn) {
|
|
5671
|
-
const disabled = values.length >= maxCount;
|
|
5672
|
-
addBtn.disabled = disabled;
|
|
5673
|
-
addBtn.style.opacity = disabled ? "0.5" : "1";
|
|
5674
|
-
addBtn.style.pointerEvents = disabled ? "none" : "auto";
|
|
5675
|
-
}
|
|
5676
|
-
countDisplay.textContent = `${values.length}/${maxCount === Infinity ? "\u221E" : maxCount}`;
|
|
6282
|
+
if (addUpdate) addUpdate(values.length, maxCount);
|
|
5677
6283
|
}
|
|
5678
6284
|
values.forEach((value) => addSliderItem(value));
|
|
5679
6285
|
updateAddButton();
|
|
@@ -5785,7 +6391,7 @@ function validateSliderElement(element, key, context) {
|
|
|
5785
6391
|
};
|
|
5786
6392
|
if (element.multiple) {
|
|
5787
6393
|
const sliders = scopeRoot.querySelectorAll(
|
|
5788
|
-
`input[type="range"][name^="${key}["]`
|
|
6394
|
+
`input[type="range"][name^="${key}\\["]`
|
|
5789
6395
|
);
|
|
5790
6396
|
const values = [];
|
|
5791
6397
|
sliders.forEach((slider, index) => {
|
|
@@ -5837,7 +6443,7 @@ function updateSliderField(element, fieldPath, value, context) {
|
|
|
5837
6443
|
return;
|
|
5838
6444
|
}
|
|
5839
6445
|
const sliders = scopeRoot.querySelectorAll(
|
|
5840
|
-
`input[type="range"][name^="${fieldPath}["]`
|
|
6446
|
+
`input[type="range"][name^="${fieldPath}\\["]`
|
|
5841
6447
|
);
|
|
5842
6448
|
sliders.forEach((slider, index) => {
|
|
5843
6449
|
if (index < value.length && value[index] !== null) {
|
|
@@ -5865,6 +6471,7 @@ function updateSliderField(element, fieldPath, value, context) {
|
|
|
5865
6471
|
}
|
|
5866
6472
|
slider.classList.remove("invalid");
|
|
5867
6473
|
slider.title = "";
|
|
6474
|
+
clearFieldError(slider);
|
|
5868
6475
|
}
|
|
5869
6476
|
});
|
|
5870
6477
|
if (value.length !== sliders.length) {
|
|
@@ -5901,6 +6508,7 @@ function updateSliderField(element, fieldPath, value, context) {
|
|
|
5901
6508
|
}
|
|
5902
6509
|
slider.classList.remove("invalid");
|
|
5903
6510
|
slider.title = "";
|
|
6511
|
+
clearFieldError(slider);
|
|
5904
6512
|
}
|
|
5905
6513
|
}
|
|
5906
6514
|
}
|
|
@@ -6025,6 +6633,37 @@ function getChildWrapperClass(isSlides, columns) {
|
|
|
6025
6633
|
const cols = columns || 1;
|
|
6026
6634
|
return cols === 1 ? "space-y-2" : `grid grid-cols-${cols} gap-2`;
|
|
6027
6635
|
}
|
|
6636
|
+
function mountRemoveButton(item, onRemove, state) {
|
|
6637
|
+
const rem = document.createElement("button");
|
|
6638
|
+
rem.type = "button";
|
|
6639
|
+
rem.className = "fb-item-remove";
|
|
6640
|
+
rem.setAttribute("aria-label", t("removeElement", state));
|
|
6641
|
+
rem.style.cssText = `
|
|
6642
|
+
width: 24px;
|
|
6643
|
+
height: 24px;
|
|
6644
|
+
display: inline-flex;
|
|
6645
|
+
align-items: center;
|
|
6646
|
+
justify-content: center;
|
|
6647
|
+
padding: 0;
|
|
6648
|
+
border: 0;
|
|
6649
|
+
border-radius: 4px;
|
|
6650
|
+
cursor: pointer;
|
|
6651
|
+
flex-shrink: 0;
|
|
6652
|
+
`;
|
|
6653
|
+
rem.innerHTML = BIN_ICON_SVG;
|
|
6654
|
+
rem.onclick = onRemove;
|
|
6655
|
+
const labelRow = item.querySelector("[data-fb-label-row]");
|
|
6656
|
+
if (labelRow) {
|
|
6657
|
+
rem.style.marginLeft = "auto";
|
|
6658
|
+
labelRow.appendChild(rem);
|
|
6659
|
+
return;
|
|
6660
|
+
}
|
|
6661
|
+
rem.style.position = "absolute";
|
|
6662
|
+
rem.style.top = "8px";
|
|
6663
|
+
rem.style.right = "8px";
|
|
6664
|
+
item.style.position = "relative";
|
|
6665
|
+
item.appendChild(rem);
|
|
6666
|
+
}
|
|
6028
6667
|
function renderMultipleContainerElement(element, ctx, wrapper, _pathKey) {
|
|
6029
6668
|
var _a, _b, _c, _d;
|
|
6030
6669
|
const state = ctx.state;
|
|
@@ -6032,15 +6671,13 @@ function renderMultipleContainerElement(element, ctx, wrapper, _pathKey) {
|
|
|
6032
6671
|
const childInheritedReadonly = containerIsReadonly || ctx.inheritedReadonly;
|
|
6033
6672
|
const containerWrap = document.createElement("div");
|
|
6034
6673
|
containerWrap.className = "border border-gray-200 rounded-lg p-2 bg-gray-50";
|
|
6035
|
-
const countDisplay = document.createElement("span");
|
|
6036
|
-
countDisplay.className = "text-sm text-gray-500";
|
|
6037
6674
|
const itemsWrap = document.createElement("div");
|
|
6038
6675
|
const isSlides = element.displayMode === "slides";
|
|
6039
6676
|
if (isSlides) {
|
|
6040
6677
|
itemsWrap.className = "fb-container-slides";
|
|
6041
6678
|
const slideCols = element.columns;
|
|
6042
6679
|
const gridTemplateColumns = typeof slideCols === "number" && slideCols > 0 ? `repeat(${slideCols}, 1fr)` : "repeat(auto-fit, minmax(280px, 1fr))";
|
|
6043
|
-
itemsWrap.style.cssText = `display:grid;grid-template-columns:${gridTemplateColumns};gap:
|
|
6680
|
+
itemsWrap.style.cssText = `display:grid;grid-template-columns:${gridTemplateColumns};gap:var(--fb-slides-gap, 14px);align-items:start;`;
|
|
6044
6681
|
} else {
|
|
6045
6682
|
itemsWrap.className = "space-y-2";
|
|
6046
6683
|
}
|
|
@@ -6055,93 +6692,68 @@ function renderMultipleContainerElement(element, ctx, wrapper, _pathKey) {
|
|
|
6055
6692
|
const pre = Array.isArray((_c = ctx.prefill) == null ? void 0 : _c[element.key]) ? ctx.prefill[element.key] : null;
|
|
6056
6693
|
const childDefaults = extractChildDefaults(element.elements);
|
|
6057
6694
|
const countItems = () => itemsWrap.querySelectorAll(":scope > .containerItem").length;
|
|
6058
|
-
const
|
|
6059
|
-
|
|
6060
|
-
|
|
6061
|
-
|
|
6062
|
-
|
|
6063
|
-
|
|
6064
|
-
|
|
6065
|
-
|
|
6066
|
-
|
|
6067
|
-
|
|
6068
|
-
|
|
6069
|
-
|
|
6070
|
-
add.addEventListener("mouseenter", () => {
|
|
6071
|
-
add.style.backgroundColor = "var(--fb-background-hover-color)";
|
|
6072
|
-
});
|
|
6073
|
-
add.addEventListener("mouseleave", () => {
|
|
6074
|
-
add.style.backgroundColor = "transparent";
|
|
6075
|
-
});
|
|
6076
|
-
add.onclick = () => {
|
|
6077
|
-
if (countItems() < max) {
|
|
6078
|
-
const idx = countItems();
|
|
6079
|
-
const currentFormData = state.formRoot ? extractRootFormData(state.formRoot) : {};
|
|
6080
|
-
const subCtx = {
|
|
6081
|
-
state: ctx.state,
|
|
6082
|
-
path: pathJoin(ctx.path, `${element.key}[${idx}]`),
|
|
6083
|
-
prefill: childDefaults,
|
|
6084
|
-
// Defaults for enableIf evaluation
|
|
6085
|
-
formData: currentFormData,
|
|
6086
|
-
// Current root data from DOM for enableIf
|
|
6087
|
-
inheritedReadonly: childInheritedReadonly
|
|
6088
|
-
};
|
|
6089
|
-
const item = document.createElement("div");
|
|
6090
|
-
item.className = "containerItem border border-gray-300 rounded-lg p-2 bg-white";
|
|
6091
|
-
item.setAttribute("data-container-item", `${element.key}[${idx}]`);
|
|
6092
|
-
const childWrapper = document.createElement("div");
|
|
6093
|
-
childWrapper.className = getChildWrapperClass(isSlides, element.columns);
|
|
6094
|
-
element.elements.forEach((child) => {
|
|
6095
|
-
var _a2;
|
|
6096
|
-
if (child.type !== "markdown" && (child.hidden || child.type === "hidden")) {
|
|
6097
|
-
childWrapper.appendChild(
|
|
6098
|
-
createHiddenInput(
|
|
6099
|
-
pathJoin(subCtx.path, child.key),
|
|
6100
|
-
(_a2 = "default" in child ? child.default : null) != null ? _a2 : null
|
|
6101
|
-
)
|
|
6102
|
-
);
|
|
6103
|
-
} else {
|
|
6104
|
-
childWrapper.appendChild(renderElement(child, subCtx));
|
|
6105
|
-
}
|
|
6106
|
-
});
|
|
6107
|
-
item.appendChild(childWrapper);
|
|
6108
|
-
if (!containerIsReadonly) {
|
|
6109
|
-
const rem = document.createElement("button");
|
|
6110
|
-
rem.type = "button";
|
|
6111
|
-
rem.className = "absolute top-2 right-2 px-2 py-1 rounded";
|
|
6112
|
-
rem.style.cssText = `
|
|
6113
|
-
color: var(--fb-error-color);
|
|
6114
|
-
background-color: transparent;
|
|
6115
|
-
transition: background-color var(--fb-transition-duration);
|
|
6116
|
-
`;
|
|
6117
|
-
rem.textContent = "\u2715";
|
|
6118
|
-
rem.addEventListener("mouseenter", () => {
|
|
6119
|
-
rem.style.backgroundColor = "var(--fb-background-hover-color)";
|
|
6120
|
-
});
|
|
6121
|
-
rem.addEventListener("mouseleave", () => {
|
|
6122
|
-
rem.style.backgroundColor = "transparent";
|
|
6123
|
-
});
|
|
6124
|
-
rem.onclick = () => handleRemoveItem(item);
|
|
6125
|
-
item.style.position = "relative";
|
|
6126
|
-
item.appendChild(rem);
|
|
6127
|
-
}
|
|
6128
|
-
itemsWrap.appendChild(item);
|
|
6129
|
-
updateAddButton();
|
|
6130
|
-
}
|
|
6695
|
+
const handleAddItem = () => {
|
|
6696
|
+
if (countItems() >= max) return;
|
|
6697
|
+
const idx = countItems();
|
|
6698
|
+
const currentFormData = state.formRoot ? extractRootFormData(state.formRoot) : {};
|
|
6699
|
+
const subCtx = {
|
|
6700
|
+
state: ctx.state,
|
|
6701
|
+
path: pathJoin(ctx.path, `${element.key}[${idx}]`),
|
|
6702
|
+
prefill: childDefaults,
|
|
6703
|
+
// Defaults for enableIf evaluation
|
|
6704
|
+
formData: currentFormData,
|
|
6705
|
+
// Current root data from DOM for enableIf
|
|
6706
|
+
inheritedReadonly: childInheritedReadonly
|
|
6131
6707
|
};
|
|
6132
|
-
|
|
6708
|
+
const item = document.createElement("div");
|
|
6709
|
+
item.className = "containerItem border border-gray-300 rounded-lg p-2 bg-white";
|
|
6710
|
+
item.setAttribute("data-container-item", `${element.key}[${idx}]`);
|
|
6711
|
+
if (isSlides) {
|
|
6712
|
+
item.setAttribute("data-fb-slide-card", "");
|
|
6713
|
+
}
|
|
6714
|
+
const childWrapper = document.createElement("div");
|
|
6715
|
+
childWrapper.className = getChildWrapperClass(isSlides, element.columns);
|
|
6716
|
+
element.elements.forEach((child) => {
|
|
6717
|
+
var _a2;
|
|
6718
|
+
if (child.type !== "markdown" && (child.hidden || child.type === "hidden")) {
|
|
6719
|
+
childWrapper.appendChild(
|
|
6720
|
+
createHiddenInput(
|
|
6721
|
+
pathJoin(subCtx.path, child.key),
|
|
6722
|
+
(_a2 = "default" in child ? child.default : null) != null ? _a2 : null
|
|
6723
|
+
)
|
|
6724
|
+
);
|
|
6725
|
+
} else {
|
|
6726
|
+
childWrapper.appendChild(renderElement(child, subCtx));
|
|
6727
|
+
}
|
|
6728
|
+
});
|
|
6729
|
+
item.appendChild(childWrapper);
|
|
6730
|
+
if (!containerIsReadonly) {
|
|
6731
|
+
mountRemoveButton(item, () => handleRemoveItem(item), state);
|
|
6732
|
+
}
|
|
6733
|
+
if (slideAddTile && slideAddTile.parentElement === itemsWrap) {
|
|
6734
|
+
itemsWrap.insertBefore(item, slideAddTile);
|
|
6735
|
+
} else {
|
|
6736
|
+
itemsWrap.appendChild(item);
|
|
6737
|
+
}
|
|
6738
|
+
updateAddButton();
|
|
6133
6739
|
};
|
|
6134
|
-
|
|
6135
|
-
|
|
6136
|
-
|
|
6137
|
-
|
|
6740
|
+
let slideAddTile = null;
|
|
6741
|
+
let slideAddUpdate = null;
|
|
6742
|
+
let pillAddUpdate = null;
|
|
6743
|
+
const syncSlideTileSize = () => {
|
|
6744
|
+
if (!slideAddTile) return;
|
|
6745
|
+
const firstSlide = itemsWrap.querySelector(
|
|
6746
|
+
":scope > .containerItem"
|
|
6138
6747
|
);
|
|
6139
|
-
if (
|
|
6140
|
-
|
|
6141
|
-
existingAddBtn.style.opacity = currentCount >= max ? "0.5" : "1";
|
|
6142
|
-
existingAddBtn.style.pointerEvents = currentCount >= max ? "none" : "auto";
|
|
6748
|
+
if (firstSlide && firstSlide.offsetHeight > 0) {
|
|
6749
|
+
slideAddTile.style.minHeight = `${firstSlide.offsetHeight}px`;
|
|
6143
6750
|
}
|
|
6144
|
-
|
|
6751
|
+
};
|
|
6752
|
+
const updateAddButton = () => {
|
|
6753
|
+
const currentCount = countItems();
|
|
6754
|
+
if (slideAddUpdate) slideAddUpdate(currentCount, max);
|
|
6755
|
+
if (pillAddUpdate) pillAddUpdate(currentCount, max);
|
|
6756
|
+
if (slideAddTile) syncSlideTileSize();
|
|
6145
6757
|
};
|
|
6146
6758
|
const handleRemoveItem = (item) => {
|
|
6147
6759
|
item.remove();
|
|
@@ -6163,6 +6775,9 @@ function renderMultipleContainerElement(element, ctx, wrapper, _pathKey) {
|
|
|
6163
6775
|
const item = document.createElement("div");
|
|
6164
6776
|
item.className = "containerItem border border-gray-300 rounded-lg p-2 bg-white";
|
|
6165
6777
|
item.setAttribute("data-container-item", `${element.key}[${idx}]`);
|
|
6778
|
+
if (isSlides) {
|
|
6779
|
+
item.setAttribute("data-fb-slide-card", "");
|
|
6780
|
+
}
|
|
6166
6781
|
const childWrapper = document.createElement("div");
|
|
6167
6782
|
if (isSlides) {
|
|
6168
6783
|
childWrapper.className = "space-y-2";
|
|
@@ -6187,24 +6802,7 @@ function renderMultipleContainerElement(element, ctx, wrapper, _pathKey) {
|
|
|
6187
6802
|
});
|
|
6188
6803
|
item.appendChild(childWrapper);
|
|
6189
6804
|
if (!containerIsReadonly) {
|
|
6190
|
-
|
|
6191
|
-
rem.type = "button";
|
|
6192
|
-
rem.className = "absolute top-2 right-2 px-2 py-1 rounded";
|
|
6193
|
-
rem.style.cssText = `
|
|
6194
|
-
color: var(--fb-error-color);
|
|
6195
|
-
background-color: transparent;
|
|
6196
|
-
transition: background-color var(--fb-transition-duration);
|
|
6197
|
-
`;
|
|
6198
|
-
rem.textContent = "\u2715";
|
|
6199
|
-
rem.addEventListener("mouseenter", () => {
|
|
6200
|
-
rem.style.backgroundColor = "var(--fb-background-hover-color)";
|
|
6201
|
-
});
|
|
6202
|
-
rem.addEventListener("mouseleave", () => {
|
|
6203
|
-
rem.style.backgroundColor = "transparent";
|
|
6204
|
-
});
|
|
6205
|
-
rem.onclick = () => handleRemoveItem(item);
|
|
6206
|
-
item.style.position = "relative";
|
|
6207
|
-
item.appendChild(rem);
|
|
6805
|
+
mountRemoveButton(item, () => handleRemoveItem(item), ctx.state);
|
|
6208
6806
|
}
|
|
6209
6807
|
itemsWrap.appendChild(item);
|
|
6210
6808
|
});
|
|
@@ -6224,6 +6822,9 @@ function renderMultipleContainerElement(element, ctx, wrapper, _pathKey) {
|
|
|
6224
6822
|
const item = document.createElement("div");
|
|
6225
6823
|
item.className = "containerItem border border-gray-300 rounded-lg p-2 bg-white";
|
|
6226
6824
|
item.setAttribute("data-container-item", `${element.key}[${idx}]`);
|
|
6825
|
+
if (isSlides) {
|
|
6826
|
+
item.setAttribute("data-fb-slide-card", "");
|
|
6827
|
+
}
|
|
6227
6828
|
const childWrapper = document.createElement("div");
|
|
6228
6829
|
if (isSlides) {
|
|
6229
6830
|
childWrapper.className = "space-y-2";
|
|
@@ -6249,41 +6850,47 @@ function renderMultipleContainerElement(element, ctx, wrapper, _pathKey) {
|
|
|
6249
6850
|
}
|
|
6250
6851
|
});
|
|
6251
6852
|
item.appendChild(childWrapper);
|
|
6252
|
-
|
|
6253
|
-
|
|
6254
|
-
|
|
6255
|
-
|
|
6256
|
-
|
|
6257
|
-
|
|
6258
|
-
|
|
6259
|
-
|
|
6260
|
-
|
|
6261
|
-
rem.addEventListener("mouseenter", () => {
|
|
6262
|
-
rem.style.backgroundColor = "var(--fb-background-hover-color)";
|
|
6263
|
-
});
|
|
6264
|
-
rem.addEventListener("mouseleave", () => {
|
|
6265
|
-
rem.style.backgroundColor = "transparent";
|
|
6266
|
-
});
|
|
6267
|
-
rem.onclick = () => {
|
|
6268
|
-
if (countItems() > min) {
|
|
6269
|
-
handleRemoveItem(item);
|
|
6270
|
-
}
|
|
6271
|
-
};
|
|
6272
|
-
item.style.position = "relative";
|
|
6273
|
-
item.appendChild(rem);
|
|
6853
|
+
mountRemoveButton(
|
|
6854
|
+
item,
|
|
6855
|
+
() => {
|
|
6856
|
+
if (countItems() > min) {
|
|
6857
|
+
handleRemoveItem(item);
|
|
6858
|
+
}
|
|
6859
|
+
},
|
|
6860
|
+
ctx.state
|
|
6861
|
+
);
|
|
6274
6862
|
itemsWrap.appendChild(item);
|
|
6275
6863
|
}
|
|
6276
6864
|
}
|
|
6277
6865
|
containerWrap.appendChild(itemsWrap);
|
|
6278
6866
|
if (!containerIsReadonly) {
|
|
6279
|
-
|
|
6280
|
-
|
|
6281
|
-
|
|
6282
|
-
|
|
6283
|
-
|
|
6867
|
+
if (isSlides) {
|
|
6868
|
+
itemsWrap.style.alignItems = "stretch";
|
|
6869
|
+
const handle = createSlideAddTile(handleAddItem, {
|
|
6870
|
+
label: element.addLabel
|
|
6871
|
+
});
|
|
6872
|
+
slideAddTile = handle.tile;
|
|
6873
|
+
slideAddUpdate = handle.update;
|
|
6874
|
+
mountCounterInLabel(wrapper, handle.counter);
|
|
6875
|
+
itemsWrap.appendChild(handle.tile);
|
|
6876
|
+
} else {
|
|
6877
|
+
const handle = createAddItemRow("container", handleAddItem, {
|
|
6878
|
+
label: element.addLabel
|
|
6879
|
+
});
|
|
6880
|
+
pillAddUpdate = handle.update;
|
|
6881
|
+
mountCounterInLabel(wrapper, handle.counter);
|
|
6882
|
+
containerWrap.appendChild(handle.row);
|
|
6883
|
+
}
|
|
6284
6884
|
}
|
|
6285
6885
|
updateAddButton();
|
|
6286
6886
|
wrapper.appendChild(containerWrap);
|
|
6887
|
+
if (slideAddTile) {
|
|
6888
|
+
if (typeof requestAnimationFrame === "function") {
|
|
6889
|
+
requestAnimationFrame(syncSlideTileSize);
|
|
6890
|
+
} else {
|
|
6891
|
+
syncSlideTileSize();
|
|
6892
|
+
}
|
|
6893
|
+
}
|
|
6287
6894
|
}
|
|
6288
6895
|
var validateElementFunc = null;
|
|
6289
6896
|
function setValidateElement(fn) {
|
|
@@ -8944,7 +9551,7 @@ function renderEditMode(element, ctx, wrapper, pathKey, initialValue) {
|
|
|
8944
9551
|
if (element.minLength != null || element.maxLength != null) {
|
|
8945
9552
|
const counterRow = document.createElement("div");
|
|
8946
9553
|
counterRow.style.cssText = "position: relative; padding: 2px 10px 4px; text-align: right;";
|
|
8947
|
-
const counter = createCharCounter(element, textarea
|
|
9554
|
+
const counter = createCharCounter(element, textarea);
|
|
8948
9555
|
counter.style.cssText = `
|
|
8949
9556
|
position: static;
|
|
8950
9557
|
display: inline-block;
|
|
@@ -9251,10 +9858,7 @@ var TAGS = {
|
|
|
9251
9858
|
"-": ["<hr />"]
|
|
9252
9859
|
};
|
|
9253
9860
|
function outdent(str) {
|
|
9254
|
-
return str.replace(
|
|
9255
|
-
RegExp("^" + (str.match(/^(\t| )+/) || "")[0], "gm"),
|
|
9256
|
-
""
|
|
9257
|
-
);
|
|
9861
|
+
return str.replace(RegExp("^" + (str.match(/^(\t| )+/) || "")[0], "gm"), "");
|
|
9258
9862
|
}
|
|
9259
9863
|
function encodeAttr(str) {
|
|
9260
9864
|
return (str + "").replace(/"/g, """).replace(/</g, "<").replace(/>/g, ">");
|
|
@@ -9417,12 +10021,7 @@ function ensureMarkdownStyles() {
|
|
|
9417
10021
|
`;
|
|
9418
10022
|
document.head.appendChild(style);
|
|
9419
10023
|
}
|
|
9420
|
-
var ANCHOR_DANGEROUS_SCHEMES = [
|
|
9421
|
-
"javascript:",
|
|
9422
|
-
"data:",
|
|
9423
|
-
"vbscript:",
|
|
9424
|
-
"blob:"
|
|
9425
|
-
];
|
|
10024
|
+
var ANCHOR_DANGEROUS_SCHEMES = ["javascript:", "data:", "vbscript:", "blob:"];
|
|
9426
10025
|
var IMG_DANGEROUS_SCHEMES = ["javascript:", "vbscript:", "blob:"];
|
|
9427
10026
|
function isImgSrcDangerous(normalized) {
|
|
9428
10027
|
if (IMG_DANGEROUS_SCHEMES.some((scheme) => normalized.startsWith(scheme))) {
|
|
@@ -9484,6 +10083,118 @@ function validateMarkdown(_element, _key, _context) {
|
|
|
9484
10083
|
function updateMarkdown(_element, _fieldPath, _value, _context) {
|
|
9485
10084
|
}
|
|
9486
10085
|
|
|
10086
|
+
// src/components/registry.ts
|
|
10087
|
+
function validateHiddenElement(element, key, context) {
|
|
10088
|
+
var _a;
|
|
10089
|
+
const { scopeRoot } = context;
|
|
10090
|
+
const input = scopeRoot.querySelector(
|
|
10091
|
+
`input[type="hidden"][data-hidden-field="true"][name="${key}"]`
|
|
10092
|
+
);
|
|
10093
|
+
const raw = (_a = input == null ? void 0 : input.value) != null ? _a : "";
|
|
10094
|
+
if (raw === "") {
|
|
10095
|
+
const defaultVal = "default" in element ? element.default : null;
|
|
10096
|
+
return { value: defaultVal !== void 0 ? defaultVal : null, errors: [] };
|
|
10097
|
+
}
|
|
10098
|
+
return { value: deserializeHiddenValue(raw), errors: [] };
|
|
10099
|
+
}
|
|
10100
|
+
function updateHiddenField(_element, fieldPath, value, context) {
|
|
10101
|
+
const { scopeRoot } = context;
|
|
10102
|
+
const input = scopeRoot.querySelector(
|
|
10103
|
+
`input[type="hidden"][data-hidden-field="true"][name="${fieldPath}"]`
|
|
10104
|
+
);
|
|
10105
|
+
if (!input) return;
|
|
10106
|
+
input.value = serializeHiddenValue(value);
|
|
10107
|
+
}
|
|
10108
|
+
var componentRegistry = {
|
|
10109
|
+
text: {
|
|
10110
|
+
validate: validateTextElement,
|
|
10111
|
+
update: updateTextField
|
|
10112
|
+
},
|
|
10113
|
+
textarea: {
|
|
10114
|
+
validate: validateTextareaElement,
|
|
10115
|
+
update: updateTextareaField
|
|
10116
|
+
},
|
|
10117
|
+
number: {
|
|
10118
|
+
validate: validateNumberElement,
|
|
10119
|
+
update: updateNumberField
|
|
10120
|
+
},
|
|
10121
|
+
select: {
|
|
10122
|
+
validate: validateSelectElement,
|
|
10123
|
+
update: updateSelectField
|
|
10124
|
+
},
|
|
10125
|
+
switcher: {
|
|
10126
|
+
validate: validateSwitcherElement,
|
|
10127
|
+
update: updateSwitcherField
|
|
10128
|
+
},
|
|
10129
|
+
boolean: {
|
|
10130
|
+
validate: validateBooleanElement,
|
|
10131
|
+
update: updateBooleanField,
|
|
10132
|
+
ownsLabel: true
|
|
10133
|
+
},
|
|
10134
|
+
file: {
|
|
10135
|
+
validate: validateFileElement,
|
|
10136
|
+
update: updateFileField
|
|
10137
|
+
},
|
|
10138
|
+
files: {
|
|
10139
|
+
// Legacy type - delegates to file
|
|
10140
|
+
validate: validateFileElement,
|
|
10141
|
+
update: updateFileField
|
|
10142
|
+
},
|
|
10143
|
+
colour: {
|
|
10144
|
+
validate: validateColourElement,
|
|
10145
|
+
update: updateColourField
|
|
10146
|
+
},
|
|
10147
|
+
slider: {
|
|
10148
|
+
validate: validateSliderElement,
|
|
10149
|
+
update: updateSliderField
|
|
10150
|
+
},
|
|
10151
|
+
container: {
|
|
10152
|
+
validate: validateContainerElement,
|
|
10153
|
+
update: updateContainerField
|
|
10154
|
+
},
|
|
10155
|
+
group: {
|
|
10156
|
+
// Deprecated type - delegates to container
|
|
10157
|
+
validate: validateGroupElement,
|
|
10158
|
+
update: updateGroupField
|
|
10159
|
+
},
|
|
10160
|
+
table: {
|
|
10161
|
+
validate: validateTableElement,
|
|
10162
|
+
update: updateTableField
|
|
10163
|
+
},
|
|
10164
|
+
richinput: {
|
|
10165
|
+
validate: validateRichInputElement,
|
|
10166
|
+
update: updateRichInputField
|
|
10167
|
+
},
|
|
10168
|
+
hidden: {
|
|
10169
|
+
// Legacy type: `type: "hidden"` — reads/writes DOM <input type="hidden"> element
|
|
10170
|
+
validate: validateHiddenElement,
|
|
10171
|
+
update: updateHiddenField
|
|
10172
|
+
},
|
|
10173
|
+
markdown: {
|
|
10174
|
+
// Display-only element — no value, no errors, skip from form data
|
|
10175
|
+
validate: validateMarkdown,
|
|
10176
|
+
update: updateMarkdown
|
|
10177
|
+
}
|
|
10178
|
+
};
|
|
10179
|
+
function getComponentOperations(elementType) {
|
|
10180
|
+
return componentRegistry[elementType] || null;
|
|
10181
|
+
}
|
|
10182
|
+
function validateElementWithComponent(element, key, context) {
|
|
10183
|
+
const ops = getComponentOperations(element.type);
|
|
10184
|
+
if (ops && ops.validate) {
|
|
10185
|
+
return ops.validate(element, key, context);
|
|
10186
|
+
}
|
|
10187
|
+
return null;
|
|
10188
|
+
}
|
|
10189
|
+
function updateElementWithComponent(element, fieldPath, value, context) {
|
|
10190
|
+
const ops = getComponentOperations(element.type);
|
|
10191
|
+
if (ops && ops.update) {
|
|
10192
|
+
ops.update(element, fieldPath, value, context);
|
|
10193
|
+
return true;
|
|
10194
|
+
}
|
|
10195
|
+
return false;
|
|
10196
|
+
}
|
|
10197
|
+
|
|
9487
10198
|
// src/components/index.ts
|
|
9488
10199
|
function showTooltip(tooltipId, button) {
|
|
9489
10200
|
const tooltip = document.getElementById(tooltipId);
|
|
@@ -9749,6 +10460,7 @@ function createInfoButton(element) {
|
|
|
9749
10460
|
function createLabelContainer(element) {
|
|
9750
10461
|
const label = document.createElement("div");
|
|
9751
10462
|
label.className = "flex items-center mb-1";
|
|
10463
|
+
label.dataset.fbLabelRow = "";
|
|
9752
10464
|
const title = createFieldLabel(element);
|
|
9753
10465
|
label.appendChild(title);
|
|
9754
10466
|
if (element.description || element.hint) {
|
|
@@ -9795,6 +10507,9 @@ function dispatchToRenderer(element, ctx, wrapper, pathKey) {
|
|
|
9795
10507
|
renderSwitcherElement(element, ctx, wrapper, pathKey);
|
|
9796
10508
|
}
|
|
9797
10509
|
break;
|
|
10510
|
+
case "boolean":
|
|
10511
|
+
renderBooleanElement(element, ctx, wrapper, pathKey);
|
|
10512
|
+
break;
|
|
9798
10513
|
case "file":
|
|
9799
10514
|
if (isMultiple) {
|
|
9800
10515
|
renderMultipleFileElement(element, ctx, wrapper, pathKey);
|
|
@@ -9873,8 +10588,11 @@ function renderElement2(element, ctx) {
|
|
|
9873
10588
|
const wrapper = document.createElement("div");
|
|
9874
10589
|
wrapper.className = "mb-2 fb-field-wrapper";
|
|
9875
10590
|
wrapper.setAttribute("data-field-key", element.key);
|
|
9876
|
-
const
|
|
9877
|
-
|
|
10591
|
+
const ops = getComponentOperations(element.type);
|
|
10592
|
+
if (!(ops == null ? void 0 : ops.ownsLabel)) {
|
|
10593
|
+
const label = createLabelContainer(element);
|
|
10594
|
+
wrapper.appendChild(label);
|
|
10595
|
+
}
|
|
9878
10596
|
const pathKey = pathJoin(ctx.path, element.key);
|
|
9879
10597
|
dispatchToRenderer(element, ctx, wrapper, pathKey);
|
|
9880
10598
|
if (initiallyDisabled) {
|
|
@@ -9966,6 +10684,7 @@ var defaultConfig = {
|
|
|
9966
10684
|
invalidFileExtension: 'File "{name}" has unsupported format. Allowed: {formats}',
|
|
9967
10685
|
invalidFileMime: 'File "{name}": file type {type} not allowed (allowed: {mimes})',
|
|
9968
10686
|
fileTooLarge: 'File "{name}" exceeds maximum size of {maxSize}MB',
|
|
10687
|
+
uploadFailed: 'Failed to upload "{name}": {error}',
|
|
9969
10688
|
filesLimitExceeded: "{skipped} file(s) skipped: maximum {max} files allowed",
|
|
9970
10689
|
unsupportedFieldType: "Unsupported field type: {type}",
|
|
9971
10690
|
invalidOption: "Invalid option",
|
|
@@ -10041,6 +10760,7 @@ var defaultConfig = {
|
|
|
10041
10760
|
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}',
|
|
10042
10761
|
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})',
|
|
10043
10762
|
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',
|
|
10763
|
+
uploadFailed: '\u041D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u0437\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044C "{name}": {error}',
|
|
10044
10764
|
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",
|
|
10045
10765
|
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}",
|
|
10046
10766
|
invalidOption: "\u041D\u0435\u0434\u043E\u043F\u0443\u0441\u0442\u0438\u043C\u043E\u0435 \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0435",
|
|
@@ -10105,28 +10825,52 @@ var defaultTheme = {
|
|
|
10105
10825
|
// blue-500
|
|
10106
10826
|
primaryHoverColor: "#2563eb",
|
|
10107
10827
|
// blue-600
|
|
10828
|
+
primarySoftColor: "#dbeafe",
|
|
10829
|
+
// blue-100
|
|
10830
|
+
primarySoftHoverColor: "#bfdbfe",
|
|
10831
|
+
// blue-200
|
|
10108
10832
|
errorColor: "#ef4444",
|
|
10109
10833
|
// red-500
|
|
10110
10834
|
errorHoverColor: "#dc2626",
|
|
10111
10835
|
// red-600
|
|
10112
10836
|
successColor: "#10b981",
|
|
10113
10837
|
// green-500
|
|
10838
|
+
accentColor: "#f59e0b",
|
|
10839
|
+
// amber-500
|
|
10840
|
+
accentSoftColor: "#fef3c7",
|
|
10841
|
+
// amber-100
|
|
10842
|
+
accentBorderColor: "#fde68a",
|
|
10843
|
+
// amber-200
|
|
10844
|
+
accentTextColor: "#92400e",
|
|
10845
|
+
// amber-800
|
|
10114
10846
|
borderColor: "#d1d5db",
|
|
10115
10847
|
// gray-300
|
|
10116
10848
|
borderHoverColor: "#9ca3af",
|
|
10117
10849
|
// gray-400
|
|
10118
10850
|
borderFocusColor: "#3b82f6",
|
|
10119
10851
|
// blue-500
|
|
10852
|
+
borderStrongColor: "#9ca3af",
|
|
10853
|
+
// gray-400
|
|
10120
10854
|
backgroundColor: "#ffffff",
|
|
10121
10855
|
// white
|
|
10122
10856
|
backgroundHoverColor: "#f9fafb",
|
|
10123
10857
|
// gray-50
|
|
10124
10858
|
backgroundReadonlyColor: "#f3f4f6",
|
|
10125
10859
|
// gray-100
|
|
10860
|
+
pageBackgroundColor: "#f9fafb",
|
|
10861
|
+
// gray-50
|
|
10862
|
+
surfaceSoftColor: "#eff6ff",
|
|
10863
|
+
// blue-50
|
|
10864
|
+
surfaceTintColor: "#f8fafc",
|
|
10865
|
+
// slate-50
|
|
10126
10866
|
textColor: "#1f2937",
|
|
10127
10867
|
// gray-800
|
|
10128
10868
|
textSecondaryColor: "#6b7280",
|
|
10129
10869
|
// gray-500
|
|
10870
|
+
textMutedColor: "#9ca3af",
|
|
10871
|
+
// gray-400
|
|
10872
|
+
textFaintColor: "#cbd5e1",
|
|
10873
|
+
// slate-300
|
|
10130
10874
|
textPlaceholderColor: "#9ca3af",
|
|
10131
10875
|
// gray-400
|
|
10132
10876
|
textDisabledColor: "#d1d5db",
|
|
@@ -10169,6 +10913,12 @@ var defaultTheme = {
|
|
|
10169
10913
|
// 4px (compact density v2)
|
|
10170
10914
|
borderRadius: "0.5rem",
|
|
10171
10915
|
// rounded-lg (8px)
|
|
10916
|
+
borderRadiusSmall: "0.375rem",
|
|
10917
|
+
// 6px
|
|
10918
|
+
borderRadiusLarge: "0.75rem",
|
|
10919
|
+
// 12px
|
|
10920
|
+
borderRadiusXLarge: "1rem",
|
|
10921
|
+
// 16px
|
|
10172
10922
|
borderWidth: "1px",
|
|
10173
10923
|
// Typography
|
|
10174
10924
|
fontSize: "0.875rem",
|
|
@@ -10180,13 +10930,31 @@ var defaultTheme = {
|
|
|
10180
10930
|
fontFamily: 'system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif',
|
|
10181
10931
|
fontWeightNormal: "400",
|
|
10182
10932
|
fontWeightMedium: "500",
|
|
10933
|
+
lineHeight: "1.5",
|
|
10934
|
+
// Shadows
|
|
10935
|
+
shadowCard: "0 1px 2px rgba(15,23,42,.04), 0 1px 0 rgba(15,23,42,.02)",
|
|
10936
|
+
shadowPopover: "0 12px 32px -12px rgba(15,23,42,.18), 0 4px 12px -6px rgba(15,23,42,.08)",
|
|
10183
10937
|
// Focus ring
|
|
10184
10938
|
focusRingWidth: "2px",
|
|
10185
10939
|
focusRingColor: "#3b82f6",
|
|
10186
10940
|
// blue-500
|
|
10187
10941
|
focusRingOpacity: "0.5",
|
|
10188
10942
|
// Transitions
|
|
10189
|
-
transitionDuration: "200ms"
|
|
10943
|
+
transitionDuration: "200ms",
|
|
10944
|
+
// Slide-card defaults — flat-white to match every other item card. The
|
|
10945
|
+
// Picaz theme overrides this with a gradient + shadow to lift the slides.
|
|
10946
|
+
slideCardBg: "#ffffff",
|
|
10947
|
+
slideCardShadow: "0 1px 2px rgba(15,23,42,.04), 0 1px 0 rgba(15,23,42,.02)",
|
|
10948
|
+
slideCardRadius: "0.5rem",
|
|
10949
|
+
// matches borderRadius
|
|
10950
|
+
slideCardMinHeight: "0",
|
|
10951
|
+
slideCardPadding: "12px",
|
|
10952
|
+
// Section-label defaults — same look as the regular field label. Themes
|
|
10953
|
+
// that want the "ПРЕИМУЩЕСТВА" caps style override these three vars.
|
|
10954
|
+
labelSectionFontSize: "0.875rem",
|
|
10955
|
+
// matches fontSize
|
|
10956
|
+
labelSectionLetterSpacing: "normal",
|
|
10957
|
+
labelSectionTextTransform: "none"
|
|
10190
10958
|
};
|
|
10191
10959
|
function generateCSSVariables(theme) {
|
|
10192
10960
|
const mergedTheme = { ...defaultTheme, ...theme };
|
|
@@ -10198,6 +10966,7 @@ function generateCSSVariables(theme) {
|
|
|
10198
10966
|
return cssVars.join("\n");
|
|
10199
10967
|
}
|
|
10200
10968
|
function injectThemeVariables(container, theme) {
|
|
10969
|
+
ensureThemingHooks(container.ownerDocument || document);
|
|
10201
10970
|
const cssVariables = generateCSSVariables(theme);
|
|
10202
10971
|
let styleTag = container.querySelector(
|
|
10203
10972
|
"style[data-fb-theme]"
|
|
@@ -10260,138 +11029,50 @@ var exampleThemes = {
|
|
|
10260
11029
|
fontSize: "16px",
|
|
10261
11030
|
fontSizeSmall: "14px",
|
|
10262
11031
|
fontFamily: '"Roboto", "Helvetica", "Arial", sans-serif'
|
|
10263
|
-
}
|
|
10264
|
-
};
|
|
10265
|
-
|
|
10266
|
-
// src/utils/styles.ts
|
|
10267
|
-
function applyActionButtonStyles(button, isFormLevel = false) {
|
|
10268
|
-
button.style.cssText = `
|
|
10269
|
-
background-color: var(--fb-action-bg-color);
|
|
10270
|
-
color: var(--fb-action-text-color);
|
|
10271
|
-
border: var(--fb-border-width) solid var(--fb-action-border-color);
|
|
10272
|
-
padding: ${isFormLevel ? "0.5rem 1rem" : "0.5rem 0.75rem"};
|
|
10273
|
-
font-size: var(--fb-font-size);
|
|
10274
|
-
font-weight: var(--fb-font-weight-medium);
|
|
10275
|
-
border-radius: var(--fb-border-radius);
|
|
10276
|
-
transition: all var(--fb-transition-duration);
|
|
10277
|
-
box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
|
|
10278
|
-
`;
|
|
10279
|
-
button.addEventListener("mouseenter", () => {
|
|
10280
|
-
button.style.backgroundColor = "var(--fb-action-hover-bg-color)";
|
|
10281
|
-
button.style.borderColor = "var(--fb-action-hover-border-color)";
|
|
10282
|
-
});
|
|
10283
|
-
button.addEventListener("mouseleave", () => {
|
|
10284
|
-
button.style.backgroundColor = "var(--fb-action-bg-color)";
|
|
10285
|
-
button.style.borderColor = "var(--fb-action-border-color)";
|
|
10286
|
-
});
|
|
10287
|
-
}
|
|
10288
|
-
|
|
10289
|
-
// src/components/registry.ts
|
|
10290
|
-
function validateHiddenElement(element, key, context) {
|
|
10291
|
-
var _a;
|
|
10292
|
-
const { scopeRoot } = context;
|
|
10293
|
-
const input = scopeRoot.querySelector(
|
|
10294
|
-
`input[type="hidden"][data-hidden-field="true"][name="${key}"]`
|
|
10295
|
-
);
|
|
10296
|
-
const raw = (_a = input == null ? void 0 : input.value) != null ? _a : "";
|
|
10297
|
-
if (raw === "") {
|
|
10298
|
-
const defaultVal = "default" in element ? element.default : null;
|
|
10299
|
-
return { value: defaultVal !== void 0 ? defaultVal : null, errors: [] };
|
|
10300
|
-
}
|
|
10301
|
-
return { value: deserializeHiddenValue(raw), errors: [] };
|
|
10302
|
-
}
|
|
10303
|
-
function updateHiddenField(_element, fieldPath, value, context) {
|
|
10304
|
-
const { scopeRoot } = context;
|
|
10305
|
-
const input = scopeRoot.querySelector(
|
|
10306
|
-
`input[type="hidden"][data-hidden-field="true"][name="${fieldPath}"]`
|
|
10307
|
-
);
|
|
10308
|
-
if (!input) return;
|
|
10309
|
-
input.value = serializeHiddenValue(value);
|
|
10310
|
-
}
|
|
10311
|
-
var componentRegistry = {
|
|
10312
|
-
text: {
|
|
10313
|
-
validate: validateTextElement,
|
|
10314
|
-
update: updateTextField
|
|
10315
|
-
},
|
|
10316
|
-
textarea: {
|
|
10317
|
-
validate: validateTextareaElement,
|
|
10318
|
-
update: updateTextareaField
|
|
10319
|
-
},
|
|
10320
|
-
number: {
|
|
10321
|
-
validate: validateNumberElement,
|
|
10322
|
-
update: updateNumberField
|
|
10323
|
-
},
|
|
10324
|
-
select: {
|
|
10325
|
-
validate: validateSelectElement,
|
|
10326
|
-
update: updateSelectField
|
|
10327
|
-
},
|
|
10328
|
-
switcher: {
|
|
10329
|
-
validate: validateSwitcherElement,
|
|
10330
|
-
update: updateSwitcherField
|
|
10331
|
-
},
|
|
10332
|
-
file: {
|
|
10333
|
-
validate: validateFileElement,
|
|
10334
|
-
update: updateFileField
|
|
10335
|
-
},
|
|
10336
|
-
files: {
|
|
10337
|
-
// Legacy type - delegates to file
|
|
10338
|
-
validate: validateFileElement,
|
|
10339
|
-
update: updateFileField
|
|
10340
|
-
},
|
|
10341
|
-
colour: {
|
|
10342
|
-
validate: validateColourElement,
|
|
10343
|
-
update: updateColourField
|
|
10344
|
-
},
|
|
10345
|
-
slider: {
|
|
10346
|
-
validate: validateSliderElement,
|
|
10347
|
-
update: updateSliderField
|
|
10348
|
-
},
|
|
10349
|
-
container: {
|
|
10350
|
-
validate: validateContainerElement,
|
|
10351
|
-
update: updateContainerField
|
|
10352
|
-
},
|
|
10353
|
-
group: {
|
|
10354
|
-
// Deprecated type - delegates to container
|
|
10355
|
-
validate: validateGroupElement,
|
|
10356
|
-
update: updateGroupField
|
|
10357
|
-
},
|
|
10358
|
-
table: {
|
|
10359
|
-
validate: validateTableElement,
|
|
10360
|
-
update: updateTableField
|
|
10361
|
-
},
|
|
10362
|
-
richinput: {
|
|
10363
|
-
validate: validateRichInputElement,
|
|
10364
|
-
update: updateRichInputField
|
|
10365
|
-
},
|
|
10366
|
-
hidden: {
|
|
10367
|
-
// Legacy type: `type: "hidden"` — reads/writes DOM <input type="hidden"> element
|
|
10368
|
-
validate: validateHiddenElement,
|
|
10369
|
-
update: updateHiddenField
|
|
10370
11032
|
},
|
|
10371
|
-
|
|
10372
|
-
|
|
10373
|
-
|
|
10374
|
-
|
|
11033
|
+
// Picaz wizard design tokens — derived from the Picaz Wizard mockups.
|
|
11034
|
+
// Pairs with the host-side .card / .section-num / .lede chrome that wraps the form.
|
|
11035
|
+
picaz: {
|
|
11036
|
+
...defaultTheme,
|
|
11037
|
+
primaryColor: "#2f5bea",
|
|
11038
|
+
primaryHoverColor: "#2349c8",
|
|
11039
|
+
primarySoftColor: "#eaf0ff",
|
|
11040
|
+
primarySoftHoverColor: "#d6e0ff",
|
|
11041
|
+
errorColor: "#ef4444",
|
|
11042
|
+
successColor: "#16a34a",
|
|
11043
|
+
accentColor: "#ffb020",
|
|
11044
|
+
accentSoftColor: "#fff7e6",
|
|
11045
|
+
accentBorderColor: "#fde7b5",
|
|
11046
|
+
accentTextColor: "#92400e",
|
|
11047
|
+
borderColor: "#e3e8f0",
|
|
11048
|
+
borderHoverColor: "#cdd6e3",
|
|
11049
|
+
borderFocusColor: "#2f5bea",
|
|
11050
|
+
borderStrongColor: "#cdd6e3",
|
|
11051
|
+
backgroundColor: "#ffffff",
|
|
11052
|
+
backgroundHoverColor: "#f3f7ff",
|
|
11053
|
+
pageBackgroundColor: "#f6f8fb",
|
|
11054
|
+
surfaceSoftColor: "#eef4ff",
|
|
11055
|
+
surfaceTintColor: "#f3f7ff",
|
|
11056
|
+
textColor: "#0f172a",
|
|
11057
|
+
textSecondaryColor: "#334155",
|
|
11058
|
+
textMutedColor: "#64748b",
|
|
11059
|
+
textFaintColor: "#94a3b8",
|
|
11060
|
+
textPlaceholderColor: "#94a3b8",
|
|
11061
|
+
buttonBgColor: "#2f5bea",
|
|
11062
|
+
buttonHoverBgColor: "#2349c8",
|
|
11063
|
+
fileUploadBgColor: "#fafcff",
|
|
11064
|
+
fileUploadBorderColor: "#cdd6e3",
|
|
11065
|
+
fileUploadHoverBorderColor: "#2f5bea",
|
|
11066
|
+
borderRadius: "12px",
|
|
11067
|
+
borderRadiusSmall: "8px",
|
|
11068
|
+
borderRadiusLarge: "16px",
|
|
11069
|
+
borderRadiusXLarge: "22px",
|
|
11070
|
+
fontFamily: '"Inter", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif',
|
|
11071
|
+
shadowCard: "0 1px 2px rgba(15,23,42,.04), 0 1px 0 rgba(15,23,42,.02)",
|
|
11072
|
+
shadowPopover: "0 12px 32px -12px rgba(15,23,42,.18), 0 4px 12px -6px rgba(15,23,42,.08)",
|
|
11073
|
+
focusRingColor: "#2f5bea"
|
|
10375
11074
|
}
|
|
10376
11075
|
};
|
|
10377
|
-
function getComponentOperations(elementType) {
|
|
10378
|
-
return componentRegistry[elementType] || null;
|
|
10379
|
-
}
|
|
10380
|
-
function validateElementWithComponent(element, key, context) {
|
|
10381
|
-
const ops = getComponentOperations(element.type);
|
|
10382
|
-
if (ops && ops.validate) {
|
|
10383
|
-
return ops.validate(element, key, context);
|
|
10384
|
-
}
|
|
10385
|
-
return null;
|
|
10386
|
-
}
|
|
10387
|
-
function updateElementWithComponent(element, fieldPath, value, context) {
|
|
10388
|
-
const ops = getComponentOperations(element.type);
|
|
10389
|
-
if (ops && ops.update) {
|
|
10390
|
-
ops.update(element, fieldPath, value, context);
|
|
10391
|
-
return true;
|
|
10392
|
-
}
|
|
10393
|
-
return false;
|
|
10394
|
-
}
|
|
10395
11076
|
|
|
10396
11077
|
// src/instance/FormBuilderInstance.ts
|
|
10397
11078
|
var FormBuilderInstance = class {
|
|
@@ -10516,26 +11197,37 @@ var FormBuilderInstance = class {
|
|
|
10516
11197
|
}
|
|
10517
11198
|
}
|
|
10518
11199
|
/**
|
|
10519
|
-
* Find the DOM element corresponding to a field path (instance-scoped)
|
|
11200
|
+
* Find the DOM element corresponding to a field path (instance-scoped).
|
|
11201
|
+
*
|
|
11202
|
+
* Strategy:
|
|
11203
|
+
* 1. Try a `[name="…"]` lookup first — works for any field that renders
|
|
11204
|
+
* an input/hidden with the path as its name, in either mode. Some
|
|
11205
|
+
* readonly renderers still emit a hidden input (boolean, switcher),
|
|
11206
|
+
* so this path must run regardless of `state.config.readonly`. A
|
|
11207
|
+
* prior version gated this on edit mode only, which made
|
|
11208
|
+
* `updateField` / `setFormData` silently miss readonly boolean
|
|
11209
|
+
* fields whose component also opts out of the standard label row
|
|
11210
|
+
* (`ownsLabel: true`).
|
|
11211
|
+
* 2. If no input matched, fall back to locating the field wrapper by
|
|
11212
|
+
* its visible label text — needed for readonly previews that don't
|
|
11213
|
+
* emit any `name=` attribute (e.g. file/markdown previews).
|
|
10520
11214
|
*/
|
|
10521
11215
|
findFormElementByFieldPath(fieldPath) {
|
|
10522
11216
|
if (!this.state.formRoot) return null;
|
|
10523
|
-
|
|
10524
|
-
|
|
10525
|
-
|
|
11217
|
+
let element = this.state.formRoot.querySelector(
|
|
11218
|
+
`[name="${fieldPath}"]`
|
|
11219
|
+
);
|
|
11220
|
+
if (element) return element;
|
|
11221
|
+
const variations = [
|
|
11222
|
+
fieldPath,
|
|
11223
|
+
fieldPath.replace(/\[(\d+)\]/g, "[$1]"),
|
|
11224
|
+
fieldPath.replace(/\./g, "[") + "]".repeat((fieldPath.match(/\./g) || []).length)
|
|
11225
|
+
];
|
|
11226
|
+
for (const variation of variations) {
|
|
11227
|
+
element = this.state.formRoot.querySelector(
|
|
11228
|
+
`[name="${variation}"]`
|
|
10526
11229
|
);
|
|
10527
11230
|
if (element) return element;
|
|
10528
|
-
const variations = [
|
|
10529
|
-
fieldPath,
|
|
10530
|
-
fieldPath.replace(/\[(\d+)\]/g, "[$1]"),
|
|
10531
|
-
fieldPath.replace(/\./g, "[") + "]".repeat((fieldPath.match(/\./g) || []).length)
|
|
10532
|
-
];
|
|
10533
|
-
for (const variation of variations) {
|
|
10534
|
-
element = this.state.formRoot.querySelector(
|
|
10535
|
-
`[name="${variation}"]`
|
|
10536
|
-
);
|
|
10537
|
-
if (element) return element;
|
|
10538
|
-
}
|
|
10539
11231
|
}
|
|
10540
11232
|
const schemaElement = this.findSchemaElement(fieldPath);
|
|
10541
11233
|
if (!schemaElement) return null;
|