@vuu-ui/grid-layout 4.0.0-alpha.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/LICENSE +201 -0
- package/README.md +141 -0
- package/package.json +40 -0
- package/src/GridCommand.js +395 -0
- package/src/GridComponentSettings.js +163 -0
- package/src/GridController.js +359 -0
- package/src/GridDragCoordinator.js +293 -0
- package/src/GridGeometry.js +1298 -0
- package/src/GridLayout.css.js +119 -0
- package/src/GridLayout.js +120 -0
- package/src/GridLayoutContext.js +58 -0
- package/src/GridLayoutDocument.js +307 -0
- package/src/GridLayoutDocumentIds.js +110 -0
- package/src/GridLayoutItem.css.js +46 -0
- package/src/GridLayoutItem.js +147 -0
- package/src/GridLayoutLegacyCompatibility.js +66 -0
- package/src/GridLayoutModel.js +138 -0
- package/src/GridLayoutProvider.js +263 -0
- package/src/GridLayoutStackedItem.css.js +26 -0
- package/src/GridLayoutStackedtem.js +140 -0
- package/src/GridModel.js +1289 -0
- package/src/GridPlaceholder.css.js +9 -0
- package/src/GridPlaceholder.js +24 -0
- package/src/GridSnapshot.js +11 -0
- package/src/GridSplitter.css.js +62 -0
- package/src/GridSplitter.js +26 -0
- package/src/GridStack.js +277 -0
- package/src/Icon.css.js +8 -0
- package/src/Icon.js +26 -0
- package/src/IconButton.css.js +9 -0
- package/src/IconButton.js +28 -0
- package/src/TabDialog.css.js +10 -0
- package/src/TabDialog.js +77 -0
- package/src/TabMenu.css.js +8 -0
- package/src/TabMenu.js +78 -0
- package/src/componentToJson.js +41 -0
- package/src/drag-drop-next/DragContextNext.js +173 -0
- package/src/drag-drop-next/DragDropProviderNext.css.js +72 -0
- package/src/drag-drop-next/DragDropProviderNext.js +91 -0
- package/src/drag-drop-next/SpaceMan.js +256 -0
- package/src/drag-drop-next/TemplateDragSession.js +36 -0
- package/src/drag-drop-next/tabstrip-drag-drop.js +251 -0
- package/src/grid-dom-utils.js +57 -0
- package/src/grid-layout-utils.js +39 -0
- package/src/grid-snapshot-adapters.js +435 -0
- package/src/index.js +24 -0
- package/src/json-value.js +56 -0
- package/src/layoutFromJson.js +15 -0
- package/src/layoutToJson.js +5 -0
- package/src/propUtils.js +15 -0
- package/src/react-element-utils.js +15 -0
- package/src/typeOf.js +15 -0
- package/src/useAsDropTarget.js +229 -0
- package/src/useDraggable.js +46 -0
- package/src/useEditTabName.js +70 -0
- package/src/useGridChildProps.js +47 -0
- package/src/useGridControllerSnapshot.js +21 -0
- package/src/useGridLayout.js +727 -0
- package/src/useGridSplitterResizing.js +262 -0
- package/src/useNotDropTarget.js +2 -0
- package/types/AddTabDialog.d.ts +5 -0
- package/types/GridCommand.d.ts +146 -0
- package/types/GridComponentSettings.d.ts +71 -0
- package/types/GridController.d.ts +50 -0
- package/types/GridDragCoordinator.d.ts +80 -0
- package/types/GridGeometry.d.ts +331 -0
- package/types/GridLayout.d.ts +20 -0
- package/types/GridLayoutContext.d.ts +141 -0
- package/types/GridLayoutDocument.d.ts +61 -0
- package/types/GridLayoutDocumentIds.d.ts +15 -0
- package/types/GridLayoutItem.d.ts +28 -0
- package/types/GridLayoutLegacyCompatibility.d.ts +57 -0
- package/types/GridLayoutModel.d.ts +72 -0
- package/types/GridLayoutProvider.d.ts +59 -0
- package/types/GridLayoutStackedtem.d.ts +10 -0
- package/types/GridModel.d.ts +504 -0
- package/types/GridPlaceholder.d.ts +5 -0
- package/types/GridSnapshot.d.ts +49 -0
- package/types/GridSplitter.d.ts +10 -0
- package/types/GridStack.d.ts +160 -0
- package/types/Icon.d.ts +6 -0
- package/types/IconButton.d.ts +6 -0
- package/types/TabDialog.d.ts +9 -0
- package/types/TabMenu.d.ts +7 -0
- package/types/componentToJson.d.ts +38 -0
- package/types/drag-drop-next/DragContextNext.d.ts +92 -0
- package/types/drag-drop-next/DragDropProviderNext.d.ts +23 -0
- package/types/drag-drop-next/SpaceMan.d.ts +35 -0
- package/types/drag-drop-next/TemplateDragSession.d.ts +21 -0
- package/types/drag-drop-next/tabstrip-drag-drop.d.ts +3 -0
- package/types/grid-dom-utils.d.ts +17 -0
- package/types/grid-layout-utils.d.ts +18 -0
- package/types/grid-matrix.d.ts +5 -0
- package/types/grid-snapshot-adapters.d.ts +22 -0
- package/types/index.d.ts +24 -0
- package/types/json-value.d.ts +23 -0
- package/types/layoutFromJson.d.ts +4 -0
- package/types/layoutToJson.d.ts +3 -0
- package/types/propUtils.d.ts +5 -0
- package/types/react-element-utils.d.ts +3 -0
- package/types/src/AddTabDialog.d.ts +5 -0
- package/types/src/GridLayout.d.ts +14 -0
- package/types/src/GridLayoutContext.d.ts +88 -0
- package/types/src/GridLayoutItem.d.ts +28 -0
- package/types/src/GridLayoutModel.d.ts +59 -0
- package/types/src/GridLayoutProvider.d.ts +37 -0
- package/types/src/GridLayoutStackedtem.d.ts +10 -0
- package/types/src/GridModel.d.ts +350 -0
- package/types/src/GridPlaceholder.d.ts +5 -0
- package/types/src/GridSplitter.d.ts +9 -0
- package/types/src/Icon.d.ts +6 -0
- package/types/src/IconButton.d.ts +6 -0
- package/types/src/TabMenu.d.ts +6 -0
- package/types/src/componentToJson.d.ts +32 -0
- package/types/src/drag-drop-next/DragContextNext.d.ts +73 -0
- package/types/src/drag-drop-next/DragDropProviderNext.d.ts +20 -0
- package/types/src/drag-drop-next/SpaceMan.d.ts +35 -0
- package/types/src/drag-drop-next/tabstrip-drag-drop.d.ts +3 -0
- package/types/src/grid-dom-utils.d.ts +17 -0
- package/types/src/grid-layout-utils.d.ts +80 -0
- package/types/src/grid-matrix.d.ts +5 -0
- package/types/src/index.d.ts +10 -0
- package/types/src/layoutFromJson.d.ts +3 -0
- package/types/src/layoutToJson.d.ts +2 -0
- package/types/src/propUtils.d.ts +5 -0
- package/types/src/react-element-utils.d.ts +3 -0
- package/types/src/typeOf.d.ts +6 -0
- package/types/src/useAsDropTarget.d.ts +7 -0
- package/types/src/useDraggable.d.ts +23 -0
- package/types/src/useGridChildProps.d.ts +13 -0
- package/types/src/useGridLayout.d.ts +43 -0
- package/types/src/useGridSplitterResizing.d.ts +12 -0
- package/types/src/useNotDropTarget.d.ts +1 -0
- package/types/typeOf.d.ts +6 -0
- package/types/useAsDropTarget.d.ts +7 -0
- package/types/useDraggable.d.ts +24 -0
- package/types/useEditTabName.d.ts +11 -0
- package/types/useGridChildProps.d.ts +13 -0
- package/types/useGridControllerSnapshot.d.ts +7 -0
- package/types/useGridLayout.d.ts +53 -0
- package/types/useGridSplitterResizing.d.ts +14 -0
- package/types/useNotDropTarget.d.ts +1 -0
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { GRID_LAYOUT_DOCUMENT_KIND, GRID_LAYOUT_DOCUMENT_VERSION } from "./GridLayoutDocument.js";
|
|
2
|
+
const createSequentialGridLayoutIdAllocator = (prefix = "clone")=>{
|
|
3
|
+
let nextId = 1;
|
|
4
|
+
return (kind)=>`${prefix}-${kind}-${nextId++}`;
|
|
5
|
+
};
|
|
6
|
+
const isNestedDocument = (value)=>{
|
|
7
|
+
if ("object" != typeof value || null === value || Array.isArray(value)) return false;
|
|
8
|
+
const record = value;
|
|
9
|
+
return record.kind === GRID_LAYOUT_DOCUMENT_KIND && record.version === GRID_LAYOUT_DOCUMENT_VERSION && "object" == typeof record.layout && null !== record.layout && !Array.isArray(record.layout) && Array.isArray(record.components);
|
|
10
|
+
};
|
|
11
|
+
const remapGridLayoutDocumentIds = (source, allocate)=>{
|
|
12
|
+
const mappings = [];
|
|
13
|
+
const remapDocument = (document, path)=>{
|
|
14
|
+
const allocateId = (kind, oldId, idPath)=>{
|
|
15
|
+
const newId = allocate(kind, oldId, idPath);
|
|
16
|
+
mappings.push({
|
|
17
|
+
kind,
|
|
18
|
+
newId,
|
|
19
|
+
oldId,
|
|
20
|
+
path: idPath
|
|
21
|
+
});
|
|
22
|
+
return newId;
|
|
23
|
+
};
|
|
24
|
+
const gridId = allocateId("grid", document.layout.id, `${path}.layout.id`);
|
|
25
|
+
const itemIds = new Map(document.layout.items.map(({ id }, index)=>[
|
|
26
|
+
id,
|
|
27
|
+
allocateId("item", id, `${path}.layout.items[${index}].id`)
|
|
28
|
+
]));
|
|
29
|
+
const implicitComponentItemIds = new Map(document.layout.items.flatMap((item)=>void 0 === item.componentInstanceId ? [
|
|
30
|
+
[
|
|
31
|
+
item.id,
|
|
32
|
+
itemIds.get(item.id) ?? item.id
|
|
33
|
+
]
|
|
34
|
+
] : []));
|
|
35
|
+
const componentIds = new Map(document.components.map(({ id }, index)=>{
|
|
36
|
+
const implicitItemId = implicitComponentItemIds.get(id);
|
|
37
|
+
if (implicitItemId) {
|
|
38
|
+
mappings.push({
|
|
39
|
+
kind: "component",
|
|
40
|
+
newId: implicitItemId,
|
|
41
|
+
oldId: id,
|
|
42
|
+
path: `${path}.components[${index}].id`
|
|
43
|
+
});
|
|
44
|
+
return [
|
|
45
|
+
id,
|
|
46
|
+
implicitItemId
|
|
47
|
+
];
|
|
48
|
+
}
|
|
49
|
+
return [
|
|
50
|
+
id,
|
|
51
|
+
allocateId("component", id, `${path}.components[${index}].id`)
|
|
52
|
+
];
|
|
53
|
+
}));
|
|
54
|
+
const stackIds = new Map(document.layout.stacks.map(({ id }, index)=>[
|
|
55
|
+
id,
|
|
56
|
+
allocateId("stack", id, `${path}.layout.stacks[${index}].id`)
|
|
57
|
+
]));
|
|
58
|
+
const remapJson = (value, valuePath)=>{
|
|
59
|
+
if (isNestedDocument(value)) return remapDocument(value, valuePath);
|
|
60
|
+
if (Array.isArray(value)) return value.map((entry, index)=>remapJson(entry, `${valuePath}[${index}]`));
|
|
61
|
+
if ("object" == typeof value && null !== value) return Object.fromEntries(Object.entries(value).map(([key, entry])=>[
|
|
62
|
+
key,
|
|
63
|
+
remapJson(entry, `${valuePath}.${key}`)
|
|
64
|
+
]));
|
|
65
|
+
return value;
|
|
66
|
+
};
|
|
67
|
+
return {
|
|
68
|
+
components: document.components.map((component, index)=>({
|
|
69
|
+
...component,
|
|
70
|
+
id: componentIds.get(component.id) ?? component.id,
|
|
71
|
+
settings: remapJson(component.settings, `${path}.components[${index}].settings`)
|
|
72
|
+
})),
|
|
73
|
+
kind: GRID_LAYOUT_DOCUMENT_KIND,
|
|
74
|
+
layout: {
|
|
75
|
+
columns: [
|
|
76
|
+
...document.layout.columns
|
|
77
|
+
],
|
|
78
|
+
id: gridId,
|
|
79
|
+
items: document.layout.items.map((item)=>({
|
|
80
|
+
...item,
|
|
81
|
+
column: {
|
|
82
|
+
...item.column
|
|
83
|
+
},
|
|
84
|
+
...void 0 === item.componentInstanceId ? {} : {
|
|
85
|
+
componentInstanceId: componentIds.get(item.componentInstanceId) ?? item.componentInstanceId
|
|
86
|
+
},
|
|
87
|
+
id: itemIds.get(item.id) ?? item.id,
|
|
88
|
+
row: {
|
|
89
|
+
...item.row
|
|
90
|
+
}
|
|
91
|
+
})),
|
|
92
|
+
placeholderIds: document.layout.placeholderIds.map((id)=>itemIds.get(id) ?? id),
|
|
93
|
+
rows: [
|
|
94
|
+
...document.layout.rows
|
|
95
|
+
],
|
|
96
|
+
stacks: document.layout.stacks.map((stack)=>({
|
|
97
|
+
id: stackIds.get(stack.id) ?? stack.id,
|
|
98
|
+
itemIds: stack.itemIds.map((id)=>itemIds.get(id) ?? id),
|
|
99
|
+
selectedItemId: itemIds.get(stack.selectedItemId) ?? stack.selectedItemId
|
|
100
|
+
}))
|
|
101
|
+
},
|
|
102
|
+
version: GRID_LAYOUT_DOCUMENT_VERSION
|
|
103
|
+
};
|
|
104
|
+
};
|
|
105
|
+
return {
|
|
106
|
+
document: remapDocument(source, "$"),
|
|
107
|
+
mappings
|
|
108
|
+
};
|
|
109
|
+
};
|
|
110
|
+
export { createSequentialGridLayoutIdAllocator, remapGridLayoutDocumentIds };
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
const css = `
|
|
2
|
+
.vuuGridLayoutItem {
|
|
3
|
+
--vuu-header-height: var(--header-height, 25px);
|
|
4
|
+
border: 1px solid #fff;
|
|
5
|
+
flex-direction: column;
|
|
6
|
+
display: flex;
|
|
7
|
+
position: relative;
|
|
8
|
+
|
|
9
|
+
&.has-h-splitter:not(.vuu-stacked) {
|
|
10
|
+
margin-top: 7px;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
&.has-v-splitter {
|
|
14
|
+
margin-left: 7px;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.vuuGridLayoutItem-active {
|
|
19
|
+
position: relative;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.vuuGridLayoutItemHeader {
|
|
23
|
+
cursor: grab;
|
|
24
|
+
flex: 0 0 var(--vuu-grid-header-height);
|
|
25
|
+
height: var(--vuu-grid-header-height);
|
|
26
|
+
padding: 0 var(--salt-spacing-100) 0 0;
|
|
27
|
+
background-color: #ccc;
|
|
28
|
+
align-items: center;
|
|
29
|
+
display: flex;
|
|
30
|
+
position: relative;
|
|
31
|
+
|
|
32
|
+
& [data-align="right"] {
|
|
33
|
+
margin-left: auto;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
& .vuuGridLayoutItemHeader-title {
|
|
37
|
+
height: 100%;
|
|
38
|
+
padding: 0 var(--salt-spacing-200);
|
|
39
|
+
align-items: center;
|
|
40
|
+
display: flex;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
`;
|
|
46
|
+
export default css;
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { createElement, isValidElement, useCallback } from "react";
|
|
3
|
+
import { useComponentCssInjection } from "@salt-ds/styles";
|
|
4
|
+
import { useWindow } from "@salt-ds/window";
|
|
5
|
+
import { queryClosest, registerComponent } from "@vuu-ui/vuu-utils";
|
|
6
|
+
import clsx from "clsx";
|
|
7
|
+
import { componentToJson } from "./componentToJson.js";
|
|
8
|
+
import { useGridLayoutDispatch, useGridLayoutDragEndHandler, useGridLayoutDragStartHandler } from "./GridLayoutContext.js";
|
|
9
|
+
import { resolveMinimumGridItemSize } from "./GridModel.js";
|
|
10
|
+
import { IconButton } from "./IconButton.js";
|
|
11
|
+
import { useAsDropTarget } from "./useAsDropTarget.js";
|
|
12
|
+
import { useDraggable } from "./useDraggable.js";
|
|
13
|
+
import { useGridChildProps } from "./useGridChildProps.js";
|
|
14
|
+
import { useNotDropTarget } from "./useNotDropTarget.js";
|
|
15
|
+
import GridLayoutItem from "./GridLayoutItem.css";
|
|
16
|
+
const classBaseItem = "vuuGridLayoutItem";
|
|
17
|
+
const getDragSource = (evt)=>{
|
|
18
|
+
const draggedItem = queryClosest(evt.target, ".vuuGridLayoutItem");
|
|
19
|
+
const dragElement = draggedItem?.querySelector(".vuuDraggableLabel") || void 0;
|
|
20
|
+
if (draggedItem) {
|
|
21
|
+
const gridLayout = queryClosest(draggedItem, ".vuuGridLayout", true);
|
|
22
|
+
return {
|
|
23
|
+
dragElement,
|
|
24
|
+
element: draggedItem,
|
|
25
|
+
id: draggedItem.id,
|
|
26
|
+
layoutId: gridLayout.id,
|
|
27
|
+
label: draggedItem.querySelector(".vuuDraggableLabel")?.textContent ?? "no label",
|
|
28
|
+
type: "component"
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
throw Error("GridLayoutItem no found");
|
|
32
|
+
};
|
|
33
|
+
const GridLayoutItem_GridLayoutItem = ({ children, className: classNameProp, contentVisible: contentVisibleProp, "data-drop-target": dataDropTarget, header: headerProp, height, id, minHeight, minWidth, stackId, resizeable, style: styleProp, title: titleProp, width, ...htmlAttributes })=>{
|
|
34
|
+
const targetWindow = useWindow();
|
|
35
|
+
useComponentCssInjection({
|
|
36
|
+
testId: "vuu-grid-layout",
|
|
37
|
+
css: GridLayoutItem,
|
|
38
|
+
window: targetWindow
|
|
39
|
+
});
|
|
40
|
+
const dispatch = useGridLayoutDispatch();
|
|
41
|
+
const modelMinHeight = resolveMinimumGridItemSize(minHeight, styleProp?.minHeight);
|
|
42
|
+
const modelMinWidth = resolveMinimumGridItemSize(minWidth, styleProp?.minWidth);
|
|
43
|
+
const { contentDetached, contentVisible, dragging, dropTarget, gridArea, header, horizontalSplitter, stacked, title, verticalSplitter } = useGridChildProps({
|
|
44
|
+
contentVisible: contentVisibleProp,
|
|
45
|
+
dropTarget: dataDropTarget,
|
|
46
|
+
header: headerProp,
|
|
47
|
+
height,
|
|
48
|
+
id,
|
|
49
|
+
minHeight: modelMinHeight,
|
|
50
|
+
minWidth: modelMinWidth,
|
|
51
|
+
resizeable,
|
|
52
|
+
stackId,
|
|
53
|
+
style: styleProp,
|
|
54
|
+
title: titleProp,
|
|
55
|
+
width
|
|
56
|
+
});
|
|
57
|
+
const onDragEnd = useGridLayoutDragEndHandler();
|
|
58
|
+
const onDragStart = useGridLayoutDragStartHandler();
|
|
59
|
+
const onClose = useCallback((evt)=>{
|
|
60
|
+
evt.stopPropagation();
|
|
61
|
+
dispatch({
|
|
62
|
+
type: "close",
|
|
63
|
+
id
|
|
64
|
+
});
|
|
65
|
+
}, [
|
|
66
|
+
dispatch,
|
|
67
|
+
id
|
|
68
|
+
]);
|
|
69
|
+
const useDropTargetHook = dropTarget ? useAsDropTarget : useNotDropTarget;
|
|
70
|
+
const droppableProps = useDropTargetHook();
|
|
71
|
+
const draggableProps = useDraggable({
|
|
72
|
+
draggableClassName: classBaseItem,
|
|
73
|
+
getDragSource: getDragSource,
|
|
74
|
+
onDragEnd,
|
|
75
|
+
onDragStart
|
|
76
|
+
});
|
|
77
|
+
const className = clsx(classBaseItem, {
|
|
78
|
+
"vuuGridLayoutItem-dragging": dragging,
|
|
79
|
+
"vuu-detached": contentDetached,
|
|
80
|
+
"vuu-stacked": stacked && !contentDetached,
|
|
81
|
+
"has-h-splitter": horizontalSplitter,
|
|
82
|
+
"has-v-splitter": verticalSplitter
|
|
83
|
+
});
|
|
84
|
+
const style = {
|
|
85
|
+
...styleProp,
|
|
86
|
+
gridArea,
|
|
87
|
+
...void 0 === minHeight ? {} : {
|
|
88
|
+
minHeight
|
|
89
|
+
},
|
|
90
|
+
...void 0 === minWidth ? {} : {
|
|
91
|
+
minWidth
|
|
92
|
+
},
|
|
93
|
+
"--header-height": header ? "25px" : "0px"
|
|
94
|
+
};
|
|
95
|
+
return contentVisible || contentDetached ? /*#__PURE__*/ createElement("div", {
|
|
96
|
+
...htmlAttributes,
|
|
97
|
+
...draggableProps,
|
|
98
|
+
...droppableProps,
|
|
99
|
+
className: clsx(className, classNameProp),
|
|
100
|
+
id: id,
|
|
101
|
+
key: id,
|
|
102
|
+
style: style,
|
|
103
|
+
children: [
|
|
104
|
+
header && !stacked ? /*#__PURE__*/ jsxs("div", {
|
|
105
|
+
className: clsx(`${classBaseItem}Header`),
|
|
106
|
+
"data-drop-target": "header",
|
|
107
|
+
children: [
|
|
108
|
+
/*#__PURE__*/ jsx("span", {
|
|
109
|
+
className: `${classBaseItem}Header-title vuuDraggableLabel`,
|
|
110
|
+
draggable: true,
|
|
111
|
+
children: title
|
|
112
|
+
}),
|
|
113
|
+
/*#__PURE__*/ jsx(IconButton, {
|
|
114
|
+
className: `${classBaseItem}Header-close`,
|
|
115
|
+
"data-align": "right",
|
|
116
|
+
icon: "close",
|
|
117
|
+
onClick: onClose,
|
|
118
|
+
variant: "secondary"
|
|
119
|
+
})
|
|
120
|
+
]
|
|
121
|
+
}) : null,
|
|
122
|
+
/*#__PURE__*/ jsx("div", {
|
|
123
|
+
className: clsx(`${classBaseItem}Content`),
|
|
124
|
+
"data-drop-target": dropTarget,
|
|
125
|
+
children: children
|
|
126
|
+
})
|
|
127
|
+
]
|
|
128
|
+
}) : null;
|
|
129
|
+
};
|
|
130
|
+
(/*#__PURE__*/ createElement(GridLayoutItem_GridLayoutItem)).type;
|
|
131
|
+
const isGridLayoutItem = (element)=>element.type === GridLayoutItem_GridLayoutItem;
|
|
132
|
+
GridLayoutItem_GridLayoutItem.toJSON = (element)=>{
|
|
133
|
+
let { children } = element.props;
|
|
134
|
+
if (Array.isArray(children)) {
|
|
135
|
+
if (children.length > 1) throw Error("[GridLayoutItem] cannot have more than one child element");
|
|
136
|
+
[children] = children;
|
|
137
|
+
}
|
|
138
|
+
if (/*#__PURE__*/ isValidElement(children)) {
|
|
139
|
+
const child = componentToJson(children);
|
|
140
|
+
return {
|
|
141
|
+
...child
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
throw Error("[GridLayoutItem] children is not a react element");
|
|
145
|
+
};
|
|
146
|
+
registerComponent("GridLayoutItem", GridLayoutItem_GridLayoutItem, "component");
|
|
147
|
+
export { GridLayoutItem_GridLayoutItem as GridLayoutItem, isGridLayoutItem };
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { createElement } from "react";
|
|
2
|
+
import { gridLayoutDescriptorToSnapshot } from "./grid-snapshot-adapters.js";
|
|
3
|
+
import { GridLayoutItem } from "./GridLayoutItem.js";
|
|
4
|
+
import { layoutFromJson } from "./layoutFromJson.js";
|
|
5
|
+
import { toJsonValue } from "./json-value.js";
|
|
6
|
+
const decodeLegacySerializedGridLayout = (value)=>{
|
|
7
|
+
const json = toJsonValue(value);
|
|
8
|
+
if (!json.ok) return {
|
|
9
|
+
error: {
|
|
10
|
+
code: "INVALID_LEGACY_GRID_LAYOUT",
|
|
11
|
+
message: json.error.message,
|
|
12
|
+
path: json.error.path,
|
|
13
|
+
version: 1
|
|
14
|
+
},
|
|
15
|
+
ok: false
|
|
16
|
+
};
|
|
17
|
+
try {
|
|
18
|
+
return {
|
|
19
|
+
ok: true,
|
|
20
|
+
value: {
|
|
21
|
+
components: value.components,
|
|
22
|
+
snapshot: gridLayoutDescriptorToSnapshot(value.layout, {
|
|
23
|
+
gridId: value.id
|
|
24
|
+
})
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
} catch (cause) {
|
|
28
|
+
return {
|
|
29
|
+
error: {
|
|
30
|
+
code: "INVALID_LEGACY_GRID_LAYOUT",
|
|
31
|
+
message: cause instanceof Error ? cause.message : "invalid legacy GridLayout",
|
|
32
|
+
path: "$.layout",
|
|
33
|
+
version: 1
|
|
34
|
+
},
|
|
35
|
+
ok: false
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
const createLegacyGridLayoutReader = (serializedLayout)=>{
|
|
40
|
+
const decoded = decodeLegacySerializedGridLayout(serializedLayout);
|
|
41
|
+
if (!decoded.ok) throw new Error(`${decoded.error.path}: ${decoded.error.message}`);
|
|
42
|
+
return {
|
|
43
|
+
getSavedGrid (id) {
|
|
44
|
+
if (id !== serializedLayout.id) return;
|
|
45
|
+
return {
|
|
46
|
+
components: Object.entries(serializedLayout.components).reduce((components, [itemId, component])=>{
|
|
47
|
+
const item = serializedLayout.layout.gridLayoutItems?.[itemId];
|
|
48
|
+
if (!item) throw new Error(`$.layout.gridLayoutItems.${itemId}: legacy component has no matching layout item`);
|
|
49
|
+
const { gridArea, ...props } = item;
|
|
50
|
+
components[itemId] = /*#__PURE__*/ createElement(GridLayoutItem, {
|
|
51
|
+
...props,
|
|
52
|
+
id: itemId,
|
|
53
|
+
key: itemId,
|
|
54
|
+
style: {
|
|
55
|
+
gridArea
|
|
56
|
+
}
|
|
57
|
+
}, layoutFromJson(component));
|
|
58
|
+
return components;
|
|
59
|
+
}, {}),
|
|
60
|
+
id,
|
|
61
|
+
layout: serializedLayout.layout
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
};
|
|
66
|
+
export { createLegacyGridLayoutReader, decodeLegacySerializedGridLayout };
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { EventEmitter, uuid } from "@vuu-ui/vuu-utils";
|
|
2
|
+
import { canSplitGridItem, doesResizeRequireNewTrack, gridResizeDirectionForSplit, insertTrackForResize, removeGridItem, replaceGridItem, shiftItemsForRemovedTrack, splitGridItem } from "./GridGeometry.js";
|
|
3
|
+
import { toGridItemUpdates } from "./GridModel.js";
|
|
4
|
+
class GridLayoutModel extends EventEmitter {
|
|
5
|
+
gridModel;
|
|
6
|
+
splitters;
|
|
7
|
+
constructor(gridModel){
|
|
8
|
+
super(), this.gridModel = gridModel;
|
|
9
|
+
}
|
|
10
|
+
applyRemoveTransition(transition) {
|
|
11
|
+
const [removal] = transition.removals;
|
|
12
|
+
const gridItem = this.gridModel.getChildItem(removal.id, true);
|
|
13
|
+
const stackId = gridItem.stackId;
|
|
14
|
+
this.gridModel.removeChildItem(removal.id, removal.reason);
|
|
15
|
+
if (transition.stackMember && stackId) {
|
|
16
|
+
const result = this.gridModel.removeStackItem(stackId, gridItem.id);
|
|
17
|
+
if (!result.ok) throw Error(result.error.message);
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
this.gridModel.applyGeometryPositions(transition.geometry);
|
|
21
|
+
if (transition.columns) this.gridModel.tracks.applyTrackTransition("column", transition.columns);
|
|
22
|
+
if (transition.rows) this.gridModel.tracks.applyTrackTransition("row", transition.rows);
|
|
23
|
+
if (transition.notify) this.emit("child-position-updates", toGridItemUpdates(transition.updates), {
|
|
24
|
+
placeholders: true,
|
|
25
|
+
splitters: true
|
|
26
|
+
});
|
|
27
|
+
if (transition.placeholders) this.gridModel.applyPlaceholderTransition(transition.placeholders);
|
|
28
|
+
}
|
|
29
|
+
removeGridItem(gridItemId, reason) {
|
|
30
|
+
const geometry = this.gridModel.toGeometry();
|
|
31
|
+
const result = this.gridModel.runGeometry((measurements)=>removeGridItem(geometry, {
|
|
32
|
+
itemId: gridItemId,
|
|
33
|
+
reason
|
|
34
|
+
}, {
|
|
35
|
+
createPlaceholderId: uuid,
|
|
36
|
+
measurements
|
|
37
|
+
}));
|
|
38
|
+
if (!result.ok) throw Error(result.error.message);
|
|
39
|
+
this.applyRemoveTransition(result.value);
|
|
40
|
+
}
|
|
41
|
+
applyReplaceTransition(transition) {
|
|
42
|
+
for (const { id, reason } of transition.removals)this.gridModel.removeChildItem(id, reason);
|
|
43
|
+
this.gridModel.applyGeometryPositions(transition.geometry);
|
|
44
|
+
}
|
|
45
|
+
dropReplaceGridItem(droppedItemId, targetItemId) {
|
|
46
|
+
const droppedGridItem = this.gridModel.getChildItem(droppedItemId, true);
|
|
47
|
+
const result = replaceGridItem(this.gridModel.toGeometry(), {
|
|
48
|
+
droppedItemId,
|
|
49
|
+
targetItemId
|
|
50
|
+
});
|
|
51
|
+
if (!result.ok) throw Error(result.error.message);
|
|
52
|
+
this.applyReplaceTransition(result.value);
|
|
53
|
+
return droppedGridItem;
|
|
54
|
+
}
|
|
55
|
+
createSplitters() {
|
|
56
|
+
return this.splitters = this.gridModel.getSplitters();
|
|
57
|
+
}
|
|
58
|
+
getSplitterById(splitterId) {
|
|
59
|
+
const splitter = this.splitters?.find(({ id })=>id === splitterId);
|
|
60
|
+
if (splitter) return splitter;
|
|
61
|
+
throw Error(`[GridLayoutModel] getSplitterId #${splitterId}`);
|
|
62
|
+
}
|
|
63
|
+
getSplitter(gridLayoutItem, resizeDirection) {
|
|
64
|
+
const splitter = this.splitters?.find(({ controls, orientation })=>controls === gridLayoutItem.id && orientation === resizeDirection);
|
|
65
|
+
if (splitter) return splitter;
|
|
66
|
+
throw Error(`no splitter for gridItem ${gridLayoutItem.id} (${resizeDirection})`);
|
|
67
|
+
}
|
|
68
|
+
applyInsertTrackTransition(trackType, transition) {
|
|
69
|
+
const trackTransition = "column" === trackType ? transition.columns : transition.rows;
|
|
70
|
+
if (trackTransition) this.gridModel.tracks.applyTrackTransition(trackType, trackTransition);
|
|
71
|
+
this.gridModel.applyUpdates(toGridItemUpdates(transition.insertUpdates));
|
|
72
|
+
this.gridModel.emit("child-position-updates", toGridItemUpdates(transition.insertUpdates), {
|
|
73
|
+
splitters: true
|
|
74
|
+
});
|
|
75
|
+
this.gridModel.applyGeometryPositions(transition.geometry);
|
|
76
|
+
this.emit("child-position-updates", toGridItemUpdates(transition.anchorUpdates), {
|
|
77
|
+
splitters: true
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
addTrackForResize(trackType, newTrackSize, resizeOperation, index, state) {
|
|
81
|
+
const { splitter } = state;
|
|
82
|
+
const { before: contraItemIds, after: resizeItemIds } = splitter.resizedChildItems;
|
|
83
|
+
const position = "expand" === resizeOperation ? "before" : "after";
|
|
84
|
+
const geometry = this.gridModel.toGeometry();
|
|
85
|
+
const result = this.gridModel.runGeometry((measurements)=>insertTrackForResize(geometry, {
|
|
86
|
+
contraItemIds,
|
|
87
|
+
index,
|
|
88
|
+
position,
|
|
89
|
+
resizeItemIds,
|
|
90
|
+
size: newTrackSize,
|
|
91
|
+
trackType
|
|
92
|
+
}, measurements));
|
|
93
|
+
if (!result.ok) throw Error(result.error.message);
|
|
94
|
+
this.applyInsertTrackTransition(trackType, result.value);
|
|
95
|
+
}
|
|
96
|
+
applySplitTransition(transition) {
|
|
97
|
+
if (transition.columns) this.gridModel.tracks.applyTrackTransition("column", transition.columns);
|
|
98
|
+
if (transition.rows) this.gridModel.tracks.applyTrackTransition("row", transition.rows);
|
|
99
|
+
this.gridModel.applyGeometryPositions(transition.geometry);
|
|
100
|
+
this.emit("child-position-updates", toGridItemUpdates(transition.updates), {
|
|
101
|
+
splitters: true
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
dropSplitGridItem(droppedItemId, targetItemId, splitDirection, resizeDirection = gridResizeDirectionForSplit(splitDirection)) {
|
|
105
|
+
const geometry = this.gridModel.toGeometry();
|
|
106
|
+
const result = this.gridModel.runGeometry((measurements)=>splitGridItem(geometry, {
|
|
107
|
+
droppedItemId,
|
|
108
|
+
resizeDirection,
|
|
109
|
+
splitDirection,
|
|
110
|
+
targetItemId
|
|
111
|
+
}, measurements));
|
|
112
|
+
if (!result.ok) {
|
|
113
|
+
if ("NON_RESIZABLE" === result.error.code) return false;
|
|
114
|
+
throw Error(result.error.message);
|
|
115
|
+
}
|
|
116
|
+
this.applySplitTransition(result.value);
|
|
117
|
+
return true;
|
|
118
|
+
}
|
|
119
|
+
canSplitGridItem(targetItemId, splitDirection) {
|
|
120
|
+
return canSplitGridItem(this.gridModel.getChildItem(targetItemId, true), splitDirection);
|
|
121
|
+
}
|
|
122
|
+
removeTrack(trackIndex, resizeDirection) {
|
|
123
|
+
const trackType = "horizontal" === resizeDirection ? "column" : "row";
|
|
124
|
+
const updates = toGridItemUpdates(shiftItemsForRemovedTrack(this.gridModel.toGeometry().items, trackType, trackIndex));
|
|
125
|
+
this.gridModel.applyUpdates(updates);
|
|
126
|
+
return updates;
|
|
127
|
+
}
|
|
128
|
+
isResizeTrackShared(splitter) {
|
|
129
|
+
if (this.splitters) return doesResizeRequireNewTrack(this.splitters, splitter);
|
|
130
|
+
throw Error("[GridLayoutModel] isResizeTrackShared, no splitters created");
|
|
131
|
+
}
|
|
132
|
+
toDebugString() {
|
|
133
|
+
return `
|
|
134
|
+
${this.gridModel.childItems.map(({ id, column, resizeable = "", row })=>`\n${id}\t\tcol ${column.start}/${column.end}\t row ${row.start}/${row.end}\t${resizeable}`).join("")}
|
|
135
|
+
`;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
export { GridLayoutModel };
|