@vuu-ui/vuu-layout 0.5.10 → 0.5.11

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.
Files changed (114) hide show
  1. package/LICENSE +201 -0
  2. package/cjs/index.js +20 -0
  3. package/cjs/index.js.map +7 -0
  4. package/esm/index.js +20 -0
  5. package/esm/index.js.map +7 -0
  6. package/index.css +2 -0
  7. package/index.css.map +7 -0
  8. package/package.json +13 -10
  9. package/src/Component.css +0 -0
  10. package/src/Component.tsx +0 -20
  11. package/src/DraggableLayout.css +0 -18
  12. package/src/DraggableLayout.tsx +0 -26
  13. package/src/__tests__/flexbox-utils.spec.js +0 -90
  14. package/src/chest-of-drawers/Chest.css +0 -36
  15. package/src/chest-of-drawers/Chest.tsx +0 -42
  16. package/src/chest-of-drawers/Drawer.css +0 -159
  17. package/src/chest-of-drawers/Drawer.tsx +0 -118
  18. package/src/chest-of-drawers/index.ts +0 -2
  19. package/src/common-types.ts +0 -9
  20. package/src/debug.ts +0 -16
  21. package/src/drag-drop/BoxModel.ts +0 -551
  22. package/src/drag-drop/DragState.ts +0 -219
  23. package/src/drag-drop/Draggable.ts +0 -282
  24. package/src/drag-drop/DropMenu.css +0 -71
  25. package/src/drag-drop/DropMenu.tsx +0 -61
  26. package/src/drag-drop/DropTarget.ts +0 -393
  27. package/src/drag-drop/DropTargetRenderer.css +0 -40
  28. package/src/drag-drop/DropTargetRenderer.tsx +0 -277
  29. package/src/drag-drop/dragDropTypes.ts +0 -47
  30. package/src/drag-drop/index.ts +0 -5
  31. package/src/editable-label/EditableLabel.css +0 -28
  32. package/src/editable-label/EditableLabel.tsx +0 -99
  33. package/src/editable-label/index.ts +0 -1
  34. package/src/flexbox/Flexbox.css +0 -45
  35. package/src/flexbox/Flexbox.tsx +0 -70
  36. package/src/flexbox/FlexboxLayout.tsx +0 -28
  37. package/src/flexbox/FluidGrid.css +0 -134
  38. package/src/flexbox/FluidGrid.tsx +0 -82
  39. package/src/flexbox/FluidGridLayout.tsx +0 -9
  40. package/src/flexbox/Splitter.css +0 -140
  41. package/src/flexbox/Splitter.tsx +0 -127
  42. package/src/flexbox/flexbox-utils.ts +0 -128
  43. package/src/flexbox/flexboxTypes.ts +0 -68
  44. package/src/flexbox/index.ts +0 -5
  45. package/src/flexbox/useResponsiveSizing.ts +0 -82
  46. package/src/flexbox/useSplitterResizing.ts +0 -270
  47. package/src/index.ts +0 -19
  48. package/src/layout-action.ts +0 -21
  49. package/src/layout-header/ActionButton.tsx +0 -23
  50. package/src/layout-header/Header.css +0 -8
  51. package/src/layout-header/Header.tsx +0 -216
  52. package/src/layout-header/index.ts +0 -1
  53. package/src/layout-provider/LayoutProvider.tsx +0 -161
  54. package/src/layout-provider/LayoutProviderContext.ts +0 -17
  55. package/src/layout-provider/index.ts +0 -3
  56. package/src/layout-provider/useLayoutDragDrop.ts +0 -210
  57. package/src/layout-reducer/flexUtils.ts +0 -276
  58. package/src/layout-reducer/index.ts +0 -5
  59. package/src/layout-reducer/insert-layout-element.ts +0 -365
  60. package/src/layout-reducer/layout-reducer.ts +0 -237
  61. package/src/layout-reducer/layoutTypes.ts +0 -159
  62. package/src/layout-reducer/layoutUtils.ts +0 -288
  63. package/src/layout-reducer/remove-layout-element.ts +0 -226
  64. package/src/layout-reducer/replace-layout-element.ts +0 -113
  65. package/src/layout-reducer/resize-flex-children.ts +0 -55
  66. package/src/layout-reducer/wrap-layout-element.ts +0 -307
  67. package/src/layout-view/View.css +0 -61
  68. package/src/layout-view/View.tsx +0 -143
  69. package/src/layout-view/ViewContext.ts +0 -30
  70. package/src/layout-view/index.ts +0 -5
  71. package/src/layout-view/useView.tsx +0 -104
  72. package/src/layout-view/useViewActionDispatcher.ts +0 -123
  73. package/src/layout-view/useViewResize.ts +0 -53
  74. package/src/layout-view/viewTypes.ts +0 -35
  75. package/src/palette/Palette.css +0 -33
  76. package/src/palette/Palette.tsx +0 -140
  77. package/src/palette/PaletteSalt.css +0 -9
  78. package/src/palette/PaletteSalt.tsx +0 -79
  79. package/src/palette/index.ts +0 -3
  80. package/src/placeholder/Placeholder.css +0 -10
  81. package/src/placeholder/Placeholder.tsx +0 -38
  82. package/src/placeholder/index.ts +0 -1
  83. package/src/registry/ComponentRegistry.ts +0 -44
  84. package/src/registry/index.ts +0 -1
  85. package/src/responsive/breakpoints.ts +0 -62
  86. package/src/responsive/index.ts +0 -3
  87. package/src/responsive/measureMinimumNodeSize.ts +0 -23
  88. package/src/responsive/overflowUtils.js +0 -14
  89. package/src/responsive/use-breakpoints.ts +0 -101
  90. package/src/responsive/useResizeObserver.ts +0 -154
  91. package/src/responsive/utils.ts +0 -37
  92. package/src/stack/Stack.css +0 -39
  93. package/src/stack/Stack.tsx +0 -173
  94. package/src/stack/StackLayout.tsx +0 -119
  95. package/src/stack/index.ts +0 -4
  96. package/src/stack/stackTypes.ts +0 -22
  97. package/src/tabs/TabPanel.css +0 -12
  98. package/src/tabs/TabPanel.tsx +0 -17
  99. package/src/tabs/index.ts +0 -1
  100. package/src/tools/config-wrapper/ConfigWrapper.tsx +0 -55
  101. package/src/tools/config-wrapper/index.ts +0 -1
  102. package/src/tools/devtools-box/layout-configurator.css +0 -112
  103. package/src/tools/devtools-box/layout-configurator.jsx +0 -369
  104. package/src/tools/devtools-tree/layout-tree-viewer.css +0 -15
  105. package/src/tools/devtools-tree/layout-tree-viewer.jsx +0 -36
  106. package/src/tools/index.ts +0 -4
  107. package/src/use-persistent-state.ts +0 -112
  108. package/src/utils/index.ts +0 -5
  109. package/src/utils/pathUtils.ts +0 -283
  110. package/src/utils/propUtils.ts +0 -26
  111. package/src/utils/refUtils.ts +0 -16
  112. package/src/utils/styleUtils.ts +0 -13
  113. package/src/utils/typeOf.ts +0 -25
  114. package/tsconfig-emit-types.json +0 -11
