@dmitryvim/form-builder 0.2.30 → 0.2.34
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser/formbuilder.min.js +577 -448
- package/dist/browser/formbuilder.v0.2.34.min.js +1162 -0
- package/dist/cjs/index.cjs +1644 -1174
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/esm/index.js +1617 -1158
- package/dist/esm/index.js.map +1 -1
- package/dist/form-builder.js +577 -448
- package/dist/types/components/colour.d.ts +1 -0
- package/dist/types/components/file/dom.d.ts +4 -5
- package/dist/types/components/file/preview.d.ts +5 -0
- package/dist/types/components/file/render-edit.d.ts +8 -1
- package/dist/types/components/file/render-readonly.d.ts +4 -7
- package/dist/types/components/file/upload.d.ts +10 -0
- package/dist/types/components/file.d.ts +2 -2
- package/dist/types/types/config.d.ts +5 -0
- package/dist/types/types/schema.d.ts +16 -0
- package/dist/types/utils/styles.d.ts +42 -0
- package/package.json +1 -1
- package/dist/browser/formbuilder.v0.2.30.min.js +0 -1033
package/dist/cjs/index.cjs
CHANGED
|
@@ -50,8 +50,10 @@ function addRangeHint(element, parts, state) {
|
|
|
50
50
|
}
|
|
51
51
|
}
|
|
52
52
|
function addFileSizeHint(element, parts, state) {
|
|
53
|
-
|
|
54
|
-
|
|
53
|
+
var _a;
|
|
54
|
+
const sizeMB = (_a = element.maxSize) != null ? _a : element.maxSizeMB;
|
|
55
|
+
if (sizeMB && sizeMB !== Infinity) {
|
|
56
|
+
parts.push(t("hintMaxSize", state, { size: sizeMB }));
|
|
55
57
|
}
|
|
56
58
|
}
|
|
57
59
|
function addFormatHint(element, parts, state) {
|
|
@@ -125,6 +127,25 @@ function validateSchema(schema) {
|
|
|
125
127
|
});
|
|
126
128
|
}
|
|
127
129
|
}
|
|
130
|
+
function validateContainerProps(element, elementPath, errors2) {
|
|
131
|
+
if ("columns" in element && element.columns !== void 0) {
|
|
132
|
+
const columns = element.columns;
|
|
133
|
+
const validColumns = [1, 2, 3, 4];
|
|
134
|
+
if (!Number.isInteger(columns) || !validColumns.includes(columns)) {
|
|
135
|
+
errors2.push(
|
|
136
|
+
`${elementPath}: columns must be 1, 2, 3, or 4 (got ${columns})`
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
if ("displayMode" in element && element.displayMode !== void 0) {
|
|
141
|
+
const displayMode = element.displayMode;
|
|
142
|
+
if (displayMode !== "stack" && displayMode !== "slides") {
|
|
143
|
+
errors2.push(
|
|
144
|
+
`${elementPath}: displayMode must be "stack" or "slides" (got ${JSON.stringify(displayMode)})`
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
128
149
|
function checkFlatOutputCollisions(elements, scopePath) {
|
|
129
150
|
var _a, _b;
|
|
130
151
|
const allOutputKeys = /* @__PURE__ */ new Set();
|
|
@@ -205,15 +226,7 @@ function validateSchema(schema) {
|
|
|
205
226
|
validateElements(element.elements, `${elementPath}.elements`);
|
|
206
227
|
}
|
|
207
228
|
if (element.type === "container" && element.elements) {
|
|
208
|
-
|
|
209
|
-
const columns = element.columns;
|
|
210
|
-
const validColumns = [1, 2, 3, 4];
|
|
211
|
-
if (!Number.isInteger(columns) || !validColumns.includes(columns)) {
|
|
212
|
-
errors.push(
|
|
213
|
-
`${elementPath}: columns must be 1, 2, 3, or 4 (got ${columns})`
|
|
214
|
-
);
|
|
215
|
-
}
|
|
216
|
-
}
|
|
229
|
+
validateContainerProps(element, elementPath, errors);
|
|
217
230
|
if ("prefillHints" in element && element.prefillHints) {
|
|
218
231
|
const prefillHints = element.prefillHints;
|
|
219
232
|
if (Array.isArray(prefillHints)) {
|
|
@@ -393,6 +406,169 @@ function deepEqual(a, b) {
|
|
|
393
406
|
return a === b;
|
|
394
407
|
}
|
|
395
408
|
|
|
409
|
+
// src/utils/styles.ts
|
|
410
|
+
function mountCounterInLabel(wrapper, counter) {
|
|
411
|
+
const labelRow = wrapper.querySelector(
|
|
412
|
+
":scope > [data-fb-label-row]"
|
|
413
|
+
);
|
|
414
|
+
if (labelRow) labelRow.appendChild(counter);
|
|
415
|
+
}
|
|
416
|
+
function createAddItemRow(classNameSuffix, onClick, options = {}) {
|
|
417
|
+
var _a;
|
|
418
|
+
const label = (_a = options.label) != null ? _a : "";
|
|
419
|
+
const showCounter = options.showCounter !== false;
|
|
420
|
+
const row = document.createElement("div");
|
|
421
|
+
row.className = "fb-add-row mt-2";
|
|
422
|
+
row.style.cssText = "display:flex;align-items:stretch;width:100%;";
|
|
423
|
+
const button = document.createElement("button");
|
|
424
|
+
button.type = "button";
|
|
425
|
+
button.className = `add-${classNameSuffix}-btn`;
|
|
426
|
+
button.style.cssText = `
|
|
427
|
+
flex: 1 1 auto;
|
|
428
|
+
display: inline-flex;
|
|
429
|
+
align-items: center;
|
|
430
|
+
justify-content: center;
|
|
431
|
+
gap: 6px;
|
|
432
|
+
padding: 6px 10px;
|
|
433
|
+
border: 1px dashed var(--fb-primary-color);
|
|
434
|
+
border-radius: var(--fb-border-radius);
|
|
435
|
+
background: transparent;
|
|
436
|
+
color: var(--fb-primary-color);
|
|
437
|
+
font-size: var(--fb-font-size-small, var(--fb-font-size));
|
|
438
|
+
font-weight: 500;
|
|
439
|
+
font-family: var(--fb-font-family);
|
|
440
|
+
cursor: pointer;
|
|
441
|
+
transition: border-color var(--fb-transition-duration), color var(--fb-transition-duration), background-color var(--fb-transition-duration);
|
|
442
|
+
`;
|
|
443
|
+
button.textContent = label ? `+ ${label}` : "+";
|
|
444
|
+
button.addEventListener("mouseenter", () => {
|
|
445
|
+
if (button.disabled) return;
|
|
446
|
+
button.style.borderStyle = "solid";
|
|
447
|
+
button.style.backgroundColor = "var(--fb-background-hover-color)";
|
|
448
|
+
});
|
|
449
|
+
button.addEventListener("mouseleave", () => {
|
|
450
|
+
button.style.borderStyle = "dashed";
|
|
451
|
+
button.style.backgroundColor = "transparent";
|
|
452
|
+
});
|
|
453
|
+
button.onclick = onClick;
|
|
454
|
+
const counter = document.createElement("span");
|
|
455
|
+
counter.className = "fb-add-counter";
|
|
456
|
+
counter.style.cssText = `
|
|
457
|
+
margin-left: auto;
|
|
458
|
+
font-size: var(--fb-font-size-small, 0.875rem);
|
|
459
|
+
color: var(--fb-text-secondary-color);
|
|
460
|
+
font-weight: 400;
|
|
461
|
+
`;
|
|
462
|
+
if (!showCounter) counter.style.display = "none";
|
|
463
|
+
row.appendChild(button);
|
|
464
|
+
const update = (current, max) => {
|
|
465
|
+
const reached = current >= max;
|
|
466
|
+
row.style.display = reached ? "none" : "flex";
|
|
467
|
+
button.style.display = reached ? "none" : "inline-flex";
|
|
468
|
+
button.disabled = reached;
|
|
469
|
+
if (showCounter) {
|
|
470
|
+
counter.textContent = `${current}/${max === Infinity ? "\u221E" : max}`;
|
|
471
|
+
}
|
|
472
|
+
};
|
|
473
|
+
return { row, button, counter, update };
|
|
474
|
+
}
|
|
475
|
+
function createSlideAddTile(onClick, options = {}) {
|
|
476
|
+
var _a;
|
|
477
|
+
const label = (_a = options.label) != null ? _a : "";
|
|
478
|
+
const tile = document.createElement("button");
|
|
479
|
+
tile.type = "button";
|
|
480
|
+
tile.className = "add-container-btn fb-slide-add";
|
|
481
|
+
tile.style.cssText = `
|
|
482
|
+
display: flex;
|
|
483
|
+
flex-direction: column;
|
|
484
|
+
align-items: center;
|
|
485
|
+
justify-content: center;
|
|
486
|
+
gap: 12px;
|
|
487
|
+
width: 100%;
|
|
488
|
+
min-height: 180px;
|
|
489
|
+
align-self: stretch;
|
|
490
|
+
padding: 24px 16px;
|
|
491
|
+
border: 1.5px dashed var(--fb-primary-color);
|
|
492
|
+
border-radius: var(--fb-border-radius);
|
|
493
|
+
background: transparent;
|
|
494
|
+
color: var(--fb-primary-color);
|
|
495
|
+
font-size: var(--fb-font-size-small, var(--fb-font-size));
|
|
496
|
+
font-weight: 500;
|
|
497
|
+
font-family: var(--fb-font-family);
|
|
498
|
+
cursor: pointer;
|
|
499
|
+
transition: border-color var(--fb-transition-duration), color var(--fb-transition-duration), background-color var(--fb-transition-duration);
|
|
500
|
+
`;
|
|
501
|
+
const circle = document.createElement("span");
|
|
502
|
+
circle.className = "fb-slide-add-circle";
|
|
503
|
+
circle.style.cssText = `
|
|
504
|
+
display: inline-flex;
|
|
505
|
+
align-items: center;
|
|
506
|
+
justify-content: center;
|
|
507
|
+
width: 36px;
|
|
508
|
+
height: 36px;
|
|
509
|
+
border: 1px solid var(--fb-primary-color);
|
|
510
|
+
border-radius: 50%;
|
|
511
|
+
background: var(--fb-background-color);
|
|
512
|
+
font-size: 20px;
|
|
513
|
+
line-height: 1;
|
|
514
|
+
color: inherit;
|
|
515
|
+
transition: inherit;
|
|
516
|
+
`;
|
|
517
|
+
circle.textContent = "+";
|
|
518
|
+
tile.appendChild(circle);
|
|
519
|
+
if (label) {
|
|
520
|
+
const text = document.createElement("span");
|
|
521
|
+
text.textContent = label;
|
|
522
|
+
tile.appendChild(text);
|
|
523
|
+
}
|
|
524
|
+
tile.addEventListener("mouseenter", () => {
|
|
525
|
+
if (tile.disabled) return;
|
|
526
|
+
tile.style.borderStyle = "solid";
|
|
527
|
+
tile.style.backgroundColor = "var(--fb-background-hover-color)";
|
|
528
|
+
});
|
|
529
|
+
tile.addEventListener("mouseleave", () => {
|
|
530
|
+
tile.style.borderStyle = "dashed";
|
|
531
|
+
tile.style.backgroundColor = "transparent";
|
|
532
|
+
});
|
|
533
|
+
tile.onclick = onClick;
|
|
534
|
+
const counter = document.createElement("span");
|
|
535
|
+
counter.className = "fb-add-counter";
|
|
536
|
+
counter.style.cssText = `
|
|
537
|
+
margin-left: auto;
|
|
538
|
+
font-size: var(--fb-font-size-small, 0.875rem);
|
|
539
|
+
color: var(--fb-text-secondary-color);
|
|
540
|
+
font-weight: 400;
|
|
541
|
+
`;
|
|
542
|
+
const update = (current, max) => {
|
|
543
|
+
const reached = current >= max;
|
|
544
|
+
tile.style.display = reached ? "none" : "flex";
|
|
545
|
+
tile.disabled = reached;
|
|
546
|
+
counter.textContent = `${current}/${max === Infinity ? "\u221E" : max}`;
|
|
547
|
+
};
|
|
548
|
+
return { tile, counter, update };
|
|
549
|
+
}
|
|
550
|
+
function applyActionButtonStyles(button, isFormLevel = false) {
|
|
551
|
+
button.style.cssText = `
|
|
552
|
+
background-color: var(--fb-action-bg-color);
|
|
553
|
+
color: var(--fb-action-text-color);
|
|
554
|
+
border: var(--fb-border-width) solid var(--fb-action-border-color);
|
|
555
|
+
padding: ${isFormLevel ? "0.5rem 1rem" : "0.5rem 0.75rem"};
|
|
556
|
+
font-size: var(--fb-font-size);
|
|
557
|
+
font-weight: var(--fb-font-weight-medium);
|
|
558
|
+
border-radius: var(--fb-border-radius);
|
|
559
|
+
transition: all var(--fb-transition-duration);
|
|
560
|
+
box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
|
|
561
|
+
`;
|
|
562
|
+
button.addEventListener("mouseenter", () => {
|
|
563
|
+
button.style.backgroundColor = "var(--fb-action-hover-bg-color)";
|
|
564
|
+
button.style.borderColor = "var(--fb-action-hover-border-color)";
|
|
565
|
+
});
|
|
566
|
+
button.addEventListener("mouseleave", () => {
|
|
567
|
+
button.style.backgroundColor = "var(--fb-action-bg-color)";
|
|
568
|
+
button.style.borderColor = "var(--fb-action-border-color)";
|
|
569
|
+
});
|
|
570
|
+
}
|
|
571
|
+
|
|
396
572
|
// src/components/text.ts
|
|
397
573
|
function createCharCounter(element, input, isTextarea = false) {
|
|
398
574
|
const counter = document.createElement("span");
|
|
@@ -445,12 +621,13 @@ function renderTextElement(element, ctx, wrapper, pathKey) {
|
|
|
445
621
|
const readonly = isElementReadonly(element, state, ctx);
|
|
446
622
|
const inputWrapper = document.createElement("div");
|
|
447
623
|
inputWrapper.style.cssText = "position: relative;";
|
|
624
|
+
const hasCharCounter = !readonly && (element.minLength != null || element.maxLength != null);
|
|
448
625
|
const textInput = document.createElement("input");
|
|
449
626
|
textInput.type = "text";
|
|
450
627
|
textInput.className = "w-full rounded-lg";
|
|
451
628
|
textInput.style.cssText = `
|
|
452
629
|
padding: var(--fb-input-padding-y) var(--fb-input-padding-x);
|
|
453
|
-
padding-right: 60px;
|
|
630
|
+
${hasCharCounter ? "padding-right: 60px;" : ""}
|
|
454
631
|
border: var(--fb-border-width) solid var(--fb-border-color);
|
|
455
632
|
border-radius: var(--fb-border-radius);
|
|
456
633
|
background-color: ${readonly ? "var(--fb-background-readonly-color)" : "var(--fb-background-color)"};
|
|
@@ -495,7 +672,7 @@ function renderTextElement(element, ctx, wrapper, pathKey) {
|
|
|
495
672
|
textInput.addEventListener("input", handleChange);
|
|
496
673
|
}
|
|
497
674
|
inputWrapper.appendChild(textInput);
|
|
498
|
-
if (
|
|
675
|
+
if (hasCharCounter) {
|
|
499
676
|
const counter = createCharCounter(element, textInput, false);
|
|
500
677
|
inputWrapper.appendChild(counter);
|
|
501
678
|
}
|
|
@@ -507,6 +684,7 @@ function renderMultipleTextElement(element, ctx, wrapper, pathKey) {
|
|
|
507
684
|
const readonly = isElementReadonly(element, state, ctx);
|
|
508
685
|
const prefillValues = ctx.prefill[element.key] || [];
|
|
509
686
|
const values = Array.isArray(prefillValues) ? [...prefillValues] : [];
|
|
687
|
+
const hasCharCounter = !readonly && (element.minLength != null || element.maxLength != null);
|
|
510
688
|
const minCount = (_a = element.minCount) != null ? _a : 1;
|
|
511
689
|
const maxCount = (_b = element.maxCount) != null ? _b : Infinity;
|
|
512
690
|
while (values.length < minCount) {
|
|
@@ -533,7 +711,7 @@ function renderMultipleTextElement(element, ctx, wrapper, pathKey) {
|
|
|
533
711
|
textInput.type = "text";
|
|
534
712
|
textInput.style.cssText = `
|
|
535
713
|
padding: var(--fb-input-padding-y) var(--fb-input-padding-x);
|
|
536
|
-
padding-right: 60px;
|
|
714
|
+
${hasCharCounter ? "padding-right: 60px;" : ""}
|
|
537
715
|
border: var(--fb-border-width) solid var(--fb-border-color);
|
|
538
716
|
border-radius: var(--fb-border-radius);
|
|
539
717
|
background-color: ${readonly ? "var(--fb-background-readonly-color)" : "var(--fb-background-color)"};
|
|
@@ -577,7 +755,7 @@ function renderMultipleTextElement(element, ctx, wrapper, pathKey) {
|
|
|
577
755
|
textInput.addEventListener("input", handleChange);
|
|
578
756
|
}
|
|
579
757
|
inputContainer.appendChild(textInput);
|
|
580
|
-
if (
|
|
758
|
+
if (hasCharCounter) {
|
|
581
759
|
const counter = createCharCounter(element, textInput, false);
|
|
582
760
|
inputContainer.appendChild(counter);
|
|
583
761
|
}
|
|
@@ -634,50 +812,24 @@ function renderMultipleTextElement(element, ctx, wrapper, pathKey) {
|
|
|
634
812
|
removeBtn.style.pointerEvents = disabled ? "none" : "auto";
|
|
635
813
|
});
|
|
636
814
|
}
|
|
637
|
-
let
|
|
638
|
-
let countDisplay = null;
|
|
815
|
+
let addUpdate = null;
|
|
639
816
|
if (!readonly) {
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
addBtn.addEventListener("mouseenter", () => {
|
|
654
|
-
addBtn.style.backgroundColor = "var(--fb-background-hover-color)";
|
|
655
|
-
});
|
|
656
|
-
addBtn.addEventListener("mouseleave", () => {
|
|
657
|
-
addBtn.style.backgroundColor = "transparent";
|
|
658
|
-
});
|
|
659
|
-
addBtn.onclick = () => {
|
|
660
|
-
values.push(element.default || "");
|
|
661
|
-
addTextItem(element.default || "");
|
|
662
|
-
updateAddButton();
|
|
663
|
-
updateRemoveButtons();
|
|
664
|
-
};
|
|
665
|
-
countDisplay = document.createElement("span");
|
|
666
|
-
countDisplay.className = "text-sm text-gray-500";
|
|
667
|
-
addRow.appendChild(addBtn);
|
|
668
|
-
addRow.appendChild(countDisplay);
|
|
669
|
-
wrapper.appendChild(addRow);
|
|
817
|
+
const handle = createAddItemRow(
|
|
818
|
+
"text",
|
|
819
|
+
() => {
|
|
820
|
+
values.push(element.default || "");
|
|
821
|
+
addTextItem(element.default || "");
|
|
822
|
+
updateAddButton();
|
|
823
|
+
updateRemoveButtons();
|
|
824
|
+
},
|
|
825
|
+
{ label: element.addLabel }
|
|
826
|
+
);
|
|
827
|
+
addUpdate = handle.update;
|
|
828
|
+
mountCounterInLabel(wrapper, handle.counter);
|
|
829
|
+
wrapper.appendChild(handle.row);
|
|
670
830
|
}
|
|
671
831
|
function updateAddButton() {
|
|
672
|
-
if (
|
|
673
|
-
const addBtn = addRow.querySelector(".add-text-btn");
|
|
674
|
-
if (addBtn) {
|
|
675
|
-
const disabled = values.length >= maxCount;
|
|
676
|
-
addBtn.disabled = disabled;
|
|
677
|
-
addBtn.style.opacity = disabled ? "0.5" : "1";
|
|
678
|
-
addBtn.style.pointerEvents = disabled ? "none" : "auto";
|
|
679
|
-
}
|
|
680
|
-
countDisplay.textContent = `${values.length}/${maxCount === Infinity ? "\u221E" : maxCount}`;
|
|
832
|
+
if (addUpdate) addUpdate(values.length, maxCount);
|
|
681
833
|
}
|
|
682
834
|
values.forEach((value) => addTextItem(value));
|
|
683
835
|
updateAddButton();
|
|
@@ -851,8 +1003,12 @@ function renderTextareaElement(element, ctx, wrapper, pathKey) {
|
|
|
851
1003
|
const textareaWrapper = document.createElement("div");
|
|
852
1004
|
textareaWrapper.style.cssText = "position: relative;";
|
|
853
1005
|
const textareaInput = document.createElement("textarea");
|
|
854
|
-
textareaInput.className = "w-full
|
|
855
|
-
textareaInput.style.cssText =
|
|
1006
|
+
textareaInput.className = "w-full border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 resize-none";
|
|
1007
|
+
textareaInput.style.cssText = `
|
|
1008
|
+
padding: var(--fb-input-padding-y) var(--fb-input-padding-x) 24px var(--fb-input-padding-x);
|
|
1009
|
+
font-size: var(--fb-font-size);
|
|
1010
|
+
font-family: var(--fb-font-family);
|
|
1011
|
+
`;
|
|
856
1012
|
textareaInput.name = pathKey;
|
|
857
1013
|
textareaInput.placeholder = element.placeholder || "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u0442\u0435\u043A\u0441\u0442";
|
|
858
1014
|
textareaInput.rows = element.rows || 4;
|
|
@@ -905,8 +1061,12 @@ function renderMultipleTextareaElement(element, ctx, wrapper, pathKey) {
|
|
|
905
1061
|
const textareaContainer = document.createElement("div");
|
|
906
1062
|
textareaContainer.style.cssText = "position: relative;";
|
|
907
1063
|
const textareaInput = document.createElement("textarea");
|
|
908
|
-
textareaInput.className = "w-full
|
|
909
|
-
textareaInput.style.cssText =
|
|
1064
|
+
textareaInput.className = "w-full border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 resize-none";
|
|
1065
|
+
textareaInput.style.cssText = `
|
|
1066
|
+
padding: var(--fb-input-padding-y) var(--fb-input-padding-x) 24px var(--fb-input-padding-x);
|
|
1067
|
+
font-size: var(--fb-font-size);
|
|
1068
|
+
font-family: var(--fb-font-family);
|
|
1069
|
+
`;
|
|
910
1070
|
textareaInput.placeholder = element.placeholder || t("placeholderText", state);
|
|
911
1071
|
textareaInput.rows = element.rows || 4;
|
|
912
1072
|
textareaInput.value = value;
|
|
@@ -969,52 +1129,24 @@ function renderMultipleTextareaElement(element, ctx, wrapper, pathKey) {
|
|
|
969
1129
|
removeBtn.style.pointerEvents = disabled ? "none" : "auto";
|
|
970
1130
|
});
|
|
971
1131
|
}
|
|
972
|
-
let
|
|
973
|
-
let countDisplay = null;
|
|
1132
|
+
let addUpdate = null;
|
|
974
1133
|
if (!readonly) {
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
addBtn.addEventListener("mouseenter", () => {
|
|
989
|
-
addBtn.style.backgroundColor = "var(--fb-background-hover-color)";
|
|
990
|
-
});
|
|
991
|
-
addBtn.addEventListener("mouseleave", () => {
|
|
992
|
-
addBtn.style.backgroundColor = "transparent";
|
|
993
|
-
});
|
|
994
|
-
addBtn.onclick = () => {
|
|
995
|
-
values.push(element.default || "");
|
|
996
|
-
addTextareaItem(element.default || "");
|
|
997
|
-
updateAddButton();
|
|
998
|
-
updateRemoveButtons();
|
|
999
|
-
};
|
|
1000
|
-
countDisplay = document.createElement("span");
|
|
1001
|
-
countDisplay.className = "text-sm text-gray-500";
|
|
1002
|
-
addRow.appendChild(addBtn);
|
|
1003
|
-
addRow.appendChild(countDisplay);
|
|
1004
|
-
wrapper.appendChild(addRow);
|
|
1134
|
+
const handle = createAddItemRow(
|
|
1135
|
+
"textarea",
|
|
1136
|
+
() => {
|
|
1137
|
+
values.push(element.default || "");
|
|
1138
|
+
addTextareaItem(element.default || "");
|
|
1139
|
+
updateAddButton();
|
|
1140
|
+
updateRemoveButtons();
|
|
1141
|
+
},
|
|
1142
|
+
{ label: element.addLabel }
|
|
1143
|
+
);
|
|
1144
|
+
addUpdate = handle.update;
|
|
1145
|
+
mountCounterInLabel(wrapper, handle.counter);
|
|
1146
|
+
wrapper.appendChild(handle.row);
|
|
1005
1147
|
}
|
|
1006
1148
|
function updateAddButton() {
|
|
1007
|
-
if (
|
|
1008
|
-
const addBtn = addRow.querySelector(
|
|
1009
|
-
".add-textarea-btn"
|
|
1010
|
-
);
|
|
1011
|
-
if (addBtn) {
|
|
1012
|
-
const disabled = values.length >= maxCount;
|
|
1013
|
-
addBtn.disabled = disabled;
|
|
1014
|
-
addBtn.style.opacity = disabled ? "0.5" : "1";
|
|
1015
|
-
addBtn.style.pointerEvents = disabled ? "none" : "auto";
|
|
1016
|
-
}
|
|
1017
|
-
countDisplay.textContent = `${values.length}/${maxCount === Infinity ? "\u221E" : maxCount}`;
|
|
1149
|
+
if (addUpdate) addUpdate(values.length, maxCount);
|
|
1018
1150
|
}
|
|
1019
1151
|
values.forEach((value) => addTextareaItem(value));
|
|
1020
1152
|
updateAddButton();
|
|
@@ -1092,8 +1224,14 @@ function renderNumberElement(element, ctx, wrapper, pathKey) {
|
|
|
1092
1224
|
inputWrapper.style.cssText = "position: relative;";
|
|
1093
1225
|
const numberInput = document.createElement("input");
|
|
1094
1226
|
numberInput.type = "number";
|
|
1095
|
-
numberInput.className = "w-full
|
|
1096
|
-
numberInput.style.cssText =
|
|
1227
|
+
numberInput.className = "w-full border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500";
|
|
1228
|
+
numberInput.style.cssText = `
|
|
1229
|
+
padding: var(--fb-input-padding-y) 60px var(--fb-input-padding-y) var(--fb-input-padding-x);
|
|
1230
|
+
font-size: var(--fb-font-size);
|
|
1231
|
+
font-family: var(--fb-font-family);
|
|
1232
|
+
width: 100%;
|
|
1233
|
+
box-sizing: border-box;
|
|
1234
|
+
`;
|
|
1097
1235
|
numberInput.name = pathKey;
|
|
1098
1236
|
numberInput.placeholder = element.placeholder || "0";
|
|
1099
1237
|
if (element.min !== void 0) numberInput.min = element.min.toString();
|
|
@@ -1146,8 +1284,14 @@ function renderMultipleNumberElement(element, ctx, wrapper, pathKey) {
|
|
|
1146
1284
|
inputContainer.style.cssText = "position: relative; flex: 1;";
|
|
1147
1285
|
const numberInput = document.createElement("input");
|
|
1148
1286
|
numberInput.type = "number";
|
|
1149
|
-
numberInput.className = "w-full
|
|
1150
|
-
numberInput.style.cssText =
|
|
1287
|
+
numberInput.className = "w-full border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500";
|
|
1288
|
+
numberInput.style.cssText = `
|
|
1289
|
+
padding: var(--fb-input-padding-y) 60px var(--fb-input-padding-y) var(--fb-input-padding-x);
|
|
1290
|
+
font-size: var(--fb-font-size);
|
|
1291
|
+
font-family: var(--fb-font-family);
|
|
1292
|
+
width: 100%;
|
|
1293
|
+
box-sizing: border-box;
|
|
1294
|
+
`;
|
|
1151
1295
|
numberInput.placeholder = element.placeholder || "0";
|
|
1152
1296
|
if (element.min !== void 0) numberInput.min = element.min.toString();
|
|
1153
1297
|
if (element.max !== void 0) numberInput.max = element.max.toString();
|
|
@@ -1209,50 +1353,24 @@ function renderMultipleNumberElement(element, ctx, wrapper, pathKey) {
|
|
|
1209
1353
|
removeBtn.style.pointerEvents = disabled ? "none" : "auto";
|
|
1210
1354
|
});
|
|
1211
1355
|
}
|
|
1212
|
-
let
|
|
1213
|
-
let countDisplay = null;
|
|
1356
|
+
let addUpdate = null;
|
|
1214
1357
|
if (!readonly) {
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
addBtn.addEventListener("mouseenter", () => {
|
|
1229
|
-
addBtn.style.backgroundColor = "var(--fb-background-hover-color)";
|
|
1230
|
-
});
|
|
1231
|
-
addBtn.addEventListener("mouseleave", () => {
|
|
1232
|
-
addBtn.style.backgroundColor = "transparent";
|
|
1233
|
-
});
|
|
1234
|
-
addBtn.onclick = () => {
|
|
1235
|
-
values.push(element.default || "");
|
|
1236
|
-
addNumberItem(element.default || "");
|
|
1237
|
-
updateAddButton();
|
|
1238
|
-
updateRemoveButtons();
|
|
1239
|
-
};
|
|
1240
|
-
countDisplay = document.createElement("span");
|
|
1241
|
-
countDisplay.className = "text-sm text-gray-500";
|
|
1242
|
-
addRow.appendChild(addBtn);
|
|
1243
|
-
addRow.appendChild(countDisplay);
|
|
1244
|
-
wrapper.appendChild(addRow);
|
|
1358
|
+
const handle = createAddItemRow(
|
|
1359
|
+
"number",
|
|
1360
|
+
() => {
|
|
1361
|
+
values.push(element.default || "");
|
|
1362
|
+
addNumberItem(element.default || "");
|
|
1363
|
+
updateAddButton();
|
|
1364
|
+
updateRemoveButtons();
|
|
1365
|
+
},
|
|
1366
|
+
{ label: element.addLabel }
|
|
1367
|
+
);
|
|
1368
|
+
addUpdate = handle.update;
|
|
1369
|
+
mountCounterInLabel(wrapper, handle.counter);
|
|
1370
|
+
wrapper.appendChild(handle.row);
|
|
1245
1371
|
}
|
|
1246
1372
|
function updateAddButton() {
|
|
1247
|
-
if (
|
|
1248
|
-
const addBtn = addRow.querySelector(".add-number-btn");
|
|
1249
|
-
if (addBtn) {
|
|
1250
|
-
const disabled = values.length >= maxCount;
|
|
1251
|
-
addBtn.disabled = disabled;
|
|
1252
|
-
addBtn.style.opacity = disabled ? "0.5" : "1";
|
|
1253
|
-
addBtn.style.pointerEvents = disabled ? "none" : "auto";
|
|
1254
|
-
}
|
|
1255
|
-
countDisplay.textContent = `${values.length}/${maxCount === Infinity ? "\u221E" : maxCount}`;
|
|
1373
|
+
if (addUpdate) addUpdate(values.length, maxCount);
|
|
1256
1374
|
}
|
|
1257
1375
|
values.forEach((value) => addNumberItem(value));
|
|
1258
1376
|
updateAddButton();
|
|
@@ -1421,7 +1539,12 @@ function renderSelectElement(element, ctx, wrapper, pathKey) {
|
|
|
1421
1539
|
const state = ctx.state;
|
|
1422
1540
|
const readonly = isElementReadonly(element, state, ctx);
|
|
1423
1541
|
const selectInput = document.createElement("select");
|
|
1424
|
-
selectInput.className = "w-full
|
|
1542
|
+
selectInput.className = "w-full border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500";
|
|
1543
|
+
selectInput.style.cssText = `
|
|
1544
|
+
padding: var(--fb-input-padding-y) var(--fb-input-padding-x);
|
|
1545
|
+
font-size: var(--fb-font-size);
|
|
1546
|
+
font-family: var(--fb-font-family);
|
|
1547
|
+
`;
|
|
1425
1548
|
selectInput.name = pathKey;
|
|
1426
1549
|
selectInput.disabled = readonly;
|
|
1427
1550
|
(element.options || []).forEach((option) => {
|
|
@@ -1474,7 +1597,12 @@ function renderMultipleSelectElement(element, ctx, wrapper, pathKey) {
|
|
|
1474
1597
|
const itemWrapper = document.createElement("div");
|
|
1475
1598
|
itemWrapper.className = "multiple-select-item flex items-center gap-2";
|
|
1476
1599
|
const selectInput = document.createElement("select");
|
|
1477
|
-
selectInput.className = "flex-1
|
|
1600
|
+
selectInput.className = "flex-1 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500";
|
|
1601
|
+
selectInput.style.cssText = `
|
|
1602
|
+
padding: var(--fb-input-padding-y) var(--fb-input-padding-x);
|
|
1603
|
+
font-size: var(--fb-font-size);
|
|
1604
|
+
font-family: var(--fb-font-family);
|
|
1605
|
+
`;
|
|
1478
1606
|
selectInput.disabled = readonly;
|
|
1479
1607
|
(element.options || []).forEach((option) => {
|
|
1480
1608
|
const optionElement = document.createElement("option");
|
|
@@ -1531,52 +1659,26 @@ function renderMultipleSelectElement(element, ctx, wrapper, pathKey) {
|
|
|
1531
1659
|
removeBtn.style.pointerEvents = disabled ? "none" : "auto";
|
|
1532
1660
|
});
|
|
1533
1661
|
}
|
|
1534
|
-
let
|
|
1535
|
-
let countDisplay = null;
|
|
1662
|
+
let addUpdate = null;
|
|
1536
1663
|
if (!readonly) {
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
});
|
|
1553
|
-
addBtn.addEventListener("mouseleave", () => {
|
|
1554
|
-
addBtn.style.backgroundColor = "transparent";
|
|
1555
|
-
});
|
|
1556
|
-
addBtn.onclick = () => {
|
|
1557
|
-
var _a2, _b2;
|
|
1558
|
-
const defaultValue = element.default || ((_b2 = (_a2 = element.options) == null ? void 0 : _a2[0]) == null ? void 0 : _b2.value) || "";
|
|
1559
|
-
values.push(defaultValue);
|
|
1560
|
-
addSelectItem(defaultValue);
|
|
1561
|
-
updateAddButton();
|
|
1562
|
-
updateRemoveButtons();
|
|
1563
|
-
};
|
|
1564
|
-
countDisplay = document.createElement("span");
|
|
1565
|
-
countDisplay.className = "text-sm text-gray-500";
|
|
1566
|
-
addRow.appendChild(addBtn);
|
|
1567
|
-
addRow.appendChild(countDisplay);
|
|
1568
|
-
wrapper.appendChild(addRow);
|
|
1664
|
+
const handle = createAddItemRow(
|
|
1665
|
+
"select",
|
|
1666
|
+
() => {
|
|
1667
|
+
var _a2, _b2;
|
|
1668
|
+
const defaultValue = element.default || ((_b2 = (_a2 = element.options) == null ? void 0 : _a2[0]) == null ? void 0 : _b2.value) || "";
|
|
1669
|
+
values.push(defaultValue);
|
|
1670
|
+
addSelectItem(defaultValue);
|
|
1671
|
+
updateAddButton();
|
|
1672
|
+
updateRemoveButtons();
|
|
1673
|
+
},
|
|
1674
|
+
{ label: element.addLabel }
|
|
1675
|
+
);
|
|
1676
|
+
addUpdate = handle.update;
|
|
1677
|
+
mountCounterInLabel(wrapper, handle.counter);
|
|
1678
|
+
wrapper.appendChild(handle.row);
|
|
1569
1679
|
}
|
|
1570
1680
|
function updateAddButton() {
|
|
1571
|
-
if (
|
|
1572
|
-
const addBtn = addRow.querySelector(".add-select-btn");
|
|
1573
|
-
if (addBtn) {
|
|
1574
|
-
const disabled = values.length >= maxCount;
|
|
1575
|
-
addBtn.disabled = disabled;
|
|
1576
|
-
addBtn.style.opacity = disabled ? "0.5" : "1";
|
|
1577
|
-
addBtn.style.pointerEvents = disabled ? "none" : "auto";
|
|
1578
|
-
}
|
|
1579
|
-
countDisplay.textContent = `${values.length}/${maxCount === Infinity ? "\u221E" : maxCount}`;
|
|
1681
|
+
if (addUpdate) addUpdate(values.length, maxCount);
|
|
1580
1682
|
}
|
|
1581
1683
|
values.forEach((value) => addSelectItem(value));
|
|
1582
1684
|
updateAddButton();
|
|
@@ -1925,54 +2027,26 @@ function renderMultipleSwitcherElement(element, ctx, wrapper, pathKey) {
|
|
|
1925
2027
|
removeBtn.style.pointerEvents = disabled ? "none" : "auto";
|
|
1926
2028
|
});
|
|
1927
2029
|
}
|
|
1928
|
-
let
|
|
1929
|
-
let countDisplay = null;
|
|
2030
|
+
let addUpdate = null;
|
|
1930
2031
|
if (!readonly) {
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
});
|
|
1947
|
-
addBtn.addEventListener("mouseleave", () => {
|
|
1948
|
-
addBtn.style.backgroundColor = "transparent";
|
|
1949
|
-
});
|
|
1950
|
-
addBtn.onclick = () => {
|
|
1951
|
-
var _a2, _b2;
|
|
1952
|
-
const defaultValue = element.default || ((_b2 = (_a2 = element.options) == null ? void 0 : _a2[0]) == null ? void 0 : _b2.value) || "";
|
|
1953
|
-
values.push(defaultValue);
|
|
1954
|
-
addSwitcherItem(defaultValue);
|
|
1955
|
-
updateAddButton();
|
|
1956
|
-
updateRemoveButtons();
|
|
1957
|
-
};
|
|
1958
|
-
countDisplay = document.createElement("span");
|
|
1959
|
-
countDisplay.className = "text-sm text-gray-500";
|
|
1960
|
-
addRow.appendChild(addBtn);
|
|
1961
|
-
addRow.appendChild(countDisplay);
|
|
1962
|
-
wrapper.appendChild(addRow);
|
|
2032
|
+
const handle = createAddItemRow(
|
|
2033
|
+
"switcher",
|
|
2034
|
+
() => {
|
|
2035
|
+
var _a2, _b2;
|
|
2036
|
+
const defaultValue = element.default || ((_b2 = (_a2 = element.options) == null ? void 0 : _a2[0]) == null ? void 0 : _b2.value) || "";
|
|
2037
|
+
values.push(defaultValue);
|
|
2038
|
+
addSwitcherItem(defaultValue);
|
|
2039
|
+
updateAddButton();
|
|
2040
|
+
updateRemoveButtons();
|
|
2041
|
+
},
|
|
2042
|
+
{ label: element.addLabel }
|
|
2043
|
+
);
|
|
2044
|
+
addUpdate = handle.update;
|
|
2045
|
+
mountCounterInLabel(wrapper, handle.counter);
|
|
2046
|
+
wrapper.appendChild(handle.row);
|
|
1963
2047
|
}
|
|
1964
2048
|
function updateAddButton() {
|
|
1965
|
-
if (
|
|
1966
|
-
const addBtn = addRow.querySelector(
|
|
1967
|
-
".add-switcher-btn"
|
|
1968
|
-
);
|
|
1969
|
-
if (addBtn) {
|
|
1970
|
-
const disabled = values.length >= maxCount;
|
|
1971
|
-
addBtn.disabled = disabled;
|
|
1972
|
-
addBtn.style.opacity = disabled ? "0.5" : "1";
|
|
1973
|
-
addBtn.style.pointerEvents = disabled ? "none" : "auto";
|
|
1974
|
-
}
|
|
1975
|
-
countDisplay.textContent = `${values.length}/${maxCount === Infinity ? "\u221E" : maxCount}`;
|
|
2049
|
+
if (addUpdate) addUpdate(values.length, maxCount);
|
|
1976
2050
|
}
|
|
1977
2051
|
values.forEach((value) => addSwitcherItem(value));
|
|
1978
2052
|
updateAddButton();
|
|
@@ -2225,7 +2299,13 @@ function ensureFileStyles() {
|
|
|
2225
2299
|
style.textContent = `
|
|
2226
2300
|
@keyframes fb-spin { to { transform: rotate(360deg); } }
|
|
2227
2301
|
|
|
2228
|
-
/*
|
|
2302
|
+
/* \u2500\u2500\u2500 Checker background utility \u2500\u2500\u2500 */
|
|
2303
|
+
/* Neutral diagonal-stripe background for image previews (never crops) */
|
|
2304
|
+
.fb-checker {
|
|
2305
|
+
background-image: repeating-linear-gradient(45deg, #fafafa 0 6px, #f3f4f6 6px 12px);
|
|
2306
|
+
}
|
|
2307
|
+
|
|
2308
|
+
/* \u2500\u2500\u2500 Spinner \u2500\u2500\u2500 */
|
|
2229
2309
|
.fb-spinner {
|
|
2230
2310
|
width: 36px;
|
|
2231
2311
|
height: 36px;
|
|
@@ -2236,207 +2316,271 @@ function ensureFileStyles() {
|
|
|
2236
2316
|
flex-shrink: 0;
|
|
2237
2317
|
}
|
|
2238
2318
|
|
|
2239
|
-
/*
|
|
2240
|
-
.fb-tile {
|
|
2241
|
-
width:
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2319
|
+
/* \u2500\u2500\u2500 Wide single-file add tile (empty state) \u2500\u2500\u2500 */
|
|
2320
|
+
.fb-wide-tile {
|
|
2321
|
+
width: 100%;
|
|
2322
|
+
border-radius: 0.75rem;
|
|
2323
|
+
border: 1px dashed #60a5fa;
|
|
2324
|
+
background: rgba(239,246,255,0.5);
|
|
2325
|
+
display: flex;
|
|
2245
2326
|
overflow: hidden;
|
|
2246
|
-
|
|
2247
|
-
|
|
2327
|
+
height: 180px;
|
|
2328
|
+
transition: border-color 150ms, background 150ms, box-shadow 150ms;
|
|
2329
|
+
cursor: pointer;
|
|
2248
2330
|
}
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
.fb-tile-resource {
|
|
2252
|
-
border: 1px solid var(--fb-file-upload-border-color, #d1d5db);
|
|
2331
|
+
.fb-wide-tile:hover {
|
|
2332
|
+
background: #eff6ff;
|
|
2253
2333
|
}
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2334
|
+
.fb-wide-tile.fb-drag-over {
|
|
2335
|
+
border-color: #3b82f6;
|
|
2336
|
+
border-width: 2px;
|
|
2337
|
+
background: #eff6ff;
|
|
2338
|
+
box-shadow: 0 0 0 4px rgba(191,219,254,0.7);
|
|
2258
2339
|
}
|
|
2259
2340
|
|
|
2260
|
-
/*
|
|
2261
|
-
.fb-tile-
|
|
2262
|
-
|
|
2341
|
+
/* Upload zone inside wide tile */
|
|
2342
|
+
.fb-wide-tile-upload {
|
|
2343
|
+
flex: 1;
|
|
2263
2344
|
display: flex;
|
|
2345
|
+
flex-direction: column;
|
|
2264
2346
|
align-items: center;
|
|
2265
2347
|
justify-content: center;
|
|
2348
|
+
gap: 8px;
|
|
2349
|
+
color: #2563eb;
|
|
2350
|
+
padding: 16px;
|
|
2351
|
+
transition: background 150ms;
|
|
2266
2352
|
cursor: pointer;
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
border-color var(--fb-transition-duration, 200ms),
|
|
2271
|
-
color var(--fb-transition-duration, 200ms);
|
|
2353
|
+
background: transparent;
|
|
2354
|
+
border: none;
|
|
2355
|
+
font-family: inherit;
|
|
2272
2356
|
}
|
|
2273
|
-
.fb-tile-
|
|
2274
|
-
|
|
2275
|
-
color: var(--fb-text-color, #1f2937);
|
|
2357
|
+
.fb-wide-tile-upload:hover {
|
|
2358
|
+
background: rgba(191,219,254,0.25);
|
|
2276
2359
|
}
|
|
2277
2360
|
|
|
2278
|
-
/*
|
|
2279
|
-
.fb-tile-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
padding: 2px 6px;
|
|
2286
|
-
align-self: flex-end;
|
|
2287
|
-
margin-bottom: 4px;
|
|
2361
|
+
/* Vertical dashed divider between upload and library zones */
|
|
2362
|
+
.fb-wide-tile-divider {
|
|
2363
|
+
width: 1px;
|
|
2364
|
+
margin: 16px 0;
|
|
2365
|
+
border-left: 1px dashed rgba(96,165,250,0.5);
|
|
2366
|
+
background: transparent;
|
|
2367
|
+
flex-shrink: 0;
|
|
2288
2368
|
}
|
|
2289
2369
|
|
|
2290
|
-
/*
|
|
2291
|
-
.fb-
|
|
2292
|
-
width:
|
|
2293
|
-
|
|
2294
|
-
border: 2px dashed var(--fb-file-upload-border-color, #d1d5db);
|
|
2295
|
-
border-radius: var(--fb-border-radius, 0.5rem);
|
|
2370
|
+
/* Library zone inside wide tile */
|
|
2371
|
+
.fb-wide-tile-library {
|
|
2372
|
+
width: 176px;
|
|
2373
|
+
flex-shrink: 0;
|
|
2296
2374
|
display: flex;
|
|
2297
2375
|
flex-direction: column;
|
|
2298
2376
|
align-items: center;
|
|
2299
2377
|
justify-content: center;
|
|
2300
|
-
gap:
|
|
2378
|
+
gap: 8px;
|
|
2379
|
+
color: #2563eb;
|
|
2380
|
+
padding: 12px;
|
|
2381
|
+
transition: background 150ms;
|
|
2301
2382
|
cursor: pointer;
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2383
|
+
background: transparent;
|
|
2384
|
+
border: none;
|
|
2385
|
+
font-family: inherit;
|
|
2305
2386
|
}
|
|
2306
|
-
.fb-
|
|
2307
|
-
|
|
2308
|
-
background: var(--fb-background-hover-color, #f9fafb);
|
|
2387
|
+
.fb-wide-tile-library:hover {
|
|
2388
|
+
background: rgba(191,219,254,0.25);
|
|
2309
2389
|
}
|
|
2310
2390
|
|
|
2311
|
-
/*
|
|
2312
|
-
.fb-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2391
|
+
/* \u2500\u2500\u2500 Multi-file outer grid container \u2500\u2500\u2500 */
|
|
2392
|
+
.fb-multi-outer {
|
|
2393
|
+
border-radius: 0.75rem;
|
|
2394
|
+
border: 1px dashed #cbd5e1;
|
|
2395
|
+
background: rgba(248,250,252,0.4);
|
|
2396
|
+
padding: 12px;
|
|
2397
|
+
transition: border-color 150ms, background 150ms, box-shadow 150ms;
|
|
2398
|
+
}
|
|
2399
|
+
.fb-multi-outer.fb-drag-over {
|
|
2400
|
+
border-width: 2px;
|
|
2401
|
+
border-color: #3b82f6;
|
|
2402
|
+
background: rgba(239,246,255,0.4);
|
|
2403
|
+
box-shadow: 0 0 0 4px rgba(191,219,254,0.7);
|
|
2319
2404
|
}
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2405
|
+
|
|
2406
|
+
/* With files present: white solid border */
|
|
2407
|
+
.fb-multi-outer.fb-multi-has-files {
|
|
2408
|
+
border-style: solid;
|
|
2409
|
+
border-color: #e2e8f0;
|
|
2410
|
+
background: #fff;
|
|
2323
2411
|
}
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2412
|
+
|
|
2413
|
+
/* The CSS grid inside */
|
|
2414
|
+
.fb-multi-grid {
|
|
2415
|
+
display: grid;
|
|
2416
|
+
grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
|
|
2417
|
+
gap: 10px;
|
|
2328
2418
|
}
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2419
|
+
|
|
2420
|
+
/* \u2500\u2500\u2500 Multi square add-tile (combined upload + library) \u2500\u2500\u2500 */
|
|
2421
|
+
.fb-multi-add-tile {
|
|
2422
|
+
aspect-ratio: 1 / 1;
|
|
2423
|
+
border-radius: 0.5rem;
|
|
2424
|
+
border: 1px dashed #60a5fa;
|
|
2425
|
+
background: rgba(239,246,255,0.5);
|
|
2426
|
+
display: flex;
|
|
2427
|
+
flex-direction: column;
|
|
2428
|
+
overflow: hidden;
|
|
2429
|
+
transition: background 150ms;
|
|
2333
2430
|
}
|
|
2334
|
-
.fb-
|
|
2335
|
-
|
|
2336
|
-
color: var(--fb-text-secondary-color, #6b7280);
|
|
2431
|
+
.fb-multi-add-tile:hover {
|
|
2432
|
+
background: #eff6ff;
|
|
2337
2433
|
}
|
|
2338
|
-
.fb-
|
|
2339
|
-
|
|
2340
|
-
color:
|
|
2434
|
+
.fb-multi-add-tile.fb-drag-over-tile {
|
|
2435
|
+
border-width: 2px;
|
|
2436
|
+
border-color: #3b82f6;
|
|
2437
|
+
background: rgba(255,255,255,0.8);
|
|
2341
2438
|
}
|
|
2342
2439
|
|
|
2343
|
-
/*
|
|
2344
|
-
.fb-
|
|
2345
|
-
|
|
2346
|
-
inset: 0;
|
|
2347
|
-
background: transparent;
|
|
2348
|
-
transition: background var(--fb-transition-duration, 200ms);
|
|
2349
|
-
display: flex;
|
|
2350
|
-
align-items: flex-start;
|
|
2351
|
-
justify-content: flex-end;
|
|
2352
|
-
}
|
|
2353
|
-
.fb-tile-resource:hover .fb-tile-overlay {
|
|
2354
|
-
background: var(--fb-tile-hover-overlay-color, rgba(0,0,0,0.4));
|
|
2355
|
-
}
|
|
2356
|
-
.fb-tile-x-btn {
|
|
2357
|
-
margin: 3px;
|
|
2358
|
-
width: 18px;
|
|
2359
|
-
height: 18px;
|
|
2360
|
-
background: var(--fb-error-color, #ef4444);
|
|
2361
|
-
color: var(--fb-file-bg-color, #fff);
|
|
2362
|
-
border: none;
|
|
2363
|
-
border-radius: 50%;
|
|
2364
|
-
font-size: 11px;
|
|
2365
|
-
line-height: 1;
|
|
2366
|
-
cursor: pointer;
|
|
2440
|
+
/* Upload half of add-tile */
|
|
2441
|
+
.fb-multi-add-upload {
|
|
2442
|
+
flex: 1;
|
|
2367
2443
|
display: flex;
|
|
2444
|
+
flex-direction: column;
|
|
2368
2445
|
align-items: center;
|
|
2369
2446
|
justify-content: center;
|
|
2370
|
-
|
|
2371
|
-
|
|
2447
|
+
gap: 4px;
|
|
2448
|
+
color: #2563eb;
|
|
2449
|
+
cursor: pointer;
|
|
2450
|
+
background: transparent;
|
|
2451
|
+
border: none;
|
|
2452
|
+
font-family: inherit;
|
|
2453
|
+
width: 100%;
|
|
2454
|
+
transition: background 150ms;
|
|
2372
2455
|
}
|
|
2373
|
-
.fb-
|
|
2374
|
-
|
|
2456
|
+
.fb-multi-add-upload:hover {
|
|
2457
|
+
background: rgba(191,219,254,0.35);
|
|
2375
2458
|
}
|
|
2376
2459
|
|
|
2377
|
-
/*
|
|
2378
|
-
.fb-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
align-items: center;
|
|
2383
|
-
justify-content: center;
|
|
2384
|
-
background: var(--fb-tile-hover-overlay-color, rgba(0,0,0,0.25));
|
|
2460
|
+
/* Horizontal dashed divider inside add-tile */
|
|
2461
|
+
.fb-multi-add-divider {
|
|
2462
|
+
border-top: 1px dashed rgba(96,165,250,0.5);
|
|
2463
|
+
margin: 0;
|
|
2464
|
+
flex-shrink: 0;
|
|
2385
2465
|
}
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2466
|
+
|
|
2467
|
+
/* Library strip at bottom of add-tile */
|
|
2468
|
+
.fb-multi-add-library {
|
|
2469
|
+
padding: 6px 0;
|
|
2389
2470
|
display: flex;
|
|
2390
2471
|
align-items: center;
|
|
2391
2472
|
justify-content: center;
|
|
2473
|
+
gap: 4px;
|
|
2474
|
+
color: #2563eb;
|
|
2475
|
+
font-size: 11px;
|
|
2476
|
+
font-weight: 500;
|
|
2477
|
+
cursor: pointer;
|
|
2478
|
+
background: transparent;
|
|
2479
|
+
border: none;
|
|
2480
|
+
font-family: inherit;
|
|
2481
|
+
width: 100%;
|
|
2482
|
+
transition: background 150ms;
|
|
2483
|
+
flex-shrink: 0;
|
|
2484
|
+
}
|
|
2485
|
+
.fb-multi-add-library:hover {
|
|
2486
|
+
background: rgba(191,219,254,0.35);
|
|
2392
2487
|
}
|
|
2393
2488
|
|
|
2394
|
-
/*
|
|
2395
|
-
.fb-
|
|
2489
|
+
/* \u2500\u2500\u2500 Capacity placeholder squares \u2500\u2500\u2500 */
|
|
2490
|
+
.fb-multi-placeholder {
|
|
2491
|
+
aspect-ratio: 1 / 1;
|
|
2492
|
+
border-radius: 0.5rem;
|
|
2493
|
+
border: 1px solid #e2e8f0;
|
|
2494
|
+
}
|
|
2495
|
+
.fb-multi-placeholder.fb-drag-over {
|
|
2496
|
+
border-width: 2px;
|
|
2497
|
+
border-style: dashed;
|
|
2498
|
+
border-color: #93c5fd;
|
|
2499
|
+
background: rgba(219,234,254,0.6);
|
|
2500
|
+
}
|
|
2501
|
+
|
|
2502
|
+
/* \u2500\u2500\u2500 Filled preview tile \u2500\u2500\u2500 */
|
|
2503
|
+
.fb-preview-tile {
|
|
2504
|
+
aspect-ratio: 1 / 1;
|
|
2505
|
+
border-radius: 0.5rem;
|
|
2506
|
+
border: 1px solid #e2e8f0;
|
|
2507
|
+
overflow: hidden;
|
|
2396
2508
|
position: relative;
|
|
2509
|
+
cursor: pointer;
|
|
2510
|
+
}
|
|
2511
|
+
.fb-preview-tile img {
|
|
2397
2512
|
width: 100%;
|
|
2398
2513
|
height: 100%;
|
|
2514
|
+
object-fit: contain;
|
|
2515
|
+
display: block;
|
|
2399
2516
|
}
|
|
2400
2517
|
|
|
2401
|
-
/*
|
|
2402
|
-
.fb-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
z-index: 10;
|
|
2518
|
+
/* \u2500\u2500\u2500 Uploading placeholder tile \u2500\u2500\u2500 */
|
|
2519
|
+
.fb-uploading-tile {
|
|
2520
|
+
aspect-ratio: 1 / 1;
|
|
2521
|
+
border-radius: 0.5rem;
|
|
2522
|
+
border: 2px dashed #d1d5db;
|
|
2407
2523
|
display: flex;
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2524
|
+
flex-direction: column;
|
|
2525
|
+
align-items: center;
|
|
2526
|
+
justify-content: center;
|
|
2527
|
+
gap: 6px;
|
|
2528
|
+
padding: 6px;
|
|
2412
2529
|
}
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2530
|
+
|
|
2531
|
+
/* \u2500\u2500\u2500 Meta line below multi grid \u2500\u2500\u2500 */
|
|
2532
|
+
.fb-meta-line {
|
|
2533
|
+
margin-top: 10px;
|
|
2534
|
+
display: flex;
|
|
2535
|
+
align-items: center;
|
|
2536
|
+
justify-content: space-between;
|
|
2537
|
+
gap: 8px;
|
|
2538
|
+
flex-wrap: wrap;
|
|
2416
2539
|
}
|
|
2417
|
-
.fb-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
line-height: 1.2;
|
|
2540
|
+
.fb-meta-text {
|
|
2541
|
+
font-size: 12px;
|
|
2542
|
+
color: #94a3b8;
|
|
2543
|
+
display: flex;
|
|
2544
|
+
align-items: center;
|
|
2545
|
+
gap: 8px;
|
|
2546
|
+
flex-wrap: wrap;
|
|
2425
2547
|
}
|
|
2426
|
-
.fb-
|
|
2427
|
-
|
|
2548
|
+
.fb-meta-dot {
|
|
2549
|
+
width: 4px;
|
|
2550
|
+
height: 4px;
|
|
2551
|
+
border-radius: 50%;
|
|
2552
|
+
background: #cbd5e1;
|
|
2553
|
+
flex-shrink: 0;
|
|
2554
|
+
}
|
|
2555
|
+
.fb-meta-mono {
|
|
2556
|
+
font-family: ui-monospace, 'JetBrains Mono', monospace;
|
|
2557
|
+
font-size: 11px;
|
|
2558
|
+
letter-spacing: -0.02em;
|
|
2428
2559
|
}
|
|
2429
|
-
.fb-
|
|
2430
|
-
|
|
2560
|
+
.fb-clear-all-btn {
|
|
2561
|
+
font-size: 12px;
|
|
2562
|
+
color: #94a3b8;
|
|
2563
|
+
background: none;
|
|
2564
|
+
border: none;
|
|
2565
|
+
cursor: pointer;
|
|
2566
|
+
padding: 0;
|
|
2567
|
+
font-family: inherit;
|
|
2568
|
+
transition: color 150ms;
|
|
2569
|
+
white-space: nowrap;
|
|
2570
|
+
flex-shrink: 0;
|
|
2431
2571
|
}
|
|
2432
|
-
.fb-
|
|
2433
|
-
|
|
2572
|
+
.fb-clear-all-btn:hover {
|
|
2573
|
+
color: #dc2626;
|
|
2434
2574
|
}
|
|
2435
|
-
|
|
2436
|
-
|
|
2575
|
+
|
|
2576
|
+
/* \u2500\u2500\u2500 Empty text (readonly) \u2500\u2500\u2500 */
|
|
2577
|
+
.fb-tile-empty-text {
|
|
2578
|
+
font-size: 11px;
|
|
2579
|
+
color: var(--fb-text-secondary-color, #6b7280);
|
|
2580
|
+
padding: 4px 0;
|
|
2437
2581
|
}
|
|
2438
2582
|
|
|
2439
|
-
/* Tile action
|
|
2583
|
+
/* \u2500\u2500\u2500 Tile action buttons (for zoom popup, compat) \u2500\u2500\u2500 */
|
|
2440
2584
|
.fb-tile-actions {
|
|
2441
2585
|
position: absolute;
|
|
2442
2586
|
top: 3px;
|
|
@@ -2448,37 +2592,35 @@ function ensureFileStyles() {
|
|
|
2448
2592
|
transition: opacity var(--fb-transition-duration, 200ms);
|
|
2449
2593
|
z-index: 10;
|
|
2450
2594
|
}
|
|
2451
|
-
.fb-tile
|
|
2595
|
+
.fb-preview-tile:hover .fb-tile-actions {
|
|
2452
2596
|
opacity: 1;
|
|
2453
2597
|
}
|
|
2454
2598
|
.fb-tile-action-btn {
|
|
2455
|
-
width:
|
|
2456
|
-
height:
|
|
2599
|
+
width: 24px;
|
|
2600
|
+
height: 24px;
|
|
2457
2601
|
display: flex;
|
|
2458
2602
|
align-items: center;
|
|
2459
2603
|
justify-content: center;
|
|
2460
|
-
border:
|
|
2461
|
-
border-radius:
|
|
2604
|
+
border: 1px solid rgba(15,23,42,0.08);
|
|
2605
|
+
border-radius: 0.375rem;
|
|
2462
2606
|
cursor: pointer;
|
|
2463
|
-
background: rgba(
|
|
2464
|
-
color: #
|
|
2607
|
+
background: rgba(255,255,255,0.92);
|
|
2608
|
+
color: #374151;
|
|
2465
2609
|
padding: 0;
|
|
2466
2610
|
flex-shrink: 0;
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2611
|
+
box-shadow: 0 1px 2px rgba(0,0,0,0.06);
|
|
2612
|
+
transition: background var(--fb-transition-duration, 200ms),
|
|
2613
|
+
color var(--fb-transition-duration, 200ms);
|
|
2470
2614
|
}
|
|
2471
2615
|
.fb-tile-action-btn:hover {
|
|
2472
|
-
background:
|
|
2473
|
-
|
|
2474
|
-
.fb-tile-action-remove {
|
|
2475
|
-
background: rgba(220, 38, 38, 0.8);
|
|
2616
|
+
background: #ffffff;
|
|
2617
|
+
color: #0f172a;
|
|
2476
2618
|
}
|
|
2477
2619
|
.fb-tile-action-remove:hover {
|
|
2478
|
-
|
|
2620
|
+
color: #dc2626;
|
|
2479
2621
|
}
|
|
2480
2622
|
|
|
2481
|
-
/*
|
|
2623
|
+
/* Zoom popup action buttons always visible */
|
|
2482
2624
|
.fb-tile-zoom-preview .fb-tile-actions {
|
|
2483
2625
|
position: absolute;
|
|
2484
2626
|
top: 6px;
|
|
@@ -2487,116 +2629,145 @@ function ensureFileStyles() {
|
|
|
2487
2629
|
z-index: 10000;
|
|
2488
2630
|
}
|
|
2489
2631
|
|
|
2490
|
-
/*
|
|
2491
|
-
.fb-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2632
|
+
/* \u2500\u2500\u2500 Hover zoom preview popup \u2500\u2500\u2500 */
|
|
2633
|
+
.fb-tile-zoom-preview {
|
|
2634
|
+
position: fixed;
|
|
2635
|
+
z-index: 9999;
|
|
2636
|
+
background: var(--fb-background-color, #fff);
|
|
2637
|
+
border: 1px solid #e2e8f0;
|
|
2638
|
+
border-radius: 0.5rem;
|
|
2639
|
+
box-shadow: 0 4px 16px rgba(0,0,0,0.18);
|
|
2640
|
+
padding: 4px;
|
|
2641
|
+
width: 350px;
|
|
2642
|
+
height: 350px;
|
|
2643
|
+
pointer-events: none;
|
|
2644
|
+
opacity: 0;
|
|
2645
|
+
transition: opacity 150ms ease;
|
|
2495
2646
|
}
|
|
2496
|
-
.fb-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2647
|
+
.fb-tile-zoom-preview.fb-tile-zoom-preview--visible {
|
|
2648
|
+
opacity: 1;
|
|
2649
|
+
}
|
|
2650
|
+
.fb-tile-zoom-preview-img {
|
|
2651
|
+
width: 100%;
|
|
2652
|
+
height: 100%;
|
|
2653
|
+
object-fit: contain;
|
|
2654
|
+
display: block;
|
|
2655
|
+
border-radius: calc(0.5rem - 2px);
|
|
2500
2656
|
}
|
|
2501
2657
|
|
|
2502
|
-
/*
|
|
2503
|
-
.fb-
|
|
2504
|
-
height:
|
|
2505
|
-
border:
|
|
2506
|
-
border
|
|
2658
|
+
/* \u2500\u2500\u2500 Single-file uploading state \u2500\u2500\u2500 */
|
|
2659
|
+
.fb-single-uploading {
|
|
2660
|
+
height: 180px;
|
|
2661
|
+
border-radius: 0.75rem;
|
|
2662
|
+
border: 1px dashed #60a5fa;
|
|
2663
|
+
background: rgba(239,246,255,0.5);
|
|
2507
2664
|
display: flex;
|
|
2508
2665
|
flex-direction: column;
|
|
2509
2666
|
align-items: center;
|
|
2510
2667
|
justify-content: center;
|
|
2511
|
-
gap:
|
|
2512
|
-
cursor: pointer;
|
|
2513
|
-
background: none;
|
|
2514
|
-
padding: 0;
|
|
2515
|
-
transition:
|
|
2516
|
-
border-color var(--fb-transition-duration, 200ms),
|
|
2517
|
-
background var(--fb-transition-duration, 200ms);
|
|
2518
|
-
width: 100%;
|
|
2668
|
+
gap: 8px;
|
|
2519
2669
|
}
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
|
|
2670
|
+
|
|
2671
|
+
/* \u2500\u2500\u2500 Video overlays \u2500\u2500\u2500 */
|
|
2672
|
+
.fb-video-overlay {
|
|
2673
|
+
position: absolute;
|
|
2674
|
+
inset: 0;
|
|
2675
|
+
display: flex;
|
|
2676
|
+
align-items: center;
|
|
2677
|
+
justify-content: center;
|
|
2678
|
+
background: rgba(0,0,0,0.25);
|
|
2525
2679
|
}
|
|
2526
|
-
.fb-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2680
|
+
.fb-play-btn {
|
|
2681
|
+
background: rgba(255,255,255,0.9);
|
|
2682
|
+
border-radius: 50%;
|
|
2683
|
+
display: flex;
|
|
2684
|
+
align-items: center;
|
|
2685
|
+
justify-content: center;
|
|
2530
2686
|
}
|
|
2531
|
-
.fb-
|
|
2532
|
-
|
|
2533
|
-
|
|
2687
|
+
.fb-video-preview-wrap {
|
|
2688
|
+
position: relative;
|
|
2689
|
+
width: 100%;
|
|
2690
|
+
height: 100%;
|
|
2534
2691
|
}
|
|
2535
|
-
.fb-
|
|
2692
|
+
.fb-video-btn-overlay {
|
|
2693
|
+
position: absolute;
|
|
2694
|
+
top: 8px;
|
|
2695
|
+
right: 8px;
|
|
2696
|
+
z-index: 10;
|
|
2697
|
+
display: flex;
|
|
2698
|
+
gap: 4px;
|
|
2699
|
+
opacity: 0;
|
|
2700
|
+
transition: opacity 150ms;
|
|
2701
|
+
pointer-events: none;
|
|
2702
|
+
}
|
|
2703
|
+
.fb-video-preview-wrap:hover .fb-video-btn-overlay {
|
|
2704
|
+
opacity: 1;
|
|
2705
|
+
pointer-events: auto;
|
|
2706
|
+
}
|
|
2707
|
+
.fb-video-btn {
|
|
2708
|
+
border: none;
|
|
2709
|
+
border-radius: 4px;
|
|
2536
2710
|
font-size: 11px;
|
|
2537
|
-
|
|
2711
|
+
padding: 4px 8px;
|
|
2712
|
+
cursor: pointer;
|
|
2713
|
+
color: #fff;
|
|
2714
|
+
line-height: 1.2;
|
|
2538
2715
|
}
|
|
2716
|
+
.fb-video-btn-delete { background: rgba(220,38,38,0.85); }
|
|
2717
|
+
.fb-video-btn-delete:hover { background: rgba(185,28,28,0.95); }
|
|
2718
|
+
.fb-video-btn-change { background: rgba(31,41,55,0.85); }
|
|
2719
|
+
.fb-video-btn-change:hover { background: rgba(17,24,39,0.95); }
|
|
2539
2720
|
|
|
2540
|
-
/*
|
|
2541
|
-
.fb-tile
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
justify-content: center;
|
|
2546
|
-
cursor: pointer;
|
|
2547
|
-
font-size: 24px;
|
|
2548
|
-
color: var(--fb-file-upload-text-color, #9ca3af);
|
|
2549
|
-
transition:
|
|
2550
|
-
border-color var(--fb-transition-duration, 200ms),
|
|
2551
|
-
color var(--fb-transition-duration, 200ms);
|
|
2552
|
-
background: none;
|
|
2553
|
-
padding: 0;
|
|
2554
|
-
width: var(--fb-tile-size, 160px);
|
|
2555
|
-
height: var(--fb-tile-size, 160px);
|
|
2556
|
-
flex-shrink: 0;
|
|
2557
|
-
position: relative;
|
|
2721
|
+
/* \u2500\u2500\u2500 Readonly readonly tile \u2500\u2500\u2500 */
|
|
2722
|
+
.fb-readonly-tile {
|
|
2723
|
+
aspect-ratio: 1 / 1;
|
|
2724
|
+
border-radius: 0.5rem;
|
|
2725
|
+
border: 1px solid #e2e8f0;
|
|
2558
2726
|
overflow: hidden;
|
|
2559
|
-
|
|
2727
|
+
position: relative;
|
|
2728
|
+
cursor: pointer;
|
|
2560
2729
|
}
|
|
2561
|
-
.fb-tile
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2730
|
+
.fb-readonly-tile img {
|
|
2731
|
+
width: 100%;
|
|
2732
|
+
height: 100%;
|
|
2733
|
+
object-fit: contain;
|
|
2734
|
+
display: block;
|
|
2566
2735
|
}
|
|
2567
|
-
|
|
2568
|
-
/* Hover zoom preview popup for image tiles \u2014 appended to document.body (fixed) */
|
|
2569
|
-
.fb-tile-zoom-preview {
|
|
2570
|
-
position: fixed;
|
|
2571
|
-
z-index: 9999;
|
|
2572
|
-
background: var(--fb-background-color, #fff);
|
|
2573
|
-
border: 1px solid var(--fb-file-upload-border-color, #d1d5db);
|
|
2574
|
-
border-radius: var(--fb-border-radius, 0.5rem);
|
|
2575
|
-
box-shadow: 0 4px 16px rgba(0,0,0,0.18);
|
|
2576
|
-
padding: 4px;
|
|
2577
|
-
width: 350px;
|
|
2578
|
-
height: 350px;
|
|
2579
|
-
pointer-events: none;
|
|
2736
|
+
.fb-readonly-tile .fb-tile-actions {
|
|
2580
2737
|
opacity: 0;
|
|
2581
|
-
transition: opacity 150ms ease;
|
|
2582
2738
|
}
|
|
2583
|
-
.fb-tile
|
|
2739
|
+
.fb-readonly-tile:hover .fb-tile-actions {
|
|
2584
2740
|
opacity: 1;
|
|
2585
2741
|
}
|
|
2586
|
-
|
|
2742
|
+
|
|
2743
|
+
/* \u2500\u2500\u2500 Readonly single-file filled \u2500\u2500\u2500 */
|
|
2744
|
+
.fb-single-readonly-filled {
|
|
2745
|
+
position: relative;
|
|
2746
|
+
border-radius: 0.75rem;
|
|
2747
|
+
border: 1px solid #e2e8f0;
|
|
2748
|
+
overflow: hidden;
|
|
2749
|
+
height: 220px;
|
|
2750
|
+
display: block;
|
|
2751
|
+
cursor: pointer;
|
|
2752
|
+
}
|
|
2753
|
+
.fb-single-readonly-filled img {
|
|
2587
2754
|
width: 100%;
|
|
2588
2755
|
height: 100%;
|
|
2589
2756
|
object-fit: contain;
|
|
2590
2757
|
display: block;
|
|
2591
|
-
|
|
2592
|
-
|
|
2758
|
+
}
|
|
2759
|
+
|
|
2760
|
+
/* \u2500\u2500\u2500 Readonly multi grid \u2500\u2500\u2500 */
|
|
2761
|
+
.fb-multi-readonly-grid {
|
|
2762
|
+
display: grid;
|
|
2763
|
+
grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
|
|
2764
|
+
gap: 10px;
|
|
2593
2765
|
}
|
|
2594
2766
|
`;
|
|
2595
2767
|
document.head.appendChild(style);
|
|
2596
2768
|
}
|
|
2597
2769
|
|
|
2598
2770
|
// src/components/file/dom.ts
|
|
2599
|
-
var TILE_SIZE = "160px";
|
|
2600
2771
|
function createFileTile() {
|
|
2601
2772
|
ensureFileStyles();
|
|
2602
2773
|
const tile = document.createElement("div");
|
|
@@ -2605,7 +2776,7 @@ function createFileTile() {
|
|
|
2605
2776
|
}
|
|
2606
2777
|
function showFileError(container, message) {
|
|
2607
2778
|
var _a, _b;
|
|
2608
|
-
const existing = (_a = container.closest("
|
|
2779
|
+
const existing = (_a = container.closest("[data-files-wrapper]")) == null ? void 0 : _a.querySelector(".file-error-message");
|
|
2609
2780
|
if (existing) existing.remove();
|
|
2610
2781
|
const errorEl = document.createElement("div");
|
|
2611
2782
|
errorEl.className = "file-error-message error-message";
|
|
@@ -2615,11 +2786,11 @@ function showFileError(container, message) {
|
|
|
2615
2786
|
margin-top: 0.25rem;
|
|
2616
2787
|
`;
|
|
2617
2788
|
errorEl.textContent = message;
|
|
2618
|
-
(_b = container.closest("
|
|
2789
|
+
(_b = container.closest("[data-files-wrapper]")) == null ? void 0 : _b.appendChild(errorEl);
|
|
2619
2790
|
}
|
|
2620
2791
|
function clearFileError(container) {
|
|
2621
2792
|
var _a;
|
|
2622
|
-
const existing = (_a = container.closest("
|
|
2793
|
+
const existing = (_a = container.closest("[data-files-wrapper]")) == null ? void 0 : _a.querySelector(".file-error-message");
|
|
2623
2794
|
if (existing) existing.remove();
|
|
2624
2795
|
}
|
|
2625
2796
|
function addDeleteButton(container, state, onDelete) {
|
|
@@ -2637,14 +2808,6 @@ function addDeleteButton(container, state, onDelete) {
|
|
|
2637
2808
|
overlay.appendChild(deleteBtn);
|
|
2638
2809
|
container.appendChild(overlay);
|
|
2639
2810
|
}
|
|
2640
|
-
function findFilePicker(container) {
|
|
2641
|
-
var _a;
|
|
2642
|
-
let el = container.parentElement;
|
|
2643
|
-
while (el && !el.dataset.filesWrapper) {
|
|
2644
|
-
el = el.parentElement;
|
|
2645
|
-
}
|
|
2646
|
-
return (_a = el == null ? void 0 : el.querySelector('input[type="file"]')) != null ? _a : null;
|
|
2647
|
-
}
|
|
2648
2811
|
function createUploadingTile(fileName, state) {
|
|
2649
2812
|
ensureFileStyles();
|
|
2650
2813
|
const tile = createFileTile();
|
|
@@ -2660,10 +2823,14 @@ function createUploadingTile(fileName, state) {
|
|
|
2660
2823
|
return tile;
|
|
2661
2824
|
}
|
|
2662
2825
|
function ensureTilesWrap(list) {
|
|
2826
|
+
var _a, _b, _c;
|
|
2827
|
+
const existingGrid = list.querySelector(".fb-multi-grid");
|
|
2828
|
+
if (existingGrid) return existingGrid;
|
|
2663
2829
|
const existing = list.querySelector(".fb-tiles-wrap");
|
|
2664
2830
|
if (existing) return existing;
|
|
2665
|
-
|
|
2666
|
-
|
|
2831
|
+
(_a = list.querySelector(".fb-file-dropzone")) == null ? void 0 : _a.remove();
|
|
2832
|
+
(_b = list.querySelector(".fb-wide-tile")) == null ? void 0 : _b.remove();
|
|
2833
|
+
(_c = list.querySelector(".fb-multi-outer")) == null ? void 0 : _c.remove();
|
|
2667
2834
|
const tilesWrap = document.createElement("div");
|
|
2668
2835
|
tilesWrap.className = "fb-tiles-wrap";
|
|
2669
2836
|
tilesWrap.style.cssText = "display:flex;flex-wrap:wrap;gap:6px;align-items:flex-start;";
|
|
@@ -2675,7 +2842,7 @@ function ensureTilesWrap(list) {
|
|
|
2675
2842
|
return tilesWrap;
|
|
2676
2843
|
}
|
|
2677
2844
|
function setEmptyFileContainer(fileContainer, state, hint) {
|
|
2678
|
-
const hintHtml =
|
|
2845
|
+
const hintHtml = "";
|
|
2679
2846
|
fileContainer.innerHTML = `
|
|
2680
2847
|
<div class="flex flex-col items-center justify-center h-full text-gray-400">
|
|
2681
2848
|
<svg class="w-6 h-6 mb-2" fill="currentColor" viewBox="0 0 24 24">
|
|
@@ -2718,9 +2885,11 @@ function setupDragAndDrop(element, dropHandler) {
|
|
|
2718
2885
|
}
|
|
2719
2886
|
|
|
2720
2887
|
// src/components/file/preview.ts
|
|
2721
|
-
var ICON_DOWNLOAD = `<svg width="
|
|
2722
|
-
var ICON_OPEN = `<svg width="
|
|
2723
|
-
var ICON_REMOVE = `<svg width="
|
|
2888
|
+
var ICON_DOWNLOAD = `<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>`;
|
|
2889
|
+
var ICON_OPEN = `<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></svg>`;
|
|
2890
|
+
var ICON_REMOVE = `<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="3 6 5 6 21 6"/><path d="M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6"/><path d="M10 11v6"/><path d="M14 11v6"/><path d="M9 6V4a2 2 0 0 1 2-2h2a2 2 0 0 1 2 2v2"/></svg>`;
|
|
2891
|
+
var ICON_REPLACE = `<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M17 18a4 4 0 000-8 6 6 0 00-11.5 2A4 4 0 006 20h11M12 12v7M12 12l-3 3M12 12l3 3"/></svg>`;
|
|
2892
|
+
var ICON_LIBRARY = `<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 4h4v16H4z"/><path d="M10 4h4v16h-4z"/><path d="M16 5l3.5 1-3 14L13 19"/></svg>`;
|
|
2724
2893
|
function canDownload(state, meta) {
|
|
2725
2894
|
return Boolean(
|
|
2726
2895
|
state.config.downloadFile || state.config.getDownloadUrl || state.config.getThumbnail || (meta == null ? void 0 : meta.file)
|
|
@@ -2732,7 +2901,16 @@ function canOpenInTab(state, meta) {
|
|
|
2732
2901
|
);
|
|
2733
2902
|
}
|
|
2734
2903
|
function createTileActions(options) {
|
|
2735
|
-
const {
|
|
2904
|
+
const {
|
|
2905
|
+
canRemove,
|
|
2906
|
+
removeHandler,
|
|
2907
|
+
state,
|
|
2908
|
+
resourceId,
|
|
2909
|
+
fileName,
|
|
2910
|
+
meta,
|
|
2911
|
+
replaceHandler,
|
|
2912
|
+
libraryHandler
|
|
2913
|
+
} = options;
|
|
2736
2914
|
const group = document.createElement("div");
|
|
2737
2915
|
group.className = "fb-tile-actions";
|
|
2738
2916
|
const makeBtn = (icon, label, cls) => {
|
|
@@ -2747,15 +2925,41 @@ function createTileActions(options) {
|
|
|
2747
2925
|
});
|
|
2748
2926
|
return btn;
|
|
2749
2927
|
};
|
|
2928
|
+
if (replaceHandler) {
|
|
2929
|
+
const replaceBtn = makeBtn(
|
|
2930
|
+
ICON_REPLACE,
|
|
2931
|
+
t("replaceFile", state),
|
|
2932
|
+
"fb-tile-action-replace"
|
|
2933
|
+
);
|
|
2934
|
+
replaceBtn.addEventListener("click", () => replaceHandler());
|
|
2935
|
+
group.appendChild(replaceBtn);
|
|
2936
|
+
}
|
|
2937
|
+
if (libraryHandler) {
|
|
2938
|
+
const libBtn = makeBtn(
|
|
2939
|
+
ICON_LIBRARY,
|
|
2940
|
+
t("fromLibrary", state),
|
|
2941
|
+
"fb-tile-action-library"
|
|
2942
|
+
);
|
|
2943
|
+
libBtn.addEventListener("click", () => libraryHandler());
|
|
2944
|
+
group.appendChild(libBtn);
|
|
2945
|
+
}
|
|
2750
2946
|
if (canDownload(state, meta)) {
|
|
2751
|
-
const dlBtn = makeBtn(
|
|
2947
|
+
const dlBtn = makeBtn(
|
|
2948
|
+
ICON_DOWNLOAD,
|
|
2949
|
+
t("downloadFile", state),
|
|
2950
|
+
"fb-tile-action-download"
|
|
2951
|
+
);
|
|
2752
2952
|
dlBtn.addEventListener("click", () => {
|
|
2753
2953
|
triggerTileDownload(resourceId, fileName, state, meta);
|
|
2754
2954
|
});
|
|
2755
2955
|
group.appendChild(dlBtn);
|
|
2756
2956
|
}
|
|
2757
2957
|
if (canOpenInTab(state, meta)) {
|
|
2758
|
-
const openBtn = makeBtn(
|
|
2958
|
+
const openBtn = makeBtn(
|
|
2959
|
+
ICON_OPEN,
|
|
2960
|
+
t("openInNewTab", state),
|
|
2961
|
+
"fb-tile-action-open"
|
|
2962
|
+
);
|
|
2759
2963
|
openBtn.addEventListener("click", () => {
|
|
2760
2964
|
triggerTileOpen(resourceId, state, meta).catch((err) => {
|
|
2761
2965
|
console.error("Open failed:", err);
|
|
@@ -2764,7 +2968,11 @@ function createTileActions(options) {
|
|
|
2764
2968
|
group.appendChild(openBtn);
|
|
2765
2969
|
}
|
|
2766
2970
|
if (canRemove && removeHandler) {
|
|
2767
|
-
const rmBtn = makeBtn(
|
|
2971
|
+
const rmBtn = makeBtn(
|
|
2972
|
+
ICON_REMOVE,
|
|
2973
|
+
t("removeElement", state),
|
|
2974
|
+
"fb-tile-action-remove"
|
|
2975
|
+
);
|
|
2768
2976
|
rmBtn.addEventListener("click", () => {
|
|
2769
2977
|
removeHandler();
|
|
2770
2978
|
});
|
|
@@ -2842,11 +3050,17 @@ function positionZoomPopup(popup, tile) {
|
|
|
2842
3050
|
} else if (tileRect.bottom + margin + popupSize + padding <= window.innerHeight) {
|
|
2843
3051
|
top = tileRect.bottom + margin;
|
|
2844
3052
|
} else {
|
|
2845
|
-
top = Math.max(
|
|
3053
|
+
top = Math.max(
|
|
3054
|
+
padding,
|
|
3055
|
+
Math.min(window.innerHeight - popupSize - padding, tileRect.top)
|
|
3056
|
+
);
|
|
2846
3057
|
}
|
|
2847
3058
|
const tileCenterX = tileRect.left + tileRect.width / 2;
|
|
2848
3059
|
let left = tileCenterX - popupSize / 2;
|
|
2849
|
-
left = Math.max(
|
|
3060
|
+
left = Math.max(
|
|
3061
|
+
padding,
|
|
3062
|
+
Math.min(window.innerWidth - popupSize - padding, left)
|
|
3063
|
+
);
|
|
2850
3064
|
popup.style.top = `${top}px`;
|
|
2851
3065
|
popup.style.left = `${left}px`;
|
|
2852
3066
|
}
|
|
@@ -2890,7 +3104,9 @@ function attachZoomHover(tile, src, alt, actionsEl) {
|
|
|
2890
3104
|
const popup = getOrCreateZoomPopup();
|
|
2891
3105
|
const existingActions = popup.querySelector(".fb-tile-actions");
|
|
2892
3106
|
if (existingActions) existingActions.remove();
|
|
2893
|
-
const img = popup.querySelector(
|
|
3107
|
+
const img = popup.querySelector(
|
|
3108
|
+
".fb-tile-zoom-preview-img"
|
|
3109
|
+
);
|
|
2894
3110
|
img.src = src;
|
|
2895
3111
|
img.alt = alt;
|
|
2896
3112
|
if (actionsEl) {
|
|
@@ -2918,7 +3134,9 @@ function attachZoomHover(tile, src, alt, actionsEl) {
|
|
|
2918
3134
|
});
|
|
2919
3135
|
}
|
|
2920
3136
|
function attachClonedActionListeners(cloned, original) {
|
|
2921
|
-
const originalBtns = Array.from(
|
|
3137
|
+
const originalBtns = Array.from(
|
|
3138
|
+
original.querySelectorAll(".fb-tile-action-btn")
|
|
3139
|
+
);
|
|
2922
3140
|
const clonedBtns = Array.from(cloned.querySelectorAll(".fb-tile-action-btn"));
|
|
2923
3141
|
clonedBtns.forEach((clonedBtn, i) => {
|
|
2924
3142
|
const origBtn = originalBtns[i];
|
|
@@ -2932,8 +3150,7 @@ function attachClonedActionListeners(cloned, original) {
|
|
|
2932
3150
|
}
|
|
2933
3151
|
function renderLocalImagePreview(container, file, fileName, state) {
|
|
2934
3152
|
const img = document.createElement("img");
|
|
2935
|
-
img.
|
|
2936
|
-
img.style.background = "var(--fb-file-upload-bg-color,#f3f4f6)";
|
|
3153
|
+
img.style.cssText = "width:100%;height:100%;object-fit:contain;background:var(--fb-file-upload-bg-color,#f3f4f6);";
|
|
2937
3154
|
img.alt = fileName || t("previewAlt", state);
|
|
2938
3155
|
const reader = new FileReader();
|
|
2939
3156
|
reader.onload = (e) => {
|
|
@@ -2956,7 +3173,7 @@ function renderLocalVideoPreview(container, file, videoType, resourceId, state,
|
|
|
2956
3173
|
const newContainer = setupDragDropless(container);
|
|
2957
3174
|
newContainer.innerHTML = `
|
|
2958
3175
|
<div class="fb-video-preview-wrap">
|
|
2959
|
-
<video
|
|
3176
|
+
<video style="width:100%;height:100%;object-fit:contain;" controls preload="auto" muted src="${videoUrl}">
|
|
2960
3177
|
${escapeHtml(t("videoNotSupported", state))}
|
|
2961
3178
|
</video>
|
|
2962
3179
|
<div class="fb-video-btn-overlay">
|
|
@@ -2973,7 +3190,9 @@ function renderLocalVideoPreview(container, file, videoType, resourceId, state,
|
|
|
2973
3190
|
return newContainer;
|
|
2974
3191
|
}
|
|
2975
3192
|
function attachVideoButtonHandlers(container, resourceId, state, deps) {
|
|
2976
|
-
const changeBtn = container.querySelector(
|
|
3193
|
+
const changeBtn = container.querySelector(
|
|
3194
|
+
".change-file-btn"
|
|
3195
|
+
);
|
|
2977
3196
|
if (changeBtn) {
|
|
2978
3197
|
changeBtn.onclick = (e) => {
|
|
2979
3198
|
var _a;
|
|
@@ -2981,7 +3200,9 @@ function attachVideoButtonHandlers(container, resourceId, state, deps) {
|
|
|
2981
3200
|
(_a = deps == null ? void 0 : deps.picker) == null ? void 0 : _a.click();
|
|
2982
3201
|
};
|
|
2983
3202
|
}
|
|
2984
|
-
const deleteBtn = container.querySelector(
|
|
3203
|
+
const deleteBtn = container.querySelector(
|
|
3204
|
+
".delete-file-btn"
|
|
3205
|
+
);
|
|
2985
3206
|
if (deleteBtn) {
|
|
2986
3207
|
deleteBtn.onclick = (e) => {
|
|
2987
3208
|
e.stopPropagation();
|
|
@@ -3002,11 +3223,11 @@ function handleVideoDelete(container, resourceId, state, deps) {
|
|
|
3002
3223
|
container.onclick = deps.fileUploadHandler;
|
|
3003
3224
|
}
|
|
3004
3225
|
container.innerHTML = `
|
|
3005
|
-
<div
|
|
3006
|
-
<svg
|
|
3226
|
+
<div style="display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;color:var(--fb-text-secondary-color,#9ca3af);">
|
|
3227
|
+
<svg style="width:1.5rem;height:1.5rem;margin-bottom:0.5rem;" fill="currentColor" viewBox="0 0 24 24">
|
|
3007
3228
|
<path d="M21 19V5c0-1.1-.9-2-2-2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2zM8.5 13.5l2.5 3.01L14.5 12l4.5 6H5l3.5-4.5z"/>
|
|
3008
3229
|
</svg>
|
|
3009
|
-
<div
|
|
3230
|
+
<div style="font-size:0.875rem;text-align:center;">${escapeHtml(t("clickDragText", state))}</div>
|
|
3010
3231
|
</div>
|
|
3011
3232
|
`;
|
|
3012
3233
|
if (deps == null ? void 0 : deps.setupDrop) {
|
|
@@ -3024,11 +3245,11 @@ function renderDeleteButton(container, resourceId, state) {
|
|
|
3024
3245
|
hiddenInput.value = "";
|
|
3025
3246
|
}
|
|
3026
3247
|
container.innerHTML = `
|
|
3027
|
-
<div
|
|
3028
|
-
<svg
|
|
3248
|
+
<div style="display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;color:var(--fb-text-secondary-color,#9ca3af);">
|
|
3249
|
+
<svg style="width:1.5rem;height:1.5rem;margin-bottom:0.5rem;" fill="currentColor" viewBox="0 0 24 24">
|
|
3029
3250
|
<path d="M21 19V5c0-1.1-.9-2-2-2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2zM8.5 13.5l2.5 3.01L14.5 12l4.5 6H5l3.5-4.5z"/>
|
|
3030
3251
|
</svg>
|
|
3031
|
-
<div
|
|
3252
|
+
<div style="font-size:0.875rem;text-align:center;">${escapeHtml(t("clickDragText", state))}</div>
|
|
3032
3253
|
</div>
|
|
3033
3254
|
`;
|
|
3034
3255
|
});
|
|
@@ -3048,7 +3269,7 @@ async function renderLocalFilePreview(container, meta, fileName, resourceId, isR
|
|
|
3048
3269
|
deps
|
|
3049
3270
|
);
|
|
3050
3271
|
} else {
|
|
3051
|
-
container.innerHTML = `<div
|
|
3272
|
+
container.innerHTML = `<div style="display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;color:var(--fb-text-secondary-color,#9ca3af);"><div style="font-size:36px;margin-bottom:0.5rem;">\u{1F4C1}</div><div style="font-size:0.875rem;">${escapeHtml(fileName)}</div></div>`;
|
|
3052
3273
|
}
|
|
3053
3274
|
if (!isReadonly && !((_c = meta.type) == null ? void 0 : _c.startsWith("video/"))) {
|
|
3054
3275
|
renderDeleteButton(container, resourceId, state);
|
|
@@ -3056,7 +3277,7 @@ async function renderLocalFilePreview(container, meta, fileName, resourceId, isR
|
|
|
3056
3277
|
}
|
|
3057
3278
|
function renderUploadedVideoPreview(container, thumbnailUrl, state) {
|
|
3058
3279
|
const video = document.createElement("video");
|
|
3059
|
-
video.
|
|
3280
|
+
video.style.cssText = "width:100%;height:100%;object-fit:contain;";
|
|
3060
3281
|
video.controls = true;
|
|
3061
3282
|
video.preload = "metadata";
|
|
3062
3283
|
video.muted = true;
|
|
@@ -3078,8 +3299,7 @@ async function renderUploadedFilePreview(container, resourceId, fileName, meta,
|
|
|
3078
3299
|
renderUploadedVideoPreview(container, thumbnailUrl, state);
|
|
3079
3300
|
} else {
|
|
3080
3301
|
const img = document.createElement("img");
|
|
3081
|
-
img.
|
|
3082
|
-
img.style.background = "var(--fb-file-upload-bg-color,#f3f4f6)";
|
|
3302
|
+
img.style.cssText = "width:100%;height:100%;object-fit:contain;background:var(--fb-file-upload-bg-color,#f3f4f6);";
|
|
3083
3303
|
img.alt = fileName || t("previewAlt", state);
|
|
3084
3304
|
img.src = thumbnailUrl;
|
|
3085
3305
|
container.appendChild(img);
|
|
@@ -3090,11 +3310,11 @@ async function renderUploadedFilePreview(container, resourceId, fileName, meta,
|
|
|
3090
3310
|
} catch (error) {
|
|
3091
3311
|
console.error("Failed to get thumbnail:", error);
|
|
3092
3312
|
container.innerHTML = `
|
|
3093
|
-
<div
|
|
3094
|
-
<svg
|
|
3313
|
+
<div style="display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;color:var(--fb-text-secondary-color,#9ca3af);">
|
|
3314
|
+
<svg style="width:1.5rem;height:1.5rem;margin-bottom:0.5rem;" fill="currentColor" viewBox="0 0 24 24">
|
|
3095
3315
|
<path d="M21 19V5c0-1.1-.9-2-2-2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2zM8.5 13.5l2.5 3.01L14.5 12l4.5 6H5l3.5-4.5z"/>
|
|
3096
3316
|
</svg>
|
|
3097
|
-
<div
|
|
3317
|
+
<div style="font-size:0.875rem;text-align:center;">${escapeHtml(fileName || t("previewUnavailable", state))}</div>
|
|
3098
3318
|
</div>
|
|
3099
3319
|
`;
|
|
3100
3320
|
}
|
|
@@ -3120,7 +3340,13 @@ async function renderFilePreview(container, resourceId, state, options = {}) {
|
|
|
3120
3340
|
deps
|
|
3121
3341
|
);
|
|
3122
3342
|
} else {
|
|
3123
|
-
await renderUploadedFilePreview(
|
|
3343
|
+
await renderUploadedFilePreview(
|
|
3344
|
+
container,
|
|
3345
|
+
resourceId,
|
|
3346
|
+
fileName,
|
|
3347
|
+
meta,
|
|
3348
|
+
state
|
|
3349
|
+
);
|
|
3124
3350
|
const isVideo = (_a = meta == null ? void 0 : meta.type) == null ? void 0 : _a.startsWith("video/");
|
|
3125
3351
|
if (!isReadonly && !isVideo) {
|
|
3126
3352
|
renderDeleteButton(container, resourceId, state);
|
|
@@ -3149,7 +3375,8 @@ async function renderFilePreviewReadonly(resourceId, state, fileName, options =
|
|
|
3149
3375
|
}
|
|
3150
3376
|
const localFileUrl = (meta == null ? void 0 : meta.file) instanceof File ? getLocalFileUrl(meta.file) : null;
|
|
3151
3377
|
const resolveOpenUrl = async () => {
|
|
3152
|
-
if (state.config.getDownloadUrl)
|
|
3378
|
+
if (state.config.getDownloadUrl)
|
|
3379
|
+
return state.config.getDownloadUrl(resourceId);
|
|
3153
3380
|
if (state.config.getThumbnail) return state.config.getThumbnail(resourceId);
|
|
3154
3381
|
return localFileUrl;
|
|
3155
3382
|
};
|
|
@@ -3267,20 +3494,15 @@ async function renderSingleFileEditTile(fileContainer, resourceId, state, deps)
|
|
|
3267
3494
|
fileContainer.appendChild(tile);
|
|
3268
3495
|
}
|
|
3269
3496
|
async function fillTileContent(tile, rid, meta, state, actionsEl) {
|
|
3270
|
-
var _a, _b
|
|
3497
|
+
var _a, _b;
|
|
3271
3498
|
if ((_a = meta == null ? void 0 : meta.type) == null ? void 0 : _a.startsWith("image/")) {
|
|
3272
3499
|
if (meta.file && meta.file instanceof File) {
|
|
3273
3500
|
const img = document.createElement("img");
|
|
3274
3501
|
img.style.cssText = "width:100%;height:100%;object-fit:contain;background:var(--fb-file-upload-bg-color,#f3f4f6);";
|
|
3275
3502
|
img.alt = meta.name;
|
|
3276
|
-
|
|
3277
|
-
reader.onload = (e) => {
|
|
3278
|
-
var _a2;
|
|
3279
|
-
img.src = ((_a2 = e.target) == null ? void 0 : _a2.result) || "";
|
|
3280
|
-
attachZoomHover(tile, img.src, meta.name, actionsEl != null ? actionsEl : null);
|
|
3281
|
-
};
|
|
3282
|
-
reader.readAsDataURL(meta.file);
|
|
3503
|
+
img.src = getLocalFileUrl(meta.file);
|
|
3283
3504
|
tile.appendChild(img);
|
|
3505
|
+
attachZoomHover(tile, img.src, meta.name, actionsEl != null ? actionsEl : null);
|
|
3284
3506
|
} else if (state.config.getThumbnail) {
|
|
3285
3507
|
try {
|
|
3286
3508
|
const url = await state.config.getThumbnail(rid);
|
|
@@ -3296,7 +3518,8 @@ async function fillTileContent(tile, rid, meta, state, actionsEl) {
|
|
|
3296
3518
|
}
|
|
3297
3519
|
} catch (error) {
|
|
3298
3520
|
const err = error instanceof Error ? error : new Error(String(error));
|
|
3299
|
-
if (state.config.onThumbnailError)
|
|
3521
|
+
if (state.config.onThumbnailError)
|
|
3522
|
+
state.config.onThumbnailError(err, rid);
|
|
3300
3523
|
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>`;
|
|
3301
3524
|
}
|
|
3302
3525
|
} else {
|
|
@@ -3329,7 +3552,8 @@ async function fillTileContent(tile, rid, meta, state, actionsEl) {
|
|
|
3329
3552
|
}
|
|
3330
3553
|
} catch (error) {
|
|
3331
3554
|
const err = error instanceof Error ? error : new Error(String(error));
|
|
3332
|
-
if (state.config.onThumbnailError)
|
|
3555
|
+
if (state.config.onThumbnailError)
|
|
3556
|
+
state.config.onThumbnailError(err, rid);
|
|
3333
3557
|
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>`;
|
|
3334
3558
|
}
|
|
3335
3559
|
} else {
|
|
@@ -3337,17 +3561,21 @@ async function fillTileContent(tile, rid, meta, state, actionsEl) {
|
|
|
3337
3561
|
}
|
|
3338
3562
|
if (actionsEl) tile.appendChild(actionsEl);
|
|
3339
3563
|
} else {
|
|
3340
|
-
|
|
3341
|
-
const hasExtension = name.includes(".");
|
|
3342
|
-
const captionHtml = hasExtension ? `<div class="fb-tile-label">${escapeHtml(name.length > 10 ? name.substring(0, 8) + "\u2026" : name)}</div>` : "";
|
|
3343
|
-
tile.innerHTML = `
|
|
3344
|
-
<div style="display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;padding:6px;gap:4px;">
|
|
3345
|
-
<div style="font-size:36px;">\u{1F4C1}</div>
|
|
3346
|
-
${captionHtml}
|
|
3347
|
-
</div>`;
|
|
3348
|
-
if (actionsEl) tile.appendChild(actionsEl);
|
|
3564
|
+
fillDocumentFallback(tile, rid, meta, actionsEl);
|
|
3349
3565
|
}
|
|
3350
3566
|
}
|
|
3567
|
+
function fillDocumentFallback(tile, rid, meta, actionsEl) {
|
|
3568
|
+
var _a, _b;
|
|
3569
|
+
const fileName = (_b = (_a = meta == null ? void 0 : meta.name) != null ? _a : rid.split("/").pop()) != null ? _b : "";
|
|
3570
|
+
if (fileName) tile.title = fileName;
|
|
3571
|
+
const labelHtml = fileName ? `<div style="font-size:11px;line-height:1.2;text-align:center;color:var(--fb-text-secondary-color,#6b7280);max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;">${escapeHtml(fileName)}</div>` : "";
|
|
3572
|
+
tile.innerHTML = `
|
|
3573
|
+
<div style="display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;padding:6px;gap:4px;">
|
|
3574
|
+
<div style="font-size:36px;">\u{1F4C1}</div>
|
|
3575
|
+
${labelHtml}
|
|
3576
|
+
</div>`;
|
|
3577
|
+
if (actionsEl) tile.appendChild(actionsEl);
|
|
3578
|
+
}
|
|
3351
3579
|
async function forceDownload(resourceId, fileName, state) {
|
|
3352
3580
|
try {
|
|
3353
3581
|
let fileUrl = null;
|
|
@@ -3359,7 +3587,8 @@ async function forceDownload(resourceId, fileName, state) {
|
|
|
3359
3587
|
if (fileUrl) {
|
|
3360
3588
|
const finalUrl = fileUrl.startsWith("http") ? fileUrl : new URL(fileUrl, window.location.href).href;
|
|
3361
3589
|
const response = await fetch(finalUrl);
|
|
3362
|
-
if (!response.ok)
|
|
3590
|
+
if (!response.ok)
|
|
3591
|
+
throw new Error(`HTTP error! status: ${response.status}`);
|
|
3363
3592
|
const blob = await response.blob();
|
|
3364
3593
|
downloadBlob(blob, fileName);
|
|
3365
3594
|
} else {
|
|
@@ -3408,11 +3637,13 @@ async function uploadSingleFile(file, state) {
|
|
|
3408
3637
|
} catch (error) {
|
|
3409
3638
|
const err = error instanceof Error ? error : new Error(String(error));
|
|
3410
3639
|
if (state.config.onUploadError) state.config.onUploadError(err, file);
|
|
3411
|
-
|
|
3640
|
+
const wrapped = new Error(`File upload failed: ${err.message}`);
|
|
3641
|
+
wrapped.cause = err;
|
|
3642
|
+
throw wrapped;
|
|
3412
3643
|
}
|
|
3413
3644
|
}
|
|
3414
3645
|
async function handleFileSelect(opts) {
|
|
3415
|
-
var _a, _b;
|
|
3646
|
+
var _a, _b, _c;
|
|
3416
3647
|
const {
|
|
3417
3648
|
file,
|
|
3418
3649
|
container,
|
|
@@ -3448,6 +3679,10 @@ async function handleFileSelect(opts) {
|
|
|
3448
3679
|
return;
|
|
3449
3680
|
}
|
|
3450
3681
|
clearFileError(container);
|
|
3682
|
+
const existingHiddenInput = (_a = container.parentElement) == null ? void 0 : _a.querySelector(
|
|
3683
|
+
'input[type="hidden"]'
|
|
3684
|
+
);
|
|
3685
|
+
const previousRid = (existingHiddenInput == null ? void 0 : existingHiddenInput.value) || null;
|
|
3451
3686
|
ensureFileStyles();
|
|
3452
3687
|
container.innerHTML = `
|
|
3453
3688
|
<div style="display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;gap:6px;padding:6px;">
|
|
@@ -3458,7 +3693,13 @@ async function handleFileSelect(opts) {
|
|
|
3458
3693
|
try {
|
|
3459
3694
|
rid = await uploadSingleFile(file, state);
|
|
3460
3695
|
} catch (error) {
|
|
3461
|
-
|
|
3696
|
+
if (previousRid && (deps == null ? void 0 : deps.onAfterUpload)) {
|
|
3697
|
+
deps.onAfterUpload(container, previousRid);
|
|
3698
|
+
} else if (deps == null ? void 0 : deps.onRemove) {
|
|
3699
|
+
deps.onRemove();
|
|
3700
|
+
} else {
|
|
3701
|
+
setEmptyFileContainer(container, state);
|
|
3702
|
+
}
|
|
3462
3703
|
throw error;
|
|
3463
3704
|
}
|
|
3464
3705
|
state.resourceIndex.set(rid, {
|
|
@@ -3468,18 +3709,21 @@ async function handleFileSelect(opts) {
|
|
|
3468
3709
|
uploadedAt: /* @__PURE__ */ new Date(),
|
|
3469
3710
|
file
|
|
3470
3711
|
});
|
|
3471
|
-
|
|
3472
|
-
|
|
3473
|
-
|
|
3712
|
+
if (previousRid && previousRid !== rid) {
|
|
3713
|
+
releaseLocalFileUrl((_b = state.resourceIndex.get(previousRid)) == null ? void 0 : _b.file);
|
|
3714
|
+
}
|
|
3715
|
+
let hiddenInput = existingHiddenInput;
|
|
3474
3716
|
if (!hiddenInput) {
|
|
3475
3717
|
hiddenInput = document.createElement("input");
|
|
3476
3718
|
hiddenInput.type = "hidden";
|
|
3477
3719
|
hiddenInput.name = fieldName;
|
|
3478
|
-
(
|
|
3720
|
+
(_c = container.parentElement) == null ? void 0 : _c.appendChild(hiddenInput);
|
|
3479
3721
|
}
|
|
3480
3722
|
hiddenInput.value = rid;
|
|
3481
3723
|
const isVideo = file.type.startsWith("video/");
|
|
3482
|
-
if (!isVideo && deps) {
|
|
3724
|
+
if (!isVideo && (deps == null ? void 0 : deps.onAfterUpload)) {
|
|
3725
|
+
deps.onAfterUpload(container, rid);
|
|
3726
|
+
} else if (!isVideo && deps) {
|
|
3483
3727
|
renderSingleFileEditTile(container, rid, state, deps).catch(console.error);
|
|
3484
3728
|
} else {
|
|
3485
3729
|
renderFilePreview(container, rid, state, {
|
|
@@ -3508,7 +3752,9 @@ function filterAndSlice(allFiles, currentCount, constraints, state) {
|
|
|
3508
3752
|
const rejectedBySize = afterMime.filter(
|
|
3509
3753
|
(f) => !isFileSizeAllowed(f, constraints.maxSize)
|
|
3510
3754
|
);
|
|
3511
|
-
const valid = afterMime.filter(
|
|
3755
|
+
const valid = afterMime.filter(
|
|
3756
|
+
(f) => isFileSizeAllowed(f, constraints.maxSize)
|
|
3757
|
+
);
|
|
3512
3758
|
const remaining = constraints.maxCount === Infinity ? valid.length : Math.max(0, constraints.maxCount - currentCount);
|
|
3513
3759
|
const accepted = valid.slice(0, remaining);
|
|
3514
3760
|
const skippedByCount = valid.length - accepted.length;
|
|
@@ -3521,7 +3767,13 @@ function filterAndSlice(allFiles, currentCount, constraints, state) {
|
|
|
3521
3767
|
if (rejectedByMime.length > 0) {
|
|
3522
3768
|
const mimes = constraints.allowedMimes.join(", ");
|
|
3523
3769
|
const names = rejectedByMime.map((f) => f.name).join(", ");
|
|
3524
|
-
errorParts.push(
|
|
3770
|
+
errorParts.push(
|
|
3771
|
+
t("invalidFileMime", state, {
|
|
3772
|
+
name: names,
|
|
3773
|
+
type: rejectedByMime.map((f) => f.type).join(", "),
|
|
3774
|
+
mimes
|
|
3775
|
+
})
|
|
3776
|
+
);
|
|
3525
3777
|
}
|
|
3526
3778
|
if (rejectedBySize.length > 0) {
|
|
3527
3779
|
const names = rejectedBySize.map((f) => f.name).join(", ");
|
|
@@ -3540,17 +3792,19 @@ function filterAndSlice(allFiles, currentCount, constraints, state) {
|
|
|
3540
3792
|
return { accepted, errorMessage: errorParts.join(" \u2022 ") };
|
|
3541
3793
|
}
|
|
3542
3794
|
async function uploadBatch(accepted, resourceIds, listEl, state) {
|
|
3543
|
-
|
|
3795
|
+
var _a;
|
|
3796
|
+
if (listEl) {
|
|
3797
|
+
const tilesWrap = ensureTilesWrap(listEl);
|
|
3798
|
+
const addTile = (_a = tilesWrap.querySelector(".fb-multi-add-tile-js")) != null ? _a : tilesWrap.querySelector(".fb-tile-add");
|
|
3799
|
+
if (addTile) addTile.style.display = "none";
|
|
3800
|
+
}
|
|
3801
|
+
const failures = [];
|
|
3802
|
+
await Promise.allSettled(
|
|
3544
3803
|
accepted.map(async (file) => {
|
|
3545
3804
|
const placeholder = createUploadingTile(file.name, state);
|
|
3546
3805
|
if (listEl) {
|
|
3547
3806
|
const tilesWrap = ensureTilesWrap(listEl);
|
|
3548
|
-
|
|
3549
|
-
if (addTile) {
|
|
3550
|
-
tilesWrap.insertBefore(placeholder, addTile);
|
|
3551
|
-
} else {
|
|
3552
|
-
tilesWrap.appendChild(placeholder);
|
|
3553
|
-
}
|
|
3807
|
+
tilesWrap.appendChild(placeholder);
|
|
3554
3808
|
}
|
|
3555
3809
|
try {
|
|
3556
3810
|
const rid = await uploadSingleFile(file, state);
|
|
@@ -3562,11 +3816,27 @@ async function uploadBatch(accepted, resourceIds, listEl, state) {
|
|
|
3562
3816
|
file: void 0
|
|
3563
3817
|
});
|
|
3564
3818
|
resourceIds.push(rid);
|
|
3819
|
+
} catch (err) {
|
|
3820
|
+
const wrapped = err instanceof Error ? err : new Error(String(err));
|
|
3821
|
+
const cause = wrapped.cause;
|
|
3822
|
+
const root = cause instanceof Error ? cause : cause !== void 0 ? new Error(String(cause)) : wrapped;
|
|
3823
|
+
failures.push({ file, error: root });
|
|
3565
3824
|
} finally {
|
|
3566
3825
|
placeholder.remove();
|
|
3567
3826
|
}
|
|
3568
3827
|
})
|
|
3569
3828
|
);
|
|
3829
|
+
return { failures };
|
|
3830
|
+
}
|
|
3831
|
+
function buildBatchErrorMessage(filterError, failures, state) {
|
|
3832
|
+
if (failures.length === 0) return filterError;
|
|
3833
|
+
const uploadMsg = failures.map(
|
|
3834
|
+
(f) => t("uploadFailed", state, {
|
|
3835
|
+
name: f.file.name,
|
|
3836
|
+
error: f.error.message
|
|
3837
|
+
})
|
|
3838
|
+
).join(" \u2022 ");
|
|
3839
|
+
return filterError ? `${filterError} \u2022 ${uploadMsg}` : uploadMsg;
|
|
3570
3840
|
}
|
|
3571
3841
|
function setupFilesDropHandler(filesContainer, resourceIds, state, updateCallback, constraints, pathKey, instance) {
|
|
3572
3842
|
setupDragAndDrop(filesContainer, async (files) => {
|
|
@@ -3583,7 +3853,13 @@ function setupFilesDropHandler(filesContainer, resourceIds, state, updateCallbac
|
|
|
3583
3853
|
clearFileError(filesContainer);
|
|
3584
3854
|
}
|
|
3585
3855
|
const list = (_a = filesContainer.querySelector(".files-list")) != null ? _a : filesContainer;
|
|
3586
|
-
await uploadBatch(accepted, resourceIds, list, state);
|
|
3856
|
+
const { failures } = await uploadBatch(accepted, resourceIds, list, state);
|
|
3857
|
+
const combined = buildBatchErrorMessage(errorMessage, failures, state);
|
|
3858
|
+
if (combined) {
|
|
3859
|
+
showFileError(filesContainer, combined);
|
|
3860
|
+
} else {
|
|
3861
|
+
clearFileError(filesContainer);
|
|
3862
|
+
}
|
|
3587
3863
|
updateCallback();
|
|
3588
3864
|
if (instance && pathKey && !state.config.readonly) {
|
|
3589
3865
|
instance.triggerOnChange(pathKey, resourceIds);
|
|
@@ -3593,7 +3869,7 @@ function setupFilesDropHandler(filesContainer, resourceIds, state, updateCallbac
|
|
|
3593
3869
|
function setupFilesPickerHandler(filesPicker, resourceIds, state, updateCallback, constraints, pathKey, instance) {
|
|
3594
3870
|
filesPicker.onchange = async () => {
|
|
3595
3871
|
if (!filesPicker.files) return;
|
|
3596
|
-
const wrapperEl = filesPicker.closest("
|
|
3872
|
+
const wrapperEl = filesPicker.closest("[data-files-wrapper]") || filesPicker.parentElement;
|
|
3597
3873
|
const { accepted, errorMessage } = filterAndSlice(
|
|
3598
3874
|
Array.from(filesPicker.files),
|
|
3599
3875
|
resourceIds.length,
|
|
@@ -3606,7 +3882,20 @@ function setupFilesPickerHandler(filesPicker, resourceIds, state, updateCallback
|
|
|
3606
3882
|
clearFileError(wrapperEl);
|
|
3607
3883
|
}
|
|
3608
3884
|
const listEl = wrapperEl == null ? void 0 : wrapperEl.querySelector(".files-list");
|
|
3609
|
-
|
|
3885
|
+
const { failures } = await uploadBatch(
|
|
3886
|
+
accepted,
|
|
3887
|
+
resourceIds,
|
|
3888
|
+
listEl != null ? listEl : null,
|
|
3889
|
+
state
|
|
3890
|
+
);
|
|
3891
|
+
if (wrapperEl) {
|
|
3892
|
+
const combined = buildBatchErrorMessage(errorMessage, failures, state);
|
|
3893
|
+
if (combined) {
|
|
3894
|
+
showFileError(wrapperEl, combined);
|
|
3895
|
+
} else {
|
|
3896
|
+
clearFileError(wrapperEl);
|
|
3897
|
+
}
|
|
3898
|
+
}
|
|
3610
3899
|
updateCallback();
|
|
3611
3900
|
filesPicker.value = "";
|
|
3612
3901
|
if (instance && pathKey && !state.config.readonly) {
|
|
@@ -3639,10 +3928,17 @@ function validatePickedResource(resource, allowedExtensions, allowedMimes, maxSi
|
|
|
3639
3928
|
}
|
|
3640
3929
|
if (!isMimeAllowed(resource.type, allowedMimes)) {
|
|
3641
3930
|
const mimes = allowedMimes.join(", ");
|
|
3642
|
-
return t("invalidFileMime", state, {
|
|
3931
|
+
return t("invalidFileMime", state, {
|
|
3932
|
+
name: resource.name,
|
|
3933
|
+
type: resource.type,
|
|
3934
|
+
mimes
|
|
3935
|
+
});
|
|
3643
3936
|
}
|
|
3644
3937
|
if (!isSizeWithinLimit(resource.size, maxSizeMB)) {
|
|
3645
|
-
return t("fileTooLarge", state, {
|
|
3938
|
+
return t("fileTooLarge", state, {
|
|
3939
|
+
name: resource.name,
|
|
3940
|
+
maxSize: maxSizeMB
|
|
3941
|
+
});
|
|
3646
3942
|
}
|
|
3647
3943
|
return null;
|
|
3648
3944
|
}
|
|
@@ -3703,7 +3999,13 @@ async function handleLibraryPickMulti(state, element, wrapper, fieldPath, resour
|
|
|
3703
3999
|
return true;
|
|
3704
4000
|
});
|
|
3705
4001
|
const validItems = deduped.filter((r) => {
|
|
3706
|
-
const err = validatePickedResource(
|
|
4002
|
+
const err = validatePickedResource(
|
|
4003
|
+
r,
|
|
4004
|
+
allowedExtensions,
|
|
4005
|
+
allowedMimes,
|
|
4006
|
+
maxSizeMB,
|
|
4007
|
+
state
|
|
4008
|
+
);
|
|
3707
4009
|
return err === null;
|
|
3708
4010
|
});
|
|
3709
4011
|
const freshRemaining = maxCount === Infinity ? validItems.length : Math.max(0, maxCount - resourceIds.length);
|
|
@@ -3728,7 +4030,7 @@ async function handleLibraryPickMulti(state, element, wrapper, fieldPath, resour
|
|
|
3728
4030
|
}
|
|
3729
4031
|
}
|
|
3730
4032
|
async function handleLibraryPickSingle(state, element, container, fileWrapper, pathKey, fieldPath, renderCallback, instance) {
|
|
3731
|
-
var _a;
|
|
4033
|
+
var _a, _b;
|
|
3732
4034
|
if (!state.config.pickExistingFiles) return;
|
|
3733
4035
|
const allowedExtensions = getAllowedExtensions(element.accept);
|
|
3734
4036
|
const allowedMimes = getAllowedMimes(element.accept);
|
|
@@ -3748,20 +4050,32 @@ async function handleLibraryPickSingle(state, element, container, fileWrapper, p
|
|
|
3748
4050
|
}
|
|
3749
4051
|
if (picked.length === 0) return;
|
|
3750
4052
|
const first = picked[0];
|
|
3751
|
-
const validationError = validatePickedResource(
|
|
4053
|
+
const validationError = validatePickedResource(
|
|
4054
|
+
first,
|
|
4055
|
+
allowedExtensions,
|
|
4056
|
+
allowedMimes,
|
|
4057
|
+
maxSizeMB,
|
|
4058
|
+
state
|
|
4059
|
+
);
|
|
3752
4060
|
if (validationError !== null) {
|
|
3753
4061
|
showFileError(container, validationError);
|
|
3754
4062
|
return;
|
|
3755
4063
|
}
|
|
3756
4064
|
clearFileError(container);
|
|
3757
4065
|
registerPickedResource(first, state);
|
|
3758
|
-
let hiddenInput = fileWrapper.querySelector(
|
|
4066
|
+
let hiddenInput = fileWrapper.querySelector(
|
|
4067
|
+
'input[type="hidden"]'
|
|
4068
|
+
);
|
|
3759
4069
|
if (!hiddenInput) {
|
|
3760
4070
|
hiddenInput = document.createElement("input");
|
|
3761
4071
|
hiddenInput.type = "hidden";
|
|
3762
4072
|
hiddenInput.name = pathKey;
|
|
3763
4073
|
fileWrapper.appendChild(hiddenInput);
|
|
3764
4074
|
}
|
|
4075
|
+
const previousRid = hiddenInput.value || null;
|
|
4076
|
+
if (previousRid && previousRid !== first.resourceId) {
|
|
4077
|
+
releaseLocalFileUrl((_b = state.resourceIndex.get(previousRid)) == null ? void 0 : _b.file);
|
|
4078
|
+
}
|
|
3765
4079
|
hiddenInput.value = first.resourceId;
|
|
3766
4080
|
await renderCallback(first.resourceId);
|
|
3767
4081
|
if (!state.config.readonly) {
|
|
@@ -3770,7 +4084,9 @@ async function handleLibraryPickSingle(state, element, container, fileWrapper, p
|
|
|
3770
4084
|
}
|
|
3771
4085
|
|
|
3772
4086
|
// src/components/file/render-edit.ts
|
|
3773
|
-
|
|
4087
|
+
var ICON_CLOUD = `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M17 18a4 4 0 000-8 6 6 0 00-11.5 2A4 4 0 006 20h11M12 12v7M12 12l-3 3M12 12l3 3"/></svg>`;
|
|
4088
|
+
var ICON_LIBRARY2 = `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 4h4v16H4z"/><path d="M10 4h4v16h-4z"/><path d="M16 5l3.5 1-3 14L13 19"/></svg>`;
|
|
4089
|
+
function handleInitialFileData(initial, fileContainer, pathKey, fileWrapper, state, deps, extras) {
|
|
3774
4090
|
var _a;
|
|
3775
4091
|
seedInferredResource(initial, state.resourceIndex);
|
|
3776
4092
|
const meta = state.resourceIndex.get(initial);
|
|
@@ -3782,7 +4098,7 @@ function handleInitialFileData(initial, fileContainer, pathKey, fileWrapper, sta
|
|
|
3782
4098
|
deps
|
|
3783
4099
|
}).catch(console.error);
|
|
3784
4100
|
} else {
|
|
3785
|
-
|
|
4101
|
+
renderSingleFileFilled(fileContainer, initial, state, deps, extras);
|
|
3786
4102
|
}
|
|
3787
4103
|
const hiddenInput = document.createElement("input");
|
|
3788
4104
|
hiddenInput.type = "hidden";
|
|
@@ -3790,38 +4106,255 @@ function handleInitialFileData(initial, fileContainer, pathKey, fileWrapper, sta
|
|
|
3790
4106
|
hiddenInput.value = initial;
|
|
3791
4107
|
fileWrapper.appendChild(hiddenInput);
|
|
3792
4108
|
}
|
|
3793
|
-
|
|
3794
|
-
|
|
3795
|
-
|
|
3796
|
-
|
|
3797
|
-
const
|
|
3798
|
-
|
|
3799
|
-
|
|
3800
|
-
|
|
3801
|
-
|
|
3802
|
-
|
|
3803
|
-
|
|
3804
|
-
|
|
3805
|
-
|
|
4109
|
+
function buildWideTile(state, hasLibrary, onUploadClick, onLibraryClick, isDragOver = false, constraintHint = "") {
|
|
4110
|
+
ensureFileStyles();
|
|
4111
|
+
const outer = document.createElement("div");
|
|
4112
|
+
outer.className = `fb-wide-tile${hasLibrary ? " fb-file-card-row" : ""}${isDragOver ? " fb-drag-over" : ""}`;
|
|
4113
|
+
const uploadBtn = document.createElement("button");
|
|
4114
|
+
uploadBtn.type = "button";
|
|
4115
|
+
uploadBtn.className = "fb-wide-tile-upload fb-file-dropzone";
|
|
4116
|
+
const cloudIcon = document.createElement("span");
|
|
4117
|
+
cloudIcon.style.cssText = "width:36px;height:36px;display:block;flex-shrink:0;";
|
|
4118
|
+
cloudIcon.innerHTML = ICON_CLOUD;
|
|
4119
|
+
uploadBtn.appendChild(cloudIcon);
|
|
4120
|
+
const primaryText = document.createElement("div");
|
|
4121
|
+
primaryText.className = "fb-wide-tile-label";
|
|
4122
|
+
primaryText.style.cssText = "font-size:14px;font-weight:600;";
|
|
4123
|
+
primaryText.textContent = isDragOver ? t("dropToUpload", state) : t("clickDragText", state);
|
|
4124
|
+
uploadBtn.appendChild(primaryText);
|
|
4125
|
+
if (constraintHint) {
|
|
4126
|
+
const hintEl = document.createElement("div");
|
|
4127
|
+
hintEl.style.cssText = "font-size:11px;opacity:0.65;margin-top:2px;";
|
|
4128
|
+
hintEl.textContent = constraintHint;
|
|
4129
|
+
uploadBtn.appendChild(hintEl);
|
|
4130
|
+
}
|
|
4131
|
+
uploadBtn.onclick = (e) => {
|
|
4132
|
+
e.stopPropagation();
|
|
4133
|
+
onUploadClick();
|
|
4134
|
+
};
|
|
4135
|
+
outer.appendChild(uploadBtn);
|
|
4136
|
+
if (hasLibrary && onLibraryClick) {
|
|
4137
|
+
const divider = document.createElement("div");
|
|
4138
|
+
divider.className = "fb-wide-tile-divider";
|
|
4139
|
+
outer.appendChild(divider);
|
|
4140
|
+
const libBtn = document.createElement("button");
|
|
4141
|
+
libBtn.type = "button";
|
|
4142
|
+
libBtn.className = "fb-wide-tile-library fb-file-library-card";
|
|
4143
|
+
const libIcon = document.createElement("span");
|
|
4144
|
+
libIcon.style.cssText = "width:28px;height:28px;display:block;flex-shrink:0;";
|
|
4145
|
+
libIcon.innerHTML = ICON_LIBRARY2;
|
|
4146
|
+
libBtn.appendChild(libIcon);
|
|
4147
|
+
const libLabel = document.createElement("div");
|
|
4148
|
+
libLabel.style.cssText = "font-size:13px;font-weight:600;text-align:center;";
|
|
4149
|
+
libLabel.textContent = t("fromLibrary", state);
|
|
4150
|
+
libBtn.appendChild(libLabel);
|
|
4151
|
+
const libHint = document.createElement("div");
|
|
4152
|
+
libHint.style.cssText = "font-size:11px;opacity:0.75;text-align:center;";
|
|
4153
|
+
libHint.textContent = t("libraryHint", state);
|
|
4154
|
+
libBtn.appendChild(libHint);
|
|
4155
|
+
libBtn.onclick = (e) => {
|
|
4156
|
+
e.stopPropagation();
|
|
4157
|
+
onLibraryClick();
|
|
4158
|
+
};
|
|
4159
|
+
outer.appendChild(libBtn);
|
|
4160
|
+
}
|
|
4161
|
+
attachDragOverFeedback(outer, {
|
|
4162
|
+
onEnter: () => {
|
|
4163
|
+
const primaryText2 = outer.querySelector(".fb-wide-tile-label");
|
|
4164
|
+
if (primaryText2) primaryText2.textContent = t("dropToUpload", state);
|
|
4165
|
+
},
|
|
4166
|
+
onLeave: () => {
|
|
4167
|
+
const primaryText2 = outer.querySelector(".fb-wide-tile-label");
|
|
4168
|
+
if (primaryText2) primaryText2.textContent = t("clickDragText", state);
|
|
4169
|
+
},
|
|
4170
|
+
activeClass: "fb-drag-over"
|
|
4171
|
+
});
|
|
4172
|
+
return outer;
|
|
3806
4173
|
}
|
|
3807
|
-
function
|
|
3808
|
-
|
|
3809
|
-
|
|
3810
|
-
|
|
3811
|
-
|
|
3812
|
-
|
|
3813
|
-
|
|
3814
|
-
|
|
3815
|
-
|
|
3816
|
-
|
|
4174
|
+
function attachDragOverFeedback(el, hooks) {
|
|
4175
|
+
let depth = 0;
|
|
4176
|
+
el.addEventListener("dragover", (e) => {
|
|
4177
|
+
e.preventDefault();
|
|
4178
|
+
});
|
|
4179
|
+
el.addEventListener("dragenter", (e) => {
|
|
4180
|
+
e.preventDefault();
|
|
4181
|
+
depth++;
|
|
4182
|
+
if (depth === 1) {
|
|
4183
|
+
el.classList.add(hooks.activeClass);
|
|
4184
|
+
hooks.onEnter();
|
|
4185
|
+
}
|
|
4186
|
+
});
|
|
4187
|
+
el.addEventListener("dragleave", (e) => {
|
|
4188
|
+
e.preventDefault();
|
|
4189
|
+
depth = Math.max(0, depth - 1);
|
|
4190
|
+
if (depth === 0) {
|
|
4191
|
+
el.classList.remove(hooks.activeClass);
|
|
4192
|
+
hooks.onLeave();
|
|
4193
|
+
}
|
|
4194
|
+
});
|
|
4195
|
+
el.addEventListener("drop", () => {
|
|
4196
|
+
depth = 0;
|
|
4197
|
+
el.classList.remove(hooks.activeClass);
|
|
4198
|
+
hooks.onLeave();
|
|
4199
|
+
});
|
|
4200
|
+
}
|
|
4201
|
+
function renderSingleFileFilled(fileContainer, resourceId, state, deps, extras) {
|
|
4202
|
+
var _a, _b;
|
|
4203
|
+
const meta = state.resourceIndex.get(resourceId);
|
|
4204
|
+
const isVideo = (_a = meta == null ? void 0 : meta.type) == null ? void 0 : _a.startsWith("video/");
|
|
4205
|
+
if (isVideo) {
|
|
4206
|
+
renderFilePreview(fileContainer, resourceId, state, {
|
|
4207
|
+
fileName: (_b = meta == null ? void 0 : meta.name) != null ? _b : "",
|
|
4208
|
+
isReadonly: false,
|
|
4209
|
+
deps
|
|
4210
|
+
}).catch(console.error);
|
|
4211
|
+
return;
|
|
4212
|
+
}
|
|
4213
|
+
ensureFileStyles();
|
|
4214
|
+
const outer = document.createElement("div");
|
|
4215
|
+
outer.className = "fb-multi-outer fb-multi-has-files";
|
|
4216
|
+
const grid = document.createElement("div");
|
|
4217
|
+
grid.className = "fb-multi-grid fb-tiles-wrap";
|
|
4218
|
+
outer.appendChild(grid);
|
|
4219
|
+
const tile = buildPreviewTile(
|
|
4220
|
+
resourceId,
|
|
4221
|
+
state,
|
|
4222
|
+
Boolean(deps.onRemove),
|
|
4223
|
+
deps.onRemove ? () => {
|
|
4224
|
+
var _a2;
|
|
4225
|
+
return (_a2 = deps.onRemove) == null ? void 0 : _a2.call(deps);
|
|
4226
|
+
} : null,
|
|
4227
|
+
extras
|
|
4228
|
+
);
|
|
4229
|
+
grid.appendChild(tile);
|
|
4230
|
+
fileContainer.className = "file-preview-container";
|
|
4231
|
+
fileContainer.removeAttribute("style");
|
|
4232
|
+
while (fileContainer.firstChild)
|
|
4233
|
+
fileContainer.removeChild(fileContainer.firstChild);
|
|
4234
|
+
fileContainer.appendChild(outer);
|
|
4235
|
+
}
|
|
4236
|
+
function buildMultiAddTile(state, hasLibrary, onUploadClick, onLibraryClick, isDragOver = false) {
|
|
4237
|
+
const tile = document.createElement("div");
|
|
4238
|
+
tile.className = `fb-multi-add-tile fb-multi-add-tile-js${isDragOver ? " fb-drag-over-tile" : ""}`;
|
|
4239
|
+
const uploadBtn = document.createElement("button");
|
|
4240
|
+
uploadBtn.type = "button";
|
|
4241
|
+
uploadBtn.className = "fb-multi-add-upload fb-tile-add fb-file-dropzone";
|
|
4242
|
+
const cloudIcon = document.createElement("span");
|
|
4243
|
+
cloudIcon.style.cssText = "width:28px;height:28px;display:block;flex-shrink:0;";
|
|
4244
|
+
cloudIcon.innerHTML = ICON_CLOUD;
|
|
4245
|
+
uploadBtn.appendChild(cloudIcon);
|
|
4246
|
+
const uploadLabel = document.createElement("span");
|
|
4247
|
+
uploadLabel.className = "fb-multi-add-label";
|
|
4248
|
+
uploadLabel.style.cssText = "font-size:11px;font-weight:600;";
|
|
4249
|
+
uploadLabel.textContent = isDragOver ? t("dropToUpload", state) : t("clickDragTextMultiple", state);
|
|
4250
|
+
uploadBtn.appendChild(uploadLabel);
|
|
4251
|
+
uploadBtn.onclick = (e) => {
|
|
4252
|
+
e.stopPropagation();
|
|
4253
|
+
onUploadClick();
|
|
4254
|
+
};
|
|
4255
|
+
tile.appendChild(uploadBtn);
|
|
4256
|
+
if (hasLibrary && onLibraryClick) {
|
|
4257
|
+
const divider = document.createElement("div");
|
|
4258
|
+
divider.className = "fb-multi-add-divider";
|
|
4259
|
+
tile.appendChild(divider);
|
|
4260
|
+
const libBtn = document.createElement("button");
|
|
4261
|
+
libBtn.type = "button";
|
|
4262
|
+
libBtn.className = "fb-multi-add-library fb-tile-add-library fb-file-library-card";
|
|
4263
|
+
libBtn.setAttribute("aria-label", t("fromLibrary", state));
|
|
4264
|
+
const libIcon = document.createElement("span");
|
|
4265
|
+
libIcon.style.cssText = "width:14px;height:14px;display:block;flex-shrink:0;";
|
|
4266
|
+
libIcon.innerHTML = ICON_LIBRARY2;
|
|
4267
|
+
libBtn.appendChild(libIcon);
|
|
4268
|
+
libBtn.appendChild(document.createTextNode(t("fromLibrary", state)));
|
|
4269
|
+
libBtn.onclick = (e) => {
|
|
4270
|
+
e.stopPropagation();
|
|
4271
|
+
onLibraryClick();
|
|
4272
|
+
};
|
|
4273
|
+
tile.appendChild(libBtn);
|
|
4274
|
+
}
|
|
4275
|
+
return tile;
|
|
4276
|
+
}
|
|
4277
|
+
function buildPreviewTile(rid, state, canRemove, onRemove, extras) {
|
|
4278
|
+
var _a, _b, _c;
|
|
4279
|
+
ensureFileStyles();
|
|
4280
|
+
const meta = state.resourceIndex.get(rid);
|
|
4281
|
+
const tile = document.createElement("div");
|
|
4282
|
+
tile.className = "fb-preview-tile fb-checker fb-tile-resource resource-pill";
|
|
4283
|
+
tile.dataset.resourceId = rid;
|
|
4284
|
+
const actionsEl = createTileActions({
|
|
4285
|
+
canRemove: canRemove && onRemove !== null,
|
|
4286
|
+
removeHandler: onRemove,
|
|
4287
|
+
state,
|
|
4288
|
+
resourceId: rid,
|
|
4289
|
+
fileName: (_a = meta == null ? void 0 : meta.name) != null ? _a : "",
|
|
4290
|
+
meta,
|
|
4291
|
+
replaceHandler: (_b = extras == null ? void 0 : extras.replaceHandler) != null ? _b : null,
|
|
4292
|
+
libraryHandler: (_c = extras == null ? void 0 : extras.libraryHandler) != null ? _c : null
|
|
4293
|
+
});
|
|
4294
|
+
fillTileContent(tile, rid, meta, state, actionsEl).catch((err) => {
|
|
4295
|
+
console.error("Failed to render tile:", err);
|
|
4296
|
+
});
|
|
4297
|
+
return tile;
|
|
4298
|
+
}
|
|
4299
|
+
function buildPlaceholderTile(isDragOver = false) {
|
|
4300
|
+
const div = document.createElement("div");
|
|
4301
|
+
div.className = `fb-multi-placeholder fb-checker${isDragOver ? " fb-drag-over" : ""}`;
|
|
4302
|
+
return div;
|
|
4303
|
+
}
|
|
4304
|
+
function buildMetaLine(state, element, ridCount, maxCount, canClearAll, onClearAll) {
|
|
4305
|
+
const line = document.createElement("div");
|
|
4306
|
+
line.className = "fb-meta-line";
|
|
4307
|
+
const metaText = document.createElement("div");
|
|
4308
|
+
metaText.className = "fb-meta-text";
|
|
4309
|
+
if (element.maxSize && element.maxSize !== Infinity) {
|
|
4310
|
+
const sizeSpan = document.createElement("span");
|
|
4311
|
+
sizeSpan.textContent = t("hintMaxSize", state, { size: element.maxSize });
|
|
4312
|
+
metaText.appendChild(sizeSpan);
|
|
4313
|
+
metaText.appendChild(buildMetaDot());
|
|
4314
|
+
}
|
|
4315
|
+
const exts = getAllowedExtensions(
|
|
4316
|
+
element.accept
|
|
4317
|
+
);
|
|
4318
|
+
if (exts.length > 0) {
|
|
4319
|
+
const fmtSpan = document.createElement("span");
|
|
4320
|
+
fmtSpan.className = "fb-meta-mono";
|
|
4321
|
+
fmtSpan.textContent = exts.map((e) => e.toUpperCase()).join(", ");
|
|
4322
|
+
metaText.appendChild(fmtSpan);
|
|
4323
|
+
metaText.appendChild(buildMetaDot());
|
|
4324
|
+
}
|
|
4325
|
+
const countSpan = document.createElement("span");
|
|
4326
|
+
if (maxCount < Infinity) {
|
|
4327
|
+
countSpan.textContent = t("fileCountWithMax", state, {
|
|
4328
|
+
count: ridCount,
|
|
4329
|
+
max: maxCount
|
|
4330
|
+
});
|
|
3817
4331
|
} else {
|
|
3818
|
-
|
|
3819
|
-
|
|
3820
|
-
|
|
4332
|
+
const countKey = ridCount === 1 ? "fileCountSingle" : "fileCountPlural";
|
|
4333
|
+
countSpan.textContent = t(countKey, state, { count: ridCount });
|
|
4334
|
+
}
|
|
4335
|
+
metaText.appendChild(countSpan);
|
|
4336
|
+
line.appendChild(metaText);
|
|
4337
|
+
if (canClearAll && ridCount > 1) {
|
|
4338
|
+
const clearBtn = document.createElement("button");
|
|
4339
|
+
clearBtn.type = "button";
|
|
4340
|
+
clearBtn.className = "fb-clear-all-btn";
|
|
4341
|
+
clearBtn.textContent = t("clearAll", state);
|
|
4342
|
+
clearBtn.onclick = (e) => {
|
|
4343
|
+
e.stopPropagation();
|
|
4344
|
+
if (window.confirm(t("clearAll", state) + "?")) {
|
|
4345
|
+
onClearAll();
|
|
4346
|
+
}
|
|
4347
|
+
};
|
|
4348
|
+
line.appendChild(clearBtn);
|
|
3821
4349
|
}
|
|
3822
|
-
|
|
3823
|
-
|
|
4350
|
+
return line;
|
|
4351
|
+
}
|
|
4352
|
+
function buildMetaDot() {
|
|
4353
|
+
const dot = document.createElement("span");
|
|
4354
|
+
dot.className = "fb-meta-dot";
|
|
4355
|
+
return dot;
|
|
3824
4356
|
}
|
|
4357
|
+
var gridResizeObservers = /* @__PURE__ */ new WeakMap();
|
|
3825
4358
|
function renderResourcePills(opts) {
|
|
3826
4359
|
var _a;
|
|
3827
4360
|
const {
|
|
@@ -3829,130 +4362,185 @@ function renderResourcePills(opts) {
|
|
|
3829
4362
|
rids,
|
|
3830
4363
|
state,
|
|
3831
4364
|
onRemove,
|
|
3832
|
-
hint,
|
|
3833
|
-
countInfo,
|
|
3834
4365
|
maxCount,
|
|
3835
4366
|
isReadonly = false,
|
|
3836
|
-
onLibraryPick
|
|
4367
|
+
onLibraryPick,
|
|
4368
|
+
element,
|
|
4369
|
+
onClearAll,
|
|
4370
|
+
openPicker: openPickerProp
|
|
3837
4371
|
} = opts;
|
|
3838
4372
|
ensureFileStyles();
|
|
3839
4373
|
const wrapper = container.closest("[data-files-wrapper]");
|
|
3840
4374
|
if (wrapper) {
|
|
3841
4375
|
wrapper.dataset.resourceIds = JSON.stringify(rids != null ? rids : []);
|
|
3842
4376
|
}
|
|
4377
|
+
const previousObserver = gridResizeObservers.get(container);
|
|
4378
|
+
if (previousObserver) {
|
|
4379
|
+
previousObserver.disconnect();
|
|
4380
|
+
gridResizeObservers.delete(container);
|
|
4381
|
+
}
|
|
3843
4382
|
while (container.firstChild) container.removeChild(container.firstChild);
|
|
3844
4383
|
const ridList = rids != null ? rids : [];
|
|
3845
|
-
const
|
|
4384
|
+
const effectiveMax = maxCount != null ? maxCount : Infinity;
|
|
4385
|
+
const atMax = effectiveMax !== Infinity && ridList.length >= effectiveMax;
|
|
3846
4386
|
const hasLibrary = !isReadonly && typeof onLibraryPick === "function";
|
|
3847
|
-
const
|
|
3848
|
-
|
|
3849
|
-
|
|
3850
|
-
if (
|
|
3851
|
-
|
|
3852
|
-
|
|
3853
|
-
|
|
3854
|
-
const picker = findFilePicker(container);
|
|
3855
|
-
if (picker) picker.click();
|
|
3856
|
-
};
|
|
3857
|
-
if (ridList.length === 0) {
|
|
3858
|
-
if (isReadonly) {
|
|
4387
|
+
const openPicker = openPickerProp != null ? openPickerProp : (() => {
|
|
4388
|
+
var _a2;
|
|
4389
|
+
const pickerEl = (_a2 = container.closest("[data-files-wrapper]")) == null ? void 0 : _a2.querySelector('input[type="file"]');
|
|
4390
|
+
if (pickerEl) pickerEl.click();
|
|
4391
|
+
});
|
|
4392
|
+
if (isReadonly) {
|
|
4393
|
+
if (ridList.length === 0) {
|
|
3859
4394
|
const emptyEl = document.createElement("div");
|
|
3860
4395
|
emptyEl.className = "fb-tile-empty-text";
|
|
3861
4396
|
emptyEl.textContent = t("noFilesSelected", state);
|
|
3862
4397
|
container.appendChild(emptyEl);
|
|
3863
|
-
} else if (hasLibrary) {
|
|
3864
|
-
const row = document.createElement("div");
|
|
3865
|
-
row.className = "fb-file-card-row";
|
|
3866
|
-
const dropzone = buildEmptyDropzone(
|
|
3867
|
-
state,
|
|
3868
|
-
t("clickDragTextMultiple", state),
|
|
3869
|
-
buildSubHint(),
|
|
3870
|
-
openPicker
|
|
3871
|
-
);
|
|
3872
|
-
const libraryBtn = buildLibraryButton("card", state, onLibraryPick);
|
|
3873
|
-
row.appendChild(dropzone);
|
|
3874
|
-
row.appendChild(libraryBtn);
|
|
3875
|
-
container.appendChild(row);
|
|
3876
4398
|
} else {
|
|
3877
|
-
const
|
|
3878
|
-
|
|
3879
|
-
|
|
3880
|
-
|
|
3881
|
-
|
|
3882
|
-
|
|
3883
|
-
|
|
4399
|
+
const grid2 = document.createElement("div");
|
|
4400
|
+
grid2.className = "fb-multi-readonly-grid";
|
|
4401
|
+
container.appendChild(grid2);
|
|
4402
|
+
for (const rid of ridList) {
|
|
4403
|
+
const meta = state.resourceIndex.get(rid);
|
|
4404
|
+
const tile = document.createElement("div");
|
|
4405
|
+
tile.className = "fb-readonly-tile fb-checker fb-tile fb-tile-resource";
|
|
4406
|
+
tile.dataset.resourceId = rid;
|
|
4407
|
+
const actionsEl = createTileActions({
|
|
4408
|
+
canRemove: false,
|
|
4409
|
+
removeHandler: null,
|
|
4410
|
+
state,
|
|
4411
|
+
resourceId: rid,
|
|
4412
|
+
fileName: (_a = meta == null ? void 0 : meta.name) != null ? _a : "",
|
|
4413
|
+
meta
|
|
4414
|
+
});
|
|
4415
|
+
fillTileContent(tile, rid, meta, state, actionsEl).catch(console.error);
|
|
4416
|
+
tile.onclick = async () => {
|
|
4417
|
+
var _a2;
|
|
4418
|
+
let url = null;
|
|
4419
|
+
if (state.config.getDownloadUrl) {
|
|
4420
|
+
url = state.config.getDownloadUrl(rid);
|
|
4421
|
+
} else if (state.config.getThumbnail) {
|
|
4422
|
+
url = await state.config.getThumbnail(rid);
|
|
4423
|
+
} else if ((meta == null ? void 0 : meta.file) instanceof File) {
|
|
4424
|
+
url = URL.createObjectURL(meta.file);
|
|
4425
|
+
}
|
|
4426
|
+
if (url) {
|
|
4427
|
+
window.open(url, "_blank");
|
|
4428
|
+
} else if (state.config.downloadFile) {
|
|
4429
|
+
state.config.downloadFile(rid, (_a2 = meta == null ? void 0 : meta.name) != null ? _a2 : "");
|
|
4430
|
+
}
|
|
4431
|
+
};
|
|
4432
|
+
grid2.appendChild(tile);
|
|
4433
|
+
}
|
|
3884
4434
|
}
|
|
3885
4435
|
return;
|
|
3886
4436
|
}
|
|
3887
|
-
const
|
|
3888
|
-
|
|
3889
|
-
|
|
3890
|
-
|
|
3891
|
-
|
|
3892
|
-
|
|
3893
|
-
|
|
3894
|
-
|
|
3895
|
-
const
|
|
3896
|
-
|
|
3897
|
-
removeHandler: onRemove ? () => onRemove(rid) : null,
|
|
4437
|
+
const outerDiv = document.createElement("div");
|
|
4438
|
+
outerDiv.className = `fb-multi-outer${ridList.length > 0 ? " fb-multi-has-files" : ""}`;
|
|
4439
|
+
const grid = document.createElement("div");
|
|
4440
|
+
grid.className = "fb-multi-grid fb-tiles-wrap";
|
|
4441
|
+
outerDiv.appendChild(grid);
|
|
4442
|
+
container.appendChild(outerDiv);
|
|
4443
|
+
for (let i = 0; i < ridList.length; i++) {
|
|
4444
|
+
const rid = ridList[i];
|
|
4445
|
+
const tile = buildPreviewTile(
|
|
4446
|
+
rid,
|
|
3898
4447
|
state,
|
|
3899
|
-
|
|
3900
|
-
|
|
3901
|
-
|
|
3902
|
-
|
|
3903
|
-
console.error("Failed to render tile:", err);
|
|
3904
|
-
});
|
|
3905
|
-
tilesWrap.appendChild(tile);
|
|
3906
|
-
}
|
|
3907
|
-
if (!isReadonly && !atMax) {
|
|
3908
|
-
const addTile = document.createElement("div");
|
|
3909
|
-
addTile.className = "fb-tile fb-tile-add";
|
|
3910
|
-
addTile.innerHTML = "+";
|
|
3911
|
-
addTile.onclick = openPicker;
|
|
3912
|
-
tilesWrap.appendChild(addTile);
|
|
3913
|
-
if (hasLibrary) {
|
|
3914
|
-
const libraryTile = buildLibraryButton("tile", state, onLibraryPick);
|
|
3915
|
-
tilesWrap.appendChild(libraryTile);
|
|
3916
|
-
}
|
|
3917
|
-
} else if (!isReadonly && atMax) {
|
|
3918
|
-
const chip = document.createElement("div");
|
|
3919
|
-
chip.className = "fb-tile-counter";
|
|
3920
|
-
chip.textContent = t("filesCounter", state, {
|
|
3921
|
-
count: ridList.length,
|
|
3922
|
-
max: maxCount
|
|
3923
|
-
});
|
|
3924
|
-
tilesWrap.appendChild(chip);
|
|
4448
|
+
onRemove !== null,
|
|
4449
|
+
onRemove ? () => onRemove(rid) : null
|
|
4450
|
+
);
|
|
4451
|
+
grid.appendChild(tile);
|
|
3925
4452
|
}
|
|
3926
|
-
|
|
3927
|
-
|
|
3928
|
-
|
|
3929
|
-
|
|
3930
|
-
|
|
3931
|
-
|
|
3932
|
-
|
|
4453
|
+
if (!atMax) {
|
|
4454
|
+
const addTile = buildMultiAddTile(
|
|
4455
|
+
state,
|
|
4456
|
+
hasLibrary,
|
|
4457
|
+
openPicker,
|
|
4458
|
+
onLibraryPick != null ? onLibraryPick : null
|
|
4459
|
+
);
|
|
4460
|
+
grid.appendChild(addTile);
|
|
4461
|
+
}
|
|
4462
|
+
const occupied = ridList.length + (atMax ? 0 : 1);
|
|
4463
|
+
const adjustPlaceholders = () => {
|
|
4464
|
+
const tpl = getComputedStyle(grid).gridTemplateColumns;
|
|
4465
|
+
const cols = tpl ? tpl.split(" ").filter(Boolean).length : 0;
|
|
4466
|
+
if (!cols) return;
|
|
4467
|
+
const remainder = occupied % cols;
|
|
4468
|
+
const rowFill = remainder === 0 ? 0 : cols - remainder;
|
|
4469
|
+
const capacityRemaining = effectiveMax === Infinity ? rowFill : Math.max(0, effectiveMax - occupied);
|
|
4470
|
+
const needed = Math.min(rowFill, capacityRemaining);
|
|
4471
|
+
const existing = grid.querySelectorAll(".fb-multi-placeholder");
|
|
4472
|
+
if (existing.length > needed) {
|
|
4473
|
+
for (let i = existing.length - 1; i >= needed; i--) existing[i].remove();
|
|
4474
|
+
} else if (existing.length < needed) {
|
|
4475
|
+
for (let i = existing.length; i < needed; i++) {
|
|
4476
|
+
grid.appendChild(buildPlaceholderTile());
|
|
4477
|
+
}
|
|
4478
|
+
}
|
|
4479
|
+
};
|
|
4480
|
+
if (effectiveMax === Infinity || effectiveMax > occupied) {
|
|
4481
|
+
grid.appendChild(buildPlaceholderTile());
|
|
4482
|
+
}
|
|
4483
|
+
requestAnimationFrame(adjustPlaceholders);
|
|
4484
|
+
if (typeof ResizeObserver !== "undefined") {
|
|
4485
|
+
const ro = new ResizeObserver(() => adjustPlaceholders());
|
|
4486
|
+
ro.observe(grid);
|
|
4487
|
+
gridResizeObservers.set(container, ro);
|
|
4488
|
+
}
|
|
4489
|
+
attachDragOverFeedback(outerDiv, {
|
|
4490
|
+
activeClass: "fb-drag-over",
|
|
4491
|
+
onEnter: () => {
|
|
4492
|
+
grid.querySelectorAll(".fb-multi-placeholder").forEach((p) => {
|
|
4493
|
+
p.classList.add("fb-drag-over");
|
|
4494
|
+
});
|
|
4495
|
+
const addTile = grid.querySelector(".fb-multi-add-tile-js");
|
|
4496
|
+
if (addTile) {
|
|
4497
|
+
addTile.classList.add("fb-drag-over-tile");
|
|
4498
|
+
const label = addTile.querySelector(".fb-multi-add-label");
|
|
4499
|
+
if (label) label.textContent = t("dropToUpload", state);
|
|
4500
|
+
}
|
|
4501
|
+
},
|
|
4502
|
+
onLeave: () => {
|
|
4503
|
+
grid.querySelectorAll(".fb-multi-placeholder").forEach((p) => {
|
|
4504
|
+
p.classList.remove("fb-drag-over");
|
|
4505
|
+
});
|
|
4506
|
+
const addTile = grid.querySelector(".fb-multi-add-tile-js");
|
|
4507
|
+
if (addTile) {
|
|
4508
|
+
addTile.classList.remove("fb-drag-over-tile");
|
|
4509
|
+
const label = addTile.querySelector(".fb-multi-add-label");
|
|
4510
|
+
if (label) label.textContent = t("clickDragTextMultiple", state);
|
|
4511
|
+
}
|
|
4512
|
+
}
|
|
4513
|
+
});
|
|
4514
|
+
if (element) {
|
|
4515
|
+
const metaLine = buildMetaLine(
|
|
4516
|
+
state,
|
|
4517
|
+
element,
|
|
4518
|
+
ridList.length,
|
|
4519
|
+
effectiveMax,
|
|
4520
|
+
Boolean(onClearAll),
|
|
4521
|
+
onClearAll != null ? onClearAll : (() => {
|
|
4522
|
+
})
|
|
4523
|
+
);
|
|
4524
|
+
container.appendChild(metaLine);
|
|
3933
4525
|
}
|
|
3934
4526
|
}
|
|
3935
4527
|
function renderFileElementEdit(element, ctx, wrapper, pathKey) {
|
|
3936
|
-
var _a, _b
|
|
4528
|
+
var _a, _b;
|
|
3937
4529
|
const state = ctx.state;
|
|
3938
4530
|
const fileWrapper = document.createElement("div");
|
|
3939
4531
|
fileWrapper.className = "space-y-2";
|
|
4532
|
+
fileWrapper.dataset.filesWrapper = pathKey;
|
|
3940
4533
|
const picker = document.createElement("input");
|
|
3941
4534
|
picker.type = "file";
|
|
3942
4535
|
picker.name = pathKey;
|
|
3943
4536
|
picker.style.display = "none";
|
|
3944
|
-
|
|
3945
|
-
picker.accept = typeof element.accept === "string" ? element.accept : [
|
|
3946
|
-
...(_b = (_a = element.accept.extensions) == null ? void 0 : _a.map((ext) => `.${ext}`)) != null ? _b : [],
|
|
3947
|
-
...(_c = element.accept.mime) != null ? _c : []
|
|
3948
|
-
].join(",") || "";
|
|
3949
|
-
}
|
|
4537
|
+
picker.accept = buildAcceptAttribute(element.accept);
|
|
3950
4538
|
const fileContainer = document.createElement("div");
|
|
3951
4539
|
fileContainer.className = "file-preview-container";
|
|
3952
4540
|
const initial = ctx.prefill[element.key];
|
|
3953
4541
|
const allowedExts = getAllowedExtensions(element.accept);
|
|
3954
4542
|
const allowedMimes = getAllowedMimes(element.accept);
|
|
3955
|
-
const maxSizeMB = (
|
|
4543
|
+
const maxSizeMB = (_a = element.maxSize) != null ? _a : Infinity;
|
|
3956
4544
|
const handlers = {
|
|
3957
4545
|
fileUploadHandler() {
|
|
3958
4546
|
picker.click();
|
|
@@ -3975,17 +4563,11 @@ function renderFileElementEdit(element, ctx, wrapper, pathKey) {
|
|
|
3975
4563
|
setupDrop(container) {
|
|
3976
4564
|
setupDragAndDrop(container, handlers.dragHandler);
|
|
3977
4565
|
},
|
|
3978
|
-
restoreDropzone() {
|
|
3979
|
-
const hint = makeFieldHint(element, state);
|
|
3980
|
-
fileContainer.className = "file-preview-container w-full max-w-md bg-gray-100 rounded-lg overflow-hidden relative group cursor-pointer";
|
|
3981
|
-
fileContainer.style.height = "128px";
|
|
3982
|
-
setEmptyFileContainer(fileContainer, state, hint);
|
|
3983
|
-
fileContainer.onclick = handlers.fileUploadHandler;
|
|
3984
|
-
setupDragAndDrop(fileContainer, handlers.dragHandler);
|
|
3985
|
-
},
|
|
3986
4566
|
onRemove() {
|
|
3987
4567
|
var _a2;
|
|
3988
|
-
const hiddenInput = fileWrapper.querySelector(
|
|
4568
|
+
const hiddenInput = fileWrapper.querySelector(
|
|
4569
|
+
'input[type="hidden"]'
|
|
4570
|
+
);
|
|
3989
4571
|
const currentRid = hiddenInput == null ? void 0 : hiddenInput.value;
|
|
3990
4572
|
if (currentRid) {
|
|
3991
4573
|
releaseLocalFileUrl((_a2 = state.resourceIndex.get(currentRid)) == null ? void 0 : _a2.file);
|
|
@@ -3994,34 +4576,13 @@ function renderFileElementEdit(element, ctx, wrapper, pathKey) {
|
|
|
3994
4576
|
renderEmptySingleState();
|
|
3995
4577
|
}
|
|
3996
4578
|
};
|
|
3997
|
-
const
|
|
3998
|
-
|
|
3999
|
-
|
|
4000
|
-
|
|
4001
|
-
|
|
4002
|
-
|
|
4003
|
-
|
|
4004
|
-
const renderEmptySingleState = () => {
|
|
4005
|
-
if (state.config.pickExistingFiles && !element.disableLibrary) {
|
|
4006
|
-
fileContainer.className = "file-preview-container";
|
|
4007
|
-
fileContainer.removeAttribute("style");
|
|
4008
|
-
fileContainer.onclick = null;
|
|
4009
|
-
while (fileContainer.firstChild) {
|
|
4010
|
-
fileContainer.removeChild(fileContainer.firstChild);
|
|
4011
|
-
}
|
|
4012
|
-
const row = document.createElement("div");
|
|
4013
|
-
row.className = "fb-file-card-row";
|
|
4014
|
-
row.style.cssText = "display:flex;gap:8px;align-items:stretch;";
|
|
4015
|
-
const hint = makeFieldHint(element, state);
|
|
4016
|
-
const uploadCard = buildEmptyDropzone(
|
|
4017
|
-
state,
|
|
4018
|
-
t("clickDragText", state),
|
|
4019
|
-
hint,
|
|
4020
|
-
handlers.fileUploadHandler
|
|
4021
|
-
);
|
|
4022
|
-
uploadCard.style.cssText = "flex:1;min-width:0;height:128px;";
|
|
4023
|
-
setupDragAndDrop(uploadCard, handlers.dragHandler);
|
|
4024
|
-
const libraryBtn = buildLibraryButton("card", state, () => {
|
|
4579
|
+
const buildSingleExtras = () => {
|
|
4580
|
+
const hasLibrary = Boolean(
|
|
4581
|
+
state.config.pickExistingFiles && !element.disableLibrary
|
|
4582
|
+
);
|
|
4583
|
+
return {
|
|
4584
|
+
replaceHandler: state.config.uploadFile ? () => picker.click() : null,
|
|
4585
|
+
libraryHandler: hasLibrary ? () => {
|
|
4025
4586
|
handleLibraryPickSingle(
|
|
4026
4587
|
state,
|
|
4027
4588
|
element,
|
|
@@ -4030,20 +4591,54 @@ function renderFileElementEdit(element, ctx, wrapper, pathKey) {
|
|
|
4030
4591
|
pathKey,
|
|
4031
4592
|
pathKey,
|
|
4032
4593
|
async (rid) => {
|
|
4033
|
-
|
|
4594
|
+
renderSingleFileFilled(
|
|
4595
|
+
fileContainer,
|
|
4596
|
+
rid,
|
|
4597
|
+
state,
|
|
4598
|
+
buildDeps(),
|
|
4599
|
+
buildSingleExtras()
|
|
4600
|
+
);
|
|
4034
4601
|
},
|
|
4035
4602
|
ctx.instance
|
|
4036
4603
|
).catch((err) => {
|
|
4037
4604
|
console.error("Library pick failed:", err);
|
|
4038
4605
|
});
|
|
4039
|
-
}
|
|
4040
|
-
|
|
4041
|
-
|
|
4042
|
-
|
|
4043
|
-
|
|
4044
|
-
|
|
4045
|
-
|
|
4606
|
+
} : null
|
|
4607
|
+
};
|
|
4608
|
+
};
|
|
4609
|
+
const buildDeps = () => ({
|
|
4610
|
+
picker,
|
|
4611
|
+
fileUploadHandler: handlers.fileUploadHandler,
|
|
4612
|
+
dragHandler: handlers.dragHandler,
|
|
4613
|
+
setupDrop: handlers.setupDrop,
|
|
4614
|
+
onRemove: handlers.onRemove,
|
|
4615
|
+
onAfterUpload: (container, rid) => {
|
|
4616
|
+
renderSingleFileFilled(
|
|
4617
|
+
container,
|
|
4618
|
+
rid,
|
|
4619
|
+
state,
|
|
4620
|
+
buildDeps(),
|
|
4621
|
+
buildSingleExtras()
|
|
4622
|
+
);
|
|
4046
4623
|
}
|
|
4624
|
+
});
|
|
4625
|
+
const renderEmptySingleState = () => {
|
|
4626
|
+
ensureFileStyles();
|
|
4627
|
+
fileContainer.className = "file-preview-container";
|
|
4628
|
+
fileContainer.removeAttribute("style");
|
|
4629
|
+
while (fileContainer.firstChild)
|
|
4630
|
+
fileContainer.removeChild(fileContainer.firstChild);
|
|
4631
|
+
const onLibraryClick = buildSingleExtras().libraryHandler;
|
|
4632
|
+
const wideTile = buildWideTile(
|
|
4633
|
+
state,
|
|
4634
|
+
onLibraryClick !== null,
|
|
4635
|
+
handlers.fileUploadHandler,
|
|
4636
|
+
onLibraryClick,
|
|
4637
|
+
false,
|
|
4638
|
+
makeFieldHint(element, state)
|
|
4639
|
+
);
|
|
4640
|
+
fileContainer.appendChild(wideTile);
|
|
4641
|
+
setupDragAndDrop(fileContainer, handlers.dragHandler);
|
|
4047
4642
|
};
|
|
4048
4643
|
if (initial) {
|
|
4049
4644
|
handleInitialFileData(
|
|
@@ -4052,11 +4647,11 @@ function renderFileElementEdit(element, ctx, wrapper, pathKey) {
|
|
|
4052
4647
|
pathKey,
|
|
4053
4648
|
fileWrapper,
|
|
4054
4649
|
state,
|
|
4055
|
-
buildDeps()
|
|
4650
|
+
buildDeps(),
|
|
4651
|
+
buildSingleExtras()
|
|
4056
4652
|
);
|
|
4057
4653
|
const prefillMeta = state.resourceIndex.get(initial);
|
|
4058
|
-
if ((
|
|
4059
|
-
fileContainer.onclick = handlers.fileUploadHandler;
|
|
4654
|
+
if ((_b = prefillMeta == null ? void 0 : prefillMeta.type) == null ? void 0 : _b.startsWith("video/")) {
|
|
4060
4655
|
setupDragAndDrop(fileContainer, handlers.dragHandler);
|
|
4061
4656
|
}
|
|
4062
4657
|
} else {
|
|
@@ -4064,116 +4659,25 @@ function renderFileElementEdit(element, ctx, wrapper, pathKey) {
|
|
|
4064
4659
|
}
|
|
4065
4660
|
picker.onchange = () => {
|
|
4066
4661
|
if (picker.files && picker.files.length > 0) {
|
|
4067
|
-
|
|
4068
|
-
file: picker.files[0],
|
|
4069
|
-
container: fileContainer,
|
|
4070
|
-
fieldName: pathKey,
|
|
4071
|
-
state,
|
|
4072
|
-
deps: buildDeps(),
|
|
4073
|
-
instance: ctx.instance,
|
|
4074
|
-
allowedExtensions: allowedExts,
|
|
4075
|
-
allowedMimes,
|
|
4076
|
-
maxSizeMB
|
|
4077
|
-
});
|
|
4662
|
+
handlers.dragHandler(picker.files);
|
|
4078
4663
|
}
|
|
4079
4664
|
};
|
|
4080
4665
|
fileWrapper.appendChild(fileContainer);
|
|
4081
4666
|
fileWrapper.appendChild(picker);
|
|
4082
4667
|
wrapper.appendChild(fileWrapper);
|
|
4083
4668
|
}
|
|
4084
|
-
function
|
|
4085
|
-
var _a, _b, _c
|
|
4086
|
-
|
|
4087
|
-
|
|
4088
|
-
|
|
4089
|
-
|
|
4090
|
-
|
|
4091
|
-
|
|
4092
|
-
|
|
4093
|
-
|
|
4094
|
-
|
|
4095
|
-
if (element.accept) {
|
|
4096
|
-
filesPicker.accept = typeof element.accept === "string" ? element.accept : [
|
|
4097
|
-
...(_b = (_a = element.accept.extensions) == null ? void 0 : _a.map((ext) => `.${ext}`)) != null ? _b : [],
|
|
4098
|
-
...(_c = element.accept.mime) != null ? _c : []
|
|
4099
|
-
].join(",") || "";
|
|
4100
|
-
}
|
|
4101
|
-
const filesContainer = document.createElement("div");
|
|
4102
|
-
filesContainer.className = "files-list-wrapper";
|
|
4103
|
-
filesContainer.style.cssText = "border:2px dashed var(--fb-file-upload-border-color,#d1d5db);border-radius:var(--fb-border-radius,0.5rem);padding:8px;transition:border-color var(--fb-transition-duration,200ms),background var(--fb-transition-duration,200ms);";
|
|
4104
|
-
const list = document.createElement("div");
|
|
4105
|
-
list.className = "files-list";
|
|
4106
|
-
const initialFiles = ctx.prefill[element.key] || [];
|
|
4107
|
-
addPrefillFilesToIndex(initialFiles, state.resourceIndex);
|
|
4108
|
-
filesWrapper.dataset.resourceIds = JSON.stringify(initialFiles);
|
|
4109
|
-
const filesFieldHint = makeFieldHint(element, state);
|
|
4110
|
-
const filesConstraints = {
|
|
4111
|
-
maxCount: Infinity,
|
|
4112
|
-
allowedExtensions: getAllowedExtensions(element.accept),
|
|
4113
|
-
allowedMimes: getAllowedMimes(element.accept),
|
|
4114
|
-
maxSize: (_d = element.maxSize) != null ? _d : Infinity
|
|
4115
|
-
};
|
|
4116
|
-
filesContainer.appendChild(list);
|
|
4117
|
-
filesWrapper.appendChild(filesPicker);
|
|
4118
|
-
filesWrapper.appendChild(filesContainer);
|
|
4119
|
-
wrapper.appendChild(filesWrapper);
|
|
4120
|
-
const onLibraryPickFiles = state.config.pickExistingFiles && !element.disableLibrary ? () => {
|
|
4121
|
-
handleLibraryPickMulti(
|
|
4122
|
-
state,
|
|
4123
|
-
element,
|
|
4124
|
-
filesWrapper,
|
|
4125
|
-
pathKey,
|
|
4126
|
-
initialFiles,
|
|
4127
|
-
Infinity,
|
|
4128
|
-
updateFilesList,
|
|
4129
|
-
ctx.instance
|
|
4130
|
-
).catch((err) => {
|
|
4131
|
-
console.error("Library pick failed:", err);
|
|
4132
|
-
});
|
|
4133
|
-
} : null;
|
|
4134
|
-
function updateFilesList() {
|
|
4135
|
-
const currentlyReadonly = isElementReadonly(element, state);
|
|
4136
|
-
renderResourcePills({
|
|
4137
|
-
container: list,
|
|
4138
|
-
rids: initialFiles,
|
|
4139
|
-
state,
|
|
4140
|
-
onRemove: currentlyReadonly ? null : (ridToRemove) => {
|
|
4141
|
-
var _a2;
|
|
4142
|
-
releaseLocalFileUrl((_a2 = state.resourceIndex.get(ridToRemove)) == null ? void 0 : _a2.file);
|
|
4143
|
-
const index = initialFiles.indexOf(ridToRemove);
|
|
4144
|
-
if (index > -1) initialFiles.splice(index, 1);
|
|
4145
|
-
updateFilesList();
|
|
4146
|
-
},
|
|
4147
|
-
hint: filesFieldHint,
|
|
4148
|
-
isReadonly: currentlyReadonly,
|
|
4149
|
-
onLibraryPick: currentlyReadonly ? null : onLibraryPickFiles
|
|
4150
|
-
});
|
|
4151
|
-
}
|
|
4152
|
-
updateFilesList();
|
|
4153
|
-
setupFilesDropHandler(
|
|
4154
|
-
filesContainer,
|
|
4155
|
-
initialFiles,
|
|
4156
|
-
state,
|
|
4157
|
-
updateFilesList,
|
|
4158
|
-
filesConstraints,
|
|
4159
|
-
pathKey,
|
|
4160
|
-
ctx.instance
|
|
4161
|
-
);
|
|
4162
|
-
setupFilesPickerHandler(
|
|
4163
|
-
filesPicker,
|
|
4164
|
-
initialFiles,
|
|
4165
|
-
state,
|
|
4166
|
-
updateFilesList,
|
|
4167
|
-
filesConstraints,
|
|
4168
|
-
pathKey,
|
|
4169
|
-
ctx.instance
|
|
4170
|
-
);
|
|
4171
|
-
}
|
|
4172
|
-
function renderMultipleFileElementEdit(element, ctx, wrapper, pathKey) {
|
|
4173
|
-
var _a, _b, _c, _d, _e, _f;
|
|
4669
|
+
function buildAcceptAttribute(accept) {
|
|
4670
|
+
var _a, _b, _c;
|
|
4671
|
+
if (!accept) return "";
|
|
4672
|
+
if (typeof accept === "string") return accept;
|
|
4673
|
+
return [
|
|
4674
|
+
...(_b = (_a = accept.extensions) == null ? void 0 : _a.map((ext) => `.${ext}`)) != null ? _b : [],
|
|
4675
|
+
...(_c = accept.mime) != null ? _c : []
|
|
4676
|
+
].join(",");
|
|
4677
|
+
}
|
|
4678
|
+
function setupMultiFileEditMode(element, ctx, wrapper, pathKey, maxFiles) {
|
|
4679
|
+
var _a, _b;
|
|
4174
4680
|
const state = ctx.state;
|
|
4175
|
-
const minFiles = (_a = element.minCount) != null ? _a : 0;
|
|
4176
|
-
const maxFiles = (_b = element.maxCount) != null ? _b : Infinity;
|
|
4177
4681
|
const filesWrapper = document.createElement("div");
|
|
4178
4682
|
filesWrapper.className = "space-y-2";
|
|
4179
4683
|
filesWrapper.dataset.filesWrapper = pathKey;
|
|
@@ -4182,15 +4686,9 @@ function renderMultipleFileElementEdit(element, ctx, wrapper, pathKey) {
|
|
|
4182
4686
|
filesPicker.name = pathKey;
|
|
4183
4687
|
filesPicker.multiple = true;
|
|
4184
4688
|
filesPicker.style.display = "none";
|
|
4185
|
-
|
|
4186
|
-
filesPicker.accept = typeof element.accept === "string" ? element.accept : [
|
|
4187
|
-
...(_d = (_c = element.accept.extensions) == null ? void 0 : _c.map((ext) => `.${ext}`)) != null ? _d : [],
|
|
4188
|
-
...(_e = element.accept.mime) != null ? _e : []
|
|
4189
|
-
].join(",") || "";
|
|
4190
|
-
}
|
|
4689
|
+
filesPicker.accept = buildAcceptAttribute(element.accept);
|
|
4191
4690
|
const filesContainer = document.createElement("div");
|
|
4192
4691
|
filesContainer.className = "files-list-wrapper";
|
|
4193
|
-
filesContainer.style.cssText = "border:2px dashed var(--fb-file-upload-border-color,#d1d5db);border-radius:var(--fb-border-radius,0.5rem);padding:8px;transition:border-color var(--fb-transition-duration,200ms),background var(--fb-transition-duration,200ms);";
|
|
4194
4692
|
const list = document.createElement("div");
|
|
4195
4693
|
list.className = "files-list";
|
|
4196
4694
|
filesWrapper.appendChild(filesPicker);
|
|
@@ -4199,19 +4697,18 @@ function renderMultipleFileElementEdit(element, ctx, wrapper, pathKey) {
|
|
|
4199
4697
|
const initialFiles = Array.isArray(ctx.prefill[element.key]) ? [...ctx.prefill[element.key]] : [];
|
|
4200
4698
|
addPrefillFilesToIndex(initialFiles, state.resourceIndex);
|
|
4201
4699
|
filesWrapper.dataset.resourceIds = JSON.stringify(initialFiles);
|
|
4202
|
-
const
|
|
4203
|
-
const multipleConstraints = {
|
|
4700
|
+
const constraints = {
|
|
4204
4701
|
maxCount: maxFiles,
|
|
4205
4702
|
allowedExtensions: getAllowedExtensions(element.accept),
|
|
4206
4703
|
allowedMimes: getAllowedMimes(element.accept),
|
|
4207
|
-
|
|
4704
|
+
// Prefer schema's `maxSize`; fall back to legacy `maxSizeMB` for
|
|
4705
|
+
// backward compatibility (matches addFileSizeHint in validation.ts).
|
|
4706
|
+
maxSize: (_b = (_a = element.maxSize) != null ? _a : element.maxSizeMB) != null ? _b : Infinity
|
|
4208
4707
|
};
|
|
4209
|
-
const
|
|
4210
|
-
|
|
4211
|
-
const minMaxText = minFiles > 0 || maxFiles < Infinity ? ` ${t("fileCountRange", state, { min: minFiles, max: maxFiles })}` : "";
|
|
4212
|
-
return countText + minMaxText;
|
|
4708
|
+
const openPicker = () => {
|
|
4709
|
+
filesPicker.click();
|
|
4213
4710
|
};
|
|
4214
|
-
const
|
|
4711
|
+
const onLibraryPick = state.config.pickExistingFiles && !element.disableLibrary ? () => {
|
|
4215
4712
|
handleLibraryPickMulti(
|
|
4216
4713
|
state,
|
|
4217
4714
|
element,
|
|
@@ -4225,31 +4722,36 @@ function renderMultipleFileElementEdit(element, ctx, wrapper, pathKey) {
|
|
|
4225
4722
|
console.error("Library pick failed:", err);
|
|
4226
4723
|
});
|
|
4227
4724
|
} : null;
|
|
4228
|
-
|
|
4725
|
+
function updateFilesDisplay() {
|
|
4229
4726
|
const currentlyReadonly = isElementReadonly(element, state);
|
|
4230
4727
|
renderResourcePills({
|
|
4231
4728
|
container: list,
|
|
4232
4729
|
rids: initialFiles,
|
|
4233
4730
|
state,
|
|
4234
|
-
onRemove: currentlyReadonly ? null : (
|
|
4731
|
+
onRemove: currentlyReadonly ? null : (ridToRemove) => {
|
|
4235
4732
|
var _a2;
|
|
4236
|
-
releaseLocalFileUrl((_a2 = state.resourceIndex.get(
|
|
4237
|
-
initialFiles.
|
|
4733
|
+
releaseLocalFileUrl((_a2 = state.resourceIndex.get(ridToRemove)) == null ? void 0 : _a2.file);
|
|
4734
|
+
const index = initialFiles.indexOf(ridToRemove);
|
|
4735
|
+
if (index > -1) initialFiles.splice(index, 1);
|
|
4238
4736
|
updateFilesDisplay();
|
|
4239
4737
|
},
|
|
4240
|
-
hint: multipleFilesHint,
|
|
4241
|
-
countInfo: buildCountInfo(),
|
|
4242
4738
|
maxCount: maxFiles < Infinity ? maxFiles : void 0,
|
|
4243
4739
|
isReadonly: currentlyReadonly,
|
|
4244
|
-
onLibraryPick: currentlyReadonly ? null :
|
|
4740
|
+
onLibraryPick: currentlyReadonly ? null : onLibraryPick,
|
|
4741
|
+
element,
|
|
4742
|
+
onClearAll: currentlyReadonly ? void 0 : () => {
|
|
4743
|
+
initialFiles.splice(0);
|
|
4744
|
+
updateFilesDisplay();
|
|
4745
|
+
},
|
|
4746
|
+
openPicker
|
|
4245
4747
|
});
|
|
4246
|
-
}
|
|
4748
|
+
}
|
|
4247
4749
|
setupFilesDropHandler(
|
|
4248
4750
|
filesContainer,
|
|
4249
4751
|
initialFiles,
|
|
4250
4752
|
state,
|
|
4251
4753
|
updateFilesDisplay,
|
|
4252
|
-
|
|
4754
|
+
constraints,
|
|
4253
4755
|
pathKey,
|
|
4254
4756
|
ctx.instance
|
|
4255
4757
|
);
|
|
@@ -4258,13 +4760,26 @@ function renderMultipleFileElementEdit(element, ctx, wrapper, pathKey) {
|
|
|
4258
4760
|
initialFiles,
|
|
4259
4761
|
state,
|
|
4260
4762
|
updateFilesDisplay,
|
|
4261
|
-
|
|
4763
|
+
constraints,
|
|
4262
4764
|
pathKey,
|
|
4263
4765
|
ctx.instance
|
|
4264
4766
|
);
|
|
4265
4767
|
updateFilesDisplay();
|
|
4266
4768
|
wrapper.appendChild(filesWrapper);
|
|
4267
4769
|
}
|
|
4770
|
+
function renderFilesElementEdit(element, ctx, wrapper, pathKey) {
|
|
4771
|
+
setupMultiFileEditMode(element, ctx, wrapper, pathKey, Infinity);
|
|
4772
|
+
}
|
|
4773
|
+
function renderMultipleFileElementEdit(element, ctx, wrapper, pathKey) {
|
|
4774
|
+
var _a;
|
|
4775
|
+
setupMultiFileEditMode(
|
|
4776
|
+
element,
|
|
4777
|
+
ctx,
|
|
4778
|
+
wrapper,
|
|
4779
|
+
pathKey,
|
|
4780
|
+
(_a = element.maxCount) != null ? _a : Infinity
|
|
4781
|
+
);
|
|
4782
|
+
}
|
|
4268
4783
|
|
|
4269
4784
|
// src/components/file/validate.ts
|
|
4270
4785
|
function readMultiFileResourceIds(scopeRoot, fullKey) {
|
|
@@ -4391,33 +4906,36 @@ function renderFileElementReadonly(element, ctx, wrapper, pathKey) {
|
|
|
4391
4906
|
hiddenInput.name = pathKey;
|
|
4392
4907
|
hiddenInput.value = initial;
|
|
4393
4908
|
wrapper.appendChild(hiddenInput);
|
|
4394
|
-
renderFilePreviewReadonly(initial, state).then((
|
|
4395
|
-
|
|
4396
|
-
|
|
4397
|
-
|
|
4398
|
-
|
|
4399
|
-
|
|
4909
|
+
renderFilePreviewReadonly(initial, state).then((tile) => {
|
|
4910
|
+
tile.classList.add(
|
|
4911
|
+
"fb-single-readonly-filled",
|
|
4912
|
+
"fb-readonly-tile",
|
|
4913
|
+
"fb-checker"
|
|
4914
|
+
);
|
|
4915
|
+
wrapper.appendChild(tile);
|
|
4916
|
+
}).catch(console.error);
|
|
4400
4917
|
} else {
|
|
4401
4918
|
wrapper.appendChild(buildEmptyReadonlyTile(state));
|
|
4402
4919
|
}
|
|
4403
4920
|
}
|
|
4404
4921
|
function buildEmptyReadonlyTile(state) {
|
|
4922
|
+
ensureFileStyles();
|
|
4405
4923
|
const emptyState = document.createElement("div");
|
|
4406
4924
|
emptyState.style.cssText = `
|
|
4407
|
-
|
|
4408
|
-
height:${TILE_SIZE};
|
|
4925
|
+
height: 220px;
|
|
4409
4926
|
display:flex;
|
|
4410
4927
|
align-items:center;
|
|
4411
4928
|
justify-content:center;
|
|
4412
|
-
background:
|
|
4413
|
-
border-radius:
|
|
4414
|
-
border:1px solid
|
|
4929
|
+
background: repeating-linear-gradient(45deg, #fafafa 0 6px, #f3f4f6 6px 12px);
|
|
4930
|
+
border-radius:0.75rem;
|
|
4931
|
+
border:1px solid #e2e8f0;
|
|
4415
4932
|
`;
|
|
4416
4933
|
emptyState.innerHTML = `<div style="font-size:11px;text-align:center;color:var(--fb-text-secondary-color,#6b7280);">${escapeHtml(t("noFileSelected", state))}</div>`;
|
|
4417
4934
|
return emptyState;
|
|
4418
4935
|
}
|
|
4419
|
-
function renderMultiFileReadonly(rids, state, wrapper, pathKey,
|
|
4936
|
+
function renderMultiFileReadonly(rids, state, wrapper, pathKey, _marginTop) {
|
|
4420
4937
|
addPrefillFilesToIndex(rids, state.resourceIndex);
|
|
4938
|
+
ensureFileStyles();
|
|
4421
4939
|
const filesWrapper = document.createElement("div");
|
|
4422
4940
|
filesWrapper.dataset.filesWrapper = pathKey;
|
|
4423
4941
|
filesWrapper.dataset.resourceIds = JSON.stringify(rids);
|
|
@@ -4429,22 +4947,32 @@ function renderMultiFileReadonly(rids, state, wrapper, pathKey, marginTop) {
|
|
|
4429
4947
|
filesWrapper.appendChild(emptyEl);
|
|
4430
4948
|
return;
|
|
4431
4949
|
}
|
|
4432
|
-
const
|
|
4433
|
-
|
|
4434
|
-
filesWrapper.appendChild(
|
|
4950
|
+
const grid = document.createElement("div");
|
|
4951
|
+
grid.className = "fb-multi-readonly-grid";
|
|
4952
|
+
filesWrapper.appendChild(grid);
|
|
4435
4953
|
const placeholders = rids.map(() => {
|
|
4436
|
-
const
|
|
4437
|
-
|
|
4438
|
-
|
|
4439
|
-
return
|
|
4954
|
+
const ph = document.createElement("div");
|
|
4955
|
+
ph.className = "fb-readonly-tile fb-checker fb-tile";
|
|
4956
|
+
grid.appendChild(ph);
|
|
4957
|
+
return ph;
|
|
4440
4958
|
});
|
|
4441
4959
|
for (let i = 0; i < rids.length; i++) {
|
|
4442
4960
|
const resourceId = rids[i];
|
|
4443
4961
|
const placeholder = placeholders[i];
|
|
4444
|
-
|
|
4445
|
-
|
|
4446
|
-
|
|
4447
|
-
|
|
4962
|
+
const meta = state.resourceIndex.get(resourceId);
|
|
4963
|
+
renderFilePreviewReadonly(resourceId, state, meta == null ? void 0 : meta.name).then((tile) => {
|
|
4964
|
+
tile.classList.add(
|
|
4965
|
+
"fb-readonly-tile",
|
|
4966
|
+
"fb-checker",
|
|
4967
|
+
"fb-tile-resource"
|
|
4968
|
+
);
|
|
4969
|
+
tile.dataset.resourceId = resourceId;
|
|
4970
|
+
placeholder.replaceWith(tile);
|
|
4971
|
+
}).catch(() => {
|
|
4972
|
+
const tile = document.createElement("div");
|
|
4973
|
+
tile.className = "fb-readonly-tile fb-checker fb-tile fb-tile-resource";
|
|
4974
|
+
tile.dataset.resourceId = resourceId;
|
|
4975
|
+
placeholder.replaceWith(tile);
|
|
4448
4976
|
});
|
|
4449
4977
|
}
|
|
4450
4978
|
}
|
|
@@ -4456,7 +4984,7 @@ function renderFilesElementReadonly(element, ctx, wrapper, pathKey) {
|
|
|
4456
4984
|
function renderMultipleFileElementReadonly(element, ctx, wrapper, pathKey) {
|
|
4457
4985
|
const rawPrefill = ctx.prefill[element.key];
|
|
4458
4986
|
const initialFiles = Array.isArray(rawPrefill) ? rawPrefill : [];
|
|
4459
|
-
renderMultiFileReadonly(initialFiles, ctx.state, wrapper, pathKey
|
|
4987
|
+
renderMultiFileReadonly(initialFiles, ctx.state, wrapper, pathKey);
|
|
4460
4988
|
}
|
|
4461
4989
|
|
|
4462
4990
|
// src/components/file.ts
|
|
@@ -4788,51 +5316,25 @@ function renderMultipleColourElement(element, ctx, wrapper, pathKey) {
|
|
|
4788
5316
|
removeBtn.style.pointerEvents = disabled ? "none" : "auto";
|
|
4789
5317
|
});
|
|
4790
5318
|
}
|
|
4791
|
-
let
|
|
4792
|
-
let countDisplay = null;
|
|
5319
|
+
let addUpdate = null;
|
|
4793
5320
|
if (!readonly) {
|
|
4794
|
-
|
|
4795
|
-
|
|
4796
|
-
|
|
4797
|
-
|
|
4798
|
-
|
|
4799
|
-
|
|
4800
|
-
|
|
4801
|
-
|
|
4802
|
-
|
|
4803
|
-
|
|
4804
|
-
|
|
4805
|
-
|
|
4806
|
-
|
|
4807
|
-
|
|
4808
|
-
addBtn.style.backgroundColor = "var(--fb-background-hover-color)";
|
|
4809
|
-
});
|
|
4810
|
-
addBtn.addEventListener("mouseleave", () => {
|
|
4811
|
-
addBtn.style.backgroundColor = "transparent";
|
|
4812
|
-
});
|
|
4813
|
-
addBtn.onclick = () => {
|
|
4814
|
-
const defaultColour = element.default || "#000000";
|
|
4815
|
-
values.push(defaultColour);
|
|
4816
|
-
addColourItem(defaultColour);
|
|
4817
|
-
updateAddButton();
|
|
4818
|
-
updateRemoveButtons();
|
|
4819
|
-
};
|
|
4820
|
-
countDisplay = document.createElement("span");
|
|
4821
|
-
countDisplay.className = "text-sm text-gray-500";
|
|
4822
|
-
addRow.appendChild(addBtn);
|
|
4823
|
-
addRow.appendChild(countDisplay);
|
|
4824
|
-
wrapper.appendChild(addRow);
|
|
5321
|
+
const handle = createAddItemRow(
|
|
5322
|
+
"colour",
|
|
5323
|
+
() => {
|
|
5324
|
+
const defaultColour = element.default || "#000000";
|
|
5325
|
+
values.push(defaultColour);
|
|
5326
|
+
addColourItem(defaultColour);
|
|
5327
|
+
updateAddButton();
|
|
5328
|
+
updateRemoveButtons();
|
|
5329
|
+
},
|
|
5330
|
+
{ label: element.addLabel }
|
|
5331
|
+
);
|
|
5332
|
+
addUpdate = handle.update;
|
|
5333
|
+
mountCounterInLabel(wrapper, handle.counter);
|
|
5334
|
+
wrapper.appendChild(handle.row);
|
|
4825
5335
|
}
|
|
4826
5336
|
function updateAddButton() {
|
|
4827
|
-
if (
|
|
4828
|
-
const addBtn = addRow.querySelector(".add-colour-btn");
|
|
4829
|
-
if (addBtn) {
|
|
4830
|
-
const disabled = values.length >= maxCount;
|
|
4831
|
-
addBtn.disabled = disabled;
|
|
4832
|
-
addBtn.style.opacity = disabled ? "0.5" : "1";
|
|
4833
|
-
addBtn.style.pointerEvents = disabled ? "none" : "auto";
|
|
4834
|
-
}
|
|
4835
|
-
countDisplay.textContent = `${values.length}/${maxCount === Infinity ? "\u221E" : maxCount}`;
|
|
5337
|
+
if (addUpdate) addUpdate(values.length, maxCount);
|
|
4836
5338
|
}
|
|
4837
5339
|
values.forEach((value) => addColourItem(value));
|
|
4838
5340
|
updateAddButton();
|
|
@@ -5274,50 +5776,24 @@ function renderMultipleSliderElement(element, ctx, wrapper, pathKey) {
|
|
|
5274
5776
|
removeBtn.style.pointerEvents = disabled ? "none" : "auto";
|
|
5275
5777
|
});
|
|
5276
5778
|
}
|
|
5277
|
-
let
|
|
5278
|
-
let countDisplay = null;
|
|
5779
|
+
let addUpdate = null;
|
|
5279
5780
|
if (!readonly) {
|
|
5280
|
-
|
|
5281
|
-
|
|
5282
|
-
|
|
5283
|
-
|
|
5284
|
-
|
|
5285
|
-
|
|
5286
|
-
|
|
5287
|
-
|
|
5288
|
-
|
|
5289
|
-
|
|
5290
|
-
|
|
5291
|
-
|
|
5292
|
-
|
|
5293
|
-
addBtn.addEventListener("mouseenter", () => {
|
|
5294
|
-
addBtn.style.backgroundColor = "var(--fb-background-hover-color)";
|
|
5295
|
-
});
|
|
5296
|
-
addBtn.addEventListener("mouseleave", () => {
|
|
5297
|
-
addBtn.style.backgroundColor = "transparent";
|
|
5298
|
-
});
|
|
5299
|
-
addBtn.onclick = () => {
|
|
5300
|
-
values.push(defaultValue);
|
|
5301
|
-
addSliderItem(defaultValue);
|
|
5302
|
-
updateAddButton();
|
|
5303
|
-
updateRemoveButtons();
|
|
5304
|
-
};
|
|
5305
|
-
countDisplay = document.createElement("span");
|
|
5306
|
-
countDisplay.className = "text-sm text-gray-500";
|
|
5307
|
-
addRow.appendChild(addBtn);
|
|
5308
|
-
addRow.appendChild(countDisplay);
|
|
5309
|
-
wrapper.appendChild(addRow);
|
|
5781
|
+
const handle = createAddItemRow(
|
|
5782
|
+
"slider",
|
|
5783
|
+
() => {
|
|
5784
|
+
values.push(defaultValue);
|
|
5785
|
+
addSliderItem(defaultValue);
|
|
5786
|
+
updateAddButton();
|
|
5787
|
+
updateRemoveButtons();
|
|
5788
|
+
},
|
|
5789
|
+
{ label: element.addLabel }
|
|
5790
|
+
);
|
|
5791
|
+
addUpdate = handle.update;
|
|
5792
|
+
mountCounterInLabel(wrapper, handle.counter);
|
|
5793
|
+
wrapper.appendChild(handle.row);
|
|
5310
5794
|
}
|
|
5311
5795
|
function updateAddButton() {
|
|
5312
|
-
if (
|
|
5313
|
-
const addBtn = addRow.querySelector(".add-slider-btn");
|
|
5314
|
-
if (addBtn) {
|
|
5315
|
-
const disabled = values.length >= maxCount;
|
|
5316
|
-
addBtn.disabled = disabled;
|
|
5317
|
-
addBtn.style.opacity = disabled ? "0.5" : "1";
|
|
5318
|
-
addBtn.style.pointerEvents = disabled ? "none" : "auto";
|
|
5319
|
-
}
|
|
5320
|
-
countDisplay.textContent = `${values.length}/${maxCount === Infinity ? "\u221E" : maxCount}`;
|
|
5796
|
+
if (addUpdate) addUpdate(values.length, maxCount);
|
|
5321
5797
|
}
|
|
5322
5798
|
values.forEach((value) => addSliderItem(value));
|
|
5323
5799
|
updateAddButton();
|
|
@@ -5604,7 +6080,7 @@ function createPrefillHints(element, pathKey) {
|
|
|
5604
6080
|
return null;
|
|
5605
6081
|
}
|
|
5606
6082
|
const hintsContainer = document.createElement("div");
|
|
5607
|
-
hintsContainer.className = "fb-prefill-hints flex flex-wrap gap-2 mb-
|
|
6083
|
+
hintsContainer.className = "fb-prefill-hints flex flex-wrap gap-2 mb-2";
|
|
5608
6084
|
element.prefillHints.forEach((hint, index) => {
|
|
5609
6085
|
const hintButton = document.createElement("button");
|
|
5610
6086
|
hintButton.type = "button";
|
|
@@ -5620,14 +6096,14 @@ function createPrefillHints(element, pathKey) {
|
|
|
5620
6096
|
function renderSingleContainerElement(element, ctx, wrapper, pathKey) {
|
|
5621
6097
|
var _a, _b;
|
|
5622
6098
|
const containerWrap = document.createElement("div");
|
|
5623
|
-
containerWrap.className = "border border-gray-200 rounded-lg p-
|
|
6099
|
+
containerWrap.className = "border border-gray-200 rounded-lg p-2 bg-gray-50";
|
|
5624
6100
|
containerWrap.setAttribute("data-container", pathKey);
|
|
5625
6101
|
const itemsWrap = document.createElement("div");
|
|
5626
6102
|
const columns = element.columns || 1;
|
|
5627
6103
|
if (columns === 1) {
|
|
5628
|
-
itemsWrap.className = "space-y-
|
|
6104
|
+
itemsWrap.className = "space-y-2";
|
|
5629
6105
|
} else {
|
|
5630
|
-
itemsWrap.className = `grid grid-cols-${columns} gap-
|
|
6106
|
+
itemsWrap.className = `grid grid-cols-${columns} gap-2`;
|
|
5631
6107
|
}
|
|
5632
6108
|
const containerIsReadonly = isElementReadonly(element, ctx.state, ctx);
|
|
5633
6109
|
if (!containerIsReadonly) {
|
|
@@ -5662,17 +6138,71 @@ function renderSingleContainerElement(element, ctx, wrapper, pathKey) {
|
|
|
5662
6138
|
containerWrap.appendChild(itemsWrap);
|
|
5663
6139
|
wrapper.appendChild(containerWrap);
|
|
5664
6140
|
}
|
|
6141
|
+
function getChildWrapperClass(isSlides, columns) {
|
|
6142
|
+
if (isSlides) {
|
|
6143
|
+
return "space-y-2";
|
|
6144
|
+
}
|
|
6145
|
+
const cols = columns || 1;
|
|
6146
|
+
return cols === 1 ? "space-y-2" : `grid grid-cols-${cols} gap-2`;
|
|
6147
|
+
}
|
|
6148
|
+
function mountRemoveButton(item, onRemove) {
|
|
6149
|
+
const rem = document.createElement("button");
|
|
6150
|
+
rem.type = "button";
|
|
6151
|
+
rem.className = "fb-item-remove";
|
|
6152
|
+
rem.style.cssText = `
|
|
6153
|
+
width: 22px;
|
|
6154
|
+
height: 22px;
|
|
6155
|
+
display: inline-flex;
|
|
6156
|
+
align-items: center;
|
|
6157
|
+
justify-content: center;
|
|
6158
|
+
padding: 0;
|
|
6159
|
+
line-height: 1;
|
|
6160
|
+
font-size: 14px;
|
|
6161
|
+
color: var(--fb-error-color);
|
|
6162
|
+
background-color: transparent;
|
|
6163
|
+
border: 0;
|
|
6164
|
+
border-radius: 4px;
|
|
6165
|
+
cursor: pointer;
|
|
6166
|
+
flex-shrink: 0;
|
|
6167
|
+
transition: background-color var(--fb-transition-duration);
|
|
6168
|
+
`;
|
|
6169
|
+
rem.textContent = "\u2715";
|
|
6170
|
+
rem.addEventListener("mouseenter", () => {
|
|
6171
|
+
rem.style.backgroundColor = "var(--fb-background-hover-color)";
|
|
6172
|
+
});
|
|
6173
|
+
rem.addEventListener("mouseleave", () => {
|
|
6174
|
+
rem.style.backgroundColor = "transparent";
|
|
6175
|
+
});
|
|
6176
|
+
rem.onclick = onRemove;
|
|
6177
|
+
const labelRow = item.querySelector("[data-fb-label-row]");
|
|
6178
|
+
if (labelRow) {
|
|
6179
|
+
rem.style.marginLeft = "auto";
|
|
6180
|
+
labelRow.appendChild(rem);
|
|
6181
|
+
return;
|
|
6182
|
+
}
|
|
6183
|
+
rem.style.position = "absolute";
|
|
6184
|
+
rem.style.top = "8px";
|
|
6185
|
+
rem.style.right = "8px";
|
|
6186
|
+
item.style.position = "relative";
|
|
6187
|
+
item.appendChild(rem);
|
|
6188
|
+
}
|
|
5665
6189
|
function renderMultipleContainerElement(element, ctx, wrapper, _pathKey) {
|
|
5666
6190
|
var _a, _b, _c, _d;
|
|
5667
6191
|
const state = ctx.state;
|
|
5668
6192
|
const containerIsReadonly = isElementReadonly(element, state, ctx);
|
|
5669
6193
|
const childInheritedReadonly = containerIsReadonly || ctx.inheritedReadonly;
|
|
5670
6194
|
const containerWrap = document.createElement("div");
|
|
5671
|
-
containerWrap.className = "border border-gray-200 rounded-lg p-
|
|
5672
|
-
const countDisplay = document.createElement("span");
|
|
5673
|
-
countDisplay.className = "text-sm text-gray-500";
|
|
6195
|
+
containerWrap.className = "border border-gray-200 rounded-lg p-2 bg-gray-50";
|
|
5674
6196
|
const itemsWrap = document.createElement("div");
|
|
5675
|
-
|
|
6197
|
+
const isSlides = element.displayMode === "slides";
|
|
6198
|
+
if (isSlides) {
|
|
6199
|
+
itemsWrap.className = "fb-container-slides";
|
|
6200
|
+
const slideCols = element.columns;
|
|
6201
|
+
const gridTemplateColumns = typeof slideCols === "number" && slideCols > 0 ? `repeat(${slideCols}, 1fr)` : "repeat(auto-fit, minmax(280px, 1fr))";
|
|
6202
|
+
itemsWrap.style.cssText = `display:grid;grid-template-columns:${gridTemplateColumns};gap:8px;align-items:start;`;
|
|
6203
|
+
} else {
|
|
6204
|
+
itemsWrap.className = "space-y-2";
|
|
6205
|
+
}
|
|
5676
6206
|
if (!containerIsReadonly) {
|
|
5677
6207
|
const hintsElement = createPrefillHints(element, element.key);
|
|
5678
6208
|
if (hintsElement) {
|
|
@@ -5684,98 +6214,65 @@ function renderMultipleContainerElement(element, ctx, wrapper, _pathKey) {
|
|
|
5684
6214
|
const pre = Array.isArray((_c = ctx.prefill) == null ? void 0 : _c[element.key]) ? ctx.prefill[element.key] : null;
|
|
5685
6215
|
const childDefaults = extractChildDefaults(element.elements);
|
|
5686
6216
|
const countItems = () => itemsWrap.querySelectorAll(":scope > .containerItem").length;
|
|
5687
|
-
const
|
|
5688
|
-
|
|
5689
|
-
|
|
5690
|
-
|
|
5691
|
-
|
|
5692
|
-
|
|
5693
|
-
|
|
5694
|
-
|
|
5695
|
-
|
|
5696
|
-
|
|
5697
|
-
|
|
5698
|
-
|
|
5699
|
-
add.addEventListener("mouseenter", () => {
|
|
5700
|
-
add.style.backgroundColor = "var(--fb-background-hover-color)";
|
|
5701
|
-
});
|
|
5702
|
-
add.addEventListener("mouseleave", () => {
|
|
5703
|
-
add.style.backgroundColor = "transparent";
|
|
5704
|
-
});
|
|
5705
|
-
add.onclick = () => {
|
|
5706
|
-
if (countItems() < max) {
|
|
5707
|
-
const idx = countItems();
|
|
5708
|
-
const currentFormData = state.formRoot ? extractRootFormData(state.formRoot) : {};
|
|
5709
|
-
const subCtx = {
|
|
5710
|
-
state: ctx.state,
|
|
5711
|
-
path: pathJoin(ctx.path, `${element.key}[${idx}]`),
|
|
5712
|
-
prefill: childDefaults,
|
|
5713
|
-
// Defaults for enableIf evaluation
|
|
5714
|
-
formData: currentFormData,
|
|
5715
|
-
// Current root data from DOM for enableIf
|
|
5716
|
-
inheritedReadonly: childInheritedReadonly
|
|
5717
|
-
};
|
|
5718
|
-
const item = document.createElement("div");
|
|
5719
|
-
item.className = "containerItem border border-gray-300 rounded-lg p-4 bg-white";
|
|
5720
|
-
item.setAttribute("data-container-item", `${element.key}[${idx}]`);
|
|
5721
|
-
const childWrapper = document.createElement("div");
|
|
5722
|
-
const columns = element.columns || 1;
|
|
5723
|
-
if (columns === 1) {
|
|
5724
|
-
childWrapper.className = "space-y-4";
|
|
5725
|
-
} else {
|
|
5726
|
-
childWrapper.className = `grid grid-cols-${columns} gap-4`;
|
|
5727
|
-
}
|
|
5728
|
-
element.elements.forEach((child) => {
|
|
5729
|
-
var _a2;
|
|
5730
|
-
if (child.type !== "markdown" && (child.hidden || child.type === "hidden")) {
|
|
5731
|
-
childWrapper.appendChild(
|
|
5732
|
-
createHiddenInput(
|
|
5733
|
-
pathJoin(subCtx.path, child.key),
|
|
5734
|
-
(_a2 = "default" in child ? child.default : null) != null ? _a2 : null
|
|
5735
|
-
)
|
|
5736
|
-
);
|
|
5737
|
-
} else {
|
|
5738
|
-
childWrapper.appendChild(renderElement(child, subCtx));
|
|
5739
|
-
}
|
|
5740
|
-
});
|
|
5741
|
-
item.appendChild(childWrapper);
|
|
5742
|
-
if (!containerIsReadonly) {
|
|
5743
|
-
const rem = document.createElement("button");
|
|
5744
|
-
rem.type = "button";
|
|
5745
|
-
rem.className = "absolute top-2 right-2 px-2 py-1 rounded";
|
|
5746
|
-
rem.style.cssText = `
|
|
5747
|
-
color: var(--fb-error-color);
|
|
5748
|
-
background-color: transparent;
|
|
5749
|
-
transition: background-color var(--fb-transition-duration);
|
|
5750
|
-
`;
|
|
5751
|
-
rem.textContent = "\u2715";
|
|
5752
|
-
rem.addEventListener("mouseenter", () => {
|
|
5753
|
-
rem.style.backgroundColor = "var(--fb-background-hover-color)";
|
|
5754
|
-
});
|
|
5755
|
-
rem.addEventListener("mouseleave", () => {
|
|
5756
|
-
rem.style.backgroundColor = "transparent";
|
|
5757
|
-
});
|
|
5758
|
-
rem.onclick = () => handleRemoveItem(item);
|
|
5759
|
-
item.style.position = "relative";
|
|
5760
|
-
item.appendChild(rem);
|
|
5761
|
-
}
|
|
5762
|
-
itemsWrap.appendChild(item);
|
|
5763
|
-
updateAddButton();
|
|
5764
|
-
}
|
|
6217
|
+
const handleAddItem = () => {
|
|
6218
|
+
if (countItems() >= max) return;
|
|
6219
|
+
const idx = countItems();
|
|
6220
|
+
const currentFormData = state.formRoot ? extractRootFormData(state.formRoot) : {};
|
|
6221
|
+
const subCtx = {
|
|
6222
|
+
state: ctx.state,
|
|
6223
|
+
path: pathJoin(ctx.path, `${element.key}[${idx}]`),
|
|
6224
|
+
prefill: childDefaults,
|
|
6225
|
+
// Defaults for enableIf evaluation
|
|
6226
|
+
formData: currentFormData,
|
|
6227
|
+
// Current root data from DOM for enableIf
|
|
6228
|
+
inheritedReadonly: childInheritedReadonly
|
|
5765
6229
|
};
|
|
5766
|
-
|
|
6230
|
+
const item = document.createElement("div");
|
|
6231
|
+
item.className = "containerItem border border-gray-300 rounded-lg p-2 bg-white";
|
|
6232
|
+
item.setAttribute("data-container-item", `${element.key}[${idx}]`);
|
|
6233
|
+
const childWrapper = document.createElement("div");
|
|
6234
|
+
childWrapper.className = getChildWrapperClass(isSlides, element.columns);
|
|
6235
|
+
element.elements.forEach((child) => {
|
|
6236
|
+
var _a2;
|
|
6237
|
+
if (child.type !== "markdown" && (child.hidden || child.type === "hidden")) {
|
|
6238
|
+
childWrapper.appendChild(
|
|
6239
|
+
createHiddenInput(
|
|
6240
|
+
pathJoin(subCtx.path, child.key),
|
|
6241
|
+
(_a2 = "default" in child ? child.default : null) != null ? _a2 : null
|
|
6242
|
+
)
|
|
6243
|
+
);
|
|
6244
|
+
} else {
|
|
6245
|
+
childWrapper.appendChild(renderElement(child, subCtx));
|
|
6246
|
+
}
|
|
6247
|
+
});
|
|
6248
|
+
item.appendChild(childWrapper);
|
|
6249
|
+
if (!containerIsReadonly) {
|
|
6250
|
+
mountRemoveButton(item, () => handleRemoveItem(item));
|
|
6251
|
+
}
|
|
6252
|
+
if (slideAddTile && slideAddTile.parentElement === itemsWrap) {
|
|
6253
|
+
itemsWrap.insertBefore(item, slideAddTile);
|
|
6254
|
+
} else {
|
|
6255
|
+
itemsWrap.appendChild(item);
|
|
6256
|
+
}
|
|
6257
|
+
updateAddButton();
|
|
5767
6258
|
};
|
|
5768
|
-
|
|
5769
|
-
|
|
5770
|
-
|
|
5771
|
-
|
|
6259
|
+
let slideAddTile = null;
|
|
6260
|
+
let slideAddUpdate = null;
|
|
6261
|
+
let pillAddUpdate = null;
|
|
6262
|
+
const syncSlideTileSize = () => {
|
|
6263
|
+
if (!slideAddTile) return;
|
|
6264
|
+
const firstSlide = itemsWrap.querySelector(
|
|
6265
|
+
":scope > .containerItem"
|
|
5772
6266
|
);
|
|
5773
|
-
if (
|
|
5774
|
-
|
|
5775
|
-
existingAddBtn.style.opacity = currentCount >= max ? "0.5" : "1";
|
|
5776
|
-
existingAddBtn.style.pointerEvents = currentCount >= max ? "none" : "auto";
|
|
6267
|
+
if (firstSlide && firstSlide.offsetHeight > 0) {
|
|
6268
|
+
slideAddTile.style.minHeight = `${firstSlide.offsetHeight}px`;
|
|
5777
6269
|
}
|
|
5778
|
-
|
|
6270
|
+
};
|
|
6271
|
+
const updateAddButton = () => {
|
|
6272
|
+
const currentCount = countItems();
|
|
6273
|
+
if (slideAddUpdate) slideAddUpdate(currentCount, max);
|
|
6274
|
+
if (pillAddUpdate) pillAddUpdate(currentCount, max);
|
|
6275
|
+
if (slideAddTile) syncSlideTileSize();
|
|
5779
6276
|
};
|
|
5780
6277
|
const handleRemoveItem = (item) => {
|
|
5781
6278
|
item.remove();
|
|
@@ -5795,14 +6292,18 @@ function renderMultipleContainerElement(element, ctx, wrapper, _pathKey) {
|
|
|
5795
6292
|
inheritedReadonly: childInheritedReadonly
|
|
5796
6293
|
};
|
|
5797
6294
|
const item = document.createElement("div");
|
|
5798
|
-
item.className = "containerItem border border-gray-300 rounded-lg p-
|
|
6295
|
+
item.className = "containerItem border border-gray-300 rounded-lg p-2 bg-white";
|
|
5799
6296
|
item.setAttribute("data-container-item", `${element.key}[${idx}]`);
|
|
5800
6297
|
const childWrapper = document.createElement("div");
|
|
5801
|
-
|
|
5802
|
-
|
|
5803
|
-
childWrapper.className = "space-y-4";
|
|
6298
|
+
if (isSlides) {
|
|
6299
|
+
childWrapper.className = "space-y-2";
|
|
5804
6300
|
} else {
|
|
5805
|
-
|
|
6301
|
+
const columns = element.columns || 1;
|
|
6302
|
+
if (columns === 1) {
|
|
6303
|
+
childWrapper.className = "space-y-2";
|
|
6304
|
+
} else {
|
|
6305
|
+
childWrapper.className = `grid grid-cols-${columns} gap-2`;
|
|
6306
|
+
}
|
|
5806
6307
|
}
|
|
5807
6308
|
element.elements.forEach((child) => {
|
|
5808
6309
|
var _a3, _b2;
|
|
@@ -5817,24 +6318,7 @@ function renderMultipleContainerElement(element, ctx, wrapper, _pathKey) {
|
|
|
5817
6318
|
});
|
|
5818
6319
|
item.appendChild(childWrapper);
|
|
5819
6320
|
if (!containerIsReadonly) {
|
|
5820
|
-
|
|
5821
|
-
rem.type = "button";
|
|
5822
|
-
rem.className = "absolute top-2 right-2 px-2 py-1 rounded";
|
|
5823
|
-
rem.style.cssText = `
|
|
5824
|
-
color: var(--fb-error-color);
|
|
5825
|
-
background-color: transparent;
|
|
5826
|
-
transition: background-color var(--fb-transition-duration);
|
|
5827
|
-
`;
|
|
5828
|
-
rem.textContent = "\u2715";
|
|
5829
|
-
rem.addEventListener("mouseenter", () => {
|
|
5830
|
-
rem.style.backgroundColor = "var(--fb-background-hover-color)";
|
|
5831
|
-
});
|
|
5832
|
-
rem.addEventListener("mouseleave", () => {
|
|
5833
|
-
rem.style.backgroundColor = "transparent";
|
|
5834
|
-
});
|
|
5835
|
-
rem.onclick = () => handleRemoveItem(item);
|
|
5836
|
-
item.style.position = "relative";
|
|
5837
|
-
item.appendChild(rem);
|
|
6321
|
+
mountRemoveButton(item, () => handleRemoveItem(item));
|
|
5838
6322
|
}
|
|
5839
6323
|
itemsWrap.appendChild(item);
|
|
5840
6324
|
});
|
|
@@ -5852,14 +6336,18 @@ function renderMultipleContainerElement(element, ctx, wrapper, _pathKey) {
|
|
|
5852
6336
|
inheritedReadonly: childInheritedReadonly
|
|
5853
6337
|
};
|
|
5854
6338
|
const item = document.createElement("div");
|
|
5855
|
-
item.className = "containerItem border border-gray-300 rounded-lg p-
|
|
6339
|
+
item.className = "containerItem border border-gray-300 rounded-lg p-2 bg-white";
|
|
5856
6340
|
item.setAttribute("data-container-item", `${element.key}[${idx}]`);
|
|
5857
6341
|
const childWrapper = document.createElement("div");
|
|
5858
|
-
|
|
5859
|
-
|
|
5860
|
-
childWrapper.className = "space-y-4";
|
|
6342
|
+
if (isSlides) {
|
|
6343
|
+
childWrapper.className = "space-y-2";
|
|
5861
6344
|
} else {
|
|
5862
|
-
|
|
6345
|
+
const columns = element.columns || 1;
|
|
6346
|
+
if (columns === 1) {
|
|
6347
|
+
childWrapper.className = "space-y-2";
|
|
6348
|
+
} else {
|
|
6349
|
+
childWrapper.className = `grid grid-cols-${columns} gap-2`;
|
|
6350
|
+
}
|
|
5863
6351
|
}
|
|
5864
6352
|
element.elements.forEach((child) => {
|
|
5865
6353
|
var _a2;
|
|
@@ -5875,41 +6363,43 @@ function renderMultipleContainerElement(element, ctx, wrapper, _pathKey) {
|
|
|
5875
6363
|
}
|
|
5876
6364
|
});
|
|
5877
6365
|
item.appendChild(childWrapper);
|
|
5878
|
-
|
|
5879
|
-
rem.type = "button";
|
|
5880
|
-
rem.className = "absolute top-2 right-2 px-2 py-1 rounded";
|
|
5881
|
-
rem.style.cssText = `
|
|
5882
|
-
color: var(--fb-error-color);
|
|
5883
|
-
background-color: transparent;
|
|
5884
|
-
transition: background-color var(--fb-transition-duration);
|
|
5885
|
-
`;
|
|
5886
|
-
rem.textContent = "\u2715";
|
|
5887
|
-
rem.addEventListener("mouseenter", () => {
|
|
5888
|
-
rem.style.backgroundColor = "var(--fb-background-hover-color)";
|
|
5889
|
-
});
|
|
5890
|
-
rem.addEventListener("mouseleave", () => {
|
|
5891
|
-
rem.style.backgroundColor = "transparent";
|
|
5892
|
-
});
|
|
5893
|
-
rem.onclick = () => {
|
|
6366
|
+
mountRemoveButton(item, () => {
|
|
5894
6367
|
if (countItems() > min) {
|
|
5895
6368
|
handleRemoveItem(item);
|
|
5896
6369
|
}
|
|
5897
|
-
};
|
|
5898
|
-
item.style.position = "relative";
|
|
5899
|
-
item.appendChild(rem);
|
|
6370
|
+
});
|
|
5900
6371
|
itemsWrap.appendChild(item);
|
|
5901
6372
|
}
|
|
5902
6373
|
}
|
|
5903
6374
|
containerWrap.appendChild(itemsWrap);
|
|
5904
6375
|
if (!containerIsReadonly) {
|
|
5905
|
-
|
|
5906
|
-
|
|
5907
|
-
|
|
5908
|
-
|
|
5909
|
-
|
|
6376
|
+
if (isSlides) {
|
|
6377
|
+
itemsWrap.style.alignItems = "stretch";
|
|
6378
|
+
const handle = createSlideAddTile(handleAddItem, {
|
|
6379
|
+
label: element.addLabel
|
|
6380
|
+
});
|
|
6381
|
+
slideAddTile = handle.tile;
|
|
6382
|
+
slideAddUpdate = handle.update;
|
|
6383
|
+
mountCounterInLabel(wrapper, handle.counter);
|
|
6384
|
+
itemsWrap.appendChild(handle.tile);
|
|
6385
|
+
} else {
|
|
6386
|
+
const handle = createAddItemRow("container", handleAddItem, {
|
|
6387
|
+
label: element.addLabel
|
|
6388
|
+
});
|
|
6389
|
+
pillAddUpdate = handle.update;
|
|
6390
|
+
mountCounterInLabel(wrapper, handle.counter);
|
|
6391
|
+
containerWrap.appendChild(handle.row);
|
|
6392
|
+
}
|
|
5910
6393
|
}
|
|
5911
6394
|
updateAddButton();
|
|
5912
6395
|
wrapper.appendChild(containerWrap);
|
|
6396
|
+
if (slideAddTile) {
|
|
6397
|
+
if (typeof requestAnimationFrame === "function") {
|
|
6398
|
+
requestAnimationFrame(syncSlideTileSize);
|
|
6399
|
+
} else {
|
|
6400
|
+
syncSlideTileSize();
|
|
6401
|
+
}
|
|
6402
|
+
}
|
|
5913
6403
|
}
|
|
5914
6404
|
var validateElementFunc = null;
|
|
5915
6405
|
function setValidateElement(fn) {
|
|
@@ -7932,7 +8422,7 @@ function filterFilesForDropdown(query, files, labels) {
|
|
|
7932
8422
|
});
|
|
7933
8423
|
}
|
|
7934
8424
|
var TEXTAREA_FONT = "font-size: var(--fb-font-size, 14px); font-family: var(--fb-font-family, inherit); line-height: 1.6;";
|
|
7935
|
-
var TEXTAREA_PADDING = "padding:
|
|
8425
|
+
var TEXTAREA_PADDING = "padding: 8px 40px 8px 10px;";
|
|
7936
8426
|
function renderEditMode(element, ctx, wrapper, pathKey, initialValue) {
|
|
7937
8427
|
var _a;
|
|
7938
8428
|
const state = ctx.state;
|
|
@@ -7981,7 +8471,7 @@ function renderEditMode(element, ctx, wrapper, pathKey, initialValue) {
|
|
|
7981
8471
|
});
|
|
7982
8472
|
const errorEl = document.createElement("div");
|
|
7983
8473
|
errorEl.className = "fb-richinput-error";
|
|
7984
|
-
errorEl.style.cssText = "display: none; color: var(--fb-error-color, #ef4444); font-size: var(--fb-font-size-small, 12px); padding: 4px
|
|
8474
|
+
errorEl.style.cssText = "display: none; color: var(--fb-error-color, #ef4444); font-size: var(--fb-font-size-small, 12px); padding: 4px 10px 6px;";
|
|
7985
8475
|
let errorTimer = null;
|
|
7986
8476
|
function showUploadError(message) {
|
|
7987
8477
|
errorEl.textContent = message;
|
|
@@ -8059,7 +8549,7 @@ function renderEditMode(element, ctx, wrapper, pathKey, initialValue) {
|
|
|
8059
8549
|
});
|
|
8060
8550
|
const filesRow = document.createElement("div");
|
|
8061
8551
|
filesRow.className = "fb-richinput-files";
|
|
8062
|
-
filesRow.style.cssText = "display: none; flex-wrap: wrap; gap:
|
|
8552
|
+
filesRow.style.cssText = "display: none; flex-wrap: wrap; gap: 4px; padding: 6px 10px 0; align-items: center;";
|
|
8063
8553
|
const fileInput = document.createElement("input");
|
|
8064
8554
|
fileInput.type = "file";
|
|
8065
8555
|
fileInput.multiple = true;
|
|
@@ -8195,13 +8685,13 @@ function renderEditMode(element, ctx, wrapper, pathKey, initialValue) {
|
|
|
8195
8685
|
paperclipBtn.title = t("richinputAttachFile", state);
|
|
8196
8686
|
paperclipBtn.style.cssText = `
|
|
8197
8687
|
position: absolute;
|
|
8198
|
-
right:
|
|
8199
|
-
bottom:
|
|
8688
|
+
right: 6px;
|
|
8689
|
+
bottom: 6px;
|
|
8200
8690
|
z-index: 2;
|
|
8201
|
-
width:
|
|
8202
|
-
height:
|
|
8691
|
+
width: 28px;
|
|
8692
|
+
height: 28px;
|
|
8203
8693
|
border: none;
|
|
8204
|
-
border-radius:
|
|
8694
|
+
border-radius: 6px;
|
|
8205
8695
|
background: transparent;
|
|
8206
8696
|
cursor: pointer;
|
|
8207
8697
|
display: flex;
|
|
@@ -8569,7 +9059,7 @@ function renderEditMode(element, ctx, wrapper, pathKey, initialValue) {
|
|
|
8569
9059
|
outerDiv.appendChild(errorEl);
|
|
8570
9060
|
if (element.minLength != null || element.maxLength != null) {
|
|
8571
9061
|
const counterRow = document.createElement("div");
|
|
8572
|
-
counterRow.style.cssText = "position: relative; padding: 2px
|
|
9062
|
+
counterRow.style.cssText = "position: relative; padding: 2px 10px 4px; text-align: right;";
|
|
8573
9063
|
const counter = createCharCounter(element, textarea, false);
|
|
8574
9064
|
counter.style.cssText = `
|
|
8575
9065
|
position: static;
|
|
@@ -8877,10 +9367,7 @@ var TAGS = {
|
|
|
8877
9367
|
"-": ["<hr />"]
|
|
8878
9368
|
};
|
|
8879
9369
|
function outdent(str) {
|
|
8880
|
-
return str.replace(
|
|
8881
|
-
RegExp("^" + (str.match(/^(\t| )+/) || "")[0], "gm"),
|
|
8882
|
-
""
|
|
8883
|
-
);
|
|
9370
|
+
return str.replace(RegExp("^" + (str.match(/^(\t| )+/) || "")[0], "gm"), "");
|
|
8884
9371
|
}
|
|
8885
9372
|
function encodeAttr(str) {
|
|
8886
9373
|
return (str + "").replace(/"/g, """).replace(/</g, "<").replace(/>/g, ">");
|
|
@@ -9043,12 +9530,7 @@ function ensureMarkdownStyles() {
|
|
|
9043
9530
|
`;
|
|
9044
9531
|
document.head.appendChild(style);
|
|
9045
9532
|
}
|
|
9046
|
-
var ANCHOR_DANGEROUS_SCHEMES = [
|
|
9047
|
-
"javascript:",
|
|
9048
|
-
"data:",
|
|
9049
|
-
"vbscript:",
|
|
9050
|
-
"blob:"
|
|
9051
|
-
];
|
|
9533
|
+
var ANCHOR_DANGEROUS_SCHEMES = ["javascript:", "data:", "vbscript:", "blob:"];
|
|
9052
9534
|
var IMG_DANGEROUS_SCHEMES = ["javascript:", "vbscript:", "blob:"];
|
|
9053
9535
|
function isImgSrcDangerous(normalized) {
|
|
9054
9536
|
if (IMG_DANGEROUS_SCHEMES.some((scheme) => normalized.startsWith(scheme))) {
|
|
@@ -9374,7 +9856,8 @@ function createInfoButton(element) {
|
|
|
9374
9856
|
}
|
|
9375
9857
|
function createLabelContainer(element) {
|
|
9376
9858
|
const label = document.createElement("div");
|
|
9377
|
-
label.className = "flex items-center mb-
|
|
9859
|
+
label.className = "flex items-center mb-1";
|
|
9860
|
+
label.dataset.fbLabelRow = "";
|
|
9378
9861
|
const title = createFieldLabel(element);
|
|
9379
9862
|
label.appendChild(title);
|
|
9380
9863
|
if (element.description || element.hint) {
|
|
@@ -9481,7 +9964,7 @@ function renderElement2(element, ctx) {
|
|
|
9481
9964
|
}
|
|
9482
9965
|
const initiallyDisabled2 = shouldDisableElement(element, ctx);
|
|
9483
9966
|
const outerWrapper = document.createElement("div");
|
|
9484
|
-
outerWrapper.className = "mb-
|
|
9967
|
+
outerWrapper.className = "mb-2 fb-field-wrapper fb-markdown-wrapper";
|
|
9485
9968
|
outerWrapper.setAttribute(
|
|
9486
9969
|
"data-field-key",
|
|
9487
9970
|
getElementLookupKey(element, ctx.state)
|
|
@@ -9497,7 +9980,7 @@ function renderElement2(element, ctx) {
|
|
|
9497
9980
|
}
|
|
9498
9981
|
const initiallyDisabled = shouldDisableElement(element, ctx);
|
|
9499
9982
|
const wrapper = document.createElement("div");
|
|
9500
|
-
wrapper.className = "mb-
|
|
9983
|
+
wrapper.className = "mb-2 fb-field-wrapper";
|
|
9501
9984
|
wrapper.setAttribute("data-field-key", element.key);
|
|
9502
9985
|
const label = createLabelContainer(element);
|
|
9503
9986
|
wrapper.appendChild(label);
|
|
@@ -9564,12 +10047,16 @@ var defaultConfig = {
|
|
|
9564
10047
|
hintPattern: "Format: {pattern}",
|
|
9565
10048
|
fileCountSingle: "{count} file",
|
|
9566
10049
|
fileCountPlural: "{count} files",
|
|
10050
|
+
fileCountWithMax: "{count} / {max} files",
|
|
9567
10051
|
fileCountRange: "({min}-{max})",
|
|
9568
10052
|
uploadingFile: "Uploading\u2026",
|
|
9569
10053
|
filesCounter: "{count}/{max}",
|
|
9570
10054
|
fromLibrary: "From library",
|
|
9571
10055
|
libraryEmpty: "Library is empty",
|
|
9572
10056
|
libraryHint: "Choose from previously uploaded files",
|
|
10057
|
+
dropToUpload: "Release to upload",
|
|
10058
|
+
replaceFile: "Replace",
|
|
10059
|
+
clearAll: "Clear all",
|
|
9573
10060
|
pickerError: "Failed to load files from library",
|
|
9574
10061
|
// Validation errors
|
|
9575
10062
|
required: "Required",
|
|
@@ -9588,6 +10075,7 @@ var defaultConfig = {
|
|
|
9588
10075
|
invalidFileExtension: 'File "{name}" has unsupported format. Allowed: {formats}',
|
|
9589
10076
|
invalidFileMime: 'File "{name}": file type {type} not allowed (allowed: {mimes})',
|
|
9590
10077
|
fileTooLarge: 'File "{name}" exceeds maximum size of {maxSize}MB',
|
|
10078
|
+
uploadFailed: 'Failed to upload "{name}": {error}',
|
|
9591
10079
|
filesLimitExceeded: "{skipped} file(s) skipped: maximum {max} files allowed",
|
|
9592
10080
|
unsupportedFieldType: "Unsupported field type: {type}",
|
|
9593
10081
|
invalidOption: "Invalid option",
|
|
@@ -9635,12 +10123,16 @@ var defaultConfig = {
|
|
|
9635
10123
|
hintPattern: "\u0424\u043E\u0440\u043C\u0430\u0442: {pattern}",
|
|
9636
10124
|
fileCountSingle: "{count} \u0444\u0430\u0439\u043B",
|
|
9637
10125
|
fileCountPlural: "{count} \u0444\u0430\u0439\u043B\u043E\u0432",
|
|
10126
|
+
fileCountWithMax: "{count} / {max} \u0444\u0430\u0439\u043B\u043E\u0432",
|
|
9638
10127
|
fileCountRange: "({min}-{max})",
|
|
9639
10128
|
uploadingFile: "\u0417\u0430\u0433\u0440\u0443\u0437\u043A\u0430\u2026",
|
|
9640
10129
|
filesCounter: "{count}/{max}",
|
|
9641
10130
|
fromLibrary: "\u0418\u0437 \u0431\u0438\u0431\u043B\u0438\u043E\u0442\u0435\u043A\u0438",
|
|
9642
10131
|
libraryEmpty: "\u0411\u0438\u0431\u043B\u0438\u043E\u0442\u0435\u043A\u0430 \u043F\u0443\u0441\u0442\u0430",
|
|
9643
10132
|
libraryHint: "\u0412\u044B\u0431\u0435\u0440\u0438\u0442\u0435 \u0438\u0437 \u0440\u0430\u043D\u0435\u0435 \u0437\u0430\u0433\u0440\u0443\u0436\u0435\u043D\u043D\u044B\u0445 \u0444\u0430\u0439\u043B\u043E\u0432",
|
|
10133
|
+
dropToUpload: "\u041E\u0442\u043F\u0443\u0441\u0442\u0438\u0442\u0435, \u0447\u0442\u043E\u0431\u044B \u0437\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044C",
|
|
10134
|
+
replaceFile: "\u0417\u0430\u043C\u0435\u043D\u0438\u0442\u044C",
|
|
10135
|
+
clearAll: "\u041E\u0447\u0438\u0441\u0442\u0438\u0442\u044C \u0432\u0441\u0435",
|
|
9644
10136
|
pickerError: "\u041D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u0437\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044C \u0444\u0430\u0439\u043B\u044B \u0438\u0437 \u0431\u0438\u0431\u043B\u0438\u043E\u0442\u0435\u043A\u0438",
|
|
9645
10137
|
// Validation errors
|
|
9646
10138
|
required: "\u041E\u0431\u044F\u0437\u0430\u0442\u0435\u043B\u044C\u043D\u043E\u0435 \u043F\u043E\u043B\u0435",
|
|
@@ -9659,6 +10151,7 @@ var defaultConfig = {
|
|
|
9659
10151
|
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}',
|
|
9660
10152
|
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})',
|
|
9661
10153
|
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',
|
|
10154
|
+
uploadFailed: '\u041D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u0437\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044C "{name}": {error}',
|
|
9662
10155
|
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",
|
|
9663
10156
|
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}",
|
|
9664
10157
|
invalidOption: "\u041D\u0435\u0434\u043E\u043F\u0443\u0441\u0442\u0438\u043C\u043E\u0435 \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0435",
|
|
@@ -9781,10 +10274,10 @@ var defaultTheme = {
|
|
|
9781
10274
|
fileUploadHoverBorderColor: "#3b82f6",
|
|
9782
10275
|
// blue-500
|
|
9783
10276
|
// Spacing
|
|
9784
|
-
inputPaddingX: "0.
|
|
9785
|
-
//
|
|
9786
|
-
inputPaddingY: "0.
|
|
9787
|
-
//
|
|
10277
|
+
inputPaddingX: "0.5rem",
|
|
10278
|
+
// 8px (compact density v2)
|
|
10279
|
+
inputPaddingY: "0.25rem",
|
|
10280
|
+
// 4px (compact density v2)
|
|
9788
10281
|
borderRadius: "0.5rem",
|
|
9789
10282
|
// rounded-lg (8px)
|
|
9790
10283
|
borderWidth: "1px",
|
|
@@ -9881,29 +10374,6 @@ var exampleThemes = {
|
|
|
9881
10374
|
}
|
|
9882
10375
|
};
|
|
9883
10376
|
|
|
9884
|
-
// src/utils/styles.ts
|
|
9885
|
-
function applyActionButtonStyles(button, isFormLevel = false) {
|
|
9886
|
-
button.style.cssText = `
|
|
9887
|
-
background-color: var(--fb-action-bg-color);
|
|
9888
|
-
color: var(--fb-action-text-color);
|
|
9889
|
-
border: var(--fb-border-width) solid var(--fb-action-border-color);
|
|
9890
|
-
padding: ${isFormLevel ? "0.5rem 1rem" : "0.5rem 0.75rem"};
|
|
9891
|
-
font-size: var(--fb-font-size);
|
|
9892
|
-
font-weight: var(--fb-font-weight-medium);
|
|
9893
|
-
border-radius: var(--fb-border-radius);
|
|
9894
|
-
transition: all var(--fb-transition-duration);
|
|
9895
|
-
box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
|
|
9896
|
-
`;
|
|
9897
|
-
button.addEventListener("mouseenter", () => {
|
|
9898
|
-
button.style.backgroundColor = "var(--fb-action-hover-bg-color)";
|
|
9899
|
-
button.style.borderColor = "var(--fb-action-hover-border-color)";
|
|
9900
|
-
});
|
|
9901
|
-
button.addEventListener("mouseleave", () => {
|
|
9902
|
-
button.style.backgroundColor = "var(--fb-action-bg-color)";
|
|
9903
|
-
button.style.borderColor = "var(--fb-action-border-color)";
|
|
9904
|
-
});
|
|
9905
|
-
}
|
|
9906
|
-
|
|
9907
10377
|
// src/components/registry.ts
|
|
9908
10378
|
function validateHiddenElement(element, key, context) {
|
|
9909
10379
|
var _a;
|
|
@@ -10230,7 +10700,7 @@ var FormBuilderInstance = class {
|
|
|
10230
10700
|
existingContainer.remove();
|
|
10231
10701
|
}
|
|
10232
10702
|
const actionsContainer = document.createElement("div");
|
|
10233
|
-
actionsContainer.className = "form-level-actions-container mt-
|
|
10703
|
+
actionsContainer.className = "form-level-actions-container mt-3 pt-2 flex flex-wrap gap-2 justify-center";
|
|
10234
10704
|
actionsContainer.style.cssText = `
|
|
10235
10705
|
border-top: var(--fb-border-width) solid var(--fb-border-color);
|
|
10236
10706
|
`;
|
|
@@ -10371,7 +10841,7 @@ var FormBuilderInstance = class {
|
|
|
10371
10841
|
*/
|
|
10372
10842
|
createRootPrefillHints(hints) {
|
|
10373
10843
|
const hintsContainer = document.createElement("div");
|
|
10374
|
-
hintsContainer.className = "fb-prefill-hints flex flex-wrap gap-2 mb-
|
|
10844
|
+
hintsContainer.className = "fb-prefill-hints flex flex-wrap gap-2 mb-2";
|
|
10375
10845
|
hints.forEach((hint) => {
|
|
10376
10846
|
const hintButton = document.createElement("button");
|
|
10377
10847
|
hintButton.type = "button";
|
|
@@ -10404,7 +10874,7 @@ var FormBuilderInstance = class {
|
|
|
10404
10874
|
root.setAttribute("data-fb-root", "true");
|
|
10405
10875
|
injectThemeVariables(root, this.state.config.theme);
|
|
10406
10876
|
const rootContainer = document.createElement("div");
|
|
10407
|
-
rootContainer.className = "space-y-
|
|
10877
|
+
rootContainer.className = "space-y-2";
|
|
10408
10878
|
if (schema.prefillHints && !this.state.config.readonly) {
|
|
10409
10879
|
const hintsContainer = this.createRootPrefillHints(schema.prefillHints);
|
|
10410
10880
|
rootContainer.appendChild(hintsContainer);
|
|
@@ -10412,9 +10882,9 @@ var FormBuilderInstance = class {
|
|
|
10412
10882
|
const fieldsWrapper = document.createElement("div");
|
|
10413
10883
|
const columns = schema.columns || 1;
|
|
10414
10884
|
if (columns === 1) {
|
|
10415
|
-
fieldsWrapper.className = "space-y-
|
|
10885
|
+
fieldsWrapper.className = "space-y-2";
|
|
10416
10886
|
} else {
|
|
10417
|
-
fieldsWrapper.className = `grid grid-cols-${columns} gap-
|
|
10887
|
+
fieldsWrapper.className = `grid grid-cols-${columns} gap-2`;
|
|
10418
10888
|
}
|
|
10419
10889
|
schema.elements.forEach((element) => {
|
|
10420
10890
|
var _a, _b;
|