@vuu-ui/vuu-layout 2.1.19-beta.1 → 2.1.19-beta.2

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 (89) hide show
  1. package/package.json +12 -13
  2. package/src/Component.js +14 -0
  3. package/src/LayoutContainer.css.js +24 -0
  4. package/src/LayoutContainer.js +26 -0
  5. package/src/debug.js +16 -0
  6. package/src/dock-layout/DockLayout.css.js +36 -0
  7. package/src/dock-layout/DockLayout.js +43 -0
  8. package/src/dock-layout/Drawer.css.js +150 -0
  9. package/src/dock-layout/Drawer.js +82 -0
  10. package/src/dock-layout/index.js +2 -0
  11. package/src/drag-drop/BoxModel.js +337 -0
  12. package/src/drag-drop/DragState.js +119 -0
  13. package/src/drag-drop/Draggable.js +140 -0
  14. package/src/drag-drop/DropTarget.js +286 -0
  15. package/src/drag-drop/DropTargetRenderer.js +275 -0
  16. package/src/drag-drop/dragDropTypes.js +0 -0
  17. package/src/drag-drop/index.js +3 -0
  18. package/src/flexbox/Flexbox.css.js +45 -0
  19. package/src/flexbox/Flexbox.js +45 -0
  20. package/src/flexbox/FlexboxLayout.js +27 -0
  21. package/src/flexbox/Splitter.css.js +57 -0
  22. package/src/flexbox/Splitter.js +112 -0
  23. package/src/flexbox/flexbox-utils.js +92 -0
  24. package/src/flexbox/flexboxTypes.js +0 -0
  25. package/src/flexbox/index.js +2 -0
  26. package/src/flexbox/useSplitterResizing.js +181 -0
  27. package/src/index.js +20 -0
  28. package/src/layout-action.js +20 -0
  29. package/src/layout-header/ActionButton.js +15 -0
  30. package/src/layout-header/Header.css.js +32 -0
  31. package/src/layout-header/Header.js +99 -0
  32. package/src/layout-header/index.js +1 -0
  33. package/src/layout-header/useHeader.js +76 -0
  34. package/src/layout-provider/LayoutProvider.js +242 -0
  35. package/src/layout-provider/LayoutProviderContext.js +13 -0
  36. package/src/layout-provider/index.js +2 -0
  37. package/src/layout-provider/useLayoutDragDrop.js +147 -0
  38. package/src/layout-reducer/flexUtils.js +186 -0
  39. package/src/layout-reducer/index.js +4 -0
  40. package/src/layout-reducer/insert-layout-element.js +179 -0
  41. package/src/layout-reducer/layout-reducer.js +121 -0
  42. package/src/layout-reducer/layoutTypes.js +34 -0
  43. package/src/layout-reducer/layoutUtils.js +178 -0
  44. package/src/layout-reducer/move-layout-element.js +18 -0
  45. package/src/layout-reducer/remove-layout-element.js +161 -0
  46. package/src/layout-reducer/replace-layout-element.js +70 -0
  47. package/src/layout-reducer/resize-flex-children.js +48 -0
  48. package/src/layout-reducer/wrap-layout-element.js +134 -0
  49. package/src/layout-view/View.css.js +76 -0
  50. package/src/layout-view/View.js +116 -0
  51. package/src/layout-view/useView.js +69 -0
  52. package/src/layout-view/useViewBroadcastChannel.js +29 -0
  53. package/src/layout-view/useViewResize.js +27 -0
  54. package/src/layout-view/viewTypes.js +0 -0
  55. package/src/layout-view-actions/ViewContext.js +11 -0
  56. package/src/layout-view-actions/useViewActionDispatcher.js +116 -0
  57. package/src/layout-view-actions/useViewContributions.js +31 -0
  58. package/src/palette/Palette.css.js +31 -0
  59. package/src/palette/Palette.js +85 -0
  60. package/src/palette/index.js +1 -0
  61. package/src/placeholder/LayoutStartPanel.css.js +35 -0
  62. package/src/placeholder/LayoutStartPanel.js +57 -0
  63. package/src/placeholder/Placeholder.css.js +20 -0
  64. package/src/placeholder/Placeholder.js +33 -0
  65. package/src/placeholder/index.js +1 -0
  66. package/src/responsive/index.js +2 -0
  67. package/src/responsive/measureMinimumNodeSize.js +24 -0
  68. package/src/responsive/useResizeObserver.js +109 -0
  69. package/src/responsive/utils.js +27 -0
  70. package/src/stack/Stack.css.js +44 -0
  71. package/src/stack/Stack.js +98 -0
  72. package/src/stack/StackLayout.js +116 -0
  73. package/src/stack/index.js +3 -0
  74. package/src/stack/stackTypes.js +0 -0
  75. package/src/tabs/TabPanel.css.js +17 -0
  76. package/src/tabs/TabPanel.js +11 -0
  77. package/src/tabs/index.js +1 -0
  78. package/src/tools/config-wrapper/ConfigWrapper.js +55 -0
  79. package/src/tools/config-wrapper/index.js +1 -0
  80. package/src/tools/devtools-box/layout-configurator.css.js +113 -0
  81. package/src/tools/devtools-tree/layout-tree-viewer.css.js +17 -0
  82. package/src/tools/index.js +3 -0
  83. package/src/use-persistent-state.js +43 -0
  84. package/src/utils/index.js +5 -0
  85. package/src/utils/pathUtils.js +212 -0
  86. package/src/utils/propUtils.js +15 -0
  87. package/src/utils/refUtils.js +5 -0
  88. package/src/utils/styleUtils.js +9 -0
  89. package/src/utils/typeOf.js +15 -0
