@webiny/react-properties 6.0.0-rc.7 → 6.1.0-beta.0

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.
@@ -0,0 +1,40 @@
1
+ type DevToolsView = "browse" | "raw";
2
+ interface DevToolsSectionProps {
3
+ name: string;
4
+ data: unknown;
5
+ /**
6
+ * Group name for the sidebar. Items with the same group appear
7
+ * under the same header. Defaults to "Sections".
8
+ *
9
+ * @example
10
+ * ```tsx
11
+ * <DevToolsSection name="Article" group="CMS" data={model} />
12
+ * <DevToolsSection name="Author" group="CMS" data={author} />
13
+ * ```
14
+ */
15
+ group?: string;
16
+ /**
17
+ * Which views to show in the detail panel.
18
+ * - `"browse"` — split view with root keys on the left, value tree on the right
19
+ * - `"raw"` — full JSON tree view
20
+ *
21
+ * Accepts a single view or an array. First item is the default tab.
22
+ * @default ["browse", "raw"]
23
+ */
24
+ views?: DevToolsView | DevToolsView[];
25
+ }
26
+ /**
27
+ * Registers a named section in the Webiny DevTools extension.
28
+ * Renders nothing — purely a data registration side-effect.
29
+ *
30
+ * When the component unmounts (e.g., route change), the section
31
+ * is automatically removed from DevTools.
32
+ *
33
+ * @example
34
+ * ```tsx
35
+ * <DevToolsSection name="CMS Model" data={model} />
36
+ * <DevToolsSection name="Article" group="CMS" data={model} views="raw" />
37
+ * ```
38
+ */
39
+ export declare function DevToolsSection({ name, data, group, views }: DevToolsSectionProps): null;
40
+ export {};
@@ -0,0 +1,70 @@
1
+ import { useEffect } from "react";
2
+ import { getHook } from "./useDebugConfig.js";
3
+ /**
4
+ * Registers a named section in the Webiny DevTools extension.
5
+ * Renders nothing — purely a data registration side-effect.
6
+ *
7
+ * When the component unmounts (e.g., route change), the section
8
+ * is automatically removed from DevTools.
9
+ *
10
+ * @example
11
+ * ```tsx
12
+ * <DevToolsSection name="CMS Model" data={model} />
13
+ * <DevToolsSection name="Article" group="CMS" data={model} views="raw" />
14
+ * ```
15
+ */
16
+ export function DevToolsSection({
17
+ name,
18
+ data,
19
+ group,
20
+ views
21
+ }) {
22
+ // Snapshot the data on every render to get a stable, plain value.
23
+ // This also ensures that if data is a MobX observable, we capture
24
+ // the current state as a plain object (no proxies on the hook).
25
+ const dataKey = safeStringify(data);
26
+ useEffect(() => {
27
+ if (process.env.NODE_ENV !== "development") {
28
+ return;
29
+ }
30
+ const normalizedViews = views ? Array.isArray(views) ? views : [views] : ["browse", "raw"];
31
+ let plainData;
32
+ try {
33
+ plainData = JSON.parse(dataKey);
34
+ } catch {
35
+ plainData = data;
36
+ }
37
+ const hook = getHook();
38
+ hook.sections[name] = {
39
+ data: plainData,
40
+ group: group || "Sections",
41
+ views: normalizedViews,
42
+ updatedAt: Date.now()
43
+ };
44
+ hook.revision++;
45
+ return () => {
46
+ if (window.__WEBINY_DEVTOOLS_HOOK__) {
47
+ delete window.__WEBINY_DEVTOOLS_HOOK__.sections[name];
48
+ window.__WEBINY_DEVTOOLS_HOOK__.revision++;
49
+ }
50
+ };
51
+ }, [name, dataKey, group, Array.isArray(views) ? views.join(",") : views]);
52
+ return null;
53
+ }
54
+ function safeStringify(value) {
55
+ try {
56
+ return JSON.stringify(value, (_key, v) => {
57
+ if (typeof v === "function") {
58
+ return `[Function: ${v.name || "anonymous"}]`;
59
+ }
60
+ if (typeof v === "undefined") {
61
+ return "[undefined]";
62
+ }
63
+ return v;
64
+ });
65
+ } catch {
66
+ return String(value);
67
+ }
68
+ }
69
+
70
+ //# sourceMappingURL=DevToolsSection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["useEffect","getHook","DevToolsSection","name","data","group","views","dataKey","safeStringify","process","env","NODE_ENV","normalizedViews","Array","isArray","plainData","JSON","parse","hook","sections","updatedAt","Date","now","revision","window","__WEBINY_DEVTOOLS_HOOK__","join","value","stringify","_key","v","String"],"sources":["DevToolsSection.tsx"],"sourcesContent":["import { useEffect } from \"react\";\nimport { getHook } from \"./useDebugConfig.js\";\n\ntype DevToolsView = \"browse\" | \"raw\";\n\ninterface DevToolsSectionProps {\n name: string;\n data: unknown;\n /**\n * Group name for the sidebar. Items with the same group appear\n * under the same header. Defaults to \"Sections\".\n *\n * @example\n * ```tsx\n * <DevToolsSection name=\"Article\" group=\"CMS\" data={model} />\n * <DevToolsSection name=\"Author\" group=\"CMS\" data={author} />\n * ```\n */\n group?: string;\n /**\n * Which views to show in the detail panel.\n * - `\"browse\"` — split view with root keys on the left, value tree on the right\n * - `\"raw\"` — full JSON tree view\n *\n * Accepts a single view or an array. First item is the default tab.\n * @default [\"browse\", \"raw\"]\n */\n views?: DevToolsView | DevToolsView[];\n}\n\n/**\n * Registers a named section in the Webiny DevTools extension.\n * Renders nothing — purely a data registration side-effect.\n *\n * When the component unmounts (e.g., route change), the section\n * is automatically removed from DevTools.\n *\n * @example\n * ```tsx\n * <DevToolsSection name=\"CMS Model\" data={model} />\n * <DevToolsSection name=\"Article\" group=\"CMS\" data={model} views=\"raw\" />\n * ```\n */\nexport function DevToolsSection({ name, data, group, views }: DevToolsSectionProps) {\n // Snapshot the data on every render to get a stable, plain value.\n // This also ensures that if data is a MobX observable, we capture\n // the current state as a plain object (no proxies on the hook).\n const dataKey = safeStringify(data);\n\n useEffect(() => {\n if (process.env.NODE_ENV !== \"development\") {\n return;\n }\n\n const normalizedViews: DevToolsView[] = views\n ? Array.isArray(views)\n ? views\n : [views]\n : [\"browse\", \"raw\"];\n\n let plainData: unknown;\n try {\n plainData = JSON.parse(dataKey);\n } catch {\n plainData = data;\n }\n\n const hook = getHook();\n hook.sections[name] = {\n data: plainData,\n group: group || \"Sections\",\n views: normalizedViews,\n updatedAt: Date.now()\n };\n hook.revision++;\n\n return () => {\n if (window.__WEBINY_DEVTOOLS_HOOK__) {\n delete window.__WEBINY_DEVTOOLS_HOOK__.sections[name];\n window.__WEBINY_DEVTOOLS_HOOK__.revision++;\n }\n };\n }, [name, dataKey, group, Array.isArray(views) ? views.join(\",\") : views]);\n\n return null;\n}\n\nfunction safeStringify(value: unknown): string {\n try {\n return JSON.stringify(value, (_key, v) => {\n if (typeof v === \"function\") {\n return `[Function: ${v.name || \"anonymous\"}]`;\n }\n if (typeof v === \"undefined\") {\n return \"[undefined]\";\n }\n return v;\n });\n } catch {\n return String(value);\n }\n}\n"],"mappings":"AAAA,SAASA,SAAS,QAAQ,OAAO;AACjC,SAASC,OAAO;AA6BhB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASC,eAAeA,CAAC;EAAEC,IAAI;EAAEC,IAAI;EAAEC,KAAK;EAAEC;AAA4B,CAAC,EAAE;EAChF;EACA;EACA;EACA,MAAMC,OAAO,GAAGC,aAAa,CAACJ,IAAI,CAAC;EAEnCJ,SAAS,CAAC,MAAM;IACZ,IAAIS,OAAO,CAACC,GAAG,CAACC,QAAQ,KAAK,aAAa,EAAE;MACxC;IACJ;IAEA,MAAMC,eAA+B,GAAGN,KAAK,GACvCO,KAAK,CAACC,OAAO,CAACR,KAAK,CAAC,GAChBA,KAAK,GACL,CAACA,KAAK,CAAC,GACX,CAAC,QAAQ,EAAE,KAAK,CAAC;IAEvB,IAAIS,SAAkB;IACtB,IAAI;MACAA,SAAS,GAAGC,IAAI,CAACC,KAAK,CAACV,OAAO,CAAC;IACnC,CAAC,CAAC,MAAM;MACJQ,SAAS,GAAGX,IAAI;IACpB;IAEA,MAAMc,IAAI,GAAGjB,OAAO,CAAC,CAAC;IACtBiB,IAAI,CAACC,QAAQ,CAAChB,IAAI,CAAC,GAAG;MAClBC,IAAI,EAAEW,SAAS;MACfV,KAAK,EAAEA,KAAK,IAAI,UAAU;MAC1BC,KAAK,EAAEM,eAAe;MACtBQ,SAAS,EAAEC,IAAI,CAACC,GAAG,CAAC;IACxB,CAAC;IACDJ,IAAI,CAACK,QAAQ,EAAE;IAEf,OAAO,MAAM;MACT,IAAIC,MAAM,CAACC,wBAAwB,EAAE;QACjC,OAAOD,MAAM,CAACC,wBAAwB,CAACN,QAAQ,CAAChB,IAAI,CAAC;QACrDqB,MAAM,CAACC,wBAAwB,CAACF,QAAQ,EAAE;MAC9C;IACJ,CAAC;EACL,CAAC,EAAE,CAACpB,IAAI,EAAEI,OAAO,EAAEF,KAAK,EAAEQ,KAAK,CAACC,OAAO,CAACR,KAAK,CAAC,GAAGA,KAAK,CAACoB,IAAI,CAAC,GAAG,CAAC,GAAGpB,KAAK,CAAC,CAAC;EAE1E,OAAO,IAAI;AACf;AAEA,SAASE,aAAaA,CAACmB,KAAc,EAAU;EAC3C,IAAI;IACA,OAAOX,IAAI,CAACY,SAAS,CAACD,KAAK,EAAE,CAACE,IAAI,EAAEC,CAAC,KAAK;MACtC,IAAI,OAAOA,CAAC,KAAK,UAAU,EAAE;QACzB,OAAO,cAAcA,CAAC,CAAC3B,IAAI,IAAI,WAAW,GAAG;MACjD;MACA,IAAI,OAAO2B,CAAC,KAAK,WAAW,EAAE;QAC1B,OAAO,aAAa;MACxB;MACA,OAAOA,CAAC;IACZ,CAAC,CAAC;EACN,CAAC,CAAC,MAAM;IACJ,OAAOC,MAAM,CAACJ,KAAK,CAAC;EACxB;AACJ","ignoreList":[]}
@@ -92,6 +92,7 @@ export function createConfigurableComponent(name) {
92
92
  return /*#__PURE__*/React.createElement(ViewContext.Provider, {
93
93
  value: context
94
94
  }, /*#__PURE__*/React.createElement(Properties, {
95
+ name: name,
95
96
  onChange: stateUpdater
96
97
  }, /*#__PURE__*/React.createElement(ConfigApplyTree, null)), properties !== null ? children : null);
97
98
  };
