@measured/puck 0.11.0-canary.c8c02fd → 0.11.0-canary.f9033e2
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 +214 -22
- package/dist/index.css +228 -172
- package/dist/index.d.ts +98 -51
- package/dist/index.js +30556 -1020
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# puck
|
|
2
2
|
|
|
3
|
+
The self-hosted, drag and drop editor for React.
|
|
4
|
+
|
|
3
5
|
<p align="left">
|
|
4
6
|
<a aria-label="Measured logo" href="https://measured.co">
|
|
5
7
|
<img src="https://img.shields.io/badge/MADE%20BY%20Measured-000000.svg?style=for-the-badge&labelColor=000">
|
|
@@ -15,7 +17,7 @@
|
|
|
15
17
|
</a>
|
|
16
18
|
</p>
|
|
17
19
|
|
|
18
|
-
|
|
20
|
+
## Features
|
|
19
21
|
|
|
20
22
|
- 🖱️ **Drag and drop**: Visual editing for your existing React component library
|
|
21
23
|
- 🌐 **Integrations**: Load your content from a 3rd party headless CMS
|
|
@@ -232,6 +234,155 @@ The current DropZone implementation has certain rules and limitations:
|
|
|
232
234
|
3. You can't drag between DropZones that don't share a parent (or _area_)
|
|
233
235
|
4. Your mouse must be directly over a DropZone for a collision to be detected
|
|
234
236
|
|
|
237
|
+
## Adaptors
|
|
238
|
+
|
|
239
|
+
Adaptors can be used to import data from a third-party API, such as a headless CMS.
|
|
240
|
+
|
|
241
|
+
### Example
|
|
242
|
+
|
|
243
|
+
The `external` field type enables us to use an adaptor to query data from a third party API:
|
|
244
|
+
|
|
245
|
+
```tsx
|
|
246
|
+
const myAdaptor = {
|
|
247
|
+
name: "My adaptor",
|
|
248
|
+
fetchList: async () => {
|
|
249
|
+
const response = await fetch("https://www.example.com/api");
|
|
250
|
+
|
|
251
|
+
return {
|
|
252
|
+
text: response.json().text,
|
|
253
|
+
};
|
|
254
|
+
},
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
const config = {
|
|
258
|
+
components: {
|
|
259
|
+
HeadingBlock: {
|
|
260
|
+
fields: {
|
|
261
|
+
myData: {
|
|
262
|
+
type: "external",
|
|
263
|
+
adaptor: myAdaptor,
|
|
264
|
+
},
|
|
265
|
+
},
|
|
266
|
+
render: ({ myData }) => {
|
|
267
|
+
return <h1>{myData.text}</h1>;
|
|
268
|
+
},
|
|
269
|
+
},
|
|
270
|
+
},
|
|
271
|
+
};
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
When the user interacts with this adaptor, they'll be presented with a list of items to choose from. Once they select an item, the value will be mapped onto the prop. In this case, `myData`.
|
|
275
|
+
|
|
276
|
+
## Dynamic prop resolution
|
|
277
|
+
|
|
278
|
+
Dynamic prop resolution allows developers to resolve props for components without saving the data to the Puck data model.
|
|
279
|
+
|
|
280
|
+
### resolveData()
|
|
281
|
+
|
|
282
|
+
`resolveData` is defined in the component config, and allows the developer to make asynchronous calls to change the [ComponentData](#componentdata) after they've been set by Puck. Receives [ComponentData](#componentdata) and returns [ComponentData](#componentdata).
|
|
283
|
+
|
|
284
|
+
#### Examples
|
|
285
|
+
|
|
286
|
+
##### Basic example
|
|
287
|
+
|
|
288
|
+
In this example, we remap the `text` prop to the `title` prop and mark the `title` field as read-only.
|
|
289
|
+
|
|
290
|
+
```tsx
|
|
291
|
+
const config = {
|
|
292
|
+
components: {
|
|
293
|
+
HeadingBlock: {
|
|
294
|
+
fields: {
|
|
295
|
+
text: {
|
|
296
|
+
type: "text",
|
|
297
|
+
},
|
|
298
|
+
title: {
|
|
299
|
+
type: "text",
|
|
300
|
+
},
|
|
301
|
+
},
|
|
302
|
+
resolveData: async (props) => {
|
|
303
|
+
return {
|
|
304
|
+
props: {
|
|
305
|
+
title: props.text,
|
|
306
|
+
},
|
|
307
|
+
readOnly: {
|
|
308
|
+
title: true,
|
|
309
|
+
},
|
|
310
|
+
};
|
|
311
|
+
},
|
|
312
|
+
render: ({ title }) => {
|
|
313
|
+
return <h1>{title}</h1>;
|
|
314
|
+
},
|
|
315
|
+
},
|
|
316
|
+
},
|
|
317
|
+
};
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
##### Combining with adaptors
|
|
321
|
+
|
|
322
|
+
A more advanced pattern is to combine the `resolveData` method with the adaptors to dynamically fetch data when rendering the component.
|
|
323
|
+
|
|
324
|
+
```tsx
|
|
325
|
+
const myAdaptor = {
|
|
326
|
+
name: "My adaptor",
|
|
327
|
+
fetchList: async () => {
|
|
328
|
+
const response = await fetch("https://www.example.com/api");
|
|
329
|
+
|
|
330
|
+
return {
|
|
331
|
+
id: response.json().id,
|
|
332
|
+
};
|
|
333
|
+
},
|
|
334
|
+
};
|
|
335
|
+
|
|
336
|
+
const config = {
|
|
337
|
+
components: {
|
|
338
|
+
HeadingBlock: {
|
|
339
|
+
fields: {
|
|
340
|
+
myData: {
|
|
341
|
+
type: "external",
|
|
342
|
+
adaptor: myAdaptor,
|
|
343
|
+
},
|
|
344
|
+
title: {
|
|
345
|
+
type: "text",
|
|
346
|
+
},
|
|
347
|
+
},
|
|
348
|
+
resolveData: async (props) => {
|
|
349
|
+
if (!myData.id) {
|
|
350
|
+
return { props, readOnly: { title: false } };
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
const latestData = await fetch(
|
|
354
|
+
`https://www.example.com/api/${myData.id}`
|
|
355
|
+
);
|
|
356
|
+
|
|
357
|
+
return {
|
|
358
|
+
props: {
|
|
359
|
+
title: latestData.json().text,
|
|
360
|
+
},
|
|
361
|
+
readOnly: {
|
|
362
|
+
title: true,
|
|
363
|
+
},
|
|
364
|
+
};
|
|
365
|
+
},
|
|
366
|
+
render: ({ title }) => {
|
|
367
|
+
return <h1>{title}</h1>;
|
|
368
|
+
},
|
|
369
|
+
},
|
|
370
|
+
},
|
|
371
|
+
};
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
### resolveAllData()
|
|
375
|
+
|
|
376
|
+
`resolveAllData` is a utility function exported by Puck to enable the developer to run all their `resolveData` methods before rendering the component with `<Render>`.
|
|
377
|
+
|
|
378
|
+
If your `resolveData` methods rely on any external APIs, you should run this before rendering your page.
|
|
379
|
+
|
|
380
|
+
```tsx
|
|
381
|
+
import { resolveAllData } from "@measured/puck";
|
|
382
|
+
|
|
383
|
+
const resolvedData = resolveAllData(data, config);
|
|
384
|
+
```
|
|
385
|
+
|
|
235
386
|
## Reference
|
|
236
387
|
|
|
237
388
|
### `<Puck>`
|
|
@@ -272,11 +423,13 @@ The `Config` object describes which components Puck should render, how they shou
|
|
|
272
423
|
- **title** (`Field`): Title of the content, typically used for the page title.
|
|
273
424
|
- **[fieldName]** (`Field`): User defined fields, used to describe the input data stored in the `root` key.
|
|
274
425
|
- **render** (`Component`): Render a React component at the root of your component tree. Useful for defining context providers.
|
|
426
|
+
- **resolveData** (`async (data: ComponentData) => ComponentData` [optional]): Function to dynamically change props before rendering the root.
|
|
275
427
|
- **components** (`object`): Definitions for each of the components you want to show in the visual editor
|
|
276
428
|
- **[componentName]** (`object`)
|
|
277
429
|
- **fields** (`Field`): The Field objects describing the input data stored against this component.
|
|
278
430
|
- **render** (`Component`): Render function for your React component. Receives props as defined in fields.
|
|
279
431
|
- **defaultProps** (`object` [optional]): Default props to pass to your component. Will show in fields.
|
|
432
|
+
- **resolveData** (`async (data: ComponentData) => ComponentData` [optional]): Function to dynamically change props before rendering the component.
|
|
280
433
|
- **categories** (`object`): Component categories for rendering in the side bar or restricting in DropZones
|
|
281
434
|
- **[categoryName]** (`object`)
|
|
282
435
|
- **components** (`sting[]`, [optional]): Array containing the names of components in this category
|
|
@@ -288,17 +441,58 @@ The `Config` object describes which components Puck should render, how they shou
|
|
|
288
441
|
|
|
289
442
|
A `Field` represents a user input field shown in the Puck interface.
|
|
290
443
|
|
|
291
|
-
|
|
444
|
+
### All Fields
|
|
445
|
+
|
|
292
446
|
- **label** (`text` [optional]): A label for the input. Will use the key if not provided.
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
- **
|
|
297
|
-
|
|
447
|
+
|
|
448
|
+
### Text Fields
|
|
449
|
+
|
|
450
|
+
- **type** (`"text"`)
|
|
451
|
+
|
|
452
|
+
### Textarea Fields
|
|
453
|
+
|
|
454
|
+
- **type** (`"textarea"`)
|
|
455
|
+
|
|
456
|
+
### Number Fields
|
|
457
|
+
|
|
458
|
+
- **type** (`"number"`)
|
|
459
|
+
|
|
460
|
+
### Select Fields
|
|
461
|
+
|
|
462
|
+
- **type** (`"select"`)
|
|
463
|
+
- **options** (`object[]`): array of items to render
|
|
464
|
+
- **label** (`string`)
|
|
465
|
+
- **value** (`string` | `number` | `boolean`)
|
|
466
|
+
|
|
467
|
+
### Radio Fields
|
|
468
|
+
|
|
469
|
+
- **type** (`"radio"`)
|
|
470
|
+
- **options** (`object[]`): array of items to render
|
|
298
471
|
- **label** (`string`)
|
|
299
472
|
- **value** (`string` | `number` | `boolean`)
|
|
300
|
-
|
|
473
|
+
|
|
474
|
+
### Array Fields
|
|
475
|
+
|
|
476
|
+
- **type** (`"array"`)
|
|
477
|
+
- **arrayFields** (`object`): Object describing sub-fields for each item
|
|
478
|
+
- **[fieldName]** (`Field`): The Field objects describing the input data for each item
|
|
479
|
+
- **getItemSummary** (`(object, number) => string` [optional]): Function to get the label of each item
|
|
480
|
+
- **defaultItemProps** (`object` [optional]): Default props to pass to each new item added, when using a `array` field type
|
|
481
|
+
|
|
482
|
+
### External Fields
|
|
483
|
+
|
|
484
|
+
External fields can be used to load content from an external content repository, like Strapi.js, using an `Adaptor`.
|
|
485
|
+
|
|
486
|
+
- **type** (`"external"`)
|
|
487
|
+
- **adaptor** (`Adaptor`): Content adaptor responsible for fetching data to show in the table
|
|
488
|
+
- **name** (`string`): The human-readable name of the adaptor
|
|
489
|
+
- **fetchList** (`(adaptorParams: object) => object`): Fetch content from a third-party API and return an array
|
|
490
|
+
- **mapProp** (`(selectedItem: object) => object`): Map the selected item into another shape
|
|
301
491
|
- **adaptorParams** (`object`): Paramaters passed to the adaptor
|
|
492
|
+
|
|
493
|
+
### Custom Fields
|
|
494
|
+
|
|
495
|
+
- **type** (`"custom"`)
|
|
302
496
|
- **render** (`Component`): Render a custom field. Receives the props:
|
|
303
497
|
- **field** (`Field`): Field configuration
|
|
304
498
|
- **name** (`string`): Name of the field
|
|
@@ -325,22 +519,20 @@ The `AppState` object stores the puck application state.
|
|
|
325
519
|
|
|
326
520
|
The `Data` object stores the puck page data.
|
|
327
521
|
|
|
328
|
-
- **root** (`
|
|
329
|
-
- **
|
|
330
|
-
|
|
331
|
-
- **content** (`
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
- **[prop]** (string): User defined data from component fields
|
|
335
|
-
|
|
336
|
-
### `Adaptor`
|
|
337
|
-
|
|
338
|
-
An `Adaptor` can be used to load content from an external content repository, like Strapi.js.
|
|
522
|
+
- **root** (`ComponentData`): The component data for the root of your configuration.
|
|
523
|
+
- **props** (object): Extends `ComponentData.props`, with some additional props
|
|
524
|
+
- **title** (`string`, [optional]): Title of the content, typically used for the page title
|
|
525
|
+
- **content** (`ComponentData[]`): Component data for the main content
|
|
526
|
+
- **zones** (`object`, [optional]): Component data for all DropZones
|
|
527
|
+
**[zoneCompound]** (`ComponentData[]`): Component data for a specific DropZone `zone` within a component instance
|
|
339
528
|
|
|
340
|
-
|
|
341
|
-
- **fetchList** (`(adaptorParams: object) => object`): Fetch a list of content and return an array
|
|
529
|
+
### `ComponentData`
|
|
342
530
|
|
|
343
|
-
|
|
531
|
+
- **type** (`string`): Component name
|
|
532
|
+
- **props** (`object`):
|
|
533
|
+
- **[prop]** (`any`): User defined data from component fields
|
|
534
|
+
- **readOnly** (`object`): Object describing which fields on the component are currently read-only. Can use dot-notation for arrays, like `array[1].text` or `array[*].text`.
|
|
535
|
+
- **[prop]** (`boolean`): boolean describing whether or not the prop field is read-only
|
|
344
536
|
|
|
345
537
|
### `Plugin`
|
|
346
538
|
|