@@ -0,0 +1,179 @@
1
+ import { uuid } from "@vuu-ui/vuu-utils";
2
+ import react from "react";
3
+ import { getProp, getProps, nextStep, resetPath, typeOf } from "../utils/index.js";
4
+ import { createPlaceHolder, getFlexDimensions, getFlexOrIntrinsicStyle, getIntrinsicSize, wrapIntrinsicSizeComponentWithFlexbox } from "./flexUtils.js";
5
+ import { getDefaultTabLabel, getManagedDimension } from "./layoutUtils.js";
6
+ function getInsertTabBeforeAfter(stack, pos) {
7
+ const { children: tabs } = stack.props;
8
+ const tabCount = tabs.length;
9
+ const { index = -1, positionRelativeToTab = "after" } = pos.tab || {};
10
+ return -1 === index || index >= tabCount ? [
11
+ tabs[tabCount - 1],
12
+ "after"
13
+ ] : [
14
+ tabs[index] ?? null,
15
+ positionRelativeToTab
16
+ ];
17
+ }
18
+ function insertIntoContainer(container, targetContainer, newComponent) {
19
+ const { active: containerActive, children: containerChildren = [], path: containerPath } = getProps(container);
20
+ const existingComponentPath = getProp(targetContainer, "path");
21
+ const { idx, finalStep } = nextStep(containerPath, existingComponentPath, true);
22
+ const [insertedIdx, children] = finalStep ? insertIntoChildren(container, containerChildren, newComponent) : [
23
+ containerActive,
24
+ containerChildren?.map((child, index)=>index === idx ? insertIntoContainer(child, targetContainer, newComponent) : child)
25
+ ];
26
+ const active = "Stack" === typeOf(container) ? Array.isArray(insertedIdx) ? insertedIdx[0] : insertedIdx : containerActive;
27
+ return react.cloneElement(container, {
28
+ active
29
+ }, children);
30
+ }
31
+ const getDefaultTitle = (containerType, component, index, existingLabels)=>"Stack" === containerType ? getDefaultTabLabel(component, index, existingLabels) : void 0;
32
+ const getChildrenTitles = (children)=>children.map((child)=>child.props.title);
33
+ function insertIntoChildren(container, containerChildren, newComponent) {
34
+ if (Array.isArray(newComponent)) {
35
+ const [firstChild, ...rest] = newComponent;
36
+ let [idx, children] = insertIntoChildren(container, containerChildren, firstChild);
37
+ for (const child of rest)[, children] = insertIntoChildren(container, children, child);
38
+ return [
39
+ idx,
40
+ children
41
+ ];
42
+ }
43
+ const containerPath = getProp(container, "path");
44
+ const count = containerChildren?.length;
45
+ const { id = uuid(), title = getDefaultTitle(typeOf(container), newComponent, count ?? 0, getChildrenTitles(containerChildren)) } = getProps(newComponent);
46
+ if (count) return [
47
+ count,
48
+ containerChildren.concat(resetPath(newComponent, `${containerPath}.${count}`, {
49
+ id,
50
+ key: id,
51
+ title
52
+ }))
53
+ ];
54
+ return [
55
+ 0,
56
+ [
57
+ resetPath(newComponent, `${containerPath}.0`, {
58
+ id,
59
+ title
60
+ })
61
+ ]
62
+ ];
63
+ }
64
+ function insertBesideChild(container, existingComponent, newComponent, insertionPosition, pos, clientRect, dropRect) {
65
+ const { active: containerActive, children: containerChildren, path: containerPath } = getProps(container);
66
+ const existingComponentPath = getProp(existingComponent, "path");
67
+ const { idx, finalStep } = nextStep(containerPath, existingComponentPath);
68
+ const [insertedIdx, children] = finalStep ? updateChildren(container, containerChildren, idx, newComponent, insertionPosition, pos, clientRect, dropRect) : [
69
+ containerActive,
70
+ containerChildren.map((child, index)=>index === idx ? insertBesideChild(child, existingComponent, newComponent, insertionPosition, pos, clientRect, dropRect) : child)
71
+ ];
72
+ const active = "Stack" === typeOf(container) ? insertedIdx : containerActive;
73
+ return react.cloneElement(container, {
74
+ active
75
+ }, children);
76
+ }
77
+ function updateChildren(container, containerChildren, idx, newComponent, insertionPosition, pos, clientRect, dropRect) {
78
+ const intrinsicSize = getIntrinsicSize(newComponent);
79
+ if (intrinsicSize?.width && intrinsicSize?.height) return insertIntrinsicSizedComponent(container, containerChildren, idx, newComponent, insertionPosition, clientRect, dropRect);
80
+ return insertFlexComponent(container, containerChildren, idx, newComponent, insertionPosition, pos?.width || pos?.height, clientRect);
81
+ }
82
+ const getLeadingPlaceholderSize = (flexDirection, insertionPosition, { top, right, bottom, left }, [rectLeft, rectTop, rectRight, rectBottom])=>{
83
+ if ("column" === flexDirection && "before" === insertionPosition) return rectTop - top;
84
+ if ("column" === flexDirection) return bottom - rectBottom;
85
+ if ("row" === flexDirection && "before" === insertionPosition) return rectLeft - left;
86
+ if ("row" === flexDirection) return right - rectRight;
87
+ };
88
+ function insertIntrinsicSizedComponent(container, containerChildren, idx, newComponent, insertionPosition, clientRect, dropRect) {
89
+ const { style: { flexDirection } } = getProps(container);
90
+ const [dimension, crossDimension, contraDirection] = getFlexDimensions(flexDirection);
91
+ const { [crossDimension]: intrinsicCrossSize, [dimension]: intrinsicSize } = getIntrinsicSize(newComponent);
92
+ const path = getProp(containerChildren[idx], "path");
93
+ const placeholderSize = getLeadingPlaceholderSize(flexDirection, insertionPosition, clientRect, dropRect);
94
+ const [itemToInsert, size] = intrinsicCrossSize < clientRect[crossDimension] ? [
95
+ wrapIntrinsicSizeComponentWithFlexbox(newComponent, contraDirection, path, clientRect, dropRect),
96
+ intrinsicSize
97
+ ] : [
98
+ newComponent,
99
+ void 0
100
+ ];
101
+ const placeholder = placeholderSize ? createPlaceHolder(path, placeholderSize, {
102
+ flexGrow: 0,
103
+ flexShrink: 0
104
+ }) : void 0;
105
+ if (intrinsicCrossSize > clientRect[crossDimension]) containerChildren = containerChildren.map((child)=>{
106
+ if (getProp(child, "placeholder")) return child;
107
+ {
108
+ const { [crossDimension]: intrinsicCrossChildSize } = getIntrinsicSize(child);
109
+ if (intrinsicCrossChildSize && intrinsicCrossChildSize < intrinsicCrossSize) return wrapIntrinsicSizeComponentWithFlexbox(child, contraDirection, getProp(child, "path"));
110
+ return child;
111
+ }
112
+ });
113
+ return insertFlexComponent(container, containerChildren, idx, itemToInsert, insertionPosition, size, clientRect, placeholder);
114
+ }
115
+ function insertFlexComponent(container, containerChildren, idx, newComponent, insertionPosition, size, targetRect, placeholder) {
116
+ const containerPath = getProp(container, "path");
117
+ let insertedIdx = 0;
118
+ const children = containerChildren && 0 !== containerChildren.length ? containerChildren.reduce((arr, child, i)=>{
119
+ if (idx === i) {
120
+ const [existingComponent, insertedComponent] = getStyledComponents(container, child, newComponent, targetRect);
121
+ if ("before" === insertionPosition) if (placeholder) arr.push(placeholder, insertedComponent, existingComponent);
122
+ else arr.push(insertedComponent, existingComponent);
123
+ else if (placeholder) arr.push(existingComponent, insertedComponent, placeholder);
124
+ else arr.push(existingComponent, insertedComponent);
125
+ insertedIdx = arr.indexOf(insertedComponent);
126
+ } else arr.push(child);
127
+ return arr;
128
+ }, []).map((child, i)=>i < insertedIdx ? child : resetPath(child, `${containerPath}.${i}`)) : [
129
+ newComponent
130
+ ];
131
+ return [
132
+ insertedIdx,
133
+ children
134
+ ];
135
+ }
136
+ function getStyledComponents(container, existingComponent, newComponent, targetRect) {
137
+ const id = uuid();
138
+ let { version = 0 } = getProps(newComponent);
139
+ version += 1;
140
+ if ("Flexbox" === typeOf(container)) {
141
+ const { style: containerStyle } = container.props;
142
+ const [dim] = getManagedDimension(containerStyle);
143
+ const splitterSize = 6;
144
+ const size = {
145
+ [dim]: (targetRect[dim] - splitterSize) / 2
146
+ };
147
+ const existingComponentStyle = getFlexOrIntrinsicStyle(existingComponent, dim, size);
148
+ const newComponentStyle = getFlexOrIntrinsicStyle(newComponent, dim, size);
149
+ return [
150
+ react.cloneElement(existingComponent, {
151
+ style: existingComponentStyle
152
+ }),
153
+ react.cloneElement(newComponent, {
154
+ id,
155
+ version,
156
+ style: newComponentStyle
157
+ })
158
+ ];
159
+ }
160
+ {
161
+ const { style: { left: _1, top: _2, flex: _3, ...style } = {
162
+ left: void 0,
163
+ top: void 0,
164
+ flex: void 0
165
+ } } = getProps(newComponent);
166
+ return [
167
+ existingComponent,
168
+ react.cloneElement(newComponent, {
169
+ id,
170
+ version,
171
+ style: {
172
+ ...style,
173
+ flex: "1 1 0px"
174
+ }
175
+ })
176
+ ];
177
+ }
178
+ }
179
+ export { getInsertTabBeforeAfter, insertBesideChild, insertIntoContainer };
@@ -0,0 +1,121 @@
1
+ import { isContainer } from "@vuu-ui/vuu-utils";
2
+ import react from "react";
3
+ import { findTarget, followPath, followPathToParent, getProp, getProps, typeOf } from "../utils/index.js";
4
+ import { getIntrinsicSize } from "./flexUtils.js";
5
+ import { getInsertTabBeforeAfter, insertBesideChild, insertIntoContainer } from "./insert-layout-element.js";
6
+ import { moveChild } from "./move-layout-element.js";
7
+ import { LayoutActionType } from "./layoutTypes.js";
8
+ import { removeChild } from "./remove-layout-element.js";
9
+ import { _replaceChild, replaceChild, swapChild } from "./replace-layout-element.js";
10
+ import { resizeFlexChild, resizeFlexChildren } from "./resize-flex-children.js";
11
+ import { wrap } from "./wrap-layout-element.js";
12
+ const layoutReducer = (state, action)=>{
13
+ switch(action.type){
14
+ case LayoutActionType.ADD:
15
+ return addChild(state, action);
16
+ case LayoutActionType.DRAG_DROP:
17
+ return dragDrop(state, action);
18
+ case "collapse":
19
+ case "expand":
20
+ return setChildProps(state, action);
21
+ case LayoutActionType.REMOVE:
22
+ return removeChild(state, action);
23
+ case LayoutActionType.REPLACE:
24
+ return replaceChild(state, action);
25
+ case LayoutActionType.SET_PROP:
26
+ return setProp(state, action);
27
+ case LayoutActionType.SET_PROPS:
28
+ return setProps(state, action);
29
+ case LayoutActionType.SET_TITLE:
30
+ return setProp(state, {
31
+ type: "set-prop",
32
+ path: action.path,
33
+ propName: "title",
34
+ propValue: action.title
35
+ });
36
+ case LayoutActionType.SPLITTER_RESIZE:
37
+ return resizeFlexChildren(state, action);
38
+ case LayoutActionType.LAYOUT_RESIZE:
39
+ return resizeFlexChild(state, action);
40
+ case LayoutActionType.SWITCH_TAB:
41
+ return switchTab(state, action);
42
+ case LayoutActionType.MOVE_CHILD:
43
+ return moveChild(state, action);
44
+ default:
45
+ return state;
46
+ }
47
+ };
48
+ const switchTab = (state, { path, nextIdx })=>{
49
+ const target = followPath(state, path, true);
50
+ const replacement = react.cloneElement(target, {
51
+ active: nextIdx
52
+ });
53
+ return swapChild(state, target, replacement);
54
+ };
55
+ const setProp = (state, { path, propName, propValue })=>{
56
+ const target = followPath(state, path, true);
57
+ const replacement = react.cloneElement(target, {
58
+ [propName]: propValue
59
+ });
60
+ return swapChild(state, target, replacement);
61
+ };
62
+ const setProps = (state, { path, props })=>{
63
+ const target = followPath(state, path, true);
64
+ const replacement = react.cloneElement(target, props);
65
+ return swapChild(state, target, replacement);
66
+ };
67
+ const setChildProps = (state, { path, type })=>{
68
+ if (!path) return state;
69
+ {
70
+ const target = followPath(state, path, true);
71
+ return swapChild(state, target, target, type);
72
+ }
73
+ };
74
+ const dragDrop = (layoutRoot, action)=>{
75
+ const { draggedReactElement: newComponent, dragInstructions, dropTarget } = action;
76
+ const existingComponent = dropTarget.component;
77
+ const { pos } = dropTarget;
78
+ const destinationTabstrip = pos?.position?.Header && "Stack" === typeOf(existingComponent);
79
+ const { id, version } = getProps(newComponent);
80
+ const intrinsicSize = getIntrinsicSize(newComponent);
81
+ let newLayoutRoot;
82
+ if (destinationTabstrip) {
83
+ const [targetTab, insertionPosition] = getInsertTabBeforeAfter(existingComponent, pos);
84
+ newLayoutRoot = void 0 === targetTab ? insertIntoContainer(layoutRoot, existingComponent, newComponent) : insertBesideChild(layoutRoot, targetTab, newComponent, insertionPosition);
85
+ } else newLayoutRoot = !intrinsicSize && pos?.position?.Centre ? _replaceChild(layoutRoot, existingComponent, newComponent) : dropLayoutIntoContainer(layoutRoot, dropTarget, newComponent);
86
+ if (dragInstructions.DoNotRemove) return newLayoutRoot;
87
+ const finalTarget = findTarget(newLayoutRoot, (props)=>props.id === id && props.version === version);
88
+ const finalPath = getProp(finalTarget, "path");
89
+ return removeChild(newLayoutRoot, {
90
+ path: finalPath,
91
+ type: "remove"
92
+ });
93
+ };
94
+ const addChild = (layoutRoot, { path: containerPath, component })=>insertIntoContainer(layoutRoot, followPath(layoutRoot, containerPath), component);
95
+ const dropLayoutIntoContainer = (layoutRoot, dropTarget, newComponent)=>{
96
+ const { component, pos, clientRect, dropRect } = dropTarget;
97
+ const existingComponent = component;
98
+ const existingComponentPath = getProp(existingComponent, "path");
99
+ if ("0.0" === existingComponentPath) return wrap(layoutRoot, existingComponent, newComponent, pos);
100
+ const targetContainer = followPathToParent(layoutRoot, existingComponentPath);
101
+ if (withTheGrain(pos, targetContainer)) {
102
+ const insertionPosition = pos.position.SouthOrEast ? "after" : "before";
103
+ return insertBesideChild(layoutRoot, existingComponent, newComponent, insertionPosition, pos, clientRect, dropRect);
104
+ }
105
+ if (!withTheGrain(pos, targetContainer)) return wrap(layoutRoot, existingComponent, newComponent, pos, clientRect, dropRect);
106
+ if (isContainer(typeOf(targetContainer))) return wrap(layoutRoot, existingComponent, newComponent, pos);
107
+ throw Error(`no support right now for position = ${pos.position}`);
108
+ };
109
+ const withTheGrain = (pos, container)=>{
110
+ if (pos.position.Centre) return isTerrace(container) || isTower(container);
111
+ return pos.position.NorthOrSouth ? isTower(container) : pos.position.EastOrWest ? isTerrace(container) : false;
112
+ };
113
+ const isTower = (container)=>{
114
+ const { style } = container.props;
115
+ return "Flexbox" === typeOf(container) && "column" === style.flexDirection;
116
+ };
117
+ const isTerrace = (container)=>{
118
+ const { style } = container.props;
119
+ return "Flexbox" === typeOf(container) && "column" !== style.flexDirection;
120
+ };
121
+ export { layoutReducer };
@@ -0,0 +1,34 @@
1
+ const LayoutActionType = {
2
+ ADD: "add",
3
+ DRAG_START: "drag-start",
4
+ DRAG_DROP: "drag-drop",
5
+ LAYOUT_RESIZE: "layout-resize",
6
+ MAXIMIZE: "maximize",
7
+ MINIMIZE: "minimize",
8
+ MOVE_CHILD: "move-child",
9
+ QUERY: "query",
10
+ REMOVE: "remove",
11
+ REPLACE: "replace",
12
+ RESTORE: "restore",
13
+ SET_PROP: "set-prop",
14
+ SET_PROPS: "set-props",
15
+ SET_TITLE: "set-title",
16
+ SPLITTER_RESIZE: "splitter-resize",
17
+ SWITCH_TAB: "switch-tab",
18
+ TEAROUT: "tearout"
19
+ };
20
+ const isApplicationLevelChange = (layoutChangeReason)=>[
21
+ "switch-active-layout",
22
+ "open-layout",
23
+ "close-layout",
24
+ "rename-layout"
25
+ ].includes(layoutChangeReason);
26
+ const isLayoutLevelChange = (layoutChangeReason)=>[
27
+ "switch-active-tab",
28
+ "edit-feature-title",
29
+ "save-feature-props",
30
+ "remove-component",
31
+ "resize-component",
32
+ "drag-drop-operation"
33
+ ].includes(layoutChangeReason);
34
+ export { LayoutActionType, isApplicationLevelChange, isLayoutLevelChange };
@@ -0,0 +1,178 @@
1
+ import { getLayoutComponent, isContainer, isLayoutComponent, uuid } from "@vuu-ui/vuu-utils";
2
+ import react, { cloneElement } from "react";
3
+ import { getPersistentState, hasPersistentState, setPersistentState } from "../use-persistent-state.js";
4
+ import { expandFlex, followPathToParent, getProps, typeOf } from "../utils/index.js";
5
+ const getManagedDimension = (style)=>"column" === style.flexDirection ? [
6
+ "height",
7
+ "width"
8
+ ] : [
9
+ "width",
10
+ "height"
11
+ ];
12
+ const theKidHasNoStyle = {};
13
+ const applyLayoutProps = (component, path = "0")=>{
14
+ const [layoutProps, children] = getChildLayoutProps(typeOf(component), component.props, path);
15
+ return react.cloneElement(component, layoutProps, children);
16
+ };
17
+ const cloneElementAddLayoutProps = (layoutElement, previousLayout)=>{
18
+ const type = typeOf(layoutElement);
19
+ const [layoutProps, children] = getChildLayoutProps(type, layoutElement.props, "0", void 0, previousLayout);
20
+ return cloneElement(layoutElement, layoutProps, children);
21
+ };
22
+ const applyLayout = (type, props, previousLayout)=>{
23
+ const [layoutProps, children] = getChildLayoutProps(type, props, "0", void 0, previousLayout);
24
+ return {
25
+ ...props,
26
+ ...layoutProps,
27
+ type,
28
+ children
29
+ };
30
+ };
31
+ function getLayoutProps(type, props, path = "0", parentType = null, previousLayout) {
32
+ const { active: prevActive = 0, "data-path": dataPath, path: prevPath = dataPath, id: prevId, style: prevStyle } = getProps(previousLayout);
33
+ const prevMatch = typeOf(previousLayout) === type && path === prevPath;
34
+ const id = prevMatch ? prevId : props.id ?? uuid();
35
+ const active = "Stack" === type ? props.active ?? prevActive : void 0;
36
+ const key = id;
37
+ const style = prevMatch ? prevStyle : getStyle(type, props, parentType);
38
+ return isLayoutComponent(type) ? {
39
+ id,
40
+ key,
41
+ path,
42
+ style,
43
+ type,
44
+ active
45
+ } : {
46
+ id,
47
+ key,
48
+ style,
49
+ "data-path": path
50
+ };
51
+ }
52
+ function getChildLayoutProps(type, props, path, parentType = null, previousLayout) {
53
+ const layoutProps = getLayoutProps(type, props, path, parentType, previousLayout);
54
+ if (props.layout && !previousLayout) return [
55
+ layoutProps,
56
+ [
57
+ layoutFromJson(props.layout, `${path}.0`)
58
+ ]
59
+ ];
60
+ const previousChildren = previousLayout?.children ?? previousLayout?.props?.children;
61
+ const hasDynamicChildren = props.dropTarget && previousChildren;
62
+ const children = hasDynamicChildren ? previousChildren : getLayoutChildren(type, props.children, path, previousChildren);
63
+ return [
64
+ layoutProps,
65
+ children
66
+ ];
67
+ }
68
+ function getLayoutChildren(type, children, path = "0", previousChildren) {
69
+ const kids = Array.isArray(children) ? children : react.isValidElement(children) ? [
70
+ children
71
+ ] : [];
72
+ return isContainer(type) ? kids.map((child, i)=>{
73
+ const childType = typeOf(child);
74
+ const previousType = typeOf(previousChildren?.[i]);
75
+ if (!previousType || childType === previousType) {
76
+ const [layoutProps, children] = getChildLayoutProps(childType, child.props, `${path}.${i}`, type, previousChildren?.[i]);
77
+ return react.cloneElement(child, layoutProps, children);
78
+ }
79
+ return previousChildren?.[i];
80
+ }) : children;
81
+ }
82
+ const getStyle = (type, props, parentType)=>{
83
+ let { style = theKidHasNoStyle } = props;
84
+ if ("Flexbox" === type) style = {
85
+ flexDirection: props.column ? "column" : "row",
86
+ ...style,
87
+ display: "flex"
88
+ };
89
+ if (style.flex) {
90
+ const { flex, ...otherStyles } = style;
91
+ style = {
92
+ ...otherStyles,
93
+ ...expandFlex("number" == typeof flex ? flex : 0)
94
+ };
95
+ } else if ("Stack" === parentType) style = {
96
+ ...style,
97
+ ...expandFlex(1)
98
+ };
99
+ else if ("Flexbox" === parentType && (style.width || style.height) && void 0 === style.flexBasis) style = {
100
+ ...style,
101
+ flexBasis: "auto",
102
+ flexGrow: 0,
103
+ flexShrink: 0
104
+ };
105
+ return style;
106
+ };
107
+ function layoutFromJson({ active, id = uuid(), type, children, props, state }, path) {
108
+ const componentType = type.match(/^[a-z]/) ? type : getLayoutComponent(type);
109
+ if (void 0 === componentType) throw Error(`layoutUtils unable to create component from JSON, unknown type ${type}`);
110
+ if (state) setPersistentState(id, state);
111
+ return react.createElement(componentType, {
112
+ active,
113
+ id,
114
+ ...props,
115
+ key: id,
116
+ path
117
+ }, children ? children.map((child, i)=>layoutFromJson(child, `${path}.${i}`)) : void 0);
118
+ }
119
+ function layoutToJSON(component) {
120
+ return componentToJson(component);
121
+ }
122
+ function componentToJson(component) {
123
+ const type = typeOf(component);
124
+ const { id, children, type: _omit, ...props } = getProps(component);
125
+ const state = hasPersistentState(id) ? getPersistentState(id) : void 0;
126
+ return {
127
+ id,
128
+ type,
129
+ props: serializeProps(props),
130
+ state,
131
+ children: react.Children.map(children, componentToJson)
132
+ };
133
+ }
134
+ function serializeProps(props) {
135
+ if (props) {
136
+ const { path, ...otherProps } = props;
137
+ const result = {};
138
+ for (const [key, value] of Object.entries(otherProps))result[key] = serializeValue(value);
139
+ return result;
140
+ }
141
+ }
142
+ function serializeValue(value) {
143
+ if ("string" == typeof value || "number" == typeof value || "boolean" == typeof value) return value;
144
+ if (Array.isArray(value)) return value.map(serializeValue);
145
+ if ("object" == typeof value && null !== value) {
146
+ const result = {};
147
+ for (const [k, v] of Object.entries(value))result[k] = serializeValue(v);
148
+ return result;
149
+ }
150
+ }
151
+ const layoutQuery = (query, path, layoutRoot)=>{
152
+ if (path && layoutRoot) {
153
+ const parentElement = followPathToParent(layoutRoot, path);
154
+ if (parentElement) {
155
+ const { id: parentContainerId } = getProps(parentElement);
156
+ const parentContainerType = typeOf(parentElement);
157
+ return {
158
+ parentContainerId,
159
+ parentContainerType
160
+ };
161
+ }
162
+ return {
163
+ parentContainerType: "Stack",
164
+ parentContainerId: "blah"
165
+ };
166
+ }
167
+ };
168
+ const getDefaultTabLabel = (component, tabIndex, existingLabels = [])=>{
169
+ let label = component.props?.title ?? component.props?.["data-tab-title"] ?? existingLabels[tabIndex];
170
+ if (label) return label;
171
+ {
172
+ let count = tabIndex;
173
+ do label = `Tab ${++count}`;
174
+ while (existingLabels.includes(label))
175
+ return label;
176
+ }
177
+ };
178
+ export { applyLayout, applyLayoutProps, cloneElementAddLayoutProps, componentToJson, getDefaultTabLabel, getManagedDimension, layoutFromJson, layoutQuery, layoutToJSON, serializeProps };
@@ -0,0 +1,18 @@
1
+ import { cloneElement } from "react";
2
+ import { followPath, getProps } from "../utils/index.js";
3
+ import { swapChild } from "./replace-layout-element.js";
4
+ function moveChild(layoutRoot, { fromIndex, path, toIndex }) {
5
+ const target = followPath(layoutRoot, path, true);
6
+ const { children } = getProps(target);
7
+ const replacementChildren = moveChildWithinChildren(children, fromIndex, toIndex);
8
+ const replacement = cloneElement(target, void 0, replacementChildren);
9
+ return swapChild(layoutRoot, target, replacement);
10
+ }
11
+ function moveChildWithinChildren(children, fromIndex, toIndex) {
12
+ const newChildren = children.slice();
13
+ const [child] = newChildren.splice(fromIndex, 1);
14
+ if (-1 === toIndex) return newChildren.concat(child);
15
+ newChildren.splice(toIndex, 0, child);
16
+ return newChildren;
17
+ }
18
+ export { moveChild };