@dmitryvim/form-builder 0.2.30 → 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/dist/browser/formbuilder.min.js +502 -387
- package/dist/browser/formbuilder.v0.2.32.min.js +1148 -0
- package/dist/cjs/index.cjs +1085 -703
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/esm/index.js +1050 -677
- package/dist/esm/index.js.map +1 -1
- package/dist/form-builder.js +502 -387
- 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/types/config.d.ts +4 -0
- package/dist/types/types/schema.d.ts +8 -0
- package/package.json +1 -1
- package/dist/browser/formbuilder.v0.2.30.min.js +0 -1033
package/dist/esm/index.js
CHANGED
|
@@ -46,8 +46,9 @@ function addRangeHint(element, parts, state) {
|
|
|
46
46
|
}
|
|
47
47
|
}
|
|
48
48
|
function addFileSizeHint(element, parts, state) {
|
|
49
|
-
|
|
50
|
-
|
|
49
|
+
const sizeMB = element.maxSize ?? element.maxSizeMB;
|
|
50
|
+
if (sizeMB && sizeMB !== Infinity) {
|
|
51
|
+
parts.push(t("hintMaxSize", state, { size: sizeMB }));
|
|
51
52
|
}
|
|
52
53
|
}
|
|
53
54
|
function addFormatHint(element, parts, state) {
|
|
@@ -120,6 +121,25 @@ function validateSchema(schema) {
|
|
|
120
121
|
});
|
|
121
122
|
}
|
|
122
123
|
}
|
|
124
|
+
function validateContainerProps(element, elementPath, errors2) {
|
|
125
|
+
if ("columns" in element && element.columns !== void 0) {
|
|
126
|
+
const columns = element.columns;
|
|
127
|
+
const validColumns = [1, 2, 3, 4];
|
|
128
|
+
if (!Number.isInteger(columns) || !validColumns.includes(columns)) {
|
|
129
|
+
errors2.push(
|
|
130
|
+
`${elementPath}: columns must be 1, 2, 3, or 4 (got ${columns})`
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
if ("displayMode" in element && element.displayMode !== void 0) {
|
|
135
|
+
const displayMode = element.displayMode;
|
|
136
|
+
if (displayMode !== "stack" && displayMode !== "slides") {
|
|
137
|
+
errors2.push(
|
|
138
|
+
`${elementPath}: displayMode must be "stack" or "slides" (got ${JSON.stringify(displayMode)})`
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
123
143
|
function checkFlatOutputCollisions(elements, scopePath) {
|
|
124
144
|
const allOutputKeys = /* @__PURE__ */ new Set();
|
|
125
145
|
for (const el of elements) {
|
|
@@ -199,15 +219,7 @@ function validateSchema(schema) {
|
|
|
199
219
|
validateElements(element.elements, `${elementPath}.elements`);
|
|
200
220
|
}
|
|
201
221
|
if (element.type === "container" && element.elements) {
|
|
202
|
-
|
|
203
|
-
const columns = element.columns;
|
|
204
|
-
const validColumns = [1, 2, 3, 4];
|
|
205
|
-
if (!Number.isInteger(columns) || !validColumns.includes(columns)) {
|
|
206
|
-
errors.push(
|
|
207
|
-
`${elementPath}: columns must be 1, 2, 3, or 4 (got ${columns})`
|
|
208
|
-
);
|
|
209
|
-
}
|
|
210
|
-
}
|
|
222
|
+
validateContainerProps(element, elementPath, errors);
|
|
211
223
|
if ("prefillHints" in element && element.prefillHints) {
|
|
212
224
|
const prefillHints = element.prefillHints;
|
|
213
225
|
if (Array.isArray(prefillHints)) {
|
|
@@ -840,8 +852,12 @@ function renderTextareaElement(element, ctx, wrapper, pathKey) {
|
|
|
840
852
|
const textareaWrapper = document.createElement("div");
|
|
841
853
|
textareaWrapper.style.cssText = "position: relative;";
|
|
842
854
|
const textareaInput = document.createElement("textarea");
|
|
843
|
-
textareaInput.className = "w-full
|
|
844
|
-
textareaInput.style.cssText =
|
|
855
|
+
textareaInput.className = "w-full border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 resize-none";
|
|
856
|
+
textareaInput.style.cssText = `
|
|
857
|
+
padding: var(--fb-input-padding-y) var(--fb-input-padding-x) 24px var(--fb-input-padding-x);
|
|
858
|
+
font-size: var(--fb-font-size);
|
|
859
|
+
font-family: var(--fb-font-family);
|
|
860
|
+
`;
|
|
845
861
|
textareaInput.name = pathKey;
|
|
846
862
|
textareaInput.placeholder = element.placeholder || "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u0442\u0435\u043A\u0441\u0442";
|
|
847
863
|
textareaInput.rows = element.rows || 4;
|
|
@@ -893,8 +909,12 @@ function renderMultipleTextareaElement(element, ctx, wrapper, pathKey) {
|
|
|
893
909
|
const textareaContainer = document.createElement("div");
|
|
894
910
|
textareaContainer.style.cssText = "position: relative;";
|
|
895
911
|
const textareaInput = document.createElement("textarea");
|
|
896
|
-
textareaInput.className = "w-full
|
|
897
|
-
textareaInput.style.cssText =
|
|
912
|
+
textareaInput.className = "w-full border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 resize-none";
|
|
913
|
+
textareaInput.style.cssText = `
|
|
914
|
+
padding: var(--fb-input-padding-y) var(--fb-input-padding-x) 24px var(--fb-input-padding-x);
|
|
915
|
+
font-size: var(--fb-font-size);
|
|
916
|
+
font-family: var(--fb-font-family);
|
|
917
|
+
`;
|
|
898
918
|
textareaInput.placeholder = element.placeholder || t("placeholderText", state);
|
|
899
919
|
textareaInput.rows = element.rows || 4;
|
|
900
920
|
textareaInput.value = value;
|
|
@@ -1080,8 +1100,14 @@ function renderNumberElement(element, ctx, wrapper, pathKey) {
|
|
|
1080
1100
|
inputWrapper.style.cssText = "position: relative;";
|
|
1081
1101
|
const numberInput = document.createElement("input");
|
|
1082
1102
|
numberInput.type = "number";
|
|
1083
|
-
numberInput.className = "w-full
|
|
1084
|
-
numberInput.style.cssText =
|
|
1103
|
+
numberInput.className = "w-full border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500";
|
|
1104
|
+
numberInput.style.cssText = `
|
|
1105
|
+
padding: var(--fb-input-padding-y) 60px var(--fb-input-padding-y) var(--fb-input-padding-x);
|
|
1106
|
+
font-size: var(--fb-font-size);
|
|
1107
|
+
font-family: var(--fb-font-family);
|
|
1108
|
+
width: 100%;
|
|
1109
|
+
box-sizing: border-box;
|
|
1110
|
+
`;
|
|
1085
1111
|
numberInput.name = pathKey;
|
|
1086
1112
|
numberInput.placeholder = element.placeholder || "0";
|
|
1087
1113
|
if (element.min !== void 0) numberInput.min = element.min.toString();
|
|
@@ -1133,8 +1159,14 @@ function renderMultipleNumberElement(element, ctx, wrapper, pathKey) {
|
|
|
1133
1159
|
inputContainer.style.cssText = "position: relative; flex: 1;";
|
|
1134
1160
|
const numberInput = document.createElement("input");
|
|
1135
1161
|
numberInput.type = "number";
|
|
1136
|
-
numberInput.className = "w-full
|
|
1137
|
-
numberInput.style.cssText =
|
|
1162
|
+
numberInput.className = "w-full border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500";
|
|
1163
|
+
numberInput.style.cssText = `
|
|
1164
|
+
padding: var(--fb-input-padding-y) 60px var(--fb-input-padding-y) var(--fb-input-padding-x);
|
|
1165
|
+
font-size: var(--fb-font-size);
|
|
1166
|
+
font-family: var(--fb-font-family);
|
|
1167
|
+
width: 100%;
|
|
1168
|
+
box-sizing: border-box;
|
|
1169
|
+
`;
|
|
1138
1170
|
numberInput.placeholder = element.placeholder || "0";
|
|
1139
1171
|
if (element.min !== void 0) numberInput.min = element.min.toString();
|
|
1140
1172
|
if (element.max !== void 0) numberInput.max = element.max.toString();
|
|
@@ -1405,7 +1437,12 @@ function renderSelectElement(element, ctx, wrapper, pathKey) {
|
|
|
1405
1437
|
const state = ctx.state;
|
|
1406
1438
|
const readonly = isElementReadonly(element, state, ctx);
|
|
1407
1439
|
const selectInput = document.createElement("select");
|
|
1408
|
-
selectInput.className = "w-full
|
|
1440
|
+
selectInput.className = "w-full border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500";
|
|
1441
|
+
selectInput.style.cssText = `
|
|
1442
|
+
padding: var(--fb-input-padding-y) var(--fb-input-padding-x);
|
|
1443
|
+
font-size: var(--fb-font-size);
|
|
1444
|
+
font-family: var(--fb-font-family);
|
|
1445
|
+
`;
|
|
1409
1446
|
selectInput.name = pathKey;
|
|
1410
1447
|
selectInput.disabled = readonly;
|
|
1411
1448
|
(element.options || []).forEach((option) => {
|
|
@@ -1457,7 +1494,12 @@ function renderMultipleSelectElement(element, ctx, wrapper, pathKey) {
|
|
|
1457
1494
|
const itemWrapper = document.createElement("div");
|
|
1458
1495
|
itemWrapper.className = "multiple-select-item flex items-center gap-2";
|
|
1459
1496
|
const selectInput = document.createElement("select");
|
|
1460
|
-
selectInput.className = "flex-1
|
|
1497
|
+
selectInput.className = "flex-1 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500";
|
|
1498
|
+
selectInput.style.cssText = `
|
|
1499
|
+
padding: var(--fb-input-padding-y) var(--fb-input-padding-x);
|
|
1500
|
+
font-size: var(--fb-font-size);
|
|
1501
|
+
font-family: var(--fb-font-family);
|
|
1502
|
+
`;
|
|
1461
1503
|
selectInput.disabled = readonly;
|
|
1462
1504
|
(element.options || []).forEach((option) => {
|
|
1463
1505
|
const optionElement = document.createElement("option");
|
|
@@ -2192,7 +2234,13 @@ function ensureFileStyles() {
|
|
|
2192
2234
|
style.textContent = `
|
|
2193
2235
|
@keyframes fb-spin { to { transform: rotate(360deg); } }
|
|
2194
2236
|
|
|
2195
|
-
/*
|
|
2237
|
+
/* \u2500\u2500\u2500 Checker background utility \u2500\u2500\u2500 */
|
|
2238
|
+
/* Neutral diagonal-stripe background for image previews (never crops) */
|
|
2239
|
+
.fb-checker {
|
|
2240
|
+
background-image: repeating-linear-gradient(45deg, #fafafa 0 6px, #f3f4f6 6px 12px);
|
|
2241
|
+
}
|
|
2242
|
+
|
|
2243
|
+
/* \u2500\u2500\u2500 Spinner \u2500\u2500\u2500 */
|
|
2196
2244
|
.fb-spinner {
|
|
2197
2245
|
width: 36px;
|
|
2198
2246
|
height: 36px;
|
|
@@ -2203,207 +2251,271 @@ function ensureFileStyles() {
|
|
|
2203
2251
|
flex-shrink: 0;
|
|
2204
2252
|
}
|
|
2205
2253
|
|
|
2206
|
-
/*
|
|
2207
|
-
.fb-tile {
|
|
2208
|
-
width:
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2254
|
+
/* \u2500\u2500\u2500 Wide single-file add tile (empty state) \u2500\u2500\u2500 */
|
|
2255
|
+
.fb-wide-tile {
|
|
2256
|
+
width: 100%;
|
|
2257
|
+
border-radius: 0.75rem;
|
|
2258
|
+
border: 1px dashed #60a5fa;
|
|
2259
|
+
background: rgba(239,246,255,0.5);
|
|
2260
|
+
display: flex;
|
|
2212
2261
|
overflow: hidden;
|
|
2213
|
-
|
|
2214
|
-
|
|
2262
|
+
height: 180px;
|
|
2263
|
+
transition: border-color 150ms, background 150ms, box-shadow 150ms;
|
|
2264
|
+
cursor: pointer;
|
|
2215
2265
|
}
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
.fb-tile-resource {
|
|
2219
|
-
border: 1px solid var(--fb-file-upload-border-color, #d1d5db);
|
|
2266
|
+
.fb-wide-tile:hover {
|
|
2267
|
+
background: #eff6ff;
|
|
2220
2268
|
}
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2269
|
+
.fb-wide-tile.fb-drag-over {
|
|
2270
|
+
border-color: #3b82f6;
|
|
2271
|
+
border-width: 2px;
|
|
2272
|
+
background: #eff6ff;
|
|
2273
|
+
box-shadow: 0 0 0 4px rgba(191,219,254,0.7);
|
|
2225
2274
|
}
|
|
2226
2275
|
|
|
2227
|
-
/*
|
|
2228
|
-
.fb-tile-
|
|
2229
|
-
|
|
2276
|
+
/* Upload zone inside wide tile */
|
|
2277
|
+
.fb-wide-tile-upload {
|
|
2278
|
+
flex: 1;
|
|
2230
2279
|
display: flex;
|
|
2280
|
+
flex-direction: column;
|
|
2231
2281
|
align-items: center;
|
|
2232
2282
|
justify-content: center;
|
|
2283
|
+
gap: 8px;
|
|
2284
|
+
color: #2563eb;
|
|
2285
|
+
padding: 16px;
|
|
2286
|
+
transition: background 150ms;
|
|
2233
2287
|
cursor: pointer;
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
border-color var(--fb-transition-duration, 200ms),
|
|
2238
|
-
color var(--fb-transition-duration, 200ms);
|
|
2288
|
+
background: transparent;
|
|
2289
|
+
border: none;
|
|
2290
|
+
font-family: inherit;
|
|
2239
2291
|
}
|
|
2240
|
-
.fb-tile-
|
|
2241
|
-
|
|
2242
|
-
color: var(--fb-text-color, #1f2937);
|
|
2292
|
+
.fb-wide-tile-upload:hover {
|
|
2293
|
+
background: rgba(191,219,254,0.25);
|
|
2243
2294
|
}
|
|
2244
2295
|
|
|
2245
|
-
/*
|
|
2246
|
-
.fb-tile-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
padding: 2px 6px;
|
|
2253
|
-
align-self: flex-end;
|
|
2254
|
-
margin-bottom: 4px;
|
|
2296
|
+
/* Vertical dashed divider between upload and library zones */
|
|
2297
|
+
.fb-wide-tile-divider {
|
|
2298
|
+
width: 1px;
|
|
2299
|
+
margin: 16px 0;
|
|
2300
|
+
border-left: 1px dashed rgba(96,165,250,0.5);
|
|
2301
|
+
background: transparent;
|
|
2302
|
+
flex-shrink: 0;
|
|
2255
2303
|
}
|
|
2256
2304
|
|
|
2257
|
-
/*
|
|
2258
|
-
.fb-
|
|
2259
|
-
width:
|
|
2260
|
-
|
|
2261
|
-
border: 2px dashed var(--fb-file-upload-border-color, #d1d5db);
|
|
2262
|
-
border-radius: var(--fb-border-radius, 0.5rem);
|
|
2305
|
+
/* Library zone inside wide tile */
|
|
2306
|
+
.fb-wide-tile-library {
|
|
2307
|
+
width: 176px;
|
|
2308
|
+
flex-shrink: 0;
|
|
2263
2309
|
display: flex;
|
|
2264
2310
|
flex-direction: column;
|
|
2265
2311
|
align-items: center;
|
|
2266
2312
|
justify-content: center;
|
|
2267
|
-
gap:
|
|
2313
|
+
gap: 8px;
|
|
2314
|
+
color: #2563eb;
|
|
2315
|
+
padding: 12px;
|
|
2316
|
+
transition: background 150ms;
|
|
2268
2317
|
cursor: pointer;
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2318
|
+
background: transparent;
|
|
2319
|
+
border: none;
|
|
2320
|
+
font-family: inherit;
|
|
2272
2321
|
}
|
|
2273
|
-
.fb-
|
|
2274
|
-
|
|
2275
|
-
background: var(--fb-background-hover-color, #f9fafb);
|
|
2322
|
+
.fb-wide-tile-library:hover {
|
|
2323
|
+
background: rgba(191,219,254,0.25);
|
|
2276
2324
|
}
|
|
2277
2325
|
|
|
2278
|
-
/*
|
|
2279
|
-
.fb-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2326
|
+
/* \u2500\u2500\u2500 Multi-file outer grid container \u2500\u2500\u2500 */
|
|
2327
|
+
.fb-multi-outer {
|
|
2328
|
+
border-radius: 0.75rem;
|
|
2329
|
+
border: 1px dashed #cbd5e1;
|
|
2330
|
+
background: rgba(248,250,252,0.4);
|
|
2331
|
+
padding: 12px;
|
|
2332
|
+
transition: border-color 150ms, background 150ms, box-shadow 150ms;
|
|
2333
|
+
}
|
|
2334
|
+
.fb-multi-outer.fb-drag-over {
|
|
2335
|
+
border-width: 2px;
|
|
2336
|
+
border-color: #3b82f6;
|
|
2337
|
+
background: rgba(239,246,255,0.4);
|
|
2338
|
+
box-shadow: 0 0 0 4px rgba(191,219,254,0.7);
|
|
2286
2339
|
}
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2340
|
+
|
|
2341
|
+
/* With files present: white solid border */
|
|
2342
|
+
.fb-multi-outer.fb-multi-has-files {
|
|
2343
|
+
border-style: solid;
|
|
2344
|
+
border-color: #e2e8f0;
|
|
2345
|
+
background: #fff;
|
|
2290
2346
|
}
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2347
|
+
|
|
2348
|
+
/* The CSS grid inside */
|
|
2349
|
+
.fb-multi-grid {
|
|
2350
|
+
display: grid;
|
|
2351
|
+
grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
|
|
2352
|
+
gap: 10px;
|
|
2295
2353
|
}
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2354
|
+
|
|
2355
|
+
/* \u2500\u2500\u2500 Multi square add-tile (combined upload + library) \u2500\u2500\u2500 */
|
|
2356
|
+
.fb-multi-add-tile {
|
|
2357
|
+
aspect-ratio: 1 / 1;
|
|
2358
|
+
border-radius: 0.5rem;
|
|
2359
|
+
border: 1px dashed #60a5fa;
|
|
2360
|
+
background: rgba(239,246,255,0.5);
|
|
2361
|
+
display: flex;
|
|
2362
|
+
flex-direction: column;
|
|
2363
|
+
overflow: hidden;
|
|
2364
|
+
transition: background 150ms;
|
|
2300
2365
|
}
|
|
2301
|
-
.fb-
|
|
2302
|
-
|
|
2303
|
-
color: var(--fb-text-secondary-color, #6b7280);
|
|
2366
|
+
.fb-multi-add-tile:hover {
|
|
2367
|
+
background: #eff6ff;
|
|
2304
2368
|
}
|
|
2305
|
-
.fb-
|
|
2306
|
-
|
|
2307
|
-
color:
|
|
2369
|
+
.fb-multi-add-tile.fb-drag-over-tile {
|
|
2370
|
+
border-width: 2px;
|
|
2371
|
+
border-color: #3b82f6;
|
|
2372
|
+
background: rgba(255,255,255,0.8);
|
|
2308
2373
|
}
|
|
2309
2374
|
|
|
2310
|
-
/*
|
|
2311
|
-
.fb-
|
|
2312
|
-
|
|
2313
|
-
inset: 0;
|
|
2314
|
-
background: transparent;
|
|
2315
|
-
transition: background var(--fb-transition-duration, 200ms);
|
|
2316
|
-
display: flex;
|
|
2317
|
-
align-items: flex-start;
|
|
2318
|
-
justify-content: flex-end;
|
|
2319
|
-
}
|
|
2320
|
-
.fb-tile-resource:hover .fb-tile-overlay {
|
|
2321
|
-
background: var(--fb-tile-hover-overlay-color, rgba(0,0,0,0.4));
|
|
2322
|
-
}
|
|
2323
|
-
.fb-tile-x-btn {
|
|
2324
|
-
margin: 3px;
|
|
2325
|
-
width: 18px;
|
|
2326
|
-
height: 18px;
|
|
2327
|
-
background: var(--fb-error-color, #ef4444);
|
|
2328
|
-
color: var(--fb-file-bg-color, #fff);
|
|
2329
|
-
border: none;
|
|
2330
|
-
border-radius: 50%;
|
|
2331
|
-
font-size: 11px;
|
|
2332
|
-
line-height: 1;
|
|
2333
|
-
cursor: pointer;
|
|
2375
|
+
/* Upload half of add-tile */
|
|
2376
|
+
.fb-multi-add-upload {
|
|
2377
|
+
flex: 1;
|
|
2334
2378
|
display: flex;
|
|
2379
|
+
flex-direction: column;
|
|
2335
2380
|
align-items: center;
|
|
2336
2381
|
justify-content: center;
|
|
2337
|
-
|
|
2338
|
-
|
|
2382
|
+
gap: 4px;
|
|
2383
|
+
color: #2563eb;
|
|
2384
|
+
cursor: pointer;
|
|
2385
|
+
background: transparent;
|
|
2386
|
+
border: none;
|
|
2387
|
+
font-family: inherit;
|
|
2388
|
+
width: 100%;
|
|
2389
|
+
transition: background 150ms;
|
|
2339
2390
|
}
|
|
2340
|
-
.fb-
|
|
2341
|
-
|
|
2391
|
+
.fb-multi-add-upload:hover {
|
|
2392
|
+
background: rgba(191,219,254,0.35);
|
|
2342
2393
|
}
|
|
2343
2394
|
|
|
2344
|
-
/*
|
|
2345
|
-
.fb-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
align-items: center;
|
|
2350
|
-
justify-content: center;
|
|
2351
|
-
background: var(--fb-tile-hover-overlay-color, rgba(0,0,0,0.25));
|
|
2395
|
+
/* Horizontal dashed divider inside add-tile */
|
|
2396
|
+
.fb-multi-add-divider {
|
|
2397
|
+
border-top: 1px dashed rgba(96,165,250,0.5);
|
|
2398
|
+
margin: 0;
|
|
2399
|
+
flex-shrink: 0;
|
|
2352
2400
|
}
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2401
|
+
|
|
2402
|
+
/* Library strip at bottom of add-tile */
|
|
2403
|
+
.fb-multi-add-library {
|
|
2404
|
+
padding: 6px 0;
|
|
2356
2405
|
display: flex;
|
|
2357
2406
|
align-items: center;
|
|
2358
2407
|
justify-content: center;
|
|
2408
|
+
gap: 4px;
|
|
2409
|
+
color: #2563eb;
|
|
2410
|
+
font-size: 11px;
|
|
2411
|
+
font-weight: 500;
|
|
2412
|
+
cursor: pointer;
|
|
2413
|
+
background: transparent;
|
|
2414
|
+
border: none;
|
|
2415
|
+
font-family: inherit;
|
|
2416
|
+
width: 100%;
|
|
2417
|
+
transition: background 150ms;
|
|
2418
|
+
flex-shrink: 0;
|
|
2419
|
+
}
|
|
2420
|
+
.fb-multi-add-library:hover {
|
|
2421
|
+
background: rgba(191,219,254,0.35);
|
|
2359
2422
|
}
|
|
2360
2423
|
|
|
2361
|
-
/*
|
|
2362
|
-
.fb-
|
|
2424
|
+
/* \u2500\u2500\u2500 Capacity placeholder squares \u2500\u2500\u2500 */
|
|
2425
|
+
.fb-multi-placeholder {
|
|
2426
|
+
aspect-ratio: 1 / 1;
|
|
2427
|
+
border-radius: 0.5rem;
|
|
2428
|
+
border: 1px solid #e2e8f0;
|
|
2429
|
+
}
|
|
2430
|
+
.fb-multi-placeholder.fb-drag-over {
|
|
2431
|
+
border-width: 2px;
|
|
2432
|
+
border-style: dashed;
|
|
2433
|
+
border-color: #93c5fd;
|
|
2434
|
+
background: rgba(219,234,254,0.6);
|
|
2435
|
+
}
|
|
2436
|
+
|
|
2437
|
+
/* \u2500\u2500\u2500 Filled preview tile \u2500\u2500\u2500 */
|
|
2438
|
+
.fb-preview-tile {
|
|
2439
|
+
aspect-ratio: 1 / 1;
|
|
2440
|
+
border-radius: 0.5rem;
|
|
2441
|
+
border: 1px solid #e2e8f0;
|
|
2442
|
+
overflow: hidden;
|
|
2363
2443
|
position: relative;
|
|
2444
|
+
cursor: pointer;
|
|
2445
|
+
}
|
|
2446
|
+
.fb-preview-tile img {
|
|
2364
2447
|
width: 100%;
|
|
2365
2448
|
height: 100%;
|
|
2449
|
+
object-fit: contain;
|
|
2450
|
+
display: block;
|
|
2366
2451
|
}
|
|
2367
2452
|
|
|
2368
|
-
/*
|
|
2369
|
-
.fb-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
z-index: 10;
|
|
2453
|
+
/* \u2500\u2500\u2500 Uploading placeholder tile \u2500\u2500\u2500 */
|
|
2454
|
+
.fb-uploading-tile {
|
|
2455
|
+
aspect-ratio: 1 / 1;
|
|
2456
|
+
border-radius: 0.5rem;
|
|
2457
|
+
border: 2px dashed #d1d5db;
|
|
2374
2458
|
display: flex;
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2459
|
+
flex-direction: column;
|
|
2460
|
+
align-items: center;
|
|
2461
|
+
justify-content: center;
|
|
2462
|
+
gap: 6px;
|
|
2463
|
+
padding: 6px;
|
|
2379
2464
|
}
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2465
|
+
|
|
2466
|
+
/* \u2500\u2500\u2500 Meta line below multi grid \u2500\u2500\u2500 */
|
|
2467
|
+
.fb-meta-line {
|
|
2468
|
+
margin-top: 10px;
|
|
2469
|
+
display: flex;
|
|
2470
|
+
align-items: center;
|
|
2471
|
+
justify-content: space-between;
|
|
2472
|
+
gap: 8px;
|
|
2473
|
+
flex-wrap: wrap;
|
|
2383
2474
|
}
|
|
2384
|
-
.fb-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
line-height: 1.2;
|
|
2475
|
+
.fb-meta-text {
|
|
2476
|
+
font-size: 12px;
|
|
2477
|
+
color: #94a3b8;
|
|
2478
|
+
display: flex;
|
|
2479
|
+
align-items: center;
|
|
2480
|
+
gap: 8px;
|
|
2481
|
+
flex-wrap: wrap;
|
|
2392
2482
|
}
|
|
2393
|
-
.fb-
|
|
2394
|
-
|
|
2483
|
+
.fb-meta-dot {
|
|
2484
|
+
width: 4px;
|
|
2485
|
+
height: 4px;
|
|
2486
|
+
border-radius: 50%;
|
|
2487
|
+
background: #cbd5e1;
|
|
2488
|
+
flex-shrink: 0;
|
|
2395
2489
|
}
|
|
2396
|
-
.fb-
|
|
2397
|
-
|
|
2490
|
+
.fb-meta-mono {
|
|
2491
|
+
font-family: ui-monospace, 'JetBrains Mono', monospace;
|
|
2492
|
+
font-size: 11px;
|
|
2493
|
+
letter-spacing: -0.02em;
|
|
2398
2494
|
}
|
|
2399
|
-
.fb-
|
|
2400
|
-
|
|
2495
|
+
.fb-clear-all-btn {
|
|
2496
|
+
font-size: 12px;
|
|
2497
|
+
color: #94a3b8;
|
|
2498
|
+
background: none;
|
|
2499
|
+
border: none;
|
|
2500
|
+
cursor: pointer;
|
|
2501
|
+
padding: 0;
|
|
2502
|
+
font-family: inherit;
|
|
2503
|
+
transition: color 150ms;
|
|
2504
|
+
white-space: nowrap;
|
|
2505
|
+
flex-shrink: 0;
|
|
2401
2506
|
}
|
|
2402
|
-
.fb-
|
|
2403
|
-
|
|
2507
|
+
.fb-clear-all-btn:hover {
|
|
2508
|
+
color: #dc2626;
|
|
2404
2509
|
}
|
|
2405
2510
|
|
|
2406
|
-
/*
|
|
2511
|
+
/* \u2500\u2500\u2500 Empty text (readonly) \u2500\u2500\u2500 */
|
|
2512
|
+
.fb-tile-empty-text {
|
|
2513
|
+
font-size: 11px;
|
|
2514
|
+
color: var(--fb-text-secondary-color, #6b7280);
|
|
2515
|
+
padding: 4px 0;
|
|
2516
|
+
}
|
|
2517
|
+
|
|
2518
|
+
/* \u2500\u2500\u2500 Tile action buttons (for zoom popup, compat) \u2500\u2500\u2500 */
|
|
2407
2519
|
.fb-tile-actions {
|
|
2408
2520
|
position: absolute;
|
|
2409
2521
|
top: 3px;
|
|
@@ -2415,37 +2527,35 @@ function ensureFileStyles() {
|
|
|
2415
2527
|
transition: opacity var(--fb-transition-duration, 200ms);
|
|
2416
2528
|
z-index: 10;
|
|
2417
2529
|
}
|
|
2418
|
-
.fb-tile
|
|
2530
|
+
.fb-preview-tile:hover .fb-tile-actions {
|
|
2419
2531
|
opacity: 1;
|
|
2420
2532
|
}
|
|
2421
2533
|
.fb-tile-action-btn {
|
|
2422
|
-
width:
|
|
2423
|
-
height:
|
|
2534
|
+
width: 24px;
|
|
2535
|
+
height: 24px;
|
|
2424
2536
|
display: flex;
|
|
2425
2537
|
align-items: center;
|
|
2426
2538
|
justify-content: center;
|
|
2427
|
-
border:
|
|
2428
|
-
border-radius:
|
|
2539
|
+
border: 1px solid rgba(15,23,42,0.08);
|
|
2540
|
+
border-radius: 0.375rem;
|
|
2429
2541
|
cursor: pointer;
|
|
2430
|
-
background: rgba(
|
|
2431
|
-
color: #
|
|
2542
|
+
background: rgba(255,255,255,0.92);
|
|
2543
|
+
color: #374151;
|
|
2432
2544
|
padding: 0;
|
|
2433
2545
|
flex-shrink: 0;
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2546
|
+
box-shadow: 0 1px 2px rgba(0,0,0,0.06);
|
|
2547
|
+
transition: background var(--fb-transition-duration, 200ms),
|
|
2548
|
+
color var(--fb-transition-duration, 200ms);
|
|
2437
2549
|
}
|
|
2438
2550
|
.fb-tile-action-btn:hover {
|
|
2439
|
-
background:
|
|
2440
|
-
|
|
2441
|
-
.fb-tile-action-remove {
|
|
2442
|
-
background: rgba(220, 38, 38, 0.8);
|
|
2551
|
+
background: #ffffff;
|
|
2552
|
+
color: #0f172a;
|
|
2443
2553
|
}
|
|
2444
2554
|
.fb-tile-action-remove:hover {
|
|
2445
|
-
|
|
2555
|
+
color: #dc2626;
|
|
2446
2556
|
}
|
|
2447
2557
|
|
|
2448
|
-
/*
|
|
2558
|
+
/* Zoom popup action buttons always visible */
|
|
2449
2559
|
.fb-tile-zoom-preview .fb-tile-actions {
|
|
2450
2560
|
position: absolute;
|
|
2451
2561
|
top: 6px;
|
|
@@ -2454,116 +2564,145 @@ function ensureFileStyles() {
|
|
|
2454
2564
|
z-index: 10000;
|
|
2455
2565
|
}
|
|
2456
2566
|
|
|
2457
|
-
/*
|
|
2458
|
-
.fb-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2567
|
+
/* \u2500\u2500\u2500 Hover zoom preview popup \u2500\u2500\u2500 */
|
|
2568
|
+
.fb-tile-zoom-preview {
|
|
2569
|
+
position: fixed;
|
|
2570
|
+
z-index: 9999;
|
|
2571
|
+
background: var(--fb-background-color, #fff);
|
|
2572
|
+
border: 1px solid #e2e8f0;
|
|
2573
|
+
border-radius: 0.5rem;
|
|
2574
|
+
box-shadow: 0 4px 16px rgba(0,0,0,0.18);
|
|
2575
|
+
padding: 4px;
|
|
2576
|
+
width: 350px;
|
|
2577
|
+
height: 350px;
|
|
2578
|
+
pointer-events: none;
|
|
2579
|
+
opacity: 0;
|
|
2580
|
+
transition: opacity 150ms ease;
|
|
2462
2581
|
}
|
|
2463
|
-
.fb-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2582
|
+
.fb-tile-zoom-preview.fb-tile-zoom-preview--visible {
|
|
2583
|
+
opacity: 1;
|
|
2584
|
+
}
|
|
2585
|
+
.fb-tile-zoom-preview-img {
|
|
2586
|
+
width: 100%;
|
|
2587
|
+
height: 100%;
|
|
2588
|
+
object-fit: contain;
|
|
2589
|
+
display: block;
|
|
2590
|
+
border-radius: calc(0.5rem - 2px);
|
|
2467
2591
|
}
|
|
2468
2592
|
|
|
2469
|
-
/*
|
|
2470
|
-
.fb-
|
|
2471
|
-
height:
|
|
2472
|
-
border:
|
|
2473
|
-
border
|
|
2593
|
+
/* \u2500\u2500\u2500 Single-file uploading state \u2500\u2500\u2500 */
|
|
2594
|
+
.fb-single-uploading {
|
|
2595
|
+
height: 180px;
|
|
2596
|
+
border-radius: 0.75rem;
|
|
2597
|
+
border: 1px dashed #60a5fa;
|
|
2598
|
+
background: rgba(239,246,255,0.5);
|
|
2474
2599
|
display: flex;
|
|
2475
2600
|
flex-direction: column;
|
|
2476
2601
|
align-items: center;
|
|
2477
2602
|
justify-content: center;
|
|
2478
|
-
gap:
|
|
2479
|
-
cursor: pointer;
|
|
2480
|
-
background: none;
|
|
2481
|
-
padding: 0;
|
|
2482
|
-
transition:
|
|
2483
|
-
border-color var(--fb-transition-duration, 200ms),
|
|
2484
|
-
background var(--fb-transition-duration, 200ms);
|
|
2485
|
-
width: 100%;
|
|
2603
|
+
gap: 8px;
|
|
2486
2604
|
}
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2605
|
+
|
|
2606
|
+
/* \u2500\u2500\u2500 Video overlays \u2500\u2500\u2500 */
|
|
2607
|
+
.fb-video-overlay {
|
|
2608
|
+
position: absolute;
|
|
2609
|
+
inset: 0;
|
|
2610
|
+
display: flex;
|
|
2611
|
+
align-items: center;
|
|
2612
|
+
justify-content: center;
|
|
2613
|
+
background: rgba(0,0,0,0.25);
|
|
2492
2614
|
}
|
|
2493
|
-
.fb-
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2615
|
+
.fb-play-btn {
|
|
2616
|
+
background: rgba(255,255,255,0.9);
|
|
2617
|
+
border-radius: 50%;
|
|
2618
|
+
display: flex;
|
|
2619
|
+
align-items: center;
|
|
2620
|
+
justify-content: center;
|
|
2497
2621
|
}
|
|
2498
|
-
.fb-
|
|
2499
|
-
|
|
2500
|
-
|
|
2622
|
+
.fb-video-preview-wrap {
|
|
2623
|
+
position: relative;
|
|
2624
|
+
width: 100%;
|
|
2625
|
+
height: 100%;
|
|
2626
|
+
}
|
|
2627
|
+
.fb-video-btn-overlay {
|
|
2628
|
+
position: absolute;
|
|
2629
|
+
top: 8px;
|
|
2630
|
+
right: 8px;
|
|
2631
|
+
z-index: 10;
|
|
2632
|
+
display: flex;
|
|
2633
|
+
gap: 4px;
|
|
2634
|
+
opacity: 0;
|
|
2635
|
+
transition: opacity 150ms;
|
|
2636
|
+
pointer-events: none;
|
|
2637
|
+
}
|
|
2638
|
+
.fb-video-preview-wrap:hover .fb-video-btn-overlay {
|
|
2639
|
+
opacity: 1;
|
|
2640
|
+
pointer-events: auto;
|
|
2501
2641
|
}
|
|
2502
|
-
.fb-
|
|
2642
|
+
.fb-video-btn {
|
|
2643
|
+
border: none;
|
|
2644
|
+
border-radius: 4px;
|
|
2503
2645
|
font-size: 11px;
|
|
2504
|
-
|
|
2646
|
+
padding: 4px 8px;
|
|
2647
|
+
cursor: pointer;
|
|
2648
|
+
color: #fff;
|
|
2649
|
+
line-height: 1.2;
|
|
2505
2650
|
}
|
|
2651
|
+
.fb-video-btn-delete { background: rgba(220,38,38,0.85); }
|
|
2652
|
+
.fb-video-btn-delete:hover { background: rgba(185,28,28,0.95); }
|
|
2653
|
+
.fb-video-btn-change { background: rgba(31,41,55,0.85); }
|
|
2654
|
+
.fb-video-btn-change:hover { background: rgba(17,24,39,0.95); }
|
|
2506
2655
|
|
|
2507
|
-
/*
|
|
2508
|
-
.fb-tile
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
justify-content: center;
|
|
2513
|
-
cursor: pointer;
|
|
2514
|
-
font-size: 24px;
|
|
2515
|
-
color: var(--fb-file-upload-text-color, #9ca3af);
|
|
2516
|
-
transition:
|
|
2517
|
-
border-color var(--fb-transition-duration, 200ms),
|
|
2518
|
-
color var(--fb-transition-duration, 200ms);
|
|
2519
|
-
background: none;
|
|
2520
|
-
padding: 0;
|
|
2521
|
-
width: var(--fb-tile-size, 160px);
|
|
2522
|
-
height: var(--fb-tile-size, 160px);
|
|
2523
|
-
flex-shrink: 0;
|
|
2524
|
-
position: relative;
|
|
2656
|
+
/* \u2500\u2500\u2500 Readonly readonly tile \u2500\u2500\u2500 */
|
|
2657
|
+
.fb-readonly-tile {
|
|
2658
|
+
aspect-ratio: 1 / 1;
|
|
2659
|
+
border-radius: 0.5rem;
|
|
2660
|
+
border: 1px solid #e2e8f0;
|
|
2525
2661
|
overflow: hidden;
|
|
2526
|
-
|
|
2662
|
+
position: relative;
|
|
2663
|
+
cursor: pointer;
|
|
2527
2664
|
}
|
|
2528
|
-
.fb-tile
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2665
|
+
.fb-readonly-tile img {
|
|
2666
|
+
width: 100%;
|
|
2667
|
+
height: 100%;
|
|
2668
|
+
object-fit: contain;
|
|
2669
|
+
display: block;
|
|
2533
2670
|
}
|
|
2534
|
-
|
|
2535
|
-
/* Hover zoom preview popup for image tiles \u2014 appended to document.body (fixed) */
|
|
2536
|
-
.fb-tile-zoom-preview {
|
|
2537
|
-
position: fixed;
|
|
2538
|
-
z-index: 9999;
|
|
2539
|
-
background: var(--fb-background-color, #fff);
|
|
2540
|
-
border: 1px solid var(--fb-file-upload-border-color, #d1d5db);
|
|
2541
|
-
border-radius: var(--fb-border-radius, 0.5rem);
|
|
2542
|
-
box-shadow: 0 4px 16px rgba(0,0,0,0.18);
|
|
2543
|
-
padding: 4px;
|
|
2544
|
-
width: 350px;
|
|
2545
|
-
height: 350px;
|
|
2546
|
-
pointer-events: none;
|
|
2671
|
+
.fb-readonly-tile .fb-tile-actions {
|
|
2547
2672
|
opacity: 0;
|
|
2548
|
-
transition: opacity 150ms ease;
|
|
2549
2673
|
}
|
|
2550
|
-
.fb-tile
|
|
2674
|
+
.fb-readonly-tile:hover .fb-tile-actions {
|
|
2551
2675
|
opacity: 1;
|
|
2552
2676
|
}
|
|
2553
|
-
|
|
2677
|
+
|
|
2678
|
+
/* \u2500\u2500\u2500 Readonly single-file filled \u2500\u2500\u2500 */
|
|
2679
|
+
.fb-single-readonly-filled {
|
|
2680
|
+
position: relative;
|
|
2681
|
+
border-radius: 0.75rem;
|
|
2682
|
+
border: 1px solid #e2e8f0;
|
|
2683
|
+
overflow: hidden;
|
|
2684
|
+
height: 220px;
|
|
2685
|
+
display: block;
|
|
2686
|
+
cursor: pointer;
|
|
2687
|
+
}
|
|
2688
|
+
.fb-single-readonly-filled img {
|
|
2554
2689
|
width: 100%;
|
|
2555
2690
|
height: 100%;
|
|
2556
2691
|
object-fit: contain;
|
|
2557
2692
|
display: block;
|
|
2558
|
-
|
|
2559
|
-
|
|
2693
|
+
}
|
|
2694
|
+
|
|
2695
|
+
/* \u2500\u2500\u2500 Readonly multi grid \u2500\u2500\u2500 */
|
|
2696
|
+
.fb-multi-readonly-grid {
|
|
2697
|
+
display: grid;
|
|
2698
|
+
grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
|
|
2699
|
+
gap: 10px;
|
|
2560
2700
|
}
|
|
2561
2701
|
`;
|
|
2562
2702
|
document.head.appendChild(style);
|
|
2563
2703
|
}
|
|
2564
2704
|
|
|
2565
2705
|
// src/components/file/dom.ts
|
|
2566
|
-
var TILE_SIZE = "160px";
|
|
2567
2706
|
function createFileTile() {
|
|
2568
2707
|
ensureFileStyles();
|
|
2569
2708
|
const tile = document.createElement("div");
|
|
@@ -2571,7 +2710,7 @@ function createFileTile() {
|
|
|
2571
2710
|
return tile;
|
|
2572
2711
|
}
|
|
2573
2712
|
function showFileError(container, message) {
|
|
2574
|
-
const existing = container.closest("
|
|
2713
|
+
const existing = container.closest("[data-files-wrapper]")?.querySelector(".file-error-message");
|
|
2575
2714
|
if (existing) existing.remove();
|
|
2576
2715
|
const errorEl = document.createElement("div");
|
|
2577
2716
|
errorEl.className = "file-error-message error-message";
|
|
@@ -2581,10 +2720,10 @@ function showFileError(container, message) {
|
|
|
2581
2720
|
margin-top: 0.25rem;
|
|
2582
2721
|
`;
|
|
2583
2722
|
errorEl.textContent = message;
|
|
2584
|
-
container.closest("
|
|
2723
|
+
container.closest("[data-files-wrapper]")?.appendChild(errorEl);
|
|
2585
2724
|
}
|
|
2586
2725
|
function clearFileError(container) {
|
|
2587
|
-
const existing = container.closest("
|
|
2726
|
+
const existing = container.closest("[data-files-wrapper]")?.querySelector(".file-error-message");
|
|
2588
2727
|
if (existing) existing.remove();
|
|
2589
2728
|
}
|
|
2590
2729
|
function addDeleteButton(container, state, onDelete) {
|
|
@@ -2602,13 +2741,6 @@ function addDeleteButton(container, state, onDelete) {
|
|
|
2602
2741
|
overlay.appendChild(deleteBtn);
|
|
2603
2742
|
container.appendChild(overlay);
|
|
2604
2743
|
}
|
|
2605
|
-
function findFilePicker(container) {
|
|
2606
|
-
let el = container.parentElement;
|
|
2607
|
-
while (el && !el.dataset.filesWrapper) {
|
|
2608
|
-
el = el.parentElement;
|
|
2609
|
-
}
|
|
2610
|
-
return el?.querySelector('input[type="file"]') ?? null;
|
|
2611
|
-
}
|
|
2612
2744
|
function createUploadingTile(fileName, state) {
|
|
2613
2745
|
ensureFileStyles();
|
|
2614
2746
|
const tile = createFileTile();
|
|
@@ -2624,10 +2756,13 @@ function createUploadingTile(fileName, state) {
|
|
|
2624
2756
|
return tile;
|
|
2625
2757
|
}
|
|
2626
2758
|
function ensureTilesWrap(list) {
|
|
2759
|
+
const existingGrid = list.querySelector(".fb-multi-grid");
|
|
2760
|
+
if (existingGrid) return existingGrid;
|
|
2627
2761
|
const existing = list.querySelector(".fb-tiles-wrap");
|
|
2628
2762
|
if (existing) return existing;
|
|
2629
|
-
|
|
2630
|
-
|
|
2763
|
+
list.querySelector(".fb-file-dropzone")?.remove();
|
|
2764
|
+
list.querySelector(".fb-wide-tile")?.remove();
|
|
2765
|
+
list.querySelector(".fb-multi-outer")?.remove();
|
|
2631
2766
|
const tilesWrap = document.createElement("div");
|
|
2632
2767
|
tilesWrap.className = "fb-tiles-wrap";
|
|
2633
2768
|
tilesWrap.style.cssText = "display:flex;flex-wrap:wrap;gap:6px;align-items:flex-start;";
|
|
@@ -2639,7 +2774,7 @@ function ensureTilesWrap(list) {
|
|
|
2639
2774
|
return tilesWrap;
|
|
2640
2775
|
}
|
|
2641
2776
|
function setEmptyFileContainer(fileContainer, state, hint) {
|
|
2642
|
-
const hintHtml =
|
|
2777
|
+
const hintHtml = "";
|
|
2643
2778
|
fileContainer.innerHTML = `
|
|
2644
2779
|
<div class="flex flex-col items-center justify-center h-full text-gray-400">
|
|
2645
2780
|
<svg class="w-6 h-6 mb-2" fill="currentColor" viewBox="0 0 24 24">
|
|
@@ -2681,9 +2816,11 @@ function setupDragAndDrop(element, dropHandler) {
|
|
|
2681
2816
|
}
|
|
2682
2817
|
|
|
2683
2818
|
// src/components/file/preview.ts
|
|
2684
|
-
var ICON_DOWNLOAD = `<svg width="
|
|
2685
|
-
var ICON_OPEN = `<svg width="
|
|
2686
|
-
var ICON_REMOVE = `<svg width="
|
|
2819
|
+
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>`;
|
|
2820
|
+
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>`;
|
|
2821
|
+
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>`;
|
|
2822
|
+
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>`;
|
|
2823
|
+
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>`;
|
|
2687
2824
|
function canDownload(state, meta) {
|
|
2688
2825
|
return Boolean(
|
|
2689
2826
|
state.config.downloadFile || state.config.getDownloadUrl || state.config.getThumbnail || meta?.file
|
|
@@ -2695,7 +2832,16 @@ function canOpenInTab(state, meta) {
|
|
|
2695
2832
|
);
|
|
2696
2833
|
}
|
|
2697
2834
|
function createTileActions(options) {
|
|
2698
|
-
const {
|
|
2835
|
+
const {
|
|
2836
|
+
canRemove,
|
|
2837
|
+
removeHandler,
|
|
2838
|
+
state,
|
|
2839
|
+
resourceId,
|
|
2840
|
+
fileName,
|
|
2841
|
+
meta,
|
|
2842
|
+
replaceHandler,
|
|
2843
|
+
libraryHandler
|
|
2844
|
+
} = options;
|
|
2699
2845
|
const group = document.createElement("div");
|
|
2700
2846
|
group.className = "fb-tile-actions";
|
|
2701
2847
|
const makeBtn = (icon, label, cls) => {
|
|
@@ -2710,6 +2856,16 @@ function createTileActions(options) {
|
|
|
2710
2856
|
});
|
|
2711
2857
|
return btn;
|
|
2712
2858
|
};
|
|
2859
|
+
if (replaceHandler) {
|
|
2860
|
+
const replaceBtn = makeBtn(ICON_REPLACE, t("replaceFile", state), "fb-tile-action-replace");
|
|
2861
|
+
replaceBtn.addEventListener("click", () => replaceHandler());
|
|
2862
|
+
group.appendChild(replaceBtn);
|
|
2863
|
+
}
|
|
2864
|
+
if (libraryHandler) {
|
|
2865
|
+
const libBtn = makeBtn(ICON_LIBRARY, t("fromLibrary", state), "fb-tile-action-library");
|
|
2866
|
+
libBtn.addEventListener("click", () => libraryHandler());
|
|
2867
|
+
group.appendChild(libBtn);
|
|
2868
|
+
}
|
|
2713
2869
|
if (canDownload(state, meta)) {
|
|
2714
2870
|
const dlBtn = makeBtn(ICON_DOWNLOAD, t("downloadFile", state), "fb-tile-action-download");
|
|
2715
2871
|
dlBtn.addEventListener("click", () => {
|
|
@@ -2895,8 +3051,7 @@ function attachClonedActionListeners(cloned, original) {
|
|
|
2895
3051
|
}
|
|
2896
3052
|
function renderLocalImagePreview(container, file, fileName, state) {
|
|
2897
3053
|
const img = document.createElement("img");
|
|
2898
|
-
img.
|
|
2899
|
-
img.style.background = "var(--fb-file-upload-bg-color,#f3f4f6)";
|
|
3054
|
+
img.style.cssText = "width:100%;height:100%;object-fit:contain;background:var(--fb-file-upload-bg-color,#f3f4f6);";
|
|
2900
3055
|
img.alt = fileName || t("previewAlt", state);
|
|
2901
3056
|
const reader = new FileReader();
|
|
2902
3057
|
reader.onload = (e) => {
|
|
@@ -2918,7 +3073,7 @@ function renderLocalVideoPreview(container, file, videoType, resourceId, state,
|
|
|
2918
3073
|
const newContainer = setupDragDropless(container);
|
|
2919
3074
|
newContainer.innerHTML = `
|
|
2920
3075
|
<div class="fb-video-preview-wrap">
|
|
2921
|
-
<video
|
|
3076
|
+
<video style="width:100%;height:100%;object-fit:contain;" controls preload="auto" muted src="${videoUrl}">
|
|
2922
3077
|
${escapeHtml(t("videoNotSupported", state))}
|
|
2923
3078
|
</video>
|
|
2924
3079
|
<div class="fb-video-btn-overlay">
|
|
@@ -2962,11 +3117,11 @@ function handleVideoDelete(container, resourceId, state, deps) {
|
|
|
2962
3117
|
container.onclick = deps.fileUploadHandler;
|
|
2963
3118
|
}
|
|
2964
3119
|
container.innerHTML = `
|
|
2965
|
-
<div
|
|
2966
|
-
<svg
|
|
3120
|
+
<div style="display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;color:var(--fb-text-secondary-color,#9ca3af);">
|
|
3121
|
+
<svg style="width:1.5rem;height:1.5rem;margin-bottom:0.5rem;" fill="currentColor" viewBox="0 0 24 24">
|
|
2967
3122
|
<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"/>
|
|
2968
3123
|
</svg>
|
|
2969
|
-
<div
|
|
3124
|
+
<div style="font-size:0.875rem;text-align:center;">${escapeHtml(t("clickDragText", state))}</div>
|
|
2970
3125
|
</div>
|
|
2971
3126
|
`;
|
|
2972
3127
|
if (deps?.setupDrop) {
|
|
@@ -2983,11 +3138,11 @@ function renderDeleteButton(container, resourceId, state) {
|
|
|
2983
3138
|
hiddenInput.value = "";
|
|
2984
3139
|
}
|
|
2985
3140
|
container.innerHTML = `
|
|
2986
|
-
<div
|
|
2987
|
-
<svg
|
|
3141
|
+
<div style="display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;color:var(--fb-text-secondary-color,#9ca3af);">
|
|
3142
|
+
<svg style="width:1.5rem;height:1.5rem;margin-bottom:0.5rem;" fill="currentColor" viewBox="0 0 24 24">
|
|
2988
3143
|
<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"/>
|
|
2989
3144
|
</svg>
|
|
2990
|
-
<div
|
|
3145
|
+
<div style="font-size:0.875rem;text-align:center;">${escapeHtml(t("clickDragText", state))}</div>
|
|
2991
3146
|
</div>
|
|
2992
3147
|
`;
|
|
2993
3148
|
});
|
|
@@ -3006,7 +3161,7 @@ async function renderLocalFilePreview(container, meta, fileName, resourceId, isR
|
|
|
3006
3161
|
deps
|
|
3007
3162
|
);
|
|
3008
3163
|
} else {
|
|
3009
|
-
container.innerHTML = `<div
|
|
3164
|
+
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>`;
|
|
3010
3165
|
}
|
|
3011
3166
|
if (!isReadonly && !meta.type?.startsWith("video/")) {
|
|
3012
3167
|
renderDeleteButton(container, resourceId, state);
|
|
@@ -3014,7 +3169,7 @@ async function renderLocalFilePreview(container, meta, fileName, resourceId, isR
|
|
|
3014
3169
|
}
|
|
3015
3170
|
function renderUploadedVideoPreview(container, thumbnailUrl, state) {
|
|
3016
3171
|
const video = document.createElement("video");
|
|
3017
|
-
video.
|
|
3172
|
+
video.style.cssText = "width:100%;height:100%;object-fit:contain;";
|
|
3018
3173
|
video.controls = true;
|
|
3019
3174
|
video.preload = "metadata";
|
|
3020
3175
|
video.muted = true;
|
|
@@ -3035,8 +3190,7 @@ async function renderUploadedFilePreview(container, resourceId, fileName, meta,
|
|
|
3035
3190
|
renderUploadedVideoPreview(container, thumbnailUrl, state);
|
|
3036
3191
|
} else {
|
|
3037
3192
|
const img = document.createElement("img");
|
|
3038
|
-
img.
|
|
3039
|
-
img.style.background = "var(--fb-file-upload-bg-color,#f3f4f6)";
|
|
3193
|
+
img.style.cssText = "width:100%;height:100%;object-fit:contain;background:var(--fb-file-upload-bg-color,#f3f4f6);";
|
|
3040
3194
|
img.alt = fileName || t("previewAlt", state);
|
|
3041
3195
|
img.src = thumbnailUrl;
|
|
3042
3196
|
container.appendChild(img);
|
|
@@ -3047,11 +3201,11 @@ async function renderUploadedFilePreview(container, resourceId, fileName, meta,
|
|
|
3047
3201
|
} catch (error) {
|
|
3048
3202
|
console.error("Failed to get thumbnail:", error);
|
|
3049
3203
|
container.innerHTML = `
|
|
3050
|
-
<div
|
|
3051
|
-
<svg
|
|
3204
|
+
<div style="display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;color:var(--fb-text-secondary-color,#9ca3af);">
|
|
3205
|
+
<svg style="width:1.5rem;height:1.5rem;margin-bottom:0.5rem;" fill="currentColor" viewBox="0 0 24 24">
|
|
3052
3206
|
<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"/>
|
|
3053
3207
|
</svg>
|
|
3054
|
-
<div
|
|
3208
|
+
<div style="font-size:0.875rem;text-align:center;">${escapeHtml(fileName || t("previewUnavailable", state))}</div>
|
|
3055
3209
|
</div>
|
|
3056
3210
|
`;
|
|
3057
3211
|
}
|
|
@@ -3225,13 +3379,9 @@ async function fillTileContent(tile, rid, meta, state, actionsEl) {
|
|
|
3225
3379
|
const img = document.createElement("img");
|
|
3226
3380
|
img.style.cssText = "width:100%;height:100%;object-fit:contain;background:var(--fb-file-upload-bg-color,#f3f4f6);";
|
|
3227
3381
|
img.alt = meta.name;
|
|
3228
|
-
|
|
3229
|
-
reader.onload = (e) => {
|
|
3230
|
-
img.src = e.target?.result || "";
|
|
3231
|
-
attachZoomHover(tile, img.src, meta.name, actionsEl ?? null);
|
|
3232
|
-
};
|
|
3233
|
-
reader.readAsDataURL(meta.file);
|
|
3382
|
+
img.src = getLocalFileUrl(meta.file);
|
|
3234
3383
|
tile.appendChild(img);
|
|
3384
|
+
attachZoomHover(tile, img.src, meta.name, actionsEl ?? null);
|
|
3235
3385
|
} else if (state.config.getThumbnail) {
|
|
3236
3386
|
try {
|
|
3237
3387
|
const url = await state.config.getThumbnail(rid);
|
|
@@ -3288,17 +3438,20 @@ async function fillTileContent(tile, rid, meta, state, actionsEl) {
|
|
|
3288
3438
|
}
|
|
3289
3439
|
if (actionsEl) tile.appendChild(actionsEl);
|
|
3290
3440
|
} else {
|
|
3291
|
-
|
|
3292
|
-
const hasExtension = name.includes(".");
|
|
3293
|
-
const captionHtml = hasExtension ? `<div class="fb-tile-label">${escapeHtml(name.length > 10 ? name.substring(0, 8) + "\u2026" : name)}</div>` : "";
|
|
3294
|
-
tile.innerHTML = `
|
|
3295
|
-
<div style="display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;padding:6px;gap:4px;">
|
|
3296
|
-
<div style="font-size:36px;">\u{1F4C1}</div>
|
|
3297
|
-
${captionHtml}
|
|
3298
|
-
</div>`;
|
|
3299
|
-
if (actionsEl) tile.appendChild(actionsEl);
|
|
3441
|
+
fillDocumentFallback(tile, rid, meta, actionsEl);
|
|
3300
3442
|
}
|
|
3301
3443
|
}
|
|
3444
|
+
function fillDocumentFallback(tile, rid, meta, actionsEl) {
|
|
3445
|
+
const fileName = meta?.name ?? rid.split("/").pop() ?? "";
|
|
3446
|
+
if (fileName) tile.title = fileName;
|
|
3447
|
+
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>` : "";
|
|
3448
|
+
tile.innerHTML = `
|
|
3449
|
+
<div style="display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;padding:6px;gap:4px;">
|
|
3450
|
+
<div style="font-size:36px;">\u{1F4C1}</div>
|
|
3451
|
+
${labelHtml}
|
|
3452
|
+
</div>`;
|
|
3453
|
+
if (actionsEl) tile.appendChild(actionsEl);
|
|
3454
|
+
}
|
|
3302
3455
|
async function forceDownload(resourceId, fileName, state) {
|
|
3303
3456
|
try {
|
|
3304
3457
|
let fileUrl = null;
|
|
@@ -3398,6 +3551,10 @@ async function handleFileSelect(opts) {
|
|
|
3398
3551
|
return;
|
|
3399
3552
|
}
|
|
3400
3553
|
clearFileError(container);
|
|
3554
|
+
const existingHiddenInput = container.parentElement?.querySelector(
|
|
3555
|
+
'input[type="hidden"]'
|
|
3556
|
+
);
|
|
3557
|
+
const previousRid = existingHiddenInput?.value || null;
|
|
3401
3558
|
ensureFileStyles();
|
|
3402
3559
|
container.innerHTML = `
|
|
3403
3560
|
<div style="display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;gap:6px;padding:6px;">
|
|
@@ -3408,7 +3565,13 @@ async function handleFileSelect(opts) {
|
|
|
3408
3565
|
try {
|
|
3409
3566
|
rid = await uploadSingleFile(file, state);
|
|
3410
3567
|
} catch (error) {
|
|
3411
|
-
|
|
3568
|
+
if (previousRid && deps?.onAfterUpload) {
|
|
3569
|
+
deps.onAfterUpload(container, previousRid);
|
|
3570
|
+
} else if (deps?.onRemove) {
|
|
3571
|
+
deps.onRemove();
|
|
3572
|
+
} else {
|
|
3573
|
+
setEmptyFileContainer(container, state);
|
|
3574
|
+
}
|
|
3412
3575
|
throw error;
|
|
3413
3576
|
}
|
|
3414
3577
|
state.resourceIndex.set(rid, {
|
|
@@ -3418,9 +3581,10 @@ async function handleFileSelect(opts) {
|
|
|
3418
3581
|
uploadedAt: /* @__PURE__ */ new Date(),
|
|
3419
3582
|
file
|
|
3420
3583
|
});
|
|
3421
|
-
|
|
3422
|
-
|
|
3423
|
-
|
|
3584
|
+
if (previousRid && previousRid !== rid) {
|
|
3585
|
+
releaseLocalFileUrl(state.resourceIndex.get(previousRid)?.file);
|
|
3586
|
+
}
|
|
3587
|
+
let hiddenInput = existingHiddenInput;
|
|
3424
3588
|
if (!hiddenInput) {
|
|
3425
3589
|
hiddenInput = document.createElement("input");
|
|
3426
3590
|
hiddenInput.type = "hidden";
|
|
@@ -3429,7 +3593,9 @@ async function handleFileSelect(opts) {
|
|
|
3429
3593
|
}
|
|
3430
3594
|
hiddenInput.value = rid;
|
|
3431
3595
|
const isVideo = file.type.startsWith("video/");
|
|
3432
|
-
if (!isVideo && deps) {
|
|
3596
|
+
if (!isVideo && deps?.onAfterUpload) {
|
|
3597
|
+
deps.onAfterUpload(container, rid);
|
|
3598
|
+
} else if (!isVideo && deps) {
|
|
3433
3599
|
renderSingleFileEditTile(container, rid, state, deps).catch(console.error);
|
|
3434
3600
|
} else {
|
|
3435
3601
|
renderFilePreview(container, rid, state, {
|
|
@@ -3490,17 +3656,17 @@ function filterAndSlice(allFiles, currentCount, constraints, state) {
|
|
|
3490
3656
|
return { accepted, errorMessage: errorParts.join(" \u2022 ") };
|
|
3491
3657
|
}
|
|
3492
3658
|
async function uploadBatch(accepted, resourceIds, listEl, state) {
|
|
3659
|
+
if (listEl) {
|
|
3660
|
+
const tilesWrap = ensureTilesWrap(listEl);
|
|
3661
|
+
const addTile = tilesWrap.querySelector(".fb-multi-add-tile-js") ?? tilesWrap.querySelector(".fb-tile-add");
|
|
3662
|
+
if (addTile) addTile.style.display = "none";
|
|
3663
|
+
}
|
|
3493
3664
|
await Promise.all(
|
|
3494
3665
|
accepted.map(async (file) => {
|
|
3495
3666
|
const placeholder = createUploadingTile(file.name, state);
|
|
3496
3667
|
if (listEl) {
|
|
3497
3668
|
const tilesWrap = ensureTilesWrap(listEl);
|
|
3498
|
-
|
|
3499
|
-
if (addTile) {
|
|
3500
|
-
tilesWrap.insertBefore(placeholder, addTile);
|
|
3501
|
-
} else {
|
|
3502
|
-
tilesWrap.appendChild(placeholder);
|
|
3503
|
-
}
|
|
3669
|
+
tilesWrap.appendChild(placeholder);
|
|
3504
3670
|
}
|
|
3505
3671
|
try {
|
|
3506
3672
|
const rid = await uploadSingleFile(file, state);
|
|
@@ -3542,7 +3708,7 @@ function setupFilesDropHandler(filesContainer, resourceIds, state, updateCallbac
|
|
|
3542
3708
|
function setupFilesPickerHandler(filesPicker, resourceIds, state, updateCallback, constraints, pathKey, instance) {
|
|
3543
3709
|
filesPicker.onchange = async () => {
|
|
3544
3710
|
if (!filesPicker.files) return;
|
|
3545
|
-
const wrapperEl = filesPicker.closest("
|
|
3711
|
+
const wrapperEl = filesPicker.closest("[data-files-wrapper]") || filesPicker.parentElement;
|
|
3546
3712
|
const { accepted, errorMessage } = filterAndSlice(
|
|
3547
3713
|
Array.from(filesPicker.files),
|
|
3548
3714
|
resourceIds.length,
|
|
@@ -3707,6 +3873,10 @@ async function handleLibraryPickSingle(state, element, container, fileWrapper, p
|
|
|
3707
3873
|
hiddenInput.name = pathKey;
|
|
3708
3874
|
fileWrapper.appendChild(hiddenInput);
|
|
3709
3875
|
}
|
|
3876
|
+
const previousRid = hiddenInput.value || null;
|
|
3877
|
+
if (previousRid && previousRid !== first.resourceId) {
|
|
3878
|
+
releaseLocalFileUrl(state.resourceIndex.get(previousRid)?.file);
|
|
3879
|
+
}
|
|
3710
3880
|
hiddenInput.value = first.resourceId;
|
|
3711
3881
|
await renderCallback(first.resourceId);
|
|
3712
3882
|
if (!state.config.readonly) {
|
|
@@ -3715,7 +3885,9 @@ async function handleLibraryPickSingle(state, element, container, fileWrapper, p
|
|
|
3715
3885
|
}
|
|
3716
3886
|
|
|
3717
3887
|
// src/components/file/render-edit.ts
|
|
3718
|
-
|
|
3888
|
+
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>`;
|
|
3889
|
+
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>`;
|
|
3890
|
+
function handleInitialFileData(initial, fileContainer, pathKey, fileWrapper, state, deps, extras) {
|
|
3719
3891
|
seedInferredResource(initial, state.resourceIndex);
|
|
3720
3892
|
const meta = state.resourceIndex.get(initial);
|
|
3721
3893
|
const isVideo = meta?.type?.startsWith("video/");
|
|
@@ -3726,7 +3898,7 @@ function handleInitialFileData(initial, fileContainer, pathKey, fileWrapper, sta
|
|
|
3726
3898
|
deps
|
|
3727
3899
|
}).catch(console.error);
|
|
3728
3900
|
} else {
|
|
3729
|
-
|
|
3901
|
+
renderSingleFileFilled(fileContainer, initial, state, deps, extras);
|
|
3730
3902
|
}
|
|
3731
3903
|
const hiddenInput = document.createElement("input");
|
|
3732
3904
|
hiddenInput.type = "hidden";
|
|
@@ -3734,161 +3906,423 @@ function handleInitialFileData(initial, fileContainer, pathKey, fileWrapper, sta
|
|
|
3734
3906
|
hiddenInput.value = initial;
|
|
3735
3907
|
fileWrapper.appendChild(hiddenInput);
|
|
3736
3908
|
}
|
|
3737
|
-
|
|
3738
|
-
|
|
3739
|
-
|
|
3740
|
-
|
|
3741
|
-
const
|
|
3742
|
-
|
|
3743
|
-
|
|
3744
|
-
|
|
3745
|
-
|
|
3746
|
-
|
|
3747
|
-
|
|
3748
|
-
|
|
3749
|
-
|
|
3909
|
+
function buildWideTile(state, hasLibrary, onUploadClick, onLibraryClick, isDragOver = false, constraintHint = "") {
|
|
3910
|
+
ensureFileStyles();
|
|
3911
|
+
const outer = document.createElement("div");
|
|
3912
|
+
outer.className = `fb-wide-tile${hasLibrary ? " fb-file-card-row" : ""}${isDragOver ? " fb-drag-over" : ""}`;
|
|
3913
|
+
const uploadBtn = document.createElement("button");
|
|
3914
|
+
uploadBtn.type = "button";
|
|
3915
|
+
uploadBtn.className = "fb-wide-tile-upload fb-file-dropzone";
|
|
3916
|
+
const cloudIcon = document.createElement("span");
|
|
3917
|
+
cloudIcon.style.cssText = "width:36px;height:36px;display:block;flex-shrink:0;";
|
|
3918
|
+
cloudIcon.innerHTML = ICON_CLOUD;
|
|
3919
|
+
uploadBtn.appendChild(cloudIcon);
|
|
3920
|
+
const primaryText = document.createElement("div");
|
|
3921
|
+
primaryText.className = "fb-wide-tile-label";
|
|
3922
|
+
primaryText.style.cssText = "font-size:14px;font-weight:600;";
|
|
3923
|
+
primaryText.textContent = isDragOver ? t("dropToUpload", state) : t("clickDragText", state);
|
|
3924
|
+
uploadBtn.appendChild(primaryText);
|
|
3925
|
+
if (constraintHint) {
|
|
3926
|
+
const hintEl = document.createElement("div");
|
|
3927
|
+
hintEl.style.cssText = "font-size:11px;opacity:0.65;margin-top:2px;";
|
|
3928
|
+
hintEl.textContent = constraintHint;
|
|
3929
|
+
uploadBtn.appendChild(hintEl);
|
|
3930
|
+
}
|
|
3931
|
+
uploadBtn.onclick = (e) => {
|
|
3932
|
+
e.stopPropagation();
|
|
3933
|
+
onUploadClick();
|
|
3934
|
+
};
|
|
3935
|
+
outer.appendChild(uploadBtn);
|
|
3936
|
+
if (hasLibrary && onLibraryClick) {
|
|
3937
|
+
const divider = document.createElement("div");
|
|
3938
|
+
divider.className = "fb-wide-tile-divider";
|
|
3939
|
+
outer.appendChild(divider);
|
|
3940
|
+
const libBtn = document.createElement("button");
|
|
3941
|
+
libBtn.type = "button";
|
|
3942
|
+
libBtn.className = "fb-wide-tile-library fb-file-library-card";
|
|
3943
|
+
const libIcon = document.createElement("span");
|
|
3944
|
+
libIcon.style.cssText = "width:28px;height:28px;display:block;flex-shrink:0;";
|
|
3945
|
+
libIcon.innerHTML = ICON_LIBRARY2;
|
|
3946
|
+
libBtn.appendChild(libIcon);
|
|
3947
|
+
const libLabel = document.createElement("div");
|
|
3948
|
+
libLabel.style.cssText = "font-size:13px;font-weight:600;text-align:center;";
|
|
3949
|
+
libLabel.textContent = t("fromLibrary", state);
|
|
3950
|
+
libBtn.appendChild(libLabel);
|
|
3951
|
+
const libHint = document.createElement("div");
|
|
3952
|
+
libHint.style.cssText = "font-size:11px;opacity:0.75;text-align:center;";
|
|
3953
|
+
libHint.textContent = t("libraryHint", state);
|
|
3954
|
+
libBtn.appendChild(libHint);
|
|
3955
|
+
libBtn.onclick = (e) => {
|
|
3956
|
+
e.stopPropagation();
|
|
3957
|
+
onLibraryClick();
|
|
3958
|
+
};
|
|
3959
|
+
outer.appendChild(libBtn);
|
|
3960
|
+
}
|
|
3961
|
+
attachDragOverFeedback(outer, {
|
|
3962
|
+
onEnter: () => {
|
|
3963
|
+
const primaryText2 = outer.querySelector(".fb-wide-tile-label");
|
|
3964
|
+
if (primaryText2) primaryText2.textContent = t("dropToUpload", state);
|
|
3965
|
+
},
|
|
3966
|
+
onLeave: () => {
|
|
3967
|
+
const primaryText2 = outer.querySelector(".fb-wide-tile-label");
|
|
3968
|
+
if (primaryText2) primaryText2.textContent = t("clickDragText", state);
|
|
3969
|
+
},
|
|
3970
|
+
activeClass: "fb-drag-over"
|
|
3971
|
+
});
|
|
3972
|
+
return outer;
|
|
3750
3973
|
}
|
|
3751
|
-
function
|
|
3752
|
-
|
|
3753
|
-
|
|
3754
|
-
|
|
3755
|
-
|
|
3756
|
-
|
|
3757
|
-
|
|
3758
|
-
|
|
3759
|
-
|
|
3760
|
-
|
|
3974
|
+
function attachDragOverFeedback(el, hooks) {
|
|
3975
|
+
let depth = 0;
|
|
3976
|
+
el.addEventListener("dragover", (e) => {
|
|
3977
|
+
e.preventDefault();
|
|
3978
|
+
});
|
|
3979
|
+
el.addEventListener("dragenter", (e) => {
|
|
3980
|
+
e.preventDefault();
|
|
3981
|
+
depth++;
|
|
3982
|
+
if (depth === 1) {
|
|
3983
|
+
el.classList.add(hooks.activeClass);
|
|
3984
|
+
hooks.onEnter();
|
|
3985
|
+
}
|
|
3986
|
+
});
|
|
3987
|
+
el.addEventListener("dragleave", (e) => {
|
|
3988
|
+
e.preventDefault();
|
|
3989
|
+
depth = Math.max(0, depth - 1);
|
|
3990
|
+
if (depth === 0) {
|
|
3991
|
+
el.classList.remove(hooks.activeClass);
|
|
3992
|
+
hooks.onLeave();
|
|
3993
|
+
}
|
|
3994
|
+
});
|
|
3995
|
+
el.addEventListener("drop", () => {
|
|
3996
|
+
depth = 0;
|
|
3997
|
+
el.classList.remove(hooks.activeClass);
|
|
3998
|
+
hooks.onLeave();
|
|
3999
|
+
});
|
|
4000
|
+
}
|
|
4001
|
+
function renderSingleFileFilled(fileContainer, resourceId, state, deps, extras) {
|
|
4002
|
+
const meta = state.resourceIndex.get(resourceId);
|
|
4003
|
+
const isVideo = meta?.type?.startsWith("video/");
|
|
4004
|
+
if (isVideo) {
|
|
4005
|
+
renderFilePreview(fileContainer, resourceId, state, {
|
|
4006
|
+
fileName: meta?.name ?? "",
|
|
4007
|
+
isReadonly: false,
|
|
4008
|
+
deps
|
|
4009
|
+
}).catch(console.error);
|
|
4010
|
+
return;
|
|
4011
|
+
}
|
|
4012
|
+
ensureFileStyles();
|
|
4013
|
+
const outer = document.createElement("div");
|
|
4014
|
+
outer.className = "fb-multi-outer fb-multi-has-files";
|
|
4015
|
+
const grid = document.createElement("div");
|
|
4016
|
+
grid.className = "fb-multi-grid fb-tiles-wrap";
|
|
4017
|
+
outer.appendChild(grid);
|
|
4018
|
+
const tile = buildPreviewTile(
|
|
4019
|
+
resourceId,
|
|
4020
|
+
state,
|
|
4021
|
+
Boolean(deps.onRemove),
|
|
4022
|
+
deps.onRemove ? () => deps.onRemove?.() : null,
|
|
4023
|
+
extras
|
|
4024
|
+
);
|
|
4025
|
+
grid.appendChild(tile);
|
|
4026
|
+
fileContainer.className = "file-preview-container";
|
|
4027
|
+
fileContainer.removeAttribute("style");
|
|
4028
|
+
while (fileContainer.firstChild) fileContainer.removeChild(fileContainer.firstChild);
|
|
4029
|
+
fileContainer.appendChild(outer);
|
|
4030
|
+
}
|
|
4031
|
+
function buildMultiAddTile(state, hasLibrary, onUploadClick, onLibraryClick, isDragOver = false) {
|
|
4032
|
+
const tile = document.createElement("div");
|
|
4033
|
+
tile.className = `fb-multi-add-tile fb-multi-add-tile-js${isDragOver ? " fb-drag-over-tile" : ""}`;
|
|
4034
|
+
const uploadBtn = document.createElement("button");
|
|
4035
|
+
uploadBtn.type = "button";
|
|
4036
|
+
uploadBtn.className = "fb-multi-add-upload fb-tile-add fb-file-dropzone";
|
|
4037
|
+
const cloudIcon = document.createElement("span");
|
|
4038
|
+
cloudIcon.style.cssText = "width:28px;height:28px;display:block;flex-shrink:0;";
|
|
4039
|
+
cloudIcon.innerHTML = ICON_CLOUD;
|
|
4040
|
+
uploadBtn.appendChild(cloudIcon);
|
|
4041
|
+
const uploadLabel = document.createElement("span");
|
|
4042
|
+
uploadLabel.className = "fb-multi-add-label";
|
|
4043
|
+
uploadLabel.style.cssText = "font-size:11px;font-weight:600;";
|
|
4044
|
+
uploadLabel.textContent = isDragOver ? t("dropToUpload", state) : t("clickDragTextMultiple", state);
|
|
4045
|
+
uploadBtn.appendChild(uploadLabel);
|
|
4046
|
+
uploadBtn.onclick = (e) => {
|
|
4047
|
+
e.stopPropagation();
|
|
4048
|
+
onUploadClick();
|
|
4049
|
+
};
|
|
4050
|
+
tile.appendChild(uploadBtn);
|
|
4051
|
+
if (hasLibrary && onLibraryClick) {
|
|
4052
|
+
const divider = document.createElement("div");
|
|
4053
|
+
divider.className = "fb-multi-add-divider";
|
|
4054
|
+
tile.appendChild(divider);
|
|
4055
|
+
const libBtn = document.createElement("button");
|
|
4056
|
+
libBtn.type = "button";
|
|
4057
|
+
libBtn.className = "fb-multi-add-library fb-tile-add-library fb-file-library-card";
|
|
4058
|
+
libBtn.setAttribute("aria-label", t("fromLibrary", state));
|
|
4059
|
+
const libIcon = document.createElement("span");
|
|
4060
|
+
libIcon.style.cssText = "width:14px;height:14px;display:block;flex-shrink:0;";
|
|
4061
|
+
libIcon.innerHTML = ICON_LIBRARY2;
|
|
4062
|
+
libBtn.appendChild(libIcon);
|
|
4063
|
+
libBtn.appendChild(document.createTextNode(t("fromLibrary", state)));
|
|
4064
|
+
libBtn.onclick = (e) => {
|
|
4065
|
+
e.stopPropagation();
|
|
4066
|
+
onLibraryClick();
|
|
4067
|
+
};
|
|
4068
|
+
tile.appendChild(libBtn);
|
|
4069
|
+
}
|
|
4070
|
+
return tile;
|
|
4071
|
+
}
|
|
4072
|
+
function buildPreviewTile(rid, state, canRemove, onRemove, extras) {
|
|
4073
|
+
ensureFileStyles();
|
|
4074
|
+
const meta = state.resourceIndex.get(rid);
|
|
4075
|
+
const tile = document.createElement("div");
|
|
4076
|
+
tile.className = "fb-preview-tile fb-checker fb-tile-resource resource-pill";
|
|
4077
|
+
tile.dataset.resourceId = rid;
|
|
4078
|
+
const actionsEl = createTileActions({
|
|
4079
|
+
canRemove: canRemove && onRemove !== null,
|
|
4080
|
+
removeHandler: onRemove,
|
|
4081
|
+
state,
|
|
4082
|
+
resourceId: rid,
|
|
4083
|
+
fileName: meta?.name ?? "",
|
|
4084
|
+
meta,
|
|
4085
|
+
replaceHandler: extras?.replaceHandler ?? null,
|
|
4086
|
+
libraryHandler: extras?.libraryHandler ?? null
|
|
4087
|
+
});
|
|
4088
|
+
fillTileContent(tile, rid, meta, state, actionsEl).catch((err) => {
|
|
4089
|
+
console.error("Failed to render tile:", err);
|
|
4090
|
+
});
|
|
4091
|
+
return tile;
|
|
4092
|
+
}
|
|
4093
|
+
function buildPlaceholderTile(isDragOver = false) {
|
|
4094
|
+
const div = document.createElement("div");
|
|
4095
|
+
div.className = `fb-multi-placeholder fb-checker${isDragOver ? " fb-drag-over" : ""}`;
|
|
4096
|
+
return div;
|
|
4097
|
+
}
|
|
4098
|
+
function buildMetaLine(state, element, ridCount, maxCount, canClearAll, onClearAll) {
|
|
4099
|
+
const line = document.createElement("div");
|
|
4100
|
+
line.className = "fb-meta-line";
|
|
4101
|
+
const metaText = document.createElement("div");
|
|
4102
|
+
metaText.className = "fb-meta-text";
|
|
4103
|
+
if (element.maxSize && element.maxSize !== Infinity) {
|
|
4104
|
+
const sizeSpan = document.createElement("span");
|
|
4105
|
+
sizeSpan.textContent = t("hintMaxSize", state, { size: element.maxSize });
|
|
4106
|
+
metaText.appendChild(sizeSpan);
|
|
4107
|
+
metaText.appendChild(buildMetaDot());
|
|
4108
|
+
}
|
|
4109
|
+
const exts = getAllowedExtensions(element.accept);
|
|
4110
|
+
if (exts.length > 0) {
|
|
4111
|
+
const fmtSpan = document.createElement("span");
|
|
4112
|
+
fmtSpan.className = "fb-meta-mono";
|
|
4113
|
+
fmtSpan.textContent = exts.map((e) => e.toUpperCase()).join(", ");
|
|
4114
|
+
metaText.appendChild(fmtSpan);
|
|
4115
|
+
metaText.appendChild(buildMetaDot());
|
|
4116
|
+
}
|
|
4117
|
+
const countSpan = document.createElement("span");
|
|
4118
|
+
if (maxCount < Infinity) {
|
|
4119
|
+
countSpan.textContent = t("fileCountWithMax", state, {
|
|
4120
|
+
count: ridCount,
|
|
4121
|
+
max: maxCount
|
|
4122
|
+
});
|
|
3761
4123
|
} else {
|
|
3762
|
-
|
|
3763
|
-
|
|
3764
|
-
|
|
4124
|
+
const countKey = ridCount === 1 ? "fileCountSingle" : "fileCountPlural";
|
|
4125
|
+
countSpan.textContent = t(countKey, state, { count: ridCount });
|
|
4126
|
+
}
|
|
4127
|
+
metaText.appendChild(countSpan);
|
|
4128
|
+
line.appendChild(metaText);
|
|
4129
|
+
if (canClearAll && ridCount > 1) {
|
|
4130
|
+
const clearBtn = document.createElement("button");
|
|
4131
|
+
clearBtn.type = "button";
|
|
4132
|
+
clearBtn.className = "fb-clear-all-btn";
|
|
4133
|
+
clearBtn.textContent = t("clearAll", state);
|
|
4134
|
+
clearBtn.onclick = (e) => {
|
|
4135
|
+
e.stopPropagation();
|
|
4136
|
+
if (window.confirm(t("clearAll", state) + "?")) {
|
|
4137
|
+
onClearAll();
|
|
4138
|
+
}
|
|
4139
|
+
};
|
|
4140
|
+
line.appendChild(clearBtn);
|
|
3765
4141
|
}
|
|
3766
|
-
|
|
3767
|
-
|
|
4142
|
+
return line;
|
|
4143
|
+
}
|
|
4144
|
+
function buildMetaDot() {
|
|
4145
|
+
const dot = document.createElement("span");
|
|
4146
|
+
dot.className = "fb-meta-dot";
|
|
4147
|
+
return dot;
|
|
3768
4148
|
}
|
|
4149
|
+
var gridResizeObservers = /* @__PURE__ */ new WeakMap();
|
|
3769
4150
|
function renderResourcePills(opts) {
|
|
3770
4151
|
const {
|
|
3771
4152
|
container,
|
|
3772
4153
|
rids,
|
|
3773
4154
|
state,
|
|
3774
4155
|
onRemove,
|
|
3775
|
-
hint,
|
|
3776
|
-
countInfo,
|
|
3777
4156
|
maxCount,
|
|
3778
4157
|
isReadonly = false,
|
|
3779
|
-
onLibraryPick
|
|
4158
|
+
onLibraryPick,
|
|
4159
|
+
element,
|
|
4160
|
+
onClearAll,
|
|
4161
|
+
openPicker: openPickerProp
|
|
3780
4162
|
} = opts;
|
|
3781
4163
|
ensureFileStyles();
|
|
3782
4164
|
const wrapper = container.closest("[data-files-wrapper]");
|
|
3783
4165
|
if (wrapper) {
|
|
3784
4166
|
wrapper.dataset.resourceIds = JSON.stringify(rids ?? []);
|
|
3785
4167
|
}
|
|
4168
|
+
const previousObserver = gridResizeObservers.get(container);
|
|
4169
|
+
if (previousObserver) {
|
|
4170
|
+
previousObserver.disconnect();
|
|
4171
|
+
gridResizeObservers.delete(container);
|
|
4172
|
+
}
|
|
3786
4173
|
while (container.firstChild) container.removeChild(container.firstChild);
|
|
3787
4174
|
const ridList = rids ?? [];
|
|
3788
|
-
const
|
|
4175
|
+
const effectiveMax = maxCount ?? Infinity;
|
|
4176
|
+
const atMax = effectiveMax !== Infinity && ridList.length >= effectiveMax;
|
|
3789
4177
|
const hasLibrary = !isReadonly && typeof onLibraryPick === "function";
|
|
3790
|
-
const
|
|
3791
|
-
const
|
|
3792
|
-
if (
|
|
3793
|
-
|
|
3794
|
-
|
|
3795
|
-
|
|
3796
|
-
const openPicker = () => {
|
|
3797
|
-
const picker = findFilePicker(container);
|
|
3798
|
-
if (picker) picker.click();
|
|
3799
|
-
};
|
|
3800
|
-
if (ridList.length === 0) {
|
|
3801
|
-
if (isReadonly) {
|
|
4178
|
+
const openPicker = openPickerProp ?? (() => {
|
|
4179
|
+
const pickerEl = container.closest("[data-files-wrapper]")?.querySelector('input[type="file"]');
|
|
4180
|
+
if (pickerEl) pickerEl.click();
|
|
4181
|
+
});
|
|
4182
|
+
if (isReadonly) {
|
|
4183
|
+
if (ridList.length === 0) {
|
|
3802
4184
|
const emptyEl = document.createElement("div");
|
|
3803
4185
|
emptyEl.className = "fb-tile-empty-text";
|
|
3804
4186
|
emptyEl.textContent = t("noFilesSelected", state);
|
|
3805
4187
|
container.appendChild(emptyEl);
|
|
3806
|
-
} else if (hasLibrary) {
|
|
3807
|
-
const row = document.createElement("div");
|
|
3808
|
-
row.className = "fb-file-card-row";
|
|
3809
|
-
const dropzone = buildEmptyDropzone(
|
|
3810
|
-
state,
|
|
3811
|
-
t("clickDragTextMultiple", state),
|
|
3812
|
-
buildSubHint(),
|
|
3813
|
-
openPicker
|
|
3814
|
-
);
|
|
3815
|
-
const libraryBtn = buildLibraryButton("card", state, onLibraryPick);
|
|
3816
|
-
row.appendChild(dropzone);
|
|
3817
|
-
row.appendChild(libraryBtn);
|
|
3818
|
-
container.appendChild(row);
|
|
3819
4188
|
} else {
|
|
3820
|
-
const
|
|
3821
|
-
|
|
3822
|
-
|
|
3823
|
-
|
|
3824
|
-
|
|
3825
|
-
|
|
3826
|
-
|
|
4189
|
+
const grid2 = document.createElement("div");
|
|
4190
|
+
grid2.className = "fb-multi-readonly-grid";
|
|
4191
|
+
container.appendChild(grid2);
|
|
4192
|
+
for (const rid of ridList) {
|
|
4193
|
+
const meta = state.resourceIndex.get(rid);
|
|
4194
|
+
const tile = document.createElement("div");
|
|
4195
|
+
tile.className = "fb-readonly-tile fb-checker fb-tile fb-tile-resource";
|
|
4196
|
+
tile.dataset.resourceId = rid;
|
|
4197
|
+
const actionsEl = createTileActions({
|
|
4198
|
+
canRemove: false,
|
|
4199
|
+
removeHandler: null,
|
|
4200
|
+
state,
|
|
4201
|
+
resourceId: rid,
|
|
4202
|
+
fileName: meta?.name ?? "",
|
|
4203
|
+
meta
|
|
4204
|
+
});
|
|
4205
|
+
fillTileContent(tile, rid, meta, state, actionsEl).catch(console.error);
|
|
4206
|
+
tile.onclick = async () => {
|
|
4207
|
+
let url = null;
|
|
4208
|
+
if (state.config.getDownloadUrl) {
|
|
4209
|
+
url = state.config.getDownloadUrl(rid);
|
|
4210
|
+
} else if (state.config.getThumbnail) {
|
|
4211
|
+
url = await state.config.getThumbnail(rid);
|
|
4212
|
+
} else if (meta?.file instanceof File) {
|
|
4213
|
+
url = URL.createObjectURL(meta.file);
|
|
4214
|
+
}
|
|
4215
|
+
if (url) {
|
|
4216
|
+
window.open(url, "_blank");
|
|
4217
|
+
} else if (state.config.downloadFile) {
|
|
4218
|
+
state.config.downloadFile(rid, meta?.name ?? "");
|
|
4219
|
+
}
|
|
4220
|
+
};
|
|
4221
|
+
grid2.appendChild(tile);
|
|
4222
|
+
}
|
|
3827
4223
|
}
|
|
3828
4224
|
return;
|
|
3829
4225
|
}
|
|
3830
|
-
const
|
|
3831
|
-
|
|
3832
|
-
|
|
3833
|
-
|
|
3834
|
-
|
|
3835
|
-
|
|
3836
|
-
|
|
3837
|
-
|
|
3838
|
-
const
|
|
3839
|
-
|
|
3840
|
-
removeHandler: onRemove ? () => onRemove(rid) : null,
|
|
4226
|
+
const outerDiv = document.createElement("div");
|
|
4227
|
+
outerDiv.className = `fb-multi-outer${ridList.length > 0 ? " fb-multi-has-files" : ""}`;
|
|
4228
|
+
const grid = document.createElement("div");
|
|
4229
|
+
grid.className = "fb-multi-grid fb-tiles-wrap";
|
|
4230
|
+
outerDiv.appendChild(grid);
|
|
4231
|
+
container.appendChild(outerDiv);
|
|
4232
|
+
for (let i = 0; i < ridList.length; i++) {
|
|
4233
|
+
const rid = ridList[i];
|
|
4234
|
+
const tile = buildPreviewTile(
|
|
4235
|
+
rid,
|
|
3841
4236
|
state,
|
|
3842
|
-
|
|
3843
|
-
|
|
3844
|
-
|
|
3845
|
-
|
|
3846
|
-
console.error("Failed to render tile:", err);
|
|
3847
|
-
});
|
|
3848
|
-
tilesWrap.appendChild(tile);
|
|
3849
|
-
}
|
|
3850
|
-
if (!isReadonly && !atMax) {
|
|
3851
|
-
const addTile = document.createElement("div");
|
|
3852
|
-
addTile.className = "fb-tile fb-tile-add";
|
|
3853
|
-
addTile.innerHTML = "+";
|
|
3854
|
-
addTile.onclick = openPicker;
|
|
3855
|
-
tilesWrap.appendChild(addTile);
|
|
3856
|
-
if (hasLibrary) {
|
|
3857
|
-
const libraryTile = buildLibraryButton("tile", state, onLibraryPick);
|
|
3858
|
-
tilesWrap.appendChild(libraryTile);
|
|
3859
|
-
}
|
|
3860
|
-
} else if (!isReadonly && atMax) {
|
|
3861
|
-
const chip = document.createElement("div");
|
|
3862
|
-
chip.className = "fb-tile-counter";
|
|
3863
|
-
chip.textContent = t("filesCounter", state, {
|
|
3864
|
-
count: ridList.length,
|
|
3865
|
-
max: maxCount
|
|
3866
|
-
});
|
|
3867
|
-
tilesWrap.appendChild(chip);
|
|
4237
|
+
onRemove !== null,
|
|
4238
|
+
onRemove ? () => onRemove(rid) : null
|
|
4239
|
+
);
|
|
4240
|
+
grid.appendChild(tile);
|
|
3868
4241
|
}
|
|
3869
|
-
|
|
3870
|
-
|
|
3871
|
-
|
|
3872
|
-
|
|
3873
|
-
|
|
3874
|
-
|
|
3875
|
-
|
|
4242
|
+
if (!atMax) {
|
|
4243
|
+
const addTile = buildMultiAddTile(
|
|
4244
|
+
state,
|
|
4245
|
+
hasLibrary,
|
|
4246
|
+
openPicker,
|
|
4247
|
+
onLibraryPick ?? null
|
|
4248
|
+
);
|
|
4249
|
+
grid.appendChild(addTile);
|
|
4250
|
+
}
|
|
4251
|
+
const occupied = ridList.length + (atMax ? 0 : 1);
|
|
4252
|
+
const adjustPlaceholders = () => {
|
|
4253
|
+
const tpl = getComputedStyle(grid).gridTemplateColumns;
|
|
4254
|
+
const cols = tpl ? tpl.split(" ").filter(Boolean).length : 0;
|
|
4255
|
+
if (!cols) return;
|
|
4256
|
+
const remainder = occupied % cols;
|
|
4257
|
+
const rowFill = remainder === 0 ? 0 : cols - remainder;
|
|
4258
|
+
const capacityRemaining = effectiveMax === Infinity ? rowFill : Math.max(0, effectiveMax - occupied);
|
|
4259
|
+
const needed = Math.min(rowFill, capacityRemaining);
|
|
4260
|
+
const existing = grid.querySelectorAll(".fb-multi-placeholder");
|
|
4261
|
+
if (existing.length > needed) {
|
|
4262
|
+
for (let i = existing.length - 1; i >= needed; i--) existing[i].remove();
|
|
4263
|
+
} else if (existing.length < needed) {
|
|
4264
|
+
for (let i = existing.length; i < needed; i++) {
|
|
4265
|
+
grid.appendChild(buildPlaceholderTile());
|
|
4266
|
+
}
|
|
4267
|
+
}
|
|
4268
|
+
};
|
|
4269
|
+
if (effectiveMax === Infinity || effectiveMax > occupied) {
|
|
4270
|
+
grid.appendChild(buildPlaceholderTile());
|
|
4271
|
+
}
|
|
4272
|
+
requestAnimationFrame(adjustPlaceholders);
|
|
4273
|
+
if (typeof ResizeObserver !== "undefined") {
|
|
4274
|
+
const ro = new ResizeObserver(() => adjustPlaceholders());
|
|
4275
|
+
ro.observe(grid);
|
|
4276
|
+
gridResizeObservers.set(container, ro);
|
|
4277
|
+
}
|
|
4278
|
+
attachDragOverFeedback(outerDiv, {
|
|
4279
|
+
activeClass: "fb-drag-over",
|
|
4280
|
+
onEnter: () => {
|
|
4281
|
+
grid.querySelectorAll(".fb-multi-placeholder").forEach((p) => {
|
|
4282
|
+
p.classList.add("fb-drag-over");
|
|
4283
|
+
});
|
|
4284
|
+
const addTile = grid.querySelector(".fb-multi-add-tile-js");
|
|
4285
|
+
if (addTile) {
|
|
4286
|
+
addTile.classList.add("fb-drag-over-tile");
|
|
4287
|
+
const label = addTile.querySelector(".fb-multi-add-label");
|
|
4288
|
+
if (label) label.textContent = t("dropToUpload", state);
|
|
4289
|
+
}
|
|
4290
|
+
},
|
|
4291
|
+
onLeave: () => {
|
|
4292
|
+
grid.querySelectorAll(".fb-multi-placeholder").forEach((p) => {
|
|
4293
|
+
p.classList.remove("fb-drag-over");
|
|
4294
|
+
});
|
|
4295
|
+
const addTile = grid.querySelector(".fb-multi-add-tile-js");
|
|
4296
|
+
if (addTile) {
|
|
4297
|
+
addTile.classList.remove("fb-drag-over-tile");
|
|
4298
|
+
const label = addTile.querySelector(".fb-multi-add-label");
|
|
4299
|
+
if (label) label.textContent = t("clickDragTextMultiple", state);
|
|
4300
|
+
}
|
|
4301
|
+
}
|
|
4302
|
+
});
|
|
4303
|
+
if (element) {
|
|
4304
|
+
const metaLine = buildMetaLine(
|
|
4305
|
+
state,
|
|
4306
|
+
element,
|
|
4307
|
+
ridList.length,
|
|
4308
|
+
effectiveMax,
|
|
4309
|
+
Boolean(onClearAll),
|
|
4310
|
+
onClearAll ?? (() => {
|
|
4311
|
+
})
|
|
4312
|
+
);
|
|
4313
|
+
container.appendChild(metaLine);
|
|
3876
4314
|
}
|
|
3877
4315
|
}
|
|
3878
4316
|
function renderFileElementEdit(element, ctx, wrapper, pathKey) {
|
|
3879
4317
|
const state = ctx.state;
|
|
3880
4318
|
const fileWrapper = document.createElement("div");
|
|
3881
4319
|
fileWrapper.className = "space-y-2";
|
|
4320
|
+
fileWrapper.dataset.filesWrapper = pathKey;
|
|
3882
4321
|
const picker = document.createElement("input");
|
|
3883
4322
|
picker.type = "file";
|
|
3884
4323
|
picker.name = pathKey;
|
|
3885
4324
|
picker.style.display = "none";
|
|
3886
|
-
|
|
3887
|
-
picker.accept = typeof element.accept === "string" ? element.accept : [
|
|
3888
|
-
...element.accept.extensions?.map((ext) => `.${ext}`) ?? [],
|
|
3889
|
-
...element.accept.mime ?? []
|
|
3890
|
-
].join(",") || "";
|
|
3891
|
-
}
|
|
4325
|
+
picker.accept = buildAcceptAttribute(element.accept);
|
|
3892
4326
|
const fileContainer = document.createElement("div");
|
|
3893
4327
|
fileContainer.className = "file-preview-container";
|
|
3894
4328
|
const initial = ctx.prefill[element.key];
|
|
@@ -3917,14 +4351,6 @@ function renderFileElementEdit(element, ctx, wrapper, pathKey) {
|
|
|
3917
4351
|
setupDrop(container) {
|
|
3918
4352
|
setupDragAndDrop(container, handlers.dragHandler);
|
|
3919
4353
|
},
|
|
3920
|
-
restoreDropzone() {
|
|
3921
|
-
const hint = makeFieldHint(element, state);
|
|
3922
|
-
fileContainer.className = "file-preview-container w-full max-w-md bg-gray-100 rounded-lg overflow-hidden relative group cursor-pointer";
|
|
3923
|
-
fileContainer.style.height = "128px";
|
|
3924
|
-
setEmptyFileContainer(fileContainer, state, hint);
|
|
3925
|
-
fileContainer.onclick = handlers.fileUploadHandler;
|
|
3926
|
-
setupDragAndDrop(fileContainer, handlers.dragHandler);
|
|
3927
|
-
},
|
|
3928
4354
|
onRemove() {
|
|
3929
4355
|
const hiddenInput = fileWrapper.querySelector('input[type="hidden"]');
|
|
3930
4356
|
const currentRid = hiddenInput?.value;
|
|
@@ -3935,34 +4361,11 @@ function renderFileElementEdit(element, ctx, wrapper, pathKey) {
|
|
|
3935
4361
|
renderEmptySingleState();
|
|
3936
4362
|
}
|
|
3937
4363
|
};
|
|
3938
|
-
const
|
|
3939
|
-
|
|
3940
|
-
|
|
3941
|
-
|
|
3942
|
-
|
|
3943
|
-
onRemove: handlers.onRemove
|
|
3944
|
-
});
|
|
3945
|
-
const renderEmptySingleState = () => {
|
|
3946
|
-
if (state.config.pickExistingFiles && !element.disableLibrary) {
|
|
3947
|
-
fileContainer.className = "file-preview-container";
|
|
3948
|
-
fileContainer.removeAttribute("style");
|
|
3949
|
-
fileContainer.onclick = null;
|
|
3950
|
-
while (fileContainer.firstChild) {
|
|
3951
|
-
fileContainer.removeChild(fileContainer.firstChild);
|
|
3952
|
-
}
|
|
3953
|
-
const row = document.createElement("div");
|
|
3954
|
-
row.className = "fb-file-card-row";
|
|
3955
|
-
row.style.cssText = "display:flex;gap:8px;align-items:stretch;";
|
|
3956
|
-
const hint = makeFieldHint(element, state);
|
|
3957
|
-
const uploadCard = buildEmptyDropzone(
|
|
3958
|
-
state,
|
|
3959
|
-
t("clickDragText", state),
|
|
3960
|
-
hint,
|
|
3961
|
-
handlers.fileUploadHandler
|
|
3962
|
-
);
|
|
3963
|
-
uploadCard.style.cssText = "flex:1;min-width:0;height:128px;";
|
|
3964
|
-
setupDragAndDrop(uploadCard, handlers.dragHandler);
|
|
3965
|
-
const libraryBtn = buildLibraryButton("card", state, () => {
|
|
4364
|
+
const buildSingleExtras = () => {
|
|
4365
|
+
const hasLibrary = Boolean(state.config.pickExistingFiles && !element.disableLibrary);
|
|
4366
|
+
return {
|
|
4367
|
+
replaceHandler: state.config.uploadFile ? () => picker.click() : null,
|
|
4368
|
+
libraryHandler: hasLibrary ? () => {
|
|
3966
4369
|
handleLibraryPickSingle(
|
|
3967
4370
|
state,
|
|
3968
4371
|
element,
|
|
@@ -3971,20 +4374,41 @@ function renderFileElementEdit(element, ctx, wrapper, pathKey) {
|
|
|
3971
4374
|
pathKey,
|
|
3972
4375
|
pathKey,
|
|
3973
4376
|
async (rid) => {
|
|
3974
|
-
|
|
4377
|
+
renderSingleFileFilled(fileContainer, rid, state, buildDeps(), buildSingleExtras());
|
|
3975
4378
|
},
|
|
3976
4379
|
ctx.instance
|
|
3977
4380
|
).catch((err) => {
|
|
3978
4381
|
console.error("Library pick failed:", err);
|
|
3979
4382
|
});
|
|
3980
|
-
}
|
|
3981
|
-
|
|
3982
|
-
|
|
3983
|
-
|
|
3984
|
-
|
|
3985
|
-
|
|
3986
|
-
|
|
4383
|
+
} : null
|
|
4384
|
+
};
|
|
4385
|
+
};
|
|
4386
|
+
const buildDeps = () => ({
|
|
4387
|
+
picker,
|
|
4388
|
+
fileUploadHandler: handlers.fileUploadHandler,
|
|
4389
|
+
dragHandler: handlers.dragHandler,
|
|
4390
|
+
setupDrop: handlers.setupDrop,
|
|
4391
|
+
onRemove: handlers.onRemove,
|
|
4392
|
+
onAfterUpload: (container, rid) => {
|
|
4393
|
+
renderSingleFileFilled(container, rid, state, buildDeps(), buildSingleExtras());
|
|
3987
4394
|
}
|
|
4395
|
+
});
|
|
4396
|
+
const renderEmptySingleState = () => {
|
|
4397
|
+
ensureFileStyles();
|
|
4398
|
+
fileContainer.className = "file-preview-container";
|
|
4399
|
+
fileContainer.removeAttribute("style");
|
|
4400
|
+
while (fileContainer.firstChild) fileContainer.removeChild(fileContainer.firstChild);
|
|
4401
|
+
const onLibraryClick = buildSingleExtras().libraryHandler;
|
|
4402
|
+
const wideTile = buildWideTile(
|
|
4403
|
+
state,
|
|
4404
|
+
onLibraryClick !== null,
|
|
4405
|
+
handlers.fileUploadHandler,
|
|
4406
|
+
onLibraryClick,
|
|
4407
|
+
false,
|
|
4408
|
+
makeFieldHint(element, state)
|
|
4409
|
+
);
|
|
4410
|
+
fileContainer.appendChild(wideTile);
|
|
4411
|
+
setupDragAndDrop(fileContainer, handlers.dragHandler);
|
|
3988
4412
|
};
|
|
3989
4413
|
if (initial) {
|
|
3990
4414
|
handleInitialFileData(
|
|
@@ -3993,11 +4417,11 @@ function renderFileElementEdit(element, ctx, wrapper, pathKey) {
|
|
|
3993
4417
|
pathKey,
|
|
3994
4418
|
fileWrapper,
|
|
3995
4419
|
state,
|
|
3996
|
-
buildDeps()
|
|
4420
|
+
buildDeps(),
|
|
4421
|
+
buildSingleExtras()
|
|
3997
4422
|
);
|
|
3998
4423
|
const prefillMeta = state.resourceIndex.get(initial);
|
|
3999
4424
|
if (prefillMeta?.type?.startsWith("video/")) {
|
|
4000
|
-
fileContainer.onclick = handlers.fileUploadHandler;
|
|
4001
4425
|
setupDragAndDrop(fileContainer, handlers.dragHandler);
|
|
4002
4426
|
}
|
|
4003
4427
|
} else {
|
|
@@ -4005,113 +4429,23 @@ function renderFileElementEdit(element, ctx, wrapper, pathKey) {
|
|
|
4005
4429
|
}
|
|
4006
4430
|
picker.onchange = () => {
|
|
4007
4431
|
if (picker.files && picker.files.length > 0) {
|
|
4008
|
-
|
|
4009
|
-
file: picker.files[0],
|
|
4010
|
-
container: fileContainer,
|
|
4011
|
-
fieldName: pathKey,
|
|
4012
|
-
state,
|
|
4013
|
-
deps: buildDeps(),
|
|
4014
|
-
instance: ctx.instance,
|
|
4015
|
-
allowedExtensions: allowedExts,
|
|
4016
|
-
allowedMimes,
|
|
4017
|
-
maxSizeMB
|
|
4018
|
-
});
|
|
4432
|
+
handlers.dragHandler(picker.files);
|
|
4019
4433
|
}
|
|
4020
4434
|
};
|
|
4021
4435
|
fileWrapper.appendChild(fileContainer);
|
|
4022
4436
|
fileWrapper.appendChild(picker);
|
|
4023
4437
|
wrapper.appendChild(fileWrapper);
|
|
4024
4438
|
}
|
|
4025
|
-
function
|
|
4026
|
-
|
|
4027
|
-
|
|
4028
|
-
|
|
4029
|
-
|
|
4030
|
-
|
|
4031
|
-
|
|
4032
|
-
filesPicker.name = pathKey;
|
|
4033
|
-
filesPicker.multiple = true;
|
|
4034
|
-
filesPicker.style.display = "none";
|
|
4035
|
-
if (element.accept) {
|
|
4036
|
-
filesPicker.accept = typeof element.accept === "string" ? element.accept : [
|
|
4037
|
-
...element.accept.extensions?.map((ext) => `.${ext}`) ?? [],
|
|
4038
|
-
...element.accept.mime ?? []
|
|
4039
|
-
].join(",") || "";
|
|
4040
|
-
}
|
|
4041
|
-
const filesContainer = document.createElement("div");
|
|
4042
|
-
filesContainer.className = "files-list-wrapper";
|
|
4043
|
-
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);";
|
|
4044
|
-
const list = document.createElement("div");
|
|
4045
|
-
list.className = "files-list";
|
|
4046
|
-
const initialFiles = ctx.prefill[element.key] || [];
|
|
4047
|
-
addPrefillFilesToIndex(initialFiles, state.resourceIndex);
|
|
4048
|
-
filesWrapper.dataset.resourceIds = JSON.stringify(initialFiles);
|
|
4049
|
-
const filesFieldHint = makeFieldHint(element, state);
|
|
4050
|
-
const filesConstraints = {
|
|
4051
|
-
maxCount: Infinity,
|
|
4052
|
-
allowedExtensions: getAllowedExtensions(element.accept),
|
|
4053
|
-
allowedMimes: getAllowedMimes(element.accept),
|
|
4054
|
-
maxSize: element.maxSize ?? Infinity
|
|
4055
|
-
};
|
|
4056
|
-
filesContainer.appendChild(list);
|
|
4057
|
-
filesWrapper.appendChild(filesPicker);
|
|
4058
|
-
filesWrapper.appendChild(filesContainer);
|
|
4059
|
-
wrapper.appendChild(filesWrapper);
|
|
4060
|
-
const onLibraryPickFiles = state.config.pickExistingFiles && !element.disableLibrary ? () => {
|
|
4061
|
-
handleLibraryPickMulti(
|
|
4062
|
-
state,
|
|
4063
|
-
element,
|
|
4064
|
-
filesWrapper,
|
|
4065
|
-
pathKey,
|
|
4066
|
-
initialFiles,
|
|
4067
|
-
Infinity,
|
|
4068
|
-
updateFilesList,
|
|
4069
|
-
ctx.instance
|
|
4070
|
-
).catch((err) => {
|
|
4071
|
-
console.error("Library pick failed:", err);
|
|
4072
|
-
});
|
|
4073
|
-
} : null;
|
|
4074
|
-
function updateFilesList() {
|
|
4075
|
-
const currentlyReadonly = isElementReadonly(element, state);
|
|
4076
|
-
renderResourcePills({
|
|
4077
|
-
container: list,
|
|
4078
|
-
rids: initialFiles,
|
|
4079
|
-
state,
|
|
4080
|
-
onRemove: currentlyReadonly ? null : (ridToRemove) => {
|
|
4081
|
-
releaseLocalFileUrl(state.resourceIndex.get(ridToRemove)?.file);
|
|
4082
|
-
const index = initialFiles.indexOf(ridToRemove);
|
|
4083
|
-
if (index > -1) initialFiles.splice(index, 1);
|
|
4084
|
-
updateFilesList();
|
|
4085
|
-
},
|
|
4086
|
-
hint: filesFieldHint,
|
|
4087
|
-
isReadonly: currentlyReadonly,
|
|
4088
|
-
onLibraryPick: currentlyReadonly ? null : onLibraryPickFiles
|
|
4089
|
-
});
|
|
4090
|
-
}
|
|
4091
|
-
updateFilesList();
|
|
4092
|
-
setupFilesDropHandler(
|
|
4093
|
-
filesContainer,
|
|
4094
|
-
initialFiles,
|
|
4095
|
-
state,
|
|
4096
|
-
updateFilesList,
|
|
4097
|
-
filesConstraints,
|
|
4098
|
-
pathKey,
|
|
4099
|
-
ctx.instance
|
|
4100
|
-
);
|
|
4101
|
-
setupFilesPickerHandler(
|
|
4102
|
-
filesPicker,
|
|
4103
|
-
initialFiles,
|
|
4104
|
-
state,
|
|
4105
|
-
updateFilesList,
|
|
4106
|
-
filesConstraints,
|
|
4107
|
-
pathKey,
|
|
4108
|
-
ctx.instance
|
|
4109
|
-
);
|
|
4439
|
+
function buildAcceptAttribute(accept) {
|
|
4440
|
+
if (!accept) return "";
|
|
4441
|
+
if (typeof accept === "string") return accept;
|
|
4442
|
+
return [
|
|
4443
|
+
...accept.extensions?.map((ext) => `.${ext}`) ?? [],
|
|
4444
|
+
...accept.mime ?? []
|
|
4445
|
+
].join(",");
|
|
4110
4446
|
}
|
|
4111
|
-
function
|
|
4447
|
+
function setupMultiFileEditMode(element, ctx, wrapper, pathKey, maxFiles) {
|
|
4112
4448
|
const state = ctx.state;
|
|
4113
|
-
const minFiles = element.minCount ?? 0;
|
|
4114
|
-
const maxFiles = element.maxCount ?? Infinity;
|
|
4115
4449
|
const filesWrapper = document.createElement("div");
|
|
4116
4450
|
filesWrapper.className = "space-y-2";
|
|
4117
4451
|
filesWrapper.dataset.filesWrapper = pathKey;
|
|
@@ -4120,15 +4454,9 @@ function renderMultipleFileElementEdit(element, ctx, wrapper, pathKey) {
|
|
|
4120
4454
|
filesPicker.name = pathKey;
|
|
4121
4455
|
filesPicker.multiple = true;
|
|
4122
4456
|
filesPicker.style.display = "none";
|
|
4123
|
-
|
|
4124
|
-
filesPicker.accept = typeof element.accept === "string" ? element.accept : [
|
|
4125
|
-
...element.accept.extensions?.map((ext) => `.${ext}`) ?? [],
|
|
4126
|
-
...element.accept.mime ?? []
|
|
4127
|
-
].join(",") || "";
|
|
4128
|
-
}
|
|
4457
|
+
filesPicker.accept = buildAcceptAttribute(element.accept);
|
|
4129
4458
|
const filesContainer = document.createElement("div");
|
|
4130
4459
|
filesContainer.className = "files-list-wrapper";
|
|
4131
|
-
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);";
|
|
4132
4460
|
const list = document.createElement("div");
|
|
4133
4461
|
list.className = "files-list";
|
|
4134
4462
|
filesWrapper.appendChild(filesPicker);
|
|
@@ -4137,19 +4465,18 @@ function renderMultipleFileElementEdit(element, ctx, wrapper, pathKey) {
|
|
|
4137
4465
|
const initialFiles = Array.isArray(ctx.prefill[element.key]) ? [...ctx.prefill[element.key]] : [];
|
|
4138
4466
|
addPrefillFilesToIndex(initialFiles, state.resourceIndex);
|
|
4139
4467
|
filesWrapper.dataset.resourceIds = JSON.stringify(initialFiles);
|
|
4140
|
-
const
|
|
4141
|
-
const multipleConstraints = {
|
|
4468
|
+
const constraints = {
|
|
4142
4469
|
maxCount: maxFiles,
|
|
4143
4470
|
allowedExtensions: getAllowedExtensions(element.accept),
|
|
4144
4471
|
allowedMimes: getAllowedMimes(element.accept),
|
|
4145
|
-
|
|
4472
|
+
// Prefer schema's `maxSize`; fall back to legacy `maxSizeMB` for
|
|
4473
|
+
// backward compatibility (matches addFileSizeHint in validation.ts).
|
|
4474
|
+
maxSize: element.maxSize ?? element.maxSizeMB ?? Infinity
|
|
4146
4475
|
};
|
|
4147
|
-
const
|
|
4148
|
-
|
|
4149
|
-
const minMaxText = minFiles > 0 || maxFiles < Infinity ? ` ${t("fileCountRange", state, { min: minFiles, max: maxFiles })}` : "";
|
|
4150
|
-
return countText + minMaxText;
|
|
4476
|
+
const openPicker = () => {
|
|
4477
|
+
filesPicker.click();
|
|
4151
4478
|
};
|
|
4152
|
-
const
|
|
4479
|
+
const onLibraryPick = state.config.pickExistingFiles && !element.disableLibrary ? () => {
|
|
4153
4480
|
handleLibraryPickMulti(
|
|
4154
4481
|
state,
|
|
4155
4482
|
element,
|
|
@@ -4163,30 +4490,35 @@ function renderMultipleFileElementEdit(element, ctx, wrapper, pathKey) {
|
|
|
4163
4490
|
console.error("Library pick failed:", err);
|
|
4164
4491
|
});
|
|
4165
4492
|
} : null;
|
|
4166
|
-
|
|
4493
|
+
function updateFilesDisplay() {
|
|
4167
4494
|
const currentlyReadonly = isElementReadonly(element, state);
|
|
4168
4495
|
renderResourcePills({
|
|
4169
4496
|
container: list,
|
|
4170
4497
|
rids: initialFiles,
|
|
4171
4498
|
state,
|
|
4172
|
-
onRemove: currentlyReadonly ? null : (
|
|
4173
|
-
releaseLocalFileUrl(state.resourceIndex.get(
|
|
4174
|
-
initialFiles.
|
|
4499
|
+
onRemove: currentlyReadonly ? null : (ridToRemove) => {
|
|
4500
|
+
releaseLocalFileUrl(state.resourceIndex.get(ridToRemove)?.file);
|
|
4501
|
+
const index = initialFiles.indexOf(ridToRemove);
|
|
4502
|
+
if (index > -1) initialFiles.splice(index, 1);
|
|
4175
4503
|
updateFilesDisplay();
|
|
4176
4504
|
},
|
|
4177
|
-
hint: multipleFilesHint,
|
|
4178
|
-
countInfo: buildCountInfo(),
|
|
4179
4505
|
maxCount: maxFiles < Infinity ? maxFiles : void 0,
|
|
4180
4506
|
isReadonly: currentlyReadonly,
|
|
4181
|
-
onLibraryPick: currentlyReadonly ? null :
|
|
4507
|
+
onLibraryPick: currentlyReadonly ? null : onLibraryPick,
|
|
4508
|
+
element,
|
|
4509
|
+
onClearAll: currentlyReadonly ? void 0 : () => {
|
|
4510
|
+
initialFiles.splice(0);
|
|
4511
|
+
updateFilesDisplay();
|
|
4512
|
+
},
|
|
4513
|
+
openPicker
|
|
4182
4514
|
});
|
|
4183
|
-
}
|
|
4515
|
+
}
|
|
4184
4516
|
setupFilesDropHandler(
|
|
4185
4517
|
filesContainer,
|
|
4186
4518
|
initialFiles,
|
|
4187
4519
|
state,
|
|
4188
4520
|
updateFilesDisplay,
|
|
4189
|
-
|
|
4521
|
+
constraints,
|
|
4190
4522
|
pathKey,
|
|
4191
4523
|
ctx.instance
|
|
4192
4524
|
);
|
|
@@ -4195,13 +4527,19 @@ function renderMultipleFileElementEdit(element, ctx, wrapper, pathKey) {
|
|
|
4195
4527
|
initialFiles,
|
|
4196
4528
|
state,
|
|
4197
4529
|
updateFilesDisplay,
|
|
4198
|
-
|
|
4530
|
+
constraints,
|
|
4199
4531
|
pathKey,
|
|
4200
4532
|
ctx.instance
|
|
4201
4533
|
);
|
|
4202
4534
|
updateFilesDisplay();
|
|
4203
4535
|
wrapper.appendChild(filesWrapper);
|
|
4204
4536
|
}
|
|
4537
|
+
function renderFilesElementEdit(element, ctx, wrapper, pathKey) {
|
|
4538
|
+
setupMultiFileEditMode(element, ctx, wrapper, pathKey, Infinity);
|
|
4539
|
+
}
|
|
4540
|
+
function renderMultipleFileElementEdit(element, ctx, wrapper, pathKey) {
|
|
4541
|
+
setupMultiFileEditMode(element, ctx, wrapper, pathKey, element.maxCount ?? Infinity);
|
|
4542
|
+
}
|
|
4205
4543
|
|
|
4206
4544
|
// src/components/file/validate.ts
|
|
4207
4545
|
function readMultiFileResourceIds(scopeRoot, fullKey) {
|
|
@@ -4324,33 +4662,36 @@ function renderFileElementReadonly(element, ctx, wrapper, pathKey) {
|
|
|
4324
4662
|
hiddenInput.name = pathKey;
|
|
4325
4663
|
hiddenInput.value = initial;
|
|
4326
4664
|
wrapper.appendChild(hiddenInput);
|
|
4327
|
-
renderFilePreviewReadonly(initial, state).then((
|
|
4328
|
-
|
|
4329
|
-
|
|
4330
|
-
|
|
4331
|
-
|
|
4332
|
-
|
|
4665
|
+
renderFilePreviewReadonly(initial, state).then((tile) => {
|
|
4666
|
+
tile.classList.add(
|
|
4667
|
+
"fb-single-readonly-filled",
|
|
4668
|
+
"fb-readonly-tile",
|
|
4669
|
+
"fb-checker"
|
|
4670
|
+
);
|
|
4671
|
+
wrapper.appendChild(tile);
|
|
4672
|
+
}).catch(console.error);
|
|
4333
4673
|
} else {
|
|
4334
4674
|
wrapper.appendChild(buildEmptyReadonlyTile(state));
|
|
4335
4675
|
}
|
|
4336
4676
|
}
|
|
4337
4677
|
function buildEmptyReadonlyTile(state) {
|
|
4678
|
+
ensureFileStyles();
|
|
4338
4679
|
const emptyState = document.createElement("div");
|
|
4339
4680
|
emptyState.style.cssText = `
|
|
4340
|
-
|
|
4341
|
-
height:${TILE_SIZE};
|
|
4681
|
+
height: 220px;
|
|
4342
4682
|
display:flex;
|
|
4343
4683
|
align-items:center;
|
|
4344
4684
|
justify-content:center;
|
|
4345
|
-
background:
|
|
4346
|
-
border-radius:
|
|
4347
|
-
border:1px solid
|
|
4685
|
+
background: repeating-linear-gradient(45deg, #fafafa 0 6px, #f3f4f6 6px 12px);
|
|
4686
|
+
border-radius:0.75rem;
|
|
4687
|
+
border:1px solid #e2e8f0;
|
|
4348
4688
|
`;
|
|
4349
4689
|
emptyState.innerHTML = `<div style="font-size:11px;text-align:center;color:var(--fb-text-secondary-color,#6b7280);">${escapeHtml(t("noFileSelected", state))}</div>`;
|
|
4350
4690
|
return emptyState;
|
|
4351
4691
|
}
|
|
4352
|
-
function renderMultiFileReadonly(rids, state, wrapper, pathKey,
|
|
4692
|
+
function renderMultiFileReadonly(rids, state, wrapper, pathKey, _marginTop) {
|
|
4353
4693
|
addPrefillFilesToIndex(rids, state.resourceIndex);
|
|
4694
|
+
ensureFileStyles();
|
|
4354
4695
|
const filesWrapper = document.createElement("div");
|
|
4355
4696
|
filesWrapper.dataset.filesWrapper = pathKey;
|
|
4356
4697
|
filesWrapper.dataset.resourceIds = JSON.stringify(rids);
|
|
@@ -4362,22 +4703,28 @@ function renderMultiFileReadonly(rids, state, wrapper, pathKey, marginTop) {
|
|
|
4362
4703
|
filesWrapper.appendChild(emptyEl);
|
|
4363
4704
|
return;
|
|
4364
4705
|
}
|
|
4365
|
-
const
|
|
4366
|
-
|
|
4367
|
-
filesWrapper.appendChild(
|
|
4706
|
+
const grid = document.createElement("div");
|
|
4707
|
+
grid.className = "fb-multi-readonly-grid";
|
|
4708
|
+
filesWrapper.appendChild(grid);
|
|
4368
4709
|
const placeholders = rids.map(() => {
|
|
4369
|
-
const
|
|
4370
|
-
|
|
4371
|
-
|
|
4372
|
-
return
|
|
4710
|
+
const ph = document.createElement("div");
|
|
4711
|
+
ph.className = "fb-readonly-tile fb-checker fb-tile";
|
|
4712
|
+
grid.appendChild(ph);
|
|
4713
|
+
return ph;
|
|
4373
4714
|
});
|
|
4374
4715
|
for (let i = 0; i < rids.length; i++) {
|
|
4375
4716
|
const resourceId = rids[i];
|
|
4376
4717
|
const placeholder = placeholders[i];
|
|
4377
|
-
|
|
4378
|
-
|
|
4379
|
-
|
|
4380
|
-
|
|
4718
|
+
const meta = state.resourceIndex.get(resourceId);
|
|
4719
|
+
renderFilePreviewReadonly(resourceId, state, meta?.name).then((tile) => {
|
|
4720
|
+
tile.classList.add("fb-readonly-tile", "fb-checker", "fb-tile-resource");
|
|
4721
|
+
tile.dataset.resourceId = resourceId;
|
|
4722
|
+
placeholder.replaceWith(tile);
|
|
4723
|
+
}).catch(() => {
|
|
4724
|
+
const tile = document.createElement("div");
|
|
4725
|
+
tile.className = "fb-readonly-tile fb-checker fb-tile fb-tile-resource";
|
|
4726
|
+
tile.dataset.resourceId = resourceId;
|
|
4727
|
+
placeholder.replaceWith(tile);
|
|
4381
4728
|
});
|
|
4382
4729
|
}
|
|
4383
4730
|
}
|
|
@@ -4389,7 +4736,7 @@ function renderFilesElementReadonly(element, ctx, wrapper, pathKey) {
|
|
|
4389
4736
|
function renderMultipleFileElementReadonly(element, ctx, wrapper, pathKey) {
|
|
4390
4737
|
const rawPrefill = ctx.prefill[element.key];
|
|
4391
4738
|
const initialFiles = Array.isArray(rawPrefill) ? rawPrefill : [];
|
|
4392
|
-
renderMultiFileReadonly(initialFiles, ctx.state, wrapper, pathKey
|
|
4739
|
+
renderMultiFileReadonly(initialFiles, ctx.state, wrapper, pathKey);
|
|
4393
4740
|
}
|
|
4394
4741
|
|
|
4395
4742
|
// src/components/file.ts
|
|
@@ -5527,7 +5874,7 @@ function createPrefillHints(element, pathKey) {
|
|
|
5527
5874
|
return null;
|
|
5528
5875
|
}
|
|
5529
5876
|
const hintsContainer = document.createElement("div");
|
|
5530
|
-
hintsContainer.className = "fb-prefill-hints flex flex-wrap gap-2 mb-
|
|
5877
|
+
hintsContainer.className = "fb-prefill-hints flex flex-wrap gap-2 mb-2";
|
|
5531
5878
|
element.prefillHints.forEach((hint, index) => {
|
|
5532
5879
|
const hintButton = document.createElement("button");
|
|
5533
5880
|
hintButton.type = "button";
|
|
@@ -5542,14 +5889,14 @@ function createPrefillHints(element, pathKey) {
|
|
|
5542
5889
|
}
|
|
5543
5890
|
function renderSingleContainerElement(element, ctx, wrapper, pathKey) {
|
|
5544
5891
|
const containerWrap = document.createElement("div");
|
|
5545
|
-
containerWrap.className = "border border-gray-200 rounded-lg p-
|
|
5892
|
+
containerWrap.className = "border border-gray-200 rounded-lg p-2 bg-gray-50";
|
|
5546
5893
|
containerWrap.setAttribute("data-container", pathKey);
|
|
5547
5894
|
const itemsWrap = document.createElement("div");
|
|
5548
5895
|
const columns = element.columns || 1;
|
|
5549
5896
|
if (columns === 1) {
|
|
5550
|
-
itemsWrap.className = "space-y-
|
|
5897
|
+
itemsWrap.className = "space-y-2";
|
|
5551
5898
|
} else {
|
|
5552
|
-
itemsWrap.className = `grid grid-cols-${columns} gap-
|
|
5899
|
+
itemsWrap.className = `grid grid-cols-${columns} gap-2`;
|
|
5553
5900
|
}
|
|
5554
5901
|
const containerIsReadonly = isElementReadonly(element, ctx.state, ctx);
|
|
5555
5902
|
if (!containerIsReadonly) {
|
|
@@ -5583,16 +5930,31 @@ function renderSingleContainerElement(element, ctx, wrapper, pathKey) {
|
|
|
5583
5930
|
containerWrap.appendChild(itemsWrap);
|
|
5584
5931
|
wrapper.appendChild(containerWrap);
|
|
5585
5932
|
}
|
|
5933
|
+
function getChildWrapperClass(isSlides, columns) {
|
|
5934
|
+
if (isSlides) {
|
|
5935
|
+
return "space-y-2";
|
|
5936
|
+
}
|
|
5937
|
+
const cols = columns || 1;
|
|
5938
|
+
return cols === 1 ? "space-y-2" : `grid grid-cols-${cols} gap-2`;
|
|
5939
|
+
}
|
|
5586
5940
|
function renderMultipleContainerElement(element, ctx, wrapper, _pathKey) {
|
|
5587
5941
|
const state = ctx.state;
|
|
5588
5942
|
const containerIsReadonly = isElementReadonly(element, state, ctx);
|
|
5589
5943
|
const childInheritedReadonly = containerIsReadonly || ctx.inheritedReadonly;
|
|
5590
5944
|
const containerWrap = document.createElement("div");
|
|
5591
|
-
containerWrap.className = "border border-gray-200 rounded-lg p-
|
|
5945
|
+
containerWrap.className = "border border-gray-200 rounded-lg p-2 bg-gray-50";
|
|
5592
5946
|
const countDisplay = document.createElement("span");
|
|
5593
5947
|
countDisplay.className = "text-sm text-gray-500";
|
|
5594
5948
|
const itemsWrap = document.createElement("div");
|
|
5595
|
-
|
|
5949
|
+
const isSlides = element.displayMode === "slides";
|
|
5950
|
+
if (isSlides) {
|
|
5951
|
+
itemsWrap.className = "fb-container-slides";
|
|
5952
|
+
const slideCols = element.columns;
|
|
5953
|
+
const gridTemplateColumns = typeof slideCols === "number" && slideCols > 0 ? `repeat(${slideCols}, 1fr)` : "repeat(auto-fit, minmax(280px, 1fr))";
|
|
5954
|
+
itemsWrap.style.cssText = `display:grid;grid-template-columns:${gridTemplateColumns};gap:8px;align-items:start;`;
|
|
5955
|
+
} else {
|
|
5956
|
+
itemsWrap.className = "space-y-2";
|
|
5957
|
+
}
|
|
5596
5958
|
if (!containerIsReadonly) {
|
|
5597
5959
|
const hintsElement = createPrefillHints(element, element.key);
|
|
5598
5960
|
if (hintsElement) {
|
|
@@ -5636,15 +5998,10 @@ function renderMultipleContainerElement(element, ctx, wrapper, _pathKey) {
|
|
|
5636
5998
|
inheritedReadonly: childInheritedReadonly
|
|
5637
5999
|
};
|
|
5638
6000
|
const item = document.createElement("div");
|
|
5639
|
-
item.className = "containerItem border border-gray-300 rounded-lg p-
|
|
6001
|
+
item.className = "containerItem border border-gray-300 rounded-lg p-2 bg-white";
|
|
5640
6002
|
item.setAttribute("data-container-item", `${element.key}[${idx}]`);
|
|
5641
6003
|
const childWrapper = document.createElement("div");
|
|
5642
|
-
|
|
5643
|
-
if (columns === 1) {
|
|
5644
|
-
childWrapper.className = "space-y-4";
|
|
5645
|
-
} else {
|
|
5646
|
-
childWrapper.className = `grid grid-cols-${columns} gap-4`;
|
|
5647
|
-
}
|
|
6004
|
+
childWrapper.className = getChildWrapperClass(isSlides, element.columns);
|
|
5648
6005
|
element.elements.forEach((child) => {
|
|
5649
6006
|
if (child.type !== "markdown" && (child.hidden || child.type === "hidden")) {
|
|
5650
6007
|
childWrapper.appendChild(
|
|
@@ -5713,14 +6070,18 @@ function renderMultipleContainerElement(element, ctx, wrapper, _pathKey) {
|
|
|
5713
6070
|
inheritedReadonly: childInheritedReadonly
|
|
5714
6071
|
};
|
|
5715
6072
|
const item = document.createElement("div");
|
|
5716
|
-
item.className = "containerItem border border-gray-300 rounded-lg p-
|
|
6073
|
+
item.className = "containerItem border border-gray-300 rounded-lg p-2 bg-white";
|
|
5717
6074
|
item.setAttribute("data-container-item", `${element.key}[${idx}]`);
|
|
5718
6075
|
const childWrapper = document.createElement("div");
|
|
5719
|
-
|
|
5720
|
-
|
|
5721
|
-
childWrapper.className = "space-y-4";
|
|
6076
|
+
if (isSlides) {
|
|
6077
|
+
childWrapper.className = "space-y-2";
|
|
5722
6078
|
} else {
|
|
5723
|
-
|
|
6079
|
+
const columns = element.columns || 1;
|
|
6080
|
+
if (columns === 1) {
|
|
6081
|
+
childWrapper.className = "space-y-2";
|
|
6082
|
+
} else {
|
|
6083
|
+
childWrapper.className = `grid grid-cols-${columns} gap-2`;
|
|
6084
|
+
}
|
|
5724
6085
|
}
|
|
5725
6086
|
element.elements.forEach((child) => {
|
|
5726
6087
|
if (child.type !== "markdown" && (child.hidden || child.type === "hidden")) {
|
|
@@ -5769,14 +6130,18 @@ function renderMultipleContainerElement(element, ctx, wrapper, _pathKey) {
|
|
|
5769
6130
|
inheritedReadonly: childInheritedReadonly
|
|
5770
6131
|
};
|
|
5771
6132
|
const item = document.createElement("div");
|
|
5772
|
-
item.className = "containerItem border border-gray-300 rounded-lg p-
|
|
6133
|
+
item.className = "containerItem border border-gray-300 rounded-lg p-2 bg-white";
|
|
5773
6134
|
item.setAttribute("data-container-item", `${element.key}[${idx}]`);
|
|
5774
6135
|
const childWrapper = document.createElement("div");
|
|
5775
|
-
|
|
5776
|
-
|
|
5777
|
-
childWrapper.className = "space-y-4";
|
|
6136
|
+
if (isSlides) {
|
|
6137
|
+
childWrapper.className = "space-y-2";
|
|
5778
6138
|
} else {
|
|
5779
|
-
|
|
6139
|
+
const columns = element.columns || 1;
|
|
6140
|
+
if (columns === 1) {
|
|
6141
|
+
childWrapper.className = "space-y-2";
|
|
6142
|
+
} else {
|
|
6143
|
+
childWrapper.className = `grid grid-cols-${columns} gap-2`;
|
|
6144
|
+
}
|
|
5780
6145
|
}
|
|
5781
6146
|
element.elements.forEach((child) => {
|
|
5782
6147
|
if (child.type !== "markdown" && (child.hidden || child.type === "hidden")) {
|
|
@@ -7804,7 +8169,7 @@ function filterFilesForDropdown(query, files, labels) {
|
|
|
7804
8169
|
});
|
|
7805
8170
|
}
|
|
7806
8171
|
var TEXTAREA_FONT = "font-size: var(--fb-font-size, 14px); font-family: var(--fb-font-family, inherit); line-height: 1.6;";
|
|
7807
|
-
var TEXTAREA_PADDING = "padding:
|
|
8172
|
+
var TEXTAREA_PADDING = "padding: 8px 40px 8px 10px;";
|
|
7808
8173
|
function renderEditMode(element, ctx, wrapper, pathKey, initialValue) {
|
|
7809
8174
|
const state = ctx.state;
|
|
7810
8175
|
const files = [...initialValue.files];
|
|
@@ -7851,7 +8216,7 @@ function renderEditMode(element, ctx, wrapper, pathKey, initialValue) {
|
|
|
7851
8216
|
});
|
|
7852
8217
|
const errorEl = document.createElement("div");
|
|
7853
8218
|
errorEl.className = "fb-richinput-error";
|
|
7854
|
-
errorEl.style.cssText = "display: none; color: var(--fb-error-color, #ef4444); font-size: var(--fb-font-size-small, 12px); padding: 4px
|
|
8219
|
+
errorEl.style.cssText = "display: none; color: var(--fb-error-color, #ef4444); font-size: var(--fb-font-size-small, 12px); padding: 4px 10px 6px;";
|
|
7855
8220
|
let errorTimer = null;
|
|
7856
8221
|
function showUploadError(message) {
|
|
7857
8222
|
errorEl.textContent = message;
|
|
@@ -7927,7 +8292,7 @@ function renderEditMode(element, ctx, wrapper, pathKey, initialValue) {
|
|
|
7927
8292
|
});
|
|
7928
8293
|
const filesRow = document.createElement("div");
|
|
7929
8294
|
filesRow.className = "fb-richinput-files";
|
|
7930
|
-
filesRow.style.cssText = "display: none; flex-wrap: wrap; gap:
|
|
8295
|
+
filesRow.style.cssText = "display: none; flex-wrap: wrap; gap: 4px; padding: 6px 10px 0; align-items: center;";
|
|
7931
8296
|
const fileInput = document.createElement("input");
|
|
7932
8297
|
fileInput.type = "file";
|
|
7933
8298
|
fileInput.multiple = true;
|
|
@@ -8057,13 +8422,13 @@ function renderEditMode(element, ctx, wrapper, pathKey, initialValue) {
|
|
|
8057
8422
|
paperclipBtn.title = t("richinputAttachFile", state);
|
|
8058
8423
|
paperclipBtn.style.cssText = `
|
|
8059
8424
|
position: absolute;
|
|
8060
|
-
right:
|
|
8061
|
-
bottom:
|
|
8425
|
+
right: 6px;
|
|
8426
|
+
bottom: 6px;
|
|
8062
8427
|
z-index: 2;
|
|
8063
|
-
width:
|
|
8064
|
-
height:
|
|
8428
|
+
width: 28px;
|
|
8429
|
+
height: 28px;
|
|
8065
8430
|
border: none;
|
|
8066
|
-
border-radius:
|
|
8431
|
+
border-radius: 6px;
|
|
8067
8432
|
background: transparent;
|
|
8068
8433
|
cursor: pointer;
|
|
8069
8434
|
display: flex;
|
|
@@ -8417,7 +8782,7 @@ function renderEditMode(element, ctx, wrapper, pathKey, initialValue) {
|
|
|
8417
8782
|
outerDiv.appendChild(errorEl);
|
|
8418
8783
|
if (element.minLength != null || element.maxLength != null) {
|
|
8419
8784
|
const counterRow = document.createElement("div");
|
|
8420
|
-
counterRow.style.cssText = "position: relative; padding: 2px
|
|
8785
|
+
counterRow.style.cssText = "position: relative; padding: 2px 10px 4px; text-align: right;";
|
|
8421
8786
|
const counter = createCharCounter(element, textarea, false);
|
|
8422
8787
|
counter.style.cssText = `
|
|
8423
8788
|
position: static;
|
|
@@ -9211,7 +9576,7 @@ function createInfoButton(element) {
|
|
|
9211
9576
|
}
|
|
9212
9577
|
function createLabelContainer(element) {
|
|
9213
9578
|
const label = document.createElement("div");
|
|
9214
|
-
label.className = "flex items-center mb-
|
|
9579
|
+
label.className = "flex items-center mb-1";
|
|
9215
9580
|
const title = createFieldLabel(element);
|
|
9216
9581
|
label.appendChild(title);
|
|
9217
9582
|
if (element.description || element.hint) {
|
|
@@ -9318,7 +9683,7 @@ function renderElement2(element, ctx) {
|
|
|
9318
9683
|
}
|
|
9319
9684
|
const initiallyDisabled2 = shouldDisableElement(element, ctx);
|
|
9320
9685
|
const outerWrapper = document.createElement("div");
|
|
9321
|
-
outerWrapper.className = "mb-
|
|
9686
|
+
outerWrapper.className = "mb-2 fb-field-wrapper fb-markdown-wrapper";
|
|
9322
9687
|
outerWrapper.setAttribute(
|
|
9323
9688
|
"data-field-key",
|
|
9324
9689
|
getElementLookupKey(element, ctx.state)
|
|
@@ -9334,7 +9699,7 @@ function renderElement2(element, ctx) {
|
|
|
9334
9699
|
}
|
|
9335
9700
|
const initiallyDisabled = shouldDisableElement(element, ctx);
|
|
9336
9701
|
const wrapper = document.createElement("div");
|
|
9337
|
-
wrapper.className = "mb-
|
|
9702
|
+
wrapper.className = "mb-2 fb-field-wrapper";
|
|
9338
9703
|
wrapper.setAttribute("data-field-key", element.key);
|
|
9339
9704
|
const label = createLabelContainer(element);
|
|
9340
9705
|
wrapper.appendChild(label);
|
|
@@ -9401,12 +9766,16 @@ var defaultConfig = {
|
|
|
9401
9766
|
hintPattern: "Format: {pattern}",
|
|
9402
9767
|
fileCountSingle: "{count} file",
|
|
9403
9768
|
fileCountPlural: "{count} files",
|
|
9769
|
+
fileCountWithMax: "{count} / {max} files",
|
|
9404
9770
|
fileCountRange: "({min}-{max})",
|
|
9405
9771
|
uploadingFile: "Uploading\u2026",
|
|
9406
9772
|
filesCounter: "{count}/{max}",
|
|
9407
9773
|
fromLibrary: "From library",
|
|
9408
9774
|
libraryEmpty: "Library is empty",
|
|
9409
9775
|
libraryHint: "Choose from previously uploaded files",
|
|
9776
|
+
dropToUpload: "Release to upload",
|
|
9777
|
+
replaceFile: "Replace",
|
|
9778
|
+
clearAll: "Clear all",
|
|
9410
9779
|
pickerError: "Failed to load files from library",
|
|
9411
9780
|
// Validation errors
|
|
9412
9781
|
required: "Required",
|
|
@@ -9472,12 +9841,16 @@ var defaultConfig = {
|
|
|
9472
9841
|
hintPattern: "\u0424\u043E\u0440\u043C\u0430\u0442: {pattern}",
|
|
9473
9842
|
fileCountSingle: "{count} \u0444\u0430\u0439\u043B",
|
|
9474
9843
|
fileCountPlural: "{count} \u0444\u0430\u0439\u043B\u043E\u0432",
|
|
9844
|
+
fileCountWithMax: "{count} / {max} \u0444\u0430\u0439\u043B\u043E\u0432",
|
|
9475
9845
|
fileCountRange: "({min}-{max})",
|
|
9476
9846
|
uploadingFile: "\u0417\u0430\u0433\u0440\u0443\u0437\u043A\u0430\u2026",
|
|
9477
9847
|
filesCounter: "{count}/{max}",
|
|
9478
9848
|
fromLibrary: "\u0418\u0437 \u0431\u0438\u0431\u043B\u0438\u043E\u0442\u0435\u043A\u0438",
|
|
9479
9849
|
libraryEmpty: "\u0411\u0438\u0431\u043B\u0438\u043E\u0442\u0435\u043A\u0430 \u043F\u0443\u0441\u0442\u0430",
|
|
9480
9850
|
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",
|
|
9851
|
+
dropToUpload: "\u041E\u0442\u043F\u0443\u0441\u0442\u0438\u0442\u0435, \u0447\u0442\u043E\u0431\u044B \u0437\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044C",
|
|
9852
|
+
replaceFile: "\u0417\u0430\u043C\u0435\u043D\u0438\u0442\u044C",
|
|
9853
|
+
clearAll: "\u041E\u0447\u0438\u0441\u0442\u0438\u0442\u044C \u0432\u0441\u0435",
|
|
9481
9854
|
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",
|
|
9482
9855
|
// Validation errors
|
|
9483
9856
|
required: "\u041E\u0431\u044F\u0437\u0430\u0442\u0435\u043B\u044C\u043D\u043E\u0435 \u043F\u043E\u043B\u0435",
|
|
@@ -9618,10 +9991,10 @@ var defaultTheme = {
|
|
|
9618
9991
|
fileUploadHoverBorderColor: "#3b82f6",
|
|
9619
9992
|
// blue-500
|
|
9620
9993
|
// Spacing
|
|
9621
|
-
inputPaddingX: "0.
|
|
9622
|
-
//
|
|
9623
|
-
inputPaddingY: "0.
|
|
9624
|
-
//
|
|
9994
|
+
inputPaddingX: "0.5rem",
|
|
9995
|
+
// 8px (compact density v2)
|
|
9996
|
+
inputPaddingY: "0.25rem",
|
|
9997
|
+
// 4px (compact density v2)
|
|
9625
9998
|
borderRadius: "0.5rem",
|
|
9626
9999
|
// rounded-lg (8px)
|
|
9627
10000
|
borderWidth: "1px",
|
|
@@ -10066,7 +10439,7 @@ var FormBuilderInstance = class {
|
|
|
10066
10439
|
existingContainer.remove();
|
|
10067
10440
|
}
|
|
10068
10441
|
const actionsContainer = document.createElement("div");
|
|
10069
|
-
actionsContainer.className = "form-level-actions-container mt-
|
|
10442
|
+
actionsContainer.className = "form-level-actions-container mt-3 pt-2 flex flex-wrap gap-2 justify-center";
|
|
10070
10443
|
actionsContainer.style.cssText = `
|
|
10071
10444
|
border-top: var(--fb-border-width) solid var(--fb-border-color);
|
|
10072
10445
|
`;
|
|
@@ -10207,7 +10580,7 @@ var FormBuilderInstance = class {
|
|
|
10207
10580
|
*/
|
|
10208
10581
|
createRootPrefillHints(hints) {
|
|
10209
10582
|
const hintsContainer = document.createElement("div");
|
|
10210
|
-
hintsContainer.className = "fb-prefill-hints flex flex-wrap gap-2 mb-
|
|
10583
|
+
hintsContainer.className = "fb-prefill-hints flex flex-wrap gap-2 mb-2";
|
|
10211
10584
|
hints.forEach((hint) => {
|
|
10212
10585
|
const hintButton = document.createElement("button");
|
|
10213
10586
|
hintButton.type = "button";
|
|
@@ -10240,7 +10613,7 @@ var FormBuilderInstance = class {
|
|
|
10240
10613
|
root.setAttribute("data-fb-root", "true");
|
|
10241
10614
|
injectThemeVariables(root, this.state.config.theme);
|
|
10242
10615
|
const rootContainer = document.createElement("div");
|
|
10243
|
-
rootContainer.className = "space-y-
|
|
10616
|
+
rootContainer.className = "space-y-2";
|
|
10244
10617
|
if (schema.prefillHints && !this.state.config.readonly) {
|
|
10245
10618
|
const hintsContainer = this.createRootPrefillHints(schema.prefillHints);
|
|
10246
10619
|
rootContainer.appendChild(hintsContainer);
|
|
@@ -10248,9 +10621,9 @@ var FormBuilderInstance = class {
|
|
|
10248
10621
|
const fieldsWrapper = document.createElement("div");
|
|
10249
10622
|
const columns = schema.columns || 1;
|
|
10250
10623
|
if (columns === 1) {
|
|
10251
|
-
fieldsWrapper.className = "space-y-
|
|
10624
|
+
fieldsWrapper.className = "space-y-2";
|
|
10252
10625
|
} else {
|
|
10253
|
-
fieldsWrapper.className = `grid grid-cols-${columns} gap-
|
|
10626
|
+
fieldsWrapper.className = `grid grid-cols-${columns} gap-2`;
|
|
10254
10627
|
}
|
|
10255
10628
|
schema.elements.forEach((element) => {
|
|
10256
10629
|
if (element.type !== "markdown" && (element.hidden || element.type === "hidden")) {
|