form-page-builder 2.2.0 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -619,6 +619,8 @@ interface TemplateChange {
619
619
  interface FormBuilderFeatures {
620
620
  /** Editable form title input in the Toolbar. Default true. */
621
621
  naming?: boolean;
622
+ /** Render the form title as a heading above the fields in Preview. Default false — the host usually shows the form name in its own chrome. */
623
+ formTitle?: boolean;
622
624
  /**
623
625
  * Templates library. `true` (default) = full CRUD + toolbar "Save"; `false` = hidden.
624
626
  * `{ manage: false }` = pick-and-apply only (no save/overwrite/delete). `{ max }` caps the count (default 5).
@@ -698,6 +700,9 @@ declare function serializeTemplate(document: FormDocument): string;
698
700
  /** Accepts a `serializeTemplate` envelope or a bare document (e.g. "View JSON" output); `null` if not a document. */
699
701
  declare function parseTemplate(raw: string | null | undefined): DocumentFields | null;
700
702
 
703
+ /** Current document schema version — stamped on everything persisted. */
704
+ declare const DOCUMENT_VERSION: 5;
705
+
701
706
  declare const DRAFT_KEY = "form-page-builder:draft";
702
707
  declare const INDEX_KEY = "form-page-builder:index";
703
708
  declare const formKey: (id: string) => string;
@@ -706,4 +711,4 @@ declare const savedFormId: (key: string) => string | null;
706
711
  /** Copy/paste-template scratch slot — always localStorage, never the StorageAdapter. */
707
712
  declare const CLIPBOARD_KEY = "form-page-builder:clipboard";
708
713
 
709
- export { CLIPBOARD_KEY, type CheckboxField, type CheckboxGroupField, type ChromeShape, type ContentBlockType, DARK_THEME, DEFAULT_THEME, DRAFT_KEY, type DocumentFields, type FieldPatch, type FieldType, FormBuilder, type FormBuilderFeatures, type FormBuilderHandle, type FormBuilderProps, type FormDocument, type FormField, type FormTemplate, INDEX_KEY, type ImageField, type InputField, type InputFieldType, type LanguageOption, type LocalizedString, type Option, type ParagraphField, type RadioField, type SavedFormMeta, type Section, type SelectField, type StorageAdapter, type StringsShape, type SubmitPayload, type SubmitStyle, type TemplateChange, type TextareaField, type Theme, type ThemeLayout, type ThemeOverrides, type ToggleField, FormBuilder as default, formKey, localStorageAdapter, parseTemplate, savedFormId, serializeTemplate };
714
+ export { CLIPBOARD_KEY, type CheckboxField, type CheckboxGroupField, type ChromeShape, type ContentBlockType, DARK_THEME, DEFAULT_THEME, DOCUMENT_VERSION, DRAFT_KEY, type DocumentFields, type FieldPatch, type FieldType, FormBuilder, type FormBuilderFeatures, type FormBuilderHandle, type FormBuilderProps, type FormDocument, type FormField, type FormTemplate, INDEX_KEY, type ImageField, type InputField, type InputFieldType, type LanguageOption, type LocalizedString, type Option, type ParagraphField, type RadioField, type SavedFormMeta, type Section, type SelectField, type StorageAdapter, type StringsShape, type SubmitPayload, type SubmitStyle, type TemplateChange, type TextareaField, type Theme, type ThemeLayout, type ThemeOverrides, type ToggleField, FormBuilder as default, formKey, localStorageAdapter, parseTemplate, savedFormId, serializeTemplate };
package/dist/index.d.ts CHANGED
@@ -619,6 +619,8 @@ interface TemplateChange {
619
619
  interface FormBuilderFeatures {
620
620
  /** Editable form title input in the Toolbar. Default true. */
621
621
  naming?: boolean;
622
+ /** Render the form title as a heading above the fields in Preview. Default false — the host usually shows the form name in its own chrome. */
623
+ formTitle?: boolean;
622
624
  /**
623
625
  * Templates library. `true` (default) = full CRUD + toolbar "Save"; `false` = hidden.
624
626
  * `{ manage: false }` = pick-and-apply only (no save/overwrite/delete). `{ max }` caps the count (default 5).
@@ -698,6 +700,9 @@ declare function serializeTemplate(document: FormDocument): string;
698
700
  /** Accepts a `serializeTemplate` envelope or a bare document (e.g. "View JSON" output); `null` if not a document. */
699
701
  declare function parseTemplate(raw: string | null | undefined): DocumentFields | null;
700
702
 
703
+ /** Current document schema version — stamped on everything persisted. */
704
+ declare const DOCUMENT_VERSION: 5;
705
+
701
706
  declare const DRAFT_KEY = "form-page-builder:draft";
702
707
  declare const INDEX_KEY = "form-page-builder:index";
703
708
  declare const formKey: (id: string) => string;
@@ -706,4 +711,4 @@ declare const savedFormId: (key: string) => string | null;
706
711
  /** Copy/paste-template scratch slot — always localStorage, never the StorageAdapter. */
707
712
  declare const CLIPBOARD_KEY = "form-page-builder:clipboard";
708
713
 
709
- export { CLIPBOARD_KEY, type CheckboxField, type CheckboxGroupField, type ChromeShape, type ContentBlockType, DARK_THEME, DEFAULT_THEME, DRAFT_KEY, type DocumentFields, type FieldPatch, type FieldType, FormBuilder, type FormBuilderFeatures, type FormBuilderHandle, type FormBuilderProps, type FormDocument, type FormField, type FormTemplate, INDEX_KEY, type ImageField, type InputField, type InputFieldType, type LanguageOption, type LocalizedString, type Option, type ParagraphField, type RadioField, type SavedFormMeta, type Section, type SelectField, type StorageAdapter, type StringsShape, type SubmitPayload, type SubmitStyle, type TemplateChange, type TextareaField, type Theme, type ThemeLayout, type ThemeOverrides, type ToggleField, FormBuilder as default, formKey, localStorageAdapter, parseTemplate, savedFormId, serializeTemplate };
714
+ export { CLIPBOARD_KEY, type CheckboxField, type CheckboxGroupField, type ChromeShape, type ContentBlockType, DARK_THEME, DEFAULT_THEME, DOCUMENT_VERSION, DRAFT_KEY, type DocumentFields, type FieldPatch, type FieldType, FormBuilder, type FormBuilderFeatures, type FormBuilderHandle, type FormBuilderProps, type FormDocument, type FormField, type FormTemplate, INDEX_KEY, type ImageField, type InputField, type InputFieldType, type LanguageOption, type LocalizedString, type Option, type ParagraphField, type RadioField, type SavedFormMeta, type Section, type SelectField, type StorageAdapter, type StringsShape, type SubmitPayload, type SubmitStyle, type TemplateChange, type TextareaField, type Theme, type ThemeLayout, type ThemeOverrides, type ToggleField, FormBuilder as default, formKey, localStorageAdapter, parseTemplate, savedFormId, serializeTemplate };
package/dist/index.js CHANGED
@@ -558,12 +558,8 @@ function defaultSection(titleEn = "") {
558
558
  return { id: genSectionId(), title: bi(titleEn, ""), background: "", collapsed: false, fields: [] };
559
559
  }
560
560
 
561
- // src/lib/submittable.ts
562
- function sectionHasFormFields(section) {
563
- return section.fields.some((f) => !getMeta(f.type).isContent);
564
- }
565
-
566
561
  // src/lib/migrate.ts
562
+ var DOCUMENT_VERSION = 5;
567
563
  function migrateField(field) {
568
564
  let f = { ...field };
569
565
  if (LEGACY_INPUT_TYPES.includes(f.type)) f = { ...f, type: "input", inputType: f.type };
@@ -594,25 +590,6 @@ function migrateField(field) {
594
590
  function migrateFields(fields) {
595
591
  return (fields || []).map(migrateField);
596
592
  }
597
- function synthesizeButtonField(label, style, scope) {
598
- const resolvedLabel = typeof label === "string" && label ? bi(label) : label && typeof label === "object" && (label.en || label.ja) ? label : bi("Submit", "");
599
- return {
600
- id: nextId(),
601
- type: "button",
602
- label: resolvedLabel,
603
- hideLabel: false,
604
- width: "1/1",
605
- verticalAlign: "top",
606
- labelPosition: "top",
607
- showIcon: false,
608
- displayIcon: "Type",
609
- action: "submit",
610
- submitScope: scope,
611
- buttonStyle: style || { color: "", size: "md" },
612
- href: "",
613
- target: "_self"
614
- };
615
- }
616
593
  function migrateDocument(raw) {
617
594
  if (!raw) return null;
618
595
  const title = typeof raw.title === "string" ? bi(raw.title) : raw.title || bi();
@@ -629,25 +606,6 @@ function migrateDocument(raw) {
629
606
  } else {
630
607
  sections = [{ ...defaultSection(), fields: migrateFields(raw.fields || []) }];
631
608
  }
632
- const legacy = raw.version === void 0 || raw.version < 5;
633
- if (legacy) {
634
- resyncIdCounter(sections.flatMap((s) => s.fields));
635
- const submitMode = raw.submitMode === "perSection" ? "perSection" : "combined";
636
- const submitLabel = raw.submitLabel;
637
- const submitStyle = raw.submitStyle || { color: "", size: "md" };
638
- if (submitMode === "perSection" && raw.sections) {
639
- sections = sections.map((s, i) => {
640
- if (!sectionHasFormFields(s)) return s;
641
- const rawSection = raw.sections[i] || {};
642
- const label = rawSection.submitLabel || submitLabel;
643
- const style = rawSection.submitStyle || submitStyle;
644
- return { ...s, fields: [...s.fields, synthesizeButtonField(label, style, "section")] };
645
- });
646
- } else if (sections.some(sectionHasFormFields)) {
647
- const lastIdx = sections.length - 1;
648
- sections = sections.map((s, i) => i === lastIdx ? { ...s, fields: [...s.fields, synthesizeButtonField(submitLabel, submitStyle, "form")] } : s);
649
- }
650
- }
651
609
  return { title, themeOverrides, sections };
652
610
  }
653
611
 
@@ -675,6 +633,7 @@ function parseTemplate(raw) {
675
633
  var DEFAULT_MAX_TEMPLATES = 5;
676
634
  var DEFAULT_FEATURES = {
677
635
  naming: true,
636
+ formTitle: false,
678
637
  templates: { enabled: true, manage: true, max: DEFAULT_MAX_TEMPLATES },
679
638
  templateClipboard: true,
680
639
  newForm: true,
@@ -1038,7 +997,7 @@ function usePersistence({
1038
997
  if (autosaveTimer.current) clearTimeout(autosaveTimer.current);
1039
998
  autosaveTimer.current = setTimeout(async () => {
1040
999
  try {
1041
- await storage.set(DRAFT_KEY, JSON.stringify({ ...document2, currentFormId }));
1000
+ await storage.set(DRAFT_KEY, JSON.stringify({ version: DOCUMENT_VERSION, ...document2, currentFormId }));
1042
1001
  setSaveState("saved");
1043
1002
  } catch {
1044
1003
  setSaveState("error");
@@ -1061,7 +1020,7 @@ function usePersistence({
1061
1020
  try {
1062
1021
  const newTitle = bi(name, "");
1063
1022
  const docToSave = { ...document2, title: newTitle };
1064
- await storage.set(formKey(id), JSON.stringify({ ...docToSave, id, updatedAt: now }));
1023
+ await storage.set(formKey(id), JSON.stringify({ version: DOCUMENT_VERSION, ...docToSave, id, updatedAt: now }));
1065
1024
  const next = [...savedForms, { id, title: name, updatedAt: now }];
1066
1025
  await storage.set(INDEX_KEY, JSON.stringify(next));
1067
1026
  setSavedForms(next);
@@ -1084,7 +1043,7 @@ function usePersistence({
1084
1043
  const now = Date.now();
1085
1044
  setTemplateState("saving");
1086
1045
  try {
1087
- await storage.set(formKey(currentFormId), JSON.stringify({ ...document2, id: currentFormId, updatedAt: now }));
1046
+ await storage.set(formKey(currentFormId), JSON.stringify({ version: DOCUMENT_VERSION, ...document2, id: currentFormId, updatedAt: now }));
1088
1047
  const next = savedForms.map((f) => f.id === currentFormId ? { ...f, title: t(document2.title, "en"), updatedAt: now } : f);
1089
1048
  setSavedForms(next);
1090
1049
  await storage.set(INDEX_KEY, JSON.stringify(next));
@@ -1909,6 +1868,8 @@ function FieldTicket({
1909
1868
  {
1910
1869
  ...dropZoneHandlers,
1911
1870
  onClick: onSelect,
1871
+ className: `fb-field fb-field--${field.type} fb-field--${field.id}`,
1872
+ "data-field-id": field.id,
1912
1873
  style: {
1913
1874
  ...styles.ticket,
1914
1875
  flex: `1 1 calc(${widthPct} - 10px)`,
@@ -2438,16 +2399,16 @@ function PreviewPane({ title, sections, onFieldChange, language, strings, chrome
2438
2399
  return /* @__PURE__ */ jsxs12("div", { style: styles.previewWrap, children: [
2439
2400
  /* @__PURE__ */ jsxs12("div", { style: { width: "100%", maxWidth, display: "flex", flexDirection: "column", alignItems: "center" }, children: [
2440
2401
  features.deviceToggle && /* @__PURE__ */ jsx14("div", { style: styles.previewToolbar, children: /* @__PURE__ */ jsx14(Segmented, { options: deviceOptions, value: device, onChange: setDevice }) }),
2441
- /* @__PURE__ */ jsxs12("div", { style: { ...styles.previewCard, maxWidth, width: "100%" }, children: [
2442
- /* @__PURE__ */ jsx14("h2", { style: styles.previewTitle, children: t(title, language) }),
2402
+ /* @__PURE__ */ jsxs12("div", { className: "fb-form", style: { ...styles.previewCard, maxWidth, width: "100%" }, children: [
2403
+ features.formTitle && /* @__PURE__ */ jsx14("h2", { style: styles.previewTitle, children: t(title, language) }),
2443
2404
  allFields.length === 0 && /* @__PURE__ */ jsx14("p", { style: { color: "var(--fb-muted)", fontSize: 14 }, children: strings.addFieldsHint }),
2444
2405
  sections.map((section) => {
2445
- return /* @__PURE__ */ jsxs12("div", { style: { background: section.background || "transparent", padding: section.background ? 16 : 0, borderRadius: section.background ? 10 : 0, marginBottom: "var(--fb-space-section)" }, children: [
2406
+ return /* @__PURE__ */ jsxs12("div", { className: "fb-section", "data-section-id": section.id, style: { background: section.background || "transparent", padding: section.background ? 16 : 0, borderRadius: section.background ? 10 : 0, marginBottom: "var(--fb-space-section)" }, children: [
2446
2407
  t(section.title, language) && /* @__PURE__ */ jsx14("h3", { style: styles.sectionRuntimeTitle, children: t(section.title, language) }),
2447
2408
  /* @__PURE__ */ jsx14("div", { style: styles.previewGrid, children: section.fields.map((field) => {
2448
2409
  const width = effectiveWidth(field.width, device);
2449
2410
  const flexBasis = `1 1 calc(${WIDTH_PERCENT[width]} - 14px)`;
2450
- return /* @__PURE__ */ jsxs12("div", { style: { flex: flexBasis, minWidth: 0, alignSelf: ALIGN_MAP[field.verticalAlign || "top"] }, children: [
2411
+ return /* @__PURE__ */ jsxs12("div", { className: `fb-field fb-field--${field.type} fb-field--${field.id}`, "data-field-id": field.id, style: { flex: flexBasis, minWidth: 0, alignSelf: ALIGN_MAP[field.verticalAlign || "top"] }, children: [
2451
2412
  /* @__PURE__ */ jsx14(FieldBlock, { field, lang: language, onFieldChange, strings, chrome, error: errors[field.id], onButtonAction: handleButtonAction }),
2452
2413
  field.type === "button" && field.id === attemptedButtonId && Object.keys(errors).length > 0 && /* @__PURE__ */ jsxs12("p", { style: styles.formErrorNote, children: [
2453
2414
  /* @__PURE__ */ jsx14(CircleAlert2, { size: 13 }),
@@ -2742,7 +2703,7 @@ var FormBuilder = forwardRef(function FormBuilder2({
2742
2703
  ensureActiveSection: () => doc.setActiveSectionId((prev) => prev ?? doc.sections[0]?.id ?? null)
2743
2704
  });
2744
2705
  const jsonDoc = useMemo2(() => ({
2745
- version: 5,
2706
+ version: DOCUMENT_VERSION,
2746
2707
  title: doc.title,
2747
2708
  theme,
2748
2709
  themeOverrides,
@@ -3019,6 +2980,7 @@ export {
3019
2980
  CLIPBOARD_KEY,
3020
2981
  DARK_THEME,
3021
2982
  DEFAULT_THEME,
2983
+ DOCUMENT_VERSION,
3022
2984
  DRAFT_KEY,
3023
2985
  FormBuilder_default as FormBuilder,
3024
2986
  INDEX_KEY,