@genesislcap/foundation-react-utils 15.31.0 → 15.32.1

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.
@@ -47,6 +47,18 @@ import type { ComponentType } from 'react';
47
47
  * // form.additionalRenderers = [priceRendererEntry];
48
48
  * ```
49
49
  *
50
+ * @remarks
51
+ * `uischema.options` may carry function values (e.g. AG-Grid `valueFormatter`, `comparator`,
52
+ * `cellRenderer`, however deeply nested — inside `columnDefs`, for instance) and they will
53
+ * reach your component intact, whether `uischema` is assigned before or after the element is
54
+ * connected to the DOM.
55
+ *
56
+ * The `props.uischema` your component receives is the *same object instance* JSON Forms holds
57
+ * internally, not a copy — treat it as read-only. Mutating it (e.g.
58
+ * `props.uischema.options.columnDefs.forEach(c => { c.width = measured })`) corrupts state shared
59
+ * with every sibling control and with subsequent reducer runs, since there is no serialization
60
+ * step to isolate your copy from the source.
61
+ *
50
62
  * @public
51
63
  */
52
64
  export declare function createReactRenderer(Component: ComponentType<RendererControlProps>, options: {
@@ -1 +1 @@
1
- {"version":3,"file":"create-react-renderer.d.ts","sourceRoot":"","sources":["../../src/create-react-renderer.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,oBAAoB,EACzB,KAAK,aAAa,EACnB,MAAM,+BAA+B,CAAC;AAEvC,OAAO,EAEL,KAAK,cAAc,EACnB,KAAK,iBAAiB,EACtB,KAAK,YAAY,EACjB,KAAK,mBAAmB,EACzB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAE3C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AACH,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,aAAa,CAAC,oBAAoB,CAAC,EAC9C,OAAO,EAAE;IACP;;;OAGG;IACH,IAAI,EAAE,MAAM,CAAC;IACb;;;OAGG;IACH,MAAM,EAAE,YAAY,CAAC;IACrB;;;OAGG;IACH,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,cAAc,EAAE,QAAQ,EAAE,iBAAiB,KAAK,mBAAmB,CAAC;IACrF;;;;;;;;;;OAUG;IACH,sBAAsB,CAAC,EAAE,OAAO,CAAC;CAClC,GACA,aAAa,CAoDf"}
1
+ {"version":3,"file":"create-react-renderer.d.ts","sourceRoot":"","sources":["../../src/create-react-renderer.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,oBAAoB,EACzB,KAAK,aAAa,EACnB,MAAM,+BAA+B,CAAC;AAGvC,OAAO,EAEL,KAAK,cAAc,EACnB,KAAK,iBAAiB,EACtB,KAAK,YAAY,EACjB,KAAK,mBAAmB,EACzB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAI3C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2DG;AACH,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,aAAa,CAAC,oBAAoB,CAAC,EAC9C,OAAO,EAAE;IACP;;;OAGG;IACH,IAAI,EAAE,MAAM,CAAC;IACb;;;OAGG;IACH,MAAM,EAAE,YAAY,CAAC;IACrB;;;OAGG;IACH,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,cAAc,EAAE,QAAQ,EAAE,iBAAiB,KAAK,mBAAmB,CAAC;IACrF;;;;;;;;;;OAUG;IACH,sBAAsB,CAAC,EAAE,OAAO,CAAC;CAClC,GACA,aAAa,CAoFf"}
@@ -1,7 +1,9 @@
1
1
  import { controlWrapperTemplate, } from '@genesislcap/foundation-forms';
2
+ import { createLogger } from '@genesislcap/foundation-logger';
2
3
  import { html } from '@genesislcap/web-core';
3
4
  import { mapStateToControlProps, } from '@jsonforms/core';
4
5
  import r2wc from '@r2wc/react-to-web-component';
6
+ const logger = createLogger('foundation-react-utils');
5
7
  /**
6
8
  * Converts a React component into a `RendererEntry` for use with `foundation-form`'s
7
9
  * `additionalRenderers` property.
@@ -48,6 +50,18 @@ import r2wc from '@r2wc/react-to-web-component';
48
50
  * // form.additionalRenderers = [priceRendererEntry];
49
51
  * ```
