@vuu-ui/vuu-layout 0.0.27

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 (117) hide show
  1. package/README.md +1 -0
  2. package/package.json +30 -0
  3. package/src/Component.css +2 -0
  4. package/src/Component.tsx +20 -0
  5. package/src/DraggableLayout.css +18 -0
  6. package/src/DraggableLayout.tsx +29 -0
  7. package/src/__tests__/flexbox-utils.spec.js +90 -0
  8. package/src/action-buttons/action-buttons.css +12 -0
  9. package/src/action-buttons/action-buttons.tsx +30 -0
  10. package/src/action-buttons/index.ts +1 -0
  11. package/src/chest-of-drawers/Chest.css +36 -0
  12. package/src/chest-of-drawers/Chest.tsx +42 -0
  13. package/src/chest-of-drawers/Drawer.css +153 -0
  14. package/src/chest-of-drawers/Drawer.tsx +118 -0
  15. package/src/chest-of-drawers/index.ts +2 -0
  16. package/src/common-types.ts +9 -0
  17. package/src/debug.ts +16 -0
  18. package/src/dialog/Dialog.css +16 -0
  19. package/src/dialog/Dialog.tsx +59 -0
  20. package/src/dialog/index.ts +1 -0
  21. package/src/drag-drop/BoxModel.ts +546 -0
  22. package/src/drag-drop/DragState.ts +222 -0
  23. package/src/drag-drop/Draggable.ts +282 -0
  24. package/src/drag-drop/DropMenu.css +70 -0
  25. package/src/drag-drop/DropMenu.tsx +68 -0
  26. package/src/drag-drop/DropTarget.ts +392 -0
  27. package/src/drag-drop/DropTargetRenderer.css +40 -0
  28. package/src/drag-drop/DropTargetRenderer.tsx +284 -0
  29. package/src/drag-drop/dragDropTypes.ts +49 -0
  30. package/src/drag-drop/index.ts +4 -0
  31. package/src/editable-label/EditableLabel.css +28 -0
  32. package/src/editable-label/EditableLabel.tsx +99 -0
  33. package/src/editable-label/index.ts +1 -0
  34. package/src/flexbox/Flexbox.css +45 -0
  35. package/src/flexbox/Flexbox.tsx +70 -0
  36. package/src/flexbox/FlexboxLayout.jsx +26 -0
  37. package/src/flexbox/FluidGrid.css +134 -0
  38. package/src/flexbox/FluidGrid.tsx +84 -0
  39. package/src/flexbox/FluidGridLayout.tsx +10 -0
  40. package/src/flexbox/Splitter.css +140 -0
  41. package/src/flexbox/Splitter.tsx +135 -0
  42. package/src/flexbox/flexbox-utils.ts +128 -0
  43. package/src/flexbox/flexboxTypes.ts +63 -0
  44. package/src/flexbox/index.ts +4 -0
  45. package/src/flexbox/useResponsiveSizing.ts +85 -0
  46. package/src/flexbox/useSplitterResizing.ts +272 -0
  47. package/src/index.ts +20 -0
  48. package/src/layout-action.ts +21 -0
  49. package/src/layout-header/ActionButton.tsx +23 -0
  50. package/src/layout-header/Header.css +8 -0
  51. package/src/layout-header/Header.tsx +222 -0
  52. package/src/layout-header/index.ts +1 -0
  53. package/src/layout-provider/LayoutProvider.tsx +160 -0
  54. package/src/layout-provider/LayoutProviderContext.ts +17 -0
  55. package/src/layout-provider/index.ts +2 -0
  56. package/src/layout-provider/useLayoutDragDrop.ts +241 -0
  57. package/src/layout-reducer/flexUtils.ts +281 -0
  58. package/src/layout-reducer/index.ts +4 -0
  59. package/src/layout-reducer/insert-layout-element.ts +365 -0
  60. package/src/layout-reducer/layout-reducer.ts +255 -0
  61. package/src/layout-reducer/layoutTypes.ts +151 -0
  62. package/src/layout-reducer/layoutUtils.ts +302 -0
  63. package/src/layout-reducer/remove-layout-element.ts +240 -0
  64. package/src/layout-reducer/replace-layout-element.ts +118 -0
  65. package/src/layout-reducer/resize-flex-children.ts +56 -0
  66. package/src/layout-reducer/wrap-layout-element.ts +317 -0
  67. package/src/layout-view/View.css +58 -0
  68. package/src/layout-view/View.tsx +149 -0
  69. package/src/layout-view/ViewContext.ts +31 -0
  70. package/src/layout-view/index.ts +4 -0
  71. package/src/layout-view/useView.tsx +104 -0
  72. package/src/layout-view/useViewActionDispatcher.ts +133 -0
  73. package/src/layout-view/useViewResize.ts +53 -0
  74. package/src/layout-view/viewTypes.ts +37 -0
  75. package/src/palette/Palette.css +37 -0
  76. package/src/palette/Palette.tsx +140 -0
  77. package/src/palette/PaletteUitk.css +9 -0
  78. package/src/palette/PaletteUitk.tsx +79 -0
  79. package/src/palette/index.ts +2 -0
  80. package/src/placeholder/Placeholder.css +10 -0
  81. package/src/placeholder/Placeholder.tsx +39 -0
  82. package/src/placeholder/index.ts +1 -0
  83. package/src/registry/ComponentRegistry.ts +35 -0
  84. package/src/registry/index.ts +1 -0
  85. package/src/responsive/OverflowMenu.css +31 -0
  86. package/src/responsive/OverflowMenu.jsx +56 -0
  87. package/src/responsive/breakpoints.ts +48 -0
  88. package/src/responsive/index.ts +4 -0
  89. package/src/responsive/measureMinimumNodeSize.ts +23 -0
  90. package/src/responsive/overflowUtils.js +14 -0
  91. package/src/responsive/use-breakpoints.ts +100 -0
  92. package/src/responsive/useOverflowObserver.ts +606 -0
  93. package/src/responsive/useResizeObserver.ts +154 -0
  94. package/src/responsive/utils.ts +37 -0
  95. package/src/stack/Stack.css +39 -0
  96. package/src/stack/Stack.tsx +160 -0
  97. package/src/stack/StackLayout.tsx +137 -0
  98. package/src/stack/index.ts +3 -0
  99. package/src/stack/stackTypes.ts +19 -0
  100. package/src/tabs/TabPanel.css +12 -0
  101. package/src/tabs/TabPanel.tsx +17 -0
  102. package/src/tabs/index.ts +1 -0
  103. package/src/tools/config-wrapper/ConfigWrapper.jsx +53 -0
  104. package/src/tools/config-wrapper/index.js +1 -0
  105. package/src/tools/devtools-box/layout-configurator.css +112 -0
  106. package/src/tools/devtools-box/layout-configurator.jsx +369 -0
  107. package/src/tools/devtools-tree/layout-tree-viewer.css +15 -0
  108. package/src/tools/devtools-tree/layout-tree-viewer.jsx +36 -0
  109. package/src/tools/index.js +3 -0
  110. package/src/use-persistent-state.ts +115 -0
  111. package/src/utils/componentFromLayout.tsx +30 -0
  112. package/src/utils/index.ts +6 -0
  113. package/src/utils/pathUtils.ts +294 -0
  114. package/src/utils/propUtils.ts +24 -0
  115. package/src/utils/refUtils.ts +16 -0
  116. package/src/utils/styleUtils.ts +14 -0
  117. package/src/utils/typeOf.ts +22 -0
