@measured/puck 0.11.0-canary.c8c02fd → 0.11.0-canary.f9033e2

Sign up to get free protection for your applications and to get access to all the features.
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
- The self-hosted, drag and drop editor for React.
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
- - **type** (`text` | `textarea` | `number` | `select` | `radio` | `external` | `array` | `custom`): The input type to render
444
+ ### All Fields
445
+
292
446
  - **label** (`text` [optional]): A label for the input. Will use the key if not provided.
293
- - **arrayFields** (`object`): Object describing sub-fields for items in an `array` input
294
- - **[fieldName]** (`Field`): The Field objects describing the input data for each item
295
- - **getItemSummary** (`(object, number) => string` [optional]): Function to get the name of each item when using the `array` or `external` field types
296
- - **defaultItemProps** (`object` [optional]): Default props to pass to each new item added, when using a `array` field type
297
- - **options** (`object[]`): array of items to render for select or radio inputs
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
- - **adaptor** (`Adaptor`): Content adaptor if using the `external` input type
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** (`object`):
329
- - **title** (string): Title of the content, typically used for the page title
330
- - **[prop]** (string): User defined data from `root` fields
331
- - **content** (`object[]`):
332
- - **type** (string): Component name
333
- - **props** (object):
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
- - **name** (`string`): The human-readable name of the adaptor
341
- - **fetchList** (`(adaptorParams: object) => object`): Fetch a list of content and return an array
529
+ ### `ComponentData`
342
530
 
343
- > NB Using an adaptor on the reserved field name `_data` will spread the resulting data over your object, and lock the overridden fields.
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