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

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
@@ -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,146 @@ 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
+ ## External fields
238
+
239
+ External fields 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 query data from a third party API:
244
+
245
+ ```tsx
246
+ const config = {
247
+ components: {
248
+ HeadingBlock: {
249
+ fields: {
250
+ myData: {
251
+ type: "external",
252
+ fetchList: async () => {
253
+ const response = await fetch("https://www.example.com/api");
254
+
255
+ return {
256
+ text: response.json().text,
257
+ };
258
+ },
259
+ },
260
+ },
261
+ render: ({ myData }) => {
262
+ return <h1>{myData.text}</h1>;
263
+ },
264
+ },
265
+ },
266
+ };
267
+ ```
268
+
269
+ When the user interacts with this external field, 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`.
270
+
271
+ ## Dynamic prop resolution
272
+
273
+ Dynamic prop resolution allows developers to resolve props for components without saving the data to the Puck data model.
274
+
275
+ ### resolveData()
276
+
277
+ `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).
278
+
279
+ #### Examples
280
+
281
+ ##### Basic example
282
+
283
+ In this example, we remap the `text` prop to the `title` prop and mark the `title` field as read-only.
284
+
285
+ ```tsx
286
+ const config = {
287
+ components: {
288
+ HeadingBlock: {
289
+ fields: {
290
+ text: {
291
+ type: "text",
292
+ },
293
+ title: {
294
+ type: "text",
295
+ },
296
+ },
297
+ resolveData: async (props) => {
298
+ return {
299
+ props: {
300
+ title: props.text,
301
+ },
302
+ readOnly: {
303
+ title: true,
304
+ },
305
+ };
306
+ },
307
+ render: ({ title }) => {
308
+ return <h1>{title}</h1>;
309
+ },
310
+ },
311
+ },
312
+ };
313
+ ```
314
+
315
+ ##### Combining with external fields
316
+
317
+ A more advanced pattern is to combine the `resolveData` method with `external` fields to dynamically fetch data when rendering the component.
318
+
319
+ ```tsx
320
+ const config = {
321
+ components: {
322
+ HeadingBlock: {
323
+ fields: {
324
+ myData: {
325
+ type: "external",
326
+ placeholder: "Select from example.com",
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
+ title: {
336
+ type: "text",
337
+ },
338
+ },
339
+ resolveData: async (props) => {
340
+ if (!myData.id) {
341
+ return { props, readOnly: { title: false } };
342
+ }
343
+
344
+ const latestData = await fetch(
345
+ `https://www.example.com/api/${myData.id}`
346
+ );
347
+
348
+ return {
349
+ props: {
350
+ title: latestData.json().text,
351
+ },
352
+ readOnly: {
353
+ title: true,
354
+ },
355
+ };
356
+ },
357
+ render: ({ title }) => {
358
+ return <h1>{title}</h1>;
359
+ },
360
+ },
361
+ },
362
+ };
363
+ ```
364
+
365
+ ### resolveAllData()
366
+
367
+ `resolveAllData` is a utility function exported by Puck to enable the developer to run all their `resolveData` methods before rendering the component with `<Render>`.
368
+
369
+ If your `resolveData` methods rely on any external APIs, you should run this before rendering your page.
370
+
371
+ ```tsx
372
+ import { resolveAllData } from "@measured/puck";
373
+
374
+ const resolvedData = resolveAllData(data, config);
375
+ ```
376
+
235
377
  ## Reference
236
378
 
237
379
  ### `<Puck>`
@@ -272,11 +414,13 @@ The `Config` object describes which components Puck should render, how they shou
272
414
  - **title** (`Field`): Title of the content, typically used for the page title.
273
415
  - **[fieldName]** (`Field`): User defined fields, used to describe the input data stored in the `root` key.
274
416
  - **render** (`Component`): Render a React component at the root of your component tree. Useful for defining context providers.
417
+ - **resolveData** (`async (data: ComponentData) => ComponentData` [optional]): Function to dynamically change props before rendering the root.
275
418
  - **components** (`object`): Definitions for each of the components you want to show in the visual editor
276
419
  - **[componentName]** (`object`)
277
420
  - **fields** (`Field`): The Field objects describing the input data stored against this component.
278
421
  - **render** (`Component`): Render function for your React component. Receives props as defined in fields.
279
422
  - **defaultProps** (`object` [optional]): Default props to pass to your component. Will show in fields.
423
+ - **resolveData** (`async (data: ComponentData) => ComponentData` [optional]): Function to dynamically change props before rendering the component.
280
424
  - **categories** (`object`): Component categories for rendering in the side bar or restricting in DropZones
281
425
  - **[categoryName]** (`object`)
282
426
  - **components** (`sting[]`, [optional]): Array containing the names of components in this category
@@ -288,17 +432,56 @@ The `Config` object describes which components Puck should render, how they shou
288
432
 
289
433
  A `Field` represents a user input field shown in the Puck interface.
290
434
 
291
- - **type** (`text` | `textarea` | `number` | `select` | `radio` | `external` | `array` | `custom`): The input type to render
435
+ ### All Fields
436
+
292
437
  - **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
438
+
439
+ ### Text Fields
440
+
441
+ - **type** (`"text"`)
442
+
443
+ ### Textarea Fields
444
+
445
+ - **type** (`"textarea"`)
446
+
447
+ ### Number Fields
448
+
449
+ - **type** (`"number"`)
450
+
451
+ ### Select Fields
452
+
453
+ - **type** (`"select"`)
454
+ - **options** (`object[]`): array of items to render
298
455
  - **label** (`string`)
299
456
  - **value** (`string` | `number` | `boolean`)
300
- - **adaptor** (`Adaptor`): Content adaptor if using the `external` input type
301
- - **adaptorParams** (`object`): Paramaters passed to the adaptor
457
+
458
+ ### Radio Fields
459
+
460
+ - **type** (`"radio"`)
461
+ - **options** (`object[]`): array of items to render
462
+ - **label** (`string`)
463
+ - **value** (`string` | `number` | `boolean`)
464
+
465
+ ### Array Fields
466
+
467
+ - **type** (`"array"`)
468
+ - **arrayFields** (`object`): Object describing sub-fields for each item
469
+ - **[fieldName]** (`Field`): The Field objects describing the input data for each item
470
+ - **getItemSummary** (`(object, number) => string` [optional]): Function to get the label of each item
471
+ - **defaultItemProps** (`object` [optional]): Default props to pass to each new item added, when using a `array` field type
472
+
473
+ ### External Fields
474
+
475
+ External fields can be used to load content from an external content repository, like Strapi.js.
476
+
477
+ - **type** (`"external"`)
478
+ - **placeholder** (`string`): A placeholder for the external field button
479
+ - **fetchList** (`() => object`): Fetch content from a third-party API and return an array
480
+ - **mapProp** (`(selectedItem: object) => object`): Map the selected item into another shape
481
+
482
+ ### Custom Fields
483
+
484
+ - **type** (`"custom"`)
302
485
  - **render** (`Component`): Render a custom field. Receives the props:
303
486
  - **field** (`Field`): Field configuration
304
487
  - **name** (`string`): Name of the field
@@ -325,22 +508,20 @@ The `AppState` object stores the puck application state.
325
508
 
326
509
  The `Data` object stores the puck page data.
327
510
 
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.
511
+ - **root** (`ComponentData`): The component data for the root of your configuration.
512
+ - **props** (object): Extends `ComponentData.props`, with some additional props
513
+ - **title** (`string`, [optional]): Title of the content, typically used for the page title
514
+ - **content** (`ComponentData[]`): Component data for the main content
515
+ - **zones** (`object`, [optional]): Component data for all DropZones
516
+ **[zoneCompound]** (`ComponentData[]`): Component data for a specific DropZone `zone` within a component instance
339
517
 
340
- - **name** (`string`): The human-readable name of the adaptor
341
- - **fetchList** (`(adaptorParams: object) => object`): Fetch a list of content and return an array
518
+ ### `ComponentData`
342
519
 
343
- > NB Using an adaptor on the reserved field name `_data` will spread the resulting data over your object, and lock the overridden fields.
520
+ - **type** (`string`): Component name
521
+ - **props** (`object`):
522
+ - **[prop]** (`any`): User defined data from component fields
523
+ - **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`.
524
+ - **[prop]** (`boolean`): boolean describing whether or not the prop field is read-only
344
525
 
345
526
  ### `Plugin`
346
527