@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.
- package/package.json +12 -13
- package/src/Component.js +14 -0
- package/src/LayoutContainer.css.js +24 -0
- package/src/LayoutContainer.js +26 -0
- package/src/debug.js +16 -0
- package/src/dock-layout/DockLayout.css.js +36 -0
- package/src/dock-layout/DockLayout.js +43 -0
- package/src/dock-layout/Drawer.css.js +150 -0
- package/src/dock-layout/Drawer.js +82 -0
- package/src/dock-layout/index.js +2 -0
- package/src/drag-drop/BoxModel.js +337 -0
- package/src/drag-drop/DragState.js +119 -0
- package/src/drag-drop/Draggable.js +140 -0
- package/src/drag-drop/DropTarget.js +286 -0
- package/src/drag-drop/DropTargetRenderer.js +275 -0
- package/src/drag-drop/dragDropTypes.js +0 -0
- package/src/drag-drop/index.js +3 -0
- package/src/flexbox/Flexbox.css.js +45 -0
- package/src/flexbox/Flexbox.js +45 -0
- package/src/flexbox/FlexboxLayout.js +27 -0
- package/src/flexbox/Splitter.css.js +57 -0
- package/src/flexbox/Splitter.js +112 -0
- package/src/flexbox/flexbox-utils.js +92 -0
- package/src/flexbox/flexboxTypes.js +0 -0
- package/src/flexbox/index.js +2 -0
- package/src/flexbox/useSplitterResizing.js +181 -0
- package/src/index.js +20 -0
- package/src/layout-action.js +20 -0
- package/src/layout-header/ActionButton.js +15 -0
- package/src/layout-header/Header.css.js +32 -0
- package/src/layout-header/Header.js +99 -0
- package/src/layout-header/index.js +1 -0
- package/src/layout-header/useHeader.js +76 -0
- package/src/layout-provider/LayoutProvider.js +242 -0
- package/src/layout-provider/LayoutProviderContext.js +13 -0
- package/src/layout-provider/index.js +2 -0
- package/src/layout-provider/useLayoutDragDrop.js +147 -0
- package/src/layout-reducer/flexUtils.js +186 -0
- package/src/layout-reducer/index.js +4 -0
- package/src/layout-reducer/insert-layout-element.js +179 -0
- package/src/layout-reducer/layout-reducer.js +121 -0
- package/src/layout-reducer/layoutTypes.js +34 -0
- package/src/layout-reducer/layoutUtils.js +178 -0
- package/src/layout-reducer/move-layout-element.js +18 -0
- package/src/layout-reducer/remove-layout-element.js +161 -0
- package/src/layout-reducer/replace-layout-element.js +70 -0
- package/src/layout-reducer/resize-flex-children.js +48 -0
- package/src/layout-reducer/wrap-layout-element.js +134 -0
- package/src/layout-view/View.css.js +76 -0
- package/src/layout-view/View.js +116 -0
- package/src/layout-view/useView.js +69 -0
- package/src/layout-view/useViewBroadcastChannel.js +29 -0
- package/src/layout-view/useViewResize.js +27 -0
- package/src/layout-view/viewTypes.js +0 -0
- package/src/layout-view-actions/ViewContext.js +11 -0
- package/src/layout-view-actions/useViewActionDispatcher.js +116 -0
- package/src/layout-view-actions/useViewContributions.js +31 -0
- package/src/palette/Palette.css.js +31 -0
- package/src/palette/Palette.js +85 -0
- package/src/palette/index.js +1 -0
- package/src/placeholder/LayoutStartPanel.css.js +35 -0
- package/src/placeholder/LayoutStartPanel.js +57 -0
- package/src/placeholder/Placeholder.css.js +20 -0
- package/src/placeholder/Placeholder.js +33 -0
- package/src/placeholder/index.js +1 -0
- package/src/responsive/index.js +2 -0
- package/src/responsive/measureMinimumNodeSize.js +24 -0
- package/src/responsive/useResizeObserver.js +109 -0
- package/src/responsive/utils.js +27 -0
- package/src/stack/Stack.css.js +44 -0
- package/src/stack/Stack.js +98 -0
- package/src/stack/StackLayout.js +116 -0
- package/src/stack/index.js +3 -0
- package/src/stack/stackTypes.js +0 -0
- package/src/tabs/TabPanel.css.js +17 -0
- package/src/tabs/TabPanel.js +11 -0
- package/src/tabs/index.js +1 -0
- package/src/tools/config-wrapper/ConfigWrapper.js +55 -0
- package/src/tools/config-wrapper/index.js +1 -0
- package/src/tools/devtools-box/layout-configurator.css.js +113 -0
- package/src/tools/devtools-tree/layout-tree-viewer.css.js +17 -0
- package/src/tools/index.js +3 -0
- package/src/use-persistent-state.js +43 -0
- package/src/utils/index.js +5 -0
- package/src/utils/pathUtils.js +212 -0
- package/src/utils/propUtils.js +15 -0
- package/src/utils/refUtils.js +5 -0
- package/src/utils/styleUtils.js +9 -0
- 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 };
|