50
52
  *
53
+ * @remarks
54
+ * `uischema.options` may carry function values (e.g. AG-Grid `valueFormatter`, `comparator`,
55
+ * `cellRenderer`, however deeply nested — inside `columnDefs`, for instance) and they will
56
+ * reach your component intact, whether `uischema` is assigned before or after the element is
57
+ * connected to the DOM.
58
+ *
59
+ * The `props.uischema` your component receives is the *same object instance* JSON Forms holds
60
+ * internally, not a copy — treat it as read-only. Mutating it (e.g.
61
+ * `props.uischema.options.columnDefs.forEach(c => { c.width = measured })`) corrupts state shared
62
+ * with every sibling control and with subsequent reducer runs, since there is no serialization
63
+ * step to isolate your copy from the source.
64
+ *
51
65
  * @public
52
66
  */
53
67
  export function createReactRenderer(Component, options) {
@@ -61,7 +75,38 @@ export function createReactRenderer(Component, options) {
61
75
  enabled: 'boolean',
62
76
  required: 'boolean',
63
77
  handleChange: 'method',
64
- uischema: 'json',
78
+ // 'uischema' is intentionally NOT declared here — see the property definition below.
79
+ },
80
+ });
81
+ // r2wc's 'json' prop type round-trips every assignment through
82
+ // JSON.stringify -> setAttribute -> JSON.parse, which silently drops any function-valued
83
+ // uischema.options properties (AG-Grid valueFormatter/comparator/cellRenderer/etc, however
84
+ // deeply nested, e.g. inside columnDefs). uischema is only ever assigned programmatically as a
85
+ // JS object — never authored as an HTML attribute — so attribute reflection provides no
86
+ // benefit here and is actively lossy. Bypass r2wc's declarative round trip and read/write its
87
+ // internal props store directly via its own globally-registered symbols. These are
88
+ // Symbol.for(...) (registry-based, not private Symbol()) so they're stable across realms, but
89
+ // this is still coupled to @r2wc/core's current internal implementation.
90
+ const R2WC_PROPS = Symbol.for('r2wc.props');
91
+ const R2WC_RENDER = Symbol.for('r2wc.render');
92
+ Object.defineProperty(WebComponent.prototype, 'uischema', {
93
+ configurable: true,
94
+ enumerable: true,
95
+ get() {
96
+ var _a;
97
+ return (_a = this[R2WC_PROPS]) === null || _a === void 0 ? void 0 : _a.uischema;
98
+ },
99
+ set(value) {
100
+ var _a;
101
+ if (!this[R2WC_PROPS]) {
102
+ logger.error(`[${options.name}] r2wc internal props store (Symbol.for('r2wc.props')) was not ` +
103
+ 'found on this element — uischema was dropped and the wrapped component will not ' +
104
+ 'receive it. This means @r2wc/core no longer matches the internal shape this ' +
105
+ 'workaround depends on; check the resolved @r2wc/core version.');
106
+ return;
107
+ }
108
+ this[R2WC_PROPS].uischema = value;
109
+ (_a = this[R2WC_RENDER]) === null || _a === void 0 ? void 0 : _a.call(this);
65
110
  },
66
111
  });
