@dmitryvim/form-builder 0.2.29 → 0.2.32
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/README.md +51 -0
- package/dist/browser/formbuilder.min.js +578 -386
- package/dist/browser/formbuilder.v0.2.32.min.js +1148 -0
- package/dist/cjs/index.cjs +1477 -789
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/esm/index.js +1449 -775
- package/dist/esm/index.js.map +1 -1
- package/dist/form-builder.js +578 -386
- 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 +6 -0
- package/dist/types/components/markdown/index.d.ts +15 -0
- package/dist/types/components/markdown/render.d.ts +6 -0
- package/dist/types/components/markdown/snarkdown.d.ts +2 -0
- package/dist/types/index.d.ts +1 -1
- package/dist/types/instance/FormBuilderInstance.d.ts +10 -0
- package/dist/types/types/component-operations.d.ts +5 -0
- package/dist/types/types/config.d.ts +4 -0
- package/dist/types/types/index.d.ts +1 -1
- package/dist/types/types/schema.d.ts +22 -1
- package/dist/types/types/state.d.ts +5 -1
- package/dist/types/utils/helpers.d.ts +14 -0
- package/package.json +1 -1
- package/dist/browser/formbuilder.v0.2.29.min.js +0 -956
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();
|
|
@@ -159,12 +180,14 @@ function validateSchema(schema) {
|
|
|
159
180
|
allOutputKeys.add(textKey);
|
|
160
181
|
allOutputKeys.add(filesKey);
|
|
161
182
|
} else {
|
|
162
|
-
if (
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
183
|
+
if (el.key) {
|
|
184
|
+
if (allOutputKeys.has(el.key)) {
|
|
185
|
+
errors.push(
|
|
186
|
+
`${scopePath}: Element key "${el.key}" collides with a flatOutput richinput key`
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
allOutputKeys.add(el.key);
|
|
166
190
|
}
|
|
167
|
-
allOutputKeys.add(el.key);
|
|
168
191
|
}
|
|
169
192
|
}
|
|
170
193
|
}
|
|
@@ -174,9 +197,17 @@ function validateSchema(schema) {
|
|
|
174
197
|
if (!element.type) {
|
|
175
198
|
errors.push(`${elementPath}: missing type`);
|
|
176
199
|
}
|
|
177
|
-
if (!element.key) {
|
|
200
|
+
if (!element.key && element.type !== "markdown") {
|
|
178
201
|
errors.push(`${elementPath}: missing key`);
|
|
179
202
|
}
|
|
203
|
+
if (element.type === "markdown") {
|
|
204
|
+
const content = element.content;
|
|
205
|
+
if (typeof content !== "string") {
|
|
206
|
+
errors.push(
|
|
207
|
+
`${elementPath}: markdown element requires "content" to be a string (got ${content === null ? "null" : typeof content})`
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
180
211
|
if (element.enableIf) {
|
|
181
212
|
const enableIf = element.enableIf;
|
|
182
213
|
if (!enableIf.key || typeof enableIf.key !== "string") {
|
|
@@ -195,15 +226,7 @@ function validateSchema(schema) {
|
|
|
195
226
|
validateElements(element.elements, `${elementPath}.elements`);
|
|
196
227
|
}
|
|
197
228
|
if (element.type === "container" && element.elements) {
|
|
198
|
-
|
|
199
|
-
const columns = element.columns;
|
|
200
|
-
const validColumns = [1, 2, 3, 4];
|
|
201
|
-
if (!Number.isInteger(columns) || !validColumns.includes(columns)) {
|
|
202
|
-
errors.push(
|
|
203
|
-
`${elementPath}: columns must be 1, 2, 3, or 4 (got ${columns})`
|
|
204
|
-
);
|
|
205
|
-
}
|
|
206
|
-
}
|
|
229
|
+
validateContainerProps(element, elementPath, errors);
|
|
207
230
|
if ("prefillHints" in element && element.prefillHints) {
|
|
208
231
|
const prefillHints = element.prefillHints;
|
|
209
232
|
if (Array.isArray(prefillHints)) {
|
|
@@ -269,6 +292,18 @@ function escapeHtml(text) {
|
|
|
269
292
|
div.textContent = text;
|
|
270
293
|
return div.innerHTML;
|
|
271
294
|
}
|
|
295
|
+
function getElementLookupKey(element, state) {
|
|
296
|
+
if (element.key) {
|
|
297
|
+
return element.key;
|
|
298
|
+
}
|
|
299
|
+
const cached = state.syntheticElementIds.get(element);
|
|
300
|
+
if (cached !== void 0) {
|
|
301
|
+
return cached;
|
|
302
|
+
}
|
|
303
|
+
const id = `fb-synthetic-${state.syntheticElementIdCounter++}`;
|
|
304
|
+
state.syntheticElementIds.set(element, id);
|
|
305
|
+
return id;
|
|
306
|
+
}
|
|
272
307
|
function pathJoin(base, key) {
|
|
273
308
|
return base ? `${base}.${key}` : key;
|
|
274
309
|
}
|
|
@@ -829,8 +864,12 @@ function renderTextareaElement(element, ctx, wrapper, pathKey) {
|
|
|
829
864
|
const textareaWrapper = document.createElement("div");
|
|
830
865
|
textareaWrapper.style.cssText = "position: relative;";
|
|
831
866
|
const textareaInput = document.createElement("textarea");
|
|
832
|
-
textareaInput.className = "w-full
|
|
833
|
-
textareaInput.style.cssText =
|
|
867
|
+
textareaInput.className = "w-full border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 resize-none";
|
|
868
|
+
textareaInput.style.cssText = `
|
|
869
|
+
padding: var(--fb-input-padding-y) var(--fb-input-padding-x) 24px var(--fb-input-padding-x);
|
|
870
|
+
font-size: var(--fb-font-size);
|
|
871
|
+
font-family: var(--fb-font-family);
|
|
872
|
+
`;
|
|
834
873
|
textareaInput.name = pathKey;
|
|
835
874
|
textareaInput.placeholder = element.placeholder || "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u0442\u0435\u043A\u0441\u0442";
|
|
836
875
|
textareaInput.rows = element.rows || 4;
|
|
@@ -883,8 +922,12 @@ function renderMultipleTextareaElement(element, ctx, wrapper, pathKey) {
|
|
|
883
922
|
const textareaContainer = document.createElement("div");
|
|
884
923
|
textareaContainer.style.cssText = "position: relative;";
|
|
885
924
|
const textareaInput = document.createElement("textarea");
|
|
886
|
-
textareaInput.className = "w-full
|
|
887
|
-
textareaInput.style.cssText =
|
|
925
|
+
textareaInput.className = "w-full border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 resize-none";
|
|
926
|
+
textareaInput.style.cssText = `
|
|
927
|
+
padding: var(--fb-input-padding-y) var(--fb-input-padding-x) 24px var(--fb-input-padding-x);
|
|
928
|
+
font-size: var(--fb-font-size);
|
|
929
|
+
font-family: var(--fb-font-family);
|
|
930
|
+
`;
|
|
888
931
|
textareaInput.placeholder = element.placeholder || t("placeholderText", state);
|
|
889
932
|
textareaInput.rows = element.rows || 4;
|
|
890
933
|
textareaInput.value = value;
|
|
@@ -1070,8 +1113,14 @@ function renderNumberElement(element, ctx, wrapper, pathKey) {
|
|
|
1070
1113
|
inputWrapper.style.cssText = "position: relative;";
|
|
1071
1114
|
const numberInput = document.createElement("input");
|
|
1072
1115
|
numberInput.type = "number";
|
|
1073
|
-
numberInput.className = "w-full
|
|
1074
|
-
numberInput.style.cssText =
|
|
1116
|
+
numberInput.className = "w-full border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500";
|
|
1117
|
+
numberInput.style.cssText = `
|
|
1118
|
+
padding: var(--fb-input-padding-y) 60px var(--fb-input-padding-y) var(--fb-input-padding-x);
|
|
1119
|
+
font-size: var(--fb-font-size);
|
|
1120
|
+
font-family: var(--fb-font-family);
|
|
1121
|
+
width: 100%;
|
|
1122
|
+
box-sizing: border-box;
|
|
1123
|
+
`;
|
|
1075
1124
|
numberInput.name = pathKey;
|
|
1076
1125
|
numberInput.placeholder = element.placeholder || "0";
|
|
1077
1126
|
if (element.min !== void 0) numberInput.min = element.min.toString();
|
|
@@ -1124,8 +1173,14 @@ function renderMultipleNumberElement(element, ctx, wrapper, pathKey) {
|
|
|
1124
1173
|
inputContainer.style.cssText = "position: relative; flex: 1;";
|
|
1125
1174
|
const numberInput = document.createElement("input");
|
|
1126
1175
|
numberInput.type = "number";
|
|
1127
|
-
numberInput.className = "w-full
|
|
1128
|
-
numberInput.style.cssText =
|
|
1176
|
+
numberInput.className = "w-full border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500";
|
|
1177
|
+
numberInput.style.cssText = `
|
|
1178
|
+
padding: var(--fb-input-padding-y) 60px var(--fb-input-padding-y) var(--fb-input-padding-x);
|
|
1179
|
+
font-size: var(--fb-font-size);
|
|
1180
|
+
font-family: var(--fb-font-family);
|
|
1181
|
+
width: 100%;
|
|
1182
|
+
box-sizing: border-box;
|
|
1183
|
+
`;
|
|
1129
1184
|
numberInput.placeholder = element.placeholder || "0";
|
|
1130
1185
|
if (element.min !== void 0) numberInput.min = element.min.toString();
|
|
1131
1186
|
if (element.max !== void 0) numberInput.max = element.max.toString();
|
|
@@ -1399,7 +1454,12 @@ function renderSelectElement(element, ctx, wrapper, pathKey) {
|
|
|
1399
1454
|
const state = ctx.state;
|
|
1400
1455
|
const readonly = isElementReadonly(element, state, ctx);
|
|
1401
1456
|
const selectInput = document.createElement("select");
|
|
1402
|
-
selectInput.className = "w-full
|
|
1457
|
+
selectInput.className = "w-full border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500";
|
|
1458
|
+
selectInput.style.cssText = `
|
|
1459
|
+
padding: var(--fb-input-padding-y) var(--fb-input-padding-x);
|
|
1460
|
+
font-size: var(--fb-font-size);
|
|
1461
|
+
font-family: var(--fb-font-family);
|
|
1462
|
+
`;
|
|
1403
1463
|
selectInput.name = pathKey;
|
|
1404
1464
|
selectInput.disabled = readonly;
|
|
1405
1465
|
(element.options || []).forEach((option) => {
|
|
@@ -1452,7 +1512,12 @@ function renderMultipleSelectElement(element, ctx, wrapper, pathKey) {
|
|
|
1452
1512
|
const itemWrapper = document.createElement("div");
|
|
1453
1513
|
itemWrapper.className = "multiple-select-item flex items-center gap-2";
|
|
1454
1514
|
const selectInput = document.createElement("select");
|
|
1455
|
-
selectInput.className = "flex-1
|
|
1515
|
+
selectInput.className = "flex-1 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500";
|
|
1516
|
+
selectInput.style.cssText = `
|
|
1517
|
+
padding: var(--fb-input-padding-y) var(--fb-input-padding-x);
|
|
1518
|
+
font-size: var(--fb-font-size);
|
|
1519
|
+
font-family: var(--fb-font-family);
|
|
1520
|
+
`;
|
|
1456
1521
|
selectInput.disabled = readonly;
|
|
1457
1522
|
(element.options || []).forEach((option) => {
|
|
1458
1523
|
const optionElement = document.createElement("option");
|
|
@@ -2203,7 +2268,13 @@ function ensureFileStyles() {
|
|
|
2203
2268
|
style.textContent = `
|
|
2204
2269
|
@keyframes fb-spin { to { transform: rotate(360deg); } }
|
|
2205
2270
|
|
|
2206
|
-
/*
|
|
2271
|
+
/* \u2500\u2500\u2500 Checker background utility \u2500\u2500\u2500 */
|
|
2272
|
+
/* Neutral diagonal-stripe background for image previews (never crops) */
|
|
2273
|
+
.fb-checker {
|
|
2274
|
+
background-image: repeating-linear-gradient(45deg, #fafafa 0 6px, #f3f4f6 6px 12px);
|
|
2275
|
+
}
|
|
2276
|
+
|
|
2277
|
+
/* \u2500\u2500\u2500 Spinner \u2500\u2500\u2500 */
|
|
2207
2278
|
.fb-spinner {
|
|
2208
2279
|
width: 36px;
|
|
2209
2280
|
height: 36px;
|
|
@@ -2214,207 +2285,271 @@ function ensureFileStyles() {
|
|
|
2214
2285
|
flex-shrink: 0;
|
|
2215
2286
|
}
|
|
2216
2287
|
|
|
2217
|
-
/*
|
|
2218
|
-
.fb-tile {
|
|
2219
|
-
width:
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2288
|
+
/* \u2500\u2500\u2500 Wide single-file add tile (empty state) \u2500\u2500\u2500 */
|
|
2289
|
+
.fb-wide-tile {
|
|
2290
|
+
width: 100%;
|
|
2291
|
+
border-radius: 0.75rem;
|
|
2292
|
+
border: 1px dashed #60a5fa;
|
|
2293
|
+
background: rgba(239,246,255,0.5);
|
|
2294
|
+
display: flex;
|
|
2223
2295
|
overflow: hidden;
|
|
2224
|
-
|
|
2225
|
-
|
|
2296
|
+
height: 180px;
|
|
2297
|
+
transition: border-color 150ms, background 150ms, box-shadow 150ms;
|
|
2298
|
+
cursor: pointer;
|
|
2226
2299
|
}
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
.fb-tile-resource {
|
|
2230
|
-
border: 1px solid var(--fb-file-upload-border-color, #d1d5db);
|
|
2300
|
+
.fb-wide-tile:hover {
|
|
2301
|
+
background: #eff6ff;
|
|
2231
2302
|
}
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2303
|
+
.fb-wide-tile.fb-drag-over {
|
|
2304
|
+
border-color: #3b82f6;
|
|
2305
|
+
border-width: 2px;
|
|
2306
|
+
background: #eff6ff;
|
|
2307
|
+
box-shadow: 0 0 0 4px rgba(191,219,254,0.7);
|
|
2236
2308
|
}
|
|
2237
2309
|
|
|
2238
|
-
/*
|
|
2239
|
-
.fb-tile-
|
|
2240
|
-
|
|
2310
|
+
/* Upload zone inside wide tile */
|
|
2311
|
+
.fb-wide-tile-upload {
|
|
2312
|
+
flex: 1;
|
|
2241
2313
|
display: flex;
|
|
2314
|
+
flex-direction: column;
|
|
2242
2315
|
align-items: center;
|
|
2243
2316
|
justify-content: center;
|
|
2317
|
+
gap: 8px;
|
|
2318
|
+
color: #2563eb;
|
|
2319
|
+
padding: 16px;
|
|
2320
|
+
transition: background 150ms;
|
|
2244
2321
|
cursor: pointer;
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
border-color var(--fb-transition-duration, 200ms),
|
|
2249
|
-
color var(--fb-transition-duration, 200ms);
|
|
2322
|
+
background: transparent;
|
|
2323
|
+
border: none;
|
|
2324
|
+
font-family: inherit;
|
|
2250
2325
|
}
|
|
2251
|
-
.fb-tile-
|
|
2252
|
-
|
|
2253
|
-
color: var(--fb-text-color, #1f2937);
|
|
2326
|
+
.fb-wide-tile-upload:hover {
|
|
2327
|
+
background: rgba(191,219,254,0.25);
|
|
2254
2328
|
}
|
|
2255
2329
|
|
|
2256
|
-
/*
|
|
2257
|
-
.fb-tile-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
padding: 2px 6px;
|
|
2264
|
-
align-self: flex-end;
|
|
2265
|
-
margin-bottom: 4px;
|
|
2330
|
+
/* Vertical dashed divider between upload and library zones */
|
|
2331
|
+
.fb-wide-tile-divider {
|
|
2332
|
+
width: 1px;
|
|
2333
|
+
margin: 16px 0;
|
|
2334
|
+
border-left: 1px dashed rgba(96,165,250,0.5);
|
|
2335
|
+
background: transparent;
|
|
2336
|
+
flex-shrink: 0;
|
|
2266
2337
|
}
|
|
2267
2338
|
|
|
2268
|
-
/*
|
|
2269
|
-
.fb-
|
|
2270
|
-
width:
|
|
2271
|
-
|
|
2272
|
-
border: 2px dashed var(--fb-file-upload-border-color, #d1d5db);
|
|
2273
|
-
border-radius: var(--fb-border-radius, 0.5rem);
|
|
2339
|
+
/* Library zone inside wide tile */
|
|
2340
|
+
.fb-wide-tile-library {
|
|
2341
|
+
width: 176px;
|
|
2342
|
+
flex-shrink: 0;
|
|
2274
2343
|
display: flex;
|
|
2275
2344
|
flex-direction: column;
|
|
2276
2345
|
align-items: center;
|
|
2277
2346
|
justify-content: center;
|
|
2278
|
-
gap:
|
|
2347
|
+
gap: 8px;
|
|
2348
|
+
color: #2563eb;
|
|
2349
|
+
padding: 12px;
|
|
2350
|
+
transition: background 150ms;
|
|
2279
2351
|
cursor: pointer;
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2352
|
+
background: transparent;
|
|
2353
|
+
border: none;
|
|
2354
|
+
font-family: inherit;
|
|
2283
2355
|
}
|
|
2284
|
-
.fb-
|
|
2285
|
-
|
|
2286
|
-
background: var(--fb-background-hover-color, #f9fafb);
|
|
2356
|
+
.fb-wide-tile-library:hover {
|
|
2357
|
+
background: rgba(191,219,254,0.25);
|
|
2287
2358
|
}
|
|
2288
2359
|
|
|
2289
|
-
/*
|
|
2290
|
-
.fb-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2360
|
+
/* \u2500\u2500\u2500 Multi-file outer grid container \u2500\u2500\u2500 */
|
|
2361
|
+
.fb-multi-outer {
|
|
2362
|
+
border-radius: 0.75rem;
|
|
2363
|
+
border: 1px dashed #cbd5e1;
|
|
2364
|
+
background: rgba(248,250,252,0.4);
|
|
2365
|
+
padding: 12px;
|
|
2366
|
+
transition: border-color 150ms, background 150ms, box-shadow 150ms;
|
|
2367
|
+
}
|
|
2368
|
+
.fb-multi-outer.fb-drag-over {
|
|
2369
|
+
border-width: 2px;
|
|
2370
|
+
border-color: #3b82f6;
|
|
2371
|
+
background: rgba(239,246,255,0.4);
|
|
2372
|
+
box-shadow: 0 0 0 4px rgba(191,219,254,0.7);
|
|
2297
2373
|
}
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2374
|
+
|
|
2375
|
+
/* With files present: white solid border */
|
|
2376
|
+
.fb-multi-outer.fb-multi-has-files {
|
|
2377
|
+
border-style: solid;
|
|
2378
|
+
border-color: #e2e8f0;
|
|
2379
|
+
background: #fff;
|
|
2301
2380
|
}
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2381
|
+
|
|
2382
|
+
/* The CSS grid inside */
|
|
2383
|
+
.fb-multi-grid {
|
|
2384
|
+
display: grid;
|
|
2385
|
+
grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
|
|
2386
|
+
gap: 10px;
|
|
2306
2387
|
}
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2388
|
+
|
|
2389
|
+
/* \u2500\u2500\u2500 Multi square add-tile (combined upload + library) \u2500\u2500\u2500 */
|
|
2390
|
+
.fb-multi-add-tile {
|
|
2391
|
+
aspect-ratio: 1 / 1;
|
|
2392
|
+
border-radius: 0.5rem;
|
|
2393
|
+
border: 1px dashed #60a5fa;
|
|
2394
|
+
background: rgba(239,246,255,0.5);
|
|
2395
|
+
display: flex;
|
|
2396
|
+
flex-direction: column;
|
|
2397
|
+
overflow: hidden;
|
|
2398
|
+
transition: background 150ms;
|
|
2311
2399
|
}
|
|
2312
|
-
.fb-
|
|
2313
|
-
|
|
2314
|
-
color: var(--fb-text-secondary-color, #6b7280);
|
|
2400
|
+
.fb-multi-add-tile:hover {
|
|
2401
|
+
background: #eff6ff;
|
|
2315
2402
|
}
|
|
2316
|
-
.fb-
|
|
2317
|
-
|
|
2318
|
-
color:
|
|
2403
|
+
.fb-multi-add-tile.fb-drag-over-tile {
|
|
2404
|
+
border-width: 2px;
|
|
2405
|
+
border-color: #3b82f6;
|
|
2406
|
+
background: rgba(255,255,255,0.8);
|
|
2319
2407
|
}
|
|
2320
2408
|
|
|
2321
|
-
/*
|
|
2322
|
-
.fb-
|
|
2323
|
-
|
|
2324
|
-
inset: 0;
|
|
2325
|
-
background: transparent;
|
|
2326
|
-
transition: background var(--fb-transition-duration, 200ms);
|
|
2327
|
-
display: flex;
|
|
2328
|
-
align-items: flex-start;
|
|
2329
|
-
justify-content: flex-end;
|
|
2330
|
-
}
|
|
2331
|
-
.fb-tile-resource:hover .fb-tile-overlay {
|
|
2332
|
-
background: var(--fb-tile-hover-overlay-color, rgba(0,0,0,0.4));
|
|
2333
|
-
}
|
|
2334
|
-
.fb-tile-x-btn {
|
|
2335
|
-
margin: 3px;
|
|
2336
|
-
width: 18px;
|
|
2337
|
-
height: 18px;
|
|
2338
|
-
background: var(--fb-error-color, #ef4444);
|
|
2339
|
-
color: var(--fb-file-bg-color, #fff);
|
|
2340
|
-
border: none;
|
|
2341
|
-
border-radius: 50%;
|
|
2342
|
-
font-size: 11px;
|
|
2343
|
-
line-height: 1;
|
|
2344
|
-
cursor: pointer;
|
|
2409
|
+
/* Upload half of add-tile */
|
|
2410
|
+
.fb-multi-add-upload {
|
|
2411
|
+
flex: 1;
|
|
2345
2412
|
display: flex;
|
|
2413
|
+
flex-direction: column;
|
|
2346
2414
|
align-items: center;
|
|
2347
2415
|
justify-content: center;
|
|
2348
|
-
|
|
2349
|
-
|
|
2416
|
+
gap: 4px;
|
|
2417
|
+
color: #2563eb;
|
|
2418
|
+
cursor: pointer;
|
|
2419
|
+
background: transparent;
|
|
2420
|
+
border: none;
|
|
2421
|
+
font-family: inherit;
|
|
2422
|
+
width: 100%;
|
|
2423
|
+
transition: background 150ms;
|
|
2350
2424
|
}
|
|
2351
|
-
.fb-
|
|
2352
|
-
|
|
2425
|
+
.fb-multi-add-upload:hover {
|
|
2426
|
+
background: rgba(191,219,254,0.35);
|
|
2353
2427
|
}
|
|
2354
2428
|
|
|
2355
|
-
/*
|
|
2356
|
-
.fb-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
align-items: center;
|
|
2361
|
-
justify-content: center;
|
|
2362
|
-
background: var(--fb-tile-hover-overlay-color, rgba(0,0,0,0.25));
|
|
2429
|
+
/* Horizontal dashed divider inside add-tile */
|
|
2430
|
+
.fb-multi-add-divider {
|
|
2431
|
+
border-top: 1px dashed rgba(96,165,250,0.5);
|
|
2432
|
+
margin: 0;
|
|
2433
|
+
flex-shrink: 0;
|
|
2363
2434
|
}
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2435
|
+
|
|
2436
|
+
/* Library strip at bottom of add-tile */
|
|
2437
|
+
.fb-multi-add-library {
|
|
2438
|
+
padding: 6px 0;
|
|
2367
2439
|
display: flex;
|
|
2368
2440
|
align-items: center;
|
|
2369
2441
|
justify-content: center;
|
|
2442
|
+
gap: 4px;
|
|
2443
|
+
color: #2563eb;
|
|
2444
|
+
font-size: 11px;
|
|
2445
|
+
font-weight: 500;
|
|
2446
|
+
cursor: pointer;
|
|
2447
|
+
background: transparent;
|
|
2448
|
+
border: none;
|
|
2449
|
+
font-family: inherit;
|
|
2450
|
+
width: 100%;
|
|
2451
|
+
transition: background 150ms;
|
|
2452
|
+
flex-shrink: 0;
|
|
2453
|
+
}
|
|
2454
|
+
.fb-multi-add-library:hover {
|
|
2455
|
+
background: rgba(191,219,254,0.35);
|
|
2370
2456
|
}
|
|
2371
2457
|
|
|
2372
|
-
/*
|
|
2373
|
-
.fb-
|
|
2458
|
+
/* \u2500\u2500\u2500 Capacity placeholder squares \u2500\u2500\u2500 */
|
|
2459
|
+
.fb-multi-placeholder {
|
|
2460
|
+
aspect-ratio: 1 / 1;
|
|
2461
|
+
border-radius: 0.5rem;
|
|
2462
|
+
border: 1px solid #e2e8f0;
|
|
2463
|
+
}
|
|
2464
|
+
.fb-multi-placeholder.fb-drag-over {
|
|
2465
|
+
border-width: 2px;
|
|
2466
|
+
border-style: dashed;
|
|
2467
|
+
border-color: #93c5fd;
|
|
2468
|
+
background: rgba(219,234,254,0.6);
|
|
2469
|
+
}
|
|
2470
|
+
|
|
2471
|
+
/* \u2500\u2500\u2500 Filled preview tile \u2500\u2500\u2500 */
|
|
2472
|
+
.fb-preview-tile {
|
|
2473
|
+
aspect-ratio: 1 / 1;
|
|
2474
|
+
border-radius: 0.5rem;
|
|
2475
|
+
border: 1px solid #e2e8f0;
|
|
2476
|
+
overflow: hidden;
|
|
2374
2477
|
position: relative;
|
|
2478
|
+
cursor: pointer;
|
|
2479
|
+
}
|
|
2480
|
+
.fb-preview-tile img {
|
|
2375
2481
|
width: 100%;
|
|
2376
2482
|
height: 100%;
|
|
2483
|
+
object-fit: contain;
|
|
2484
|
+
display: block;
|
|
2377
2485
|
}
|
|
2378
2486
|
|
|
2379
|
-
/*
|
|
2380
|
-
.fb-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
z-index: 10;
|
|
2487
|
+
/* \u2500\u2500\u2500 Uploading placeholder tile \u2500\u2500\u2500 */
|
|
2488
|
+
.fb-uploading-tile {
|
|
2489
|
+
aspect-ratio: 1 / 1;
|
|
2490
|
+
border-radius: 0.5rem;
|
|
2491
|
+
border: 2px dashed #d1d5db;
|
|
2385
2492
|
display: flex;
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2493
|
+
flex-direction: column;
|
|
2494
|
+
align-items: center;
|
|
2495
|
+
justify-content: center;
|
|
2496
|
+
gap: 6px;
|
|
2497
|
+
padding: 6px;
|
|
2390
2498
|
}
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2499
|
+
|
|
2500
|
+
/* \u2500\u2500\u2500 Meta line below multi grid \u2500\u2500\u2500 */
|
|
2501
|
+
.fb-meta-line {
|
|
2502
|
+
margin-top: 10px;
|
|
2503
|
+
display: flex;
|
|
2504
|
+
align-items: center;
|
|
2505
|
+
justify-content: space-between;
|
|
2506
|
+
gap: 8px;
|
|
2507
|
+
flex-wrap: wrap;
|
|
2394
2508
|
}
|
|
2395
|
-
.fb-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
line-height: 1.2;
|
|
2509
|
+
.fb-meta-text {
|
|
2510
|
+
font-size: 12px;
|
|
2511
|
+
color: #94a3b8;
|
|
2512
|
+
display: flex;
|
|
2513
|
+
align-items: center;
|
|
2514
|
+
gap: 8px;
|
|
2515
|
+
flex-wrap: wrap;
|
|
2403
2516
|
}
|
|
2404
|
-
.fb-
|
|
2405
|
-
|
|
2517
|
+
.fb-meta-dot {
|
|
2518
|
+
width: 4px;
|
|
2519
|
+
height: 4px;
|
|
2520
|
+
border-radius: 50%;
|
|
2521
|
+
background: #cbd5e1;
|
|
2522
|
+
flex-shrink: 0;
|
|
2523
|
+
}
|
|
2524
|
+
.fb-meta-mono {
|
|
2525
|
+
font-family: ui-monospace, 'JetBrains Mono', monospace;
|
|
2526
|
+
font-size: 11px;
|
|
2527
|
+
letter-spacing: -0.02em;
|
|
2406
2528
|
}
|
|
2407
|
-
.fb-
|
|
2408
|
-
|
|
2529
|
+
.fb-clear-all-btn {
|
|
2530
|
+
font-size: 12px;
|
|
2531
|
+
color: #94a3b8;
|
|
2532
|
+
background: none;
|
|
2533
|
+
border: none;
|
|
2534
|
+
cursor: pointer;
|
|
2535
|
+
padding: 0;
|
|
2536
|
+
font-family: inherit;
|
|
2537
|
+
transition: color 150ms;
|
|
2538
|
+
white-space: nowrap;
|
|
2539
|
+
flex-shrink: 0;
|
|
2409
2540
|
}
|
|
2410
|
-
.fb-
|
|
2411
|
-
|
|
2541
|
+
.fb-clear-all-btn:hover {
|
|
2542
|
+
color: #dc2626;
|
|
2412
2543
|
}
|
|
2413
|
-
|
|
2414
|
-
|
|
2544
|
+
|
|
2545
|
+
/* \u2500\u2500\u2500 Empty text (readonly) \u2500\u2500\u2500 */
|
|
2546
|
+
.fb-tile-empty-text {
|
|
2547
|
+
font-size: 11px;
|
|
2548
|
+
color: var(--fb-text-secondary-color, #6b7280);
|
|
2549
|
+
padding: 4px 0;
|
|
2415
2550
|
}
|
|
2416
2551
|
|
|
2417
|
-
/* Tile action
|
|
2552
|
+
/* \u2500\u2500\u2500 Tile action buttons (for zoom popup, compat) \u2500\u2500\u2500 */
|
|
2418
2553
|
.fb-tile-actions {
|
|
2419
2554
|
position: absolute;
|
|
2420
2555
|
top: 3px;
|
|
@@ -2426,37 +2561,35 @@ function ensureFileStyles() {
|
|
|
2426
2561
|
transition: opacity var(--fb-transition-duration, 200ms);
|
|
2427
2562
|
z-index: 10;
|
|
2428
2563
|
}
|
|
2429
|
-
.fb-tile
|
|
2564
|
+
.fb-preview-tile:hover .fb-tile-actions {
|
|
2430
2565
|
opacity: 1;
|
|
2431
2566
|
}
|
|
2432
2567
|
.fb-tile-action-btn {
|
|
2433
|
-
width:
|
|
2434
|
-
height:
|
|
2568
|
+
width: 24px;
|
|
2569
|
+
height: 24px;
|
|
2435
2570
|
display: flex;
|
|
2436
2571
|
align-items: center;
|
|
2437
2572
|
justify-content: center;
|
|
2438
|
-
border:
|
|
2439
|
-
border-radius:
|
|
2573
|
+
border: 1px solid rgba(15,23,42,0.08);
|
|
2574
|
+
border-radius: 0.375rem;
|
|
2440
2575
|
cursor: pointer;
|
|
2441
|
-
background: rgba(
|
|
2442
|
-
color: #
|
|
2576
|
+
background: rgba(255,255,255,0.92);
|
|
2577
|
+
color: #374151;
|
|
2443
2578
|
padding: 0;
|
|
2444
2579
|
flex-shrink: 0;
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2580
|
+
box-shadow: 0 1px 2px rgba(0,0,0,0.06);
|
|
2581
|
+
transition: background var(--fb-transition-duration, 200ms),
|
|
2582
|
+
color var(--fb-transition-duration, 200ms);
|
|
2448
2583
|
}
|
|
2449
2584
|
.fb-tile-action-btn:hover {
|
|
2450
|
-
background:
|
|
2451
|
-
|
|
2452
|
-
.fb-tile-action-remove {
|
|
2453
|
-
background: rgba(220, 38, 38, 0.8);
|
|
2585
|
+
background: #ffffff;
|
|
2586
|
+
color: #0f172a;
|
|
2454
2587
|
}
|
|
2455
2588
|
.fb-tile-action-remove:hover {
|
|
2456
|
-
|
|
2589
|
+
color: #dc2626;
|
|
2457
2590
|
}
|
|
2458
2591
|
|
|
2459
|
-
/*
|
|
2592
|
+
/* Zoom popup action buttons always visible */
|
|
2460
2593
|
.fb-tile-zoom-preview .fb-tile-actions {
|
|
2461
2594
|
position: absolute;
|
|
2462
2595
|
top: 6px;
|
|
@@ -2465,116 +2598,145 @@ function ensureFileStyles() {
|
|
|
2465
2598
|
z-index: 10000;
|
|
2466
2599
|
}
|
|
2467
2600
|
|
|
2468
|
-
/*
|
|
2469
|
-
.fb-
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2601
|
+
/* \u2500\u2500\u2500 Hover zoom preview popup \u2500\u2500\u2500 */
|
|
2602
|
+
.fb-tile-zoom-preview {
|
|
2603
|
+
position: fixed;
|
|
2604
|
+
z-index: 9999;
|
|
2605
|
+
background: var(--fb-background-color, #fff);
|
|
2606
|
+
border: 1px solid #e2e8f0;
|
|
2607
|
+
border-radius: 0.5rem;
|
|
2608
|
+
box-shadow: 0 4px 16px rgba(0,0,0,0.18);
|
|
2609
|
+
padding: 4px;
|
|
2610
|
+
width: 350px;
|
|
2611
|
+
height: 350px;
|
|
2612
|
+
pointer-events: none;
|
|
2613
|
+
opacity: 0;
|
|
2614
|
+
transition: opacity 150ms ease;
|
|
2473
2615
|
}
|
|
2474
|
-
.fb-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2616
|
+
.fb-tile-zoom-preview.fb-tile-zoom-preview--visible {
|
|
2617
|
+
opacity: 1;
|
|
2618
|
+
}
|
|
2619
|
+
.fb-tile-zoom-preview-img {
|
|
2620
|
+
width: 100%;
|
|
2621
|
+
height: 100%;
|
|
2622
|
+
object-fit: contain;
|
|
2623
|
+
display: block;
|
|
2624
|
+
border-radius: calc(0.5rem - 2px);
|
|
2478
2625
|
}
|
|
2479
2626
|
|
|
2480
|
-
/*
|
|
2481
|
-
.fb-
|
|
2482
|
-
height:
|
|
2483
|
-
border:
|
|
2484
|
-
border
|
|
2627
|
+
/* \u2500\u2500\u2500 Single-file uploading state \u2500\u2500\u2500 */
|
|
2628
|
+
.fb-single-uploading {
|
|
2629
|
+
height: 180px;
|
|
2630
|
+
border-radius: 0.75rem;
|
|
2631
|
+
border: 1px dashed #60a5fa;
|
|
2632
|
+
background: rgba(239,246,255,0.5);
|
|
2485
2633
|
display: flex;
|
|
2486
2634
|
flex-direction: column;
|
|
2487
2635
|
align-items: center;
|
|
2488
2636
|
justify-content: center;
|
|
2489
|
-
gap:
|
|
2490
|
-
cursor: pointer;
|
|
2491
|
-
background: none;
|
|
2492
|
-
padding: 0;
|
|
2493
|
-
transition:
|
|
2494
|
-
border-color var(--fb-transition-duration, 200ms),
|
|
2495
|
-
background var(--fb-transition-duration, 200ms);
|
|
2496
|
-
width: 100%;
|
|
2637
|
+
gap: 8px;
|
|
2497
2638
|
}
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2639
|
+
|
|
2640
|
+
/* \u2500\u2500\u2500 Video overlays \u2500\u2500\u2500 */
|
|
2641
|
+
.fb-video-overlay {
|
|
2642
|
+
position: absolute;
|
|
2643
|
+
inset: 0;
|
|
2644
|
+
display: flex;
|
|
2645
|
+
align-items: center;
|
|
2646
|
+
justify-content: center;
|
|
2647
|
+
background: rgba(0,0,0,0.25);
|
|
2503
2648
|
}
|
|
2504
|
-
.fb-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2649
|
+
.fb-play-btn {
|
|
2650
|
+
background: rgba(255,255,255,0.9);
|
|
2651
|
+
border-radius: 50%;
|
|
2652
|
+
display: flex;
|
|
2653
|
+
align-items: center;
|
|
2654
|
+
justify-content: center;
|
|
2508
2655
|
}
|
|
2509
|
-
.fb-
|
|
2510
|
-
|
|
2511
|
-
|
|
2656
|
+
.fb-video-preview-wrap {
|
|
2657
|
+
position: relative;
|
|
2658
|
+
width: 100%;
|
|
2659
|
+
height: 100%;
|
|
2660
|
+
}
|
|
2661
|
+
.fb-video-btn-overlay {
|
|
2662
|
+
position: absolute;
|
|
2663
|
+
top: 8px;
|
|
2664
|
+
right: 8px;
|
|
2665
|
+
z-index: 10;
|
|
2666
|
+
display: flex;
|
|
2667
|
+
gap: 4px;
|
|
2668
|
+
opacity: 0;
|
|
2669
|
+
transition: opacity 150ms;
|
|
2670
|
+
pointer-events: none;
|
|
2512
2671
|
}
|
|
2513
|
-
.fb-
|
|
2672
|
+
.fb-video-preview-wrap:hover .fb-video-btn-overlay {
|
|
2673
|
+
opacity: 1;
|
|
2674
|
+
pointer-events: auto;
|
|
2675
|
+
}
|
|
2676
|
+
.fb-video-btn {
|
|
2677
|
+
border: none;
|
|
2678
|
+
border-radius: 4px;
|
|
2514
2679
|
font-size: 11px;
|
|
2515
|
-
|
|
2680
|
+
padding: 4px 8px;
|
|
2681
|
+
cursor: pointer;
|
|
2682
|
+
color: #fff;
|
|
2683
|
+
line-height: 1.2;
|
|
2516
2684
|
}
|
|
2685
|
+
.fb-video-btn-delete { background: rgba(220,38,38,0.85); }
|
|
2686
|
+
.fb-video-btn-delete:hover { background: rgba(185,28,28,0.95); }
|
|
2687
|
+
.fb-video-btn-change { background: rgba(31,41,55,0.85); }
|
|
2688
|
+
.fb-video-btn-change:hover { background: rgba(17,24,39,0.95); }
|
|
2517
2689
|
|
|
2518
|
-
/*
|
|
2519
|
-
.fb-tile
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
justify-content: center;
|
|
2524
|
-
cursor: pointer;
|
|
2525
|
-
font-size: 24px;
|
|
2526
|
-
color: var(--fb-file-upload-text-color, #9ca3af);
|
|
2527
|
-
transition:
|
|
2528
|
-
border-color var(--fb-transition-duration, 200ms),
|
|
2529
|
-
color var(--fb-transition-duration, 200ms);
|
|
2530
|
-
background: none;
|
|
2531
|
-
padding: 0;
|
|
2532
|
-
width: var(--fb-tile-size, 160px);
|
|
2533
|
-
height: var(--fb-tile-size, 160px);
|
|
2534
|
-
flex-shrink: 0;
|
|
2535
|
-
position: relative;
|
|
2690
|
+
/* \u2500\u2500\u2500 Readonly readonly tile \u2500\u2500\u2500 */
|
|
2691
|
+
.fb-readonly-tile {
|
|
2692
|
+
aspect-ratio: 1 / 1;
|
|
2693
|
+
border-radius: 0.5rem;
|
|
2694
|
+
border: 1px solid #e2e8f0;
|
|
2536
2695
|
overflow: hidden;
|
|
2537
|
-
|
|
2696
|
+
position: relative;
|
|
2697
|
+
cursor: pointer;
|
|
2538
2698
|
}
|
|
2539
|
-
.fb-tile
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
|
|
2699
|
+
.fb-readonly-tile img {
|
|
2700
|
+
width: 100%;
|
|
2701
|
+
height: 100%;
|
|
2702
|
+
object-fit: contain;
|
|
2703
|
+
display: block;
|
|
2544
2704
|
}
|
|
2545
|
-
|
|
2546
|
-
/* Hover zoom preview popup for image tiles \u2014 appended to document.body (fixed) */
|
|
2547
|
-
.fb-tile-zoom-preview {
|
|
2548
|
-
position: fixed;
|
|
2549
|
-
z-index: 9999;
|
|
2550
|
-
background: var(--fb-background-color, #fff);
|
|
2551
|
-
border: 1px solid var(--fb-file-upload-border-color, #d1d5db);
|
|
2552
|
-
border-radius: var(--fb-border-radius, 0.5rem);
|
|
2553
|
-
box-shadow: 0 4px 16px rgba(0,0,0,0.18);
|
|
2554
|
-
padding: 4px;
|
|
2555
|
-
width: 350px;
|
|
2556
|
-
height: 350px;
|
|
2557
|
-
pointer-events: none;
|
|
2705
|
+
.fb-readonly-tile .fb-tile-actions {
|
|
2558
2706
|
opacity: 0;
|
|
2559
|
-
transition: opacity 150ms ease;
|
|
2560
2707
|
}
|
|
2561
|
-
.fb-tile
|
|
2708
|
+
.fb-readonly-tile:hover .fb-tile-actions {
|
|
2562
2709
|
opacity: 1;
|
|
2563
2710
|
}
|
|
2564
|
-
|
|
2711
|
+
|
|
2712
|
+
/* \u2500\u2500\u2500 Readonly single-file filled \u2500\u2500\u2500 */
|
|
2713
|
+
.fb-single-readonly-filled {
|
|
2714
|
+
position: relative;
|
|
2715
|
+
border-radius: 0.75rem;
|
|
2716
|
+
border: 1px solid #e2e8f0;
|
|
2717
|
+
overflow: hidden;
|
|
2718
|
+
height: 220px;
|
|
2719
|
+
display: block;
|
|
2720
|
+
cursor: pointer;
|
|
2721
|
+
}
|
|
2722
|
+
.fb-single-readonly-filled img {
|
|
2565
2723
|
width: 100%;
|
|
2566
2724
|
height: 100%;
|
|
2567
2725
|
object-fit: contain;
|
|
2568
2726
|
display: block;
|
|
2569
|
-
|
|
2570
|
-
|
|
2727
|
+
}
|
|
2728
|
+
|
|
2729
|
+
/* \u2500\u2500\u2500 Readonly multi grid \u2500\u2500\u2500 */
|
|
2730
|
+
.fb-multi-readonly-grid {
|
|
2731
|
+
display: grid;
|
|
2732
|
+
grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
|
|
2733
|
+
gap: 10px;
|
|
2571
2734
|
}
|
|
2572
2735
|
`;
|
|
2573
2736
|
document.head.appendChild(style);
|
|
2574
2737
|
}
|
|
2575
2738
|
|
|
2576
2739
|
// src/components/file/dom.ts
|
|
2577
|
-
var TILE_SIZE = "160px";
|
|
2578
2740
|
function createFileTile() {
|
|
2579
2741
|
ensureFileStyles();
|
|
2580
2742
|
const tile = document.createElement("div");
|
|
@@ -2583,7 +2745,7 @@ function createFileTile() {
|
|
|
2583
2745
|
}
|
|
2584
2746
|
function showFileError(container, message) {
|
|
2585
2747
|
var _a, _b;
|
|
2586
|
-
const existing = (_a = container.closest("
|
|
2748
|
+
const existing = (_a = container.closest("[data-files-wrapper]")) == null ? void 0 : _a.querySelector(".file-error-message");
|
|
2587
2749
|
if (existing) existing.remove();
|
|
2588
2750
|
const errorEl = document.createElement("div");
|
|
2589
2751
|
errorEl.className = "file-error-message error-message";
|
|
@@ -2593,11 +2755,11 @@ function showFileError(container, message) {
|
|
|
2593
2755
|
margin-top: 0.25rem;
|
|
2594
2756
|
`;
|
|
2595
2757
|
errorEl.textContent = message;
|
|
2596
|
-
(_b = container.closest("
|
|
2758
|
+
(_b = container.closest("[data-files-wrapper]")) == null ? void 0 : _b.appendChild(errorEl);
|
|
2597
2759
|
}
|
|
2598
2760
|
function clearFileError(container) {
|
|
2599
2761
|
var _a;
|
|
2600
|
-
const existing = (_a = container.closest("
|
|
2762
|
+
const existing = (_a = container.closest("[data-files-wrapper]")) == null ? void 0 : _a.querySelector(".file-error-message");
|
|
2601
2763
|
if (existing) existing.remove();
|
|
2602
2764
|
}
|
|
2603
2765
|
function addDeleteButton(container, state, onDelete) {
|
|
@@ -2615,14 +2777,6 @@ function addDeleteButton(container, state, onDelete) {
|
|
|
2615
2777
|
overlay.appendChild(deleteBtn);
|
|
2616
2778
|
container.appendChild(overlay);
|
|
2617
2779
|
}
|
|
2618
|
-
function findFilePicker(container) {
|
|
2619
|
-
var _a;
|
|
2620
|
-
let el = container.parentElement;
|
|
2621
|
-
while (el && !el.dataset.filesWrapper) {
|
|
2622
|
-
el = el.parentElement;
|
|
2623
|
-
}
|
|
2624
|
-
return (_a = el == null ? void 0 : el.querySelector('input[type="file"]')) != null ? _a : null;
|
|
2625
|
-
}
|
|
2626
2780
|
function createUploadingTile(fileName, state) {
|
|
2627
2781
|
ensureFileStyles();
|
|
2628
2782
|
const tile = createFileTile();
|
|
@@ -2638,10 +2792,14 @@ function createUploadingTile(fileName, state) {
|
|
|
2638
2792
|
return tile;
|
|
2639
2793
|
}
|
|
2640
2794
|
function ensureTilesWrap(list) {
|
|
2795
|
+
var _a, _b, _c;
|
|
2796
|
+
const existingGrid = list.querySelector(".fb-multi-grid");
|
|
2797
|
+
if (existingGrid) return existingGrid;
|
|
2641
2798
|
const existing = list.querySelector(".fb-tiles-wrap");
|
|
2642
2799
|
if (existing) return existing;
|
|
2643
|
-
|
|
2644
|
-
|
|
2800
|
+
(_a = list.querySelector(".fb-file-dropzone")) == null ? void 0 : _a.remove();
|
|
2801
|
+
(_b = list.querySelector(".fb-wide-tile")) == null ? void 0 : _b.remove();
|
|
2802
|
+
(_c = list.querySelector(".fb-multi-outer")) == null ? void 0 : _c.remove();
|
|
2645
2803
|
const tilesWrap = document.createElement("div");
|
|
2646
2804
|
tilesWrap.className = "fb-tiles-wrap";
|
|
2647
2805
|
tilesWrap.style.cssText = "display:flex;flex-wrap:wrap;gap:6px;align-items:flex-start;";
|
|
@@ -2653,7 +2811,7 @@ function ensureTilesWrap(list) {
|
|
|
2653
2811
|
return tilesWrap;
|
|
2654
2812
|
}
|
|
2655
2813
|
function setEmptyFileContainer(fileContainer, state, hint) {
|
|
2656
|
-
const hintHtml =
|
|
2814
|
+
const hintHtml = "";
|
|
2657
2815
|
fileContainer.innerHTML = `
|
|
2658
2816
|
<div class="flex flex-col items-center justify-center h-full text-gray-400">
|
|
2659
2817
|
<svg class="w-6 h-6 mb-2" fill="currentColor" viewBox="0 0 24 24">
|
|
@@ -2696,9 +2854,11 @@ function setupDragAndDrop(element, dropHandler) {
|
|
|
2696
2854
|
}
|
|
2697
2855
|
|
|
2698
2856
|
// src/components/file/preview.ts
|
|
2699
|
-
var ICON_DOWNLOAD = `<svg width="
|
|
2700
|
-
var ICON_OPEN = `<svg width="
|
|
2701
|
-
var ICON_REMOVE = `<svg width="
|
|
2857
|
+
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>`;
|
|
2858
|
+
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>`;
|
|
2859
|
+
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>`;
|
|
2860
|
+
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>`;
|
|
2861
|
+
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>`;
|
|
2702
2862
|
function canDownload(state, meta) {
|
|
2703
2863
|
return Boolean(
|
|
2704
2864
|
state.config.downloadFile || state.config.getDownloadUrl || state.config.getThumbnail || (meta == null ? void 0 : meta.file)
|
|
@@ -2710,7 +2870,16 @@ function canOpenInTab(state, meta) {
|
|
|
2710
2870
|
);
|
|
2711
2871
|
}
|
|
2712
2872
|
function createTileActions(options) {
|
|
2713
|
-
const {
|
|
2873
|
+
const {
|
|
2874
|
+
canRemove,
|
|
2875
|
+
removeHandler,
|
|
2876
|
+
state,
|
|
2877
|
+
resourceId,
|
|
2878
|
+
fileName,
|
|
2879
|
+
meta,
|
|
2880
|
+
replaceHandler,
|
|
2881
|
+
libraryHandler
|
|
2882
|
+
} = options;
|
|
2714
2883
|
const group = document.createElement("div");
|
|
2715
2884
|
group.className = "fb-tile-actions";
|
|
2716
2885
|
const makeBtn = (icon, label, cls) => {
|
|
@@ -2725,6 +2894,16 @@ function createTileActions(options) {
|
|
|
2725
2894
|
});
|
|
2726
2895
|
return btn;
|
|
2727
2896
|
};
|
|
2897
|
+
if (replaceHandler) {
|
|
2898
|
+
const replaceBtn = makeBtn(ICON_REPLACE, t("replaceFile", state), "fb-tile-action-replace");
|
|
2899
|
+
replaceBtn.addEventListener("click", () => replaceHandler());
|
|
2900
|
+
group.appendChild(replaceBtn);
|
|
2901
|
+
}
|
|
2902
|
+
if (libraryHandler) {
|
|
2903
|
+
const libBtn = makeBtn(ICON_LIBRARY, t("fromLibrary", state), "fb-tile-action-library");
|
|
2904
|
+
libBtn.addEventListener("click", () => libraryHandler());
|
|
2905
|
+
group.appendChild(libBtn);
|
|
2906
|
+
}
|
|
2728
2907
|
if (canDownload(state, meta)) {
|
|
2729
2908
|
const dlBtn = makeBtn(ICON_DOWNLOAD, t("downloadFile", state), "fb-tile-action-download");
|
|
2730
2909
|
dlBtn.addEventListener("click", () => {
|
|
@@ -2910,8 +3089,7 @@ function attachClonedActionListeners(cloned, original) {
|
|
|
2910
3089
|
}
|
|
2911
3090
|
function renderLocalImagePreview(container, file, fileName, state) {
|
|
2912
3091
|
const img = document.createElement("img");
|
|
2913
|
-
img.
|
|
2914
|
-
img.style.background = "var(--fb-file-upload-bg-color,#f3f4f6)";
|
|
3092
|
+
img.style.cssText = "width:100%;height:100%;object-fit:contain;background:var(--fb-file-upload-bg-color,#f3f4f6);";
|
|
2915
3093
|
img.alt = fileName || t("previewAlt", state);
|
|
2916
3094
|
const reader = new FileReader();
|
|
2917
3095
|
reader.onload = (e) => {
|
|
@@ -2934,7 +3112,7 @@ function renderLocalVideoPreview(container, file, videoType, resourceId, state,
|
|
|
2934
3112
|
const newContainer = setupDragDropless(container);
|
|
2935
3113
|
newContainer.innerHTML = `
|
|
2936
3114
|
<div class="fb-video-preview-wrap">
|
|
2937
|
-
<video
|
|
3115
|
+
<video style="width:100%;height:100%;object-fit:contain;" controls preload="auto" muted src="${videoUrl}">
|
|
2938
3116
|
${escapeHtml(t("videoNotSupported", state))}
|
|
2939
3117
|
</video>
|
|
2940
3118
|
<div class="fb-video-btn-overlay">
|
|
@@ -2980,11 +3158,11 @@ function handleVideoDelete(container, resourceId, state, deps) {
|
|
|
2980
3158
|
container.onclick = deps.fileUploadHandler;
|
|
2981
3159
|
}
|
|
2982
3160
|
container.innerHTML = `
|
|
2983
|
-
<div
|
|
2984
|
-
<svg
|
|
3161
|
+
<div style="display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;color:var(--fb-text-secondary-color,#9ca3af);">
|
|
3162
|
+
<svg style="width:1.5rem;height:1.5rem;margin-bottom:0.5rem;" fill="currentColor" viewBox="0 0 24 24">
|
|
2985
3163
|
<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"/>
|
|
2986
3164
|
</svg>
|
|
2987
|
-
<div
|
|
3165
|
+
<div style="font-size:0.875rem;text-align:center;">${escapeHtml(t("clickDragText", state))}</div>
|
|
2988
3166
|
</div>
|
|
2989
3167
|
`;
|
|
2990
3168
|
if (deps == null ? void 0 : deps.setupDrop) {
|
|
@@ -3002,11 +3180,11 @@ function renderDeleteButton(container, resourceId, state) {
|
|
|
3002
3180
|
hiddenInput.value = "";
|
|
3003
3181
|
}
|
|
3004
3182
|
container.innerHTML = `
|
|
3005
|
-
<div
|
|
3006
|
-
<svg
|
|
3183
|
+
<div style="display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;color:var(--fb-text-secondary-color,#9ca3af);">
|
|
3184
|
+
<svg style="width:1.5rem;height:1.5rem;margin-bottom:0.5rem;" fill="currentColor" viewBox="0 0 24 24">
|
|
3007
3185
|
<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
3186
|
</svg>
|
|
3009
|
-
<div
|
|
3187
|
+
<div style="font-size:0.875rem;text-align:center;">${escapeHtml(t("clickDragText", state))}</div>
|
|
3010
3188
|
</div>
|
|
3011
3189
|
`;
|
|
3012
3190
|
});
|
|
@@ -3026,7 +3204,7 @@ async function renderLocalFilePreview(container, meta, fileName, resourceId, isR
|
|
|
3026
3204
|
deps
|
|
3027
3205
|
);
|
|
3028
3206
|
} else {
|
|
3029
|
-
container.innerHTML = `<div
|
|
3207
|
+
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>`;
|
|
3030
3208
|
}
|
|
3031
3209
|
if (!isReadonly && !((_c = meta.type) == null ? void 0 : _c.startsWith("video/"))) {
|
|
3032
3210
|
renderDeleteButton(container, resourceId, state);
|
|
@@ -3034,7 +3212,7 @@ async function renderLocalFilePreview(container, meta, fileName, resourceId, isR
|
|
|
3034
3212
|
}
|
|
3035
3213
|
function renderUploadedVideoPreview(container, thumbnailUrl, state) {
|
|
3036
3214
|
const video = document.createElement("video");
|
|
3037
|
-
video.
|
|
3215
|
+
video.style.cssText = "width:100%;height:100%;object-fit:contain;";
|
|
3038
3216
|
video.controls = true;
|
|
3039
3217
|
video.preload = "metadata";
|
|
3040
3218
|
video.muted = true;
|
|
@@ -3056,8 +3234,7 @@ async function renderUploadedFilePreview(container, resourceId, fileName, meta,
|
|
|
3056
3234
|
renderUploadedVideoPreview(container, thumbnailUrl, state);
|
|
3057
3235
|
} else {
|
|
3058
3236
|
const img = document.createElement("img");
|
|
3059
|
-
img.
|
|
3060
|
-
img.style.background = "var(--fb-file-upload-bg-color,#f3f4f6)";
|
|
3237
|
+
img.style.cssText = "width:100%;height:100%;object-fit:contain;background:var(--fb-file-upload-bg-color,#f3f4f6);";
|
|
3061
3238
|
img.alt = fileName || t("previewAlt", state);
|
|
3062
3239
|
img.src = thumbnailUrl;
|
|
3063
3240
|
container.appendChild(img);
|
|
@@ -3068,11 +3245,11 @@ async function renderUploadedFilePreview(container, resourceId, fileName, meta,
|
|
|
3068
3245
|
} catch (error) {
|
|
3069
3246
|
console.error("Failed to get thumbnail:", error);
|
|
3070
3247
|
container.innerHTML = `
|
|
3071
|
-
<div
|
|
3072
|
-
<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">
|
|
3073
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"/>
|
|
3074
3251
|
</svg>
|
|
3075
|
-
<div
|
|
3252
|
+
<div style="font-size:0.875rem;text-align:center;">${escapeHtml(fileName || t("previewUnavailable", state))}</div>
|
|
3076
3253
|
</div>
|
|
3077
3254
|
`;
|
|
3078
3255
|
}
|
|
@@ -3245,20 +3422,15 @@ async function renderSingleFileEditTile(fileContainer, resourceId, state, deps)
|
|
|
3245
3422
|
fileContainer.appendChild(tile);
|
|
3246
3423
|
}
|
|
3247
3424
|
async function fillTileContent(tile, rid, meta, state, actionsEl) {
|
|
3248
|
-
var _a, _b
|
|
3425
|
+
var _a, _b;
|
|
3249
3426
|
if ((_a = meta == null ? void 0 : meta.type) == null ? void 0 : _a.startsWith("image/")) {
|
|
3250
3427
|
if (meta.file && meta.file instanceof File) {
|
|
3251
3428
|
const img = document.createElement("img");
|
|
3252
3429
|
img.style.cssText = "width:100%;height:100%;object-fit:contain;background:var(--fb-file-upload-bg-color,#f3f4f6);";
|
|
3253
3430
|
img.alt = meta.name;
|
|
3254
|
-
|
|
3255
|
-
reader.onload = (e) => {
|
|
3256
|
-
var _a2;
|
|
3257
|
-
img.src = ((_a2 = e.target) == null ? void 0 : _a2.result) || "";
|
|
3258
|
-
attachZoomHover(tile, img.src, meta.name, actionsEl != null ? actionsEl : null);
|
|
3259
|
-
};
|
|
3260
|
-
reader.readAsDataURL(meta.file);
|
|
3431
|
+
img.src = getLocalFileUrl(meta.file);
|
|
3261
3432
|
tile.appendChild(img);
|
|
3433
|
+
attachZoomHover(tile, img.src, meta.name, actionsEl != null ? actionsEl : null);
|
|
3262
3434
|
} else if (state.config.getThumbnail) {
|
|
3263
3435
|
try {
|
|
3264
3436
|
const url = await state.config.getThumbnail(rid);
|
|
@@ -3315,17 +3487,21 @@ async function fillTileContent(tile, rid, meta, state, actionsEl) {
|
|
|
3315
3487
|
}
|
|
3316
3488
|
if (actionsEl) tile.appendChild(actionsEl);
|
|
3317
3489
|
} else {
|
|
3318
|
-
|
|
3319
|
-
const hasExtension = name.includes(".");
|
|
3320
|
-
const captionHtml = hasExtension ? `<div class="fb-tile-label">${escapeHtml(name.length > 10 ? name.substring(0, 8) + "\u2026" : name)}</div>` : "";
|
|
3321
|
-
tile.innerHTML = `
|
|
3322
|
-
<div style="display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;padding:6px;gap:4px;">
|
|
3323
|
-
<div style="font-size:36px;">\u{1F4C1}</div>
|
|
3324
|
-
${captionHtml}
|
|
3325
|
-
</div>`;
|
|
3326
|
-
if (actionsEl) tile.appendChild(actionsEl);
|
|
3490
|
+
fillDocumentFallback(tile, rid, meta, actionsEl);
|
|
3327
3491
|
}
|
|
3328
3492
|
}
|
|
3493
|
+
function fillDocumentFallback(tile, rid, meta, actionsEl) {
|
|
3494
|
+
var _a, _b;
|
|
3495
|
+
const fileName = (_b = (_a = meta == null ? void 0 : meta.name) != null ? _a : rid.split("/").pop()) != null ? _b : "";
|
|
3496
|
+
if (fileName) tile.title = fileName;
|
|
3497
|
+
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>` : "";
|
|
3498
|
+
tile.innerHTML = `
|
|
3499
|
+
<div style="display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;padding:6px;gap:4px;">
|
|
3500
|
+
<div style="font-size:36px;">\u{1F4C1}</div>
|
|
3501
|
+
${labelHtml}
|
|
3502
|
+
</div>`;
|
|
3503
|
+
if (actionsEl) tile.appendChild(actionsEl);
|
|
3504
|
+
}
|
|
3329
3505
|
async function forceDownload(resourceId, fileName, state) {
|
|
3330
3506
|
try {
|
|
3331
3507
|
let fileUrl = null;
|
|
@@ -3390,7 +3566,7 @@ async function uploadSingleFile(file, state) {
|
|
|
3390
3566
|
}
|
|
3391
3567
|
}
|
|
3392
3568
|
async function handleFileSelect(opts) {
|
|
3393
|
-
var _a, _b;
|
|
3569
|
+
var _a, _b, _c;
|
|
3394
3570
|
const {
|
|
3395
3571
|
file,
|
|
3396
3572
|
container,
|
|
@@ -3426,6 +3602,10 @@ async function handleFileSelect(opts) {
|
|
|
3426
3602
|
return;
|
|
3427
3603
|
}
|
|
3428
3604
|
clearFileError(container);
|
|
3605
|
+
const existingHiddenInput = (_a = container.parentElement) == null ? void 0 : _a.querySelector(
|
|
3606
|
+
'input[type="hidden"]'
|
|
3607
|
+
);
|
|
3608
|
+
const previousRid = (existingHiddenInput == null ? void 0 : existingHiddenInput.value) || null;
|
|
3429
3609
|
ensureFileStyles();
|
|
3430
3610
|
container.innerHTML = `
|
|
3431
3611
|
<div style="display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;gap:6px;padding:6px;">
|
|
@@ -3436,7 +3616,13 @@ async function handleFileSelect(opts) {
|
|
|
3436
3616
|
try {
|
|
3437
3617
|
rid = await uploadSingleFile(file, state);
|
|
3438
3618
|
} catch (error) {
|
|
3439
|
-
|
|
3619
|
+
if (previousRid && (deps == null ? void 0 : deps.onAfterUpload)) {
|
|
3620
|
+
deps.onAfterUpload(container, previousRid);
|
|
3621
|
+
} else if (deps == null ? void 0 : deps.onRemove) {
|
|
3622
|
+
deps.onRemove();
|
|
3623
|
+
} else {
|
|
3624
|
+
setEmptyFileContainer(container, state);
|
|
3625
|
+
}
|
|
3440
3626
|
throw error;
|
|
3441
3627
|
}
|
|
3442
3628
|
state.resourceIndex.set(rid, {
|
|
@@ -3446,18 +3632,21 @@ async function handleFileSelect(opts) {
|
|
|
3446
3632
|
uploadedAt: /* @__PURE__ */ new Date(),
|
|
3447
3633
|
file
|
|
3448
3634
|
});
|
|
3449
|
-
|
|
3450
|
-
|
|
3451
|
-
|
|
3635
|
+
if (previousRid && previousRid !== rid) {
|
|
3636
|
+
releaseLocalFileUrl((_b = state.resourceIndex.get(previousRid)) == null ? void 0 : _b.file);
|
|
3637
|
+
}
|
|
3638
|
+
let hiddenInput = existingHiddenInput;
|
|
3452
3639
|
if (!hiddenInput) {
|
|
3453
3640
|
hiddenInput = document.createElement("input");
|
|
3454
3641
|
hiddenInput.type = "hidden";
|
|
3455
3642
|
hiddenInput.name = fieldName;
|
|
3456
|
-
(
|
|
3643
|
+
(_c = container.parentElement) == null ? void 0 : _c.appendChild(hiddenInput);
|
|
3457
3644
|
}
|
|
3458
3645
|
hiddenInput.value = rid;
|
|
3459
3646
|
const isVideo = file.type.startsWith("video/");
|
|
3460
|
-
if (!isVideo && deps) {
|
|
3647
|
+
if (!isVideo && (deps == null ? void 0 : deps.onAfterUpload)) {
|
|
3648
|
+
deps.onAfterUpload(container, rid);
|
|
3649
|
+
} else if (!isVideo && deps) {
|
|
3461
3650
|
renderSingleFileEditTile(container, rid, state, deps).catch(console.error);
|
|
3462
3651
|
} else {
|
|
3463
3652
|
renderFilePreview(container, rid, state, {
|
|
@@ -3518,17 +3707,18 @@ function filterAndSlice(allFiles, currentCount, constraints, state) {
|
|
|
3518
3707
|
return { accepted, errorMessage: errorParts.join(" \u2022 ") };
|
|
3519
3708
|
}
|
|
3520
3709
|
async function uploadBatch(accepted, resourceIds, listEl, state) {
|
|
3710
|
+
var _a;
|
|
3711
|
+
if (listEl) {
|
|
3712
|
+
const tilesWrap = ensureTilesWrap(listEl);
|
|
3713
|
+
const addTile = (_a = tilesWrap.querySelector(".fb-multi-add-tile-js")) != null ? _a : tilesWrap.querySelector(".fb-tile-add");
|
|
3714
|
+
if (addTile) addTile.style.display = "none";
|
|
3715
|
+
}
|
|
3521
3716
|
await Promise.all(
|
|
3522
3717
|
accepted.map(async (file) => {
|
|
3523
3718
|
const placeholder = createUploadingTile(file.name, state);
|
|
3524
3719
|
if (listEl) {
|
|
3525
3720
|
const tilesWrap = ensureTilesWrap(listEl);
|
|
3526
|
-
|
|
3527
|
-
if (addTile) {
|
|
3528
|
-
tilesWrap.insertBefore(placeholder, addTile);
|
|
3529
|
-
} else {
|
|
3530
|
-
tilesWrap.appendChild(placeholder);
|
|
3531
|
-
}
|
|
3721
|
+
tilesWrap.appendChild(placeholder);
|
|
3532
3722
|
}
|
|
3533
3723
|
try {
|
|
3534
3724
|
const rid = await uploadSingleFile(file, state);
|
|
@@ -3571,7 +3761,7 @@ function setupFilesDropHandler(filesContainer, resourceIds, state, updateCallbac
|
|
|
3571
3761
|
function setupFilesPickerHandler(filesPicker, resourceIds, state, updateCallback, constraints, pathKey, instance) {
|
|
3572
3762
|
filesPicker.onchange = async () => {
|
|
3573
3763
|
if (!filesPicker.files) return;
|
|
3574
|
-
const wrapperEl = filesPicker.closest("
|
|
3764
|
+
const wrapperEl = filesPicker.closest("[data-files-wrapper]") || filesPicker.parentElement;
|
|
3575
3765
|
const { accepted, errorMessage } = filterAndSlice(
|
|
3576
3766
|
Array.from(filesPicker.files),
|
|
3577
3767
|
resourceIds.length,
|
|
@@ -3706,7 +3896,7 @@ async function handleLibraryPickMulti(state, element, wrapper, fieldPath, resour
|
|
|
3706
3896
|
}
|
|
3707
3897
|
}
|
|
3708
3898
|
async function handleLibraryPickSingle(state, element, container, fileWrapper, pathKey, fieldPath, renderCallback, instance) {
|
|
3709
|
-
var _a;
|
|
3899
|
+
var _a, _b;
|
|
3710
3900
|
if (!state.config.pickExistingFiles) return;
|
|
3711
3901
|
const allowedExtensions = getAllowedExtensions(element.accept);
|
|
3712
3902
|
const allowedMimes = getAllowedMimes(element.accept);
|
|
@@ -3740,6 +3930,10 @@ async function handleLibraryPickSingle(state, element, container, fileWrapper, p
|
|
|
3740
3930
|
hiddenInput.name = pathKey;
|
|
3741
3931
|
fileWrapper.appendChild(hiddenInput);
|
|
3742
3932
|
}
|
|
3933
|
+
const previousRid = hiddenInput.value || null;
|
|
3934
|
+
if (previousRid && previousRid !== first.resourceId) {
|
|
3935
|
+
releaseLocalFileUrl((_b = state.resourceIndex.get(previousRid)) == null ? void 0 : _b.file);
|
|
3936
|
+
}
|
|
3743
3937
|
hiddenInput.value = first.resourceId;
|
|
3744
3938
|
await renderCallback(first.resourceId);
|
|
3745
3939
|
if (!state.config.readonly) {
|
|
@@ -3748,7 +3942,9 @@ async function handleLibraryPickSingle(state, element, container, fileWrapper, p
|
|
|
3748
3942
|
}
|
|
3749
3943
|
|
|
3750
3944
|
// src/components/file/render-edit.ts
|
|
3751
|
-
|
|
3945
|
+
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>`;
|
|
3946
|
+
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>`;
|
|
3947
|
+
function handleInitialFileData(initial, fileContainer, pathKey, fileWrapper, state, deps, extras) {
|
|
3752
3948
|
var _a;
|
|
3753
3949
|
seedInferredResource(initial, state.resourceIndex);
|
|
3754
3950
|
const meta = state.resourceIndex.get(initial);
|
|
@@ -3760,7 +3956,7 @@ function handleInitialFileData(initial, fileContainer, pathKey, fileWrapper, sta
|
|
|
3760
3956
|
deps
|
|
3761
3957
|
}).catch(console.error);
|
|
3762
3958
|
} else {
|
|
3763
|
-
|
|
3959
|
+
renderSingleFileFilled(fileContainer, initial, state, deps, extras);
|
|
3764
3960
|
}
|
|
3765
3961
|
const hiddenInput = document.createElement("input");
|
|
3766
3962
|
hiddenInput.type = "hidden";
|
|
@@ -3768,38 +3964,252 @@ function handleInitialFileData(initial, fileContainer, pathKey, fileWrapper, sta
|
|
|
3768
3964
|
hiddenInput.value = initial;
|
|
3769
3965
|
fileWrapper.appendChild(hiddenInput);
|
|
3770
3966
|
}
|
|
3771
|
-
|
|
3772
|
-
|
|
3773
|
-
|
|
3774
|
-
|
|
3775
|
-
const
|
|
3776
|
-
|
|
3777
|
-
|
|
3778
|
-
|
|
3779
|
-
|
|
3780
|
-
|
|
3781
|
-
|
|
3782
|
-
|
|
3783
|
-
|
|
3967
|
+
function buildWideTile(state, hasLibrary, onUploadClick, onLibraryClick, isDragOver = false, constraintHint = "") {
|
|
3968
|
+
ensureFileStyles();
|
|
3969
|
+
const outer = document.createElement("div");
|
|
3970
|
+
outer.className = `fb-wide-tile${hasLibrary ? " fb-file-card-row" : ""}${isDragOver ? " fb-drag-over" : ""}`;
|
|
3971
|
+
const uploadBtn = document.createElement("button");
|
|
3972
|
+
uploadBtn.type = "button";
|
|
3973
|
+
uploadBtn.className = "fb-wide-tile-upload fb-file-dropzone";
|
|
3974
|
+
const cloudIcon = document.createElement("span");
|
|
3975
|
+
cloudIcon.style.cssText = "width:36px;height:36px;display:block;flex-shrink:0;";
|
|
3976
|
+
cloudIcon.innerHTML = ICON_CLOUD;
|
|
3977
|
+
uploadBtn.appendChild(cloudIcon);
|
|
3978
|
+
const primaryText = document.createElement("div");
|
|
3979
|
+
primaryText.className = "fb-wide-tile-label";
|
|
3980
|
+
primaryText.style.cssText = "font-size:14px;font-weight:600;";
|
|
3981
|
+
primaryText.textContent = isDragOver ? t("dropToUpload", state) : t("clickDragText", state);
|
|
3982
|
+
uploadBtn.appendChild(primaryText);
|
|
3983
|
+
if (constraintHint) {
|
|
3984
|
+
const hintEl = document.createElement("div");
|
|
3985
|
+
hintEl.style.cssText = "font-size:11px;opacity:0.65;margin-top:2px;";
|
|
3986
|
+
hintEl.textContent = constraintHint;
|
|
3987
|
+
uploadBtn.appendChild(hintEl);
|
|
3988
|
+
}
|
|
3989
|
+
uploadBtn.onclick = (e) => {
|
|
3990
|
+
e.stopPropagation();
|
|
3991
|
+
onUploadClick();
|
|
3992
|
+
};
|
|
3993
|
+
outer.appendChild(uploadBtn);
|
|
3994
|
+
if (hasLibrary && onLibraryClick) {
|
|
3995
|
+
const divider = document.createElement("div");
|
|
3996
|
+
divider.className = "fb-wide-tile-divider";
|
|
3997
|
+
outer.appendChild(divider);
|
|
3998
|
+
const libBtn = document.createElement("button");
|
|
3999
|
+
libBtn.type = "button";
|
|
4000
|
+
libBtn.className = "fb-wide-tile-library fb-file-library-card";
|
|
4001
|
+
const libIcon = document.createElement("span");
|
|
4002
|
+
libIcon.style.cssText = "width:28px;height:28px;display:block;flex-shrink:0;";
|
|
4003
|
+
libIcon.innerHTML = ICON_LIBRARY2;
|
|
4004
|
+
libBtn.appendChild(libIcon);
|
|
4005
|
+
const libLabel = document.createElement("div");
|
|
4006
|
+
libLabel.style.cssText = "font-size:13px;font-weight:600;text-align:center;";
|
|
4007
|
+
libLabel.textContent = t("fromLibrary", state);
|
|
4008
|
+
libBtn.appendChild(libLabel);
|
|
4009
|
+
const libHint = document.createElement("div");
|
|
4010
|
+
libHint.style.cssText = "font-size:11px;opacity:0.75;text-align:center;";
|
|
4011
|
+
libHint.textContent = t("libraryHint", state);
|
|
4012
|
+
libBtn.appendChild(libHint);
|
|
4013
|
+
libBtn.onclick = (e) => {
|
|
4014
|
+
e.stopPropagation();
|
|
4015
|
+
onLibraryClick();
|
|
4016
|
+
};
|
|
4017
|
+
outer.appendChild(libBtn);
|
|
4018
|
+
}
|
|
4019
|
+
attachDragOverFeedback(outer, {
|
|
4020
|
+
onEnter: () => {
|
|
4021
|
+
const primaryText2 = outer.querySelector(".fb-wide-tile-label");
|
|
4022
|
+
if (primaryText2) primaryText2.textContent = t("dropToUpload", state);
|
|
4023
|
+
},
|
|
4024
|
+
onLeave: () => {
|
|
4025
|
+
const primaryText2 = outer.querySelector(".fb-wide-tile-label");
|
|
4026
|
+
if (primaryText2) primaryText2.textContent = t("clickDragText", state);
|
|
4027
|
+
},
|
|
4028
|
+
activeClass: "fb-drag-over"
|
|
4029
|
+
});
|
|
4030
|
+
return outer;
|
|
3784
4031
|
}
|
|
3785
|
-
function
|
|
3786
|
-
|
|
3787
|
-
|
|
3788
|
-
|
|
3789
|
-
|
|
3790
|
-
|
|
3791
|
-
|
|
3792
|
-
|
|
3793
|
-
|
|
3794
|
-
|
|
4032
|
+
function attachDragOverFeedback(el, hooks) {
|
|
4033
|
+
let depth = 0;
|
|
4034
|
+
el.addEventListener("dragover", (e) => {
|
|
4035
|
+
e.preventDefault();
|
|
4036
|
+
});
|
|
4037
|
+
el.addEventListener("dragenter", (e) => {
|
|
4038
|
+
e.preventDefault();
|
|
4039
|
+
depth++;
|
|
4040
|
+
if (depth === 1) {
|
|
4041
|
+
el.classList.add(hooks.activeClass);
|
|
4042
|
+
hooks.onEnter();
|
|
4043
|
+
}
|
|
4044
|
+
});
|
|
4045
|
+
el.addEventListener("dragleave", (e) => {
|
|
4046
|
+
e.preventDefault();
|
|
4047
|
+
depth = Math.max(0, depth - 1);
|
|
4048
|
+
if (depth === 0) {
|
|
4049
|
+
el.classList.remove(hooks.activeClass);
|
|
4050
|
+
hooks.onLeave();
|
|
4051
|
+
}
|
|
4052
|
+
});
|
|
4053
|
+
el.addEventListener("drop", () => {
|
|
4054
|
+
depth = 0;
|
|
4055
|
+
el.classList.remove(hooks.activeClass);
|
|
4056
|
+
hooks.onLeave();
|
|
4057
|
+
});
|
|
4058
|
+
}
|
|
4059
|
+
function renderSingleFileFilled(fileContainer, resourceId, state, deps, extras) {
|
|
4060
|
+
var _a, _b;
|
|
4061
|
+
const meta = state.resourceIndex.get(resourceId);
|
|
4062
|
+
const isVideo = (_a = meta == null ? void 0 : meta.type) == null ? void 0 : _a.startsWith("video/");
|
|
4063
|
+
if (isVideo) {
|
|
4064
|
+
renderFilePreview(fileContainer, resourceId, state, {
|
|
4065
|
+
fileName: (_b = meta == null ? void 0 : meta.name) != null ? _b : "",
|
|
4066
|
+
isReadonly: false,
|
|
4067
|
+
deps
|
|
4068
|
+
}).catch(console.error);
|
|
4069
|
+
return;
|
|
4070
|
+
}
|
|
4071
|
+
ensureFileStyles();
|
|
4072
|
+
const outer = document.createElement("div");
|
|
4073
|
+
outer.className = "fb-multi-outer fb-multi-has-files";
|
|
4074
|
+
const grid = document.createElement("div");
|
|
4075
|
+
grid.className = "fb-multi-grid fb-tiles-wrap";
|
|
4076
|
+
outer.appendChild(grid);
|
|
4077
|
+
const tile = buildPreviewTile(
|
|
4078
|
+
resourceId,
|
|
4079
|
+
state,
|
|
4080
|
+
Boolean(deps.onRemove),
|
|
4081
|
+
deps.onRemove ? () => {
|
|
4082
|
+
var _a2;
|
|
4083
|
+
return (_a2 = deps.onRemove) == null ? void 0 : _a2.call(deps);
|
|
4084
|
+
} : null,
|
|
4085
|
+
extras
|
|
4086
|
+
);
|
|
4087
|
+
grid.appendChild(tile);
|
|
4088
|
+
fileContainer.className = "file-preview-container";
|
|
4089
|
+
fileContainer.removeAttribute("style");
|
|
4090
|
+
while (fileContainer.firstChild) fileContainer.removeChild(fileContainer.firstChild);
|
|
4091
|
+
fileContainer.appendChild(outer);
|
|
4092
|
+
}
|
|
4093
|
+
function buildMultiAddTile(state, hasLibrary, onUploadClick, onLibraryClick, isDragOver = false) {
|
|
4094
|
+
const tile = document.createElement("div");
|
|
4095
|
+
tile.className = `fb-multi-add-tile fb-multi-add-tile-js${isDragOver ? " fb-drag-over-tile" : ""}`;
|
|
4096
|
+
const uploadBtn = document.createElement("button");
|
|
4097
|
+
uploadBtn.type = "button";
|
|
4098
|
+
uploadBtn.className = "fb-multi-add-upload fb-tile-add fb-file-dropzone";
|
|
4099
|
+
const cloudIcon = document.createElement("span");
|
|
4100
|
+
cloudIcon.style.cssText = "width:28px;height:28px;display:block;flex-shrink:0;";
|
|
4101
|
+
cloudIcon.innerHTML = ICON_CLOUD;
|
|
4102
|
+
uploadBtn.appendChild(cloudIcon);
|
|
4103
|
+
const uploadLabel = document.createElement("span");
|
|
4104
|
+
uploadLabel.className = "fb-multi-add-label";
|
|
4105
|
+
uploadLabel.style.cssText = "font-size:11px;font-weight:600;";
|
|
4106
|
+
uploadLabel.textContent = isDragOver ? t("dropToUpload", state) : t("clickDragTextMultiple", state);
|
|
4107
|
+
uploadBtn.appendChild(uploadLabel);
|
|
4108
|
+
uploadBtn.onclick = (e) => {
|
|
4109
|
+
e.stopPropagation();
|
|
4110
|
+
onUploadClick();
|
|
4111
|
+
};
|
|
4112
|
+
tile.appendChild(uploadBtn);
|
|
4113
|
+
if (hasLibrary && onLibraryClick) {
|
|
4114
|
+
const divider = document.createElement("div");
|
|
4115
|
+
divider.className = "fb-multi-add-divider";
|
|
4116
|
+
tile.appendChild(divider);
|
|
4117
|
+
const libBtn = document.createElement("button");
|
|
4118
|
+
libBtn.type = "button";
|
|
4119
|
+
libBtn.className = "fb-multi-add-library fb-tile-add-library fb-file-library-card";
|
|
4120
|
+
libBtn.setAttribute("aria-label", t("fromLibrary", state));
|
|
4121
|
+
const libIcon = document.createElement("span");
|
|
4122
|
+
libIcon.style.cssText = "width:14px;height:14px;display:block;flex-shrink:0;";
|
|
4123
|
+
libIcon.innerHTML = ICON_LIBRARY2;
|
|
4124
|
+
libBtn.appendChild(libIcon);
|
|
4125
|
+
libBtn.appendChild(document.createTextNode(t("fromLibrary", state)));
|
|
4126
|
+
libBtn.onclick = (e) => {
|
|
4127
|
+
e.stopPropagation();
|
|
4128
|
+
onLibraryClick();
|
|
4129
|
+
};
|
|
4130
|
+
tile.appendChild(libBtn);
|
|
4131
|
+
}
|
|
4132
|
+
return tile;
|
|
4133
|
+
}
|
|
4134
|
+
function buildPreviewTile(rid, state, canRemove, onRemove, extras) {
|
|
4135
|
+
var _a, _b, _c;
|
|
4136
|
+
ensureFileStyles();
|
|
4137
|
+
const meta = state.resourceIndex.get(rid);
|
|
4138
|
+
const tile = document.createElement("div");
|
|
4139
|
+
tile.className = "fb-preview-tile fb-checker fb-tile-resource resource-pill";
|
|
4140
|
+
tile.dataset.resourceId = rid;
|
|
4141
|
+
const actionsEl = createTileActions({
|
|
4142
|
+
canRemove: canRemove && onRemove !== null,
|
|
4143
|
+
removeHandler: onRemove,
|
|
4144
|
+
state,
|
|
4145
|
+
resourceId: rid,
|
|
4146
|
+
fileName: (_a = meta == null ? void 0 : meta.name) != null ? _a : "",
|
|
4147
|
+
meta,
|
|
4148
|
+
replaceHandler: (_b = extras == null ? void 0 : extras.replaceHandler) != null ? _b : null,
|
|
4149
|
+
libraryHandler: (_c = extras == null ? void 0 : extras.libraryHandler) != null ? _c : null
|
|
4150
|
+
});
|
|
4151
|
+
fillTileContent(tile, rid, meta, state, actionsEl).catch((err) => {
|
|
4152
|
+
console.error("Failed to render tile:", err);
|
|
4153
|
+
});
|
|
4154
|
+
return tile;
|
|
4155
|
+
}
|
|
4156
|
+
function buildPlaceholderTile(isDragOver = false) {
|
|
4157
|
+
const div = document.createElement("div");
|
|
4158
|
+
div.className = `fb-multi-placeholder fb-checker${isDragOver ? " fb-drag-over" : ""}`;
|
|
4159
|
+
return div;
|
|
4160
|
+
}
|
|
4161
|
+
function buildMetaLine(state, element, ridCount, maxCount, canClearAll, onClearAll) {
|
|
4162
|
+
const line = document.createElement("div");
|
|
4163
|
+
line.className = "fb-meta-line";
|
|
4164
|
+
const metaText = document.createElement("div");
|
|
4165
|
+
metaText.className = "fb-meta-text";
|
|
4166
|
+
if (element.maxSize && element.maxSize !== Infinity) {
|
|
4167
|
+
const sizeSpan = document.createElement("span");
|
|
4168
|
+
sizeSpan.textContent = t("hintMaxSize", state, { size: element.maxSize });
|
|
4169
|
+
metaText.appendChild(sizeSpan);
|
|
4170
|
+
metaText.appendChild(buildMetaDot());
|
|
4171
|
+
}
|
|
4172
|
+
const exts = getAllowedExtensions(element.accept);
|
|
4173
|
+
if (exts.length > 0) {
|
|
4174
|
+
const fmtSpan = document.createElement("span");
|
|
4175
|
+
fmtSpan.className = "fb-meta-mono";
|
|
4176
|
+
fmtSpan.textContent = exts.map((e) => e.toUpperCase()).join(", ");
|
|
4177
|
+
metaText.appendChild(fmtSpan);
|
|
4178
|
+
metaText.appendChild(buildMetaDot());
|
|
4179
|
+
}
|
|
4180
|
+
const countSpan = document.createElement("span");
|
|
4181
|
+
if (maxCount < Infinity) {
|
|
4182
|
+
countSpan.textContent = t("fileCountWithMax", state, {
|
|
4183
|
+
count: ridCount,
|
|
4184
|
+
max: maxCount
|
|
4185
|
+
});
|
|
3795
4186
|
} else {
|
|
3796
|
-
|
|
3797
|
-
|
|
3798
|
-
|
|
4187
|
+
const countKey = ridCount === 1 ? "fileCountSingle" : "fileCountPlural";
|
|
4188
|
+
countSpan.textContent = t(countKey, state, { count: ridCount });
|
|
4189
|
+
}
|
|
4190
|
+
metaText.appendChild(countSpan);
|
|
4191
|
+
line.appendChild(metaText);
|
|
4192
|
+
if (canClearAll && ridCount > 1) {
|
|
4193
|
+
const clearBtn = document.createElement("button");
|
|
4194
|
+
clearBtn.type = "button";
|
|
4195
|
+
clearBtn.className = "fb-clear-all-btn";
|
|
4196
|
+
clearBtn.textContent = t("clearAll", state);
|
|
4197
|
+
clearBtn.onclick = (e) => {
|
|
4198
|
+
e.stopPropagation();
|
|
4199
|
+
if (window.confirm(t("clearAll", state) + "?")) {
|
|
4200
|
+
onClearAll();
|
|
4201
|
+
}
|
|
4202
|
+
};
|
|
4203
|
+
line.appendChild(clearBtn);
|
|
3799
4204
|
}
|
|
3800
|
-
|
|
3801
|
-
return btn;
|
|
4205
|
+
return line;
|
|
3802
4206
|
}
|
|
4207
|
+
function buildMetaDot() {
|
|
4208
|
+
const dot = document.createElement("span");
|
|
4209
|
+
dot.className = "fb-meta-dot";
|
|
4210
|
+
return dot;
|
|
4211
|
+
}
|
|
4212
|
+
var gridResizeObservers = /* @__PURE__ */ new WeakMap();
|
|
3803
4213
|
function renderResourcePills(opts) {
|
|
3804
4214
|
var _a;
|
|
3805
4215
|
const {
|
|
@@ -3807,130 +4217,185 @@ function renderResourcePills(opts) {
|
|
|
3807
4217
|
rids,
|
|
3808
4218
|
state,
|
|
3809
4219
|
onRemove,
|
|
3810
|
-
hint,
|
|
3811
|
-
countInfo,
|
|
3812
4220
|
maxCount,
|
|
3813
4221
|
isReadonly = false,
|
|
3814
|
-
onLibraryPick
|
|
4222
|
+
onLibraryPick,
|
|
4223
|
+
element,
|
|
4224
|
+
onClearAll,
|
|
4225
|
+
openPicker: openPickerProp
|
|
3815
4226
|
} = opts;
|
|
3816
4227
|
ensureFileStyles();
|
|
3817
4228
|
const wrapper = container.closest("[data-files-wrapper]");
|
|
3818
4229
|
if (wrapper) {
|
|
3819
4230
|
wrapper.dataset.resourceIds = JSON.stringify(rids != null ? rids : []);
|
|
3820
4231
|
}
|
|
4232
|
+
const previousObserver = gridResizeObservers.get(container);
|
|
4233
|
+
if (previousObserver) {
|
|
4234
|
+
previousObserver.disconnect();
|
|
4235
|
+
gridResizeObservers.delete(container);
|
|
4236
|
+
}
|
|
3821
4237
|
while (container.firstChild) container.removeChild(container.firstChild);
|
|
3822
4238
|
const ridList = rids != null ? rids : [];
|
|
3823
|
-
const
|
|
4239
|
+
const effectiveMax = maxCount != null ? maxCount : Infinity;
|
|
4240
|
+
const atMax = effectiveMax !== Infinity && ridList.length >= effectiveMax;
|
|
3824
4241
|
const hasLibrary = !isReadonly && typeof onLibraryPick === "function";
|
|
3825
|
-
const
|
|
3826
|
-
|
|
3827
|
-
|
|
3828
|
-
if (
|
|
3829
|
-
|
|
3830
|
-
|
|
3831
|
-
|
|
3832
|
-
const picker = findFilePicker(container);
|
|
3833
|
-
if (picker) picker.click();
|
|
3834
|
-
};
|
|
3835
|
-
if (ridList.length === 0) {
|
|
3836
|
-
if (isReadonly) {
|
|
4242
|
+
const openPicker = openPickerProp != null ? openPickerProp : (() => {
|
|
4243
|
+
var _a2;
|
|
4244
|
+
const pickerEl = (_a2 = container.closest("[data-files-wrapper]")) == null ? void 0 : _a2.querySelector('input[type="file"]');
|
|
4245
|
+
if (pickerEl) pickerEl.click();
|
|
4246
|
+
});
|
|
4247
|
+
if (isReadonly) {
|
|
4248
|
+
if (ridList.length === 0) {
|
|
3837
4249
|
const emptyEl = document.createElement("div");
|
|
3838
4250
|
emptyEl.className = "fb-tile-empty-text";
|
|
3839
4251
|
emptyEl.textContent = t("noFilesSelected", state);
|
|
3840
4252
|
container.appendChild(emptyEl);
|
|
3841
|
-
} else if (hasLibrary) {
|
|
3842
|
-
const row = document.createElement("div");
|
|
3843
|
-
row.className = "fb-file-card-row";
|
|
3844
|
-
const dropzone = buildEmptyDropzone(
|
|
3845
|
-
state,
|
|
3846
|
-
t("clickDragTextMultiple", state),
|
|
3847
|
-
buildSubHint(),
|
|
3848
|
-
openPicker
|
|
3849
|
-
);
|
|
3850
|
-
const libraryBtn = buildLibraryButton("card", state, onLibraryPick);
|
|
3851
|
-
row.appendChild(dropzone);
|
|
3852
|
-
row.appendChild(libraryBtn);
|
|
3853
|
-
container.appendChild(row);
|
|
3854
4253
|
} else {
|
|
3855
|
-
const
|
|
3856
|
-
|
|
3857
|
-
|
|
3858
|
-
|
|
3859
|
-
|
|
3860
|
-
|
|
3861
|
-
|
|
4254
|
+
const grid2 = document.createElement("div");
|
|
4255
|
+
grid2.className = "fb-multi-readonly-grid";
|
|
4256
|
+
container.appendChild(grid2);
|
|
4257
|
+
for (const rid of ridList) {
|
|
4258
|
+
const meta = state.resourceIndex.get(rid);
|
|
4259
|
+
const tile = document.createElement("div");
|
|
4260
|
+
tile.className = "fb-readonly-tile fb-checker fb-tile fb-tile-resource";
|
|
4261
|
+
tile.dataset.resourceId = rid;
|
|
4262
|
+
const actionsEl = createTileActions({
|
|
4263
|
+
canRemove: false,
|
|
4264
|
+
removeHandler: null,
|
|
4265
|
+
state,
|
|
4266
|
+
resourceId: rid,
|
|
4267
|
+
fileName: (_a = meta == null ? void 0 : meta.name) != null ? _a : "",
|
|
4268
|
+
meta
|
|
4269
|
+
});
|
|
4270
|
+
fillTileContent(tile, rid, meta, state, actionsEl).catch(console.error);
|
|
4271
|
+
tile.onclick = async () => {
|
|
4272
|
+
var _a2;
|
|
4273
|
+
let url = null;
|
|
4274
|
+
if (state.config.getDownloadUrl) {
|
|
4275
|
+
url = state.config.getDownloadUrl(rid);
|
|
4276
|
+
} else if (state.config.getThumbnail) {
|
|
4277
|
+
url = await state.config.getThumbnail(rid);
|
|
4278
|
+
} else if ((meta == null ? void 0 : meta.file) instanceof File) {
|
|
4279
|
+
url = URL.createObjectURL(meta.file);
|
|
4280
|
+
}
|
|
4281
|
+
if (url) {
|
|
4282
|
+
window.open(url, "_blank");
|
|
4283
|
+
} else if (state.config.downloadFile) {
|
|
4284
|
+
state.config.downloadFile(rid, (_a2 = meta == null ? void 0 : meta.name) != null ? _a2 : "");
|
|
4285
|
+
}
|
|
4286
|
+
};
|
|
4287
|
+
grid2.appendChild(tile);
|
|
4288
|
+
}
|
|
3862
4289
|
}
|
|
3863
4290
|
return;
|
|
3864
4291
|
}
|
|
3865
|
-
const
|
|
3866
|
-
|
|
3867
|
-
|
|
3868
|
-
|
|
3869
|
-
|
|
3870
|
-
|
|
3871
|
-
|
|
3872
|
-
|
|
3873
|
-
const
|
|
3874
|
-
|
|
3875
|
-
removeHandler: onRemove ? () => onRemove(rid) : null,
|
|
4292
|
+
const outerDiv = document.createElement("div");
|
|
4293
|
+
outerDiv.className = `fb-multi-outer${ridList.length > 0 ? " fb-multi-has-files" : ""}`;
|
|
4294
|
+
const grid = document.createElement("div");
|
|
4295
|
+
grid.className = "fb-multi-grid fb-tiles-wrap";
|
|
4296
|
+
outerDiv.appendChild(grid);
|
|
4297
|
+
container.appendChild(outerDiv);
|
|
4298
|
+
for (let i = 0; i < ridList.length; i++) {
|
|
4299
|
+
const rid = ridList[i];
|
|
4300
|
+
const tile = buildPreviewTile(
|
|
4301
|
+
rid,
|
|
3876
4302
|
state,
|
|
3877
|
-
|
|
3878
|
-
|
|
3879
|
-
|
|
3880
|
-
|
|
3881
|
-
console.error("Failed to render tile:", err);
|
|
3882
|
-
});
|
|
3883
|
-
tilesWrap.appendChild(tile);
|
|
3884
|
-
}
|
|
3885
|
-
if (!isReadonly && !atMax) {
|
|
3886
|
-
const addTile = document.createElement("div");
|
|
3887
|
-
addTile.className = "fb-tile fb-tile-add";
|
|
3888
|
-
addTile.innerHTML = "+";
|
|
3889
|
-
addTile.onclick = openPicker;
|
|
3890
|
-
tilesWrap.appendChild(addTile);
|
|
3891
|
-
if (hasLibrary) {
|
|
3892
|
-
const libraryTile = buildLibraryButton("tile", state, onLibraryPick);
|
|
3893
|
-
tilesWrap.appendChild(libraryTile);
|
|
3894
|
-
}
|
|
3895
|
-
} else if (!isReadonly && atMax) {
|
|
3896
|
-
const chip = document.createElement("div");
|
|
3897
|
-
chip.className = "fb-tile-counter";
|
|
3898
|
-
chip.textContent = t("filesCounter", state, {
|
|
3899
|
-
count: ridList.length,
|
|
3900
|
-
max: maxCount
|
|
3901
|
-
});
|
|
3902
|
-
tilesWrap.appendChild(chip);
|
|
4303
|
+
onRemove !== null,
|
|
4304
|
+
onRemove ? () => onRemove(rid) : null
|
|
4305
|
+
);
|
|
4306
|
+
grid.appendChild(tile);
|
|
3903
4307
|
}
|
|
3904
|
-
|
|
3905
|
-
|
|
3906
|
-
|
|
3907
|
-
|
|
3908
|
-
|
|
3909
|
-
|
|
3910
|
-
|
|
4308
|
+
if (!atMax) {
|
|
4309
|
+
const addTile = buildMultiAddTile(
|
|
4310
|
+
state,
|
|
4311
|
+
hasLibrary,
|
|
4312
|
+
openPicker,
|
|
4313
|
+
onLibraryPick != null ? onLibraryPick : null
|
|
4314
|
+
);
|
|
4315
|
+
grid.appendChild(addTile);
|
|
4316
|
+
}
|
|
4317
|
+
const occupied = ridList.length + (atMax ? 0 : 1);
|
|
4318
|
+
const adjustPlaceholders = () => {
|
|
4319
|
+
const tpl = getComputedStyle(grid).gridTemplateColumns;
|
|
4320
|
+
const cols = tpl ? tpl.split(" ").filter(Boolean).length : 0;
|
|
4321
|
+
if (!cols) return;
|
|
4322
|
+
const remainder = occupied % cols;
|
|
4323
|
+
const rowFill = remainder === 0 ? 0 : cols - remainder;
|
|
4324
|
+
const capacityRemaining = effectiveMax === Infinity ? rowFill : Math.max(0, effectiveMax - occupied);
|
|
4325
|
+
const needed = Math.min(rowFill, capacityRemaining);
|
|
4326
|
+
const existing = grid.querySelectorAll(".fb-multi-placeholder");
|
|
4327
|
+
if (existing.length > needed) {
|
|
4328
|
+
for (let i = existing.length - 1; i >= needed; i--) existing[i].remove();
|
|
4329
|
+
} else if (existing.length < needed) {
|
|
4330
|
+
for (let i = existing.length; i < needed; i++) {
|
|
4331
|
+
grid.appendChild(buildPlaceholderTile());
|
|
4332
|
+
}
|
|
4333
|
+
}
|
|
4334
|
+
};
|
|
4335
|
+
if (effectiveMax === Infinity || effectiveMax > occupied) {
|
|
4336
|
+
grid.appendChild(buildPlaceholderTile());
|
|
4337
|
+
}
|
|
4338
|
+
requestAnimationFrame(adjustPlaceholders);
|
|
4339
|
+
if (typeof ResizeObserver !== "undefined") {
|
|
4340
|
+
const ro = new ResizeObserver(() => adjustPlaceholders());
|
|
4341
|
+
ro.observe(grid);
|
|
4342
|
+
gridResizeObservers.set(container, ro);
|
|
4343
|
+
}
|
|
4344
|
+
attachDragOverFeedback(outerDiv, {
|
|
4345
|
+
activeClass: "fb-drag-over",
|
|
4346
|
+
onEnter: () => {
|
|
4347
|
+
grid.querySelectorAll(".fb-multi-placeholder").forEach((p) => {
|
|
4348
|
+
p.classList.add("fb-drag-over");
|
|
4349
|
+
});
|
|
4350
|
+
const addTile = grid.querySelector(".fb-multi-add-tile-js");
|
|
4351
|
+
if (addTile) {
|
|
4352
|
+
addTile.classList.add("fb-drag-over-tile");
|
|
4353
|
+
const label = addTile.querySelector(".fb-multi-add-label");
|
|
4354
|
+
if (label) label.textContent = t("dropToUpload", state);
|
|
4355
|
+
}
|
|
4356
|
+
},
|
|
4357
|
+
onLeave: () => {
|
|
4358
|
+
grid.querySelectorAll(".fb-multi-placeholder").forEach((p) => {
|
|
4359
|
+
p.classList.remove("fb-drag-over");
|
|
4360
|
+
});
|
|
4361
|
+
const addTile = grid.querySelector(".fb-multi-add-tile-js");
|
|
4362
|
+
if (addTile) {
|
|
4363
|
+
addTile.classList.remove("fb-drag-over-tile");
|
|
4364
|
+
const label = addTile.querySelector(".fb-multi-add-label");
|
|
4365
|
+
if (label) label.textContent = t("clickDragTextMultiple", state);
|
|
4366
|
+
}
|
|
4367
|
+
}
|
|
4368
|
+
});
|
|
4369
|
+
if (element) {
|
|
4370
|
+
const metaLine = buildMetaLine(
|
|
4371
|
+
state,
|
|
4372
|
+
element,
|
|
4373
|
+
ridList.length,
|
|
4374
|
+
effectiveMax,
|
|
4375
|
+
Boolean(onClearAll),
|
|
4376
|
+
onClearAll != null ? onClearAll : (() => {
|
|
4377
|
+
})
|
|
4378
|
+
);
|
|
4379
|
+
container.appendChild(metaLine);
|
|
3911
4380
|
}
|
|
3912
4381
|
}
|
|
3913
4382
|
function renderFileElementEdit(element, ctx, wrapper, pathKey) {
|
|
3914
|
-
var _a, _b
|
|
4383
|
+
var _a, _b;
|
|
3915
4384
|
const state = ctx.state;
|
|
3916
4385
|
const fileWrapper = document.createElement("div");
|
|
3917
4386
|
fileWrapper.className = "space-y-2";
|
|
4387
|
+
fileWrapper.dataset.filesWrapper = pathKey;
|
|
3918
4388
|
const picker = document.createElement("input");
|
|
3919
4389
|
picker.type = "file";
|
|
3920
4390
|
picker.name = pathKey;
|
|
3921
4391
|
picker.style.display = "none";
|
|
3922
|
-
|
|
3923
|
-
picker.accept = typeof element.accept === "string" ? element.accept : [
|
|
3924
|
-
...(_b = (_a = element.accept.extensions) == null ? void 0 : _a.map((ext) => `.${ext}`)) != null ? _b : [],
|
|
3925
|
-
...(_c = element.accept.mime) != null ? _c : []
|
|
3926
|
-
].join(",") || "";
|
|
3927
|
-
}
|
|
4392
|
+
picker.accept = buildAcceptAttribute(element.accept);
|
|
3928
4393
|
const fileContainer = document.createElement("div");
|
|
3929
4394
|
fileContainer.className = "file-preview-container";
|
|
3930
4395
|
const initial = ctx.prefill[element.key];
|
|
3931
4396
|
const allowedExts = getAllowedExtensions(element.accept);
|
|
3932
4397
|
const allowedMimes = getAllowedMimes(element.accept);
|
|
3933
|
-
const maxSizeMB = (
|
|
4398
|
+
const maxSizeMB = (_a = element.maxSize) != null ? _a : Infinity;
|
|
3934
4399
|
const handlers = {
|
|
3935
4400
|
fileUploadHandler() {
|
|
3936
4401
|
picker.click();
|
|
@@ -3953,14 +4418,6 @@ function renderFileElementEdit(element, ctx, wrapper, pathKey) {
|
|
|
3953
4418
|
setupDrop(container) {
|
|
3954
4419
|
setupDragAndDrop(container, handlers.dragHandler);
|
|
3955
4420
|
},
|
|
3956
|
-
restoreDropzone() {
|
|
3957
|
-
const hint = makeFieldHint(element, state);
|
|
3958
|
-
fileContainer.className = "file-preview-container w-full max-w-md bg-gray-100 rounded-lg overflow-hidden relative group cursor-pointer";
|
|
3959
|
-
fileContainer.style.height = "128px";
|
|
3960
|
-
setEmptyFileContainer(fileContainer, state, hint);
|
|
3961
|
-
fileContainer.onclick = handlers.fileUploadHandler;
|
|
3962
|
-
setupDragAndDrop(fileContainer, handlers.dragHandler);
|
|
3963
|
-
},
|
|
3964
4421
|
onRemove() {
|
|
3965
4422
|
var _a2;
|
|
3966
4423
|
const hiddenInput = fileWrapper.querySelector('input[type="hidden"]');
|
|
@@ -3972,34 +4429,11 @@ function renderFileElementEdit(element, ctx, wrapper, pathKey) {
|
|
|
3972
4429
|
renderEmptySingleState();
|
|
3973
4430
|
}
|
|
3974
4431
|
};
|
|
3975
|
-
const
|
|
3976
|
-
|
|
3977
|
-
|
|
3978
|
-
|
|
3979
|
-
|
|
3980
|
-
onRemove: handlers.onRemove
|
|
3981
|
-
});
|
|
3982
|
-
const renderEmptySingleState = () => {
|
|
3983
|
-
if (state.config.pickExistingFiles && !element.disableLibrary) {
|
|
3984
|
-
fileContainer.className = "file-preview-container";
|
|
3985
|
-
fileContainer.removeAttribute("style");
|
|
3986
|
-
fileContainer.onclick = null;
|
|
3987
|
-
while (fileContainer.firstChild) {
|
|
3988
|
-
fileContainer.removeChild(fileContainer.firstChild);
|
|
3989
|
-
}
|
|
3990
|
-
const row = document.createElement("div");
|
|
3991
|
-
row.className = "fb-file-card-row";
|
|
3992
|
-
row.style.cssText = "display:flex;gap:8px;align-items:stretch;";
|
|
3993
|
-
const hint = makeFieldHint(element, state);
|
|
3994
|
-
const uploadCard = buildEmptyDropzone(
|
|
3995
|
-
state,
|
|
3996
|
-
t("clickDragText", state),
|
|
3997
|
-
hint,
|
|
3998
|
-
handlers.fileUploadHandler
|
|
3999
|
-
);
|
|
4000
|
-
uploadCard.style.cssText = "flex:1;min-width:0;height:128px;";
|
|
4001
|
-
setupDragAndDrop(uploadCard, handlers.dragHandler);
|
|
4002
|
-
const libraryBtn = buildLibraryButton("card", state, () => {
|
|
4432
|
+
const buildSingleExtras = () => {
|
|
4433
|
+
const hasLibrary = Boolean(state.config.pickExistingFiles && !element.disableLibrary);
|
|
4434
|
+
return {
|
|
4435
|
+
replaceHandler: state.config.uploadFile ? () => picker.click() : null,
|
|
4436
|
+
libraryHandler: hasLibrary ? () => {
|
|
4003
4437
|
handleLibraryPickSingle(
|
|
4004
4438
|
state,
|
|
4005
4439
|
element,
|
|
@@ -4008,20 +4442,41 @@ function renderFileElementEdit(element, ctx, wrapper, pathKey) {
|
|
|
4008
4442
|
pathKey,
|
|
4009
4443
|
pathKey,
|
|
4010
4444
|
async (rid) => {
|
|
4011
|
-
|
|
4445
|
+
renderSingleFileFilled(fileContainer, rid, state, buildDeps(), buildSingleExtras());
|
|
4012
4446
|
},
|
|
4013
4447
|
ctx.instance
|
|
4014
4448
|
).catch((err) => {
|
|
4015
4449
|
console.error("Library pick failed:", err);
|
|
4016
4450
|
});
|
|
4017
|
-
}
|
|
4018
|
-
|
|
4019
|
-
|
|
4020
|
-
|
|
4021
|
-
|
|
4022
|
-
|
|
4023
|
-
|
|
4451
|
+
} : null
|
|
4452
|
+
};
|
|
4453
|
+
};
|
|
4454
|
+
const buildDeps = () => ({
|
|
4455
|
+
picker,
|
|
4456
|
+
fileUploadHandler: handlers.fileUploadHandler,
|
|
4457
|
+
dragHandler: handlers.dragHandler,
|
|
4458
|
+
setupDrop: handlers.setupDrop,
|
|
4459
|
+
onRemove: handlers.onRemove,
|
|
4460
|
+
onAfterUpload: (container, rid) => {
|
|
4461
|
+
renderSingleFileFilled(container, rid, state, buildDeps(), buildSingleExtras());
|
|
4024
4462
|
}
|
|
4463
|
+
});
|
|
4464
|
+
const renderEmptySingleState = () => {
|
|
4465
|
+
ensureFileStyles();
|
|
4466
|
+
fileContainer.className = "file-preview-container";
|
|
4467
|
+
fileContainer.removeAttribute("style");
|
|
4468
|
+
while (fileContainer.firstChild) fileContainer.removeChild(fileContainer.firstChild);
|
|
4469
|
+
const onLibraryClick = buildSingleExtras().libraryHandler;
|
|
4470
|
+
const wideTile = buildWideTile(
|
|
4471
|
+
state,
|
|
4472
|
+
onLibraryClick !== null,
|
|
4473
|
+
handlers.fileUploadHandler,
|
|
4474
|
+
onLibraryClick,
|
|
4475
|
+
false,
|
|
4476
|
+
makeFieldHint(element, state)
|
|
4477
|
+
);
|
|
4478
|
+
fileContainer.appendChild(wideTile);
|
|
4479
|
+
setupDragAndDrop(fileContainer, handlers.dragHandler);
|
|
4025
4480
|
};
|
|
4026
4481
|
if (initial) {
|
|
4027
4482
|
handleInitialFileData(
|
|
@@ -4030,11 +4485,11 @@ function renderFileElementEdit(element, ctx, wrapper, pathKey) {
|
|
|
4030
4485
|
pathKey,
|
|
4031
4486
|
fileWrapper,
|
|
4032
4487
|
state,
|
|
4033
|
-
buildDeps()
|
|
4488
|
+
buildDeps(),
|
|
4489
|
+
buildSingleExtras()
|
|
4034
4490
|
);
|
|
4035
4491
|
const prefillMeta = state.resourceIndex.get(initial);
|
|
4036
|
-
if ((
|
|
4037
|
-
fileContainer.onclick = handlers.fileUploadHandler;
|
|
4492
|
+
if ((_b = prefillMeta == null ? void 0 : prefillMeta.type) == null ? void 0 : _b.startsWith("video/")) {
|
|
4038
4493
|
setupDragAndDrop(fileContainer, handlers.dragHandler);
|
|
4039
4494
|
}
|
|
4040
4495
|
} else {
|
|
@@ -4042,116 +4497,25 @@ function renderFileElementEdit(element, ctx, wrapper, pathKey) {
|
|
|
4042
4497
|
}
|
|
4043
4498
|
picker.onchange = () => {
|
|
4044
4499
|
if (picker.files && picker.files.length > 0) {
|
|
4045
|
-
|
|
4046
|
-
file: picker.files[0],
|
|
4047
|
-
container: fileContainer,
|
|
4048
|
-
fieldName: pathKey,
|
|
4049
|
-
state,
|
|
4050
|
-
deps: buildDeps(),
|
|
4051
|
-
instance: ctx.instance,
|
|
4052
|
-
allowedExtensions: allowedExts,
|
|
4053
|
-
allowedMimes,
|
|
4054
|
-
maxSizeMB
|
|
4055
|
-
});
|
|
4500
|
+
handlers.dragHandler(picker.files);
|
|
4056
4501
|
}
|
|
4057
4502
|
};
|
|
4058
4503
|
fileWrapper.appendChild(fileContainer);
|
|
4059
4504
|
fileWrapper.appendChild(picker);
|
|
4060
4505
|
wrapper.appendChild(fileWrapper);
|
|
4061
4506
|
}
|
|
4062
|
-
function
|
|
4063
|
-
var _a, _b, _c
|
|
4064
|
-
|
|
4065
|
-
|
|
4066
|
-
|
|
4067
|
-
|
|
4068
|
-
|
|
4069
|
-
|
|
4070
|
-
|
|
4071
|
-
|
|
4072
|
-
|
|
4073
|
-
if (element.accept) {
|
|
4074
|
-
filesPicker.accept = typeof element.accept === "string" ? element.accept : [
|
|
4075
|
-
...(_b = (_a = element.accept.extensions) == null ? void 0 : _a.map((ext) => `.${ext}`)) != null ? _b : [],
|
|
4076
|
-
...(_c = element.accept.mime) != null ? _c : []
|
|
4077
|
-
].join(",") || "";
|
|
4078
|
-
}
|
|
4079
|
-
const filesContainer = document.createElement("div");
|
|
4080
|
-
filesContainer.className = "files-list-wrapper";
|
|
4081
|
-
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);";
|
|
4082
|
-
const list = document.createElement("div");
|
|
4083
|
-
list.className = "files-list";
|
|
4084
|
-
const initialFiles = ctx.prefill[element.key] || [];
|
|
4085
|
-
addPrefillFilesToIndex(initialFiles, state.resourceIndex);
|
|
4086
|
-
filesWrapper.dataset.resourceIds = JSON.stringify(initialFiles);
|
|
4087
|
-
const filesFieldHint = makeFieldHint(element, state);
|
|
4088
|
-
const filesConstraints = {
|
|
4089
|
-
maxCount: Infinity,
|
|
4090
|
-
allowedExtensions: getAllowedExtensions(element.accept),
|
|
4091
|
-
allowedMimes: getAllowedMimes(element.accept),
|
|
4092
|
-
maxSize: (_d = element.maxSize) != null ? _d : Infinity
|
|
4093
|
-
};
|
|
4094
|
-
filesContainer.appendChild(list);
|
|
4095
|
-
filesWrapper.appendChild(filesPicker);
|
|
4096
|
-
filesWrapper.appendChild(filesContainer);
|
|
4097
|
-
wrapper.appendChild(filesWrapper);
|
|
4098
|
-
const onLibraryPickFiles = state.config.pickExistingFiles && !element.disableLibrary ? () => {
|
|
4099
|
-
handleLibraryPickMulti(
|
|
4100
|
-
state,
|
|
4101
|
-
element,
|
|
4102
|
-
filesWrapper,
|
|
4103
|
-
pathKey,
|
|
4104
|
-
initialFiles,
|
|
4105
|
-
Infinity,
|
|
4106
|
-
updateFilesList,
|
|
4107
|
-
ctx.instance
|
|
4108
|
-
).catch((err) => {
|
|
4109
|
-
console.error("Library pick failed:", err);
|
|
4110
|
-
});
|
|
4111
|
-
} : null;
|
|
4112
|
-
function updateFilesList() {
|
|
4113
|
-
const currentlyReadonly = isElementReadonly(element, state);
|
|
4114
|
-
renderResourcePills({
|
|
4115
|
-
container: list,
|
|
4116
|
-
rids: initialFiles,
|
|
4117
|
-
state,
|
|
4118
|
-
onRemove: currentlyReadonly ? null : (ridToRemove) => {
|
|
4119
|
-
var _a2;
|
|
4120
|
-
releaseLocalFileUrl((_a2 = state.resourceIndex.get(ridToRemove)) == null ? void 0 : _a2.file);
|
|
4121
|
-
const index = initialFiles.indexOf(ridToRemove);
|
|
4122
|
-
if (index > -1) initialFiles.splice(index, 1);
|
|
4123
|
-
updateFilesList();
|
|
4124
|
-
},
|
|
4125
|
-
hint: filesFieldHint,
|
|
4126
|
-
isReadonly: currentlyReadonly,
|
|
4127
|
-
onLibraryPick: currentlyReadonly ? null : onLibraryPickFiles
|
|
4128
|
-
});
|
|
4129
|
-
}
|
|
4130
|
-
updateFilesList();
|
|
4131
|
-
setupFilesDropHandler(
|
|
4132
|
-
filesContainer,
|
|
4133
|
-
initialFiles,
|
|
4134
|
-
state,
|
|
4135
|
-
updateFilesList,
|
|
4136
|
-
filesConstraints,
|
|
4137
|
-
pathKey,
|
|
4138
|
-
ctx.instance
|
|
4139
|
-
);
|
|
4140
|
-
setupFilesPickerHandler(
|
|
4141
|
-
filesPicker,
|
|
4142
|
-
initialFiles,
|
|
4143
|
-
state,
|
|
4144
|
-
updateFilesList,
|
|
4145
|
-
filesConstraints,
|
|
4146
|
-
pathKey,
|
|
4147
|
-
ctx.instance
|
|
4148
|
-
);
|
|
4149
|
-
}
|
|
4150
|
-
function renderMultipleFileElementEdit(element, ctx, wrapper, pathKey) {
|
|
4151
|
-
var _a, _b, _c, _d, _e, _f;
|
|
4507
|
+
function buildAcceptAttribute(accept) {
|
|
4508
|
+
var _a, _b, _c;
|
|
4509
|
+
if (!accept) return "";
|
|
4510
|
+
if (typeof accept === "string") return accept;
|
|
4511
|
+
return [
|
|
4512
|
+
...(_b = (_a = accept.extensions) == null ? void 0 : _a.map((ext) => `.${ext}`)) != null ? _b : [],
|
|
4513
|
+
...(_c = accept.mime) != null ? _c : []
|
|
4514
|
+
].join(",");
|
|
4515
|
+
}
|
|
4516
|
+
function setupMultiFileEditMode(element, ctx, wrapper, pathKey, maxFiles) {
|
|
4517
|
+
var _a, _b;
|
|
4152
4518
|
const state = ctx.state;
|
|
4153
|
-
const minFiles = (_a = element.minCount) != null ? _a : 0;
|
|
4154
|
-
const maxFiles = (_b = element.maxCount) != null ? _b : Infinity;
|
|
4155
4519
|
const filesWrapper = document.createElement("div");
|
|
4156
4520
|
filesWrapper.className = "space-y-2";
|
|
4157
4521
|
filesWrapper.dataset.filesWrapper = pathKey;
|
|
@@ -4160,15 +4524,9 @@ function renderMultipleFileElementEdit(element, ctx, wrapper, pathKey) {
|
|
|
4160
4524
|
filesPicker.name = pathKey;
|
|
4161
4525
|
filesPicker.multiple = true;
|
|
4162
4526
|
filesPicker.style.display = "none";
|
|
4163
|
-
|
|
4164
|
-
filesPicker.accept = typeof element.accept === "string" ? element.accept : [
|
|
4165
|
-
...(_d = (_c = element.accept.extensions) == null ? void 0 : _c.map((ext) => `.${ext}`)) != null ? _d : [],
|
|
4166
|
-
...(_e = element.accept.mime) != null ? _e : []
|
|
4167
|
-
].join(",") || "";
|
|
4168
|
-
}
|
|
4527
|
+
filesPicker.accept = buildAcceptAttribute(element.accept);
|
|
4169
4528
|
const filesContainer = document.createElement("div");
|
|
4170
4529
|
filesContainer.className = "files-list-wrapper";
|
|
4171
|
-
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);";
|
|
4172
4530
|
const list = document.createElement("div");
|
|
4173
4531
|
list.className = "files-list";
|
|
4174
4532
|
filesWrapper.appendChild(filesPicker);
|
|
@@ -4177,19 +4535,18 @@ function renderMultipleFileElementEdit(element, ctx, wrapper, pathKey) {
|
|
|
4177
4535
|
const initialFiles = Array.isArray(ctx.prefill[element.key]) ? [...ctx.prefill[element.key]] : [];
|
|
4178
4536
|
addPrefillFilesToIndex(initialFiles, state.resourceIndex);
|
|
4179
4537
|
filesWrapper.dataset.resourceIds = JSON.stringify(initialFiles);
|
|
4180
|
-
const
|
|
4181
|
-
const multipleConstraints = {
|
|
4538
|
+
const constraints = {
|
|
4182
4539
|
maxCount: maxFiles,
|
|
4183
4540
|
allowedExtensions: getAllowedExtensions(element.accept),
|
|
4184
4541
|
allowedMimes: getAllowedMimes(element.accept),
|
|
4185
|
-
|
|
4542
|
+
// Prefer schema's `maxSize`; fall back to legacy `maxSizeMB` for
|
|
4543
|
+
// backward compatibility (matches addFileSizeHint in validation.ts).
|
|
4544
|
+
maxSize: (_b = (_a = element.maxSize) != null ? _a : element.maxSizeMB) != null ? _b : Infinity
|
|
4186
4545
|
};
|
|
4187
|
-
const
|
|
4188
|
-
|
|
4189
|
-
const minMaxText = minFiles > 0 || maxFiles < Infinity ? ` ${t("fileCountRange", state, { min: minFiles, max: maxFiles })}` : "";
|
|
4190
|
-
return countText + minMaxText;
|
|
4546
|
+
const openPicker = () => {
|
|
4547
|
+
filesPicker.click();
|
|
4191
4548
|
};
|
|
4192
|
-
const
|
|
4549
|
+
const onLibraryPick = state.config.pickExistingFiles && !element.disableLibrary ? () => {
|
|
4193
4550
|
handleLibraryPickMulti(
|
|
4194
4551
|
state,
|
|
4195
4552
|
element,
|
|
@@ -4203,31 +4560,36 @@ function renderMultipleFileElementEdit(element, ctx, wrapper, pathKey) {
|
|
|
4203
4560
|
console.error("Library pick failed:", err);
|
|
4204
4561
|
});
|
|
4205
4562
|
} : null;
|
|
4206
|
-
|
|
4563
|
+
function updateFilesDisplay() {
|
|
4207
4564
|
const currentlyReadonly = isElementReadonly(element, state);
|
|
4208
4565
|
renderResourcePills({
|
|
4209
4566
|
container: list,
|
|
4210
4567
|
rids: initialFiles,
|
|
4211
4568
|
state,
|
|
4212
|
-
onRemove: currentlyReadonly ? null : (
|
|
4569
|
+
onRemove: currentlyReadonly ? null : (ridToRemove) => {
|
|
4213
4570
|
var _a2;
|
|
4214
|
-
releaseLocalFileUrl((_a2 = state.resourceIndex.get(
|
|
4215
|
-
initialFiles.
|
|
4571
|
+
releaseLocalFileUrl((_a2 = state.resourceIndex.get(ridToRemove)) == null ? void 0 : _a2.file);
|
|
4572
|
+
const index = initialFiles.indexOf(ridToRemove);
|
|
4573
|
+
if (index > -1) initialFiles.splice(index, 1);
|
|
4216
4574
|
updateFilesDisplay();
|
|
4217
4575
|
},
|
|
4218
|
-
hint: multipleFilesHint,
|
|
4219
|
-
countInfo: buildCountInfo(),
|
|
4220
4576
|
maxCount: maxFiles < Infinity ? maxFiles : void 0,
|
|
4221
4577
|
isReadonly: currentlyReadonly,
|
|
4222
|
-
onLibraryPick: currentlyReadonly ? null :
|
|
4578
|
+
onLibraryPick: currentlyReadonly ? null : onLibraryPick,
|
|
4579
|
+
element,
|
|
4580
|
+
onClearAll: currentlyReadonly ? void 0 : () => {
|
|
4581
|
+
initialFiles.splice(0);
|
|
4582
|
+
updateFilesDisplay();
|
|
4583
|
+
},
|
|
4584
|
+
openPicker
|
|
4223
4585
|
});
|
|
4224
|
-
}
|
|
4586
|
+
}
|
|
4225
4587
|
setupFilesDropHandler(
|
|
4226
4588
|
filesContainer,
|
|
4227
4589
|
initialFiles,
|
|
4228
4590
|
state,
|
|
4229
4591
|
updateFilesDisplay,
|
|
4230
|
-
|
|
4592
|
+
constraints,
|
|
4231
4593
|
pathKey,
|
|
4232
4594
|
ctx.instance
|
|
4233
4595
|
);
|
|
@@ -4236,13 +4598,20 @@ function renderMultipleFileElementEdit(element, ctx, wrapper, pathKey) {
|
|
|
4236
4598
|
initialFiles,
|
|
4237
4599
|
state,
|
|
4238
4600
|
updateFilesDisplay,
|
|
4239
|
-
|
|
4601
|
+
constraints,
|
|
4240
4602
|
pathKey,
|
|
4241
4603
|
ctx.instance
|
|
4242
4604
|
);
|
|
4243
4605
|
updateFilesDisplay();
|
|
4244
4606
|
wrapper.appendChild(filesWrapper);
|
|
4245
4607
|
}
|
|
4608
|
+
function renderFilesElementEdit(element, ctx, wrapper, pathKey) {
|
|
4609
|
+
setupMultiFileEditMode(element, ctx, wrapper, pathKey, Infinity);
|
|
4610
|
+
}
|
|
4611
|
+
function renderMultipleFileElementEdit(element, ctx, wrapper, pathKey) {
|
|
4612
|
+
var _a;
|
|
4613
|
+
setupMultiFileEditMode(element, ctx, wrapper, pathKey, (_a = element.maxCount) != null ? _a : Infinity);
|
|
4614
|
+
}
|
|
4246
4615
|
|
|
4247
4616
|
// src/components/file/validate.ts
|
|
4248
4617
|
function readMultiFileResourceIds(scopeRoot, fullKey) {
|
|
@@ -4369,33 +4738,36 @@ function renderFileElementReadonly(element, ctx, wrapper, pathKey) {
|
|
|
4369
4738
|
hiddenInput.name = pathKey;
|
|
4370
4739
|
hiddenInput.value = initial;
|
|
4371
4740
|
wrapper.appendChild(hiddenInput);
|
|
4372
|
-
renderFilePreviewReadonly(initial, state).then((
|
|
4373
|
-
|
|
4374
|
-
|
|
4375
|
-
|
|
4376
|
-
|
|
4377
|
-
|
|
4741
|
+
renderFilePreviewReadonly(initial, state).then((tile) => {
|
|
4742
|
+
tile.classList.add(
|
|
4743
|
+
"fb-single-readonly-filled",
|
|
4744
|
+
"fb-readonly-tile",
|
|
4745
|
+
"fb-checker"
|
|
4746
|
+
);
|
|
4747
|
+
wrapper.appendChild(tile);
|
|
4748
|
+
}).catch(console.error);
|
|
4378
4749
|
} else {
|
|
4379
4750
|
wrapper.appendChild(buildEmptyReadonlyTile(state));
|
|
4380
4751
|
}
|
|
4381
4752
|
}
|
|
4382
4753
|
function buildEmptyReadonlyTile(state) {
|
|
4754
|
+
ensureFileStyles();
|
|
4383
4755
|
const emptyState = document.createElement("div");
|
|
4384
4756
|
emptyState.style.cssText = `
|
|
4385
|
-
|
|
4386
|
-
height:${TILE_SIZE};
|
|
4757
|
+
height: 220px;
|
|
4387
4758
|
display:flex;
|
|
4388
4759
|
align-items:center;
|
|
4389
4760
|
justify-content:center;
|
|
4390
|
-
background:
|
|
4391
|
-
border-radius:
|
|
4392
|
-
border:1px solid
|
|
4761
|
+
background: repeating-linear-gradient(45deg, #fafafa 0 6px, #f3f4f6 6px 12px);
|
|
4762
|
+
border-radius:0.75rem;
|
|
4763
|
+
border:1px solid #e2e8f0;
|
|
4393
4764
|
`;
|
|
4394
4765
|
emptyState.innerHTML = `<div style="font-size:11px;text-align:center;color:var(--fb-text-secondary-color,#6b7280);">${escapeHtml(t("noFileSelected", state))}</div>`;
|
|
4395
4766
|
return emptyState;
|
|
4396
4767
|
}
|
|
4397
|
-
function renderMultiFileReadonly(rids, state, wrapper, pathKey,
|
|
4768
|
+
function renderMultiFileReadonly(rids, state, wrapper, pathKey, _marginTop) {
|
|
4398
4769
|
addPrefillFilesToIndex(rids, state.resourceIndex);
|
|
4770
|
+
ensureFileStyles();
|
|
4399
4771
|
const filesWrapper = document.createElement("div");
|
|
4400
4772
|
filesWrapper.dataset.filesWrapper = pathKey;
|
|
4401
4773
|
filesWrapper.dataset.resourceIds = JSON.stringify(rids);
|
|
@@ -4407,22 +4779,28 @@ function renderMultiFileReadonly(rids, state, wrapper, pathKey, marginTop) {
|
|
|
4407
4779
|
filesWrapper.appendChild(emptyEl);
|
|
4408
4780
|
return;
|
|
4409
4781
|
}
|
|
4410
|
-
const
|
|
4411
|
-
|
|
4412
|
-
filesWrapper.appendChild(
|
|
4782
|
+
const grid = document.createElement("div");
|
|
4783
|
+
grid.className = "fb-multi-readonly-grid";
|
|
4784
|
+
filesWrapper.appendChild(grid);
|
|
4413
4785
|
const placeholders = rids.map(() => {
|
|
4414
|
-
const
|
|
4415
|
-
|
|
4416
|
-
|
|
4417
|
-
return
|
|
4786
|
+
const ph = document.createElement("div");
|
|
4787
|
+
ph.className = "fb-readonly-tile fb-checker fb-tile";
|
|
4788
|
+
grid.appendChild(ph);
|
|
4789
|
+
return ph;
|
|
4418
4790
|
});
|
|
4419
4791
|
for (let i = 0; i < rids.length; i++) {
|
|
4420
4792
|
const resourceId = rids[i];
|
|
4421
4793
|
const placeholder = placeholders[i];
|
|
4422
|
-
|
|
4423
|
-
|
|
4424
|
-
|
|
4425
|
-
|
|
4794
|
+
const meta = state.resourceIndex.get(resourceId);
|
|
4795
|
+
renderFilePreviewReadonly(resourceId, state, meta == null ? void 0 : meta.name).then((tile) => {
|
|
4796
|
+
tile.classList.add("fb-readonly-tile", "fb-checker", "fb-tile-resource");
|
|
4797
|
+
tile.dataset.resourceId = resourceId;
|
|
4798
|
+
placeholder.replaceWith(tile);
|
|
4799
|
+
}).catch(() => {
|
|
4800
|
+
const tile = document.createElement("div");
|
|
4801
|
+
tile.className = "fb-readonly-tile fb-checker fb-tile fb-tile-resource";
|
|
4802
|
+
tile.dataset.resourceId = resourceId;
|
|
4803
|
+
placeholder.replaceWith(tile);
|
|
4426
4804
|
});
|
|
4427
4805
|
}
|
|
4428
4806
|
}
|
|
@@ -4434,7 +4812,7 @@ function renderFilesElementReadonly(element, ctx, wrapper, pathKey) {
|
|
|
4434
4812
|
function renderMultipleFileElementReadonly(element, ctx, wrapper, pathKey) {
|
|
4435
4813
|
const rawPrefill = ctx.prefill[element.key];
|
|
4436
4814
|
const initialFiles = Array.isArray(rawPrefill) ? rawPrefill : [];
|
|
4437
|
-
renderMultiFileReadonly(initialFiles, ctx.state, wrapper, pathKey
|
|
4815
|
+
renderMultiFileReadonly(initialFiles, ctx.state, wrapper, pathKey);
|
|
4438
4816
|
}
|
|
4439
4817
|
|
|
4440
4818
|
// src/components/file.ts
|
|
@@ -5531,7 +5909,7 @@ function updateSliderField(element, fieldPath, value, context) {
|
|
|
5531
5909
|
function extractChildDefaults(elements) {
|
|
5532
5910
|
const defaults = {};
|
|
5533
5911
|
for (const child of elements) {
|
|
5534
|
-
if ("default" in child && child.default !== void 0) {
|
|
5912
|
+
if (child.key && "default" in child && child.default !== void 0) {
|
|
5535
5913
|
defaults[child.key] = child.default;
|
|
5536
5914
|
}
|
|
5537
5915
|
}
|
|
@@ -5582,7 +5960,7 @@ function createPrefillHints(element, pathKey) {
|
|
|
5582
5960
|
return null;
|
|
5583
5961
|
}
|
|
5584
5962
|
const hintsContainer = document.createElement("div");
|
|
5585
|
-
hintsContainer.className = "fb-prefill-hints flex flex-wrap gap-2 mb-
|
|
5963
|
+
hintsContainer.className = "fb-prefill-hints flex flex-wrap gap-2 mb-2";
|
|
5586
5964
|
element.prefillHints.forEach((hint, index) => {
|
|
5587
5965
|
const hintButton = document.createElement("button");
|
|
5588
5966
|
hintButton.type = "button";
|
|
@@ -5598,14 +5976,14 @@ function createPrefillHints(element, pathKey) {
|
|
|
5598
5976
|
function renderSingleContainerElement(element, ctx, wrapper, pathKey) {
|
|
5599
5977
|
var _a, _b;
|
|
5600
5978
|
const containerWrap = document.createElement("div");
|
|
5601
|
-
containerWrap.className = "border border-gray-200 rounded-lg p-
|
|
5979
|
+
containerWrap.className = "border border-gray-200 rounded-lg p-2 bg-gray-50";
|
|
5602
5980
|
containerWrap.setAttribute("data-container", pathKey);
|
|
5603
5981
|
const itemsWrap = document.createElement("div");
|
|
5604
5982
|
const columns = element.columns || 1;
|
|
5605
5983
|
if (columns === 1) {
|
|
5606
|
-
itemsWrap.className = "space-y-
|
|
5984
|
+
itemsWrap.className = "space-y-2";
|
|
5607
5985
|
} else {
|
|
5608
|
-
itemsWrap.className = `grid grid-cols-${columns} gap-
|
|
5986
|
+
itemsWrap.className = `grid grid-cols-${columns} gap-2`;
|
|
5609
5987
|
}
|
|
5610
5988
|
const containerIsReadonly = isElementReadonly(element, ctx.state, ctx);
|
|
5611
5989
|
if (!containerIsReadonly) {
|
|
@@ -5628,8 +6006,8 @@ function renderSingleContainerElement(element, ctx, wrapper, pathKey) {
|
|
|
5628
6006
|
};
|
|
5629
6007
|
element.elements.forEach((child) => {
|
|
5630
6008
|
var _a2, _b2;
|
|
5631
|
-
if (child.hidden || child.type === "hidden") {
|
|
5632
|
-
const prefillVal = (_b2 = (_a2 = containerPrefill[child.key]) != null ? _a2 : child.default) != null ? _b2 : null;
|
|
6009
|
+
if (child.type !== "markdown" && (child.hidden || child.type === "hidden")) {
|
|
6010
|
+
const prefillVal = (_b2 = (_a2 = containerPrefill[child.key]) != null ? _a2 : "default" in child ? child.default : null) != null ? _b2 : null;
|
|
5633
6011
|
itemsWrap.appendChild(
|
|
5634
6012
|
createHiddenInput(pathJoin(subCtx.path, child.key), prefillVal)
|
|
5635
6013
|
);
|
|
@@ -5640,17 +6018,32 @@ function renderSingleContainerElement(element, ctx, wrapper, pathKey) {
|
|
|
5640
6018
|
containerWrap.appendChild(itemsWrap);
|
|
5641
6019
|
wrapper.appendChild(containerWrap);
|
|
5642
6020
|
}
|
|
6021
|
+
function getChildWrapperClass(isSlides, columns) {
|
|
6022
|
+
if (isSlides) {
|
|
6023
|
+
return "space-y-2";
|
|
6024
|
+
}
|
|
6025
|
+
const cols = columns || 1;
|
|
6026
|
+
return cols === 1 ? "space-y-2" : `grid grid-cols-${cols} gap-2`;
|
|
6027
|
+
}
|
|
5643
6028
|
function renderMultipleContainerElement(element, ctx, wrapper, _pathKey) {
|
|
5644
6029
|
var _a, _b, _c, _d;
|
|
5645
6030
|
const state = ctx.state;
|
|
5646
6031
|
const containerIsReadonly = isElementReadonly(element, state, ctx);
|
|
5647
6032
|
const childInheritedReadonly = containerIsReadonly || ctx.inheritedReadonly;
|
|
5648
6033
|
const containerWrap = document.createElement("div");
|
|
5649
|
-
containerWrap.className = "border border-gray-200 rounded-lg p-
|
|
6034
|
+
containerWrap.className = "border border-gray-200 rounded-lg p-2 bg-gray-50";
|
|
5650
6035
|
const countDisplay = document.createElement("span");
|
|
5651
6036
|
countDisplay.className = "text-sm text-gray-500";
|
|
5652
6037
|
const itemsWrap = document.createElement("div");
|
|
5653
|
-
|
|
6038
|
+
const isSlides = element.displayMode === "slides";
|
|
6039
|
+
if (isSlides) {
|
|
6040
|
+
itemsWrap.className = "fb-container-slides";
|
|
6041
|
+
const slideCols = element.columns;
|
|
6042
|
+
const gridTemplateColumns = typeof slideCols === "number" && slideCols > 0 ? `repeat(${slideCols}, 1fr)` : "repeat(auto-fit, minmax(280px, 1fr))";
|
|
6043
|
+
itemsWrap.style.cssText = `display:grid;grid-template-columns:${gridTemplateColumns};gap:8px;align-items:start;`;
|
|
6044
|
+
} else {
|
|
6045
|
+
itemsWrap.className = "space-y-2";
|
|
6046
|
+
}
|
|
5654
6047
|
if (!containerIsReadonly) {
|
|
5655
6048
|
const hintsElement = createPrefillHints(element, element.key);
|
|
5656
6049
|
if (hintsElement) {
|
|
@@ -5694,22 +6087,17 @@ function renderMultipleContainerElement(element, ctx, wrapper, _pathKey) {
|
|
|
5694
6087
|
inheritedReadonly: childInheritedReadonly
|
|
5695
6088
|
};
|
|
5696
6089
|
const item = document.createElement("div");
|
|
5697
|
-
item.className = "containerItem border border-gray-300 rounded-lg p-
|
|
6090
|
+
item.className = "containerItem border border-gray-300 rounded-lg p-2 bg-white";
|
|
5698
6091
|
item.setAttribute("data-container-item", `${element.key}[${idx}]`);
|
|
5699
6092
|
const childWrapper = document.createElement("div");
|
|
5700
|
-
|
|
5701
|
-
if (columns === 1) {
|
|
5702
|
-
childWrapper.className = "space-y-4";
|
|
5703
|
-
} else {
|
|
5704
|
-
childWrapper.className = `grid grid-cols-${columns} gap-4`;
|
|
5705
|
-
}
|
|
6093
|
+
childWrapper.className = getChildWrapperClass(isSlides, element.columns);
|
|
5706
6094
|
element.elements.forEach((child) => {
|
|
5707
6095
|
var _a2;
|
|
5708
|
-
if (child.hidden || child.type === "hidden") {
|
|
6096
|
+
if (child.type !== "markdown" && (child.hidden || child.type === "hidden")) {
|
|
5709
6097
|
childWrapper.appendChild(
|
|
5710
6098
|
createHiddenInput(
|
|
5711
6099
|
pathJoin(subCtx.path, child.key),
|
|
5712
|
-
(_a2 = child.default) != null ? _a2 : null
|
|
6100
|
+
(_a2 = "default" in child ? child.default : null) != null ? _a2 : null
|
|
5713
6101
|
)
|
|
5714
6102
|
);
|
|
5715
6103
|
} else {
|
|
@@ -5773,19 +6161,23 @@ function renderMultipleContainerElement(element, ctx, wrapper, _pathKey) {
|
|
|
5773
6161
|
inheritedReadonly: childInheritedReadonly
|
|
5774
6162
|
};
|
|
5775
6163
|
const item = document.createElement("div");
|
|
5776
|
-
item.className = "containerItem border border-gray-300 rounded-lg p-
|
|
6164
|
+
item.className = "containerItem border border-gray-300 rounded-lg p-2 bg-white";
|
|
5777
6165
|
item.setAttribute("data-container-item", `${element.key}[${idx}]`);
|
|
5778
6166
|
const childWrapper = document.createElement("div");
|
|
5779
|
-
|
|
5780
|
-
|
|
5781
|
-
childWrapper.className = "space-y-4";
|
|
6167
|
+
if (isSlides) {
|
|
6168
|
+
childWrapper.className = "space-y-2";
|
|
5782
6169
|
} else {
|
|
5783
|
-
|
|
6170
|
+
const columns = element.columns || 1;
|
|
6171
|
+
if (columns === 1) {
|
|
6172
|
+
childWrapper.className = "space-y-2";
|
|
6173
|
+
} else {
|
|
6174
|
+
childWrapper.className = `grid grid-cols-${columns} gap-2`;
|
|
6175
|
+
}
|
|
5784
6176
|
}
|
|
5785
6177
|
element.elements.forEach((child) => {
|
|
5786
6178
|
var _a3, _b2;
|
|
5787
|
-
if (child.hidden || child.type === "hidden") {
|
|
5788
|
-
const prefillVal = (_b2 = (_a3 = prefillObj == null ? void 0 : prefillObj[child.key]) != null ? _a3 : child.default) != null ? _b2 : null;
|
|
6179
|
+
if (child.type !== "markdown" && (child.hidden || child.type === "hidden")) {
|
|
6180
|
+
const prefillVal = (_b2 = (_a3 = prefillObj == null ? void 0 : prefillObj[child.key]) != null ? _a3 : "default" in child ? child.default : null) != null ? _b2 : null;
|
|
5789
6181
|
childWrapper.appendChild(
|
|
5790
6182
|
createHiddenInput(pathJoin(subCtx.path, child.key), prefillVal)
|
|
5791
6183
|
);
|
|
@@ -5830,22 +6222,26 @@ function renderMultipleContainerElement(element, ctx, wrapper, _pathKey) {
|
|
|
5830
6222
|
inheritedReadonly: childInheritedReadonly
|
|
5831
6223
|
};
|
|
5832
6224
|
const item = document.createElement("div");
|
|
5833
|
-
item.className = "containerItem border border-gray-300 rounded-lg p-
|
|
6225
|
+
item.className = "containerItem border border-gray-300 rounded-lg p-2 bg-white";
|
|
5834
6226
|
item.setAttribute("data-container-item", `${element.key}[${idx}]`);
|
|
5835
6227
|
const childWrapper = document.createElement("div");
|
|
5836
|
-
|
|
5837
|
-
|
|
5838
|
-
childWrapper.className = "space-y-4";
|
|
6228
|
+
if (isSlides) {
|
|
6229
|
+
childWrapper.className = "space-y-2";
|
|
5839
6230
|
} else {
|
|
5840
|
-
|
|
6231
|
+
const columns = element.columns || 1;
|
|
6232
|
+
if (columns === 1) {
|
|
6233
|
+
childWrapper.className = "space-y-2";
|
|
6234
|
+
} else {
|
|
6235
|
+
childWrapper.className = `grid grid-cols-${columns} gap-2`;
|
|
6236
|
+
}
|
|
5841
6237
|
}
|
|
5842
6238
|
element.elements.forEach((child) => {
|
|
5843
6239
|
var _a2;
|
|
5844
|
-
if (child.hidden || child.type === "hidden") {
|
|
6240
|
+
if (child.type !== "markdown" && (child.hidden || child.type === "hidden")) {
|
|
5845
6241
|
childWrapper.appendChild(
|
|
5846
6242
|
createHiddenInput(
|
|
5847
6243
|
pathJoin(subCtx.path, child.key),
|
|
5848
|
-
(_a2 = child.default) != null ? _a2 : null
|
|
6244
|
+
(_a2 = "default" in child ? child.default : null) != null ? _a2 : null
|
|
5849
6245
|
)
|
|
5850
6246
|
);
|
|
5851
6247
|
} else {
|
|
@@ -6035,6 +6431,7 @@ function updateContainerField(element, fieldPath, value, context) {
|
|
|
6035
6431
|
if (isPlainObject(itemValue)) {
|
|
6036
6432
|
element.elements.forEach((childElement) => {
|
|
6037
6433
|
var _a, _b;
|
|
6434
|
+
if (childElement.type === "markdown" || !childElement.key) return;
|
|
6038
6435
|
const childPath = `${fieldPath}[${index}].${childElement.key}`;
|
|
6039
6436
|
if (childElement.type === "richinput" && childElement.flatOutput) {
|
|
6040
6437
|
const richChild = childElement;
|
|
@@ -6075,6 +6472,7 @@ function updateContainerField(element, fieldPath, value, context) {
|
|
|
6075
6472
|
}
|
|
6076
6473
|
element.elements.forEach((childElement) => {
|
|
6077
6474
|
var _a, _b;
|
|
6475
|
+
if (childElement.type === "markdown" || !childElement.key) return;
|
|
6078
6476
|
const childPath = `${fieldPath}.${childElement.key}`;
|
|
6079
6477
|
if (childElement.type === "richinput" && childElement.flatOutput) {
|
|
6080
6478
|
const richChild = childElement;
|
|
@@ -7908,7 +8306,7 @@ function filterFilesForDropdown(query, files, labels) {
|
|
|
7908
8306
|
});
|
|
7909
8307
|
}
|
|
7910
8308
|
var TEXTAREA_FONT = "font-size: var(--fb-font-size, 14px); font-family: var(--fb-font-family, inherit); line-height: 1.6;";
|
|
7911
|
-
var TEXTAREA_PADDING = "padding:
|
|
8309
|
+
var TEXTAREA_PADDING = "padding: 8px 40px 8px 10px;";
|
|
7912
8310
|
function renderEditMode(element, ctx, wrapper, pathKey, initialValue) {
|
|
7913
8311
|
var _a;
|
|
7914
8312
|
const state = ctx.state;
|
|
@@ -7957,7 +8355,7 @@ function renderEditMode(element, ctx, wrapper, pathKey, initialValue) {
|
|
|
7957
8355
|
});
|
|
7958
8356
|
const errorEl = document.createElement("div");
|
|
7959
8357
|
errorEl.className = "fb-richinput-error";
|
|
7960
|
-
errorEl.style.cssText = "display: none; color: var(--fb-error-color, #ef4444); font-size: var(--fb-font-size-small, 12px); padding: 4px
|
|
8358
|
+
errorEl.style.cssText = "display: none; color: var(--fb-error-color, #ef4444); font-size: var(--fb-font-size-small, 12px); padding: 4px 10px 6px;";
|
|
7961
8359
|
let errorTimer = null;
|
|
7962
8360
|
function showUploadError(message) {
|
|
7963
8361
|
errorEl.textContent = message;
|
|
@@ -8035,7 +8433,7 @@ function renderEditMode(element, ctx, wrapper, pathKey, initialValue) {
|
|
|
8035
8433
|
});
|
|
8036
8434
|
const filesRow = document.createElement("div");
|
|
8037
8435
|
filesRow.className = "fb-richinput-files";
|
|
8038
|
-
filesRow.style.cssText = "display: none; flex-wrap: wrap; gap:
|
|
8436
|
+
filesRow.style.cssText = "display: none; flex-wrap: wrap; gap: 4px; padding: 6px 10px 0; align-items: center;";
|
|
8039
8437
|
const fileInput = document.createElement("input");
|
|
8040
8438
|
fileInput.type = "file";
|
|
8041
8439
|
fileInput.multiple = true;
|
|
@@ -8171,13 +8569,13 @@ function renderEditMode(element, ctx, wrapper, pathKey, initialValue) {
|
|
|
8171
8569
|
paperclipBtn.title = t("richinputAttachFile", state);
|
|
8172
8570
|
paperclipBtn.style.cssText = `
|
|
8173
8571
|
position: absolute;
|
|
8174
|
-
right:
|
|
8175
|
-
bottom:
|
|
8572
|
+
right: 6px;
|
|
8573
|
+
bottom: 6px;
|
|
8176
8574
|
z-index: 2;
|
|
8177
|
-
width:
|
|
8178
|
-
height:
|
|
8575
|
+
width: 28px;
|
|
8576
|
+
height: 28px;
|
|
8179
8577
|
border: none;
|
|
8180
|
-
border-radius:
|
|
8578
|
+
border-radius: 6px;
|
|
8181
8579
|
background: transparent;
|
|
8182
8580
|
cursor: pointer;
|
|
8183
8581
|
display: flex;
|
|
@@ -8545,7 +8943,7 @@ function renderEditMode(element, ctx, wrapper, pathKey, initialValue) {
|
|
|
8545
8943
|
outerDiv.appendChild(errorEl);
|
|
8546
8944
|
if (element.minLength != null || element.maxLength != null) {
|
|
8547
8945
|
const counterRow = document.createElement("div");
|
|
8548
|
-
counterRow.style.cssText = "position: relative; padding: 2px
|
|
8946
|
+
counterRow.style.cssText = "position: relative; padding: 2px 10px 4px; text-align: right;";
|
|
8549
8947
|
const counter = createCharCounter(element, textarea, false);
|
|
8550
8948
|
counter.style.cssText = `
|
|
8551
8949
|
position: static;
|
|
@@ -8842,6 +9240,250 @@ function updateRichInputField(element, fieldPath, value, context) {
|
|
|
8842
9240
|
}
|
|
8843
9241
|
}
|
|
8844
9242
|
|
|
9243
|
+
// src/components/markdown/snarkdown.ts
|
|
9244
|
+
var TAGS = {
|
|
9245
|
+
"": ["<em>", "</em>"],
|
|
9246
|
+
_: ["<strong>", "</strong>"],
|
|
9247
|
+
"*": ["<strong>", "</strong>"],
|
|
9248
|
+
"~": ["<s>", "</s>"],
|
|
9249
|
+
"\n": ["<br />"],
|
|
9250
|
+
" ": ["<br />"],
|
|
9251
|
+
"-": ["<hr />"]
|
|
9252
|
+
};
|
|
9253
|
+
function outdent(str) {
|
|
9254
|
+
return str.replace(
|
|
9255
|
+
RegExp("^" + (str.match(/^(\t| )+/) || "")[0], "gm"),
|
|
9256
|
+
""
|
|
9257
|
+
);
|
|
9258
|
+
}
|
|
9259
|
+
function encodeAttr(str) {
|
|
9260
|
+
return (str + "").replace(/"/g, """).replace(/</g, "<").replace(/>/g, ">");
|
|
9261
|
+
}
|
|
9262
|
+
function parse(md, prevLinks) {
|
|
9263
|
+
const tokenizer = /((?:^|\n+)(?:\n---+|\*[ ]\*(?:[ ]\*)+)\n)|(?:^```[ ]*(\w*)\n([\s\S]*?)\n```$)|((?:(?:^|\n+)(?:\t|[ ]{2,}).+)+\n*)|((?:(?:^|\n)([>*+-]|\d+\.)\s+.*)+)|(?:!\[([^\]]*?)\]\(([^)]+?)\))|(\[)|(\](?:\(([^)]+?)\))?)|(?:(?:^|\n+)([^\s].*)\n(-{3,}|={3,})(?:\n+|$))|(?:(?:^|\n+)(#{1,6})\s*(.+)(?:\n+|$))|(?:`([^`].*?)`)|([ ]{2}\n\n*|\n{2,}|__|\*\*|[_*]|~~)/gm;
|
|
9264
|
+
const context = [];
|
|
9265
|
+
let out = "";
|
|
9266
|
+
const links = prevLinks || {};
|
|
9267
|
+
let last = 0;
|
|
9268
|
+
let chunk;
|
|
9269
|
+
let prev;
|
|
9270
|
+
let token;
|
|
9271
|
+
let inner;
|
|
9272
|
+
let t2;
|
|
9273
|
+
function tag(token2) {
|
|
9274
|
+
const desc = TAGS[token2[1] || ""];
|
|
9275
|
+
const end = context[context.length - 1] === token2;
|
|
9276
|
+
if (!desc) return token2;
|
|
9277
|
+
if (!desc[1]) return desc[0];
|
|
9278
|
+
if (end) context.pop();
|
|
9279
|
+
else context.push(token2);
|
|
9280
|
+
return desc[end ? 1 : 0];
|
|
9281
|
+
}
|
|
9282
|
+
function flush() {
|
|
9283
|
+
let str = "";
|
|
9284
|
+
while (context.length) str += tag(context[context.length - 1]);
|
|
9285
|
+
return str;
|
|
9286
|
+
}
|
|
9287
|
+
md = md.replace(/^\[(.+?)\]:\s*(.+)$/gm, (_s, name, url) => {
|
|
9288
|
+
links[name.toLowerCase()] = url;
|
|
9289
|
+
return "";
|
|
9290
|
+
}).replace(/^\n+|\n+$/g, "");
|
|
9291
|
+
while (token = tokenizer.exec(md)) {
|
|
9292
|
+
prev = md.substring(last, token.index);
|
|
9293
|
+
last = tokenizer.lastIndex;
|
|
9294
|
+
chunk = token[0];
|
|
9295
|
+
if (prev.match(/[^\\](\\\\)*\\$/)) ; else if (t2 = token[3] || token[4]) {
|
|
9296
|
+
chunk = '<pre class="code ' + (token[4] ? "poetry" : token[2].toLowerCase()) + '"><code' + (token[2] ? ` class="language-${token[2].toLowerCase()}"` : "") + ">" + outdent(encodeAttr(t2).replace(/^\n+|\n+$/g, "")) + "</code></pre>";
|
|
9297
|
+
} else if (t2 = token[6]) {
|
|
9298
|
+
if (t2.match(/\./)) {
|
|
9299
|
+
token[5] = token[5].replace(/^\d+/gm, "");
|
|
9300
|
+
}
|
|
9301
|
+
inner = parse(outdent(token[5].replace(/^\s*[>*+.-]/gm, "")));
|
|
9302
|
+
if (t2 === ">") t2 = "blockquote";
|
|
9303
|
+
else {
|
|
9304
|
+
t2 = t2.match(/\./) ? "ol" : "ul";
|
|
9305
|
+
inner = inner.replace(/^(.*)(\n|$)/gm, "<li>$1</li>");
|
|
9306
|
+
}
|
|
9307
|
+
chunk = "<" + t2 + ">" + inner + "</" + t2 + ">";
|
|
9308
|
+
} else if (token[8]) {
|
|
9309
|
+
chunk = `<img src="${encodeAttr(token[8])}" alt="${encodeAttr(token[7])}">`;
|
|
9310
|
+
} else if (token[10]) {
|
|
9311
|
+
out = out.replace(
|
|
9312
|
+
"<a>",
|
|
9313
|
+
`<a href="${encodeAttr(token[11] || links[prev.toLowerCase()])}">`
|
|
9314
|
+
);
|
|
9315
|
+
chunk = flush() + "</a>";
|
|
9316
|
+
} else if (token[9]) {
|
|
9317
|
+
chunk = "<a>";
|
|
9318
|
+
} else if (token[12] || token[14]) {
|
|
9319
|
+
t2 = "h" + (token[14] ? token[14].length : token[13] > "=" ? 1 : 2);
|
|
9320
|
+
chunk = "<" + t2 + ">" + parse(token[12] || token[15], links) + "</" + t2 + ">";
|
|
9321
|
+
} else if (token[16]) {
|
|
9322
|
+
chunk = "<code>" + encodeAttr(token[16]) + "</code>";
|
|
9323
|
+
} else if (token[17] || token[1]) {
|
|
9324
|
+
chunk = tag(token[17] || "--");
|
|
9325
|
+
}
|
|
9326
|
+
out += prev;
|
|
9327
|
+
out += chunk;
|
|
9328
|
+
}
|
|
9329
|
+
return (out + md.substring(last) + flush()).replace(/^\n+|\n+$/g, "");
|
|
9330
|
+
}
|
|
9331
|
+
|
|
9332
|
+
// src/components/markdown/render.ts
|
|
9333
|
+
var STYLE_ID2 = "fb-markdown-styles";
|
|
9334
|
+
function ensureMarkdownStyles() {
|
|
9335
|
+
if (typeof document === "undefined") return;
|
|
9336
|
+
if (document.getElementById(STYLE_ID2)) return;
|
|
9337
|
+
const style = document.createElement("style");
|
|
9338
|
+
style.id = STYLE_ID2;
|
|
9339
|
+
style.setAttribute("data-fb-markdown-styles", "true");
|
|
9340
|
+
style.textContent = `
|
|
9341
|
+
.fb-markdown {
|
|
9342
|
+
font-family: var(--fb-font-family, inherit);
|
|
9343
|
+
font-size: var(--fb-font-size, 1rem);
|
|
9344
|
+
color: var(--fb-text-color, #1f2937);
|
|
9345
|
+
line-height: 1.6;
|
|
9346
|
+
}
|
|
9347
|
+
.fb-markdown h1,
|
|
9348
|
+
.fb-markdown h2,
|
|
9349
|
+
.fb-markdown h3,
|
|
9350
|
+
.fb-markdown h4,
|
|
9351
|
+
.fb-markdown h5,
|
|
9352
|
+
.fb-markdown h6 {
|
|
9353
|
+
font-weight: var(--fb-font-weight-medium, 600);
|
|
9354
|
+
color: var(--fb-text-color, #1f2937);
|
|
9355
|
+
margin-top: 0.75em;
|
|
9356
|
+
margin-bottom: 0.25em;
|
|
9357
|
+
}
|
|
9358
|
+
.fb-markdown h1 { font-size: 1.5rem; }
|
|
9359
|
+
.fb-markdown h2 { font-size: 1.25rem; }
|
|
9360
|
+
.fb-markdown h3 { font-size: 1.1rem; }
|
|
9361
|
+
.fb-markdown h4,
|
|
9362
|
+
.fb-markdown h5,
|
|
9363
|
+
.fb-markdown h6 { font-size: 1rem; }
|
|
9364
|
+
.fb-markdown p {
|
|
9365
|
+
margin-top: 0;
|
|
9366
|
+
margin-bottom: 0.5em;
|
|
9367
|
+
}
|
|
9368
|
+
.fb-markdown ul,
|
|
9369
|
+
.fb-markdown ol {
|
|
9370
|
+
margin: 0.25em 0 0.5em 1.5em;
|
|
9371
|
+
padding: 0;
|
|
9372
|
+
}
|
|
9373
|
+
.fb-markdown li {
|
|
9374
|
+
margin-bottom: 0.15em;
|
|
9375
|
+
}
|
|
9376
|
+
.fb-markdown a {
|
|
9377
|
+
color: var(--fb-primary-color, #2563eb);
|
|
9378
|
+
text-decoration: underline;
|
|
9379
|
+
}
|
|
9380
|
+
.fb-markdown a:hover {
|
|
9381
|
+
opacity: 0.8;
|
|
9382
|
+
}
|
|
9383
|
+
.fb-markdown code {
|
|
9384
|
+
background: var(--fb-input-background-color, #f3f4f6);
|
|
9385
|
+
border-radius: 3px;
|
|
9386
|
+
padding: 0.1em 0.35em;
|
|
9387
|
+
font-size: 0.9em;
|
|
9388
|
+
font-family: monospace;
|
|
9389
|
+
}
|
|
9390
|
+
.fb-markdown pre {
|
|
9391
|
+
background: var(--fb-input-background-color, #f3f4f6);
|
|
9392
|
+
border-radius: var(--fb-border-radius, 0.375rem);
|
|
9393
|
+
padding: 0.75em 1em;
|
|
9394
|
+
overflow-x: auto;
|
|
9395
|
+
margin: 0.5em 0;
|
|
9396
|
+
}
|
|
9397
|
+
.fb-markdown pre code {
|
|
9398
|
+
background: none;
|
|
9399
|
+
padding: 0;
|
|
9400
|
+
border-radius: 0;
|
|
9401
|
+
font-size: inherit;
|
|
9402
|
+
}
|
|
9403
|
+
.fb-markdown blockquote {
|
|
9404
|
+
border-left: 3px solid var(--fb-border-color, #d1d5db);
|
|
9405
|
+
margin: 0.5em 0;
|
|
9406
|
+
padding-left: 1em;
|
|
9407
|
+
color: var(--fb-text-secondary-color, #6b7280);
|
|
9408
|
+
}
|
|
9409
|
+
.fb-markdown hr {
|
|
9410
|
+
border: none;
|
|
9411
|
+
border-top: 1px solid var(--fb-border-color, #d1d5db);
|
|
9412
|
+
margin: 0.75em 0;
|
|
9413
|
+
}
|
|
9414
|
+
.fb-markdown strong { font-weight: var(--fb-font-weight-medium, 600); }
|
|
9415
|
+
.fb-markdown em { font-style: italic; }
|
|
9416
|
+
.fb-markdown s { text-decoration: line-through; }
|
|
9417
|
+
`;
|
|
9418
|
+
document.head.appendChild(style);
|
|
9419
|
+
}
|
|
9420
|
+
var ANCHOR_DANGEROUS_SCHEMES = [
|
|
9421
|
+
"javascript:",
|
|
9422
|
+
"data:",
|
|
9423
|
+
"vbscript:",
|
|
9424
|
+
"blob:"
|
|
9425
|
+
];
|
|
9426
|
+
var IMG_DANGEROUS_SCHEMES = ["javascript:", "vbscript:", "blob:"];
|
|
9427
|
+
function isImgSrcDangerous(normalized) {
|
|
9428
|
+
if (IMG_DANGEROUS_SCHEMES.some((scheme) => normalized.startsWith(scheme))) {
|
|
9429
|
+
return true;
|
|
9430
|
+
}
|
|
9431
|
+
if (normalized.startsWith("data:") && !normalized.startsWith("data:image/")) {
|
|
9432
|
+
return true;
|
|
9433
|
+
}
|
|
9434
|
+
return false;
|
|
9435
|
+
}
|
|
9436
|
+
function escapeRawHtml(content) {
|
|
9437
|
+
return content.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
9438
|
+
}
|
|
9439
|
+
function sanitizeElements(container) {
|
|
9440
|
+
const anchors = container.querySelectorAll("a[href]");
|
|
9441
|
+
anchors.forEach((a) => {
|
|
9442
|
+
var _a;
|
|
9443
|
+
const href = (_a = a.getAttribute("href")) != null ? _a : "";
|
|
9444
|
+
const normalized = href.trim().toLowerCase();
|
|
9445
|
+
const isDangerous = ANCHOR_DANGEROUS_SCHEMES.some(
|
|
9446
|
+
(scheme) => normalized.startsWith(scheme)
|
|
9447
|
+
);
|
|
9448
|
+
if (isDangerous) {
|
|
9449
|
+
a.setAttribute("href", "#");
|
|
9450
|
+
}
|
|
9451
|
+
a.setAttribute("target", "_blank");
|
|
9452
|
+
a.setAttribute("rel", "noopener noreferrer");
|
|
9453
|
+
});
|
|
9454
|
+
const images = container.querySelectorAll("img[src]");
|
|
9455
|
+
images.forEach((img) => {
|
|
9456
|
+
var _a;
|
|
9457
|
+
const src = (_a = img.getAttribute("src")) != null ? _a : "";
|
|
9458
|
+
const normalized = src.trim().toLowerCase();
|
|
9459
|
+
if (isImgSrcDangerous(normalized)) {
|
|
9460
|
+
img.setAttribute("src", "");
|
|
9461
|
+
}
|
|
9462
|
+
});
|
|
9463
|
+
}
|
|
9464
|
+
function renderMarkdown(element, _ctx, parent) {
|
|
9465
|
+
if (typeof element.content !== "string") {
|
|
9466
|
+
throw new Error(
|
|
9467
|
+
`renderMarkdown: markdown element${element.key ? ` "${element.key}"` : ""} requires "content" to be a string (got ${element.content === null ? "null" : typeof element.content})`
|
|
9468
|
+
);
|
|
9469
|
+
}
|
|
9470
|
+
ensureMarkdownStyles();
|
|
9471
|
+
const wrapper = document.createElement("div");
|
|
9472
|
+
wrapper.className = "fb-markdown";
|
|
9473
|
+
const escaped = escapeRawHtml(element.content);
|
|
9474
|
+
wrapper.innerHTML = parse(escaped);
|
|
9475
|
+
sanitizeElements(wrapper);
|
|
9476
|
+
parent.appendChild(wrapper);
|
|
9477
|
+
return wrapper;
|
|
9478
|
+
}
|
|
9479
|
+
|
|
9480
|
+
// src/components/markdown/index.ts
|
|
9481
|
+
function validateMarkdown(_element, _key, _context) {
|
|
9482
|
+
return { value: void 0, errors: [], skip: true };
|
|
9483
|
+
}
|
|
9484
|
+
function updateMarkdown(_element, _fieldPath, _value, _context) {
|
|
9485
|
+
}
|
|
9486
|
+
|
|
8845
9487
|
// src/components/index.ts
|
|
8846
9488
|
function showTooltip(tooltipId, button) {
|
|
8847
9489
|
const tooltip = document.getElementById(tooltipId);
|
|
@@ -9073,9 +9715,10 @@ function setupEnableIfListeners(wrapper, element, ctx) {
|
|
|
9073
9715
|
});
|
|
9074
9716
|
}
|
|
9075
9717
|
function createFieldLabel(element) {
|
|
9718
|
+
var _a, _b;
|
|
9076
9719
|
const title = document.createElement("label");
|
|
9077
9720
|
title.className = "text-sm font-medium text-gray-900";
|
|
9078
|
-
title.textContent = element.label
|
|
9721
|
+
title.textContent = (_b = (_a = element.label) != null ? _a : element.key) != null ? _b : "";
|
|
9079
9722
|
if (element.required) {
|
|
9080
9723
|
const req = document.createElement("span");
|
|
9081
9724
|
req.className = "text-red-500 ml-1";
|
|
@@ -9105,7 +9748,7 @@ function createInfoButton(element) {
|
|
|
9105
9748
|
}
|
|
9106
9749
|
function createLabelContainer(element) {
|
|
9107
9750
|
const label = document.createElement("div");
|
|
9108
|
-
label.className = "flex items-center mb-
|
|
9751
|
+
label.className = "flex items-center mb-1";
|
|
9109
9752
|
const title = createFieldLabel(element);
|
|
9110
9753
|
label.appendChild(title);
|
|
9111
9754
|
if (element.description || element.hint) {
|
|
@@ -9203,9 +9846,32 @@ function dispatchToRenderer(element, ctx, wrapper, pathKey) {
|
|
|
9203
9846
|
}
|
|
9204
9847
|
}
|
|
9205
9848
|
function renderElement2(element, ctx) {
|
|
9849
|
+
if (element.type === "markdown") {
|
|
9850
|
+
if (element.hidden === true) {
|
|
9851
|
+
const placeholder = document.createElement("div");
|
|
9852
|
+
placeholder.style.display = "none";
|
|
9853
|
+
placeholder.setAttribute("data-fb-hidden-markdown", "true");
|
|
9854
|
+
return placeholder;
|
|
9855
|
+
}
|
|
9856
|
+
const initiallyDisabled2 = shouldDisableElement(element, ctx);
|
|
9857
|
+
const outerWrapper = document.createElement("div");
|
|
9858
|
+
outerWrapper.className = "mb-2 fb-field-wrapper fb-markdown-wrapper";
|
|
9859
|
+
outerWrapper.setAttribute(
|
|
9860
|
+
"data-field-key",
|
|
9861
|
+
getElementLookupKey(element, ctx.state)
|
|
9862
|
+
);
|
|
9863
|
+
renderMarkdown(element, ctx, outerWrapper);
|
|
9864
|
+
if (initiallyDisabled2) {
|
|
9865
|
+
outerWrapper.style.display = "none";
|
|
9866
|
+
outerWrapper.classList.add("fb-field-wrapper-disabled");
|
|
9867
|
+
outerWrapper.setAttribute("data-conditionally-disabled", "true");
|
|
9868
|
+
}
|
|
9869
|
+
setupEnableIfListeners(outerWrapper, element, ctx);
|
|
9870
|
+
return outerWrapper;
|
|
9871
|
+
}
|
|
9206
9872
|
const initiallyDisabled = shouldDisableElement(element, ctx);
|
|
9207
9873
|
const wrapper = document.createElement("div");
|
|
9208
|
-
wrapper.className = "mb-
|
|
9874
|
+
wrapper.className = "mb-2 fb-field-wrapper";
|
|
9209
9875
|
wrapper.setAttribute("data-field-key", element.key);
|
|
9210
9876
|
const label = createLabelContainer(element);
|
|
9211
9877
|
wrapper.appendChild(label);
|
|
@@ -9272,12 +9938,16 @@ var defaultConfig = {
|
|
|
9272
9938
|
hintPattern: "Format: {pattern}",
|
|
9273
9939
|
fileCountSingle: "{count} file",
|
|
9274
9940
|
fileCountPlural: "{count} files",
|
|
9941
|
+
fileCountWithMax: "{count} / {max} files",
|
|
9275
9942
|
fileCountRange: "({min}-{max})",
|
|
9276
9943
|
uploadingFile: "Uploading\u2026",
|
|
9277
9944
|
filesCounter: "{count}/{max}",
|
|
9278
9945
|
fromLibrary: "From library",
|
|
9279
9946
|
libraryEmpty: "Library is empty",
|
|
9280
9947
|
libraryHint: "Choose from previously uploaded files",
|
|
9948
|
+
dropToUpload: "Release to upload",
|
|
9949
|
+
replaceFile: "Replace",
|
|
9950
|
+
clearAll: "Clear all",
|
|
9281
9951
|
pickerError: "Failed to load files from library",
|
|
9282
9952
|
// Validation errors
|
|
9283
9953
|
required: "Required",
|
|
@@ -9343,12 +10013,16 @@ var defaultConfig = {
|
|
|
9343
10013
|
hintPattern: "\u0424\u043E\u0440\u043C\u0430\u0442: {pattern}",
|
|
9344
10014
|
fileCountSingle: "{count} \u0444\u0430\u0439\u043B",
|
|
9345
10015
|
fileCountPlural: "{count} \u0444\u0430\u0439\u043B\u043E\u0432",
|
|
10016
|
+
fileCountWithMax: "{count} / {max} \u0444\u0430\u0439\u043B\u043E\u0432",
|
|
9346
10017
|
fileCountRange: "({min}-{max})",
|
|
9347
10018
|
uploadingFile: "\u0417\u0430\u0433\u0440\u0443\u0437\u043A\u0430\u2026",
|
|
9348
10019
|
filesCounter: "{count}/{max}",
|
|
9349
10020
|
fromLibrary: "\u0418\u0437 \u0431\u0438\u0431\u043B\u0438\u043E\u0442\u0435\u043A\u0438",
|
|
9350
10021
|
libraryEmpty: "\u0411\u0438\u0431\u043B\u0438\u043E\u0442\u0435\u043A\u0430 \u043F\u0443\u0441\u0442\u0430",
|
|
9351
10022
|
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",
|
|
10023
|
+
dropToUpload: "\u041E\u0442\u043F\u0443\u0441\u0442\u0438\u0442\u0435, \u0447\u0442\u043E\u0431\u044B \u0437\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044C",
|
|
10024
|
+
replaceFile: "\u0417\u0430\u043C\u0435\u043D\u0438\u0442\u044C",
|
|
10025
|
+
clearAll: "\u041E\u0447\u0438\u0441\u0442\u0438\u0442\u044C \u0432\u0441\u0435",
|
|
9352
10026
|
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",
|
|
9353
10027
|
// Validation errors
|
|
9354
10028
|
required: "\u041E\u0431\u044F\u0437\u0430\u0442\u0435\u043B\u044C\u043D\u043E\u0435 \u043F\u043E\u043B\u0435",
|
|
@@ -9413,7 +10087,9 @@ function createInstanceState(config) {
|
|
|
9413
10087
|
translations: mergedTranslations
|
|
9414
10088
|
},
|
|
9415
10089
|
debounceTimer: null,
|
|
9416
|
-
prefill: {}
|
|
10090
|
+
prefill: {},
|
|
10091
|
+
syntheticElementIds: /* @__PURE__ */ new WeakMap(),
|
|
10092
|
+
syntheticElementIdCounter: 0
|
|
9417
10093
|
};
|
|
9418
10094
|
}
|
|
9419
10095
|
function generateInstanceId() {
|
|
@@ -9487,10 +10163,10 @@ var defaultTheme = {
|
|
|
9487
10163
|
fileUploadHoverBorderColor: "#3b82f6",
|
|
9488
10164
|
// blue-500
|
|
9489
10165
|
// Spacing
|
|
9490
|
-
inputPaddingX: "0.
|
|
9491
|
-
//
|
|
9492
|
-
inputPaddingY: "0.
|
|
9493
|
-
//
|
|
10166
|
+
inputPaddingX: "0.5rem",
|
|
10167
|
+
// 8px (compact density v2)
|
|
10168
|
+
inputPaddingY: "0.25rem",
|
|
10169
|
+
// 4px (compact density v2)
|
|
9494
10170
|
borderRadius: "0.5rem",
|
|
9495
10171
|
// rounded-lg (8px)
|
|
9496
10172
|
borderWidth: "1px",
|
|
@@ -9691,6 +10367,11 @@ var componentRegistry = {
|
|
|
9691
10367
|
// Legacy type: `type: "hidden"` — reads/writes DOM <input type="hidden"> element
|
|
9692
10368
|
validate: validateHiddenElement,
|
|
9693
10369
|
update: updateHiddenField
|
|
10370
|
+
},
|
|
10371
|
+
markdown: {
|
|
10372
|
+
// Display-only element — no value, no errors, skip from form data
|
|
10373
|
+
validate: validateMarkdown,
|
|
10374
|
+
update: updateMarkdown
|
|
9694
10375
|
}
|
|
9695
10376
|
};
|
|
9696
10377
|
function getComponentOperations(elementType) {
|
|
@@ -9931,7 +10612,7 @@ var FormBuilderInstance = class {
|
|
|
9931
10612
|
existingContainer.remove();
|
|
9932
10613
|
}
|
|
9933
10614
|
const actionsContainer = document.createElement("div");
|
|
9934
|
-
actionsContainer.className = "form-level-actions-container mt-
|
|
10615
|
+
actionsContainer.className = "form-level-actions-container mt-3 pt-2 flex flex-wrap gap-2 justify-center";
|
|
9935
10616
|
actionsContainer.style.cssText = `
|
|
9936
10617
|
border-top: var(--fb-border-width) solid var(--fb-border-color);
|
|
9937
10618
|
`;
|
|
@@ -10072,7 +10753,7 @@ var FormBuilderInstance = class {
|
|
|
10072
10753
|
*/
|
|
10073
10754
|
createRootPrefillHints(hints) {
|
|
10074
10755
|
const hintsContainer = document.createElement("div");
|
|
10075
|
-
hintsContainer.className = "fb-prefill-hints flex flex-wrap gap-2 mb-
|
|
10756
|
+
hintsContainer.className = "fb-prefill-hints flex flex-wrap gap-2 mb-2";
|
|
10076
10757
|
hints.forEach((hint) => {
|
|
10077
10758
|
const hintButton = document.createElement("button");
|
|
10078
10759
|
hintButton.type = "button";
|
|
@@ -10105,7 +10786,7 @@ var FormBuilderInstance = class {
|
|
|
10105
10786
|
root.setAttribute("data-fb-root", "true");
|
|
10106
10787
|
injectThemeVariables(root, this.state.config.theme);
|
|
10107
10788
|
const rootContainer = document.createElement("div");
|
|
10108
|
-
rootContainer.className = "space-y-
|
|
10789
|
+
rootContainer.className = "space-y-2";
|
|
10109
10790
|
if (schema.prefillHints && !this.state.config.readonly) {
|
|
10110
10791
|
const hintsContainer = this.createRootPrefillHints(schema.prefillHints);
|
|
10111
10792
|
rootContainer.appendChild(hintsContainer);
|
|
@@ -10113,13 +10794,13 @@ var FormBuilderInstance = class {
|
|
|
10113
10794
|
const fieldsWrapper = document.createElement("div");
|
|
10114
10795
|
const columns = schema.columns || 1;
|
|
10115
10796
|
if (columns === 1) {
|
|
10116
|
-
fieldsWrapper.className = "space-y-
|
|
10797
|
+
fieldsWrapper.className = "space-y-2";
|
|
10117
10798
|
} else {
|
|
10118
|
-
fieldsWrapper.className = `grid grid-cols-${columns} gap-
|
|
10799
|
+
fieldsWrapper.className = `grid grid-cols-${columns} gap-2`;
|
|
10119
10800
|
}
|
|
10120
10801
|
schema.elements.forEach((element) => {
|
|
10121
10802
|
var _a, _b;
|
|
10122
|
-
if (element.hidden || element.type === "hidden") {
|
|
10803
|
+
if (element.type !== "markdown" && (element.hidden || element.type === "hidden")) {
|
|
10123
10804
|
const val = (_b = (_a = prefill == null ? void 0 : prefill[element.key]) != null ? _a : element.default) != null ? _b : null;
|
|
10124
10805
|
fieldsWrapper.appendChild(createHiddenInput(element.key, val));
|
|
10125
10806
|
return;
|
|
@@ -10154,7 +10835,8 @@ var FormBuilderInstance = class {
|
|
|
10154
10835
|
const errors = [];
|
|
10155
10836
|
const data = {};
|
|
10156
10837
|
const validateElement2 = (element, ctx, customScopeRoot = null) => {
|
|
10157
|
-
|
|
10838
|
+
var _a;
|
|
10839
|
+
const key = (_a = element.key) != null ? _a : "";
|
|
10158
10840
|
const scopeRoot = customScopeRoot || this.state.formRoot;
|
|
10159
10841
|
const componentContext = {
|
|
10160
10842
|
scopeRoot,
|
|
@@ -10172,7 +10854,8 @@ var FormBuilderInstance = class {
|
|
|
10172
10854
|
errors.push(...componentResult.errors);
|
|
10173
10855
|
return {
|
|
10174
10856
|
value: componentResult.value,
|
|
10175
|
-
spread: !!componentResult.spread
|
|
10857
|
+
spread: !!componentResult.spread,
|
|
10858
|
+
skip: !!componentResult.skip
|
|
10176
10859
|
};
|
|
10177
10860
|
}
|
|
10178
10861
|
console.warn(`Unknown field type "${element.type}" for key "${key}"`);
|
|
@@ -10194,6 +10877,9 @@ var FormBuilderInstance = class {
|
|
|
10194
10877
|
);
|
|
10195
10878
|
}
|
|
10196
10879
|
}
|
|
10880
|
+
if (element.type === "markdown") {
|
|
10881
|
+
return;
|
|
10882
|
+
}
|
|
10197
10883
|
if (element.hidden || element.type === "hidden") {
|
|
10198
10884
|
const hiddenInput = this.state.formRoot.querySelector(
|
|
10199
10885
|
`input[type="hidden"][data-hidden-field="true"][name="${element.key}"]`
|
|
@@ -10206,9 +10892,10 @@ var FormBuilderInstance = class {
|
|
|
10206
10892
|
}
|
|
10207
10893
|
} else {
|
|
10208
10894
|
const result = validateElement2(element, { path: "" });
|
|
10895
|
+
if (result.skip) return;
|
|
10209
10896
|
if (result.spread && result.value !== null && typeof result.value === "object") {
|
|
10210
10897
|
Object.assign(data, result.value);
|
|
10211
|
-
} else {
|
|
10898
|
+
} else if (element.key) {
|
|
10212
10899
|
data[element.key] = result.value;
|
|
10213
10900
|
}
|
|
10214
10901
|
}
|
|
@@ -10284,6 +10971,7 @@ var FormBuilderInstance = class {
|
|
|
10284
10971
|
buildHiddenFieldsData(elements) {
|
|
10285
10972
|
const data = {};
|
|
10286
10973
|
for (const element of elements) {
|
|
10974
|
+
if (element.type === "markdown" || !element.key) continue;
|
|
10287
10975
|
const key = element.key;
|
|
10288
10976
|
if (element.hidden && element.default !== void 0) {
|
|
10289
10977
|
data[key] = element.default;
|
|
@@ -10405,6 +11093,72 @@ var FormBuilderInstance = class {
|
|
|
10405
11093
|
);
|
|
10406
11094
|
}
|
|
10407
11095
|
}
|
|
11096
|
+
/**
|
|
11097
|
+
* Find the field wrapper DOM element for a given element+path combo.
|
|
11098
|
+
* Used by reevaluateConditionalFields.
|
|
11099
|
+
*/
|
|
11100
|
+
findFieldWrapper(element, currentPath) {
|
|
11101
|
+
const formRoot = this.state.formRoot;
|
|
11102
|
+
const lookupKey = getElementLookupKey(element, this.state);
|
|
11103
|
+
if (!currentPath) {
|
|
11104
|
+
return formRoot.querySelector(`[data-field-key="${lookupKey}"]`);
|
|
11105
|
+
}
|
|
11106
|
+
const pathMatch = currentPath.match(/^(.+)\[(\d+)\]$/);
|
|
11107
|
+
if (pathMatch) {
|
|
11108
|
+
const containerEl2 = formRoot.querySelector(
|
|
11109
|
+
`[data-container-item="${pathMatch[1]}[${pathMatch[2]}]"]`
|
|
11110
|
+
);
|
|
11111
|
+
return containerEl2 ? containerEl2.querySelector(`[data-field-key="${lookupKey}"]`) : null;
|
|
11112
|
+
}
|
|
11113
|
+
const containerEl = formRoot.querySelector(
|
|
11114
|
+
`[data-container="${currentPath}"]`
|
|
11115
|
+
);
|
|
11116
|
+
return containerEl ? containerEl.querySelector(`[data-field-key="${lookupKey}"]`) : null;
|
|
11117
|
+
}
|
|
11118
|
+
/**
|
|
11119
|
+
* Apply enableIf show/hide logic to a single field wrapper.
|
|
11120
|
+
* Extracted to reduce cyclomatic complexity of checkElements.
|
|
11121
|
+
*/
|
|
11122
|
+
applyEnableIfVisibility(element, wrapper, currentPath, fullPath, formData) {
|
|
11123
|
+
var _a, _b, _c, _d;
|
|
11124
|
+
try {
|
|
11125
|
+
const scope = (_a = element.enableIf.scope) != null ? _a : "relative";
|
|
11126
|
+
const containerData = scope === "relative" && currentPath ? getValueByPath(formData, currentPath) : void 0;
|
|
11127
|
+
const shouldEnable = evaluateEnableCondition(
|
|
11128
|
+
element.enableIf,
|
|
11129
|
+
formData,
|
|
11130
|
+
containerData
|
|
11131
|
+
);
|
|
11132
|
+
const isCurrentlyDisabled = wrapper.getAttribute("data-conditionally-disabled") === "true";
|
|
11133
|
+
if (shouldEnable && isCurrentlyDisabled) {
|
|
11134
|
+
const containerPrefill = currentPath ? getValueByPath(formData, currentPath) : formData;
|
|
11135
|
+
const prefillContext = containerPrefill && typeof containerPrefill === "object" ? containerPrefill : {};
|
|
11136
|
+
const newWrapper = renderElement2(element, {
|
|
11137
|
+
path: currentPath,
|
|
11138
|
+
prefill: prefillContext,
|
|
11139
|
+
formData,
|
|
11140
|
+
state: this.state,
|
|
11141
|
+
instance: this
|
|
11142
|
+
});
|
|
11143
|
+
(_b = wrapper.parentNode) == null ? void 0 : _b.replaceChild(newWrapper, wrapper);
|
|
11144
|
+
} else if (!shouldEnable && !isCurrentlyDisabled) {
|
|
11145
|
+
const disabledWrapper = document.createElement("div");
|
|
11146
|
+
disabledWrapper.className = "fb-field-wrapper-disabled";
|
|
11147
|
+
disabledWrapper.style.display = "none";
|
|
11148
|
+
disabledWrapper.setAttribute(
|
|
11149
|
+
"data-field-key",
|
|
11150
|
+
getElementLookupKey(element, this.state)
|
|
11151
|
+
);
|
|
11152
|
+
disabledWrapper.setAttribute("data-conditionally-disabled", "true");
|
|
11153
|
+
(_c = wrapper.parentNode) == null ? void 0 : _c.replaceChild(disabledWrapper, wrapper);
|
|
11154
|
+
}
|
|
11155
|
+
} catch (error) {
|
|
11156
|
+
console.error(
|
|
11157
|
+
`Error re-evaluating enableIf for field "${(_d = element.key) != null ? _d : "<no key>"}" at path "${fullPath}":`,
|
|
11158
|
+
error
|
|
11159
|
+
);
|
|
11160
|
+
}
|
|
11161
|
+
}
|
|
10408
11162
|
/**
|
|
10409
11163
|
* Re-evaluate all conditional fields (enableIf) based on current form data
|
|
10410
11164
|
* This is called automatically when form data changes (via onChange events)
|
|
@@ -10414,98 +11168,32 @@ var FormBuilderInstance = class {
|
|
|
10414
11168
|
const formData = this.validateForm(true).data;
|
|
10415
11169
|
const checkElements = (elements, currentPath) => {
|
|
10416
11170
|
elements.forEach((element) => {
|
|
10417
|
-
var _a, _b
|
|
10418
|
-
const fullPath = currentPath ? `${currentPath}.${element.key}` : element.key;
|
|
11171
|
+
var _a, _b;
|
|
11172
|
+
const fullPath = currentPath ? `${currentPath}.${(_a = element.key) != null ? _a : ""}` : (_b = element.key) != null ? _b : "";
|
|
10419
11173
|
if (element.enableIf) {
|
|
10420
|
-
|
|
10421
|
-
if (currentPath) {
|
|
10422
|
-
const pathMatch = currentPath.match(/^(.+)\[(\d+)\]$/);
|
|
10423
|
-
if (pathMatch) {
|
|
10424
|
-
const containerKey = pathMatch[1];
|
|
10425
|
-
const containerIndex = pathMatch[2];
|
|
10426
|
-
const containerElement = this.state.formRoot.querySelector(
|
|
10427
|
-
`[data-container-item="${containerKey}[${containerIndex}]"]`
|
|
10428
|
-
);
|
|
10429
|
-
if (containerElement) {
|
|
10430
|
-
fieldWrapper = containerElement.querySelector(
|
|
10431
|
-
`[data-field-key="${element.key}"]`
|
|
10432
|
-
);
|
|
10433
|
-
}
|
|
10434
|
-
} else {
|
|
10435
|
-
const containerElement = this.state.formRoot.querySelector(
|
|
10436
|
-
`[data-container="${currentPath}"]`
|
|
10437
|
-
);
|
|
10438
|
-
if (containerElement) {
|
|
10439
|
-
fieldWrapper = containerElement.querySelector(
|
|
10440
|
-
`[data-field-key="${element.key}"]`
|
|
10441
|
-
);
|
|
10442
|
-
}
|
|
10443
|
-
}
|
|
10444
|
-
} else {
|
|
10445
|
-
fieldWrapper = this.state.formRoot.querySelector(
|
|
10446
|
-
`[data-field-key="${element.key}"]`
|
|
10447
|
-
);
|
|
10448
|
-
}
|
|
11174
|
+
const fieldWrapper = this.findFieldWrapper(element, currentPath);
|
|
10449
11175
|
if (fieldWrapper) {
|
|
10450
|
-
|
|
10451
|
-
|
|
10452
|
-
|
|
10453
|
-
|
|
10454
|
-
|
|
10455
|
-
|
|
10456
|
-
|
|
10457
|
-
const shouldEnable = evaluateEnableCondition(
|
|
10458
|
-
element.enableIf,
|
|
10459
|
-
formData,
|
|
10460
|
-
// Use complete formData for absolute scope
|
|
10461
|
-
containerData
|
|
10462
|
-
// Use container-specific data for relative scope
|
|
10463
|
-
);
|
|
10464
|
-
const isCurrentlyDisabled = wrapper.getAttribute("data-conditionally-disabled") === "true";
|
|
10465
|
-
if (shouldEnable && isCurrentlyDisabled) {
|
|
10466
|
-
const containerPrefill = currentPath ? getValueByPath(formData, currentPath) : formData;
|
|
10467
|
-
const prefillContext = containerPrefill && typeof containerPrefill === "object" ? containerPrefill : {};
|
|
10468
|
-
const newWrapper = renderElement2(element, {
|
|
10469
|
-
path: currentPath,
|
|
10470
|
-
// Use container path (empty string for root-level)
|
|
10471
|
-
prefill: prefillContext,
|
|
10472
|
-
formData,
|
|
10473
|
-
// Pass complete formData for enableIf evaluation
|
|
10474
|
-
state: this.state,
|
|
10475
|
-
instance: this
|
|
10476
|
-
});
|
|
10477
|
-
(_b = wrapper.parentNode) == null ? void 0 : _b.replaceChild(newWrapper, wrapper);
|
|
10478
|
-
} else if (!shouldEnable && !isCurrentlyDisabled) {
|
|
10479
|
-
const disabledWrapper = document.createElement("div");
|
|
10480
|
-
disabledWrapper.className = "fb-field-wrapper-disabled";
|
|
10481
|
-
disabledWrapper.style.display = "none";
|
|
10482
|
-
disabledWrapper.setAttribute("data-field-key", element.key);
|
|
10483
|
-
disabledWrapper.setAttribute(
|
|
10484
|
-
"data-conditionally-disabled",
|
|
10485
|
-
"true"
|
|
10486
|
-
);
|
|
10487
|
-
(_c = wrapper.parentNode) == null ? void 0 : _c.replaceChild(disabledWrapper, wrapper);
|
|
10488
|
-
}
|
|
10489
|
-
} catch (error) {
|
|
10490
|
-
console.error(
|
|
10491
|
-
`Error re-evaluating enableIf for field "${element.key}" at path "${fullPath}":`,
|
|
10492
|
-
error
|
|
10493
|
-
);
|
|
10494
|
-
}
|
|
11176
|
+
this.applyEnableIfVisibility(
|
|
11177
|
+
element,
|
|
11178
|
+
fieldWrapper,
|
|
11179
|
+
currentPath,
|
|
11180
|
+
fullPath,
|
|
11181
|
+
formData
|
|
11182
|
+
);
|
|
10495
11183
|
}
|
|
10496
11184
|
}
|
|
10497
11185
|
if ((element.type === "container" || element.type === "group") && "elements" in element && element.elements) {
|
|
10498
|
-
const containerData = formData == null ? void 0 : formData[element.key];
|
|
11186
|
+
const containerData = element.key ? formData == null ? void 0 : formData[element.key] : void 0;
|
|
10499
11187
|
if (Array.isArray(containerData)) {
|
|
10500
11188
|
const containerItems = this.state.formRoot.querySelectorAll(
|
|
10501
11189
|
`[data-container-item]`
|
|
10502
11190
|
);
|
|
10503
|
-
const directItems = Array.from(containerItems).filter((el) => {
|
|
11191
|
+
const directItems = fullPath ? Array.from(containerItems).filter((el) => {
|
|
10504
11192
|
const attr = el.getAttribute("data-container-item") || "";
|
|
10505
11193
|
if (!attr.startsWith(`${fullPath}[`)) return false;
|
|
10506
11194
|
const suffix = attr.slice(fullPath.length);
|
|
10507
11195
|
return /^\[\d+\]$/.test(suffix);
|
|
10508
|
-
});
|
|
11196
|
+
}) : [];
|
|
10509
11197
|
directItems.forEach((el) => {
|
|
10510
11198
|
const attr = el.getAttribute("data-container-item") || "";
|
|
10511
11199
|
checkElements(element.elements, attr);
|