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

Sign up to get free protection for your applications and to get access to all the features.
package/README.md CHANGED
@@ -1,7 +1,5 @@
1
1
  # puck
2
2
 
3
- The self-hosted, drag and drop editor for React.
4
-
5
3
  <p align="left">
6
4
  <a aria-label="Measured logo" href="https://measured.co">
7
5
  <img src="https://img.shields.io/badge/MADE%20BY%20Measured-000000.svg?style=for-the-badge&labelColor=000">
@@ -17,7 +15,7 @@ The self-hosted, drag and drop editor for React.
17
15
  </a>
18
16
  </p>
19
17
 
20
- ## Features
18
+ The self-hosted, drag and drop editor for React.
21
19
 
22
20
  - 🖱️ **Drag and drop**: Visual editing for your existing React component library
23
21
  - 🌐 **Integrations**: Load your content from a 3rd party headless CMS
@@ -234,163 +232,6 @@ The current DropZone implementation has certain rules and limitations:
234
232
  3. You can't drag between DropZones that don't share a parent (or _area_)
235
233
  4. Your mouse must be directly over a DropZone for a collision to be detected
236
234
 
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
- ### resolveProps()
281
-
282
- `resolveProps` is defined in the component config, and allows the developer to make asynchronous calls to change the props after they've been set by Puck.
283
-
284
- #### Args
285
-
286
- - **props** (`object`): the current props for your component stored in the Puck data
287
-
288
- #### Response
289
-
290
- - **props** (`object`): the resolved props for your component. Will not be stored in the Puck data
291
- - **readOnly** (`object`): an object describing which fields on the component are currently read-only
292
- - **[prop]** (`boolean`): boolean describing whether or not the prop field is read-only
293
-
294
- #### Examples
295
-
296
- ##### Basic example
297
-
298
- In this example, we remap the `text` prop to the `title` prop and mark the `title` field as read-only.
299
-
300
- ```tsx
301
- const config = {
302
- components: {
303
- HeadingBlock: {
304
- fields: {
305
- text: {
306
- type: "text",
307
- },
308
- title: {
309
- type: "text",
310
- },
311
- },
312
- resolveProps: async (props) => {
313
- return {
314
- props: {
315
- title: props.text,
316
- },
317
- readOnly: {
318
- title: true,
319
- },
320
- };
321
- },
322
- render: ({ title }) => {
323
- return <h1>{title}</h1>;
324
- },
325
- },
326
- },
327
- };
328
- ```
329
-
330
- ##### Combining with adaptors
331
-
332
- A more advanced pattern is to combine the `resolveProps` method with the adaptors to dynamically fetch data when rendering the component.
333
-
334
- ```tsx
335
- const myAdaptor = {
336
- name: "My adaptor",
337
- fetchList: async () => {
338
- const response = await fetch("https://www.example.com/api");
339
-
340
- return {
341
- id: response.json().id,
342
- };
343
- },
344
- };
345
-
346
- const config = {
347
- components: {
348
- HeadingBlock: {
349
- fields: {
350
- myData: {
351
- type: "external",
352
- adaptor: myAdaptor,
353
- },
354
- title: {
355
- type: "text",
356
- },
357
- },
358
- resolveProps: async (props) => {
359
- if (!myData.id) {
360
- return { props, readOnly: { title: false } };
361
- }
362
-
363
- const latestData = await fetch(
364
- `https://www.example.com/api/${myData.id}`
365
- );
366
-
367
- return {
368
- props: {
369
- title: latestData.json().text,
370
- },
371
- readOnly: {
372
- title: true,
373
- },
374
- };
375
- },
376
- render: ({ title }) => {
377
- return <h1>{title}</h1>;
378
- },
379
- },
380
- },
381
- };
382
- ```
383
-
384
- ### resolveData()
385
-
386
- `resolveData` is a utility function exported by Puck to enable the developer to resolve their custom props before rendering their component with `<Render>`. This is ideally done on the server. If you're using `resolveProps`, you _must_ use `resolveData` before rendering.
387
-
388
- ```tsx
389
- import { resolveData } from "@measured/puck";
390
-
391
- const resolvedData = resolveData(data, config);
392
- ```
393
-
394
235
  ## Reference
395
236
 
396
237
  ### `<Puck>`
@@ -436,13 +277,6 @@ The `Config` object describes which components Puck should render, how they shou
436
277
  - **fields** (`Field`): The Field objects describing the input data stored against this component.
437
278
  - **render** (`Component`): Render function for your React component. Receives props as defined in fields.
438
279
  - **defaultProps** (`object` [optional]): Default props to pass to your component. Will show in fields.
439
- - **resolveProps** (`async (props: object) => object` [optional]): Function to dynamically change props before rendering the component.
440
- - Args
441
- - **props** (`object`): the current props for your component stored in the Puck data
442
- - Response
443
- - **props** (`object`): the resolved props for your component. Will not be stored in the Puck data
444
- - **readOnly** (`object`): an object describing which fields on the component are currently read-only
445
- - **[prop]** (`boolean`): boolean describing whether or not the prop field is read-only
446
280
  - **categories** (`object`): Component categories for rendering in the side bar or restricting in DropZones
447
281
  - **[categoryName]** (`object`)
448
282
  - **components** (`sting[]`, [optional]): Array containing the names of components in this category
@@ -454,58 +288,17 @@ The `Config` object describes which components Puck should render, how they shou
454
288
 
455
289
  A `Field` represents a user input field shown in the Puck interface.
456
290
 
457
- ### All Fields
458
-
291
+ - **type** (`text` | `textarea` | `number` | `select` | `radio` | `external` | `array` | `custom`): The input type to render
459
292
  - **label** (`text` [optional]): A label for the input. Will use the key if not provided.
460
-
461
- ### Text Fields
462
-
463
- - **type** (`"text"`)
464
-
465
- ### Textarea Fields
466
-
467
- - **type** (`"textarea"`)
468
-
469
- ### Number Fields
470
-
471
- - **type** (`"number"`)
472
-
473
- ### Select Fields
474
-
475
- - **type** (`"select"`)
476
- - **options** (`object[]`): array of items to render
477
- - **label** (`string`)
478
- - **value** (`string` | `number` | `boolean`)
479
-
480
- ### Radio Fields
481
-
482
- - **type** (`"radio"`)
483
- - **options** (`object[]`): array of items to render
484
- - **label** (`string`)
485
- - **value** (`string` | `number` | `boolean`)
486
-
487
- ### Array Fields
488
-
489
- - **type** (`"array"`)
490
- - **arrayFields** (`object`): Object describing sub-fields for each item
293
+ - **arrayFields** (`object`): Object describing sub-fields for items in an `array` input
491
294
  - **[fieldName]** (`Field`): The Field objects describing the input data for each item
492
- - **getItemSummary** (`(object, number) => string` [optional]): Function to get the label of each item
295
+ - **getItemSummary** (`(object, number) => string` [optional]): Function to get the name of each item when using the `array` or `external` field types
493
296
  - **defaultItemProps** (`object` [optional]): Default props to pass to each new item added, when using a `array` field type
494
-
495
- ### External Fields
496
-
497
- External fields can be used to load content from an external content repository, like Strapi.js, using an `Adaptor`.
498
-
499
- - **type** (`"external"`)
500
- - **adaptor** (`Adaptor`): Content adaptor responsible for fetching data to show in the table
501
- - **name** (`string`): The human-readable name of the adaptor
502
- - **fetchList** (`(adaptorParams: object) => object`): Fetch content from a third-party API and return an array
503
- - **mapProp** (`(selectedItem: object) => object`): Map the selected item into another shape
297
+ - **options** (`object[]`): array of items to render for select or radio inputs
298
+ - **label** (`string`)
299
+ - **value** (`string` | `number` | `boolean`)
300
+ - **adaptor** (`Adaptor`): Content adaptor if using the `external` input type
504
301
  - **adaptorParams** (`object`): Paramaters passed to the adaptor
505
-
506
- ### Custom Fields
507
-
508
- - **type** (`"custom"`)
509
302
  - **render** (`Component`): Render a custom field. Receives the props:
510
303
  - **field** (`Field`): Field configuration
511
304
  - **name** (`string`): Name of the field
@@ -540,6 +333,15 @@ The `Data` object stores the puck page data.
540
333
  - **props** (object):
541
334
  - **[prop]** (string): User defined data from component fields
542
335
 
336
+ ### `Adaptor`
337
+
338
+ An `Adaptor` can be used to load content from an external content repository, like Strapi.js.
339
+
340
+ - **name** (`string`): The human-readable name of the adaptor
341
+ - **fetchList** (`(adaptorParams: object) => object`): Fetch a list of content and return an array
342
+
343
+ > NB Using an adaptor on the reserved field name `_data` will spread the resulting data over your object, and lock the overridden fields.
344
+
543
345
  ### `Plugin`
544
346
 
545
347
  Plugins that can be used to enhance Puck.
package/dist/index.d.ts CHANGED
@@ -8,71 +8,39 @@ type ItemSelector = {
8
8
  zone?: string;
9
9
  };
10
10
 
11
- type Adaptor<AdaptorParams = {}, TableShape extends Record<string, any> = {}, PropShape = TableShape> = {
11
+ type Adaptor<AdaptorParams = {}> = {
12
12
  name: string;
13
- fetchList: (adaptorParams?: AdaptorParams) => Promise<TableShape[] | null>;
14
- mapProp?: (value: TableShape) => PropShape;
13
+ fetchList: (adaptorParams?: AdaptorParams) => Promise<Record<string, any>[] | null>;
15
14
  };
16
- type WithPuckProps<Props> = Props & {
15
+ type WithId<T> = T & {
17
16
  id: string;
18
- _meta?: {
19
- readOnly: Partial<Record<keyof Props, boolean>>;
20
- };
21
- };
22
- type BaseField = {
23
- label?: string;
24
- };
25
- type TextField = BaseField & {
26
- type: "text" | "number" | "textarea";
27
- };
28
- type SelectField = BaseField & {
29
- type: "select" | "radio";
30
- options: {
31
- label: string;
32
- value: string | number | boolean;
33
- }[];
34
- };
35
- type ArrayField<Props extends {
36
- [key: string]: any;
37
- } = {
38
- [key: string]: any;
39
- }> = BaseField & {
40
- type: "array";
41
- arrayFields: {
42
- [SubPropName in keyof Props[0]]: Field<Props[0][SubPropName]>;
43
- };
44
- defaultItemProps?: Props[0];
45
- getItemSummary?: (item: Props[0], index?: number) => string;
46
17
  };
47
- type ExternalField<Props extends {
18
+ type Field<Props extends {
48
19
  [key: string]: any;
49
20
  } = {
50
21
  [key: string]: any;
51
- }> = BaseField & {
52
- type: "external";
53
- adaptor: Adaptor<any, any, Props>;
22
+ }> = {
23
+ type: "text" | "textarea" | "number" | "select" | "array" | "external" | "radio" | "custom";
24
+ label?: string;
25
+ adaptor?: Adaptor;
54
26
  adaptorParams?: object;
55
- getItemSummary: (item: Props, index?: number) => string;
56
- };
57
- type CustomField<Props extends {
58
- [key: string]: any;
59
- } = {
60
- [key: string]: any;
61
- }> = BaseField & {
62
- type: "custom";
63
- render: (props: {
64
- field: CustomField;
27
+ arrayFields?: {
28
+ [SubPropName in keyof Props]: Field<Props[SubPropName][0]>;
29
+ };
30
+ getItemSummary?: (item: Props, index?: number) => string;
31
+ defaultItemProps?: Props;
32
+ render?: (props: {
33
+ field: Field;
65
34
  name: string;
66
35
  value: any;
67
- onChange: (value: Props) => void;
36
+ onChange: (value: any) => void;
68
37
  readOnly?: boolean;
69
38
  }) => ReactElement;
39
+ options?: {
40
+ label: string;
41
+ value: string | number | boolean;
42
+ }[];
70
43
  };
71
- type Field<Props extends {
72
- [key: string]: any;
73
- } = {
74
- [key: string]: any;
75
- }> = TextField | SelectField | ArrayField<Props> | ExternalField<Props> | CustomField;
76
44
  type DefaultRootProps = {
77
45
  children: ReactNode;
78
46
  title: string;
@@ -84,7 +52,7 @@ type DefaultComponentProps = {
84
52
  editMode?: boolean;
85
53
  };
86
54
  type Fields<ComponentProps extends DefaultComponentProps = DefaultComponentProps> = {
87
- [PropName in keyof Omit<Required<ComponentProps>, "children" | "editMode">]: Field<ComponentProps[PropName]>;
55
+ [PropName in keyof Omit<Required<ComponentProps>, "children" | "editMode">]: Field<ComponentProps[PropName][0]>;
88
56
  };
89
57
  type Content<Props extends {
90
58
  [key: string]: any;
@@ -92,13 +60,9 @@ type Content<Props extends {
92
60
  [key: string]: any;
93
61
  }> = MappedItem<Props>[];
94
62
  type ComponentConfig<ComponentProps extends DefaultComponentProps = DefaultComponentProps, DefaultProps = ComponentProps> = {
95
- render: (props: WithPuckProps<ComponentProps>) => ReactElement;
63
+ render: (props: WithId<ComponentProps>) => ReactElement;
96
64
  defaultProps?: DefaultProps;
97
65
  fields?: Fields<ComponentProps>;
98
- resolveProps?: (props: WithPuckProps<ComponentProps>) => Promise<{
99
- props: WithPuckProps<ComponentProps>;
100
- readOnly?: Partial<Record<keyof ComponentProps, boolean>>;
101
- }>;
102
66
  };
103
67
  type Category<ComponentName> = {
104
68
  components?: ComponentName[];
@@ -129,7 +93,7 @@ type MappedItem<Props extends {
129
93
  [key: string]: any;
130
94
  }> = {
131
95
  type: keyof Props;
132
- props: WithPuckProps<{
96
+ props: WithId<{
133
97
  [key: string]: any;
134
98
  }>;
135
99
  };
@@ -252,7 +216,6 @@ type PathData = Record<string, {
252
216
  type DropZoneContext = {
253
217
  data: Data;
254
218
  config: Config;
255
- dynamicProps?: Record<string, any>;
256
219
  itemSelector?: ItemSelector | null;
257
220
  setItemSelector?: (newIndex: ItemSelector | null) => void;
258
221
  dispatch?: (action: PuckAction) => void;
@@ -346,24 +309,10 @@ declare function Render({ config, data }: {
346
309
  data: Data;
347
310
  }): react_jsx_runtime.JSX.Element;
348
311
 
349
- declare const resolveData: (data: Data, config: Config) => Promise<{
350
- content: {
351
- props: any;
352
- type: string | number;
353
- }[];
354
- zones: Record<string, MappedItem<{
355
- [key: string]: any;
356
- }>[]>;
357
- root: {
358
- [key: string]: any;
359
- title: string;
360
- };
361
- }>;
362
-
363
312
  declare const FieldLabel: ({ children, icon, label, }: {
364
313
  children?: ReactNode;
365
314
  icon?: ReactNode;
366
315
  label: string;
367
316
  }) => react_jsx_runtime.JSX.Element;
368
317
 
369
- export { Adaptor, AppState, ArrayField, ArrayState, BaseField, Button, ComponentConfig, Config, Content, CustomField, Data, DefaultComponentProps, DefaultRootProps, DropZone, DropZoneProvider, ExternalField, Field, FieldLabel, Fields, IconButton, ItemWithId, MappedItem, Puck, Render, SelectField, TextField, UiState, dropZoneContext, resolveData };
318
+ export { Adaptor, AppState, ArrayState, Button, ComponentConfig, Config, Content, Data, DefaultComponentProps, DefaultRootProps, DropZone, DropZoneProvider, Field, FieldLabel, Fields, IconButton, ItemWithId, MappedItem, Puck, Render, UiState, dropZoneContext };
package/dist/index.js CHANGED
@@ -1109,8 +1109,7 @@ __export(core_exports, {
1109
1109
  IconButton: () => IconButton,
1110
1110
  Puck: () => Puck,
1111
1111
  Render: () => Render,
1112
- dropZoneContext: () => dropZoneContext,
1113
- resolveData: () => resolveData
1112
+ dropZoneContext: () => dropZoneContext
1114
1113
  });
1115
1114
  module.exports = __toCommonJS(core_exports);
1116
1115
  init_react_import();
@@ -2664,13 +2663,11 @@ var setupZone = (data, zoneKey) => {
2664
2663
  };
2665
2664
 
2666
2665
  // lib/get-item.ts
2667
- var getItem = (selector, data, dynamicProps = {}) => {
2666
+ var getItem = (selector, data) => {
2668
2667
  if (!selector.zone || selector.zone === rootDroppableId) {
2669
- const item2 = data.content[selector.index];
2670
- return __spreadProps(__spreadValues({}, item2), { props: dynamicProps[item2.props.id] || item2.props });
2668
+ return data.content[selector.index];
2671
2669
  }
2672
- const item = setupZone(data, selector.zone).zones[selector.zone][selector.index];
2673
- return __spreadProps(__spreadValues({}, item), { props: dynamicProps[item.props.id] || item.props });
2670
+ return setupZone(data, selector.zone).zones[selector.zone][selector.index];
2674
2671
  };
2675
2672
 
2676
2673
  // lib/index.ts
@@ -2678,6 +2675,14 @@ init_react_import();
2678
2675
 
2679
2676
  // lib/filter.ts
2680
2677
  init_react_import();
2678
+ var filter = (obj, validKeys) => {
2679
+ return validKeys.reduce((acc, item) => {
2680
+ if (typeof obj[item] !== "undefined") {
2681
+ return __spreadProps(__spreadValues({}, acc), { [item]: obj[item] });
2682
+ }
2683
+ return acc;
2684
+ }, {});
2685
+ };
2681
2686
 
2682
2687
  // lib/reorder.ts
2683
2688
  init_react_import();
@@ -2828,7 +2833,6 @@ function DropZoneEdit({ zone, style }) {
2828
2833
  const {
2829
2834
  // These all need setting via context
2830
2835
  data,
2831
- dynamicProps = {},
2832
2836
  dispatch = () => null,
2833
2837
  config,
2834
2838
  itemSelector,
@@ -2930,7 +2934,7 @@ function DropZoneEdit({ zone, style }) {
2930
2934
  content.map((item, i) => {
2931
2935
  var _a2;
2932
2936
  const componentId = item.props.id;
2933
- const defaultedProps = __spreadProps(__spreadValues(__spreadValues({}, (_a2 = config.components[item.type]) == null ? void 0 : _a2.defaultProps), dynamicProps[item.props.id] || item.props), {
2937
+ const defaultedProps = __spreadProps(__spreadValues(__spreadValues({}, (_a2 = config.components[item.type]) == null ? void 0 : _a2.defaultProps), item.props), {
2934
2938
  editMode: true
2935
2939
  });
2936
2940
  const isSelected = (selectedItem == null ? void 0 : selectedItem.props.id) === componentId || false;
@@ -3320,7 +3324,7 @@ var ArrayField = ({
3320
3324
  });
3321
3325
  setArrayState({ items: newItems });
3322
3326
  }, [value]);
3323
- if (field.type !== "array" || !field.arrayFields) {
3327
+ if (!field.arrayFields) {
3324
3328
  return null;
3325
3329
  }
3326
3330
  return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: getClassNameInput(), children: [
@@ -3519,7 +3523,6 @@ var ExternalInput = ({
3519
3523
  onChange,
3520
3524
  value = null
3521
3525
  }) => {
3522
- const { mapProp = (val) => val } = field.adaptor || {};
3523
3526
  const [data, setData] = (0, import_react27.useState)([]);
3524
3527
  const [isOpen, setOpen] = (0, import_react27.useState)(false);
3525
3528
  const [selectedData, setSelectedData] = (0, import_react27.useState)(value);
@@ -3597,9 +3600,9 @@ var ExternalInput = ({
3597
3600
  {
3598
3601
  style: { whiteSpace: "nowrap" },
3599
3602
  onClick: (e) => {
3600
- onChange(mapProp(item));
3603
+ onChange(item);
3601
3604
  setOpen(false);
3602
- setSelectedData(mapProp(item));
3605
+ setSelectedData(item);
3603
3606
  },
3604
3607
  children: keys.map((key) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("td", { children: item[key] }, key))
3605
3608
  },
@@ -3621,18 +3624,16 @@ var getClassName9 = get_class_name_factory_default("Input", styles_module_defaul
3621
3624
  var ExternalField = ({
3622
3625
  field,
3623
3626
  onChange,
3627
+ readOnly,
3624
3628
  value,
3625
3629
  name,
3626
3630
  label
3627
3631
  }) => {
3628
- if (field.type !== "external" || !field.adaptor) {
3632
+ if (!field.adaptor) {
3629
3633
  return null;
3630
3634
  }
3631
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: getClassName9(), children: [
3632
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: getClassName9("label"), children: [
3633
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: getClassName9("labelIcon"), children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(link_default, { size: 16 }) }),
3634
- label || name
3635
- ] }),
3635
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: getClassName9(""), children: [
3636
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: getClassName9("label"), children: name === "_data" ? "External content" : label || name }),
3636
3637
  /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(ExternalInput, { field, onChange, value })
3637
3638
  ] });
3638
3639
  };
@@ -3648,7 +3649,7 @@ var RadioField = ({
3648
3649
  value,
3649
3650
  name
3650
3651
  }) => {
3651
- if (field.type !== "radio" || !field.options) {
3652
+ if (!field.options) {
3652
3653
  return null;
3653
3654
  }
3654
3655
  return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: getClassName10(), children: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: getClassName10("radioGroup"), children: [
@@ -3698,7 +3699,7 @@ var SelectField = ({
3698
3699
  value,
3699
3700
  name
3700
3701
  }) => {
3701
- if (field.type !== "select" || !field.options) {
3702
+ if (!field.options) {
3702
3703
  return null;
3703
3704
  }
3704
3705
  return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("label", { className: getClassName11(), children: [
@@ -4745,26 +4746,6 @@ var useComponentList = (config, ui) => {
4745
4746
  return componentList;
4746
4747
  };
4747
4748
 
4748
- // lib/resolve-all-props.ts
4749
- init_react_import();
4750
- var resolveAllProps = (content, config) => __async(void 0, null, function* () {
4751
- return yield Promise.all(
4752
- content.map((item) => __async(void 0, null, function* () {
4753
- const configForItem = config.components[item.type];
4754
- if (configForItem.resolveProps) {
4755
- const { props: resolvedProps, readOnly = {} } = yield configForItem.resolveProps(item.props);
4756
- const { _meta: { readOnly: existingReadOnly = {} } = {} } = item.props || {};
4757
- return __spreadProps(__spreadValues({}, item), {
4758
- props: __spreadProps(__spreadValues({}, resolvedProps), {
4759
- _meta: { readOnly: __spreadValues(__spreadValues({}, existingReadOnly), readOnly) }
4760
- })
4761
- });
4762
- }
4763
- return item;
4764
- }))
4765
- );
4766
- });
4767
-
4768
4749
  // components/Puck/index.tsx
4769
4750
  var import_jsx_runtime22 = require("react/jsx-runtime");
4770
4751
  var defaultPageFields = {
@@ -4795,7 +4776,6 @@ function Puck({
4795
4776
  headerPath
4796
4777
  }) {
4797
4778
  var _a, _b;
4798
- const [dynamicProps, setDynamicProps] = (0, import_react34.useState)({});
4799
4779
  const [reducer] = (0, import_react34.useState)(() => createReducer({ config }));
4800
4780
  const initialAppState = __spreadProps(__spreadValues({}, defaultAppState), {
4801
4781
  data: initialData,
@@ -4821,21 +4801,6 @@ function Puck({
4821
4801
  flushZones(initialAppState)
4822
4802
  );
4823
4803
  const { data, ui } = appState;
4824
- (0, import_react34.useEffect)(() => {
4825
- const flatContent = Object.keys(data.zones || {}).reduce(
4826
- (acc, zone) => [...acc, ...data.zones[zone]],
4827
- data.content
4828
- );
4829
- resolveAllProps(flatContent, config).then((dynamicContent) => {
4830
- const newDynamicProps = dynamicContent.reduce(
4831
- (acc, item) => {
4832
- return __spreadProps(__spreadValues({}, acc), { [item.props.id]: item.props });
4833
- },
4834
- {}
4835
- );
4836
- setDynamicProps(newDynamicProps);
4837
- });
4838
- }, [data]);
4839
4804
  const { canForward, canRewind, rewind, forward } = usePuckHistory({
4840
4805
  appState,
4841
4806
  dispatch
@@ -4850,7 +4815,7 @@ function Puck({
4850
4815
  },
4851
4816
  []
4852
4817
  );
4853
- const selectedItem = itemSelector ? getItem(itemSelector, data, dynamicProps) : null;
4818
+ const selectedItem = itemSelector ? getItem(itemSelector, data) : null;
4854
4819
  const Page = (0, import_react34.useCallback)(
4855
4820
  (pageProps) => {
4856
4821
  var _a2, _b2;
@@ -4979,7 +4944,6 @@ function Puck({
4979
4944
  value: {
4980
4945
  data,
4981
4946
  itemSelector,
4982
- dynamicProps,
4983
4947
  setItemSelector,
4984
4948
  config,
4985
4949
  dispatch,
@@ -5260,18 +5224,42 @@ function Puck({
5260
5224
  showBreadcrumbs: true,
5261
5225
  title: selectedItem ? selectedItem.type : "Page",
5262
5226
  children: Object.keys(fields).map((fieldName) => {
5227
+ var _a2, _b2, _c, _d;
5263
5228
  const field = fields[fieldName];
5264
5229
  const onChange2 = (value) => {
5265
5230
  let currentProps;
5231
+ let newProps;
5266
5232
  if (selectedItem) {
5267
5233
  currentProps = selectedItem.props;
5268
5234
  } else {
5269
5235
  currentProps = data.root;
5270
5236
  }
5271
- const _a2 = currentProps._meta || {}, { readOnly } = _a2, _meta = __objRest(_a2, ["readOnly"]);
5272
- const newProps = __spreadProps(__spreadValues({}, currentProps), {
5273
- [fieldName]: value
5274
- });
5237
+ if (fieldName === "_data") {
5238
+ if (!value) {
5239
+ const _a3 = currentProps._meta || {}, { locked } = _a3, _meta = __objRest(_a3, ["locked"]);
5240
+ newProps = __spreadProps(__spreadValues({}, currentProps), {
5241
+ _data: void 0,
5242
+ _meta
5243
+ });
5244
+ } else {
5245
+ const changedFields = filter(
5246
+ // filter out anything not supported by this component
5247
+ value,
5248
+ Object.keys(fields)
5249
+ );
5250
+ newProps = __spreadProps(__spreadValues(__spreadValues({}, currentProps), changedFields), {
5251
+ _data: value,
5252
+ // TODO perf - this is duplicative and will make payload larger
5253
+ _meta: {
5254
+ locked: Object.keys(changedFields)
5255
+ }
5256
+ });
5257
+ }
5258
+ } else {
5259
+ newProps = __spreadProps(__spreadValues({}, currentProps), {
5260
+ [fieldName]: value
5261
+ });
5262
+ }
5275
5263
  if (itemSelector) {
5276
5264
  dispatch({
5277
5265
  type: "replace",
@@ -5287,28 +5275,31 @@ function Puck({
5287
5275
  }
5288
5276
  };
5289
5277
  if (selectedItem && itemSelector) {
5290
- const { readOnly = {} } = selectedItem.props._meta || {};
5291
5278
  return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
5292
5279
  InputOrGroup,
5293
5280
  {
5294
5281
  field,
5295
5282
  name: fieldName,
5296
5283
  label: field.label,
5297
- readOnly: readOnly[fieldName],
5284
+ readOnly: ((_b2 = (_a2 = getItem(
5285
+ itemSelector,
5286
+ data
5287
+ ).props._meta) == null ? void 0 : _a2.locked) == null ? void 0 : _b2.indexOf(fieldName)) > -1,
5298
5288
  value: selectedItem.props[fieldName],
5299
5289
  onChange: onChange2
5300
5290
  },
5301
5291
  `${selectedItem.props.id}_${fieldName}`
5302
5292
  );
5303
5293
  } else {
5304
- const { readOnly = {} } = data.root._meta || {};
5305
5294
  return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
5306
5295
  InputOrGroup,
5307
5296
  {
5308
5297
  field,
5309
5298
  name: fieldName,
5310
5299
  label: field.label,
5311
- readOnly: readOnly[fieldName],
5300
+ readOnly: ((_d = (_c = data.root._meta) == null ? void 0 : _c.locked) == null ? void 0 : _d.indexOf(
5301
+ fieldName
5302
+ )) > -1,
5312
5303
  value: data.root[fieldName],
5313
5304
  onChange: onChange2
5314
5305
  },
@@ -5339,22 +5330,6 @@ function Render({ config, data }) {
5339
5330
  }
5340
5331
  return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(DropZoneProvider, { value: { data, config, mode: "render" }, children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(DropZone, { zone: rootDroppableId }) });
5341
5332
  }
5342
-
5343
- // lib/resolve-data.ts
5344
- init_react_import();
5345
- var resolveData = (data, config) => __async(void 0, null, function* () {
5346
- const { zones = {} } = data;
5347
- const zoneKeys = Object.keys(zones);
5348
- const resolvedZones = {};
5349
- for (let i = 0; i < zoneKeys.length; i++) {
5350
- const zoneKey = zoneKeys[i];
5351
- resolvedZones[zoneKey] = yield resolveAllProps(zones[zoneKey], config);
5352
- }
5353
- return __spreadProps(__spreadValues({}, data), {
5354
- content: yield resolveAllProps(data.content, config),
5355
- zones: resolvedZones
5356
- });
5357
- });
5358
5333
  // Annotate the CommonJS export names for ESM import in node:
5359
5334
  0 && (module.exports = {
5360
5335
  Button,
@@ -5364,8 +5339,7 @@ var resolveData = (data, config) => __async(void 0, null, function* () {
5364
5339
  IconButton,
5365
5340
  Puck,
5366
5341
  Render,
5367
- dropZoneContext,
5368
- resolveData
5342
+ dropZoneContext
5369
5343
  });
5370
5344
  /*! Bundled license information:
5371
5345
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@measured/puck",
3
- "version": "0.11.0-canary.b28404f",
3
+ "version": "0.11.0-canary.c8c02fd",
4
4
  "private": false,
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",