form-page-builder 2.0.0 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +53 -16
- package/dist/index.cjs +561 -193
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +94 -16
- package/dist/index.d.ts +94 -16
- package/dist/index.js +568 -207
- package/dist/index.js.map +1 -1
- package/package.json +12 -2
package/dist/index.js
CHANGED
|
@@ -1,8 +1,69 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
// src/FormBuilder.tsx
|
|
4
|
-
import { forwardRef, useImperativeHandle, useState as
|
|
5
|
-
import { Loader2 as
|
|
4
|
+
import { forwardRef, useEffect as useEffect4, useImperativeHandle, useMemo as useMemo2, useState as useState9 } from "react";
|
|
5
|
+
import { Loader2 as Loader24, Menu, SlidersHorizontal } from "lucide-react";
|
|
6
|
+
|
|
7
|
+
// src/constants/fieldTypes.ts
|
|
8
|
+
import {
|
|
9
|
+
Type,
|
|
10
|
+
AlignLeft,
|
|
11
|
+
ChevronDown,
|
|
12
|
+
Circle,
|
|
13
|
+
ListChecks,
|
|
14
|
+
CheckSquare,
|
|
15
|
+
ToggleLeft,
|
|
16
|
+
FileText,
|
|
17
|
+
Image as ImageIcon,
|
|
18
|
+
MousePointerClick
|
|
19
|
+
} from "lucide-react";
|
|
20
|
+
var FIELD_TYPES = [
|
|
21
|
+
{ type: "paragraph", icon: FileText, isContent: true, defaultIcon: "FileText" },
|
|
22
|
+
{ type: "image", icon: ImageIcon, isContent: true, isImage: true, defaultIcon: "Type" },
|
|
23
|
+
{ type: "button", icon: MousePointerClick, isContent: true, defaultIcon: "Type" },
|
|
24
|
+
{ type: "input", icon: Type, placeholder: true, hasSubtype: true, defaultIcon: "Type" },
|
|
25
|
+
{ type: "textarea", icon: AlignLeft, placeholder: true, defaultIcon: "AlignLeft" },
|
|
26
|
+
{ type: "select", icon: ChevronDown, options: true, defaultIcon: "ChevronDown" },
|
|
27
|
+
{ type: "radio", icon: Circle, options: true, defaultIcon: "Circle" },
|
|
28
|
+
{ type: "checkboxGroup", icon: ListChecks, options: true, multiValue: true, defaultIcon: "ListChecks" },
|
|
29
|
+
{ type: "checkbox", icon: CheckSquare, boolean: true, defaultIcon: "CheckSquare" },
|
|
30
|
+
{ type: "toggle", icon: ToggleLeft, boolean: true, defaultIcon: "ToggleLeft" }
|
|
31
|
+
];
|
|
32
|
+
var TYPE_MAP = Object.fromEntries(FIELD_TYPES.map((f) => [f.type, f]));
|
|
33
|
+
var CONTENT_TYPES = FIELD_TYPES.filter((f) => f.isContent);
|
|
34
|
+
var FORM_TYPES = FIELD_TYPES.filter((f) => !f.isContent);
|
|
35
|
+
function getMeta(type) {
|
|
36
|
+
return TYPE_MAP[type] || TYPE_MAP.input;
|
|
37
|
+
}
|
|
38
|
+
var LEGACY_INPUT_TYPES = ["text", "email", "phone", "number", "password", "date"];
|
|
39
|
+
var INPUT_SUBTYPES = ["text", "email", "phone", "number", "password", "date", "time"];
|
|
40
|
+
var IMAGE_SHAPES = ["square", "circle", "banner"];
|
|
41
|
+
var FIELD_TYPE_CHROME_KEY = {
|
|
42
|
+
paragraph: "fieldTypeParagraph",
|
|
43
|
+
image: "fieldTypeImage",
|
|
44
|
+
button: "fieldTypeButton",
|
|
45
|
+
input: "fieldTypeInput",
|
|
46
|
+
textarea: "fieldTypeTextarea",
|
|
47
|
+
select: "fieldTypeSelect",
|
|
48
|
+
radio: "fieldTypeRadio",
|
|
49
|
+
checkboxGroup: "fieldTypeCheckboxGroup",
|
|
50
|
+
checkbox: "fieldTypeCheckbox",
|
|
51
|
+
toggle: "fieldTypeToggle"
|
|
52
|
+
};
|
|
53
|
+
var INPUT_SUBTYPE_CHROME_KEY = {
|
|
54
|
+
text: "subtypeText",
|
|
55
|
+
email: "subtypeEmail",
|
|
56
|
+
phone: "subtypePhone",
|
|
57
|
+
number: "subtypeNumber",
|
|
58
|
+
password: "subtypePassword",
|
|
59
|
+
date: "subtypeDate",
|
|
60
|
+
time: "subtypeTime"
|
|
61
|
+
};
|
|
62
|
+
var IMAGE_SHAPE_CHROME_KEY = {
|
|
63
|
+
square: "shapeSquare",
|
|
64
|
+
circle: "shapeCircle",
|
|
65
|
+
banner: "shapeBanner"
|
|
66
|
+
};
|
|
6
67
|
|
|
7
68
|
// src/i18n/languages.ts
|
|
8
69
|
var DEFAULT_LANGUAGES = [
|
|
@@ -43,6 +104,7 @@ var DEFAULT_STRINGS = {
|
|
|
43
104
|
tooLarge: (n) => `${n}\u4EE5\u4E0B\u306E\u5024\u3092\u5165\u529B\u3057\u3066\u304F\u3060\u3055\u3044\u3002`
|
|
44
105
|
}
|
|
45
106
|
};
|
|
107
|
+
DEFAULT_STRINGS.ja;
|
|
46
108
|
|
|
47
109
|
// src/i18n/chrome.ts
|
|
48
110
|
var CHROME = {
|
|
@@ -118,16 +180,33 @@ var CHROME = {
|
|
|
118
180
|
formJson: "Form JSON",
|
|
119
181
|
copy: "Copy",
|
|
120
182
|
copied: "Copied",
|
|
183
|
+
copyTemplate: "Copy template",
|
|
184
|
+
pasteTemplate: "Paste template",
|
|
185
|
+
copyTemplateTitle: "Copy this form as a template you can paste into another builder",
|
|
186
|
+
pasteTemplateTitle: "Replace this form with the copied template",
|
|
187
|
+
pasteTemplateConfirm: "Replace the current form with the copied template? This can't be undone.",
|
|
121
188
|
noTemplates: 'No templates saved yet. Click "Save" in the toolbar to store this form here.',
|
|
189
|
+
noTemplatesReadonly: "No templates available yet.",
|
|
122
190
|
updated: "Updated",
|
|
123
191
|
open: "Open",
|
|
192
|
+
applyTemplate: "Apply",
|
|
124
193
|
saveForm: "Save as template",
|
|
125
194
|
formName: "Template name",
|
|
126
195
|
saveToLibrary: "Save template",
|
|
127
196
|
loadingDraft: "Loading your draft...",
|
|
128
|
-
templatesLimitReached:
|
|
197
|
+
templatesLimitReached: (max) => `You can save up to ${max} template${max === 1 ? "" : "s"}. Delete one to save another.`,
|
|
198
|
+
templatesLimitTitle: "Template limit reached",
|
|
199
|
+
confirm: "Confirm",
|
|
200
|
+
cancel: "Cancel",
|
|
201
|
+
dismiss: "OK",
|
|
202
|
+
confirmReplace: "Replace",
|
|
129
203
|
current: "current",
|
|
130
204
|
customColor: "Custom color",
|
|
205
|
+
templateLabel: (name) => `Template: ${name}`,
|
|
206
|
+
templateEdited: "Edited",
|
|
207
|
+
templateLoading: "Loading template...",
|
|
208
|
+
templateLoadFailed: "Couldn't load that template.",
|
|
209
|
+
templateSaveFailed: "Couldn't save the template.",
|
|
131
210
|
fieldsCount: (n) => `${n} field${n === 1 ? "" : "s"}`,
|
|
132
211
|
fieldTypeInput: "Input",
|
|
133
212
|
fieldTypeTextarea: "Textarea",
|
|
@@ -254,16 +333,33 @@ var CHROME = {
|
|
|
254
333
|
formJson: "\u30D5\u30A9\u30FC\u30E0JSON",
|
|
255
334
|
copy: "\u30B3\u30D4\u30FC",
|
|
256
335
|
copied: "\u30B3\u30D4\u30FC\u3057\u307E\u3057\u305F",
|
|
336
|
+
copyTemplate: "\u30C6\u30F3\u30D7\u30EC\u30FC\u30C8\u3092\u30B3\u30D4\u30FC",
|
|
337
|
+
pasteTemplate: "\u30C6\u30F3\u30D7\u30EC\u30FC\u30C8\u3092\u8CBC\u308A\u4ED8\u3051",
|
|
338
|
+
copyTemplateTitle: "\u3053\u306E\u30D5\u30A9\u30FC\u30E0\u3092\u30C6\u30F3\u30D7\u30EC\u30FC\u30C8\u3068\u3057\u3066\u30B3\u30D4\u30FC\u3057\u3001\u4ED6\u306E\u30D3\u30EB\u30C0\u30FC\u306B\u8CBC\u308A\u4ED8\u3051\u307E\u3059",
|
|
339
|
+
pasteTemplateTitle: "\u30B3\u30D4\u30FC\u3057\u305F\u30C6\u30F3\u30D7\u30EC\u30FC\u30C8\u3067\u3053\u306E\u30D5\u30A9\u30FC\u30E0\u3092\u7F6E\u304D\u63DB\u3048\u307E\u3059",
|
|
340
|
+
pasteTemplateConfirm: "\u73FE\u5728\u306E\u30D5\u30A9\u30FC\u30E0\u3092\u30B3\u30D4\u30FC\u3057\u305F\u30C6\u30F3\u30D7\u30EC\u30FC\u30C8\u3067\u7F6E\u304D\u63DB\u3048\u307E\u3059\u304B\uFF1F\u3053\u306E\u64CD\u4F5C\u306F\u53D6\u308A\u6D88\u305B\u307E\u305B\u3093\u3002",
|
|
257
341
|
noTemplates: "\u307E\u3060\u30C6\u30F3\u30D7\u30EC\u30FC\u30C8\u304C\u3042\u308A\u307E\u305B\u3093\u3002\u30C4\u30FC\u30EB\u30D0\u30FC\u306E\u300C\u4FDD\u5B58\u300D\u3092\u30AF\u30EA\u30C3\u30AF\u3057\u3066\u304F\u3060\u3055\u3044\u3002",
|
|
342
|
+
noTemplatesReadonly: "\u5229\u7528\u3067\u304D\u308B\u30C6\u30F3\u30D7\u30EC\u30FC\u30C8\u304C\u307E\u3060\u3042\u308A\u307E\u305B\u3093\u3002",
|
|
258
343
|
updated: "\u66F4\u65B0\u65E5\u6642",
|
|
259
344
|
open: "\u958B\u304F",
|
|
345
|
+
applyTemplate: "\u9069\u7528",
|
|
260
346
|
saveForm: "\u30C6\u30F3\u30D7\u30EC\u30FC\u30C8\u3068\u3057\u3066\u4FDD\u5B58",
|
|
261
347
|
formName: "\u30C6\u30F3\u30D7\u30EC\u30FC\u30C8\u540D",
|
|
262
348
|
saveToLibrary: "\u30C6\u30F3\u30D7\u30EC\u30FC\u30C8\u3092\u4FDD\u5B58",
|
|
263
349
|
loadingDraft: "\u4E0B\u66F8\u304D\u3092\u8AAD\u307F\u8FBC\u3093\u3067\u3044\u307E\u3059...",
|
|
264
|
-
templatesLimitReached:
|
|
350
|
+
templatesLimitReached: (max) => `\u30C6\u30F3\u30D7\u30EC\u30FC\u30C8\u306F${max}\u4EF6\u307E\u3067\u4FDD\u5B58\u3067\u304D\u307E\u3059\u30021\u4EF6\u524A\u9664\u3057\u3066\u304B\u3089\u4FDD\u5B58\u3057\u3066\u304F\u3060\u3055\u3044\u3002`,
|
|
351
|
+
templatesLimitTitle: "\u30C6\u30F3\u30D7\u30EC\u30FC\u30C8\u306E\u4E0A\u9650\u306B\u9054\u3057\u307E\u3057\u305F",
|
|
352
|
+
confirm: "\u78BA\u5B9A",
|
|
353
|
+
cancel: "\u30AD\u30E3\u30F3\u30BB\u30EB",
|
|
354
|
+
dismiss: "OK",
|
|
355
|
+
confirmReplace: "\u7F6E\u304D\u63DB\u3048\u308B",
|
|
265
356
|
current: "\u73FE\u5728\u7DE8\u96C6\u4E2D",
|
|
266
357
|
customColor: "\u30AB\u30B9\u30BF\u30E0\u30AB\u30E9\u30FC",
|
|
358
|
+
templateLabel: (name) => `\u30C6\u30F3\u30D7\u30EC\u30FC\u30C8: ${name}`,
|
|
359
|
+
templateEdited: "\u7DE8\u96C6\u6E08\u307F",
|
|
360
|
+
templateLoading: "\u30C6\u30F3\u30D7\u30EC\u30FC\u30C8\u3092\u8AAD\u307F\u8FBC\u307F\u4E2D...",
|
|
361
|
+
templateLoadFailed: "\u30C6\u30F3\u30D7\u30EC\u30FC\u30C8\u3092\u8AAD\u307F\u8FBC\u3081\u307E\u305B\u3093\u3067\u3057\u305F\u3002",
|
|
362
|
+
templateSaveFailed: "\u30C6\u30F3\u30D7\u30EC\u30FC\u30C8\u3092\u4FDD\u5B58\u3067\u304D\u307E\u305B\u3093\u3067\u3057\u305F\u3002",
|
|
267
363
|
fieldsCount: (n) => `${n}\u4EF6\u306E\u30D5\u30A3\u30FC\u30EB\u30C9`,
|
|
268
364
|
fieldTypeInput: "\u5165\u529B",
|
|
269
365
|
fieldTypeTextarea: "\u30C6\u30AD\u30B9\u30C8\u30A8\u30EA\u30A2",
|
|
@@ -319,6 +415,7 @@ var CHROME = {
|
|
|
319
415
|
spacingTicketPadding: "\u30D5\u30A3\u30FC\u30EB\u30C9\u5185\u4F59\u767D"
|
|
320
416
|
}
|
|
321
417
|
};
|
|
418
|
+
CHROME.ja;
|
|
322
419
|
|
|
323
420
|
// src/lib/bilingual.ts
|
|
324
421
|
function bi(en = "", ja = "") {
|
|
@@ -391,67 +488,6 @@ var localStorageAdapter = {
|
|
|
391
488
|
}
|
|
392
489
|
};
|
|
393
490
|
|
|
394
|
-
// src/constants/fieldTypes.ts
|
|
395
|
-
import {
|
|
396
|
-
Type,
|
|
397
|
-
AlignLeft,
|
|
398
|
-
ChevronDown,
|
|
399
|
-
Circle,
|
|
400
|
-
ListChecks,
|
|
401
|
-
CheckSquare,
|
|
402
|
-
ToggleLeft,
|
|
403
|
-
FileText,
|
|
404
|
-
Image as ImageIcon,
|
|
405
|
-
MousePointerClick
|
|
406
|
-
} from "lucide-react";
|
|
407
|
-
var FIELD_TYPES = [
|
|
408
|
-
{ type: "paragraph", icon: FileText, isContent: true, defaultIcon: "FileText" },
|
|
409
|
-
{ type: "image", icon: ImageIcon, isContent: true, isImage: true, defaultIcon: "Type" },
|
|
410
|
-
{ type: "button", icon: MousePointerClick, isContent: true, defaultIcon: "Type" },
|
|
411
|
-
{ type: "input", icon: Type, placeholder: true, hasSubtype: true, defaultIcon: "Type" },
|
|
412
|
-
{ type: "textarea", icon: AlignLeft, placeholder: true, defaultIcon: "AlignLeft" },
|
|
413
|
-
{ type: "select", icon: ChevronDown, options: true, defaultIcon: "ChevronDown" },
|
|
414
|
-
{ type: "radio", icon: Circle, options: true, defaultIcon: "Circle" },
|
|
415
|
-
{ type: "checkboxGroup", icon: ListChecks, options: true, multiValue: true, defaultIcon: "ListChecks" },
|
|
416
|
-
{ type: "checkbox", icon: CheckSquare, boolean: true, defaultIcon: "CheckSquare" },
|
|
417
|
-
{ type: "toggle", icon: ToggleLeft, boolean: true, defaultIcon: "ToggleLeft" }
|
|
418
|
-
];
|
|
419
|
-
var TYPE_MAP = Object.fromEntries(FIELD_TYPES.map((f) => [f.type, f]));
|
|
420
|
-
var CONTENT_TYPES = FIELD_TYPES.filter((f) => f.isContent);
|
|
421
|
-
var FORM_TYPES = FIELD_TYPES.filter((f) => !f.isContent);
|
|
422
|
-
function getMeta(type) {
|
|
423
|
-
return TYPE_MAP[type] || TYPE_MAP.input;
|
|
424
|
-
}
|
|
425
|
-
var LEGACY_INPUT_TYPES = ["text", "email", "phone", "number", "password", "date"];
|
|
426
|
-
var INPUT_SUBTYPES = ["text", "email", "phone", "number", "password", "date", "time"];
|
|
427
|
-
var IMAGE_SHAPES = ["square", "circle", "banner"];
|
|
428
|
-
var FIELD_TYPE_CHROME_KEY = {
|
|
429
|
-
paragraph: "fieldTypeParagraph",
|
|
430
|
-
image: "fieldTypeImage",
|
|
431
|
-
button: "fieldTypeButton",
|
|
432
|
-
input: "fieldTypeInput",
|
|
433
|
-
textarea: "fieldTypeTextarea",
|
|
434
|
-
select: "fieldTypeSelect",
|
|
435
|
-
radio: "fieldTypeRadio",
|
|
436
|
-
checkboxGroup: "fieldTypeCheckboxGroup",
|
|
437
|
-
checkbox: "fieldTypeCheckbox",
|
|
438
|
-
toggle: "fieldTypeToggle"
|
|
439
|
-
};
|
|
440
|
-
var INPUT_SUBTYPE_CHROME_KEY = {
|
|
441
|
-
text: "subtypeText",
|
|
442
|
-
email: "subtypeEmail",
|
|
443
|
-
phone: "subtypePhone",
|
|
444
|
-
number: "subtypeNumber",
|
|
445
|
-
password: "subtypePassword",
|
|
446
|
-
date: "subtypeDate",
|
|
447
|
-
time: "subtypeTime"
|
|
448
|
-
};
|
|
449
|
-
var IMAGE_SHAPE_CHROME_KEY = {
|
|
450
|
-
square: "shapeSquare",
|
|
451
|
-
circle: "shapeCircle",
|
|
452
|
-
banner: "shapeBanner"
|
|
453
|
-
};
|
|
454
|
-
|
|
455
491
|
// src/lib/id.ts
|
|
456
492
|
var genFormId = () => `form_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 7)}`;
|
|
457
493
|
var genSectionId = () => `section_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 7)}`;
|
|
@@ -615,10 +651,32 @@ function migrateDocument(raw) {
|
|
|
615
651
|
return { title, themeOverrides, sections };
|
|
616
652
|
}
|
|
617
653
|
|
|
654
|
+
// src/lib/template.ts
|
|
655
|
+
var TEMPLATE_FORMAT = 1;
|
|
656
|
+
function serializeTemplate(document2) {
|
|
657
|
+
const envelope = { __fpb: "template", v: TEMPLATE_FORMAT, document: document2 };
|
|
658
|
+
return JSON.stringify(envelope);
|
|
659
|
+
}
|
|
660
|
+
function parseTemplate(raw) {
|
|
661
|
+
if (!raw) return null;
|
|
662
|
+
let parsed;
|
|
663
|
+
try {
|
|
664
|
+
parsed = JSON.parse(raw);
|
|
665
|
+
} catch {
|
|
666
|
+
return null;
|
|
667
|
+
}
|
|
668
|
+
if (!parsed || typeof parsed !== "object") return null;
|
|
669
|
+
const obj = parsed;
|
|
670
|
+
const candidate = obj.__fpb === "template" && obj.document ? obj.document : obj;
|
|
671
|
+
return migrateDocument(candidate);
|
|
672
|
+
}
|
|
673
|
+
|
|
618
674
|
// src/lib/features.ts
|
|
675
|
+
var DEFAULT_MAX_TEMPLATES = 5;
|
|
619
676
|
var DEFAULT_FEATURES = {
|
|
620
677
|
naming: true,
|
|
621
|
-
templates: true,
|
|
678
|
+
templates: { enabled: true, manage: true, max: DEFAULT_MAX_TEMPLATES },
|
|
679
|
+
templateClipboard: true,
|
|
622
680
|
newForm: true,
|
|
623
681
|
autosave: true,
|
|
624
682
|
jsonView: true,
|
|
@@ -629,14 +687,24 @@ var DEFAULT_FEATURES = {
|
|
|
629
687
|
contentBlocks: true,
|
|
630
688
|
fieldTypes: true,
|
|
631
689
|
sections: true,
|
|
632
|
-
|
|
690
|
+
sectionBackground: true,
|
|
691
|
+
dragReorder: true,
|
|
692
|
+
deviceToggle: true
|
|
633
693
|
};
|
|
694
|
+
function resolveTemplates(t2) {
|
|
695
|
+
if (t2 === false) return { enabled: false, manage: false, max: DEFAULT_MAX_TEMPLATES };
|
|
696
|
+
if (t2 === void 0 || t2 === true) return { enabled: true, manage: true, max: DEFAULT_MAX_TEMPLATES };
|
|
697
|
+
return { enabled: true, manage: t2.manage ?? true, max: t2.max ?? DEFAULT_MAX_TEMPLATES };
|
|
698
|
+
}
|
|
634
699
|
function resolveFeatures(features) {
|
|
635
700
|
return {
|
|
636
701
|
...DEFAULT_FEATURES,
|
|
637
702
|
...features,
|
|
703
|
+
templates: resolveTemplates(features?.templates),
|
|
638
704
|
contentBlocks: Array.isArray(features?.contentBlocks) ? new Set(features.contentBlocks) : features?.contentBlocks ?? DEFAULT_FEATURES.contentBlocks,
|
|
639
|
-
fieldTypes: Array.isArray(features?.fieldTypes) ? new Set(features.fieldTypes) : features?.fieldTypes ?? DEFAULT_FEATURES.fieldTypes
|
|
705
|
+
fieldTypes: Array.isArray(features?.fieldTypes) ? new Set(features.fieldTypes) : features?.fieldTypes ?? DEFAULT_FEATURES.fieldTypes,
|
|
706
|
+
// Unset → follow `sections`, so an existing `sections: false` still hides the picker.
|
|
707
|
+
sectionBackground: features?.sectionBackground ?? features?.sections ?? DEFAULT_FEATURES.sectionBackground
|
|
640
708
|
};
|
|
641
709
|
}
|
|
642
710
|
function isContentBlockEnabled(features, type) {
|
|
@@ -647,7 +715,7 @@ function isFieldTypeEnabled(features, type) {
|
|
|
647
715
|
}
|
|
648
716
|
|
|
649
717
|
// src/hooks/useTheme.ts
|
|
650
|
-
import { useState } from "react";
|
|
718
|
+
import { useMemo, useState } from "react";
|
|
651
719
|
|
|
652
720
|
// src/theme/defaultTheme.ts
|
|
653
721
|
var DEFAULT_THEME = {
|
|
@@ -666,17 +734,17 @@ var DEFAULT_THEME = {
|
|
|
666
734
|
|
|
667
735
|
// src/hooks/useTheme.ts
|
|
668
736
|
function useTheme(themeOverrideProp) {
|
|
669
|
-
const
|
|
737
|
+
const [themeOverrides, setThemeOverrides] = useState({});
|
|
738
|
+
const baseTheme = useMemo(() => ({
|
|
670
739
|
...DEFAULT_THEME,
|
|
671
740
|
...themeOverrideProp || {},
|
|
672
741
|
layout: { ...DEFAULT_THEME.layout, ...themeOverrideProp && themeOverrideProp.layout || {} }
|
|
673
|
-
};
|
|
674
|
-
const
|
|
675
|
-
const theme = {
|
|
742
|
+
}), [themeOverrideProp]);
|
|
743
|
+
const theme = useMemo(() => ({
|
|
676
744
|
...baseTheme,
|
|
677
745
|
...themeOverrides,
|
|
678
746
|
layout: { ...baseTheme.layout, ...themeOverrides.layout || {} }
|
|
679
|
-
};
|
|
747
|
+
}), [baseTheme, themeOverrides]);
|
|
680
748
|
function updateThemeColor(key, value) {
|
|
681
749
|
setThemeOverrides((prev) => ({ ...prev, [key]: value }));
|
|
682
750
|
}
|
|
@@ -875,35 +943,49 @@ import { useEffect, useRef, useState as useState3 } from "react";
|
|
|
875
943
|
var DRAFT_KEY = "form-page-builder:draft";
|
|
876
944
|
var INDEX_KEY = "form-page-builder:index";
|
|
877
945
|
var formKey = (id) => `form-page-builder:saved:${id}`;
|
|
946
|
+
var savedFormId = (key) => key.startsWith("form-page-builder:saved:") ? key.slice("form-page-builder:saved:".length) : null;
|
|
947
|
+
var CLIPBOARD_KEY = "form-page-builder:clipboard";
|
|
878
948
|
|
|
879
949
|
// src/hooks/usePersistence.ts
|
|
880
|
-
|
|
950
|
+
function snapshotOf(d) {
|
|
951
|
+
return JSON.stringify({ title: d.title, themeOverrides: d.themeOverrides, sections: d.sections });
|
|
952
|
+
}
|
|
881
953
|
function usePersistence({
|
|
882
954
|
storage,
|
|
883
955
|
autosave,
|
|
956
|
+
templateMax,
|
|
957
|
+
templateManage,
|
|
884
958
|
language,
|
|
885
959
|
chrome,
|
|
886
|
-
document,
|
|
960
|
+
document: document2,
|
|
887
961
|
initialDocument,
|
|
888
962
|
onLoadDocument,
|
|
889
963
|
onLoadThemeOverrides,
|
|
890
964
|
onTitleChange,
|
|
891
965
|
onNewForm,
|
|
966
|
+
onTemplateChange,
|
|
892
967
|
ensureActiveSection
|
|
893
968
|
}) {
|
|
894
969
|
const [currentFormId, setCurrentFormId] = useState3(null);
|
|
895
970
|
const [loadingDraft, setLoadingDraft] = useState3(true);
|
|
896
971
|
const [saveState, setSaveState] = useState3("idle");
|
|
972
|
+
const [templateState, setTemplateState] = useState3("idle");
|
|
897
973
|
const [savedForms, setSavedForms] = useState3([]);
|
|
898
974
|
const [saveAsPrompt, setSaveAsPrompt] = useState3(null);
|
|
975
|
+
const [notice, setNotice] = useState3(null);
|
|
899
976
|
const autosaveTimer = useRef(null);
|
|
900
977
|
const hasLoadedOnce = useRef(false);
|
|
978
|
+
const savedSnapshot = useRef(null);
|
|
979
|
+
function flashSaved() {
|
|
980
|
+
setTemplateState("saved");
|
|
981
|
+
setTimeout(() => setTemplateState((s) => s === "saved" ? "idle" : s), 1500);
|
|
982
|
+
}
|
|
901
983
|
async function refreshLibrary() {
|
|
902
984
|
try {
|
|
903
985
|
const raw = await storage.get(INDEX_KEY);
|
|
904
986
|
const list = raw ? JSON.parse(raw) : [];
|
|
905
987
|
setSavedForms(list.sort((a, b) => b.updatedAt - a.updatedAt));
|
|
906
|
-
} catch
|
|
988
|
+
} catch {
|
|
907
989
|
setSavedForms([]);
|
|
908
990
|
}
|
|
909
991
|
}
|
|
@@ -927,12 +1009,21 @@ function usePersistence({
|
|
|
927
1009
|
onLoadDocument(doc);
|
|
928
1010
|
onLoadThemeOverrides(doc.themeOverrides);
|
|
929
1011
|
}
|
|
930
|
-
if (parsed.currentFormId)
|
|
1012
|
+
if (parsed.currentFormId && templateManage) {
|
|
1013
|
+
setCurrentFormId(parsed.currentFormId);
|
|
1014
|
+
try {
|
|
1015
|
+
const tplRaw = await storage.get(formKey(parsed.currentFormId));
|
|
1016
|
+
const tplDoc = tplRaw ? migrateDocument(JSON.parse(tplRaw)) : null;
|
|
1017
|
+
savedSnapshot.current = tplDoc ? snapshotOf(tplDoc) : null;
|
|
1018
|
+
} catch {
|
|
1019
|
+
savedSnapshot.current = null;
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
931
1022
|
} else {
|
|
932
1023
|
ensureActiveSection();
|
|
933
1024
|
}
|
|
934
1025
|
}
|
|
935
|
-
} catch
|
|
1026
|
+
} catch {
|
|
936
1027
|
ensureActiveSection();
|
|
937
1028
|
} finally {
|
|
938
1029
|
setLoadingDraft(false);
|
|
@@ -947,86 +1038,125 @@ function usePersistence({
|
|
|
947
1038
|
if (autosaveTimer.current) clearTimeout(autosaveTimer.current);
|
|
948
1039
|
autosaveTimer.current = setTimeout(async () => {
|
|
949
1040
|
try {
|
|
950
|
-
await storage.set(DRAFT_KEY, JSON.stringify({ ...
|
|
1041
|
+
await storage.set(DRAFT_KEY, JSON.stringify({ ...document2, currentFormId }));
|
|
951
1042
|
setSaveState("saved");
|
|
952
|
-
} catch
|
|
1043
|
+
} catch {
|
|
953
1044
|
setSaveState("error");
|
|
954
1045
|
}
|
|
955
1046
|
}, 600);
|
|
956
1047
|
return () => {
|
|
957
1048
|
if (autosaveTimer.current) clearTimeout(autosaveTimer.current);
|
|
958
1049
|
};
|
|
959
|
-
}, [
|
|
1050
|
+
}, [document2.title, document2.themeOverrides, document2.sections, currentFormId]);
|
|
960
1051
|
async function saveAs(name) {
|
|
961
|
-
if (
|
|
962
|
-
|
|
1052
|
+
if (!templateManage) return;
|
|
1053
|
+
if (savedForms.length >= templateMax) {
|
|
1054
|
+
setSaveAsPrompt(null);
|
|
1055
|
+
setNotice({ title: chrome.templatesLimitTitle, message: chrome.templatesLimitReached(templateMax) });
|
|
963
1056
|
return;
|
|
964
1057
|
}
|
|
965
1058
|
const id = genFormId();
|
|
966
1059
|
const now = Date.now();
|
|
1060
|
+
setTemplateState("saving");
|
|
967
1061
|
try {
|
|
968
1062
|
const newTitle = bi(name, "");
|
|
969
|
-
|
|
1063
|
+
const docToSave = { ...document2, title: newTitle };
|
|
1064
|
+
await storage.set(formKey(id), JSON.stringify({ ...docToSave, id, updatedAt: now }));
|
|
970
1065
|
const next = [...savedForms, { id, title: name, updatedAt: now }];
|
|
971
1066
|
await storage.set(INDEX_KEY, JSON.stringify(next));
|
|
972
1067
|
setSavedForms(next);
|
|
973
1068
|
setCurrentFormId(id);
|
|
974
1069
|
onTitleChange(newTitle);
|
|
975
1070
|
setSaveAsPrompt(null);
|
|
976
|
-
|
|
977
|
-
|
|
1071
|
+
savedSnapshot.current = snapshotOf(docToSave);
|
|
1072
|
+
flashSaved();
|
|
1073
|
+
onTemplateChange?.({ id, title: name, source: "new" });
|
|
1074
|
+
} catch {
|
|
1075
|
+
setTemplateState("error");
|
|
978
1076
|
}
|
|
979
1077
|
}
|
|
980
1078
|
async function saveExisting() {
|
|
1079
|
+
if (!templateManage) return;
|
|
981
1080
|
if (!currentFormId) {
|
|
982
|
-
setSaveAsPrompt({ open: true, suggestedName: t(
|
|
1081
|
+
setSaveAsPrompt({ open: true, suggestedName: t(document2.title, language) });
|
|
983
1082
|
return;
|
|
984
1083
|
}
|
|
985
1084
|
const now = Date.now();
|
|
1085
|
+
setTemplateState("saving");
|
|
986
1086
|
try {
|
|
987
|
-
await storage.set(formKey(currentFormId), JSON.stringify({ ...
|
|
988
|
-
const next = savedForms.map((f) => f.id === currentFormId ? { ...f, title: t(
|
|
1087
|
+
await storage.set(formKey(currentFormId), JSON.stringify({ ...document2, id: currentFormId, updatedAt: now }));
|
|
1088
|
+
const next = savedForms.map((f) => f.id === currentFormId ? { ...f, title: t(document2.title, "en"), updatedAt: now } : f);
|
|
989
1089
|
setSavedForms(next);
|
|
990
1090
|
await storage.set(INDEX_KEY, JSON.stringify(next));
|
|
991
|
-
|
|
992
|
-
|
|
1091
|
+
savedSnapshot.current = snapshotOf(document2);
|
|
1092
|
+
flashSaved();
|
|
1093
|
+
onTemplateChange?.({ id: currentFormId, title: t(document2.title, language), source: "saved" });
|
|
1094
|
+
} catch {
|
|
1095
|
+
setTemplateState("error");
|
|
993
1096
|
}
|
|
994
1097
|
}
|
|
995
1098
|
async function loadForm(id) {
|
|
1099
|
+
setTemplateState("loading");
|
|
996
1100
|
try {
|
|
997
1101
|
const raw = await storage.get(formKey(id));
|
|
998
|
-
if (!raw)
|
|
1102
|
+
if (!raw) {
|
|
1103
|
+
setTemplateState("error");
|
|
1104
|
+
return false;
|
|
1105
|
+
}
|
|
999
1106
|
const doc = migrateDocument(JSON.parse(raw));
|
|
1000
|
-
if (!doc)
|
|
1107
|
+
if (!doc) {
|
|
1108
|
+
setTemplateState("error");
|
|
1109
|
+
return false;
|
|
1110
|
+
}
|
|
1001
1111
|
onLoadDocument(doc);
|
|
1002
1112
|
onLoadThemeOverrides(doc.themeOverrides);
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1113
|
+
const boundId = templateManage ? id : null;
|
|
1114
|
+
setCurrentFormId(boundId);
|
|
1115
|
+
savedSnapshot.current = boundId ? snapshotOf(doc) : null;
|
|
1116
|
+
setTemplateState("idle");
|
|
1117
|
+
onTemplateChange?.({ id, title: t(doc.title, language), source: "applied" });
|
|
1118
|
+
return true;
|
|
1119
|
+
} catch {
|
|
1120
|
+
setTemplateState("error");
|
|
1121
|
+
return false;
|
|
1006
1122
|
}
|
|
1007
1123
|
}
|
|
1008
1124
|
async function deleteForm(id) {
|
|
1125
|
+
if (!templateManage) return;
|
|
1126
|
+
const removed = savedForms.find((f) => f.id === id);
|
|
1009
1127
|
try {
|
|
1010
1128
|
await storage.delete(formKey(id));
|
|
1011
1129
|
const next = savedForms.filter((f) => f.id !== id);
|
|
1012
1130
|
setSavedForms(next);
|
|
1013
1131
|
await storage.set(INDEX_KEY, JSON.stringify(next));
|
|
1014
|
-
if (currentFormId === id)
|
|
1015
|
-
|
|
1132
|
+
if (currentFormId === id) {
|
|
1133
|
+
setCurrentFormId(null);
|
|
1134
|
+
savedSnapshot.current = null;
|
|
1135
|
+
}
|
|
1136
|
+
onTemplateChange?.({ id: currentFormId === id ? null : id, title: removed?.title ?? "", source: "deleted" });
|
|
1137
|
+
} catch {
|
|
1138
|
+
setTemplateState("error");
|
|
1016
1139
|
}
|
|
1017
1140
|
}
|
|
1018
1141
|
function newForm() {
|
|
1019
1142
|
onNewForm();
|
|
1020
1143
|
setCurrentFormId(null);
|
|
1144
|
+
savedSnapshot.current = null;
|
|
1145
|
+
setTemplateState("idle");
|
|
1021
1146
|
}
|
|
1147
|
+
const activeTemplate = currentFormId ? savedForms.find((f) => f.id === currentFormId) ?? null : null;
|
|
1148
|
+
const isTemplateDirty = savedSnapshot.current !== null && snapshotOf(document2) !== savedSnapshot.current;
|
|
1022
1149
|
function dismissSaveAsPrompt() {
|
|
1023
1150
|
setSaveAsPrompt(null);
|
|
1024
1151
|
}
|
|
1025
1152
|
return {
|
|
1026
1153
|
loadingDraft,
|
|
1027
1154
|
saveState,
|
|
1155
|
+
templateState,
|
|
1028
1156
|
savedForms,
|
|
1029
1157
|
currentFormId,
|
|
1158
|
+
activeTemplateTitle: activeTemplate?.title ?? null,
|
|
1159
|
+
isTemplateDirty,
|
|
1030
1160
|
saveAs,
|
|
1031
1161
|
saveExisting,
|
|
1032
1162
|
loadForm,
|
|
@@ -1034,15 +1164,64 @@ function usePersistence({
|
|
|
1034
1164
|
refreshLibrary,
|
|
1035
1165
|
newForm,
|
|
1036
1166
|
saveAsPrompt,
|
|
1037
|
-
dismissSaveAsPrompt
|
|
1167
|
+
dismissSaveAsPrompt,
|
|
1168
|
+
notice,
|
|
1169
|
+
dismissNotice: () => setNotice(null)
|
|
1038
1170
|
};
|
|
1039
1171
|
}
|
|
1040
1172
|
|
|
1173
|
+
// src/hooks/useTemplateClipboard.ts
|
|
1174
|
+
import { useCallback, useEffect as useEffect2, useRef as useRef2, useState as useState4 } from "react";
|
|
1175
|
+
var sameTabListeners = /* @__PURE__ */ new Set();
|
|
1176
|
+
function notifySameTab() {
|
|
1177
|
+
sameTabListeners.forEach((fn) => fn());
|
|
1178
|
+
}
|
|
1179
|
+
function readKey(key) {
|
|
1180
|
+
try {
|
|
1181
|
+
return typeof window !== "undefined" && window.localStorage ? window.localStorage.getItem(key) : null;
|
|
1182
|
+
} catch {
|
|
1183
|
+
return null;
|
|
1184
|
+
}
|
|
1185
|
+
}
|
|
1186
|
+
function useTemplateClipboard(clipboardKey = CLIPBOARD_KEY) {
|
|
1187
|
+
const keyRef = useRef2(clipboardKey);
|
|
1188
|
+
keyRef.current = clipboardKey;
|
|
1189
|
+
const [hasClipboard, setHasClipboard] = useState4(false);
|
|
1190
|
+
const refresh = useCallback(() => {
|
|
1191
|
+
setHasClipboard(parseTemplate(readKey(keyRef.current)) !== null);
|
|
1192
|
+
}, []);
|
|
1193
|
+
useEffect2(() => {
|
|
1194
|
+
refresh();
|
|
1195
|
+
const onStorage = (e) => {
|
|
1196
|
+
if (e.key === null || e.key === keyRef.current) refresh();
|
|
1197
|
+
};
|
|
1198
|
+
sameTabListeners.add(refresh);
|
|
1199
|
+
window.addEventListener("storage", onStorage);
|
|
1200
|
+
return () => {
|
|
1201
|
+
sameTabListeners.delete(refresh);
|
|
1202
|
+
window.removeEventListener("storage", onStorage);
|
|
1203
|
+
};
|
|
1204
|
+
}, [refresh]);
|
|
1205
|
+
const copyTemplate = useCallback((document2) => {
|
|
1206
|
+
const str = serializeTemplate(document2);
|
|
1207
|
+
try {
|
|
1208
|
+
window.localStorage?.setItem(keyRef.current, str);
|
|
1209
|
+
} catch {
|
|
1210
|
+
}
|
|
1211
|
+
notifySameTab();
|
|
1212
|
+
refresh();
|
|
1213
|
+
}, [refresh]);
|
|
1214
|
+
const readTemplate = useCallback(() => {
|
|
1215
|
+
return parseTemplate(readKey(keyRef.current));
|
|
1216
|
+
}, []);
|
|
1217
|
+
return { hasClipboard, copyTemplate, readTemplate };
|
|
1218
|
+
}
|
|
1219
|
+
|
|
1041
1220
|
// src/hooks/useDragReorder.ts
|
|
1042
|
-
import { useRef as
|
|
1221
|
+
import { useRef as useRef3, useState as useState5 } from "react";
|
|
1043
1222
|
function useDragReorder(reorderWithinSection) {
|
|
1044
|
-
const dragRef =
|
|
1045
|
-
const [dragOverKey, setDragOverKey] =
|
|
1223
|
+
const dragRef = useRef3({ sectionId: null, index: null });
|
|
1224
|
+
const [dragOverKey, setDragOverKey] = useState5(null);
|
|
1046
1225
|
function getDropZoneHandlers(sectionId, index) {
|
|
1047
1226
|
const dragKey = `${sectionId}:${index}`;
|
|
1048
1227
|
return {
|
|
@@ -1070,13 +1249,13 @@ function useDragReorder(reorderWithinSection) {
|
|
|
1070
1249
|
}
|
|
1071
1250
|
|
|
1072
1251
|
// src/components/Toolbar.tsx
|
|
1073
|
-
import { Loader2, AlertCircle, Pencil, Eye, FilePlus2, FolderOpen, Save, Code2 } from "lucide-react";
|
|
1252
|
+
import { Loader2, AlertCircle, Pencil, Eye, FilePlus2, FolderOpen, Save, Code2, ClipboardCopy, ClipboardPaste, Check } from "lucide-react";
|
|
1074
1253
|
|
|
1075
1254
|
// src/styles/styles.ts
|
|
1076
1255
|
var styles = {
|
|
1077
1256
|
app: { fontFamily: "'Inter', -apple-system, BlinkMacSystemFont, sans-serif", background: "var(--fb-canvas)", borderRadius: 12, overflow: "hidden", border: "1px solid var(--fb-border)", color: "var(--fb-ink)", display: "flex", flexDirection: "column" },
|
|
1078
1257
|
toolbar: { display: "flex", alignItems: "center", justifyContent: "space-between", padding: "var(--fb-space-toolbar) 16px", borderBottom: "1px solid var(--fb-border)", background: "var(--fb-surface)", flexWrap: "wrap", gap: 8, position: "relative" },
|
|
1079
|
-
toolbarLeft: { display: "flex", alignItems: "center", gap: 10 },
|
|
1258
|
+
toolbarLeft: { display: "flex", alignItems: "center", gap: 10, minWidth: 0, flexShrink: 1 },
|
|
1080
1259
|
logoMark: { width: 26, height: 26, borderRadius: 7, background: "var(--fb-primary)", color: "#fff", fontSize: 11, fontWeight: 700, display: "flex", alignItems: "center", justifyContent: "center", fontFamily: "'JetBrains Mono', monospace", letterSpacing: "-0.5px" },
|
|
1081
1260
|
titleInput: { border: "none", outline: "none", fontSize: 14, fontWeight: 600, background: "transparent", color: "var(--fb-ink)", padding: "4px 6px", borderRadius: 6, minWidth: 160 },
|
|
1082
1261
|
toolbarRight: { display: "flex", alignItems: "center", gap: 6, flexWrap: "wrap" },
|
|
@@ -1086,6 +1265,8 @@ var styles = {
|
|
|
1086
1265
|
ghostBtn: { display: "flex", alignItems: "center", gap: 6, padding: "6px 12px", borderRadius: 7, border: "1px solid var(--fb-border)", background: "var(--fb-surface)", color: "var(--fb-ink)", fontSize: 13, fontWeight: 500 },
|
|
1087
1266
|
primaryBtn: { display: "flex", alignItems: "center", gap: 6, padding: "6px 13px", borderRadius: 7, border: "1px solid var(--fb-primary)", background: "var(--fb-primary)", color: "#fff", fontSize: 13, fontWeight: 600 },
|
|
1088
1267
|
saveStatus: { display: "flex", alignItems: "center", gap: 5, fontSize: 12, color: "var(--fb-muted)", minWidth: 60 },
|
|
1268
|
+
templateTag: { display: "flex", alignItems: "center", gap: 6, fontSize: 12, color: "var(--fb-muted)", minWidth: 0, flexShrink: 1, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" },
|
|
1269
|
+
toolbarIconBtn: { display: "flex", alignItems: "center", justifyContent: "center", padding: "6px 9px", borderRadius: 7, border: "1px solid var(--fb-border)", background: "var(--fb-surface)", color: "var(--fb-ink)" },
|
|
1089
1270
|
countBadge: { fontSize: 10.5, fontWeight: 700, background: "var(--fb-primary-soft)", color: "var(--fb-primary)", borderRadius: 9, padding: "1px 6px", marginLeft: 2 },
|
|
1090
1271
|
loadingScreen: { flex: 1, display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", color: "var(--fb-muted)" },
|
|
1091
1272
|
libraryBody: { padding: "var(--fb-space-panel)", overflowY: "auto", display: "flex", flexDirection: "column", gap: 4 },
|
|
@@ -1222,21 +1403,36 @@ function Toolbar({
|
|
|
1222
1403
|
languages,
|
|
1223
1404
|
mode,
|
|
1224
1405
|
saveState,
|
|
1406
|
+
templateState,
|
|
1407
|
+
activeTemplateTitle,
|
|
1408
|
+
templateDirty,
|
|
1225
1409
|
chrome,
|
|
1226
1410
|
features,
|
|
1227
1411
|
savedFormsCount,
|
|
1412
|
+
clipboardReady,
|
|
1413
|
+
templateCopied,
|
|
1228
1414
|
onTitleChange,
|
|
1229
1415
|
onLanguageChange,
|
|
1230
1416
|
onModeChange,
|
|
1231
1417
|
onNewForm,
|
|
1232
1418
|
onOpenLibrary,
|
|
1233
1419
|
onSaveExisting,
|
|
1234
|
-
onOpenJson
|
|
1420
|
+
onOpenJson,
|
|
1421
|
+
onCopyTemplate,
|
|
1422
|
+
onPasteTemplate
|
|
1235
1423
|
}) {
|
|
1236
1424
|
return /* @__PURE__ */ jsxs2("div", { style: styles.toolbar, children: [
|
|
1237
1425
|
/* @__PURE__ */ jsxs2("div", { style: styles.toolbarLeft, children: [
|
|
1238
1426
|
/* @__PURE__ */ jsx2("div", { style: styles.logoMark, children: "FB" }),
|
|
1239
|
-
features.naming ? /* @__PURE__ */ jsx2("input", { value: t(title, language), onChange: (e) => onTitleChange(e.target.value), style: styles.titleInput, "aria-label": "Form title" }) : /* @__PURE__ */ jsx2("span", { style: styles.titleInput, children: t(title, language) })
|
|
1427
|
+
features.naming ? /* @__PURE__ */ jsx2("input", { value: t(title, language), onChange: (e) => onTitleChange(e.target.value), style: styles.titleInput, "aria-label": "Form title" }) : /* @__PURE__ */ jsx2("span", { style: styles.titleInput, children: t(title, language) }),
|
|
1428
|
+
templateState === "loading" ? /* @__PURE__ */ jsxs2("span", { style: styles.saveStatus, children: [
|
|
1429
|
+
/* @__PURE__ */ jsx2(Loader2, { size: 12, className: "spin" }),
|
|
1430
|
+
" ",
|
|
1431
|
+
chrome.templateLoading
|
|
1432
|
+
] }) : activeTemplateTitle && /* @__PURE__ */ jsxs2("span", { className: "fb-template-tag", style: styles.templateTag, title: chrome.templateLabel(activeTemplateTitle), children: [
|
|
1433
|
+
/* @__PURE__ */ jsx2("span", { style: { overflow: "hidden", textOverflow: "ellipsis" }, children: chrome.templateLabel(activeTemplateTitle) }),
|
|
1434
|
+
templateDirty && /* @__PURE__ */ jsx2("span", { style: styles.currentBadge, children: chrome.templateEdited })
|
|
1435
|
+
] })
|
|
1240
1436
|
] }),
|
|
1241
1437
|
/* @__PURE__ */ jsxs2("div", { style: styles.toolbarRight, children: [
|
|
1242
1438
|
/* @__PURE__ */ jsxs2("span", { style: styles.saveStatus, children: [
|
|
@@ -1269,20 +1465,20 @@ function Toolbar({
|
|
|
1269
1465
|
chrome.preview
|
|
1270
1466
|
] })
|
|
1271
1467
|
] }),
|
|
1272
|
-
(features.newForm || features.templates) && /* @__PURE__ */ jsx2("div", { style: styles.toolbarDivider }),
|
|
1468
|
+
(features.newForm || features.templates.enabled) && /* @__PURE__ */ jsx2("div", { style: styles.toolbarDivider }),
|
|
1273
1469
|
features.newForm && /* @__PURE__ */ jsxs2("button", { style: styles.ghostBtn, onClick: onNewForm, title: chrome.startNewForm, children: [
|
|
1274
1470
|
/* @__PURE__ */ jsx2(FilePlus2, { size: 14 }),
|
|
1275
1471
|
" ",
|
|
1276
1472
|
chrome.newForm
|
|
1277
1473
|
] }),
|
|
1278
|
-
features.templates && /* @__PURE__ */ jsxs2(Fragment, { children: [
|
|
1474
|
+
features.templates.enabled && /* @__PURE__ */ jsxs2(Fragment, { children: [
|
|
1279
1475
|
/* @__PURE__ */ jsxs2("button", { style: styles.ghostBtn, onClick: onOpenLibrary, title: chrome.openTemplatesTitle, children: [
|
|
1280
1476
|
/* @__PURE__ */ jsx2(FolderOpen, { size: 14 }),
|
|
1281
1477
|
" ",
|
|
1282
1478
|
chrome.templates,
|
|
1283
1479
|
savedFormsCount > 0 && /* @__PURE__ */ jsx2("span", { style: styles.countBadge, children: savedFormsCount })
|
|
1284
1480
|
] }),
|
|
1285
|
-
/* @__PURE__ */ jsxs2("button", { style: styles.primaryBtn, onClick: onSaveExisting, title: chrome.saveToLibraryTitle, children: [
|
|
1481
|
+
features.templates.manage && /* @__PURE__ */ jsxs2("button", { style: styles.primaryBtn, onClick: onSaveExisting, title: chrome.saveToLibraryTitle, children: [
|
|
1286
1482
|
/* @__PURE__ */ jsx2(Save, { size: 14 }),
|
|
1287
1483
|
" ",
|
|
1288
1484
|
chrome.save
|
|
@@ -1292,13 +1488,17 @@ function Toolbar({
|
|
|
1292
1488
|
/* @__PURE__ */ jsx2(Code2, { size: 14 }),
|
|
1293
1489
|
" ",
|
|
1294
1490
|
chrome.viewJson
|
|
1491
|
+
] }),
|
|
1492
|
+
features.templateClipboard && /* @__PURE__ */ jsxs2(Fragment, { children: [
|
|
1493
|
+
/* @__PURE__ */ jsx2("button", { style: styles.toolbarIconBtn, onClick: onCopyTemplate, title: chrome.copyTemplateTitle, "aria-label": chrome.copyTemplate, children: templateCopied ? /* @__PURE__ */ jsx2(Check, { size: 14 }) : /* @__PURE__ */ jsx2(ClipboardCopy, { size: 14 }) }),
|
|
1494
|
+
/* @__PURE__ */ jsx2("button", { style: styles.toolbarIconBtn, onClick: onPasteTemplate, disabled: !clipboardReady, title: chrome.pasteTemplateTitle, "aria-label": chrome.pasteTemplate, children: /* @__PURE__ */ jsx2(ClipboardPaste, { size: 14 }) })
|
|
1295
1495
|
] })
|
|
1296
1496
|
] })
|
|
1297
1497
|
] });
|
|
1298
1498
|
}
|
|
1299
1499
|
|
|
1300
1500
|
// src/components/Palette.tsx
|
|
1301
|
-
import { useState as
|
|
1501
|
+
import { useState as useState6 } from "react";
|
|
1302
1502
|
import { Plus } from "lucide-react";
|
|
1303
1503
|
|
|
1304
1504
|
// src/components/Segmented.tsx
|
|
@@ -1393,11 +1593,13 @@ function Palette({
|
|
|
1393
1593
|
theme,
|
|
1394
1594
|
updateThemeColor,
|
|
1395
1595
|
updateThemeLayout,
|
|
1396
|
-
resetTheme
|
|
1596
|
+
resetTheme,
|
|
1597
|
+
fieldCount
|
|
1397
1598
|
}) {
|
|
1398
|
-
const [tab, setTab] =
|
|
1599
|
+
const [tab, setTab] = useState6("blocks");
|
|
1399
1600
|
const contentTypes = CONTENT_TYPES.filter((f) => isContentBlockEnabled(features, f.type));
|
|
1400
1601
|
const formTypes = FORM_TYPES.filter((f) => isFieldTypeEnabled(features, f.type));
|
|
1602
|
+
const atFieldCap = typeof features.maxFields === "number" && fieldCount >= features.maxFields;
|
|
1401
1603
|
return /* @__PURE__ */ jsxs5("div", { className: "fb-palette", style: styles.palette, children: [
|
|
1402
1604
|
features.design && /* @__PURE__ */ jsx5("div", { style: { marginBottom: 14 }, children: /* @__PURE__ */ jsx5(
|
|
1403
1605
|
Segmented,
|
|
@@ -1440,7 +1642,8 @@ function Palette({
|
|
|
1440
1642
|
/* @__PURE__ */ jsx5("div", { style: { ...styles.panelHeading, marginTop: 16 }, children: chrome.formFields }),
|
|
1441
1643
|
/* @__PURE__ */ jsx5("div", { style: styles.paletteList, children: formTypes.map((f) => {
|
|
1442
1644
|
const Icon = f.icon;
|
|
1443
|
-
|
|
1645
|
+
const itemStyle = atFieldCap ? { ...styles.paletteItem, opacity: 0.45, cursor: "not-allowed" } : styles.paletteItem;
|
|
1646
|
+
return /* @__PURE__ */ jsxs5("button", { type: "button", disabled: atFieldCap, style: itemStyle, onClick: () => onAddField(f.type), children: [
|
|
1444
1647
|
/* @__PURE__ */ jsx5(Icon, { size: 16, color: "var(--fb-primary)" }),
|
|
1445
1648
|
/* @__PURE__ */ jsx5("span", { children: chrome[FIELD_TYPE_CHROME_KEY[f.type]] }),
|
|
1446
1649
|
/* @__PURE__ */ jsx5(Plus, { size: 13, color: "var(--fb-muted)", style: { marginLeft: "auto" } })
|
|
@@ -1802,7 +2005,7 @@ function SectionCard({
|
|
|
1802
2005
|
),
|
|
1803
2006
|
section.collapsed && /* @__PURE__ */ jsx10("span", { style: styles.miniBadge, children: chrome.fieldsCount(section.fields.length) }),
|
|
1804
2007
|
/* @__PURE__ */ jsxs8("div", { style: styles.sectionHeaderActions, onClick: (e) => e.stopPropagation(), children: [
|
|
1805
|
-
features.
|
|
2008
|
+
features.sectionBackground && !section.collapsed && /* @__PURE__ */ jsxs8("div", { style: styles.swatchRow, children: [
|
|
1806
2009
|
SECTION_BG_SWATCHES.map((c) => /* @__PURE__ */ jsx10("button", { type: "button", title: c || chrome.none, style: { ...styles.swatchBtn, background: c || "var(--fb-canvas)", ...section.background === c ? styles.swatchBtnActive : {} }, onClick: () => onUpdateBackground(c) }, c || "none")),
|
|
1807
2010
|
/* @__PURE__ */ jsx10("input", { type: "color", title: chrome.customColor, value: /^#/.test(section.background) ? section.background : "#ffffff", onChange: (e) => onUpdateBackground(e.target.value), style: styles.colorPickerInput })
|
|
1808
2011
|
] }),
|
|
@@ -2123,7 +2326,7 @@ function InspectorBody({ selected, language, chrome, features, onUpdateField, on
|
|
|
2123
2326
|
}
|
|
2124
2327
|
|
|
2125
2328
|
// src/components/PreviewPane.tsx
|
|
2126
|
-
import { useState as
|
|
2329
|
+
import { useState as useState7 } from "react";
|
|
2127
2330
|
import { PartyPopper, X as X2, CircleAlert as CircleAlert2 } from "lucide-react";
|
|
2128
2331
|
|
|
2129
2332
|
// src/lib/validate.ts
|
|
@@ -2171,11 +2374,11 @@ function collectFieldValues(fields) {
|
|
|
2171
2374
|
|
|
2172
2375
|
// src/components/PreviewPane.tsx
|
|
2173
2376
|
import { jsx as jsx14, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
2174
|
-
function PreviewPane({ title, sections, onFieldChange, language, strings, chrome, baseMaxWidth, onSubmit }) {
|
|
2175
|
-
const [device, setDevice] =
|
|
2176
|
-
const [errors, setErrors] =
|
|
2177
|
-
const [submitted, setSubmitted] =
|
|
2178
|
-
const [attemptedButtonId, setAttemptedButtonId] =
|
|
2377
|
+
function PreviewPane({ title, sections, onFieldChange, language, strings, chrome, baseMaxWidth, features, onSubmit }) {
|
|
2378
|
+
const [device, setDevice] = useState7("laptop");
|
|
2379
|
+
const [errors, setErrors] = useState7({});
|
|
2380
|
+
const [submitted, setSubmitted] = useState7(null);
|
|
2381
|
+
const [attemptedButtonId, setAttemptedButtonId] = useState7(null);
|
|
2179
2382
|
const deviceOptions = buildDeviceOptions(baseMaxWidth, chrome);
|
|
2180
2383
|
const maxWidth = (deviceOptions.find((d) => d.value === device) || deviceOptions[0]).maxWidth;
|
|
2181
2384
|
const allFields = sections.flatMap((s) => s.fields);
|
|
@@ -2234,7 +2437,7 @@ function PreviewPane({ title, sections, onFieldChange, language, strings, chrome
|
|
|
2234
2437
|
}
|
|
2235
2438
|
return /* @__PURE__ */ jsxs12("div", { style: styles.previewWrap, children: [
|
|
2236
2439
|
/* @__PURE__ */ jsxs12("div", { style: { width: "100%", maxWidth, display: "flex", flexDirection: "column", alignItems: "center" }, children: [
|
|
2237
|
-
/* @__PURE__ */ jsx14("div", { style: styles.previewToolbar, children: /* @__PURE__ */ jsx14(Segmented, { options: deviceOptions, value: device, onChange: setDevice }) }),
|
|
2440
|
+
features.deviceToggle && /* @__PURE__ */ jsx14("div", { style: styles.previewToolbar, children: /* @__PURE__ */ jsx14(Segmented, { options: deviceOptions, value: device, onChange: setDevice }) }),
|
|
2238
2441
|
/* @__PURE__ */ jsxs12("div", { style: { ...styles.previewCard, maxWidth, width: "100%" }, children: [
|
|
2239
2442
|
/* @__PURE__ */ jsx14("h2", { style: styles.previewTitle, children: t(title, language) }),
|
|
2240
2443
|
allFields.length === 0 && /* @__PURE__ */ jsx14("p", { style: { color: "var(--fb-muted)", fontSize: 14 }, children: strings.addFieldsHint }),
|
|
@@ -2279,18 +2482,67 @@ function PreviewPane({ title, sections, onFieldChange, language, strings, chrome
|
|
|
2279
2482
|
}
|
|
2280
2483
|
|
|
2281
2484
|
// src/components/modals/JsonModal.tsx
|
|
2282
|
-
import { Check, ClipboardCopy, X as X3 } from "lucide-react";
|
|
2485
|
+
import { Check as Check2, ClipboardCopy as ClipboardCopy2, X as X3 } from "lucide-react";
|
|
2486
|
+
|
|
2487
|
+
// src/hooks/useModalA11y.ts
|
|
2488
|
+
import { useEffect as useEffect3, useRef as useRef4 } from "react";
|
|
2489
|
+
var FOCUSABLE = 'a[href],button:not([disabled]),input:not([disabled]),select:not([disabled]),textarea:not([disabled]),[tabindex]:not([tabindex="-1"])';
|
|
2490
|
+
function useModalA11y(onClose) {
|
|
2491
|
+
const panelRef = useRef4(null);
|
|
2492
|
+
const onCloseRef = useRef4(onClose);
|
|
2493
|
+
onCloseRef.current = onClose;
|
|
2494
|
+
useEffect3(() => {
|
|
2495
|
+
const opener = document.activeElement;
|
|
2496
|
+
const panel = panelRef.current;
|
|
2497
|
+
const focusables = () => Array.from(panel?.querySelectorAll(FOCUSABLE) ?? []);
|
|
2498
|
+
if (panel && !panel.contains(document.activeElement)) {
|
|
2499
|
+
(panel.querySelector("[autofocus]") ?? focusables()[0] ?? panel).focus();
|
|
2500
|
+
}
|
|
2501
|
+
const onKeyDown = (e) => {
|
|
2502
|
+
if (e.key === "Escape") {
|
|
2503
|
+
e.stopPropagation();
|
|
2504
|
+
onCloseRef.current();
|
|
2505
|
+
return;
|
|
2506
|
+
}
|
|
2507
|
+
if (e.key !== "Tab") return;
|
|
2508
|
+
const items = focusables();
|
|
2509
|
+
if (items.length === 0) {
|
|
2510
|
+
e.preventDefault();
|
|
2511
|
+
return;
|
|
2512
|
+
}
|
|
2513
|
+
const first = items[0];
|
|
2514
|
+
const last = items[items.length - 1];
|
|
2515
|
+
const active = document.activeElement;
|
|
2516
|
+
if (e.shiftKey && (active === first || !panel?.contains(active))) {
|
|
2517
|
+
e.preventDefault();
|
|
2518
|
+
last.focus();
|
|
2519
|
+
} else if (!e.shiftKey && active === last) {
|
|
2520
|
+
e.preventDefault();
|
|
2521
|
+
first.focus();
|
|
2522
|
+
}
|
|
2523
|
+
};
|
|
2524
|
+
document.addEventListener("keydown", onKeyDown, true);
|
|
2525
|
+
return () => {
|
|
2526
|
+
document.removeEventListener("keydown", onKeyDown, true);
|
|
2527
|
+
opener?.focus?.();
|
|
2528
|
+
};
|
|
2529
|
+
}, []);
|
|
2530
|
+
return panelRef;
|
|
2531
|
+
}
|
|
2532
|
+
|
|
2533
|
+
// src/components/modals/JsonModal.tsx
|
|
2283
2534
|
import { jsx as jsx15, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
2284
2535
|
function JsonModal({ chrome, jsonString, copied, onCopy, onClose }) {
|
|
2285
|
-
|
|
2536
|
+
const panelRef = useModalA11y(onClose);
|
|
2537
|
+
return /* @__PURE__ */ jsx15("div", { style: styles.modalOverlay, onClick: onClose, children: /* @__PURE__ */ jsxs13("div", { ref: panelRef, style: styles.modal, role: "dialog", "aria-modal": "true", "aria-label": chrome.formJson, onClick: (e) => e.stopPropagation(), children: [
|
|
2286
2538
|
/* @__PURE__ */ jsxs13("div", { style: styles.modalHeader, children: [
|
|
2287
2539
|
/* @__PURE__ */ jsx15("span", { style: { fontWeight: 600, fontSize: 14 }, children: chrome.formJson }),
|
|
2288
2540
|
/* @__PURE__ */ jsxs13("div", { style: { display: "flex", gap: 8 }, children: [
|
|
2289
|
-
/* @__PURE__ */ jsxs13("button", { style: styles.ghostBtn, onClick: onCopy, children: [
|
|
2290
|
-
copied ? /* @__PURE__ */ jsx15(
|
|
2541
|
+
/* @__PURE__ */ jsxs13("button", { type: "button", style: styles.ghostBtn, onClick: onCopy, children: [
|
|
2542
|
+
copied ? /* @__PURE__ */ jsx15(Check2, { size: 14 }) : /* @__PURE__ */ jsx15(ClipboardCopy2, { size: 14 }),
|
|
2291
2543
|
copied ? chrome.copied : chrome.copy
|
|
2292
2544
|
] }),
|
|
2293
|
-
/* @__PURE__ */ jsx15("button", { style: styles.iconBtn, onClick: onClose, children: /* @__PURE__ */ jsx15(X3, { size: 16 }) })
|
|
2545
|
+
/* @__PURE__ */ jsx15("button", { type: "button", style: styles.iconBtn, onClick: onClose, children: /* @__PURE__ */ jsx15(X3, { size: 16 }) })
|
|
2294
2546
|
] })
|
|
2295
2547
|
] }),
|
|
2296
2548
|
/* @__PURE__ */ jsx15("pre", { style: styles.jsonPre, children: jsonString })
|
|
@@ -2298,57 +2550,99 @@ function JsonModal({ chrome, jsonString, copied, onCopy, onClose }) {
|
|
|
2298
2550
|
}
|
|
2299
2551
|
|
|
2300
2552
|
// src/components/modals/TemplatesModal.tsx
|
|
2301
|
-
import { Trash2 as Trash24, X as X4 } from "lucide-react";
|
|
2553
|
+
import { AlertCircle as AlertCircle2, Loader2 as Loader22, Trash2 as Trash24, X as X4 } from "lucide-react";
|
|
2302
2554
|
import { jsx as jsx16, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
2303
|
-
function TemplatesModal({ chrome, savedForms, currentFormId, onOpen, onDelete, onClose }) {
|
|
2304
|
-
|
|
2555
|
+
function TemplatesModal({ chrome, savedForms, currentFormId, manage, templateState, onOpen, onDelete, onClose }) {
|
|
2556
|
+
const busy = templateState === "loading";
|
|
2557
|
+
const panelRef = useModalA11y(onClose);
|
|
2558
|
+
return /* @__PURE__ */ jsx16("div", { style: styles.modalOverlay, onClick: onClose, children: /* @__PURE__ */ jsxs14("div", { ref: panelRef, style: styles.modal, role: "dialog", "aria-modal": "true", "aria-label": chrome.templates, onClick: (e) => e.stopPropagation(), children: [
|
|
2305
2559
|
/* @__PURE__ */ jsxs14("div", { style: styles.modalHeader, children: [
|
|
2306
2560
|
/* @__PURE__ */ jsx16("span", { style: { fontWeight: 600, fontSize: 14 }, children: chrome.templates }),
|
|
2307
|
-
/* @__PURE__ */ jsx16("button", { style: styles.iconBtn, onClick: onClose, children: /* @__PURE__ */ jsx16(X4, { size: 16 }) })
|
|
2561
|
+
/* @__PURE__ */ jsx16("button", { type: "button", style: styles.iconBtn, onClick: onClose, children: /* @__PURE__ */ jsx16(X4, { size: 16 }) })
|
|
2308
2562
|
] }),
|
|
2309
|
-
/* @__PURE__ */
|
|
2310
|
-
/* @__PURE__ */
|
|
2311
|
-
/* @__PURE__ */ jsxs14("div", { style:
|
|
2312
|
-
|
|
2313
|
-
|
|
2563
|
+
/* @__PURE__ */ jsxs14("div", { style: styles.libraryBody, children: [
|
|
2564
|
+
savedForms.length === 0 ? /* @__PURE__ */ jsx16("div", { style: styles.inspectorEmpty, children: manage ? chrome.noTemplates : chrome.noTemplatesReadonly }) : savedForms.map((f) => /* @__PURE__ */ jsxs14("div", { style: styles.libraryRow, children: [
|
|
2565
|
+
/* @__PURE__ */ jsxs14("div", { style: { minWidth: 0 }, children: [
|
|
2566
|
+
/* @__PURE__ */ jsxs14("div", { style: styles.libraryRowTitle, children: [
|
|
2567
|
+
f.title,
|
|
2568
|
+
f.id === currentFormId && /* @__PURE__ */ jsx16("span", { style: styles.currentBadge, children: chrome.current })
|
|
2569
|
+
] }),
|
|
2570
|
+
/* @__PURE__ */ jsxs14("div", { style: styles.libraryRowMeta, children: [
|
|
2571
|
+
chrome.updated,
|
|
2572
|
+
" ",
|
|
2573
|
+
new Date(f.updatedAt).toLocaleString()
|
|
2574
|
+
] })
|
|
2314
2575
|
] }),
|
|
2315
|
-
/* @__PURE__ */ jsxs14("div", { style:
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2576
|
+
/* @__PURE__ */ jsxs14("div", { style: { display: "flex", gap: 6, flexShrink: 0 }, children: [
|
|
2577
|
+
/* @__PURE__ */ jsxs14("button", { type: "button", style: styles.ghostBtn, disabled: busy, onClick: () => onOpen(f.id), children: [
|
|
2578
|
+
busy && /* @__PURE__ */ jsx16(Loader22, { size: 12, className: "spin" }),
|
|
2579
|
+
" ",
|
|
2580
|
+
manage ? chrome.open : chrome.applyTemplate
|
|
2581
|
+
] }),
|
|
2582
|
+
manage && /* @__PURE__ */ jsx16("button", { type: "button", style: { ...styles.iconBtn, ...styles.iconBtnDanger }, disabled: busy, title: chrome.delete, onClick: () => onDelete(f.id), children: /* @__PURE__ */ jsx16(Trash24, { size: 13 }) })
|
|
2319
2583
|
] })
|
|
2320
|
-
] }),
|
|
2321
|
-
/* @__PURE__ */ jsxs14("div", { style: { display: "flex", gap:
|
|
2322
|
-
/* @__PURE__ */ jsx16(
|
|
2323
|
-
|
|
2584
|
+
] }, f.id)),
|
|
2585
|
+
templateState === "error" && /* @__PURE__ */ jsxs14("div", { style: { ...styles.libraryRowMeta, color: "var(--fb-danger)", display: "flex", alignItems: "center", gap: 5, marginTop: 4 }, children: [
|
|
2586
|
+
/* @__PURE__ */ jsx16(AlertCircle2, { size: 12 }),
|
|
2587
|
+
" ",
|
|
2588
|
+
chrome.templateLoadFailed
|
|
2324
2589
|
] })
|
|
2325
|
-
] }
|
|
2590
|
+
] })
|
|
2326
2591
|
] }) });
|
|
2327
2592
|
}
|
|
2328
2593
|
|
|
2329
2594
|
// src/components/modals/SaveAsModal.tsx
|
|
2330
|
-
import { useState as
|
|
2331
|
-
import { Save as Save2, X as X5 } from "lucide-react";
|
|
2595
|
+
import { useState as useState8 } from "react";
|
|
2596
|
+
import { AlertCircle as AlertCircle3, Loader2 as Loader23, Save as Save2, X as X5 } from "lucide-react";
|
|
2332
2597
|
import { jsx as jsx17, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
2333
|
-
function SaveAsModal({ chrome, suggestedName, onSave, onClose }) {
|
|
2334
|
-
const [name, setName] =
|
|
2335
|
-
|
|
2598
|
+
function SaveAsModal({ chrome, suggestedName, templateState, onSave, onClose }) {
|
|
2599
|
+
const [name, setName] = useState8(suggestedName);
|
|
2600
|
+
const saving = templateState === "saving";
|
|
2601
|
+
const canSave = !!name.trim() && !saving;
|
|
2602
|
+
const panelRef = useModalA11y(onClose);
|
|
2603
|
+
return /* @__PURE__ */ jsx17("div", { style: styles.modalOverlay, onClick: onClose, children: /* @__PURE__ */ jsxs15("div", { ref: panelRef, style: { ...styles.modal, maxWidth: 380 }, role: "dialog", "aria-modal": "true", "aria-label": chrome.saveForm, onClick: (e) => e.stopPropagation(), children: [
|
|
2336
2604
|
/* @__PURE__ */ jsxs15("div", { style: styles.modalHeader, children: [
|
|
2337
2605
|
/* @__PURE__ */ jsx17("span", { style: { fontWeight: 600, fontSize: 14 }, children: chrome.saveForm }),
|
|
2338
|
-
/* @__PURE__ */ jsx17("button", { style: styles.iconBtn, onClick: onClose, children: /* @__PURE__ */ jsx17(X5, { size: 16 }) })
|
|
2606
|
+
/* @__PURE__ */ jsx17("button", { type: "button", style: styles.iconBtn, onClick: onClose, children: /* @__PURE__ */ jsx17(X5, { size: 16 }) })
|
|
2339
2607
|
] }),
|
|
2340
2608
|
/* @__PURE__ */ jsxs15("div", { style: { padding: 16 }, children: [
|
|
2341
2609
|
/* @__PURE__ */ jsx17("label", { style: styles.propLabel, children: chrome.formName }),
|
|
2342
|
-
/* @__PURE__ */ jsx17("input", { autoFocus: true, style: styles.propInput, value: name, onChange: (e) => setName(e.target.value), onKeyDown: (e) => e.key === "Enter" &&
|
|
2343
|
-
/* @__PURE__ */ jsxs15("button", { style: { ...styles.primaryBtn, width: "100%", justifyContent: "center", marginTop: 14 }, disabled: !
|
|
2344
|
-
/* @__PURE__ */ jsx17(Save2, { size: 14 }),
|
|
2610
|
+
/* @__PURE__ */ jsx17("input", { autoFocus: true, "aria-label": chrome.formName, style: styles.propInput, value: name, onChange: (e) => setName(e.target.value), onKeyDown: (e) => e.key === "Enter" && canSave && onSave(name.trim()) }),
|
|
2611
|
+
/* @__PURE__ */ jsxs15("button", { type: "button", style: { ...styles.primaryBtn, width: "100%", justifyContent: "center", marginTop: 14 }, disabled: !canSave, onClick: () => onSave(name.trim()), children: [
|
|
2612
|
+
saving ? /* @__PURE__ */ jsx17(Loader23, { size: 14, className: "spin" }) : /* @__PURE__ */ jsx17(Save2, { size: 14 }),
|
|
2345
2613
|
" ",
|
|
2346
2614
|
chrome.saveToLibrary
|
|
2615
|
+
] }),
|
|
2616
|
+
templateState === "error" && /* @__PURE__ */ jsxs15("div", { style: { fontSize: 12, color: "var(--fb-danger)", display: "flex", alignItems: "center", gap: 5, marginTop: 10 }, children: [
|
|
2617
|
+
/* @__PURE__ */ jsx17(AlertCircle3, { size: 12 }),
|
|
2618
|
+
" ",
|
|
2619
|
+
chrome.templateSaveFailed
|
|
2347
2620
|
] })
|
|
2348
2621
|
] })
|
|
2349
2622
|
] }) });
|
|
2350
2623
|
}
|
|
2351
2624
|
|
|
2625
|
+
// src/components/modals/ConfirmModal.tsx
|
|
2626
|
+
import { X as X6 } from "lucide-react";
|
|
2627
|
+
import { Fragment as Fragment6, jsx as jsx18, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
2628
|
+
function ConfirmModal({ chrome, title, message, onConfirm, confirmLabel, tone = "default", onClose }) {
|
|
2629
|
+
const panelRef = useModalA11y(onClose);
|
|
2630
|
+
const confirmStyle = tone === "danger" ? { ...styles.primaryBtn, background: "var(--fb-danger)", borderColor: "var(--fb-danger)" } : styles.primaryBtn;
|
|
2631
|
+
return /* @__PURE__ */ jsx18("div", { style: styles.modalOverlay, onClick: onClose, children: /* @__PURE__ */ jsxs16("div", { ref: panelRef, style: { ...styles.modal, maxWidth: 380 }, role: "alertdialog", "aria-modal": "true", "aria-label": title, onClick: (e) => e.stopPropagation(), children: [
|
|
2632
|
+
/* @__PURE__ */ jsxs16("div", { style: styles.modalHeader, children: [
|
|
2633
|
+
/* @__PURE__ */ jsx18("span", { style: { fontWeight: 600, fontSize: 14 }, children: title }),
|
|
2634
|
+
/* @__PURE__ */ jsx18("button", { type: "button", style: styles.iconBtn, onClick: onClose, children: /* @__PURE__ */ jsx18(X6, { size: 16 }) })
|
|
2635
|
+
] }),
|
|
2636
|
+
/* @__PURE__ */ jsxs16("div", { style: { padding: 16 }, children: [
|
|
2637
|
+
/* @__PURE__ */ jsx18("p", { style: { margin: 0, fontSize: 13, lineHeight: 1.5, color: "var(--fb-ink)" }, children: message }),
|
|
2638
|
+
/* @__PURE__ */ jsx18("div", { style: { display: "flex", justifyContent: "flex-end", gap: 8, marginTop: 16 }, children: onConfirm ? /* @__PURE__ */ jsxs16(Fragment6, { children: [
|
|
2639
|
+
/* @__PURE__ */ jsx18("button", { type: "button", autoFocus: true, style: styles.ghostBtn, onClick: onClose, children: chrome.cancel }),
|
|
2640
|
+
/* @__PURE__ */ jsx18("button", { type: "button", style: confirmStyle, onClick: onConfirm, children: confirmLabel ?? chrome.confirm })
|
|
2641
|
+
] }) : /* @__PURE__ */ jsx18("button", { type: "button", autoFocus: true, style: styles.primaryBtn, onClick: onClose, children: chrome.dismiss }) })
|
|
2642
|
+
] })
|
|
2643
|
+
] }) });
|
|
2644
|
+
}
|
|
2645
|
+
|
|
2352
2646
|
// src/styles/globalCss.ts
|
|
2353
2647
|
var css = `
|
|
2354
2648
|
* { box-sizing: border-box; }
|
|
@@ -2356,20 +2650,14 @@ var css = `
|
|
|
2356
2650
|
input, textarea, select { font-family: inherit; }
|
|
2357
2651
|
@keyframes fb-spin { to { transform: rotate(360deg); } }
|
|
2358
2652
|
.spin { animation: fb-spin 0.8s linear infinite; }
|
|
2359
|
-
/*
|
|
2360
|
-
than growing past whatever space its host container gives it; the
|
|
2361
|
-
second declaration upgrades to the dynamic viewport unit on browsers
|
|
2362
|
-
that support it (mobile browser chrome resizing the viewport), and is
|
|
2363
|
-
simply ignored by ones that don't. */
|
|
2653
|
+
/* Cap at viewport height so the widget scrolls internally; dvh upgrade is ignored where unsupported. */
|
|
2364
2654
|
.fb-root { max-height: 100vh; max-height: 100dvh; }
|
|
2365
2655
|
|
|
2366
|
-
/*
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
buttons, tracked by .fb-work-area's data-mobile-panel attribute,
|
|
2372
|
-
and dismissible via .fb-mobile-backdrop. */
|
|
2656
|
+
/* Toolbar's active-template label is nice-to-have \u2014 drop it first when width is tight. */
|
|
2657
|
+
@media (max-width: 600px) { .fb-template-tag { display: none; } }
|
|
2658
|
+
|
|
2659
|
+
/* Under 720px: Palette/Inspector become drawers over Canvas, toggled by .fb-mobile-bar,
|
|
2660
|
+
tracked by .fb-work-area[data-mobile-panel], dismissed via .fb-mobile-backdrop. */
|
|
2373
2661
|
.fb-mobile-bar { display: none; align-items: center; gap: 6px; padding: 6px 10px; border-bottom: 1px solid var(--fb-border); background: var(--fb-surface); flex-shrink: 0; }
|
|
2374
2662
|
.fb-mobile-btn { display: flex; align-items: center; gap: 6px; padding: 6px 10px; border-radius: 7px; border: 1px solid var(--fb-border); background: var(--fb-surface); color: var(--fb-muted); font-size: 12.5px; font-weight: 600; }
|
|
2375
2663
|
.fb-mobile-btn[aria-pressed="true"] { border-color: var(--fb-primary); background: var(--fb-primary-soft); color: var(--fb-primary); }
|
|
@@ -2395,7 +2683,7 @@ var css = `
|
|
|
2395
2683
|
`;
|
|
2396
2684
|
|
|
2397
2685
|
// src/FormBuilder.tsx
|
|
2398
|
-
import { jsx as
|
|
2686
|
+
import { jsx as jsx19, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
2399
2687
|
var FormBuilder = forwardRef(function FormBuilder2({
|
|
2400
2688
|
theme: themeOverrideProp,
|
|
2401
2689
|
language: languageOverride,
|
|
@@ -2405,24 +2693,40 @@ var FormBuilder = forwardRef(function FormBuilder2({
|
|
|
2405
2693
|
features: featuresProp,
|
|
2406
2694
|
storage: storageProp,
|
|
2407
2695
|
onSubmit,
|
|
2408
|
-
initialDocument
|
|
2696
|
+
initialDocument,
|
|
2697
|
+
initialMode,
|
|
2698
|
+
onModeChange,
|
|
2699
|
+
onTemplateChange,
|
|
2700
|
+
templateClipboardKey
|
|
2409
2701
|
}, ref) {
|
|
2410
2702
|
const features = resolveFeatures(featuresProp);
|
|
2411
2703
|
const storage = storageProp ?? localStorageAdapter;
|
|
2412
2704
|
const { theme, updateThemeColor, updateThemeLayout, resetTheme, replaceThemeOverrides, themeOverrides } = useTheme(themeOverrideProp);
|
|
2413
|
-
const [language, setLanguage] =
|
|
2705
|
+
const [language, setLanguage] = useState9(languageOverride || languages[0]?.code || "en");
|
|
2414
2706
|
const strings = { ...DEFAULT_STRINGS[language] || DEFAULT_STRINGS.en, ...stringsOverride && stringsOverride[language] || {} };
|
|
2415
2707
|
const chrome = { ...CHROME[language] || CHROME.en, ...chromeOverride && chromeOverride[language] || {} };
|
|
2416
2708
|
const doc = useFormDocument({ language, chrome });
|
|
2417
2709
|
const drag = useDragReorder(doc.reorderWithinSection);
|
|
2418
|
-
const [mode,
|
|
2419
|
-
const
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2710
|
+
const [mode, setModeState] = useState9(initialMode ?? "build");
|
|
2711
|
+
const setMode = (next) => {
|
|
2712
|
+
setModeState(next);
|
|
2713
|
+
onModeChange?.(next);
|
|
2714
|
+
};
|
|
2715
|
+
useEffect4(() => {
|
|
2716
|
+
onModeChange?.(mode);
|
|
2717
|
+
}, []);
|
|
2718
|
+
const [showJson, setShowJson] = useState9(false);
|
|
2719
|
+
const [showLibrary, setShowLibrary] = useState9(false);
|
|
2720
|
+
const [copied, setCopied] = useState9(false);
|
|
2721
|
+
const [templateCopied, setTemplateCopied] = useState9(false);
|
|
2722
|
+
const [pastePrompt, setPastePrompt] = useState9(false);
|
|
2723
|
+
const clipboard = useTemplateClipboard(templateClipboardKey);
|
|
2724
|
+
const [mobilePanel, setMobilePanel] = useState9("none");
|
|
2423
2725
|
const persistence = usePersistence({
|
|
2424
2726
|
storage,
|
|
2425
2727
|
autosave: features.autosave,
|
|
2728
|
+
templateMax: features.templates.max,
|
|
2729
|
+
templateManage: features.templates.manage,
|
|
2426
2730
|
language,
|
|
2427
2731
|
chrome,
|
|
2428
2732
|
document: { title: doc.title, themeOverrides, sections: doc.sections },
|
|
@@ -2434,16 +2738,17 @@ var FormBuilder = forwardRef(function FormBuilder2({
|
|
|
2434
2738
|
doc.resetToBlank();
|
|
2435
2739
|
resetTheme();
|
|
2436
2740
|
},
|
|
2741
|
+
onTemplateChange,
|
|
2437
2742
|
ensureActiveSection: () => doc.setActiveSectionId((prev) => prev ?? doc.sections[0]?.id ?? null)
|
|
2438
2743
|
});
|
|
2439
|
-
const jsonDoc = {
|
|
2744
|
+
const jsonDoc = useMemo2(() => ({
|
|
2440
2745
|
version: 5,
|
|
2441
2746
|
title: doc.title,
|
|
2442
2747
|
theme,
|
|
2443
2748
|
themeOverrides,
|
|
2444
2749
|
sections: doc.sections.map((s) => ({ id: s.id, title: s.title, background: s.background, collapsed: s.collapsed, fields: s.fields }))
|
|
2445
|
-
};
|
|
2446
|
-
const jsonString = JSON.stringify(jsonDoc, null, 2);
|
|
2750
|
+
}), [doc.title, doc.sections, theme, themeOverrides]);
|
|
2751
|
+
const jsonString = useMemo2(() => JSON.stringify(jsonDoc, null, 2), [jsonDoc]);
|
|
2447
2752
|
function copyJson() {
|
|
2448
2753
|
navigator.clipboard.writeText(jsonString).then(() => {
|
|
2449
2754
|
setCopied(true);
|
|
@@ -2451,17 +2756,31 @@ var FormBuilder = forwardRef(function FormBuilder2({
|
|
|
2451
2756
|
}).catch(() => {
|
|
2452
2757
|
});
|
|
2453
2758
|
}
|
|
2759
|
+
function applyDocument(migrated) {
|
|
2760
|
+
if (!migrated) return false;
|
|
2761
|
+
doc.loadDocument(migrated);
|
|
2762
|
+
replaceThemeOverrides(migrated.themeOverrides);
|
|
2763
|
+
return true;
|
|
2764
|
+
}
|
|
2765
|
+
function copyTemplate() {
|
|
2766
|
+
clipboard.copyTemplate(jsonDoc);
|
|
2767
|
+
setTemplateCopied(true);
|
|
2768
|
+
setTimeout(() => setTemplateCopied(false), 1500);
|
|
2769
|
+
}
|
|
2770
|
+
function pasteTemplate() {
|
|
2771
|
+
if (clipboard.readTemplate()) setPastePrompt(true);
|
|
2772
|
+
}
|
|
2454
2773
|
useImperativeHandle(ref, () => ({
|
|
2455
2774
|
getDocument: () => jsonDoc,
|
|
2456
2775
|
exportJson: () => jsonString,
|
|
2457
2776
|
loadDocument: (raw) => {
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
}
|
|
2777
|
+
applyDocument(migrateDocument(raw));
|
|
2778
|
+
},
|
|
2779
|
+
getTemplate: () => ({ __fpb: "template", v: TEMPLATE_FORMAT, document: jsonDoc }),
|
|
2780
|
+
loadTemplate: (input) => applyDocument(parseTemplate(typeof input === "string" ? input : JSON.stringify(input)))
|
|
2463
2781
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
2464
2782
|
}), [jsonDoc, jsonString]);
|
|
2783
|
+
const fieldCount = doc.sections.reduce((n, s) => n + s.fields.filter((f) => !getMeta(f.type).isContent).length, 0);
|
|
2465
2784
|
const activeSectionIdx = doc.sections.findIndex((s) => s.id === doc.activeSection?.id);
|
|
2466
2785
|
const activeSectionLabel = t(doc.activeSection?.title, language) || `#${activeSectionIdx + 1}`;
|
|
2467
2786
|
const rootStyle = {
|
|
@@ -2485,9 +2804,9 @@ var FormBuilder = forwardRef(function FormBuilder2({
|
|
|
2485
2804
|
"--fb-space-panel": `${theme.layout.panelPadding}px`,
|
|
2486
2805
|
"--fb-space-ticket": `${theme.layout.ticketPadding}px`
|
|
2487
2806
|
};
|
|
2488
|
-
return /* @__PURE__ */
|
|
2489
|
-
/* @__PURE__ */
|
|
2490
|
-
/* @__PURE__ */
|
|
2807
|
+
return /* @__PURE__ */ jsxs17("div", { className: "fb-root", style: rootStyle, children: [
|
|
2808
|
+
/* @__PURE__ */ jsx19("style", { children: css }),
|
|
2809
|
+
/* @__PURE__ */ jsx19(
|
|
2491
2810
|
Toolbar,
|
|
2492
2811
|
{
|
|
2493
2812
|
title: doc.title,
|
|
@@ -2495,24 +2814,31 @@ var FormBuilder = forwardRef(function FormBuilder2({
|
|
|
2495
2814
|
languages,
|
|
2496
2815
|
mode,
|
|
2497
2816
|
saveState: persistence.saveState,
|
|
2817
|
+
templateState: persistence.templateState,
|
|
2818
|
+
activeTemplateTitle: persistence.activeTemplateTitle,
|
|
2819
|
+
templateDirty: persistence.isTemplateDirty,
|
|
2498
2820
|
chrome,
|
|
2499
2821
|
features,
|
|
2500
2822
|
savedFormsCount: persistence.savedForms.length,
|
|
2823
|
+
clipboardReady: clipboard.hasClipboard,
|
|
2824
|
+
templateCopied,
|
|
2501
2825
|
onTitleChange: doc.updateTitle,
|
|
2502
2826
|
onLanguageChange: setLanguage,
|
|
2503
2827
|
onModeChange: setMode,
|
|
2504
2828
|
onNewForm: persistence.newForm,
|
|
2505
2829
|
onOpenLibrary: () => setShowLibrary(true),
|
|
2506
2830
|
onSaveExisting: persistence.saveExisting,
|
|
2507
|
-
onOpenJson: () => setShowJson(true)
|
|
2831
|
+
onOpenJson: () => setShowJson(true),
|
|
2832
|
+
onCopyTemplate: copyTemplate,
|
|
2833
|
+
onPasteTemplate: pasteTemplate
|
|
2508
2834
|
}
|
|
2509
2835
|
),
|
|
2510
|
-
persistence.loadingDraft ? /* @__PURE__ */
|
|
2511
|
-
/* @__PURE__ */
|
|
2512
|
-
/* @__PURE__ */
|
|
2513
|
-
] }) : mode === "build" ? /* @__PURE__ */
|
|
2514
|
-
/* @__PURE__ */
|
|
2515
|
-
/* @__PURE__ */
|
|
2836
|
+
persistence.loadingDraft ? /* @__PURE__ */ jsxs17("div", { style: styles.loadingScreen, children: [
|
|
2837
|
+
/* @__PURE__ */ jsx19(Loader24, { size: 20, className: "spin" }),
|
|
2838
|
+
/* @__PURE__ */ jsx19("span", { style: { marginTop: 8, fontSize: 13, color: "var(--fb-muted)" }, children: chrome.loadingDraft })
|
|
2839
|
+
] }) : mode === "build" ? /* @__PURE__ */ jsxs17("div", { className: "fb-work-area", style: styles.workArea, "data-mobile-panel": mobilePanel, children: [
|
|
2840
|
+
/* @__PURE__ */ jsxs17("div", { className: "fb-mobile-bar", children: [
|
|
2841
|
+
/* @__PURE__ */ jsxs17(
|
|
2516
2842
|
"button",
|
|
2517
2843
|
{
|
|
2518
2844
|
type: "button",
|
|
@@ -2520,13 +2846,13 @@ var FormBuilder = forwardRef(function FormBuilder2({
|
|
|
2520
2846
|
"aria-pressed": mobilePanel === "palette",
|
|
2521
2847
|
onClick: () => setMobilePanel((p) => p === "palette" ? "none" : "palette"),
|
|
2522
2848
|
children: [
|
|
2523
|
-
/* @__PURE__ */
|
|
2849
|
+
/* @__PURE__ */ jsx19(Menu, { size: 14 }),
|
|
2524
2850
|
" ",
|
|
2525
2851
|
chrome.paletteTabBlocks
|
|
2526
2852
|
]
|
|
2527
2853
|
}
|
|
2528
2854
|
),
|
|
2529
|
-
/* @__PURE__ */
|
|
2855
|
+
/* @__PURE__ */ jsxs17(
|
|
2530
2856
|
"button",
|
|
2531
2857
|
{
|
|
2532
2858
|
type: "button",
|
|
@@ -2534,16 +2860,16 @@ var FormBuilder = forwardRef(function FormBuilder2({
|
|
|
2534
2860
|
"aria-pressed": mobilePanel === "inspector",
|
|
2535
2861
|
onClick: () => setMobilePanel((p) => p === "inspector" ? "none" : "inspector"),
|
|
2536
2862
|
children: [
|
|
2537
|
-
/* @__PURE__ */
|
|
2863
|
+
/* @__PURE__ */ jsx19(SlidersHorizontal, { size: 14 }),
|
|
2538
2864
|
" ",
|
|
2539
2865
|
chrome.properties
|
|
2540
2866
|
]
|
|
2541
2867
|
}
|
|
2542
2868
|
)
|
|
2543
2869
|
] }),
|
|
2544
|
-
/* @__PURE__ */
|
|
2545
|
-
mobilePanel !== "none" && /* @__PURE__ */
|
|
2546
|
-
/* @__PURE__ */
|
|
2870
|
+
/* @__PURE__ */ jsxs17("div", { className: "fb-canvas-area", style: styles.workArea, children: [
|
|
2871
|
+
mobilePanel !== "none" && /* @__PURE__ */ jsx19("div", { className: "fb-mobile-backdrop", onClick: () => setMobilePanel("none") }),
|
|
2872
|
+
/* @__PURE__ */ jsx19(
|
|
2547
2873
|
Palette,
|
|
2548
2874
|
{
|
|
2549
2875
|
activeSectionLabel,
|
|
@@ -2553,13 +2879,14 @@ var FormBuilder = forwardRef(function FormBuilder2({
|
|
|
2553
2879
|
setMobilePanel("none");
|
|
2554
2880
|
},
|
|
2555
2881
|
features,
|
|
2882
|
+
fieldCount,
|
|
2556
2883
|
theme,
|
|
2557
2884
|
updateThemeColor,
|
|
2558
2885
|
updateThemeLayout,
|
|
2559
2886
|
resetTheme
|
|
2560
2887
|
}
|
|
2561
2888
|
),
|
|
2562
|
-
/* @__PURE__ */
|
|
2889
|
+
/* @__PURE__ */ jsx19(
|
|
2563
2890
|
Canvas,
|
|
2564
2891
|
{
|
|
2565
2892
|
sections: doc.sections,
|
|
@@ -2591,7 +2918,7 @@ var FormBuilder = forwardRef(function FormBuilder2({
|
|
|
2591
2918
|
getDragHandleProps: drag.getDragHandleProps
|
|
2592
2919
|
}
|
|
2593
2920
|
),
|
|
2594
|
-
/* @__PURE__ */
|
|
2921
|
+
/* @__PURE__ */ jsx19(
|
|
2595
2922
|
Inspector,
|
|
2596
2923
|
{
|
|
2597
2924
|
selected: doc.selected,
|
|
@@ -2606,7 +2933,7 @@ var FormBuilder = forwardRef(function FormBuilder2({
|
|
|
2606
2933
|
}
|
|
2607
2934
|
)
|
|
2608
2935
|
] })
|
|
2609
|
-
] }) : /* @__PURE__ */
|
|
2936
|
+
] }) : /* @__PURE__ */ jsx19(
|
|
2610
2937
|
PreviewPane,
|
|
2611
2938
|
{
|
|
2612
2939
|
title: doc.title,
|
|
@@ -2616,32 +2943,59 @@ var FormBuilder = forwardRef(function FormBuilder2({
|
|
|
2616
2943
|
strings,
|
|
2617
2944
|
chrome,
|
|
2618
2945
|
baseMaxWidth: theme.layout.maxWidth,
|
|
2946
|
+
features,
|
|
2619
2947
|
onSubmit
|
|
2620
2948
|
}
|
|
2621
2949
|
),
|
|
2622
|
-
showJson && /* @__PURE__ */
|
|
2623
|
-
showLibrary && /* @__PURE__ */
|
|
2950
|
+
showJson && /* @__PURE__ */ jsx19(JsonModal, { chrome, jsonString, copied, onCopy: copyJson, onClose: () => setShowJson(false) }),
|
|
2951
|
+
showLibrary && /* @__PURE__ */ jsx19(
|
|
2624
2952
|
TemplatesModal,
|
|
2625
2953
|
{
|
|
2626
2954
|
chrome,
|
|
2627
2955
|
savedForms: persistence.savedForms,
|
|
2628
2956
|
currentFormId: persistence.currentFormId,
|
|
2957
|
+
manage: features.templates.manage,
|
|
2958
|
+
templateState: persistence.templateState,
|
|
2629
2959
|
onOpen: async (id) => {
|
|
2630
|
-
await persistence.loadForm(id);
|
|
2631
|
-
setShowLibrary(false);
|
|
2960
|
+
if (await persistence.loadForm(id)) setShowLibrary(false);
|
|
2632
2961
|
},
|
|
2633
2962
|
onDelete: persistence.deleteForm,
|
|
2634
2963
|
onClose: () => setShowLibrary(false)
|
|
2635
2964
|
}
|
|
2636
2965
|
),
|
|
2637
|
-
persistence.saveAsPrompt && /* @__PURE__ */
|
|
2966
|
+
persistence.saveAsPrompt && /* @__PURE__ */ jsx19(
|
|
2638
2967
|
SaveAsModal,
|
|
2639
2968
|
{
|
|
2640
2969
|
chrome,
|
|
2641
2970
|
suggestedName: persistence.saveAsPrompt.suggestedName,
|
|
2971
|
+
templateState: persistence.templateState,
|
|
2642
2972
|
onSave: persistence.saveAs,
|
|
2643
2973
|
onClose: persistence.dismissSaveAsPrompt
|
|
2644
2974
|
}
|
|
2975
|
+
),
|
|
2976
|
+
pastePrompt && /* @__PURE__ */ jsx19(
|
|
2977
|
+
ConfirmModal,
|
|
2978
|
+
{
|
|
2979
|
+
chrome,
|
|
2980
|
+
title: chrome.pasteTemplate,
|
|
2981
|
+
message: chrome.pasteTemplateConfirm,
|
|
2982
|
+
confirmLabel: chrome.confirmReplace,
|
|
2983
|
+
tone: "danger",
|
|
2984
|
+
onConfirm: () => {
|
|
2985
|
+
applyDocument(clipboard.readTemplate());
|
|
2986
|
+
setPastePrompt(false);
|
|
2987
|
+
},
|
|
2988
|
+
onClose: () => setPastePrompt(false)
|
|
2989
|
+
}
|
|
2990
|
+
),
|
|
2991
|
+
persistence.notice && /* @__PURE__ */ jsx19(
|
|
2992
|
+
ConfirmModal,
|
|
2993
|
+
{
|
|
2994
|
+
chrome,
|
|
2995
|
+
title: persistence.notice.title,
|
|
2996
|
+
message: persistence.notice.message,
|
|
2997
|
+
onClose: persistence.dismissNotice
|
|
2998
|
+
}
|
|
2645
2999
|
)
|
|
2646
3000
|
] });
|
|
2647
3001
|
});
|
|
@@ -2662,10 +3016,17 @@ var DARK_THEME = {
|
|
|
2662
3016
|
layout: DEFAULT_THEME.layout
|
|
2663
3017
|
};
|
|
2664
3018
|
export {
|
|
3019
|
+
CLIPBOARD_KEY,
|
|
2665
3020
|
DARK_THEME,
|
|
2666
3021
|
DEFAULT_THEME,
|
|
3022
|
+
DRAFT_KEY,
|
|
2667
3023
|
FormBuilder_default as FormBuilder,
|
|
3024
|
+
INDEX_KEY,
|
|
2668
3025
|
FormBuilder_default as default,
|
|
2669
|
-
|
|
3026
|
+
formKey,
|
|
3027
|
+
localStorageAdapter,
|
|
3028
|
+
parseTemplate,
|
|
3029
|
+
savedFormId,
|
|
3030
|
+
serializeTemplate
|
|
2670
3031
|
};
|
|
2671
3032
|
//# sourceMappingURL=index.js.map
|