form-page-builder 1.0.0 → 2.0.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 CHANGED
@@ -8,6 +8,21 @@
8
8
 
9
9
  Embeddable, bilingual (EN/JA by default, extensible), drag-and-drop form builder widget for React. Ships a single `<FormBuilder />` component with a Build mode (drag/drop canvas, field inspector) and a Preview mode (responsive, validating runtime form), plus a JSON export of the resulting document.
10
10
 
11
+ ## Examples
12
+
13
+ The [live demo](https://mainsolutioncoltd.github.io/form-page-builder/) is a gallery of six `<FormBuilder />` configurations, each showing a different realistic way to compose `features`/`theme`/`storage`/`initialDocument`/`language` — jump straight to one. Its sticky header (its headings/captions, not the widget's own EN/JA switcher) has an EN/日本語 toggle, a Light/Dark toggle that swaps every example except "Branded" (badged "Fixed theme", since its whole point is a locked theme) between `DEFAULT_THEME` and `DARK_THEME`, and links back to this repo and the npm package.
14
+
15
+ | Example | What it shows |
16
+ |---|---|
17
+ | [Full-featured](https://mainsolutioncoltd.github.io/form-page-builder/#full-featured) | No props beyond `features={{ design: true }}` — the default, everything on. |
18
+ | [Minimal (forms-only embed)](https://mainsolutioncoltd.github.io/form-page-builder/#minimal) | Every optional surface off, a 3-type field allowlist — stripped down for embedding inside a larger app's own chrome. |
19
+ | [Branded, theme locked](https://mainsolutioncoltd.github.io/form-page-builder/#branded) | A fixed `theme` plus `features.design`/`blockStyling` off — colors are locked to the brand, no styling UI exposed. |
20
+ | [Survey builder](https://mainsolutioncoltd.github.io/form-page-builder/#survey) | `fieldTypes`/`contentBlocks` allowlists plus `onSubmit` — a single-shape survey embed that hands you the answers. |
21
+ | [Locked-structure form](https://mainsolutioncoltd.github.io/form-page-builder/#locked-structure) | `initialDocument` seeds a fixed field set; structural toggles are off so only styling/labels stay editable. |
22
+ | [Localized (French)](https://mainsolutioncoltd.github.io/form-page-builder/#localized) | `language`/`languages`/`strings`/`chrome` — a language beyond the built-in EN/JA, partially translated. |
23
+
24
+ The source for all six lives in [dev/main.tsx](./dev/main.tsx) — `npm run dev` runs the same gallery locally against `src/` directly, and is the fastest way to try a `features`/`theme` combination before wiring it into your app.
25
+
11
26
  **This is a builder + viewer, not a data handler.** It builds and previews a JSON *schema* describing a form's fields, sections, and layout blocks (including plain content blocks like paragraphs and images, not just inputs). Preview mode's "Submit" validates and shows a mock "here's what would be sent to your backend" modal, and — if you pass `onSubmit` — hands you the entered values too; either way, this package never sends or stores them itself. The only thing it persists on its own is the *builder's own* draft/Templates state (via the pluggable `StorageAdapter` below); actually delivering submissions to a backend is up to the host app.
12
27
 
13
28
  ## Install
@@ -38,12 +53,16 @@ The widget caps itself at the viewport height (`100vh`, upgrading to `100dvh` on
38
53
 
39
54
  If you *do* size a wrapper to exactly `100vh`/`100dvh` and still see the page itself scroll by a few extra pixels, that's almost always the browser's default `<body>` margin (commonly `8px`) adding to that full-viewport height — reset it yourself (`body { margin: 0; }`), same as you would for any other full-height layout; this package doesn't touch your page's global styles.
40
55
 
56
+ ### Responsive layout
57
+
58
+ Above ~720px wide, Build mode is the usual three-column Palette/Canvas/Inspector layout. Below that, Canvas becomes the full-width primary view, and Palette/Inspector become full-bleed drawers that slide over it instead of permanently eating vertical space — a small "Blocks"/"Properties" bar (reusing the same chrome labels as the desktop tabs, no separate i18n) toggles between them. Tapping a block type in the drawer adds it and returns straight to Canvas; tapping a field in Canvas opens the Properties drawer on it automatically. Nothing gets clipped by the widget's own `overflow: hidden`, and there's no horizontal scrolling. This is CSS-only (a media query in the stylesheet the widget injects) with a small bit of state driving which drawer is open, so it responds to the container's width, not a JS-measured breakpoint — useful if you're embedding it in a narrow sidebar on an otherwise-wide page. Modals size themselves as `width: 100%` up to a `max-width` (with padding around the overlay) instead of a fixed pixel width, so they never overflow a narrow viewport either.
59
+
41
60
  ### Props
42
61
 
43
62
  | Prop | Type | Description |
44
63
  |---|---|---|
45
- | `theme` | `Partial<Theme>` | Override default colors/layout spacing. |
46
- | `themeEditable` | `boolean` | Show the in-app "Design" tab (theme/spacing/submit-button controls) in the left sidebar. |
64
+ | `theme` | `Partial<Theme>` | Override default colors/layout spacing — see "Features vs. theming" below. |
65
+ | `features` | `FormBuilderFeatures` | Independently toggle UI surfaces on/off — full-featured by default. See "Features" below. |
47
66
  | `language` | `string` | Initial builder language (defaults to the first entry in `languages`). |
48
67
  | `languages` | `{ code: string; label: string }[]` | Language switcher options (default: EN/JA). |
49
68
  | `strings` | partial override of runtime/validation strings, keyed by language code | |
@@ -54,6 +73,66 @@ If you *do* size a wrapper to exactly `100vh`/`100dvh` and still see the page it
54
73
 
55
74
  A `ref` on `<FormBuilder />` gives you a `FormBuilderHandle` (`getDocument()` / `loadDocument()` / `exportJson()`) — see "Programmatic integration" below.
56
75
 
76
+ ### Features: `features`
77
+
78
+ Every optional UI surface can be switched on or off independently through one `features` prop, kept deliberately separate from `theme`/`ThemeOverrides` (which control *how* things look, not *whether* they appear). Everything defaults to `true` (`design` defaults to `false`, matching the pre-`features` default), so the default `<FormBuilder />` is unchanged and fully featured; pass only the keys you want to restrict.
79
+
80
+ ```tsx
81
+ // A forms-only embed: no title editing, no templates/JSON/preview chrome,
82
+ // no theming UI, and only two field types available to add.
83
+ <FormBuilder
84
+ features={{
85
+ naming: false,
86
+ templates: false,
87
+ newForm: false,
88
+ autosave: false,
89
+ jsonView: false,
90
+ previewMode: false,
91
+ languageSwitcher: false,
92
+ design: false,
93
+ blockStyling: false,
94
+ contentBlocks: false,
95
+ fieldTypes: ["input", "select"],
96
+ sections: false,
97
+ dragReorder: false,
98
+ }}
99
+ />
100
+ ```
101
+
102
+ | Key | Type | Default | Controls |
103
+ |---|---|---|---|
104
+ | `naming` | `boolean` | `true` | The editable form-title input in the toolbar. |
105
+ | `templates` | `boolean` | `true` | The Templates library (browse/open/delete) and the "Save" button. |
106
+ | `newForm` | `boolean` | `true` | The "New Form" reset button. |
107
+ | `autosave` | `boolean` | `true` | Autosaving the draft to `storage`. The initial draft *load* always happens regardless — this only gates the write path. |
108
+ | `jsonView` | `boolean` | `true` | The "View JSON" button and modal. |
109
+ | `previewMode` | `boolean` | `true` | The Build/Preview tabs. When `false`, the builder stays in Build mode and the tabs are hidden. |
110
+ | `languageSwitcher` | `boolean` | `true` | The language-switcher pill in the toolbar. |
111
+ | `design` | `boolean` | `false` | The global "Design" tab (colors/spacing), i.e. the old `themeEditable` prop. |
112
+ | `blockStyling` | `boolean` | `true` | Per-field styling controls: paragraph heading/font/color, button color — independent of `design`. |
113
+ | `contentBlocks` | `boolean \| ("paragraph" \| "image" \| "button")[]` | `true` | Which content blocks can be *added* from the palette. `true` = all, `false` = none, or an allowlist. |
114
+ | `fieldTypes` | `boolean \| ("input" \| "textarea" \| "select" \| "radio" \| "checkboxGroup" \| "checkbox" \| "toggle")[]` | `true` | Which form field types can be *added* from the palette. `true` = all, `false` = none, or an allowlist. |
115
+ | `sections` | `boolean` | `true` | Add/duplicate/move/delete-section controls and the section background picker. |
116
+ | `dragReorder` | `boolean` | `true` | Drag-to-reorder fields within a section. |
117
+
118
+ Disabling `contentBlocks`/`fieldTypes` for a given type only hides it from the palette going forward — if a document loaded via `initialDocument` (or a saved template) already contains fields of a now-disabled type, they still render and remain editable in Build mode; nothing is stripped or hidden.
119
+
120
+ ### Features vs. theming
121
+
122
+ `features` and `theme` are separate, composable concerns: `features.design` decides whether the Design tab's color/spacing *controls* are shown at all, while `theme` (and the Design tab, when shown) decide what those colors/spacing values *are*. You can, for instance, pass a fixed `theme` with `features.design` and `features.blockStyling` both `false` to lock a form to your brand's colors with zero styling UI exposed to the builder's user.
123
+
124
+ ### Dark theme
125
+
126
+ Every color in the widget — including modals, toggles, and badges, not just the canvas/toolbar — is driven by the `theme` prop via CSS custom properties, so dark mode is just a different set of color values, no separate "dark mode" flag needed:
127
+
128
+ ```tsx
129
+ import { FormBuilder, DARK_THEME } from "form-page-builder";
130
+
131
+ <FormBuilder theme={DARK_THEME} />;
132
+ ```
133
+
134
+ `DARK_THEME` (and `DEFAULT_THEME`, the light palette used when `theme` is omitted) are both exported as plain `Theme` objects, so you can spread and tweak either one (`{ ...DARK_THEME, primary: "#22c55e" }`) or swap between them at runtime for a user-facing light/dark toggle — see the "Localized (French)"-adjacent light/Dark switch in the [live demo](https://mainsolutioncoltd.github.io/form-page-builder/) for a working example.
135
+
57
136
  ### Persistence: `StorageAdapter`
58
137
 
59
138
  By default the component autosaves a draft, plus a "Templates" library (up to 5 saved forms, shown via the toolbar's Templates button), to `window.localStorage`. Being local-storage-only means neither persists across devices or browsers. To persist the builder's state to your own backend (a Next.js API route, a PHP endpoint, etc.) instead — so drafts and templates are shared across devices, and your backend can populate/manage the template list itself — implement and pass a `StorageAdapter`. Its `get`/`set`/`delete` calls *are* the create/update/delete hooks: whatever your implementation does inside them (write to a database, call your API) runs on every template save/update/delete, so no separate event callbacks are needed.
@@ -183,10 +262,13 @@ The `?external=react,react-dom` query on the `form-page-builder` import tells es
183
262
  npm install
184
263
  npm run dev # Vite dev harness at http://localhost:5173, imports FormBuilder from src/
185
264
  npm run typecheck
265
+ npm run test # vitest run -- see tests/
186
266
  npm run build # tsup -> dist/ (ESM + CJS + .d.ts), the published package
187
267
  npm run build:demo # vite build -> pages-dist/, the GitHub Pages demo site
188
268
  ```
189
269
 
270
+ Tests use [Vitest](https://vitest.dev) + [Testing Library](https://testing-library.com/react) against jsdom, covering rendering, the `features` prop's gating of each UI surface, the `FormBuilderHandle` ref API, autosave/`initialDocument`, and the Preview-mode validation + `onSubmit` flow. `npm run test:watch` re-runs on change.
271
+
190
272
  ## Releasing
191
273
 
192
274
  Versioning and the changelog are managed by [Changesets](https://github.com/changesets/changesets); publishing to npm is a separate, deliberate step.