@nextbridgehq/payload-block-builder 0.1.8 → 0.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 +280 -76
- package/dist/bin/init.js +0 -0
- package/dist/client.cjs +1487 -603
- package/dist/client.d.cts +6 -2
- package/dist/client.d.ts +6 -2
- package/dist/client.js +1438 -530
- package/dist/index.cjs +887 -520
- package/dist/index.d.cts +52 -8
- package/dist/index.d.ts +52 -8
- package/dist/index.js +884 -518
- package/package.json +29 -10
- package/src/block-builder/builder.css +391 -36
- package/src/components/BlockDataField/BlockDataField.css +652 -393
- package/src/components/SchemaBuilderField/SchemaBuilderField.css +361 -361
package/README.md
CHANGED
|
@@ -1,50 +1,70 @@
|
|
|
1
1
|
# Payload Block Builder
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/@nextbridgehq/payload-block-builder)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
[](https://payloadcms.com)
|
|
4
6
|
|
|
5
|
-
|
|
7
|
+
> A visual block builder plugin for Payload CMS v3. Design content blocks through a drag-and-drop UI, store schemas in your database, and let editors build pages without waiting on a developer.
|
|
6
8
|
|
|
7
|
-
|
|
9
|
+
Developed and open-sourced by [Nextbridge](https://nextbridge.com).
|
|
8
10
|
|
|
9
|
-
|
|
11
|
+
[GitHub](https://github.com/nextbridgehq/block-builder) · [Releases](https://github.com/nextbridgehq/block-builder/releases) · [Issues](https://github.com/nextbridgehq/block-builder/issues) · [Changelog](./CHANGELOG.md) · [Payload CMS](https://payloadcms.com)
|
|
10
12
|
|
|
11
|
-
|
|
12
|
-
- **Multi-tenant platforms:** Each tenant can have its own block definitions without touching shared config or triggering redeployments.
|
|
13
|
-
- **Marketing teams:** Give marketing full control to create, update, and reorder blocks on any page, any time.
|
|
14
|
-
- **Evolving content schemas:** Roll out new block versions without breaking content that was built against older ones.
|
|
15
|
-
- **Headless frontends:** Fetch structured block data from the Payload API and render it with any framework.
|
|
13
|
+
## Screenshots
|
|
16
14
|
|
|
17
|
-
|
|
15
|
+

|
|
16
|
+

|
|
17
|
+

|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
---
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
21
|
+
## 🎯 The Problem
|
|
22
|
+
|
|
23
|
+
Content editors need to manage flexible page layouts — but every new block type or layout change requires a developer to update code, redeploy, and migrate. This creates bottlenecks, slows down marketing teams, and turns simple content tasks into engineering tickets.
|
|
24
|
+
|
|
25
|
+
## 💡 The Solution
|
|
26
|
+
|
|
27
|
+
Payload Block Builder moves block schema definitions from code into your database. Editors design blocks visually, publish them instantly, and use them across any collection — all without touching code or triggering deployments.
|
|
26
28
|
|
|
27
29
|
---
|
|
28
30
|
|
|
29
|
-
##
|
|
31
|
+
## ✨ Features
|
|
32
|
+
|
|
33
|
+
- **Visual drag-and-drop block designer** — No code required to create new block types
|
|
34
|
+
- **Database-stored schemas** — Block definitions live in your DB, not your codebase
|
|
35
|
+
- **Version snapshots with restore** — Every publish creates an immutable, concurrency-safe version; browse history, view any past version read-only, or restore it as a new version — existing content never breaks
|
|
36
|
+
- **Collection integration** — Adds a "DB Layout" tab to any collection with one line of config, plus a nav-sidebar shortcut and an "Edit in Builder" button on each block definition
|
|
37
|
+
- **16 field types**, including layout containers — text, textarea, number, email, date, checkbox, select, image, file, relationship, json, and the Row / Group / Array / Tabs / Collapsible layout fields for structuring and nesting a block's own fields
|
|
38
|
+
- **Live Preview** — A self-contained, zero-config panel that renders a block's schema as a mock form as you build it — labels, field slugs (the exact JSON path a frontend integration should read, including array/group nesting and named-tab keys), and realistic placeholders per field type. No external URL or frontend receiver needed.
|
|
39
|
+
- **JSON Import / Export** — Export any block's schema to a `.json` file, or import a hand-authored or previously-exported one back into the builder
|
|
40
|
+
- **Multi-tenant ready** — Each tenant can have its own block definitions without shared config changes
|
|
41
|
+
- **Framework agnostic frontend** — Fetch structured JSON and render with React, Vue, Svelte, or anything else
|
|
42
|
+
- **Automatic init command** — Get up and running in under 2 minutes
|
|
43
|
+
- **Works with all Payload databases** — PostgreSQL, SQLite, MongoDB — no database-specific code
|
|
44
|
+
|
|
45
|
+
---
|
|
30
46
|
|
|
31
|
-
|
|
47
|
+
## 🚀 Quick Start
|
|
32
48
|
|
|
33
|
-
|
|
49
|
+
### Option A — Automatic Setup (Recommended)
|
|
34
50
|
|
|
35
51
|
```bash
|
|
52
|
+
# Install
|
|
36
53
|
pnpm add @nextbridgehq/payload-block-builder
|
|
37
54
|
# or: npm install @nextbridgehq/payload-block-builder
|
|
38
55
|
|
|
56
|
+
# Initialize
|
|
39
57
|
npx payload-block-builder init
|
|
40
58
|
```
|
|
41
59
|
|
|
42
60
|
The init command automatically:
|
|
43
61
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
62
|
+
| What it does | File |
|
|
63
|
+
|---|---|
|
|
64
|
+
| Creates the builder UI page | `src/app/block-builder/page.tsx` |
|
|
65
|
+
| Creates a standalone layout | `src/app/block-builder/layout.tsx` |
|
|
66
|
+
| Injects admin field styles | `src/app/(payload)/custom.scss` |
|
|
67
|
+
| Adds plugin config | `payload.config.ts` |
|
|
48
68
|
|
|
49
69
|
Then regenerate the import map and start your dev server:
|
|
50
70
|
|
|
@@ -53,17 +73,10 @@ pnpm generate:importmap
|
|
|
53
73
|
pnpm dev
|
|
54
74
|
```
|
|
55
75
|
|
|
56
|
-
Visit `
|
|
57
|
-
|
|
58
|
-
> **PostgreSQL users:** Payload will automatically push the new schema tables on first startup in dev mode. If you are using migrations in production, run:
|
|
59
|
-
> ```bash
|
|
60
|
-
> pnpm payload migrate:create --name=add_block_builder
|
|
61
|
-
> pnpm payload migrate
|
|
62
|
-
> ```
|
|
63
|
-
|
|
64
|
-
---
|
|
76
|
+
Visit `https://your-domain.com/block-builder` and you're ready to build.
|
|
65
77
|
|
|
66
|
-
|
|
78
|
+
<details>
|
|
79
|
+
<summary><strong>Option B — Manual Setup</strong></summary>
|
|
67
80
|
|
|
68
81
|
**1. Install:**
|
|
69
82
|
|
|
@@ -138,19 +151,70 @@ pnpm generate:importmap
|
|
|
138
151
|
pnpm dev
|
|
139
152
|
```
|
|
140
153
|
|
|
154
|
+
</details>
|
|
155
|
+
|
|
141
156
|
---
|
|
142
157
|
|
|
143
|
-
##
|
|
158
|
+
## Upgrading from 0.1.x
|
|
159
|
+
|
|
160
|
+
**0.2.0 changes the database schema.** The `block-definition-versions`
|
|
161
|
+
collection gains a `versionIdString` column with a unique index — this is what
|
|
162
|
+
makes concurrent publishing safe, replacing the previous count-then-insert
|
|
163
|
+
version numbering that could hand out the same number twice.
|
|
164
|
+
|
|
165
|
+
Run a migration before deploying:
|
|
166
|
+
|
|
167
|
+
```bash
|
|
168
|
+
pnpm payload migrate:create --name=block_builder_0_2_0
|
|
169
|
+
pnpm payload migrate
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
In dev mode Payload pushes the column automatically; **production will not start
|
|
173
|
+
correctly without the migration.**
|
|
174
|
+
|
|
175
|
+
Notes:
|
|
176
|
+
|
|
177
|
+
- Existing version rows are backfilled with `NULL`, which the unique index
|
|
178
|
+
permits. Historical versions are readable and restorable as before, but the
|
|
179
|
+
uniqueness guarantee only applies to versions published from 0.2.0 onward.
|
|
180
|
+
- MongoDB users need no migration.
|
|
181
|
+
|
|
182
|
+
### Other breaking changes in 0.2.0
|
|
183
|
+
|
|
184
|
+
| Change | Impact |
|
|
185
|
+
| --- | --- |
|
|
186
|
+
| Internal endpoints require an `X-Block-Builder: 1` header | Only affects code calling `/api/blocks/*` directly. The builder UI and admin components send it already. |
|
|
187
|
+
| `generateAllBlocks()` returns one file per block again | Pass `{ react: true }` to also emit the `.tsx` component stub, which 0.2.0-beta emitted unconditionally. |
|
|
188
|
+
| Field-type vocabulary unified | The builder now uses `richtext`, `image`, `file`, and `collection` internally, matching the stored schema. Generated Payload config is unaffected — it is translated at emit time to `richText`, `upload`, and `relationTo`. |
|
|
189
|
+
| `radio` and `upload` removed from the palette | Use `select` and `image`/`file`. Existing schemas still load. |
|
|
190
|
+
|
|
191
|
+
> **PostgreSQL users:** Payload will automatically push new schema tables on first startup in dev mode. For production migrations:
|
|
192
|
+
>
|
|
193
|
+
> ```bash
|
|
194
|
+
> pnpm payload migrate:create --name=add_block_builder
|
|
195
|
+
> pnpm payload migrate
|
|
196
|
+
> ```
|
|
197
|
+
|
|
198
|
+
---
|
|
199
|
+
|
|
200
|
+
## ⚙️ Configuration Options
|
|
144
201
|
|
|
145
202
|
```ts
|
|
146
203
|
dynamicBlocksPlugin({
|
|
147
|
-
enabled?: boolean
|
|
148
|
-
collections?: string[]
|
|
149
|
-
fieldName?: string
|
|
150
|
-
tabLabel?: string
|
|
204
|
+
enabled?: boolean, // Disable without removing. Default: true
|
|
205
|
+
collections?: string[], // Collection slugs that get the DB Layout tab. Default: []
|
|
206
|
+
fieldName?: string, // Name of the layout array field. Default: 'dbLayout'
|
|
207
|
+
tabLabel?: string, // Label shown on the tab in the admin UI. Default: 'DB Layout'
|
|
151
208
|
})
|
|
152
209
|
```
|
|
153
210
|
|
|
211
|
+
| Option | Type | Default | Description |
|
|
212
|
+
|---|---|---|---|
|
|
213
|
+
| `enabled` | `boolean` | `true` | Toggle the plugin on/off without removing it from config |
|
|
214
|
+
| `collections` | `string[]` | `[]` | Collection slugs that receive the DB Layout tab |
|
|
215
|
+
| `fieldName` | `string` | `'dbLayout'` | The field name for the layout array stored on documents |
|
|
216
|
+
| `tabLabel` | `string` | `'DB Layout'` | Label displayed on the tab in the Payload admin UI |
|
|
217
|
+
|
|
154
218
|
The `--collections` flag is also supported in the init command:
|
|
155
219
|
|
|
156
220
|
```bash
|
|
@@ -159,74 +223,138 @@ npx payload-block-builder init --collections=pages,posts
|
|
|
159
223
|
|
|
160
224
|
---
|
|
161
225
|
|
|
162
|
-
## Usage
|
|
226
|
+
## 📖 Usage
|
|
163
227
|
|
|
164
|
-
### Creating a
|
|
228
|
+
### Creating a Block
|
|
165
229
|
|
|
166
|
-
1. Open `/block-builder` in your browser.
|
|
230
|
+
1. Open `/block-builder` in your browser (also reachable from the admin sidebar and via the "Edit in Builder" button on any block definition).
|
|
167
231
|
2. Click "Add Block" and give it a name and slug.
|
|
168
|
-
3. Drag fields from the panel on the
|
|
169
|
-
4. Configure each field (label, name, required, options, etc.).
|
|
170
|
-
5.
|
|
232
|
+
3. Drag fields from the panel on the left onto the canvas — including layout fields (Row, Group, Array, Tabs, Collapsible) to nest and structure fields; use each layout field's "Edit Fields" button to drill into its contents.
|
|
233
|
+
4. Configure each field (label, name, required, options, etc.) in the panel on the right.
|
|
234
|
+
5. Open **Live Preview** at any time to see a mock rendering of the block's current shape, with each field's exact data path.
|
|
235
|
+
6. Click Publish — the block schema is validated and saved to your database, and a new immutable version snapshot is created.
|
|
236
|
+
7. Use **Import JSON** / **Export JSON** in the toolbar to move a block's schema in or out as a file — handy for backups, sharing a schema between projects, or hand-authoring one.
|
|
171
237
|
|
|
172
|
-
### Using
|
|
238
|
+
### Using Blocks in a Collection
|
|
173
239
|
|
|
174
240
|
Any collection listed in the `collections` option gets a new "DB Layout" tab in the Payload admin. Editors can:
|
|
175
241
|
|
|
176
242
|
1. Click "Add Row" to add a block instance.
|
|
177
|
-
2. Select a block definition
|
|
243
|
+
2. Select a block definition — its current version is auto-selected, or pick a specific version manually.
|
|
178
244
|
3. Fill in the fields — they render dynamically based on the selected schema.
|
|
179
245
|
4. Reorder, hide, or add anchor IDs to individual block instances.
|
|
180
246
|
5. Save the document as normal.
|
|
181
247
|
|
|
182
|
-
###
|
|
248
|
+
### Live Preview
|
|
249
|
+
|
|
250
|
+
The Live Preview panel (toggled from the builder's toolbar) renders the *shape* of a block being designed — not a real page, since the builder has no way to know what a "Hero" block should look like on your actual frontend. For each field it shows:
|
|
251
|
+
|
|
252
|
+
- The field's label and, right beside it, its **slug** — the exact key a frontend integration reads. For a top-level field this is just its name (`heading`); for a field nested in a Group it's dot-prefixed (`cta.label`); for one inside an Array it gets a trailing `[]` (`items[].title`); for a field inside a *named* Tab it's prefixed by the tab's name (`seo.metaTitle`). Row, Collapsible, and unnamed Tabs are presentation-only in Payload and flatten their children into the surrounding data — so they never show a slug of their own, and their children inherit whatever prefix they themselves received.
|
|
253
|
+
- A realistic, type-appropriate placeholder (respecting an explicit `admin.placeholder` if one is set).
|
|
254
|
+
|
|
255
|
+
### Reading Block Data on the Frontend
|
|
183
256
|
|
|
184
257
|
```ts
|
|
185
258
|
const res = await fetch('/api/pages/my-page?depth=2')
|
|
186
259
|
const page = await res.json()
|
|
187
260
|
|
|
188
261
|
for (const block of page.dbLayout) {
|
|
189
|
-
const type = block.blockDefinition.slug
|
|
190
|
-
const fields = block.data
|
|
262
|
+
const type = block.blockDefinition.slug // e.g. "hero"
|
|
263
|
+
const fields = block.data // { heading: '...', image: '...', ... }
|
|
191
264
|
const isHidden = block.hidden
|
|
265
|
+
const anchor = block.anchor
|
|
266
|
+
}
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
### Example: React Component Map
|
|
270
|
+
|
|
271
|
+
```tsx
|
|
272
|
+
const blockComponents = {
|
|
273
|
+
hero: HeroBlock,
|
|
274
|
+
features: FeaturesBlock,
|
|
275
|
+
testimonials: TestimonialsBlock,
|
|
276
|
+
cta: CTABlock,
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
function PageRenderer({ blocks }) {
|
|
280
|
+
return (
|
|
281
|
+
<>
|
|
282
|
+
{blocks
|
|
283
|
+
.filter((block) => !block.hidden)
|
|
284
|
+
.map((block, i) => {
|
|
285
|
+
const Component = blockComponents[block.blockDefinition.slug]
|
|
286
|
+
if (!Component) return null
|
|
287
|
+
return (
|
|
288
|
+
<section key={block.instanceId ?? i} id={block.anchor || undefined}>
|
|
289
|
+
<Component {...block.data} />
|
|
290
|
+
</section>
|
|
291
|
+
)
|
|
292
|
+
})}
|
|
293
|
+
</>
|
|
294
|
+
)
|
|
192
295
|
}
|
|
193
296
|
```
|
|
194
297
|
|
|
195
|
-
|
|
298
|
+
---
|
|
299
|
+
|
|
300
|
+
## 🧩 Supported Field Types
|
|
301
|
+
|
|
302
|
+
| Category | Types |
|
|
303
|
+
|---|---|
|
|
304
|
+
| Basic | `text`, `textarea`, `number`, `email`, `date`, `checkbox` |
|
|
305
|
+
| Choice | `select` (custom label/value options) |
|
|
306
|
+
| Media | `image`, `file` |
|
|
307
|
+
| Relational | `relationship` (link to any collection, optionally `hasMany`) |
|
|
308
|
+
| Layout | `array`, `group`, `row`, `tabs`, `collapsible` |
|
|
309
|
+
| Advanced | `json` (raw JSON data) |
|
|
310
|
+
|
|
311
|
+
**Layout fields** structure and nest a block's other fields rather than holding a value themselves:
|
|
312
|
+
|
|
313
|
+
| Type | Behavior |
|
|
314
|
+
|---|---|
|
|
315
|
+
| `group` | Nests its fields' data under its own name (`group.field`) |
|
|
316
|
+
| `array` | A repeating list of fields; each row's data lives under the array's name (`items[].field`) |
|
|
317
|
+
| `row` | Presentation only — lays its fields out horizontally; their data flattens into the parent, no nesting |
|
|
318
|
+
| `collapsible` | Presentation only — an expandable section; its fields also flatten into the parent |
|
|
319
|
+
| `tabs` | A tab strip; a *named* tab nests its fields under the tab's name, an *unnamed* tab flattens into the parent |
|
|
320
|
+
|
|
321
|
+
Live Preview shows the exact resolved data path for every field, including through nested layout fields — see the Live Preview section above.
|
|
196
322
|
|
|
197
323
|
---
|
|
198
324
|
|
|
199
|
-
##
|
|
325
|
+
## 🗺️ Use Cases
|
|
200
326
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
-
|
|
205
|
-
|
|
327
|
+
| Use Case | How It Helps |
|
|
328
|
+
|---|---|
|
|
329
|
+
| Dynamic landing pages | Editors compose pages from a library of blocks (hero, features, testimonials, CTA) without code changes |
|
|
330
|
+
| Multi-tenant platforms | Each tenant gets its own block definitions without touching shared config or triggering redeployments |
|
|
331
|
+
| Marketing teams | Full control to create, update, and reorder blocks on any page, any time |
|
|
332
|
+
| Evolving content schemas | Roll out new block versions without breaking content built against older ones |
|
|
333
|
+
| Headless frontends | Fetch structured block data from the Payload API and render with any framework |
|
|
206
334
|
|
|
207
335
|
---
|
|
208
336
|
|
|
209
|
-
##
|
|
337
|
+
## 📋 Compatibility
|
|
210
338
|
|
|
211
|
-
|
|
339
|
+
| Requirement | Version |
|
|
340
|
+
|---|---|
|
|
341
|
+
| Payload CMS | v3.x |
|
|
342
|
+
| Node.js | ≥ 18 |
|
|
343
|
+
| Next.js | ≥ 14 |
|
|
344
|
+
|
|
345
|
+
### Database Support
|
|
212
346
|
|
|
213
|
-
|
|
|
347
|
+
| Database | Adapter |
|
|
214
348
|
|---|---|
|
|
215
|
-
|
|
|
216
|
-
|
|
|
217
|
-
|
|
|
218
|
-
| `email` | Email address |
|
|
219
|
-
| `date` | Date picker |
|
|
220
|
-
| `checkbox` | Boolean toggle |
|
|
221
|
-
| `select` | Dropdown with custom options |
|
|
222
|
-
| `radio` | Radio button group with custom options |
|
|
223
|
-
| `upload` | File / image picker (from the media collection) |
|
|
224
|
-
| `relationship` | Document picker from any collection |
|
|
225
|
-
| `json` | Raw JSON data |
|
|
349
|
+
| PostgreSQL / Supabase / Neon | `@payloadcms/db-postgres` |
|
|
350
|
+
| SQLite / Turso / LibSQL | `@payloadcms/db-sqlite` |
|
|
351
|
+
| MongoDB | `@payloadcms/db-mongodb` |
|
|
226
352
|
|
|
227
353
|
---
|
|
228
354
|
|
|
229
|
-
##
|
|
355
|
+
## 🔧 Advanced Usage
|
|
356
|
+
|
|
357
|
+
### Using `dbLayoutField` Directly
|
|
230
358
|
|
|
231
359
|
If you prefer not to use the plugin's `collections` option, you can add the layout tab manually to any collection:
|
|
232
360
|
|
|
@@ -239,9 +367,9 @@ export const Pages: CollectionConfig = {
|
|
|
239
367
|
{
|
|
240
368
|
type: 'tabs',
|
|
241
369
|
tabs: [
|
|
242
|
-
{ label: 'Content', fields: [] },
|
|
243
|
-
dbLayoutField(),
|
|
244
|
-
dbLayoutField('heroBlocks', 'Hero'),
|
|
370
|
+
{ label: 'Content', fields: [/* your fields */] },
|
|
371
|
+
dbLayoutField(), // fieldName='dbLayout', tab label='DB Layout'
|
|
372
|
+
dbLayoutField('heroBlocks', 'Hero'), // custom field name and tab label
|
|
245
373
|
],
|
|
246
374
|
},
|
|
247
375
|
],
|
|
@@ -250,7 +378,44 @@ export const Pages: CollectionConfig = {
|
|
|
250
378
|
|
|
251
379
|
---
|
|
252
380
|
|
|
253
|
-
##
|
|
381
|
+
## 🏗️ Architecture
|
|
382
|
+
|
|
383
|
+
```
|
|
384
|
+
┌─────────────────────────────────────────────────────┐
|
|
385
|
+
│ Block Builder UI │
|
|
386
|
+
│ /block-builder (drag & drop) │
|
|
387
|
+
└──────────────────────────┬──────────────────────────┘
|
|
388
|
+
│ Publish
|
|
389
|
+
▼
|
|
390
|
+
┌─────────────────────────────────────────────────────┐
|
|
391
|
+
│ block-definitions collection │
|
|
392
|
+
│ (name, slug, field definitions) │
|
|
393
|
+
└──────────────────────────┬──────────────────────────┘
|
|
394
|
+
│ Snapshot
|
|
395
|
+
▼
|
|
396
|
+
┌─────────────────────────────────────────────────────┐
|
|
397
|
+
│ block-definition-versions collection │
|
|
398
|
+
│ (immutable schema snapshots per publish) │
|
|
399
|
+
└──────────────────────────┬──────────────────────────┘
|
|
400
|
+
│ Referenced by
|
|
401
|
+
▼
|
|
402
|
+
┌─────────────────────────────────────────────────────┐
|
|
403
|
+
│ Your Collection (e.g. "pages") │
|
|
404
|
+
│ dbLayout: [{ blockDefinition, version, data }] │
|
|
405
|
+
└─────────────────────────────────────────────────────┘
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
Key design decisions:
|
|
409
|
+
|
|
410
|
+
- Block definitions are stored in a `block-definitions` collection. Each document is a named block type with a slug and a list of field definitions.
|
|
411
|
+
- Versions are stored in a `block-definition-versions` collection. Every publish creates an immutable snapshot; version numbers are assigned with a retry loop against a unique constraint, so concurrent publishes from two editors can never collide.
|
|
412
|
+
- Documents in opted-in collections store a reference to the exact block version they were built against — updating a block schema later does not break existing content.
|
|
413
|
+
- The DB Layout tab is injected automatically into each collection you list. It renders a dynamic array field where editors pick a block and version.
|
|
414
|
+
- Four internal API endpoints power the builder UI and admin field components — each requires an authenticated Payload session and a `X-Block-Builder: 1` header (CSRF protection). You do not need to call them directly.
|
|
415
|
+
|
|
416
|
+
---
|
|
417
|
+
|
|
418
|
+
## 📦 CSS Imports Reference
|
|
254
419
|
|
|
255
420
|
| Import path | Purpose |
|
|
256
421
|
|---|---|
|
|
@@ -260,6 +425,45 @@ export const Pages: CollectionConfig = {
|
|
|
260
425
|
|
|
261
426
|
---
|
|
262
427
|
|
|
263
|
-
##
|
|
428
|
+
## 🧪 Testing
|
|
429
|
+
|
|
430
|
+
The package ships a Vitest suite (114 tests, including a golden-file snapshot of generated code per field type) covering schema normalization, validation, and code generation:
|
|
431
|
+
|
|
432
|
+
```bash
|
|
433
|
+
npm test # run once
|
|
434
|
+
npm run test:watch # watch mode
|
|
435
|
+
npm run test:coverage # with coverage thresholds
|
|
436
|
+
npm run check:encoding # scan for stray UTF-8 BOMs / mojibake
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
CI (`.github/workflows/ci.yml`) runs typecheck → test → build → encoding check on every push.
|
|
440
|
+
|
|
441
|
+
---
|
|
442
|
+
|
|
443
|
+
## 📝 Changelog
|
|
444
|
+
|
|
445
|
+
See [CHANGELOG.md](./CHANGELOG.md) for the full history. Latest release:
|
|
446
|
+
|
|
447
|
+
**0.2.0** — Live Preview, Layout fields (Row/Group/Array/Tabs/Collapsible), JSON Import/Export, version history & restore, concurrency-safe publishing, CSRF protection, a 114-test suite with CI, and a large batch of correctness fixes (generated-code compilation, nested-field validation, breadcrumb navigation, and more — see CHANGELOG.md for the full list).
|
|
448
|
+
|
|
449
|
+
---
|
|
450
|
+
|
|
451
|
+
## 🤝 Contributing
|
|
452
|
+
|
|
453
|
+
Contributions are welcome! Please see our Contributing Guide for details.
|
|
454
|
+
|
|
455
|
+
- Fork the repository
|
|
456
|
+
- Create your feature branch (`git checkout -b feature/amazing-feature`)
|
|
457
|
+
- Commit your changes (`git commit -m 'Add amazing feature'`)
|
|
458
|
+
- Push to the branch (`git push origin feature/amazing-feature`)
|
|
459
|
+
- Open a Pull Request
|
|
460
|
+
|
|
461
|
+
---
|
|
462
|
+
|
|
463
|
+
## 📄 License
|
|
264
464
|
|
|
265
465
|
MIT © [Nextbridge](https://nextbridge.com)
|
|
466
|
+
|
|
467
|
+
---
|
|
468
|
+
|
|
469
|
+
Built and maintained by **[Nextbridge](https://nextbridge.com)** — If Payload Block Builder helped you ship content blocks without waiting on a developer, a ⭐ would mean a lot — it helps other developers discover Payload Block Builder.
|
package/dist/bin/init.js
CHANGED
|
File without changes
|