@genesislcap/foundation-react-utils 15.78.2 → 15.79.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.
package/README.md CHANGED
@@ -102,6 +102,68 @@ and dark mode. To override them, set `--eb-*` custom properties (`--eb-bg`, `--e
102
102
  on an ancestor. Without a design system it uses its own light palette. The last 20 reports are also kept
103
103
  on `window.__GENESIS_ERROR_BOUNDARY_REPORTS__` for dev tooling such as the Genesis Create preview.
104
104
 
105
+ ## foundation-workspace
106
+
107
+ Workspaces (save/load of layouts, grid column state and filters) work in React through the element
108
+ wrappers `@genesislcap/foundation-workspace/react` already ships, plus one hook for filter state.
109
+ `@genesislcap/foundation-workspace` is an optional peer dependency: add it to your app's own
110
+ dependencies to use `@genesislcap/foundation-react-utils/workspace`.
111
+
112
+
113
+ ```tsx
114
+ import { registerFactory } from '@genesislcap/foundation-layout';
115
+ import { reactFactory } from '@genesislcap/foundation-react-utils';
116
+ import { useWorkspaceFilter } from '@genesislcap/foundation-react-utils/workspace';
117
+ import { GridRegister, LayoutWrapper, WorkspaceManager } from '@genesislcap/foundation-workspace/react';
118
+
119
+ const PositionsPanel = () => {
120
+ const [search, setSearch] = useState('');
121
+ useWorkspaceFilter('positions-search', search, setSearch);
122
+ return (
123
+ <GridRegister gridKey="positions">
124
+ <RapidGridPro persistColumnStateKey="positions">…</RapidGridPro>
125
+ </GridRegister>
126
+ );
127
+ };
128
+
129
+ registerFactory('positions', reactFactory(PositionsPanel));
130
+
131
+ <LayoutWrapper layoutKey="my-layout">
132
+ <rapid-layout auto-save-key="my-layout">
133
+ <rapid-layout-region>
134
+ <rapid-layout-item registration="positions" title="Positions" />
135
+ </rapid-layout-region>
136
+ </rapid-layout>
137
+ </LayoutWrapper>
138
+
139
+ <WorkspaceManager />
140
+ ```
141
+
142
+ - Register `workspaceComponents` from `@genesislcap/foundation-workspace` with your design system.
143
+ - Render layout panes through `registerFactory` + `reactFactory`, not as JSX children of
144
+ `rapid-layout-item`. Loading a saved layout rebuilds the panes, and cloned DOM loses React-set
145
+ properties such as `gridOptions`.
146
+ - `useWorkspaceFilter` also applies the current workspace's saved value on mount, so a filter
147
+ inside a pane that is recreated by a layout load still picks up its saved value.
148
+
149
+ ### flexlayout-react layouts
150
+
151
+ `LayoutWrapper` only covers `rapid-layout`. For a flexlayout-react layout, hold the `Model` in
152
+ state and register it with `useWorkspaceFlexLayout`; loading a workspace hands you a new model:
153
+
154
+ ```tsx
155
+ import { Layout, Model } from '@genesislcap/foundation-react-utils/flexlayout';
156
+ import { useWorkspaceFlexLayout } from '@genesislcap/foundation-react-utils/workspace';
157
+
158
+ const [model, setModel] = useState(() => Model.fromJson(defaultLayout));
159
+ useWorkspaceFlexLayout('home-layout', model, setModel);
160
+
161
+ return <Layout model={model} factory={factory} />;
162
+ ```
163
+
164
+ Tabs opened or closed by a loaded layout mount and unmount as usual, so `GridRegister` and
165
+ `useWorkspaceFilter` inside them pick up the workspace's state as they register.
166
+
105
167
  ## License
106
168
 
107
169
  Note: this project provides front-end dependencies and uses licensed components listed in the next section; thus, licenses for those components are required during development. Contact [Genesis Global](https://genesis.global/contact-us/) for more details.
@@ -648,6 +648,127 @@
648
648
  }
649
649
  }
650
650
  ]
651
+ },
652
+ {
653
+ "kind": "javascript-module",
654
+ "path": "src/workspace/index.ts",
655
+ "declarations": [],
656
+ "exports": [
657
+ {
658
+ "kind": "js",
659
+ "name": "useWorkspaceFilter",
660
+ "declaration": {
661
+ "name": "useWorkspaceFilter",
662
+ "module": "./use-workspace-filter"
663
+ }
664
+ },
665
+ {
666
+ "kind": "js",
667
+ "name": "useWorkspaceFlexLayout",
668
+ "declaration": {
669
+ "name": "useWorkspaceFlexLayout",
670
+ "module": "./use-workspace-flex-layout"
671
+ }
672
+ }
673
+ ]
674
+ },
675
+ {
676
+ "kind": "javascript-module",
677
+ "path": "src/workspace/use-workspace-filter.ts",
678
+ "declarations": [
679
+ {
680
+ "kind": "function",
681
+ "name": "useWorkspaceFilter",
682
+ "return": {
683
+ "type": {
684
+ "text": "void"
685
+ }
686
+ },
687
+ "parameters": [
688
+ {
689
+ "name": "filterKey",
690
+ "type": {
691
+ "text": "string"
692
+ },
693
+ "description": "Unique key the value is saved under. An empty key skips registration."
694
+ },
695
+ {
696
+ "name": "value",
697
+ "type": {
698
+ "text": "string"
699
+ },
700
+ "description": "Current filter value."
701
+ },
702
+ {
703
+ "name": "setValue",
704
+ "type": {
705
+ "text": "(value: string) => void"
706
+ },
707
+ "description": "Called with the saved value when a workspace is loaded."
708
+ }
709
+ ],
710
+ "description": "Registers a piece of React filter state with the foundation-workspace `FilterRegistry`, so\n`<workspace-manager>` saves it with the workspace and restores it on load."
711
+ }
712
+ ],
713
+ "exports": [
714
+ {
715
+ "kind": "js",
716
+ "name": "useWorkspaceFilter",
717
+ "declaration": {
718
+ "name": "useWorkspaceFilter",
719
+ "module": "src/workspace/use-workspace-filter.ts"
720
+ }
721
+ }
722
+ ]
723
+ },
724
+ {
725
+ "kind": "javascript-module",
726
+ "path": "src/workspace/use-workspace-flex-layout.ts",
727
+ "declarations": [
728
+ {
729
+ "kind": "function",
730
+ "name": "useWorkspaceFlexLayout",
731
+ "return": {
732
+ "type": {
733
+ "text": "void"
734
+ }
735
+ },
736
+ "parameters": [
737
+ {
738
+ "name": "layoutKey",
739
+ "type": {
740
+ "text": "string"
741
+ },
742
+ "description": "Unique key the layout is saved under. An empty key skips registration."
743
+ },
744
+ {
745
+ "name": "model",
746
+ "type": {
747
+ "text": "Model"
748
+ },
749
+ "description": "The model currently rendered by `<Layout>`."
750
+ },
751
+ {
752
+ "name": "setModel",
753
+ "type": {
754
+ "text": "(model: Model) => void"
755
+ },
756
+ "description": "Called with a new model when a workspace is loaded."
757
+ }
758
+ ],
759
+ "description": "Registers a flexlayout-react `Model` with the foundation-workspace `LayoutRegistry`, so\n`<workspace-manager>` saves the layout (which tabs are open, how they're split and sized, which\nis selected) with the workspace and restores it on load."
760
+ }
761
+ ],
762
+ "exports": [
763
+ {
764
+ "kind": "js",
765
+ "name": "useWorkspaceFlexLayout",
766
+ "declaration": {
767
+ "name": "useWorkspaceFlexLayout",
768
+ "module": "src/workspace/use-workspace-flex-layout.ts"
769
+ }
770
+ }
771
+ ]
651
772
  }
652
773
  ]
653
774
  }
@@ -20,6 +20,7 @@
20
20
  * Rapid Design System theme for it installed on import. Import from there rather than adding
21
21
  * `flexlayout-react` to an app's own dependencies.
22
22
  * - `./router` — route table helpers (`renderAppRoutes` and friends).
23
+ * - `./workspace` — foundation-workspace helpers (`useWorkspaceFilter`, `useWorkspaceFlexLayout`).
23
24
  */
24
25
  export { reactFactory, reactFactoryWithProvider } from './react-layout-factory';
25
26
  export { createReactRenderer } from './create-react-renderer';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EAAE,YAAY,EAAE,wBAAwB,EAAE,MAAM,wBAAwB,CAAC;AAChF,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAC9D,OAAO,EAAE,yBAAyB,EAAE,MAAM,iCAAiC,CAAC;AAC5E,OAAO,EAAE,wBAAwB,EAAE,MAAM,gCAAgC,CAAC;AAC1E,YAAY,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AACzE,YAAY,EACV,0BAA0B,EAC1B,wBAAwB,EACxB,wBAAwB,GACzB,MAAM,iCAAiC,CAAC;AACzC,cAAc,kBAAkB,CAAC;AACjC,YAAY,EAAE,oBAAoB,EAAE,MAAM,+BAA+B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EAAE,YAAY,EAAE,wBAAwB,EAAE,MAAM,wBAAwB,CAAC;AAChF,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAC9D,OAAO,EAAE,yBAAyB,EAAE,MAAM,iCAAiC,CAAC;AAC5E,OAAO,EAAE,wBAAwB,EAAE,MAAM,gCAAgC,CAAC;AAC1E,YAAY,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AACzE,YAAY,EACV,0BAA0B,EAC1B,wBAAwB,EACxB,wBAAwB,GACzB,MAAM,iCAAiC,CAAC;AACzC,cAAc,kBAAkB,CAAC;AACjC,YAAY,EAAE,oBAAoB,EAAE,MAAM,+BAA+B,CAAC"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * `@genesislcap/foundation-react-utils/workspace`
3
+ *
4
+ * React helpers for `@genesislcap/foundation-workspace` (save/load of layouts, grid column state
5
+ * and filters).
6
+ *
7
+ * Exposed as a subpath (`/workspace`) rather than from the package root so that consumers which
8
+ * don't use workspaces don't load foundation-workspace (and the grid-pro graph behind it).
9
+ *
10
+ * Grids, `rapid-layout` and the save/load UI need no hook: use the element wrappers from
11
+ * `@genesislcap/foundation-workspace/react` (`GridRegister`, `LayoutWrapper`, `WorkspaceManager`).
12
+ * Inside a `rapid-layout`, render panes through `registerFactory` + `reactFactory` rather than as
13
+ * JSX children, because loading a saved layout rebuilds panes and cloned DOM loses React state.
14
+ *
15
+ * Key exports:
16
+ * - `useWorkspaceFilter` — register React filter state so workspaces save and restore it.
17
+ * - `useWorkspaceFlexLayout` — register a flexlayout-react `Model` so workspaces save and restore
18
+ * the layout.
19
+ */
20
+ export { useWorkspaceFilter } from './use-workspace-filter';
21
+ export { useWorkspaceFlexLayout } from './use-workspace-flex-layout';
22
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/workspace/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Registers a piece of React filter state with the foundation-workspace `FilterRegistry`, so
3
+ * `<workspace-manager>` saves it with the workspace and restores it on load.
4
+ *
5
+ * @remarks
6
+ * Grids and layouts are covered by the `GridRegister` / `LayoutWrapper` elements; filters have no
7
+ * element wrapper because their value lives in app state, so this hook is the React equivalent.
8
+ *
9
+ * - Registers on mount and unregisters on unmount (or when `filterKey` changes).
10
+ * - The registry always reads the latest `value` and calls the latest `setValue`, so neither needs
11
+ * to be memoised.
12
+ * - On mount, the registry applies its stored value for `filterKey` (if any): the loaded
13
+ * workspace's value, or this filter's last value from earlier in the session. This covers a
14
+ * filter that wasn't mounted when the workspace loaded (e.g. on another route), and a filter
15
+ * inside a layout pane that a layout reload remounts.
16
+ * - On unmount, the registry keeps the last value, so saving a workspace while the filter isn't
17
+ * mounted still includes it.
18
+ *
19
+ * @param filterKey - Unique key the value is saved under. An empty key skips registration.
20
+ * @param value - Current filter value.
21
+ * @param setValue - Called with the saved value when a workspace is loaded.
22
+ *
23
+ * @example
24
+ * ```tsx
25
+ * const [instrument, setInstrument] = useState('');
26
+ * useWorkspaceFilter('positions-instrument', instrument, setInstrument);
27
+ * ```
28
+ * @alpha
29
+ */
30
+ export declare function useWorkspaceFilter(filterKey: string, value: string, setValue: (value: string) => void): void;
31
+ //# sourceMappingURL=use-workspace-filter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-workspace-filter.d.ts","sourceRoot":"","sources":["../../../src/workspace/use-workspace-filter.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,kBAAkB,CAChC,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,GAChC,IAAI,CAwBN"}
@@ -0,0 +1,34 @@
1
+ import { Model } from 'flexlayout-react';
2
+ /**
3
+ * Registers a flexlayout-react `Model` with the foundation-workspace `LayoutRegistry`, so
4
+ * `<workspace-manager>` saves the layout (which tabs are open, how they're split and sized, which
5
+ * is selected) with the workspace and restores it on load.
6
+ *
7
+ * @remarks
8
+ * The React counterpart of `LayoutWrapper` (`layout-register`), which only covers `rapid-layout`.
9
+ * A workspace stores the model's `toJson()`; loading one builds a new model with `Model.fromJson`
10
+ * and hands it to `setModel`, so the app renders `<Layout model={model} />` from state.
11
+ *
12
+ * - Registers on mount and unregisters on unmount (or when `layoutKey` changes). Registering
13
+ * applies the layout's stored config straight away, when a workspace loaded earlier has one.
14
+ * - The registry always reads the latest `model` and calls the latest `setModel`, so neither needs
15
+ * to be memoised.
16
+ * - Tabs that a loaded layout closes unmount, and ones it opens mount: their grids and filters
17
+ * (`GridRegister`, `useWorkspaceFilter`) pick up the workspace's state as they register.
18
+ * - A saved config that flexlayout-react can't read is logged and ignored; the current layout
19
+ * stays.
20
+ *
21
+ * @param layoutKey - Unique key the layout is saved under. An empty key skips registration.
22
+ * @param model - The model currently rendered by `<Layout>`.
23
+ * @param setModel - Called with a new model when a workspace is loaded.
24
+ *
25
+ * @example
26
+ * ```tsx
27
+ * const [model, setModel] = useState(() => Model.fromJson(defaultLayout));
28
+ * useWorkspaceFlexLayout('home-layout', model, setModel);
29
+ * return <Layout model={model} factory={factory} />;
30
+ * ```
31
+ * @alpha
32
+ */
33
+ export declare function useWorkspaceFlexLayout(layoutKey: string, model: Model, setModel: (model: Model) => void): void;
34
+ //# sourceMappingURL=use-workspace-flex-layout.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-workspace-flex-layout.d.ts","sourceRoot":"","sources":["../../../src/workspace/use-workspace-flex-layout.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,EAAmB,MAAM,kBAAkB,CAAC;AAG1D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,wBAAgB,sBAAsB,CACpC,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,KAAK,EACZ,QAAQ,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,GAC/B,IAAI,CAqCN"}
package/dist/esm/index.js CHANGED
@@ -20,6 +20,7 @@
20
20
  * Rapid Design System theme for it installed on import. Import from there rather than adding
21
21
  * `flexlayout-react` to an app's own dependencies.
22
22
  * - `./router` — route table helpers (`renderAppRoutes` and friends).
23
+ * - `./workspace` — foundation-workspace helpers (`useWorkspaceFilter`, `useWorkspaceFlexLayout`).
23
24
  */
24
25
  export { reactFactory, reactFactoryWithProvider } from './react-layout-factory';
25
26
  export { createReactRenderer } from './create-react-renderer';
@@ -0,0 +1,21 @@
1
+ /**
2
+ * `@genesislcap/foundation-react-utils/workspace`
3
+ *
4
+ * React helpers for `@genesislcap/foundation-workspace` (save/load of layouts, grid column state
5
+ * and filters).
6
+ *
7
+ * Exposed as a subpath (`/workspace`) rather than from the package root so that consumers which
8
+ * don't use workspaces don't load foundation-workspace (and the grid-pro graph behind it).
9
+ *
10
+ * Grids, `rapid-layout` and the save/load UI need no hook: use the element wrappers from
11
+ * `@genesislcap/foundation-workspace/react` (`GridRegister`, `LayoutWrapper`, `WorkspaceManager`).
12
+ * Inside a `rapid-layout`, render panes through `registerFactory` + `reactFactory` rather than as
13
+ * JSX children, because loading a saved layout rebuilds panes and cloned DOM loses React state.
14
+ *
15
+ * Key exports:
16
+ * - `useWorkspaceFilter` — register React filter state so workspaces save and restore it.
17
+ * - `useWorkspaceFlexLayout` — register a flexlayout-react `Model` so workspaces save and restore
18
+ * the layout.
19
+ */
20
+ export { useWorkspaceFilter } from './use-workspace-filter';
21
+ export { useWorkspaceFlexLayout } from './use-workspace-flex-layout';
@@ -0,0 +1,48 @@
1
+ import { FilterRegistry } from '@genesislcap/foundation-workspace';
2
+ import { DI } from '@genesislcap/web-core';
3
+ import { useEffect, useLayoutEffect, useRef } from 'react';
4
+ /**
5
+ * Registers a piece of React filter state with the foundation-workspace `FilterRegistry`, so
6
+ * `<workspace-manager>` saves it with the workspace and restores it on load.
7
+ *
8
+ * @remarks
9
+ * Grids and layouts are covered by the `GridRegister` / `LayoutWrapper` elements; filters have no
10
+ * element wrapper because their value lives in app state, so this hook is the React equivalent.
11
+ *
12
+ * - Registers on mount and unregisters on unmount (or when `filterKey` changes).
13
+ * - The registry always reads the latest `value` and calls the latest `setValue`, so neither needs
14
+ * to be memoised.
15
+ * - On mount, the registry applies its stored value for `filterKey` (if any): the loaded
16
+ * workspace's value, or this filter's last value from earlier in the session. This covers a
17
+ * filter that wasn't mounted when the workspace loaded (e.g. on another route), and a filter
18
+ * inside a layout pane that a layout reload remounts.
19
+ * - On unmount, the registry keeps the last value, so saving a workspace while the filter isn't
20
+ * mounted still includes it.
21
+ *
22
+ * @param filterKey - Unique key the value is saved under. An empty key skips registration.
23
+ * @param value - Current filter value.
24
+ * @param setValue - Called with the saved value when a workspace is loaded.
25
+ *
26
+ * @example
27
+ * ```tsx
28
+ * const [instrument, setInstrument] = useState('');
29
+ * useWorkspaceFilter('positions-instrument', instrument, setInstrument);
30
+ * ```
31
+ * @alpha
32
+ */
33
+ export function useWorkspaceFilter(filterKey, value, setValue) {
34
+ const latest = useRef({ value, setValue });
35
+ useLayoutEffect(() => {
36
+ latest.current = { value, setValue };
37
+ });
38
+ useEffect(() => {
39
+ if (!filterKey) {
40
+ return undefined;
41
+ }
42
+ const filterRegistry = DI.getOrCreateDOMContainer().get(FilterRegistry);
43
+ filterRegistry.registerFilter(filterKey, () => ({ filterKey, value: latest.current.value }), (savedValue) => latest.current.setValue(savedValue));
44
+ return () => {
45
+ filterRegistry.unregisterFilter(filterKey);
46
+ };
47
+ }, [filterKey]);
48
+ }
@@ -0,0 +1,87 @@
1
+ import { LayoutRegistry } from '@genesislcap/foundation-workspace';
2
+ import { DI } from '@genesislcap/web-core';
3
+ import { Model } from 'flexlayout-react';
4
+ import { useEffect, useLayoutEffect, useRef } from 'react';
5
+ /**
6
+ * Registers a flexlayout-react `Model` with the foundation-workspace `LayoutRegistry`, so
7
+ * `<workspace-manager>` saves the layout (which tabs are open, how they're split and sized, which
8
+ * is selected) with the workspace and restores it on load.
9
+ *
10
+ * @remarks
11
+ * The React counterpart of `LayoutWrapper` (`layout-register`), which only covers `rapid-layout`.
12
+ * A workspace stores the model's `toJson()`; loading one builds a new model with `Model.fromJson`
13
+ * and hands it to `setModel`, so the app renders `<Layout model={model} />` from state.
14
+ *
15
+ * - Registers on mount and unregisters on unmount (or when `layoutKey` changes). Registering
16
+ * applies the layout's stored config straight away, when a workspace loaded earlier has one.
17
+ * - The registry always reads the latest `model` and calls the latest `setModel`, so neither needs
18
+ * to be memoised.
19
+ * - Tabs that a loaded layout closes unmount, and ones it opens mount: their grids and filters
20
+ * (`GridRegister`, `useWorkspaceFilter`) pick up the workspace's state as they register.
21
+ * - A saved config that flexlayout-react can't read is logged and ignored; the current layout
22
+ * stays.
23
+ *
24
+ * @param layoutKey - Unique key the layout is saved under. An empty key skips registration.
25
+ * @param model - The model currently rendered by `<Layout>`.
26
+ * @param setModel - Called with a new model when a workspace is loaded.
27
+ *
28
+ * @example
29
+ * ```tsx
30
+ * const [model, setModel] = useState(() => Model.fromJson(defaultLayout));
31
+ * useWorkspaceFlexLayout('home-layout', model, setModel);
32
+ * return <Layout model={model} factory={factory} />;
33
+ * ```
34
+ * @alpha
35
+ */
36
+ export function useWorkspaceFlexLayout(layoutKey, model, setModel) {
37
+ const latest = useRef({ model, setModel });
38
+ useLayoutEffect(() => {
39
+ latest.current = { model, setModel };
40
+ });
41
+ useEffect(() => {
42
+ if (!layoutKey) {
43
+ return undefined;
44
+ }
45
+ const layoutRegistry = DI.getOrCreateDOMContainer().get(LayoutRegistry);
46
+ const layout = {
47
+ // A flexlayout Model is complete as soon as it exists; there's no first load to wait for.
48
+ hasFirstLoaded: true,
49
+ getLayout: () => latest.current.model.toJson(),
50
+ loadLayout: (saved) => {
51
+ const next = readModel(saved);
52
+ if (next) {
53
+ // Keep `latest` in step with what React is asked to render: a cleanup that runs before
54
+ // the re-render (StrictMode's mount, unmount, mount) must store this model, not the old.
55
+ latest.current.model = next;
56
+ latest.current.setModel(next);
57
+ }
58
+ else {
59
+ console.warn(`useWorkspaceFlexLayout: ignoring unreadable layout for ${layoutKey}`);
60
+ }
61
+ },
62
+ addEventListener: () => { },
63
+ };
64
+ layoutRegistry.registerLayout(layoutKey, layout);
65
+ return () => {
66
+ layoutRegistry.unregisterLayout(layoutKey);
67
+ };
68
+ }, [layoutKey]);
69
+ }
70
+ /**
71
+ * Builds a model from a saved flexlayout config, or returns null for anything else — e.g. a
72
+ * `rapid-layout` config saved under the same key. `Model.fromJson` doesn't validate: it builds a
73
+ * broken model from malformed JSON rather than throwing.
74
+ */
75
+ function readModel(saved) {
76
+ const json = saved;
77
+ const root = json === null || json === void 0 ? void 0 : json.layout;
78
+ if (!root || typeof root !== 'object' || typeof root.type !== 'string') {
79
+ return null;
80
+ }
81
+ try {
82
+ return Model.fromJson(json);
83
+ }
84
+ catch (_a) {
85
+ return null;
86
+ }
87
+ }
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.78.2",
4
+ "version": "15.79.1",
5
5
  "sideEffects": [
6
6
  "./dist/esm/flexlayout/index.js"
7
7
  ],
@@ -21,6 +21,10 @@
21
21
  "types": "./dist/dts/flexlayout/index.d.ts",
22
22
  "default": "./dist/esm/flexlayout/index.js"
23
23
  },
24
+ "./workspace": {
25
+ "types": "./dist/dts/workspace/index.d.ts",
26
+ "default": "./dist/esm/workspace/index.js"
27
+ },
24
28
  "./package.json": "./package.json"
25
29
  },
26
30
  "typesVersions": {
@@ -30,6 +34,9 @@
30
34
  ],
31
35
  "flexlayout": [
32
36
  "./dist/dts/flexlayout/index.d.ts"
37
+ ],
38
+ "workspace": [
39
+ "./dist/dts/workspace/index.d.ts"
33
40
  ]
34
41
  }
35
42
  },
@@ -56,30 +63,35 @@
56
63
  }
57
64
  },
58
65
  "devDependencies": {
59
- "@genesislcap/foundation-testing": "15.78.2",
60
- "@genesislcap/genx": "15.78.2",
61
- "@genesislcap/ts-builder": "15.78.2",
62
- "@genesislcap/uvu-playwright-builder": "15.78.2",
63
- "@genesislcap/vite-builder": "15.78.2",
64
- "@genesislcap/webpack-builder": "15.78.2",
66
+ "@genesislcap/foundation-testing": "15.79.1",
67
+ "@genesislcap/foundation-workspace": "15.79.1",
68
+ "@genesislcap/genx": "15.79.1",
69
+ "@genesislcap/ts-builder": "15.79.1",
70
+ "@genesislcap/uvu-playwright-builder": "15.79.1",
71
+ "@genesislcap/vite-builder": "15.79.1",
72
+ "@genesislcap/webpack-builder": "15.79.1",
65
73
  "react-router-dom": "^7.1.3"
66
74
  },
67
75
  "peerDependencies": {
76
+ "@genesislcap/foundation-workspace": "^15.79.1",
68
77
  "react": "^19.0.0",
69
78
  "react-dom": "^19.0.0",
70
79
  "react-router-dom": "^7.0.0"
71
80
  },
72
81
  "peerDependenciesMeta": {
82
+ "@genesislcap/foundation-workspace": {
83
+ "optional": true
84
+ },
73
85
  "react-router-dom": {
74
86
  "optional": true
75
87
  }
76
88
  },
77
89
  "dependencies": {
78
- "@genesislcap/foundation-forms": "15.78.2",
79
- "@genesislcap/foundation-layout": "15.78.2",
80
- "@genesislcap/foundation-logger": "15.78.2",
81
- "@genesislcap/rapid-design-system": "15.78.2",
82
- "@genesislcap/web-core": "15.78.2",
90
+ "@genesislcap/foundation-forms": "15.79.1",
91
+ "@genesislcap/foundation-layout": "15.79.1",
92
+ "@genesislcap/foundation-logger": "15.79.1",
93
+ "@genesislcap/rapid-design-system": "15.79.1",
94
+ "@genesislcap/web-core": "15.79.1",
83
95
  "@jsonforms/core": "^3.2.1",
84
96
  "@r2wc/core": "1.3.1",
85
97
  "@r2wc/react-to-web-component": "^2.0.2",