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

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,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