67
112
  if (!customElements.get(options.name)) {
@@ -307,7 +307,7 @@
307
307
  {
308
308
  "kind": "Function",
309
309
  "canonicalReference": "@genesislcap/foundation-react-utils!createReactRenderer:function(1)",
310
- "docComment": "/**\n * Converts a React component into a `RendererEntry` for use with `foundation-form`'s `additionalRenderers` property.\n *\n * The component is registered as a custom element via `r2wc` and wrapped in a FAST `html` template so it integrates with the JSON Forms dispatch renderer.\n *\n * **Important — use platform web components for inputs, not native HTML elements.** The React component runs inside an `r2wc` custom element inside a FAST template, creating a three-layer shadow DOM boundary. React's synthetic event system does not cross shadow boundaries, so `onChange` on a plain `<input>` will never fire. Use `rapid-number-field`, `rapid-text-field`, etc. and attach listeners via `addEventListener` in a `useEffect` instead.\n *\n * @example\n * ```tsx\n * import { useEffect, useRef } from 'react';\n * import { createReactRenderer, type RendererControlProps } from '@genesislcap/foundation-react-utils';\n * import { isNumberControl, rankWith } from '@jsonforms/core';\n *\n * function PriceRenderer({ data, path, enabled, handleChange }: RendererControlProps) {\n * const ref = useRef<HTMLElement>(null);\n *\n * useEffect(() => {\n * const el = ref.current as any;\n * if (!el) return;\n * const onChange = (e: CustomEvent) => {\n * const val = (e.target as any).value;\n * handleChange(path, val === '' ? undefined : Number(val));\n * };\n * el.addEventListener('change', onChange);\n * return () => el.removeEventListener('change', onChange);\n * }, [path, handleChange]);\n *\n * return <rapid-number-field ref={ref} value={data ?? ''} disabled={!enabled || undefined} />;\n * }\n *\n * export const priceRendererEntry = createReactRenderer(PriceRenderer, {\n * name: 'my-price-renderer',\n * tester: rankWith(6, isNumberControl),\n * });\n *\n * // On the form element:\n * // form.additionalRenderers = [priceRendererEntry];\n * ```\n *\n * @public\n */\n",
310
+ "docComment": "/**\n * Converts a React component into a `RendererEntry` for use with `foundation-form`'s `additionalRenderers` property.\n *\n * The component is registered as a custom element via `r2wc` and wrapped in a FAST `html` template so it integrates with the JSON Forms dispatch renderer.\n *\n * **Important — use platform web components for inputs, not native HTML elements.** The React component runs inside an `r2wc` custom element inside a FAST template, creating a three-layer shadow DOM boundary. React's synthetic event system does not cross shadow boundaries, so `onChange` on a plain `<input>` will never fire. Use `rapid-number-field`, `rapid-text-field`, etc. and attach listeners via `addEventListener` in a `useEffect` instead.\n *\n * @remarks\n *\n * `uischema.options` may carry function values (e.g. AG-Grid `valueFormatter`, `comparator`, `cellRenderer`, however deeply nested — inside `columnDefs`, for instance) and they will reach your component intact, whether `uischema` is assigned before or after the element is connected to the DOM.\n *\n * The `props.uischema` your component receives is the *same object instance* JSON Forms holds internally, not a copy — treat it as read-only. Mutating it (e.g. `props.uischema.options.columnDefs.forEach(c => { c.width = measured })`) corrupts state shared with every sibling control and with subsequent reducer runs, since there is no serialization step to isolate your copy from the source.\n *\n * @example\n * ```tsx\n * import { useEffect, useRef } from 'react';\n * import { createReactRenderer, type RendererControlProps } from '@genesislcap/foundation-react-utils';\n * import { isNumberControl, rankWith } from '@jsonforms/core';\n *\n * function PriceRenderer({ data, path, enabled, handleChange }: RendererControlProps) {\n * const ref = useRef<HTMLElement>(null);\n *\n * useEffect(() => {\n * const el = ref.current as any;\n * if (!el) return;\n * const onChange = (e: CustomEvent) => {\n * const val = (e.target as any).value;\n * handleChange(path, val === '' ? undefined : Number(val));\n * };\n * el.addEventListener('change', onChange);\n * return () => el.removeEventListener('change', onChange);\n * }, [path, handleChange]);\n *\n * return <rapid-number-field ref={ref} value={data ?? ''} disabled={!enabled || undefined} />;\n * }\n *\n * export const priceRendererEntry = createReactRenderer(PriceRenderer, {\n * name: 'my-price-renderer',\n * tester: rankWith(6, isNumberControl),\n * });\n *\n * // On the form element:\n * // form.additionalRenderers = [priceRendererEntry];\n * ```\n *\n * @public\n */\n",
311
311
  "excerptTokens": [
312
312
  {
313
313
  "kind": "Content",
@@ -158,6 +158,18 @@ export declare function createGridProCellRenderer<P extends GridProCellRendererP
158
158
  * // form.additionalRenderers = [priceRendererEntry];
159
159
  * ```
160
160
  *
161
+ * @remarks
162
+ * `uischema.options` may carry function values (e.g. AG-Grid `valueFormatter`, `comparator`,
163
+ * `cellRenderer`, however deeply nested — inside `columnDefs`, for instance) and they will
164
+ * reach your component intact, whether `uischema` is assigned before or after the element is
165
+ * connected to the DOM.
166
+ *
167
+ * The `props.uischema` your component receives is the *same object instance* JSON Forms holds
168
+ * internally, not a copy — treat it as read-only. Mutating it (e.g.
169
+ * `props.uischema.options.columnDefs.forEach(c => { c.width = measured })`) corrupts state shared
170
+ * with every sibling control and with subsequent reducer runs, since there is no serialization
171
+ * step to isolate your copy from the source.
172
+ *
161
173
  * @public
162
174
  */
163
175
  export declare function createReactRenderer(Component: ComponentType<RendererControlProps>, options: {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@genesislcap/foundation-react-utils",
3
3
  "description": "Genesis Foundation React Utils",
4
- "version": "15.31.0",
4
+ "version": "15.32.1",
5
5
  "sideEffects": [
6
6
  "./dist/esm/flexlayout/index.js"
7
7
  ],
@@ -56,13 +56,13 @@
56
56
  }
57
57
  },
58
58
  "devDependencies": {
59
- "@genesislcap/foundation-testing": "15.31.0",
60
- "@genesislcap/genx": "15.31.0",
61
- "@genesislcap/rollup-builder": "15.31.0",
62
- "@genesislcap/ts-builder": "15.31.0",
63
- "@genesislcap/uvu-playwright-builder": "15.31.0",
64
- "@genesislcap/vite-builder": "15.31.0",
65
- "@genesislcap/webpack-builder": "15.31.0",
59
+ "@genesislcap/foundation-testing": "15.32.1",
60
+ "@genesislcap/genx": "15.32.1",
61
+ "@genesislcap/rollup-builder": "15.32.1",
62
+ "@genesislcap/ts-builder": "15.32.1",
63
+ "@genesislcap/uvu-playwright-builder": "15.32.1",
64
+ "@genesislcap/vite-builder": "15.32.1",
65
+ "@genesislcap/webpack-builder": "15.32.1",
66
66
  "react-router-dom": "^7.1.3"
67
67
  },
68
68
  "peerDependencies": {
@@ -76,12 +76,13 @@
76
76
  }
77
77
  },
78
78
  "dependencies": {
79
- "@genesislcap/foundation-forms": "15.31.0",
80
- "@genesislcap/foundation-layout": "15.31.0",
81
- "@genesislcap/foundation-logger": "15.31.0",
82
- "@genesislcap/rapid-design-system": "15.31.0",
83
- "@genesislcap/web-core": "15.31.0",
79
+ "@genesislcap/foundation-forms": "15.32.1",
80
+ "@genesislcap/foundation-layout": "15.32.1",
81
+ "@genesislcap/foundation-logger": "15.32.1",
82
+ "@genesislcap/rapid-design-system": "15.32.1",
83
+ "@genesislcap/web-core": "15.32.1",
84
84
  "@jsonforms/core": "^3.2.1",
85
+ "@r2wc/core": "1.3.1",
85
86
  "@r2wc/react-to-web-component": "^2.0.2",
86
87
  "flexlayout-react": "0.10.8"
87
88
  },