@@ -1,237 +0,0 @@
1
- import React, { ReactElement } from "react";
2
- import { DropPos } from "../drag-drop/dragDropTypes";
3
- import { DropTarget } from "../drag-drop/DropTarget";
4
- import { isContainer } from "../registry/ComponentRegistry";
5
- import {
6
- findTarget,
7
- followPath,
8
- followPathToParent,
9
- getProp,
10
- getProps,
11
- typeOf
12
- } from "../utils";
13
- import { getIntrinsicSize } from "./flexUtils";
14
- import {
15
- getInsertTabBeforeAfter,
16
- insertBesideChild,
17
- insertIntoContainer
18
- } from "./insert-layout-element";
19
- import {
20
- AddAction,
21
- DragDropAction, LayoutActionType, LayoutReducerAction, MaximizeAction, SetTitleAction,
22
- SwitchTabAction
23
- } from "./layoutTypes";
24
- import { LayoutProps } from "./layoutUtils";
25
- import { removeChild } from "./remove-layout-element";
26
- import {
27
- replaceChild,
28
- swapChild,
29
- _replaceChild
30
- } from "./replace-layout-element";
31
- import { resizeFlexChildren } from "./resize-flex-children";
32
- import { wrap } from "./wrap-layout-element";
33
-
34
- export const layoutReducer = (
35
- state: ReactElement,
36
- action: LayoutReducerAction
37
- ): ReactElement => {
38
- switch (action.type) {
39
- case LayoutActionType.ADD:
40
- return addChild(state, action);
41
- case LayoutActionType.DRAG_DROP:
42
- return dragDrop(state, action);
43
- case LayoutActionType.MAXIMIZE:
44
- return setChildProps(state, action);
45
- case LayoutActionType.REMOVE:
46
- return removeChild(state, action);
47
- case LayoutActionType.REPLACE:
48
- return replaceChild(state, action);
49
- case LayoutActionType.SET_TITLE:
50
- return setTitle(state, action);
51
- case LayoutActionType.SPLITTER_RESIZE:
52
- return resizeFlexChildren(state, action);
53
- case LayoutActionType.SWITCH_TAB:
54
- return switchTab(state, action);
55
- default:
56
- return state;
57
- }
58
- };
59
-
60
- const switchTab = (state: ReactElement, { path, nextIdx }: SwitchTabAction) => {
61
- const target = followPath(state, path, true);
62
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
63
- const replacement = React.cloneElement<any>(target, {
64
- active: nextIdx,
65
- });
66
- return swapChild(state, target, replacement);
67
- }
68
-
69
- const setTitle = (state: ReactElement, { path, title }: SetTitleAction) => {
70
- const target = followPath(state, path, true);
71
- const replacement = React.cloneElement(target, {
72
- title,
73
- });
74
- return swapChild(state, target, replacement);
75
- }
76
-
77
- const setChildProps = (state: ReactElement, { path, type }: MaximizeAction) => {
78
- if (path) {
79
- const target = followPath(state, path, true);
80
- return swapChild(state, target, target, type);
81
- } else {
82
- return state;
83
- }
84
- }
85
-
86
- const dragDrop = (
87
- layoutRoot: ReactElement,
88
- action: DragDropAction
89
- ): ReactElement => {
90
- console.log("drag drop");
91
- const {
92
- draggedReactElement: newComponent,
93
- dragInstructions,
94
- dropTarget,
95
- } = action;
96
- const existingComponent = dropTarget.component as ReactElement;
97
- const { pos } = dropTarget;
98
- const destinationTabstrip =
99
- pos?.position?.Header && typeOf(existingComponent) === "Stack";
100
- const { id, version } = getProps(newComponent);
101
- const intrinsicSize = getIntrinsicSize(newComponent);
102
- let newLayoutRoot: ReactElement;
103
- if (destinationTabstrip) {
104
- const [targetTab, insertionPosition] = getInsertTabBeforeAfter(
105
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
106
- existingComponent!,
107
- pos
108
- );
109
- if (targetTab === undefined) {
110
- newLayoutRoot = insertIntoContainer(
111
- layoutRoot,
112
- existingComponent,
113
- newComponent
114
- );
115
- } else {
116
- newLayoutRoot = insertBesideChild(
117
- layoutRoot,
118
- targetTab,
119
- newComponent,
120
- insertionPosition
121
- );
122
- }
123
- } else if (!intrinsicSize && pos?.position?.Centre) {
124
- newLayoutRoot = _replaceChild(
125
- layoutRoot,
126
- existingComponent as ReactElement,
127
- newComponent
128
- );
129
- } else {
130
- newLayoutRoot = dropLayoutIntoContainer(
131
- layoutRoot,
132
- dropTarget as DropTarget,
133
- newComponent
134
- );
135
- }
136
-
137
- if (dragInstructions.DoNotRemove) {
138
- return newLayoutRoot;
139
- }
140
-
141
- const finalTarget = findTarget(
142
- newLayoutRoot,
143
- (props: LayoutProps) => props.id === id && props.version === version
144
- ) as ReactElement;
145
- const finalPath = getProp(finalTarget, "path");
146
- return removeChild(newLayoutRoot, { path: finalPath, type: "remove" });
147
- }
148
-
149
- const addChild = (
150
- layoutRoot: ReactElement,
151
- { path: containerPath, component }: AddAction
152
- ) => {
153
- return insertIntoContainer(
154
- layoutRoot,
155
- followPath(layoutRoot, containerPath) as ReactElement,
156
- component
157
- );
158
- }
159
-
160
- const dropLayoutIntoContainer = (
161
- layoutRoot: ReactElement,
162
- dropTarget: DropTarget,
163
- newComponent: ReactElement
164
- ): ReactElement => {
165
- const {
166
- component: existingComponent,
167
- pos,
168
- clientRect,
169
- dropRect,
170
- } = dropTarget;
171
- const existingComponentPath = getProp(existingComponent, "path");
172
-
173
- if (existingComponentPath === "0.0") {
174
- return wrap(layoutRoot, existingComponent, newComponent, pos);
175
- }
176
-
177
- const targetContainer = followPathToParent(
178
- layoutRoot,
179
- existingComponentPath
180
- ) as ReactElement;
181
-
182
- if (withTheGrain(pos, targetContainer)) {
183
- const insertionPosition = pos.position.SouthOrEast ? "after" : "before";
184
- return insertBesideChild(
185
- layoutRoot,
186
- existingComponent,
187
- newComponent,
188
- insertionPosition,
189
- pos,
190
- clientRect,
191
- dropRect
192
- );
193
- }
194
-
195
- if (!withTheGrain(pos, targetContainer)) {
196
- return wrap(
197
- layoutRoot,
198
- existingComponent,
199
- newComponent,
200
- pos,
201
- clientRect,
202
- dropRect
203
- );
204
- }
205
-
206
- if (isContainer(typeOf(targetContainer) as string)) {
207
- return wrap(layoutRoot, existingComponent, newComponent, pos);
208
- }
209
-
210
- throw Error(`no support right now for position = ${pos.position}`);
211
- }
212
-
213
- const withTheGrain = (pos: DropPos, container: ReactElement) => {
214
- if (pos.position.Centre) {
215
- return isTerrace(container) || isTower(container);
216
- }
217
-
218
- return pos.position.NorthOrSouth
219
- ? isTower(container)
220
- : pos.position.EastOrWest
221
- ? isTerrace(container)
222
- : false;
223
- }
224
-
225
- const isTower = (container: ReactElement) => {
226
- return (
227
- typeOf(container) === "Flexbox" &&
228
- container.props.style.flexDirection === "column"
229
- );
230
- }
231
-
232
- const isTerrace = (container: ReactElement) => {
233
- return (
234
- typeOf(container) === "Flexbox" &&
235
- container.props.style.flexDirection !== "column"
236
- );
237
- }
@@ -1,159 +0,0 @@
1
- /* eslint-disable @typescript-eslint/no-explicit-any */
2
- import { ReactElement } from "react";
3
- import { DragDropRect, DragInstructions } from "../drag-drop";
4
- import { DropTarget } from "../drag-drop/DropTarget";
5
-
6
- export interface WithProps {
7
- props?: { [key: string]: any };
8
- }
9
-
10
- export interface WithType {
11
- props?: any;
12
- title?: string;
13
- type: string;
14
- }
15
-
16
- export interface LayoutRoot extends WithProps {
17
- active?: number;
18
- children?: ReactElement[];
19
- type: string;
20
- }
21
-
22
- export interface LayoutJSON extends WithType {
23
- children?: LayoutJSON[];
24
- id?: string;
25
- props?: { [key: string]: any };
26
- state?: any;
27
- type: string;
28
- }
29
-
30
- export interface WithActive {
31
- active?: number;
32
- }
33
-
34
- export type LayoutModel = LayoutRoot | ReactElement | WithType;
35
-
36
- export type layoutType = "Flexbox" | "View" | "DraggableLayout" | "Stack";
37
-
38
- export const LayoutActionType = {
39
- ADD: "add",
40
- DRAG_START: "drag-start",
41
- DRAG_DROP: "drag-drop",
42
- MAXIMIZE: "maximize",
43
- MINIMIZE: "minimize",
44
- REMOVE: "remove",
45
- REPLACE: "replace",
46
- RESTORE: "restore",
47
- SAVE: "save",
48
- SET_TITLE: "set-title",
49
- SPLITTER_RESIZE: "splitter-resize",
50
- SWITCH_TAB: "switch-tab",
51
- TEAROUT: "tearout",
52
- } as const;
53
-
54
- export type AddAction = {
55
- component: any;
56
- path: string;
57
- type: typeof LayoutActionType.ADD;
58
- };
59
-
60
- export type DragDropAction = {
61
- draggedReactElement: ReactElement;
62
- dragInstructions: any;
63
- dropTarget: Partial<DropTarget>;
64
- type: typeof LayoutActionType.DRAG_DROP;
65
- };
66
-
67
- export type MaximizeAction = {
68
- path?: string;
69
- type: typeof LayoutActionType.MAXIMIZE;
70
- };
71
-
72
- export type MinimizeAction = {
73
- path?: string;
74
- type: typeof LayoutActionType.MINIMIZE;
75
- };
76
-
77
- export type RemoveAction = {
78
- path?: string;
79
- type: typeof LayoutActionType.REMOVE;
80
- };
81
-
82
- export type ReplaceAction = {
83
- replacement: any;
84
- target: any;
85
- type: typeof LayoutActionType.REPLACE;
86
- };
87
-
88
- export type RestoreAction = {
89
- path?: string;
90
- type: typeof LayoutActionType.RESTORE;
91
- };
92
-
93
- export type SetTitleAction = {
94
- path: string;
95
- title: string;
96
- type: typeof LayoutActionType.SET_TITLE;
97
- };
98
-
99
- export type SplitterResizeAction = {
100
- path: string;
101
- sizes: { currentSize: number; flexBasis: number }[];
102
- type: typeof LayoutActionType.SPLITTER_RESIZE;
103
- };
104
-
105
- export type SwitchTabAction = {
106
- nextIdx: number;
107
- path: string;
108
- type: typeof LayoutActionType.SWITCH_TAB;
109
- };
110
-
111
- export type TearoutAction = {
112
- path?: string;
113
- type: typeof LayoutActionType.TEAROUT;
114
- };
115
-
116
- export type LayoutReducerAction =
117
- | AddAction
118
- | DragDropAction
119
- | MaximizeAction
120
- | MinimizeAction
121
- | RemoveAction
122
- | ReplaceAction
123
- | RestoreAction
124
- | SetTitleAction
125
- | SplitterResizeAction
126
- | SwitchTabAction;
127
-
128
- export type SaveAction = {
129
- type: typeof LayoutActionType.SAVE;
130
- };
131
-
132
- export type AddToolbarContributionViewAction = {
133
- content: ReactElement;
134
- location: string;
135
- type: "add-toolbar-contribution";
136
- };
137
-
138
- export type RemoveToolbarContributionViewAction = {
139
- location: string;
140
- type: "remove-toolbar-contribution";
141
- };
142
-
143
- export type MousedownViewAction = {
144
- preDragActivity?: any;
145
- index?: number;
146
- type: "mousedown";
147
- };
148
-
149
- export type DragStartAction = {
150
- payload?: ReactElement;
151
- dragContainerPath?: string;
152
- dragElement?: HTMLElement;
153
- dragRect: DragDropRect;
154
- dropTargets?: string[];
155
- evt: MouseEvent;
156
- instructions?: DragInstructions;
157
- path: string;
158
- type: typeof LayoutActionType.DRAG_START;
159
- };
@@ -1,288 +0,0 @@
1
- /* eslint-disable @typescript-eslint/no-explicit-any */
2
- import { uuid } from "@vuu-ui/vuu-utils";
3
- import React, { cloneElement, CSSProperties, ReactElement } from "react";
4
- import { dimension } from "../common-types";
5
- import {
6
- ComponentWithId,
7
- ComponentRegistry,
8
- isContainer,
9
- isLayoutComponent,
10
- } from "../registry/ComponentRegistry";
11
- import {
12
- getPersistentState,
13
- hasPersistentState,
14
- setPersistentState,
15
- } from "../use-persistent-state";
16
- import { expandFlex, getProps, typeOf } from "../utils";
17
- import { LayoutJSON, LayoutModel, layoutType } from "./layoutTypes";
18
-
19
- export const getManagedDimension = (
20
- style: CSSProperties
21
- ): [dimension, dimension] =>
22
- style.flexDirection === "column" ? ["height", "width"] : ["width", "height"];
23
-
24
- const theKidHasNoStyle: CSSProperties = {};
25
-
26
- export const applyLayoutProps = (component: ReactElement, path = "0") => {
27
- const [layoutProps, children] = getChildLayoutProps(
28
- typeOf(component) as string,
29
- component.props,
30
- path
31
- );
32
- return React.cloneElement(component, layoutProps, children);
33
- };
34
-
35
- export interface LayoutProps extends ComponentWithId {
36
- active?: number;
37
- "data-path"?: string;
38
- children?: ReactElement[];
39
- column?: any;
40
- dropTarget?: any;
41
- key: string;
42
- layout?: any;
43
- path?: string;
44
- resizeable?: boolean;
45
- style: CSSProperties;
46
- type?: string;
47
- version?: number;
48
- }
49
-
50
- export const processLayoutElement = (
51
- layoutElement: ReactElement,
52
- previousLayout?: ReactElement
53
- ): ReactElement => {
54
- const type = typeOf(layoutElement) as string;
55
- const [layoutProps, children] = getChildLayoutProps(
56
- type,
57
- layoutElement.props,
58
- "0",
59
- undefined,
60
- previousLayout
61
- );
62
- return cloneElement(layoutElement, layoutProps, children);
63
- };
64
-
65
- export const applyLayout = (
66
- type: layoutType,
67
- props: LayoutProps,
68
- previousLayout?: LayoutModel
69
- ): LayoutModel => {
70
- const [layoutProps, children] = getChildLayoutProps(
71
- type,
72
- props,
73
- "0",
74
- undefined,
75
- previousLayout
76
- );
77
- return {
78
- ...props,
79
- ...layoutProps,
80
- type,
81
- children,
82
- };
83
- };
84
-
85
- function getLayoutProps(
86
- type: string,
87
- props: LayoutProps,
88
- path = "0",
89
- parentType: string | null = null,
90
- previousLayout?: LayoutModel
91
- ): LayoutProps {
92
- const {
93
- active: prevActive = 0,
94
- "data-path": dataPath,
95
- path: prevPath = dataPath,
96
- id: prevId,
97
- style: prevStyle,
98
- } = getProps(previousLayout);
99
-
100
- const prevMatch = typeOf(previousLayout) === type && path === prevPath;
101
- const id = prevMatch ? prevId : props.id ?? uuid();
102
- const active = type === "Stack" ? props.active ?? prevActive : undefined;
103
-
104
- const key = id;
105
- const style = prevMatch ? prevStyle : getStyle(type, props, parentType);
106
- return isLayoutComponent(type)
107
- ? { id, key, path, style, type, active }
108
- : { id, key, style, "data-path": path };
109
- }
110
-
111
- function getChildLayoutProps(
112
- type: string,
113
- props: LayoutProps,
114
- path: string,
115
- parentType: string | null = null,
116
- previousLayout?: LayoutModel
117
- ): [LayoutProps, ReactElement[]] {
118
- const layoutProps = getLayoutProps(
119
- type,
120
- props,
121
- path,
122
- parentType,
123
- previousLayout
124
- );
125
-
126
- if (props.layout && !previousLayout) {
127
- return [layoutProps, [layoutFromJson(props.layout, `${path}.0`)]];
128
- }
129
-
130
- const previousChildren =
131
- (previousLayout as any)?.children ?? previousLayout?.props?.children;
132
- const hasDynamicChildren = props.dropTarget && previousChildren;
133
- const children = hasDynamicChildren
134
- ? previousChildren
135
- : getLayoutChildren(type, props.children, path, previousChildren);
136
- return [layoutProps, children];
137
- }
138
-
139
- function getLayoutChildren(
140
- type: string,
141
- children?: ReactElement[],
142
- path = "0",
143
- previousChildren?: ReactElement[]
144
- ) {
145
- const kids = Array.isArray(children)
146
- ? children
147
- : React.isValidElement(children)
148
- ? [children]
149
- : [];
150
- return isContainer(type)
151
- ? kids.map((child, i) => {
152
- const childType = typeOf(child) as string;
153
- const previousType = typeOf(previousChildren?.[i]);
154
-
155
- if (!previousType || childType === previousType) {
156
- const [layoutProps, children] = getChildLayoutProps(
157
- childType,
158
- child.props,
159
- `${path}.${i}`,
160
- type,
161
- previousChildren?.[i]
162
- );
163
- return React.cloneElement(child, layoutProps, children);
164
- }
165
-
166
- return previousChildren?.[i];
167
- })
168
- : children;
169
- }
170
-
171
- const getStyle = (
172
- type: string,
173
- props: LayoutProps,
174
- parentType?: string | null
175
- ) => {
176
- let { style = theKidHasNoStyle } = props;
177
- if (type === "Flexbox") {
178
- style = {
179
- flexDirection: props.column ? "column" : "row",
180
- ...style,
181
- display: "flex",
182
- };
183
- }
184
-
185
- if (style.flex) {
186
- const { flex, ...otherStyles } = style;
187
- style = {
188
- ...otherStyles,
189
- ...expandFlex(typeof flex === "number" ? flex : 0),
190
- };
191
- } else if (parentType === "Stack") {
192
- style = {
193
- ...style,
194
- ...expandFlex(1),
195
- };
196
- } else if (
197
- parentType === "Flexbox" &&
198
- (style.width || style.height) &&
199
- style.flexBasis === undefined
200
- ) {
201
- style = {
202
- ...style,
203
- flexBasis: "auto",
204
- flexGrow: 0,
205
- flexShrink: 0,
206
- };
207
- }
208
-
209
- return style;
210
- };
211
-
212
- export function layoutFromJson(
213
- { id = uuid(), type, children, props, state }: LayoutJSON,
214
- path: string
215
- ): ReactElement {
216
- const componentType = type.match(/^[a-z]/) ? type : ComponentRegistry[type];
217
-
218
- if (componentType === undefined) {
219
- throw Error(
220
- `layoutUtils unable to create component from JSON, unknown type ${type}`
221
- );
222
- }
223
-
224
- if (state) {
225
- setPersistentState(id, state);
226
- }
227
-
228
- return React.createElement(
229
- componentType,
230
- {
231
- id,
232
- ...props,
233
- key: id,
234
- path,
235
- },
236
- children
237
- ? children.map((child, i) => layoutFromJson(child, `${path}.${i}`))
238
- : undefined
239
- );
240
- }
241
-
242
- export function layoutToJSON(component: ReactElement) {
243
- return componentToJson(component);
244
- }
245
-
246
- export function componentToJson(component: ReactElement): LayoutJSON {
247
- const type = typeOf(component) as string;
248
- const { id, children, type: _omit, ...props } = getProps(component);
249
-
250
- const state = hasPersistentState(id) ? getPersistentState(id) : undefined;
251
-
252
- return {
253
- id,
254
- type,
255
- props: serializeProps(props as LayoutProps),
256
- state,
257
- children: React.Children.map(children, componentToJson),
258
- };
259
- }
260
-
261
- export function serializeProps(props?: LayoutProps) {
262
- if (props) {
263
- const { path, ...otherProps } = props;
264
- const result: { [key: string]: any } = {};
265
- for (const [key, value] of Object.entries(otherProps)) {
266
- result[key] = serializeValue(value);
267
- }
268
- return result;
269
- }
270
- }
271
-
272
- function serializeValue(value: unknown): any {
273
- if (
274
- typeof value === "string" ||
275
- typeof value === "number" ||
276
- typeof value === "boolean"
277
- ) {
278
- return value;
279
- } else if (Array.isArray(value)) {
280
- return value.map(serializeValue);
281
- } else if (typeof value === "object" && value !== null) {
282
- const result: { [key: string]: any } = {};
283
- for (const [k, v] of Object.entries(value)) {
284
- result[k] = serializeValue(v);
285
- }
286
- return result;
287
- }
288
- }