@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.
Files changed (142) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +141 -0
  3. package/package.json +40 -0
  4. package/src/GridCommand.js +395 -0
  5. package/src/GridComponentSettings.js +163 -0
  6. package/src/GridController.js +359 -0
  7. package/src/GridDragCoordinator.js +293 -0
  8. package/src/GridGeometry.js +1298 -0
  9. package/src/GridLayout.css.js +119 -0
  10. package/src/GridLayout.js +120 -0
  11. package/src/GridLayoutContext.js +58 -0
  12. package/src/GridLayoutDocument.js +307 -0
  13. package/src/GridLayoutDocumentIds.js +110 -0
  14. package/src/GridLayoutItem.css.js +46 -0
  15. package/src/GridLayoutItem.js +147 -0
  16. package/src/GridLayoutLegacyCompatibility.js +66 -0
  17. package/src/GridLayoutModel.js +138 -0
  18. package/src/GridLayoutProvider.js +263 -0
  19. package/src/GridLayoutStackedItem.css.js +26 -0
  20. package/src/GridLayoutStackedtem.js +140 -0
  21. package/src/GridModel.js +1289 -0
  22. package/src/GridPlaceholder.css.js +9 -0
  23. package/src/GridPlaceholder.js +24 -0
  24. package/src/GridSnapshot.js +11 -0
  25. package/src/GridSplitter.css.js +62 -0
  26. package/src/GridSplitter.js +26 -0
  27. package/src/GridStack.js +277 -0
  28. package/src/Icon.css.js +8 -0
  29. package/src/Icon.js +26 -0
  30. package/src/IconButton.css.js +9 -0
  31. package/src/IconButton.js +28 -0
  32. package/src/TabDialog.css.js +10 -0
  33. package/src/TabDialog.js +77 -0
  34. package/src/TabMenu.css.js +8 -0
  35. package/src/TabMenu.js +78 -0
  36. package/src/componentToJson.js +41 -0
  37. package/src/drag-drop-next/DragContextNext.js +173 -0
  38. package/src/drag-drop-next/DragDropProviderNext.css.js +72 -0
  39. package/src/drag-drop-next/DragDropProviderNext.js +91 -0
  40. package/src/drag-drop-next/SpaceMan.js +256 -0
  41. package/src/drag-drop-next/TemplateDragSession.js +36 -0
  42. package/src/drag-drop-next/tabstrip-drag-drop.js +251 -0
  43. package/src/grid-dom-utils.js +57 -0
  44. package/src/grid-layout-utils.js +39 -0
  45. package/src/grid-snapshot-adapters.js +435 -0
  46. package/src/index.js +24 -0
  47. package/src/json-value.js +56 -0
  48. package/src/layoutFromJson.js +15 -0
  49. package/src/layoutToJson.js +5 -0
  50. package/src/propUtils.js +15 -0
  51. package/src/react-element-utils.js +15 -0
  52. package/src/typeOf.js +15 -0
  53. package/src/useAsDropTarget.js +229 -0
  54. package/src/useDraggable.js +46 -0
  55. package/src/useEditTabName.js +70 -0
  56. package/src/useGridChildProps.js +47 -0
  57. package/src/useGridControllerSnapshot.js +21 -0
  58. package/src/useGridLayout.js +727 -0
  59. package/src/useGridSplitterResizing.js +262 -0
  60. package/src/useNotDropTarget.js +2 -0
  61. package/types/AddTabDialog.d.ts +5 -0
  62. package/types/GridCommand.d.ts +146 -0
  63. package/types/GridComponentSettings.d.ts +71 -0
  64. package/types/GridController.d.ts +50 -0
  65. package/types/GridDragCoordinator.d.ts +80 -0
  66. package/types/GridGeometry.d.ts +331 -0
  67. package/types/GridLayout.d.ts +20 -0
  68. package/types/GridLayoutContext.d.ts +141 -0
  69. package/types/GridLayoutDocument.d.ts +61 -0
  70. package/types/GridLayoutDocumentIds.d.ts +15 -0
  71. package/types/GridLayoutItem.d.ts +28 -0
  72. package/types/GridLayoutLegacyCompatibility.d.ts +57 -0
  73. package/types/GridLayoutModel.d.ts +72 -0
  74. package/types/GridLayoutProvider.d.ts +59 -0
  75. package/types/GridLayoutStackedtem.d.ts +10 -0
  76. package/types/GridModel.d.ts +504 -0
  77. package/types/GridPlaceholder.d.ts +5 -0
  78. package/types/GridSnapshot.d.ts +49 -0
  79. package/types/GridSplitter.d.ts +10 -0
  80. package/types/GridStack.d.ts +160 -0
  81. package/types/Icon.d.ts +6 -0
  82. package/types/IconButton.d.ts +6 -0
  83. package/types/TabDialog.d.ts +9 -0
  84. package/types/TabMenu.d.ts +7 -0
  85. package/types/componentToJson.d.ts +38 -0
  86. package/types/drag-drop-next/DragContextNext.d.ts +92 -0
  87. package/types/drag-drop-next/DragDropProviderNext.d.ts +23 -0
  88. package/types/drag-drop-next/SpaceMan.d.ts +35 -0
  89. package/types/drag-drop-next/TemplateDragSession.d.ts +21 -0
  90. package/types/drag-drop-next/tabstrip-drag-drop.d.ts +3 -0
  91. package/types/grid-dom-utils.d.ts +17 -0
  92. package/types/grid-layout-utils.d.ts +18 -0
  93. package/types/grid-matrix.d.ts +5 -0
  94. package/types/grid-snapshot-adapters.d.ts +22 -0
  95. package/types/index.d.ts +24 -0
  96. package/types/json-value.d.ts +23 -0
  97. package/types/layoutFromJson.d.ts +4 -0
  98. package/types/layoutToJson.d.ts +3 -0
  99. package/types/propUtils.d.ts +5 -0
  100. package/types/react-element-utils.d.ts +3 -0
  101. package/types/src/AddTabDialog.d.ts +5 -0
  102. package/types/src/GridLayout.d.ts +14 -0
  103. package/types/src/GridLayoutContext.d.ts +88 -0
  104. package/types/src/GridLayoutItem.d.ts +28 -0
  105. package/types/src/GridLayoutModel.d.ts +59 -0
  106. package/types/src/GridLayoutProvider.d.ts +37 -0
  107. package/types/src/GridLayoutStackedtem.d.ts +10 -0
  108. package/types/src/GridModel.d.ts +350 -0
  109. package/types/src/GridPlaceholder.d.ts +5 -0
  110. package/types/src/GridSplitter.d.ts +9 -0
  111. package/types/src/Icon.d.ts +6 -0
  112. package/types/src/IconButton.d.ts +6 -0
  113. package/types/src/TabMenu.d.ts +6 -0
  114. package/types/src/componentToJson.d.ts +32 -0
  115. package/types/src/drag-drop-next/DragContextNext.d.ts +73 -0
  116. package/types/src/drag-drop-next/DragDropProviderNext.d.ts +20 -0
  117. package/types/src/drag-drop-next/SpaceMan.d.ts +35 -0
  118. package/types/src/drag-drop-next/tabstrip-drag-drop.d.ts +3 -0
  119. package/types/src/grid-dom-utils.d.ts +17 -0
  120. package/types/src/grid-layout-utils.d.ts +80 -0
  121. package/types/src/grid-matrix.d.ts +5 -0
  122. package/types/src/index.d.ts +10 -0
  123. package/types/src/layoutFromJson.d.ts +3 -0
  124. package/types/src/layoutToJson.d.ts +2 -0
  125. package/types/src/propUtils.d.ts +5 -0
  126. package/types/src/react-element-utils.d.ts +3 -0
  127. package/types/src/typeOf.d.ts +6 -0
  128. package/types/src/useAsDropTarget.d.ts +7 -0
  129. package/types/src/useDraggable.d.ts +23 -0
  130. package/types/src/useGridChildProps.d.ts +13 -0
  131. package/types/src/useGridLayout.d.ts +43 -0
  132. package/types/src/useGridSplitterResizing.d.ts +12 -0
  133. package/types/src/useNotDropTarget.d.ts +1 -0
  134. package/types/typeOf.d.ts +6 -0
  135. package/types/useAsDropTarget.d.ts +7 -0
  136. package/types/useDraggable.d.ts +24 -0
  137. package/types/useEditTabName.d.ts +11 -0
  138. package/types/useGridChildProps.d.ts +13 -0
  139. package/types/useGridControllerSnapshot.d.ts +7 -0
  140. package/types/useGridLayout.d.ts +53 -0
  141. package/types/useGridSplitterResizing.d.ts +14 -0
  142. 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 };