@@ -0,0 +1,281 @@
1
+ import React from "react";
2
+ import { CSSProperties, ReactElement, ReactNode } from "react";
3
+ import { uuid } from "@vuu-ui/vuu-utils";
4
+ import { ComponentRegistry } from "../registry/ComponentRegistry";
5
+ import { getProps, resetPath } from "../utils";
6
+ import { dimension, rect, rectTuple } from "../common-types";
7
+ import { DropPos } from "../drag-drop/dragDropTypes";
8
+ const placeHolderProps = { "data-placeholder": true, "data-resizeable": true };
9
+
10
+ const NO_STYLE = {};
11
+ const auto = "auto";
12
+ const defaultFlexStyle = {
13
+ flexBasis: 0,
14
+ flexGrow: 1,
15
+ flexShrink: 1,
16
+ height: auto,
17
+ width: auto,
18
+ };
19
+
20
+ const CROSS_DIMENSION = {
21
+ height: "width",
22
+ width: "height",
23
+ };
24
+
25
+ export type flexDirection = "row" | "column";
26
+
27
+ type contraDimension = dimension;
28
+ type flexDimensionTuple = [dimension, contraDimension, flexDirection];
29
+ export type position = {
30
+ height?: number;
31
+ width?: number;
32
+ };
33
+
34
+ export const getFlexDimensions = (flexDirection: flexDirection = "row") => {
35
+ if (flexDirection === "row") {
36
+ return ["width", "height", "column"] as flexDimensionTuple;
37
+ } else {
38
+ return ["height", "width", "row"] as flexDimensionTuple;
39
+ }
40
+ };
41
+
42
+ const isPercentageSize = (value: string | number) =>
43
+ typeof value === "string" && value.endsWith("%");
44
+
45
+ export const getIntrinsicSize = (
46
+ component: ReactElement
47
+ ): { height?: number; width?: number } | undefined => {
48
+ const { style: { width = auto, height = auto } = NO_STYLE } = component.props;
49
+
50
+ // Eliminate 'auto' and percentage sizes
51
+ const numHeight = typeof height === "number";
52
+ const numWidth = typeof width === "number";
53
+
54
+ if (numHeight && numWidth) {
55
+ return { height, width };
56
+ } else if (numHeight) {
57
+ return { height };
58
+ } else if (numWidth) {
59
+ return { width };
60
+ } else {
61
+ return undefined;
62
+ }
63
+ };
64
+
65
+ export function getFlexStyle(
66
+ component: ReactElement,
67
+ dimension: dimension,
68
+ pos?: DropPos
69
+ ) {
70
+ const crossDimension = CROSS_DIMENSION[dimension];
71
+ const {
72
+ style: {
73
+ [crossDimension]: intrinsicCrossSize = auto,
74
+ ...intrinsicStyles
75
+ } = NO_STYLE,
76
+ } = component.props;
77
+
78
+ if (pos && pos[dimension]) {
79
+ return {
80
+ ...intrinsicStyles,
81
+ ...defaultFlexStyle,
82
+ flexBasis: pos[dimension],
83
+ flexGrow: 0,
84
+ flexShrink: 0,
85
+ };
86
+ } else {
87
+ return {
88
+ ...intrinsicStyles,
89
+ ...defaultFlexStyle,
90
+ [crossDimension]: intrinsicCrossSize,
91
+ };
92
+ }
93
+ }
94
+
95
+ //TODO this is not comprehensive
96
+ export function hasUnboundedFlexStyle(component: ReactElement) {
97
+ const { style: { flex, flexGrow, flexShrink, flexBasis } = NO_STYLE } =
98
+ component.props;
99
+ // console.log(`flex ${flex}, flexBasis ${flexBasis}, flexShrink ${flexShrink}, flexGrow ${flexGrow}`)
100
+ if (typeof flex === "number") {
101
+ return true;
102
+ } else if (flexBasis === 0 && flexGrow === 1 && flexShrink === 1) {
103
+ return true;
104
+ } else if (typeof flexBasis === "number") {
105
+ return false;
106
+ } else {
107
+ return true;
108
+ }
109
+ }
110
+
111
+ export function getFlexOrIntrinsicStyle(
112
+ component: ReactElement,
113
+ dimension: dimension,
114
+ pos: position
115
+ ) {
116
+ const crossDimension = CROSS_DIMENSION[dimension];
117
+ const {
118
+ style: {
119
+ [dimension]: intrinsicSize = auto,
120
+ [crossDimension]: intrinsicCrossSize = auto,
121
+ ...intrinsicStyles
122
+ } = NO_STYLE,
123
+ } = component.props;
124
+
125
+ if (intrinsicSize !== auto) {
126
+ if (isPercentageSize(intrinsicSize)) {
127
+ return {
128
+ // Is this right? discrad the percenbtage size ?
129
+ flexBasis: 0,
130
+ flexGrow: 1,
131
+ flexShrink: 1,
132
+ [dimension]: undefined,
133
+ [crossDimension]: intrinsicCrossSize,
134
+ };
135
+ } else {
136
+ return {
137
+ // or should we leave this as auto until user resizes ?
138
+ flexBasis: intrinsicSize,
139
+ flexGrow: 0,
140
+ flexShrink: 0,
141
+ [dimension]: intrinsicSize,
142
+ [crossDimension]: intrinsicCrossSize,
143
+ };
144
+ }
145
+ } else if (pos && pos[dimension]) {
146
+ return {
147
+ ...intrinsicStyles,
148
+ ...defaultFlexStyle,
149
+ flexBasis: pos[dimension],
150
+ flexGrow: 0,
151
+ flexShrink: 0,
152
+ };
153
+ } else {
154
+ return {
155
+ ...intrinsicStyles,
156
+ // ...defaultFlexStyle,
157
+ [crossDimension]: intrinsicCrossSize,
158
+ };
159
+ }
160
+ }
161
+
162
+ export function wrapIntrinsicSizeComponentWithFlexbox(
163
+ component: ReactElement,
164
+ flexDirection: flexDirection,
165
+ path: string,
166
+ clientRect?: rect,
167
+ dropRect?: rectTuple
168
+ ) {
169
+ const wrappedChildren = [];
170
+ let pathIndex = 0;
171
+ let endPlaceholder;
172
+
173
+ if (clientRect && dropRect) {
174
+ let startPlaceholder;
175
+ const [dropLeft, dropTop, dropRight, dropBottom] = dropRect;
176
+ [startPlaceholder, endPlaceholder] =
177
+ flexDirection === "column"
178
+ ? [dropTop - clientRect.top, clientRect.bottom - dropBottom]
179
+ : [dropLeft - clientRect.left, clientRect.right - dropRight];
180
+
181
+ if (startPlaceholder) {
182
+ wrappedChildren.push(
183
+ createPlaceHolder(`${path}.${pathIndex++}`, startPlaceholder, {
184
+ flexGrow: 0,
185
+ flexShrink: 0,
186
+ })
187
+ );
188
+ }
189
+ } else {
190
+ // If we don't pass the rect values, we are wrapping an existing child, this is always a trailing placeholder
191
+ endPlaceholder = true;
192
+ }
193
+
194
+ const { version = 0, style } = getProps(component);
195
+
196
+ wrappedChildren.push(
197
+ resetPath(component, `${path}.${pathIndex++}`, {
198
+ version: version + 1,
199
+ style: {
200
+ ...style,
201
+ flexBasis: "auto",
202
+ flexGrow: 0,
203
+ flexShrink: 0,
204
+ },
205
+ })
206
+ );
207
+
208
+ if (endPlaceholder) {
209
+ wrappedChildren.push(
210
+ createPlaceHolder(`${path}.${pathIndex++}`, 0, undefined, {
211
+ [`data-${flexDirection}-placeholder`]: true,
212
+ })
213
+ );
214
+ }
215
+
216
+ return createFlexbox(
217
+ flexDirection,
218
+ { resizeable: false, style: { flexBasis: "auto" } },
219
+ wrappedChildren,
220
+ path
221
+ );
222
+ }
223
+
224
+ const getFlexValue = (flexBasis: number, flexFill: boolean) => {
225
+ if (flexFill) {
226
+ return undefined;
227
+ } else if (flexBasis === 0) {
228
+ return 1;
229
+ } else {
230
+ return 0;
231
+ }
232
+ };
233
+
234
+ export function createFlexbox(
235
+ flexDirection: flexDirection,
236
+ props: any,
237
+ children: ReactNode,
238
+ path: string
239
+ ) {
240
+ const id = uuid();
241
+ const { flexFill, style, resizeable = true } = props;
242
+ const { flexBasis = flexFill ? undefined : "auto" } = style;
243
+ const flex = getFlexValue(flexBasis, flexFill);
244
+ return React.createElement<any>(
245
+ ComponentRegistry.Flexbox,
246
+ {
247
+ id,
248
+ key: id,
249
+ path,
250
+ flexFill,
251
+ style: {
252
+ ...style,
253
+ flexDirection,
254
+ flexBasis,
255
+ flexGrow: flex,
256
+ flexShrink: flex,
257
+ },
258
+ resizeable,
259
+ },
260
+ children
261
+ );
262
+ }
263
+
264
+ const baseStyle = { flexGrow: 1, flexShrink: 1 };
265
+
266
+ export function createPlaceHolder(
267
+ path: string,
268
+ size: number,
269
+ style?: CSSProperties,
270
+ props?: any
271
+ ) {
272
+ const id = uuid();
273
+ return React.createElement("div", {
274
+ ...placeHolderProps,
275
+ ...props,
276
+ "data-path": path,
277
+ id,
278
+ key: id,
279
+ style: { ...baseStyle, ...style, flexBasis: size },
280
+ });
281
+ }
@@ -0,0 +1,4 @@
1
+ export * from './layout-reducer';
2
+ export * from './layoutTypes';
3
+ export * from './layoutUtils';
4
+ export * from './flexUtils';
@@ -0,0 +1,365 @@
1
+ import React, { ReactElement } from "react";
2
+ import { uuid } from "@vuu-ui/vuu-utils";
3
+ import { getManagedDimension, LayoutProps } from "./layoutUtils";
4
+ import { getProp, getProps, nextStep, resetPath, typeOf } from "../utils";
5
+ import {
6
+ createPlaceHolder,
7
+ flexDirection,
8
+ getFlexDimensions,
9
+ getFlexOrIntrinsicStyle,
10
+ getIntrinsicSize,
11
+ wrapIntrinsicSizeComponentWithFlexbox,
12
+ } from "./flexUtils";
13
+ import { LayoutModel, LayoutRoot } from "./layoutTypes";
14
+ import { DropPos } from "../drag-drop";
15
+ import { DropTarget } from "../drag-drop/DropTarget";
16
+ import { rectTuple } from "../common-types";
17
+
18
+ type insertionPosition = "before" | "after";
19
+
20
+ export function getInsertTabBeforeAfter(stack: LayoutModel, pos: DropPos) {
21
+ const tabs = stack.props.children;
22
+ const tabCount = tabs.length;
23
+ const { index = -1, positionRelativeToTab = "after" } = pos.tab || {};
24
+ return index === -1 || index >= tabCount
25
+ ? [tabs[tabCount - 1], "after"]
26
+ : [tabs[index] ?? null, positionRelativeToTab];
27
+ }
28
+
29
+ export function insertIntoContainer(
30
+ container: ReactElement,
31
+ targetContainer: ReactElement,
32
+ newComponent: ReactElement
33
+ ): ReactElement {
34
+ const {
35
+ active: containerActive,
36
+ children: containerChildren = [],
37
+ path: containerPath,
38
+ } = getProps(container) as LayoutProps;
39
+
40
+ const existingComponentPath = getProp(targetContainer, "path");
41
+ const { idx, finalStep } = nextStep(
42
+ containerPath!,
43
+ existingComponentPath,
44
+ true
45
+ );
46
+ const [insertedIdx, children] = finalStep
47
+ ? insertIntoChildren(container, containerChildren, newComponent)
48
+ : [
49
+ containerActive,
50
+ containerChildren?.map((child, index) =>
51
+ index === idx
52
+ ? (insertIntoContainer(
53
+ child,
54
+ targetContainer,
55
+ newComponent
56
+ ) as ReactElement)
57
+ : child
58
+ ),
59
+ ];
60
+ const active =
61
+ typeOf(container) === "Stack"
62
+ ? Array.isArray(insertedIdx)
63
+ ? (insertedIdx[0] as number)
64
+ : insertedIdx
65
+ : containerActive;
66
+
67
+ return React.cloneElement(container, { active }, children);
68
+ }
69
+ function insertIntoChildren(
70
+ container: LayoutModel,
71
+ containerChildren: ReactElement[],
72
+ newComponent: ReactElement
73
+ ): [number, ReactElement[]] {
74
+ const containerPath = getProp(container, "path");
75
+ const count = containerChildren?.length;
76
+ const { id = uuid() } = getProps(newComponent);
77
+
78
+ if (count) {
79
+ return [
80
+ count,
81
+ containerChildren.concat(
82
+ resetPath(newComponent, `${containerPath}.${count}`, { id, key: id })
83
+ ),
84
+ ];
85
+ } else {
86
+ return [0, [resetPath(newComponent, `${containerPath}.0`, { id })]];
87
+ }
88
+ }
89
+
90
+ export function insertBesideChild(
91
+ container: ReactElement,
92
+ existingComponent: any,
93
+ newComponent: any,
94
+ insertionPosition: insertionPosition,
95
+ pos?: DropPos,
96
+ clientRect?: any,
97
+ dropRect?: any
98
+ ): ReactElement {
99
+ const {
100
+ active: containerActive,
101
+ children: containerChildren,
102
+ path: containerPath,
103
+ } = getProps(container);
104
+
105
+ const existingComponentPath = getProp(existingComponent, "path");
106
+ const { idx, finalStep } = nextStep(containerPath, existingComponentPath);
107
+ const [insertedIdx, children] = finalStep
108
+ ? updateChildren(
109
+ container,
110
+ containerChildren,
111
+ idx,
112
+ newComponent,
113
+ insertionPosition,
114
+ pos!,
115
+ clientRect,
116
+ dropRect
117
+ )
118
+ : [
119
+ containerActive,
120
+ containerChildren.map((child: ReactElement, index: number) =>
121
+ index === idx
122
+ ? insertBesideChild(
123
+ child,
124
+ existingComponent,
125
+ newComponent,
126
+ insertionPosition,
127
+ pos,
128
+ clientRect,
129
+ dropRect
130
+ )
131
+ : child
132
+ ),
133
+ ];
134
+
135
+ const active = typeOf(container) === "Stack" ? insertedIdx : containerActive;
136
+ return React.cloneElement(container, { active }, children);
137
+ }
138
+
139
+ function updateChildren(
140
+ container: LayoutModel,
141
+ containerChildren: ReactElement[],
142
+ idx: number,
143
+ newComponent: ReactElement,
144
+ insertionPosition: insertionPosition,
145
+ pos: DropPos,
146
+ clientRect: DropTarget["clientRect"],
147
+ dropRect: DropTarget["dropRect"]
148
+ ) {
149
+ const intrinsicSize = getIntrinsicSize(newComponent);
150
+ if (intrinsicSize?.width && intrinsicSize?.height) {
151
+ return insertIntrinsicSizedComponent(
152
+ container,
153
+ containerChildren,
154
+ idx,
155
+ newComponent,
156
+ insertionPosition,
157
+ clientRect,
158
+ dropRect!
159
+ );
160
+ } else {
161
+ return insertFlexComponent(
162
+ container,
163
+ containerChildren,
164
+ idx,
165
+ newComponent,
166
+ insertionPosition,
167
+ pos?.width || pos?.height,
168
+ clientRect
169
+ );
170
+ }
171
+ }
172
+
173
+ const getLeadingPlaceholderSize = (
174
+ flexDirection: flexDirection,
175
+ insertionPosition: insertionPosition,
176
+ { top, right, bottom, left }: DropTarget["clientRect"],
177
+ [rectLeft, rectTop, rectRight, rectBottom]: rectTuple
178
+ ) => {
179
+ if (flexDirection === "column" && insertionPosition === "before") {
180
+ return rectTop - top;
181
+ } else if (flexDirection === "column") {
182
+ return bottom - rectBottom;
183
+ } else if (flexDirection === "row" && insertionPosition === "before") {
184
+ return rectLeft - left;
185
+ } else if (flexDirection === "row") {
186
+ return right - rectRight;
187
+ }
188
+ };
189
+
190
+ function insertIntrinsicSizedComponent(
191
+ container: LayoutModel,
192
+ containerChildren: ReactElement[],
193
+ idx: number,
194
+ newComponent: ReactElement,
195
+ insertionPosition: insertionPosition,
196
+ clientRect: DropTarget["clientRect"],
197
+ dropRect: rectTuple
198
+ ) {
199
+ const {
200
+ style: { flexDirection },
201
+ } = getProps(container);
202
+ const [dimension, crossDimension, contraDirection] =
203
+ getFlexDimensions(flexDirection);
204
+ const { [crossDimension]: intrinsicCrossSize, [dimension]: intrinsicSize } =
205
+ getIntrinsicSize(newComponent) as { height: number; width: number };
206
+ const path = getProp(containerChildren[idx], "path");
207
+
208
+ // If we are introducing a new item into a row/column, but it is not flush against existing child, we will insert
209
+ // a leading placeholder ...
210
+ const placeholderSize = getLeadingPlaceholderSize(
211
+ flexDirection,
212
+ insertionPosition,
213
+ clientRect,
214
+ dropRect
215
+ );
216
+
217
+ const [itemToInsert, size] =
218
+ intrinsicCrossSize < clientRect[crossDimension]
219
+ ? [
220
+ wrapIntrinsicSizeComponentWithFlexbox(
221
+ newComponent,
222
+ contraDirection,
223
+ path,
224
+ clientRect,
225
+ dropRect
226
+ ),
227
+ intrinsicSize,
228
+ ]
229
+ : [newComponent, undefined];
230
+
231
+ const placeholder = placeholderSize
232
+ ? createPlaceHolder(path, placeholderSize, { flexGrow: 0, flexShrink: 0 })
233
+ : undefined;
234
+
235
+ if (intrinsicCrossSize > clientRect[crossDimension]) {
236
+ containerChildren = containerChildren.map((child) => {
237
+ if (getProp(child, "placeholder")) {
238
+ return child;
239
+ } else {
240
+ const { [crossDimension]: intrinsicCrossChildSize } = getIntrinsicSize(
241
+ child
242
+ ) as {
243
+ height: number;
244
+ width: number;
245
+ };
246
+ if (
247
+ intrinsicCrossChildSize &&
248
+ intrinsicCrossChildSize < intrinsicCrossSize
249
+ ) {
250
+ return wrapIntrinsicSizeComponentWithFlexbox(
251
+ child,
252
+ contraDirection,
253
+ getProp(child, "path")
254
+ );
255
+ } else {
256
+ return child;
257
+ }
258
+ }
259
+ });
260
+ }
261
+
262
+ return insertFlexComponent(
263
+ container,
264
+ containerChildren,
265
+ idx,
266
+ itemToInsert,
267
+ insertionPosition,
268
+ size,
269
+ clientRect,
270
+ placeholder
271
+ );
272
+ }
273
+
274
+ function insertFlexComponent(
275
+ container: LayoutModel,
276
+ containerChildren: ReactElement[],
277
+ idx: number,
278
+ newComponent: ReactElement,
279
+ insertionPosition: "before" | "after",
280
+ size: number | undefined,
281
+ targetRect: DropTarget["clientRect"],
282
+ placeholder?: ReactElement
283
+ ) {
284
+ const containerPath = getProp(container, "path");
285
+ let insertedIdx = 0;
286
+ const children =
287
+ !containerChildren || containerChildren.length === 0
288
+ ? [newComponent]
289
+ : containerChildren
290
+ .reduce<ReactElement[]>((arr, child, i) => {
291
+ if (idx === i) {
292
+ const [existingComponent, insertedComponent] =
293
+ getStyledComponents(container, child, newComponent, targetRect);
294
+ if (insertionPosition === "before") {
295
+ if (placeholder) {
296
+ arr.push(placeholder, insertedComponent, existingComponent);
297
+ } else {
298
+ arr.push(insertedComponent, existingComponent);
299
+ }
300
+ } else {
301
+ if (placeholder) {
302
+ arr.push(existingComponent, insertedComponent, placeholder);
303
+ } else {
304
+ arr.push(existingComponent, insertedComponent);
305
+ }
306
+ }
307
+ insertedIdx = arr.indexOf(insertedComponent);
308
+ } else {
309
+ arr.push(child);
310
+ }
311
+ return arr;
312
+ }, [])
313
+ .map((child, i) =>
314
+ i < insertedIdx ? child : resetPath(child, `${containerPath}.${i}`)
315
+ );
316
+
317
+ return [insertedIdx, children];
318
+ }
319
+
320
+ function getStyledComponents(
321
+ container: LayoutModel,
322
+ existingComponent: ReactElement,
323
+ newComponent: ReactElement,
324
+ targetRect: DropTarget["clientRect"]
325
+ ): [ReactElement, ReactElement] {
326
+ let { id = uuid(), version = 0 } = getProps(newComponent);
327
+ version += 1;
328
+ if (typeOf(container) === "Flexbox") {
329
+ const [dim] = getManagedDimension(container.props.style);
330
+ const splitterSize = 6;
331
+ const size = { [dim]: (targetRect[dim] - splitterSize) / 2 };
332
+ const existingComponentStyle = getFlexOrIntrinsicStyle(
333
+ existingComponent,
334
+ dim,
335
+ size
336
+ );
337
+ const newComponentStyle = getFlexOrIntrinsicStyle(newComponent, dim, size);
338
+
339
+ return [
340
+ React.cloneElement(existingComponent, {
341
+ style: existingComponentStyle,
342
+ }),
343
+ React.cloneElement(newComponent, {
344
+ id,
345
+ version,
346
+ style: newComponentStyle,
347
+ }),
348
+ ];
349
+ } else {
350
+ const {
351
+ style: { left: _1, top: _2, flex: _3, ...style } = {
352
+ left: undefined,
353
+ top: undefined,
354
+ flex: undefined,
355
+ },
356
+ } = getProps(newComponent);
357
+ // TODO why would we strip out width, height if resizeable
358
+ // we might need these if in a Stack, for example
359
+ // const dimensions = source.props.resizeable ? {} : { width, height };
360
+ return [
361
+ existingComponent,
362
+ React.cloneElement(newComponent, { id, version, style }),
363
+ ];
364
+ }
365
+ }