@@ -1 +1 @@
1
- {"version":3,"names":["React","useCallback","useContext","useEffect","useMemo","useState","Compose","makeDecoratable","Properties","toObject","useDebugConfig","PropertyPriorityProvider","createHOC","newChildren","BaseComponent","ConfigHOC","children","createElement","createConfigurableComponent","name","ConfigApplyPrimary","Fragment","ConfigApplySecondary","Config","priority","component","with","defaultContext","properties","ViewContext","createContext","ConfigApplyTree","memo","WithConfig","onProperties","setProperties","resolvedProperties","context","stateUpdater","Provider","value","onChange","useConfig"],"sources":["createConfigurableComponent.tsx"],"sourcesContent":["import React, { useCallback, useContext, useEffect, useMemo, useState } from \"react\";\nimport type { Decorator } from \"@webiny/react-composition\";\nimport { Compose, makeDecoratable } from \"@webiny/react-composition\";\nimport type { GenericComponent } from \"@webiny/react-composition/types.js\";\nimport type { Property } from \"~/index.js\";\nimport { Properties, toObject } from \"~/index.js\";\nimport { useDebugConfig } from \"./useDebugConfig.js\";\nimport { PropertyPriorityProvider } from \"./PropertyPriority.js\";\n\n/**\n * Each `<Config>` call composes a new HOC around the previous one via `Compose`.\n * The last composed HOC is the outermost wrapper. By placing `{newChildren}`\n * (this HOC's addition) before `{children}` (all previously composed configs),\n * the final render order matches declaration order:\n *\n * <Config>A</Config> → renders first (outermost HOC, its newChildren rendered first)\n * <Config>B</Config> → renders second\n * <Config>C</Config> → renders third (innermost, rendered last via children chain)\n *\n * This is important because Property components register in mount order,\n * so declaration order = mount order = predictable config resolution.\n */\nconst createHOC =\n (newChildren: React.ReactNode): Decorator<GenericComponent<{ children?: React.ReactNode }>> =>\n BaseComponent => {\n return function ConfigHOC({ children }) {\n return (\n <BaseComponent>\n {newChildren}\n {children}\n </BaseComponent>\n );\n };\n };\n\nexport interface WithConfigProps {\n children: React.ReactNode;\n onProperties?(properties: Property[]): void;\n}\n\ninterface ConfigApplyProps {\n children?: React.ReactNode;\n}\n\nexport interface ConfigProps {\n children: React.ReactNode;\n priority?: \"primary\" | \"secondary\";\n}\n\nexport function createConfigurableComponent<TConfig>(name: string) {\n const ConfigApplyPrimary = makeDecoratable(\n `${name}ConfigApply<Primary>`,\n ({ children }: ConfigApplyProps) => {\n return <>{children}</>;\n }\n );\n\n const ConfigApplySecondary = makeDecoratable(\n `${name}ConfigApply<Secondary>`,\n ({ children }: ConfigApplyProps) => {\n return <>{children}</>;\n }\n );\n\n const Config = ({ priority = \"primary\", children }: ConfigProps) => {\n if (priority === \"primary\") {\n return <Compose component={ConfigApplyPrimary} with={createHOC(children)} />;\n }\n return <Compose component={ConfigApplySecondary} with={createHOC(children)} />;\n };\n\n interface ViewContext {\n properties: Property[];\n }\n\n const defaultContext = { properties: [] };\n\n const ViewContext = React.createContext<ViewContext>(defaultContext);\n\n /**\n * Memoized config subtree — ConfigApply components don't depend on WithConfig\n * props, so they must not remount when the parent re-renders. Without this,\n * every parent re-render causes Property components inside HOCs to unmount\n * and remount, corrupting the config object.\n */\n const ConfigApplyTree = React.memo(function ConfigApplyTree() {\n return (\n <>\n <ConfigApplyPrimary />\n <PropertyPriorityProvider priority={1}>\n <ConfigApplySecondary />\n </PropertyPriorityProvider>\n </>\n );\n });\n\n const WithConfig = ({ onProperties, children }: WithConfigProps) => {\n // `null` = config not yet collected; `[]` = collected but empty.\n // This distinction is critical: children must NOT render until the\n // PropertyStore debounce has flushed and delivered the initial config.\n // Rendering children with partial/empty config causes errors in\n // consumers like LexicalEditor that require a complete config on mount.\n const [properties, setProperties] = useState<Property[] | null>(null);\n const resolvedProperties = properties ?? [];\n useDebugConfig(name, resolvedProperties);\n const context = { properties: resolvedProperties };\n\n useEffect(() => {\n if (properties !== null && typeof onProperties === \"function\") {\n onProperties(properties);\n }\n }, [properties]);\n\n const stateUpdater = useCallback((properties: Property[]) => {\n setProperties(properties);\n }, []);\n\n return (\n <ViewContext.Provider value={context}>\n {/* ConfigApplyTree always renders so Property components inside\n composed HOCs can mount and register with the PropertyStore.\n It lives outside the children gate below. */}\n <Properties onChange={stateUpdater}>\n <ConfigApplyTree />\n </Properties>\n {/* Gate: only render children once the PropertyStore has flushed\n its first batch (properties !== null). This guarantees that\n useConfig() returns a complete config object on first render. */}\n {properties !== null ? children : null}\n </ViewContext.Provider>\n );\n };\n\n function useConfig<TExtra extends object>(): TConfig & TExtra {\n const { properties } = useContext(ViewContext);\n return useMemo(() => toObject<TConfig & TExtra>(properties), [properties]);\n }\n\n return {\n WithConfig,\n Config,\n useConfig\n };\n}\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["React","useCallback","useContext","useEffect","useMemo","useState","Compose","makeDecoratable","Properties","toObject","useDebugConfig","PropertyPriorityProvider","createHOC","newChildren","BaseComponent","ConfigHOC","children","createElement","createConfigurableComponent","name","ConfigApplyPrimary","Fragment","ConfigApplySecondary","Config","priority","component","with","defaultContext","properties","ViewContext","createContext","ConfigApplyTree","memo","WithConfig","onProperties","setProperties","resolvedProperties","context","stateUpdater","Provider","value","onChange","useConfig"],"sources":["createConfigurableComponent.tsx"],"sourcesContent":["import React, { useCallback, useContext, useEffect, useMemo, useState } from \"react\";\nimport type { Decorator } from \"@webiny/react-composition\";\nimport { Compose, makeDecoratable } from \"@webiny/react-composition\";\nimport type { GenericComponent } from \"@webiny/react-composition/types.js\";\nimport type { Property } from \"~/index.js\";\nimport { Properties, toObject } from \"~/index.js\";\nimport { useDebugConfig } from \"./useDebugConfig.js\";\nimport { PropertyPriorityProvider } from \"./PropertyPriority.js\";\n\n/**\n * Each `<Config>` call composes a new HOC around the previous one via `Compose`.\n * The last composed HOC is the outermost wrapper. By placing `{newChildren}`\n * (this HOC's addition) before `{children}` (all previously composed configs),\n * the final render order matches declaration order:\n *\n * <Config>A</Config> → renders first (outermost HOC, its newChildren rendered first)\n * <Config>B</Config> → renders second\n * <Config>C</Config> → renders third (innermost, rendered last via children chain)\n *\n * This is important because Property components register in mount order,\n * so declaration order = mount order = predictable config resolution.\n */\nconst createHOC =\n (newChildren: React.ReactNode): Decorator<GenericComponent<{ children?: React.ReactNode }>> =>\n BaseComponent => {\n return function ConfigHOC({ children }) {\n return (\n <BaseComponent>\n {newChildren}\n {children}\n </BaseComponent>\n );\n };\n };\n\nexport interface WithConfigProps {\n children: React.ReactNode;\n onProperties?(properties: Property[]): void;\n}\n\ninterface ConfigApplyProps {\n children?: React.ReactNode;\n}\n\nexport interface ConfigProps {\n children: React.ReactNode;\n priority?: \"primary\" | \"secondary\";\n}\n\nexport function createConfigurableComponent<TConfig>(name: string) {\n const ConfigApplyPrimary = makeDecoratable(\n `${name}ConfigApply<Primary>`,\n ({ children }: ConfigApplyProps) => {\n return <>{children}</>;\n }\n );\n\n const ConfigApplySecondary = makeDecoratable(\n `${name}ConfigApply<Secondary>`,\n ({ children }: ConfigApplyProps) => {\n return <>{children}</>;\n }\n );\n\n const Config = ({ priority = \"primary\", children }: ConfigProps) => {\n if (priority === \"primary\") {\n return <Compose component={ConfigApplyPrimary} with={createHOC(children)} />;\n }\n return <Compose component={ConfigApplySecondary} with={createHOC(children)} />;\n };\n\n interface ViewContext {\n properties: Property[];\n }\n\n const defaultContext = { properties: [] };\n\n const ViewContext = React.createContext<ViewContext>(defaultContext);\n\n /**\n * Memoized config subtree — ConfigApply components don't depend on WithConfig\n * props, so they must not remount when the parent re-renders. Without this,\n * every parent re-render causes Property components inside HOCs to unmount\n * and remount, corrupting the config object.\n */\n const ConfigApplyTree = React.memo(function ConfigApplyTree() {\n return (\n <>\n <ConfigApplyPrimary />\n <PropertyPriorityProvider priority={1}>\n <ConfigApplySecondary />\n </PropertyPriorityProvider>\n </>\n );\n });\n\n const WithConfig = ({ onProperties, children }: WithConfigProps) => {\n // `null` = config not yet collected; `[]` = collected but empty.\n // This distinction is critical: children must NOT render until the\n // PropertyStore debounce has flushed and delivered the initial config.\n // Rendering children with partial/empty config causes errors in\n // consumers like LexicalEditor that require a complete config on mount.\n const [properties, setProperties] = useState<Property[] | null>(null);\n const resolvedProperties = properties ?? [];\n useDebugConfig(name, resolvedProperties);\n const context = { properties: resolvedProperties };\n\n useEffect(() => {\n if (properties !== null && typeof onProperties === \"function\") {\n onProperties(properties);\n }\n }, [properties]);\n\n const stateUpdater = useCallback((properties: Property[]) => {\n setProperties(properties);\n }, []);\n\n return (\n <ViewContext.Provider value={context}>\n {/* ConfigApplyTree always renders so Property components inside\n composed HOCs can mount and register with the PropertyStore.\n It lives outside the children gate below. */}\n <Properties name={name} onChange={stateUpdater}>\n <ConfigApplyTree />\n </Properties>\n {/* Gate: only render children once the PropertyStore has flushed\n its first batch (properties !== null). This guarantees that\n useConfig() returns a complete config object on first render. */}\n {properties !== null ? children : null}\n </ViewContext.Provider>\n );\n };\n\n function useConfig<TExtra extends object>(): TConfig & TExtra {\n const { properties } = useContext(ViewContext);\n return useMemo(() => toObject<TConfig & TExtra>(properties), [properties]);\n }\n\n return {\n WithConfig,\n Config,\n useConfig\n };\n}\n"],"mappings":"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","ignoreList":[]}
@@ -10,6 +10,9 @@ export declare class PropertyStore {
10
10
  private order;
11
11
  private queue;
12
12
  private listeners;
13
+ private priorities;
14
+ /** Properties that were explicitly positioned via before/after. */
15
+ private positioned;
13
16
  /**
14
17
  * Synchronous lookup map — written immediately on addProperty (before debounce),
15
18
  * so useAncestor can find properties during render.
@@ -4,6 +4,9 @@ export class PropertyStore {
4
4
  order = [];
5
5
  queue = [];
6
6
  listeners = new Set();
7
+ priorities = new Map();
8
+ /** Properties that were explicitly positioned via before/after. */
9
+ positioned = new Set();
7
10
 
8
11
  /**
9
12
  * Synchronous lookup map — written immediately on addProperty (before debounce),
@@ -108,14 +111,30 @@ export class PropertyStore {
108
111
  break;
109
112
  }
110
113
  }
114
+
115
+ // Stable-sort the order array by priority, but only for properties
116
+ // that were NOT explicitly positioned via before/after. Explicitly
117
+ // positioned properties keep their placement.
118
+ this.order.sort((a, b) => {
119
+ if (this.positioned.has(a) || this.positioned.has(b)) {
120
+ return 0;
121
+ }
122
+ return (this.priorities.get(a) ?? 0) - (this.priorities.get(b) ?? 0);
123
+ });
111
124
  const properties = this.allProperties;
112
125
  for (const listener of this.listeners) {
113
126
  listener(properties);
114
127
  }
115
128
  }
116
129
  executeAdd(property, options) {
130
+ if (options.after || options.before) {
131
+ this.positioned.add(property.id);
132
+ }
117
133
  const exists = this.map.has(property.id);
118
134
  if (exists) {
135
+ // Merge into existing property. Keep the original priority so
136
+ // that a secondary config overriding a primary property doesn't
137
+ // cause the re-sort to move it after all primary properties.
119
138
  const existing = this.map.get(property.id);
120
139
  this.map.set(property.id, {
121
140
  ...existing,
@@ -129,6 +148,8 @@ export class PropertyStore {
129
148
  return;
130
149
  }
131
150
  this.map.set(property.id, property);
151
+ // Set priority only for new properties — not merges (handled above).
152
+ this.priorities.set(property.id, options.priority ?? 0);
132
153
  if (options.after) {
133
154
  this.insertAfter(property.id, options.after);
134
155
  } else if (options.before) {
@@ -142,8 +163,16 @@ export class PropertyStore {
142
163
  return;
143
164
  }
144
165
  this.map.delete(id);
166
+ this.priorities.delete(id);
167
+ this.positioned.delete(id);
145
168
  this.order = this.order.filter(oid => oid !== id);
146
- this.removeDescendants(id);
169
+ // Note: we intentionally do NOT call removeDescendants here.
170
+ // React's component lifecycle ensures that when a parent Property
171
+ // unmounts, all child Properties unmount too — each triggering its
172
+ // own removeProperty call. Calling removeDescendants would wipe
173
+ // children that belong to OTHER still-mounted configs sharing the
174
+ // same parent ID (e.g., id="pageSettings" used by both primary
175
+ // and secondary configs).
147
176
  }
148
177
  executeReplace(oldId, newProperty) {
149
178
  const idx = this.order.indexOf(oldId);
@@ -1 +1 @@
1
- {"version":3,"names":["debounce","PropertyStore","map","Map","order","queue","listeners","Set","lookup","scheduleFlush","processQueue","allProperties","filter","id","has","get","subscribe","listener","add","delete","getChildrenOf","parentId","Array","from","values","p","parent","getById","registerLookup","property","existing","set","addProperty","options","push","type","removeProperty","replaceProperty","oldId","newProperty","length","ops","splice","sort","a","b","pa","priority","pb","op","executeAdd","executeRemove","executeReplace","properties","exists","after","reposition","before","insertAfter","insertBefore","oid","removeDescendants","idx","indexOf","endsWith","unshift","targetIdx","targetId","position","children","child"],"sources":["PropertyStore.ts"],"sourcesContent":["import debounce from \"lodash/debounce.js\";\nimport type { Property } from \"../Properties.js\";\n\ninterface AddPropertyOptions {\n after?: string;\n before?: string;\n priority?: number;\n}\n\ntype Operation =\n | { type: \"add\"; property: Property; options: AddPropertyOptions }\n | { type: \"remove\"; id: string }\n | { type: \"replace\"; oldId: string; newProperty: Property };\n\ntype Listener = (properties: Property[]) => void;\n\nexport class PropertyStore {\n private map = new Map<string, Property>();\n private order: string[] = [];\n private queue: Operation[] = [];\n private listeners = new Set<Listener>();\n\n /**\n * Synchronous lookup map — written immediately on addProperty (before debounce),\n * so useAncestor can find properties during render.\n */\n private lookup = new Map<string, Property>();\n\n private scheduleFlush = debounce(() => {\n this.processQueue();\n }, 0);\n\n get allProperties(): Property[] {\n return this.order.filter(id => this.map.has(id)).map(id => this.map.get(id)!);\n }\n\n subscribe(listener: Listener): () => void {\n this.listeners.add(listener);\n return () => {\n this.listeners.delete(listener);\n };\n }\n\n /**\n * Returns properties that are children of the given parent ID.\n * Reads from the synchronous lookup map, so it works during render\n * before the debounced queue has flushed.\n */\n getChildrenOf(parentId: string): Property[] {\n return Array.from(this.lookup.values()).filter(p => p.parent === parentId);\n }\n\n /**\n * Find a property by ID from the synchronous lookup map.\n */\n getById(id: string): Property | undefined {\n return this.lookup.get(id);\n }\n\n /**\n * Register a property in the synchronous lookup map during render,\n * so useAncestor can find it before the debounced queue flushes.\n */\n registerLookup(property: Property): void {\n if (this.lookup.has(property.id)) {\n const existing = this.lookup.get(property.id)!;\n this.lookup.set(property.id, { ...existing, ...property });\n } else {\n this.lookup.set(property.id, property);\n }\n }\n\n addProperty(property: Property, options: AddPropertyOptions = {}): void {\n this.registerLookup(property);\n this.queue.push({ type: \"add\", property, options });\n this.scheduleFlush();\n }\n\n removeProperty(id: string): void {\n this.lookup.delete(id);\n this.queue.push({ type: \"remove\", id });\n this.scheduleFlush();\n }\n\n replaceProperty(oldId: string, newProperty: Property): void {\n this.lookup.delete(oldId);\n this.lookup.set(newProperty.id, newProperty);\n this.queue.push({ type: \"replace\", oldId, newProperty });\n this.scheduleFlush();\n }\n\n private processQueue(): void {\n if (this.queue.length === 0) {\n return;\n }\n\n const ops = this.queue.splice(0);\n\n // Stable-sort operations so that \"add\" ops with lower priority numbers\n // are processed first. Non-add operations and adds with default priority (0)\n // keep their original order.\n ops.sort((a, b) => {\n const pa = a.type === \"add\" ? (a.options.priority ?? 0) : 0;\n const pb = b.type === \"add\" ? (b.options.priority ?? 0) : 0;\n return pa - pb;\n });\n\n for (const op of ops) {\n switch (op.type) {\n case \"add\":\n this.executeAdd(op.property, op.options);\n break;\n case \"remove\":\n this.executeRemove(op.id);\n break;\n case \"replace\":\n this.executeReplace(op.oldId, op.newProperty);\n break;\n }\n }\n\n const properties = this.allProperties;\n for (const listener of this.listeners) {\n listener(properties);\n }\n }\n\n private executeAdd(property: Property, options: AddPropertyOptions): void {\n const exists = this.map.has(property.id);\n\n if (exists) {\n const existing = this.map.get(property.id)!;\n this.map.set(property.id, { ...existing, ...property });\n\n if (options.after) {\n this.reposition(property.id, options.after, \"after\");\n } else if (options.before) {\n this.reposition(property.id, options.before, \"before\");\n }\n return;\n }\n\n this.map.set(property.id, property);\n\n if (options.after) {\n this.insertAfter(property.id, options.after);\n } else if (options.before) {\n this.insertBefore(property.id, options.before);\n } else {\n this.order.push(property.id);\n }\n }\n\n private executeRemove(id: string): void {\n if (!this.map.has(id)) {\n return;\n }\n this.map.delete(id);\n this.order = this.order.filter(oid => oid !== id);\n this.removeDescendants(id);\n }\n\n private executeReplace(oldId: string, newProperty: Property): void {\n const idx = this.order.indexOf(oldId);\n if (idx === -1) {\n return;\n }\n\n this.map.delete(oldId);\n this.map.set(newProperty.id, newProperty);\n this.order[idx] = newProperty.id;\n this.removeDescendants(oldId);\n }\n\n private insertBefore(id: string, before: string): void {\n if (before.endsWith(\"$first\")) {\n this.order.unshift(id);\n return;\n }\n const targetIdx = this.order.indexOf(before);\n if (targetIdx === -1) {\n this.order.push(id);\n return;\n }\n this.order.splice(targetIdx, 0, id);\n }\n\n private insertAfter(id: string, after: string): void {\n if (after.endsWith(\"$last\")) {\n this.order.push(id);\n return;\n }\n const targetIdx = this.order.indexOf(after);\n if (targetIdx === -1) {\n this.order.push(id);\n return;\n }\n this.order.splice(targetIdx + 1, 0, id);\n }\n\n private reposition(id: string, targetId: string, position: \"before\" | \"after\"): void {\n this.order = this.order.filter(oid => oid !== id);\n\n if (position === \"before\") {\n this.insertBefore(id, targetId);\n } else {\n this.insertAfter(id, targetId);\n }\n }\n\n private removeDescendants(parentId: string): void {\n const children = Array.from(this.map.values()).filter(p => p.parent === parentId);\n for (const child of children) {\n this.map.delete(child.id);\n this.order = this.order.filter(oid => oid !== child.id);\n this.removeDescendants(child.id);\n }\n }\n}\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["debounce","PropertyStore","map","Map","order","queue","listeners","Set","priorities","positioned","lookup","scheduleFlush","processQueue","allProperties","filter","id","has","get","subscribe","listener","add","delete","getChildrenOf","parentId","Array","from","values","p","parent","getById","registerLookup","property","existing","set","addProperty","options","push","type","removeProperty","replaceProperty","oldId","newProperty","length","ops","splice","sort","a","b","pa","priority","pb","op","executeAdd","executeRemove","executeReplace","properties","after","before","exists","reposition","insertAfter","insertBefore","oid","idx","indexOf","removeDescendants","endsWith","unshift","targetIdx","targetId","position","children","child"],"sources":["PropertyStore.ts"],"sourcesContent":["import debounce from \"lodash/debounce.js\";\nimport type { Property } from \"../Properties.js\";\n\ninterface AddPropertyOptions {\n after?: string;\n before?: string;\n priority?: number;\n}\n\ntype Operation =\n | { type: \"add\"; property: Property; options: AddPropertyOptions }\n | { type: \"remove\"; id: string }\n | { type: \"replace\"; oldId: string; newProperty: Property };\n\ntype Listener = (properties: Property[]) => void;\n\nexport class PropertyStore {\n private map = new Map<string, Property>();\n private order: string[] = [];\n private queue: Operation[] = [];\n private listeners = new Set<Listener>();\n private priorities = new Map<string, number>();\n /** Properties that were explicitly positioned via before/after. */\n private positioned = new Set<string>();\n\n /**\n * Synchronous lookup map — written immediately on addProperty (before debounce),\n * so useAncestor can find properties during render.\n */\n private lookup = new Map<string, Property>();\n\n private scheduleFlush = debounce(() => {\n this.processQueue();\n }, 0);\n\n get allProperties(): Property[] {\n return this.order.filter(id => this.map.has(id)).map(id => this.map.get(id)!);\n }\n\n subscribe(listener: Listener): () => void {\n this.listeners.add(listener);\n return () => {\n this.listeners.delete(listener);\n };\n }\n\n /**\n * Returns properties that are children of the given parent ID.\n * Reads from the synchronous lookup map, so it works during render\n * before the debounced queue has flushed.\n */\n getChildrenOf(parentId: string): Property[] {\n return Array.from(this.lookup.values()).filter(p => p.parent === parentId);\n }\n\n /**\n * Find a property by ID from the synchronous lookup map.\n */\n getById(id: string): Property | undefined {\n return this.lookup.get(id);\n }\n\n /**\n * Register a property in the synchronous lookup map during render,\n * so useAncestor can find it before the debounced queue flushes.\n */\n registerLookup(property: Property): void {\n if (this.lookup.has(property.id)) {\n const existing = this.lookup.get(property.id)!;\n this.lookup.set(property.id, { ...existing, ...property });\n } else {\n this.lookup.set(property.id, property);\n }\n }\n\n addProperty(property: Property, options: AddPropertyOptions = {}): void {\n this.registerLookup(property);\n this.queue.push({ type: \"add\", property, options });\n this.scheduleFlush();\n }\n\n removeProperty(id: string): void {\n this.lookup.delete(id);\n this.queue.push({ type: \"remove\", id });\n this.scheduleFlush();\n }\n\n replaceProperty(oldId: string, newProperty: Property): void {\n this.lookup.delete(oldId);\n this.lookup.set(newProperty.id, newProperty);\n this.queue.push({ type: \"replace\", oldId, newProperty });\n this.scheduleFlush();\n }\n\n private processQueue(): void {\n if (this.queue.length === 0) {\n return;\n }\n\n const ops = this.queue.splice(0);\n\n // Stable-sort operations so that \"add\" ops with lower priority numbers\n // are processed first. Non-add operations and adds with default priority (0)\n // keep their original order.\n ops.sort((a, b) => {\n const pa = a.type === \"add\" ? (a.options.priority ?? 0) : 0;\n const pb = b.type === \"add\" ? (b.options.priority ?? 0) : 0;\n return pa - pb;\n });\n\n for (const op of ops) {\n switch (op.type) {\n case \"add\":\n this.executeAdd(op.property, op.options);\n break;\n case \"remove\":\n this.executeRemove(op.id);\n break;\n case \"replace\":\n this.executeReplace(op.oldId, op.newProperty);\n break;\n }\n }\n\n // Stable-sort the order array by priority, but only for properties\n // that were NOT explicitly positioned via before/after. Explicitly\n // positioned properties keep their placement.\n this.order.sort((a, b) => {\n if (this.positioned.has(a) || this.positioned.has(b)) {\n return 0;\n }\n return (this.priorities.get(a) ?? 0) - (this.priorities.get(b) ?? 0);\n });\n\n const properties = this.allProperties;\n for (const listener of this.listeners) {\n listener(properties);\n }\n }\n\n private executeAdd(property: Property, options: AddPropertyOptions): void {\n if (options.after || options.before) {\n this.positioned.add(property.id);\n }\n\n const exists = this.map.has(property.id);\n\n if (exists) {\n // Merge into existing property. Keep the original priority so\n // that a secondary config overriding a primary property doesn't\n // cause the re-sort to move it after all primary properties.\n const existing = this.map.get(property.id)!;\n this.map.set(property.id, { ...existing, ...property });\n\n if (options.after) {\n this.reposition(property.id, options.after, \"after\");\n } else if (options.before) {\n this.reposition(property.id, options.before, \"before\");\n }\n return;\n }\n\n this.map.set(property.id, property);\n // Set priority only for new properties — not merges (handled above).\n this.priorities.set(property.id, options.priority ?? 0);\n\n if (options.after) {\n this.insertAfter(property.id, options.after);\n } else if (options.before) {\n this.insertBefore(property.id, options.before);\n } else {\n this.order.push(property.id);\n }\n }\n\n private executeRemove(id: string): void {\n if (!this.map.has(id)) {\n return;\n }\n this.map.delete(id);\n this.priorities.delete(id);\n this.positioned.delete(id);\n this.order = this.order.filter(oid => oid !== id);\n // Note: we intentionally do NOT call removeDescendants here.\n // React's component lifecycle ensures that when a parent Property\n // unmounts, all child Properties unmount too — each triggering its\n // own removeProperty call. Calling removeDescendants would wipe\n // children that belong to OTHER still-mounted configs sharing the\n // same parent ID (e.g., id=\"pageSettings\" used by both primary\n // and secondary configs).\n }\n\n private executeReplace(oldId: string, newProperty: Property): void {\n const idx = this.order.indexOf(oldId);\n if (idx === -1) {\n return;\n }\n\n this.map.delete(oldId);\n this.map.set(newProperty.id, newProperty);\n this.order[idx] = newProperty.id;\n this.removeDescendants(oldId);\n }\n\n private insertBefore(id: string, before: string): void {\n if (before.endsWith(\"$first\")) {\n this.order.unshift(id);\n return;\n }\n const targetIdx = this.order.indexOf(before);\n if (targetIdx === -1) {\n this.order.push(id);\n return;\n }\n this.order.splice(targetIdx, 0, id);\n }\n\n private insertAfter(id: string, after: string): void {\n if (after.endsWith(\"$last\")) {\n this.order.push(id);\n return;\n }\n const targetIdx = this.order.indexOf(after);\n if (targetIdx === -1) {\n this.order.push(id);\n return;\n }\n this.order.splice(targetIdx + 1, 0, id);\n }\n\n private reposition(id: string, targetId: string, position: \"before\" | \"after\"): void {\n this.order = this.order.filter(oid => oid !== id);\n\n if (position === \"before\") {\n this.insertBefore(id, targetId);\n } else {\n this.insertAfter(id, targetId);\n }\n }\n\n private removeDescendants(parentId: string): void {\n const children = Array.from(this.map.values()).filter(p => p.parent === parentId);\n for (const child of children) {\n this.map.delete(child.id);\n this.order = this.order.filter(oid => oid !== child.id);\n this.removeDescendants(child.id);\n }\n }\n}\n"],"mappings":"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","ignoreList":[]}
package/index.d.ts CHANGED
@@ -4,3 +4,4 @@ export * from "./useDebugConfig.js";
4
4
  export * from "./useIdGenerator.js";
5
5
  export * from "./createConfigurableComponent.js";
6
6
  export * from "./domain/index.js";
7
+ export { DevToolsSection } from "./DevToolsSection.js";
package/index.js CHANGED
@@ -4,5 +4,6 @@ export * from "./useDebugConfig.js";
4
4
  export * from "./useIdGenerator.js";
5
5
  export * from "./createConfigurableComponent.js";
6
6
  export * from "./domain/index.js";
7
+ export { DevToolsSection } from "./DevToolsSection.js";
7
8
 
8
9
  //# sourceMappingURL=index.js.map
package/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"names":[],"sources":["index.ts"],"sourcesContent":["export * from \"./utils.js\";\nexport * from \"./Properties.js\";\nexport * from \"./useDebugConfig.js\";\nexport * from \"./useIdGenerator.js\";\nexport * from \"./createConfigurableComponent.js\";\nexport * from \"./domain/index.js\";\n"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA","ignoreList":[]}
1
+ {"version":3,"names":["DevToolsSection"],"sources":["index.ts"],"sourcesContent":["export * from \"./utils.js\";\nexport * from \"./Properties.js\";\nexport * from \"./useDebugConfig.js\";\nexport * from \"./useIdGenerator.js\";\nexport * from \"./createConfigurableComponent.js\";\nexport * from \"./domain/index.js\";\nexport { DevToolsSection } from \"./DevToolsSection.js\";\n"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA,SAASA,eAAe","ignoreList":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webiny/react-properties",
3
- "version": "6.0.0-rc.7",
3
+ "version": "6.1.0-beta.0",
4
4
  "type": "module",
5
5
  "main": "index.js",
6
6
  "repository": {
@@ -12,20 +12,20 @@
12
12
  "license": "MIT",
13
13
  "dependencies": {
14
14
  "@types/react": "18.2.79",
15
- "@webiny/react-composition": "6.0.0-rc.7",
15
+ "@webiny/react-composition": "6.1.0-beta.0",
16
16
  "lodash": "4.17.23",
17
- "nanoid": "5.1.6",
17
+ "nanoid": "5.1.7",
18
18
  "react": "18.2.0"
19
19
  },
20
20
  "devDependencies": {
21
- "@testing-library/react": "15.0.7",
22
- "@webiny/build-tools": "6.0.0-rc.7",
21
+ "@testing-library/react": "16.3.2",
22
+ "@webiny/build-tools": "6.1.0-beta.0",
23
23
  "prettier": "3.6.2",
24
- "vitest": "4.0.18"
24
+ "vitest": "4.1.2"
25
25
  },
26
26
  "publishConfig": {
27
27
  "access": "public",
28
28
  "directory": "dist"
29
29
  },
30
- "gitHead": "9c6892640a45679ff521e25cd6587dff57393a2e"
30
+ "gitHead": "a3bd3695c66c79238e380d7360d9731b5fcf9c87"
31
31
  }
@@ -1,7 +1,32 @@
1
1
  import type { Property } from "./Properties.js";
2
+ interface WebinyDevtoolsConfig {
3
+ properties: Array<{
4
+ id: string;
5
+ parent: string;
6
+ name: string;
7
+ value?: unknown;
8
+ array?: boolean;
9
+ }>;
10
+ config: unknown;
11
+ updatedAt: number;
12
+ }
13
+ interface WebinyDevtoolsSection {
14
+ data: unknown;
15
+ group: string;
16
+ views: string[];
17
+ updatedAt: number;
18
+ }
19
+ interface WebinyDevtoolsHook {
20
+ revision: number;
21
+ configs: Record<string, WebinyDevtoolsConfig>;
22
+ sections: Record<string, WebinyDevtoolsSection>;
23
+ }
2
24
  declare global {
3
25
  interface Window {
4
26
  __debugConfigs: Record<string, () => void>;
27
+ __WEBINY_DEVTOOLS_HOOK__?: WebinyDevtoolsHook;
5
28
  }
6
29
  }
30
+ export declare function getHook(): WebinyDevtoolsHook;
7
31
  export declare function useDebugConfig(name: string, properties: Property[]): void;
32
+ export {};
package/useDebugConfig.js CHANGED
@@ -1,17 +1,51 @@
1
1
  import { useEffect } from "react";
2
2
  import { toObject } from "./utils.js";
3
+ export function getHook() {
4
+ if (!window.__WEBINY_DEVTOOLS_HOOK__) {
5
+ window.__WEBINY_DEVTOOLS_HOOK__ = {
6
+ revision: 0,
7
+ configs: {},
8
+ sections: {}
9
+ };
10
+ }
11
+ return window.__WEBINY_DEVTOOLS_HOOK__;
12
+ }
3
13
  export function useDebugConfig(name, properties) {
4
14
  useEffect(() => {
5
15
  if (process.env.NODE_ENV !== "development") {
6
16
  return;
7
17
  }
18
+
19
+ // Legacy console.log support
8
20
  const configs = window.__debugConfigs ?? {};
9
21
  configs[name] = () => console.log(toObject(properties));
10
22
  window.__debugConfigs = configs;
23
+
24
+ // DevTools hook: structured data for the Chrome extension
25
+ const hook = getHook();
26
+ hook.configs[name] = {
27
+ properties: properties.map(p => ({
28
+ id: p.id,
29
+ parent: p.parent,
30
+ name: p.name,
31
+ value: p.value,
32
+ array: p.array
33
+ })),
34
+ config: toObject(properties),
35
+ updatedAt: Date.now()
36
+ };
37
+ hook.revision++;
11
38
  return () => {
39
+ // Legacy cleanup
12
40
  const configs = window.__debugConfigs ?? {};
13
41
  delete configs[name];
14
42
  window.__debugConfigs = configs;
43
+
44
+ // DevTools hook cleanup
45
+ if (window.__WEBINY_DEVTOOLS_HOOK__) {
46
+ delete window.__WEBINY_DEVTOOLS_HOOK__.configs[name];
47
+ window.__WEBINY_DEVTOOLS_HOOK__.revision++;
48
+ }
15
49
  };
16
50
  }, [properties]);
17
51
  }
@@ -1 +1 @@
1
- {"version":3,"names":["useEffect","toObject","useDebugConfig","name","properties","process","env","NODE_ENV","configs","window","__debugConfigs","console","log"],"sources":["useDebugConfig.ts"],"sourcesContent":["import { useEffect } from \"react\";\nimport type { Property } from \"./Properties.js\";\nimport { toObject } from \"./utils.js\";\n\ndeclare global {\n interface Window {\n __debugConfigs: Record<string, () => void>;\n }\n}\n\nexport function useDebugConfig(name: string, properties: Property[]) {\n useEffect(() => {\n if (process.env.NODE_ENV !== \"development\") {\n return;\n }\n\n const configs = window.__debugConfigs ?? {};\n configs[name] = () => console.log(toObject(properties));\n window.__debugConfigs = configs;\n\n return () => {\n const configs = window.__debugConfigs ?? {};\n delete configs[name];\n window.__debugConfigs = configs;\n };\n }, [properties]);\n}\n"],"mappings":"AAAA,SAASA,SAAS,QAAQ,OAAO;AAEjC,SAASC,QAAQ;AAQjB,OAAO,SAASC,cAAcA,CAACC,IAAY,EAAEC,UAAsB,EAAE;EACjEJ,SAAS,CAAC,MAAM;IACZ,IAAIK,OAAO,CAACC,GAAG,CAACC,QAAQ,KAAK,aAAa,EAAE;MACxC;IACJ;IAEA,MAAMC,OAAO,GAAGC,MAAM,CAACC,cAAc,IAAI,CAAC,CAAC;IAC3CF,OAAO,CAACL,IAAI,CAAC,GAAG,MAAMQ,OAAO,CAACC,GAAG,CAACX,QAAQ,CAACG,UAAU,CAAC,CAAC;IACvDK,MAAM,CAACC,cAAc,GAAGF,OAAO;IAE/B,OAAO,MAAM;MACT,MAAMA,OAAO,GAAGC,MAAM,CAACC,cAAc,IAAI,CAAC,CAAC;MAC3C,OAAOF,OAAO,CAACL,IAAI,CAAC;MACpBM,MAAM,CAACC,cAAc,GAAGF,OAAO;IACnC,CAAC;EACL,CAAC,EAAE,CAACJ,UAAU,CAAC,CAAC;AACpB","ignoreList":[]}
1
+ {"version":3,"names":["useEffect","toObject","getHook","window","__WEBINY_DEVTOOLS_HOOK__","revision","configs","sections","useDebugConfig","name","properties","process","env","NODE_ENV","__debugConfigs","console","log","hook","map","p","id","parent","value","array","config","updatedAt","Date","now"],"sources":["useDebugConfig.ts"],"sourcesContent":["import { useEffect } from \"react\";\nimport type { Property } from \"./Properties.js\";\nimport { toObject } from \"./utils.js\";\n\ninterface WebinyDevtoolsConfig {\n properties: Array<{\n id: string;\n parent: string;\n name: string;\n value?: unknown;\n array?: boolean;\n }>;\n config: unknown;\n updatedAt: number;\n}\n\ninterface WebinyDevtoolsSection {\n data: unknown;\n group: string;\n views: string[];\n updatedAt: number;\n}\n\ninterface WebinyDevtoolsHook {\n revision: number;\n configs: Record<string, WebinyDevtoolsConfig>;\n sections: Record<string, WebinyDevtoolsSection>;\n}\n\ndeclare global {\n interface Window {\n __debugConfigs: Record<string, () => void>;\n __WEBINY_DEVTOOLS_HOOK__?: WebinyDevtoolsHook;\n }\n}\n\nexport function getHook(): WebinyDevtoolsHook {\n if (!window.__WEBINY_DEVTOOLS_HOOK__) {\n window.__WEBINY_DEVTOOLS_HOOK__ = { revision: 0, configs: {}, sections: {} };\n }\n return window.__WEBINY_DEVTOOLS_HOOK__;\n}\n\nexport function useDebugConfig(name: string, properties: Property[]) {\n useEffect(() => {\n if (process.env.NODE_ENV !== \"development\") {\n return;\n }\n\n // Legacy console.log support\n const configs = window.__debugConfigs ?? {};\n configs[name] = () => console.log(toObject(properties));\n window.__debugConfigs = configs;\n\n // DevTools hook: structured data for the Chrome extension\n const hook = getHook();\n hook.configs[name] = {\n properties: properties.map(p => ({\n id: p.id,\n parent: p.parent,\n name: p.name,\n value: p.value,\n array: p.array\n })),\n config: toObject(properties),\n updatedAt: Date.now()\n };\n hook.revision++;\n\n return () => {\n // Legacy cleanup\n const configs = window.__debugConfigs ?? {};\n delete configs[name];\n window.__debugConfigs = configs;\n\n // DevTools hook cleanup\n if (window.__WEBINY_DEVTOOLS_HOOK__) {\n delete window.__WEBINY_DEVTOOLS_HOOK__.configs[name];\n window.__WEBINY_DEVTOOLS_HOOK__.revision++;\n }\n };\n }, [properties]);\n}\n"],"mappings":"AAAA,SAASA,SAAS,QAAQ,OAAO;AAEjC,SAASC,QAAQ;AAkCjB,OAAO,SAASC,OAAOA,CAAA,EAAuB;EAC1C,IAAI,CAACC,MAAM,CAACC,wBAAwB,EAAE;IAClCD,MAAM,CAACC,wBAAwB,GAAG;MAAEC,QAAQ,EAAE,CAAC;MAAEC,OAAO,EAAE,CAAC,CAAC;MAAEC,QAAQ,EAAE,CAAC;IAAE,CAAC;EAChF;EACA,OAAOJ,MAAM,CAACC,wBAAwB;AAC1C;AAEA,OAAO,SAASI,cAAcA,CAACC,IAAY,EAAEC,UAAsB,EAAE;EACjEV,SAAS,CAAC,MAAM;IACZ,IAAIW,OAAO,CAACC,GAAG,CAACC,QAAQ,KAAK,aAAa,EAAE;MACxC;IACJ;;IAEA;IACA,MAAMP,OAAO,GAAGH,MAAM,CAACW,cAAc,IAAI,CAAC,CAAC;IAC3CR,OAAO,CAACG,IAAI,CAAC,GAAG,MAAMM,OAAO,CAACC,GAAG,CAACf,QAAQ,CAACS,UAAU,CAAC,CAAC;IACvDP,MAAM,CAACW,cAAc,GAAGR,OAAO;;IAE/B;IACA,MAAMW,IAAI,GAAGf,OAAO,CAAC,CAAC;IACtBe,IAAI,CAACX,OAAO,CAACG,IAAI,CAAC,GAAG;MACjBC,UAAU,EAAEA,UAAU,CAACQ,GAAG,CAACC,CAAC,KAAK;QAC7BC,EAAE,EAAED,CAAC,CAACC,EAAE;QACRC,MAAM,EAAEF,CAAC,CAACE,MAAM;QAChBZ,IAAI,EAAEU,CAAC,CAACV,IAAI;QACZa,KAAK,EAAEH,CAAC,CAACG,KAAK;QACdC,KAAK,EAAEJ,CAAC,CAACI;MACb,CAAC,CAAC,CAAC;MACHC,MAAM,EAAEvB,QAAQ,CAACS,UAAU,CAAC;MAC5Be,SAAS,EAAEC,IAAI,CAACC,GAAG,CAAC;IACxB,CAAC;IACDV,IAAI,CAACZ,QAAQ,EAAE;IAEf,OAAO,MAAM;MACT;MACA,MAAMC,OAAO,GAAGH,MAAM,CAACW,cAAc,IAAI,CAAC,CAAC;MAC3C,OAAOR,OAAO,CAACG,IAAI,CAAC;MACpBN,MAAM,CAACW,cAAc,GAAGR,OAAO;;MAE/B;MACA,IAAIH,MAAM,CAACC,wBAAwB,EAAE;QACjC,OAAOD,MAAM,CAACC,wBAAwB,CAACE,OAAO,CAACG,IAAI,CAAC;QACpDN,MAAM,CAACC,wBAAwB,CAACC,QAAQ,EAAE;MAC9C;IACJ,CAAC;EACL,CAAC,EAAE,CAACK,UAAU,CAAC,CAAC;AACpB","ignoreList":[]}