@netlisian/softconfig 0.1.5 → 0.1.6

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,122 +1,62 @@
1
- # Puck Soft Config
2
-
3
- Enhanced configuration and UI utilities for Puck that add “soft components” (composable, versioned, and remodelable components) plus ready-to-use UI chrome.
4
-
5
- ## Packages & Exports
6
-
7
- - `@netlisian/softconfig` → bundled CJS/ESM + types (`./dist/index.{js,mjs,d.ts}`)
8
- - `@netlisian/softconfig/puck` → Puck-focused entry (`./dist/puck/index.*`)
9
- - Styles: `@netlisian/softconfig/styles.css` (alias `./dist/index.css`) and `@netlisian/softconfig/puck/index.css`
10
-
11
- Peer dependencies: `react@^18`, `@measured/puck@0.20.x`.
12
-
13
- ## Install
14
-
15
- ```bash
16
- pnpm add @netlisian/softconfig @measured/puck react
17
- ```
18
-
19
- ## Quick Start
20
-
21
- ```tsx
22
- import { SoftConfigProvider } from "@netlisian/softconfig/puck";
23
- import "@netlisian/softconfig/styles.css";
24
-
25
- const hardConfig = { components: {/* your hard Puck components */} };
26
- const softComponents = {/* optional persisted soft components */};
27
- const overrides = {/* optional UI overrides like map/action bar/etc. */};
28
-
29
- export default function App({ children }: { children: React.ReactNode }) {
30
- return (
31
- <SoftConfigProvider
32
- hardConfig={hardConfig}
33
- softComponents={softComponents}
34
- overrides={overrides}
35
- >
36
- {(softConfig, softComponentsRegistry) => (
37
- /* render Puck with the soft-config-driven config */
38
- <YourPuckHost config={softConfig} softComponents={softComponentsRegistry} />
39
- )}
40
- </SoftConfigProvider>
41
- );
42
- }
43
- ```
44
-
45
- `SoftConfigProvider` accepts an optional `value` prop if you want to bring your own Zustand store (see `src/puck/context/storeProvider.tsx`). Children are rendered via a render-prop to give you the hydrated `softConfig` and `softComponents` to pass directly to Puck.
46
-
47
- ## Field Mapping & Transforms
48
-
49
- - Soft components support `_map` entries with optional transforms or CVA-like configs. Because functions are not persisted, the library regenerates missing transforms at runtime (including during remodel/decompose) using the stored CVA config.
50
- - Field defaults are read from `_fieldSettings` when resolving mappings, so mapped props fall back to configured defaults when source data is absent.
51
-
52
- ## Styles
53
-
54
- Import once in your app root:
55
-
56
- ```ts
57
- import "@netlisian/softconfig/styles.css";
58
- ```
59
-
60
- ## Overrides
61
-
62
- You can supply custom overrides (action bar, headers, map UI, etc.) through the `overrides` prop. See `src/puck/types/Overrides.ts` for the full surface.
63
-
64
- ## Action Lifecycle Events
65
-
66
- `SoftConfigProvider` supports `onActions` for lifecycle callbacks:
67
-
68
- - `build` → when build mode starts
69
- - `remodel` → when remodel mode starts
70
- - `complete` → when a build/remodel is finalized
71
- - `inspect` → when inspect is requested
72
- - `demolish` → when a soft component is deleted
73
- - `setDefaultVersion`, `decompose`, `cancel`, `publish`
74
-
75
- Version-aware payloads are included for build finalization and inspection flows:
76
-
77
- ```ts
78
- type ActionEventPayload =
79
- | {
80
- type: "remodel";
81
- payload: {
82
- id: string;
83
- version?: string;
84
- softComponent?: VersionedSoftComponent["versions"][string];
85
- };
86
- }
87
- | {
88
- type: "complete";
89
- payload: {
90
- id: string;
91
- version: string;
92
- componentData: Record<string, any>;
93
- softComponent: VersionedSoftComponent["versions"][string];
94
- };
95
- }
96
- | {
97
- type: "inspect";
98
- payload: {
99
- id: string;
100
- version?: string;
101
- softComponent?: VersionedSoftComponent["versions"][string];
102
- };
103
- }
104
- | {
105
- type: "demolish";
106
- payload: {
107
- id: string;
108
- };
109
- };
110
- ```
111
-
112
- This contract allows downstream consumers (for example, save/sync bridges) to persist using exact version metadata without relying on inferred/default versions.
113
-
114
- ## Development
115
-
116
- - Build: `pnpm --filter @netlisian/softconfig build`
117
- - Dev (watch): `pnpm --filter @netlisian/softconfig dev`
118
- - Lint: `pnpm --filter @netlisian/softconfig lint`
119
-
120
- ## License
121
-
122
- MIT
1
+ # @netlisian/soft-config
2
+
3
+ The core library for building and managing **Soft Components** within the Netlisian ecosystem. It provides type-safe field definitions, recursive mapping logic, and state management for the Puck editor.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @netlisian/soft-config
9
+ ```
10
+
11
+ ## Features
12
+
13
+ - **Soft Component Builder**: Easy-to-use interface for creating versioned components.
14
+ - **Dynamic Field Options**: Automatically generates dot-notated mapping paths for nested objects and arrays.
15
+ - **Custom Field Support**: Extend the editor with custom UI while maintaining return type safety.
16
+ - **Puck Overrides**: Pre-built component overrides (ActionBar, Header, ComponentList) to seamlessly integrate Soft Components into the Puck editor.
17
+
18
+ ## Key Concepts
19
+
20
+ ### Soft Component Definitions
21
+
22
+ A Soft Component consists of fields, default props, and typed components.
23
+
24
+ ```typescript
25
+ import { SoftFieldDefinition } from "@netlisian/soft-config";
26
+
27
+ const galleryField: SoftFieldDefinition = {
28
+ name: "gallery",
29
+ type: "array",
30
+ subFields: [
31
+ { name: "src", type: "text" },
32
+ { name: "alt", type: "text" }
33
+ ]
34
+ };
35
+ ```
36
+
37
+ ### Custom Field Extensibility
38
+
39
+ Define custom field types with specified return types (`string`, `number`, `boolean`, `object`, `array`).
40
+
41
+ ```typescript
42
+ import { CustomFields } from "@netlisian/soft-config";
43
+
44
+ export const myCustomFields: CustomFields = {
45
+ "my-color": {
46
+ field: { type: "custom", render: () => <div>Color Picker</div> },
47
+ returnType: "string"
48
+ }
49
+ };
50
+ ```
51
+
52
+ ### Field Mapping
53
+
54
+ The library handles the logic for mapping configuration data to component props, including recursive array iteration using `[]` syntax (e.g., `features[].title`).
55
+
56
+ ## Documentation
57
+
58
+ For detailed guides and API references, check the [Netlisian Docs](https://docs.netlisian.com).
59
+
60
+ ## License
61
+
62
+ MIT
@@ -342,6 +342,7 @@ type Overrides = {
342
342
  props: RootData<AsFieldProps<WithChildren<BuilderRootConfig>>> | Promise<RootData<AsFieldProps<WithChildren<BuilderRootConfig>>>>;
343
343
  readOnly: Readonly<Record<string, boolean>> | undefined;
344
344
  };
345
+ mapComponentConfig?: (componentName: string, defaultConfig: ComponentConfig) => Partial<ComponentConfig>;
345
346
  };
346
347
 
347
348
  type Status = "building" | "remodeling" | "ready" | "inspecting";
@@ -342,6 +342,7 @@ type Overrides = {
342
342
  props: RootData<AsFieldProps<WithChildren<BuilderRootConfig>>> | Promise<RootData<AsFieldProps<WithChildren<BuilderRootConfig>>>>;
343
343
  readOnly: Readonly<Record<string, boolean>> | undefined;
344
344
  };
345
+ mapComponentConfig?: (componentName: string, defaultConfig: ComponentConfig) => Partial<ComponentConfig>;
345
346
  };
346
347
 
347
348
  type Status = "building" | "remodeling" | "ready" | "inspecting";
@@ -1644,21 +1644,29 @@ var import_uuid = require("uuid");
1644
1644
  var generateId = (type) => type ? `${type}-${(0, import_uuid.v4)()}` : (0, import_uuid.v4)();
1645
1645
 
1646
1646
  // src/puck/lib/component-key.ts
1647
- var defaultToCamelCase = (value) => {
1648
- const tokens = value.trim().replace(/[^a-zA-Z0-9\s_-]/g, " ").split(/[\s_-]+/).filter(Boolean);
1649
- if (tokens.length === 0) return "";
1650
- const [first, ...rest] = tokens;
1651
- return `${first.toLowerCase()}${rest.map((token) => token.charAt(0).toUpperCase() + token.slice(1).toLowerCase()).join("")}`;
1647
+ var toSlug = (value) => {
1648
+ return value.replace(/([a-z])([A-Z])/g, "$1-$2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1-$2").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/-{2,}/g, "-").replace(/^-+|-+$/g, "");
1649
+ };
1650
+ var defaultComponentNameToKey = (displayName, context) => {
1651
+ var _a, _b;
1652
+ const registry = (_b = (_a = context.registryName) != null ? _a : context.registry) != null ? _b : "default";
1653
+ return `${toSlug(registry)}/${toSlug(displayName)}`;
1654
+ };
1655
+ var defaultComponentKeyToName = (key) => {
1656
+ const slashIndex = key.indexOf("/");
1657
+ const componentPart = slashIndex === -1 ? key : key.slice(slashIndex + 1);
1658
+ if (!componentPart) return "";
1659
+ return componentPart.split("-").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
1652
1660
  };
1653
1661
  var createComponentKeyFromName = (displayName, overrides, context) => {
1654
- const key = overrides.componentNameToKey ? overrides.componentNameToKey(displayName, context) : defaultToCamelCase(displayName);
1662
+ const key = overrides.componentNameToKey ? overrides.componentNameToKey(displayName, context) : defaultComponentNameToKey(displayName, context);
1655
1663
  return key.trim();
1656
1664
  };
1657
1665
  var getComponentNameFromKey = (key, overrides) => {
1658
1666
  if (overrides == null ? void 0 : overrides.componentKeyToName) {
1659
1667
  return overrides.componentKeyToName(key);
1660
1668
  }
1661
- return key;
1669
+ return defaultComponentKeyToName(key);
1662
1670
  };
1663
1671
 
1664
1672
  // src/puck/lib/soft-component-to-appstate.ts
@@ -2057,10 +2065,9 @@ var hydrateCustomField = (fieldName, field, fieldSettings, customFields) => {
2057
2065
  label: field.label || customField.field.label || fieldName
2058
2066
  });
2059
2067
  };
2060
- var createVersionedComponentConfig = (componentName, displayName, version, allVersions, config, softComponents, defaultProps, showVersioning = true, customFields) => {
2068
+ var createVersionedComponentConfig = (componentName, displayName, version, allVersions, config, softComponents, defaultProps, showVersioning = true, customFields, overrides) => {
2061
2069
  var _a, _b;
2062
- const softConfig = config;
2063
- return {
2070
+ const baseConfig = {
2064
2071
  label: displayName,
2065
2072
  fields: Object.fromEntries(
2066
2073
  (Object.entries(
@@ -2104,12 +2111,16 @@ var createVersionedComponentConfig = (componentName, displayName, version, allVe
2104
2111
  softComponentFields: versionedComponent.fields,
2105
2112
  softComponentFieldSettings: versionedComponent.fieldSettings,
2106
2113
  softSubComponent: versionedComponent.components,
2107
- configComponents: softConfig.components,
2114
+ configComponents: config.components,
2108
2115
  props
2109
2116
  }
2110
2117
  );
2111
2118
  }
2112
2119
  };
2120
+ if (overrides == null ? void 0 : overrides.mapComponentConfig) {
2121
+ return __spreadValues(__spreadValues({}, baseConfig), overrides.mapComponentConfig(componentName, baseConfig));
2122
+ }
2123
+ return baseConfig;
2113
2124
  };
2114
2125
 
2115
2126
  // src/puck/lib/builder/sub-component-decomposer.tsx
@@ -2838,7 +2849,8 @@ function buildInitialSoftComponents(hardConfig, softComponents, overrides, showV
2838
2849
  hydratedSoftComponents,
2839
2850
  versionedComponent.defaultProps,
2840
2851
  showVersioning,
2841
- customFields
2852
+ customFields,
2853
+ overrides
2842
2854
  );
2843
2855
  componentConfigs[name] = newSoftComponentConfig;
2844
2856
  buildingConfig.components[name] = newSoftComponentConfig;
@@ -2867,7 +2879,8 @@ function buildInitialSoftComponents(hardConfig, softComponents, overrides, showV
2867
2879
  hydratedSoftComponents,
2868
2880
  versionedComponent.defaultProps,
2869
2881
  showVersioning,
2870
- customFields
2882
+ customFields,
2883
+ overrides
2871
2884
  );
2872
2885
  componentConfigs[name] = newSoftComponentConfig;
2873
2886
  }
@@ -1586,21 +1586,29 @@ import { v4 as uuidv4 } from "uuid";
1586
1586
  var generateId = (type) => type ? `${type}-${uuidv4()}` : uuidv4();
1587
1587
 
1588
1588
  // src/puck/lib/component-key.ts
1589
- var defaultToCamelCase = (value) => {
1590
- const tokens = value.trim().replace(/[^a-zA-Z0-9\s_-]/g, " ").split(/[\s_-]+/).filter(Boolean);
1591
- if (tokens.length === 0) return "";
1592
- const [first, ...rest] = tokens;
1593
- return `${first.toLowerCase()}${rest.map((token) => token.charAt(0).toUpperCase() + token.slice(1).toLowerCase()).join("")}`;
1589
+ var toSlug = (value) => {
1590
+ return value.replace(/([a-z])([A-Z])/g, "$1-$2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1-$2").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/-{2,}/g, "-").replace(/^-+|-+$/g, "");
1591
+ };
1592
+ var defaultComponentNameToKey = (displayName, context) => {
1593
+ var _a, _b;
1594
+ const registry = (_b = (_a = context.registryName) != null ? _a : context.registry) != null ? _b : "default";
1595
+ return `${toSlug(registry)}/${toSlug(displayName)}`;
1596
+ };
1597
+ var defaultComponentKeyToName = (key) => {
1598
+ const slashIndex = key.indexOf("/");
1599
+ const componentPart = slashIndex === -1 ? key : key.slice(slashIndex + 1);
1600
+ if (!componentPart) return "";
1601
+ return componentPart.split("-").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
1594
1602
  };
1595
1603
  var createComponentKeyFromName = (displayName, overrides, context) => {
1596
- const key = overrides.componentNameToKey ? overrides.componentNameToKey(displayName, context) : defaultToCamelCase(displayName);
1604
+ const key = overrides.componentNameToKey ? overrides.componentNameToKey(displayName, context) : defaultComponentNameToKey(displayName, context);
1597
1605
  return key.trim();
1598
1606
  };
1599
1607
  var getComponentNameFromKey = (key, overrides) => {
1600
1608
  if (overrides == null ? void 0 : overrides.componentKeyToName) {
1601
1609
  return overrides.componentKeyToName(key);
1602
1610
  }
1603
- return key;
1611
+ return defaultComponentKeyToName(key);
1604
1612
  };
1605
1613
 
1606
1614
  // src/puck/lib/soft-component-to-appstate.ts
@@ -1999,10 +2007,9 @@ var hydrateCustomField = (fieldName, field, fieldSettings, customFields) => {
1999
2007
  label: field.label || customField.field.label || fieldName
2000
2008
  });
2001
2009
  };
2002
- var createVersionedComponentConfig = (componentName, displayName, version, allVersions, config, softComponents, defaultProps, showVersioning = true, customFields) => {
2010
+ var createVersionedComponentConfig = (componentName, displayName, version, allVersions, config, softComponents, defaultProps, showVersioning = true, customFields, overrides) => {
2003
2011
  var _a, _b;
2004
- const softConfig = config;
2005
- return {
2012
+ const baseConfig = {
2006
2013
  label: displayName,
2007
2014
  fields: Object.fromEntries(
2008
2015
  (Object.entries(
@@ -2046,12 +2053,16 @@ var createVersionedComponentConfig = (componentName, displayName, version, allVe
2046
2053
  softComponentFields: versionedComponent.fields,
2047
2054
  softComponentFieldSettings: versionedComponent.fieldSettings,
2048
2055
  softSubComponent: versionedComponent.components,
2049
- configComponents: softConfig.components,
2056
+ configComponents: config.components,
2050
2057
  props
2051
2058
  }
2052
2059
  );
2053
2060
  }
2054
2061
  };
2062
+ if (overrides == null ? void 0 : overrides.mapComponentConfig) {
2063
+ return __spreadValues(__spreadValues({}, baseConfig), overrides.mapComponentConfig(componentName, baseConfig));
2064
+ }
2065
+ return baseConfig;
2055
2066
  };
2056
2067
 
2057
2068
  // src/puck/lib/builder/sub-component-decomposer.tsx
@@ -2780,7 +2791,8 @@ function buildInitialSoftComponents(hardConfig, softComponents, overrides, showV
2780
2791
  hydratedSoftComponents,
2781
2792
  versionedComponent.defaultProps,
2782
2793
  showVersioning,
2783
- customFields
2794
+ customFields,
2795
+ overrides
2784
2796
  );
2785
2797
  componentConfigs[name] = newSoftComponentConfig;
2786
2798
  buildingConfig.components[name] = newSoftComponentConfig;
@@ -2809,7 +2821,8 @@ function buildInitialSoftComponents(hardConfig, softComponents, overrides, showV
2809
2821
  hydratedSoftComponents,
2810
2822
  versionedComponent.defaultProps,
2811
2823
  showVersioning,
2812
- customFields
2824
+ customFields,
2825
+ overrides
2813
2826
  );
2814
2827
  componentConfigs[name] = newSoftComponentConfig;
2815
2828
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@netlisian/softconfig",
3
- "version": "0.1.5",
3
+ "version": "0.1.6",
4
4
  "main": "./dist/index.js",
5
5
  "module": "./dist/index.mjs",
6
6
  "types": "./dist/index.d.ts",