@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,240 @@
1
+ import React, { ReactElement } from 'react';
2
+ import { createPlaceHolder } from './flexUtils';
3
+ import { swapChild } from './replace-layout-element';
4
+
5
+ import {
6
+ followPath,
7
+ followPathToParent,
8
+ getProp,
9
+ getProps,
10
+ nextStep,
11
+ resetPath,
12
+ typeOf
13
+ } from '../utils';
14
+ import { RemoveAction } from './layoutTypes';
15
+
16
+ export function removeChild(layoutRoot: ReactElement, { path }: RemoveAction) {
17
+ const target = followPath(layoutRoot, path!) as ReactElement;
18
+ let targetParent = followPathToParent(layoutRoot, path!);
19
+ if (targetParent === null) {
20
+ return layoutRoot;
21
+ }
22
+ const { children } = getProps(targetParent);
23
+ if (children.length > 1 && allOtherChildrenArePlaceholders(children, path)) {
24
+ // eslint-disable-next-line no-unused-vars
25
+ const {
26
+ style: { flexBasis, display, flexDirection, ...style }
27
+ } = getProps(targetParent);
28
+ let containerPath = getProp(targetParent, 'path');
29
+ let newLayout = swapChild(
30
+ layoutRoot,
31
+ targetParent,
32
+ createPlaceHolder(containerPath, flexBasis, style)
33
+ );
34
+ // eslint-disable-next-line no-cond-assign
35
+ while ((targetParent = followPathToParent(newLayout, containerPath))) {
36
+ if (getProp(targetParent, 'path') === '0') {
37
+ break;
38
+ }
39
+ const { children } = getProps(targetParent);
40
+ if (allOtherChildrenArePlaceholders(children)) {
41
+ containerPath = getProp(targetParent, 'path');
42
+ // eslint-disable-next-line no-unused-vars
43
+ const {
44
+ style: { flexBasis, display, flexDirection, ...style }
45
+ } = getProps(targetParent);
46
+ newLayout = swapChild(
47
+ layoutRoot,
48
+ targetParent,
49
+ createPlaceHolder(containerPath, flexBasis, style)
50
+ );
51
+ } else if (hasAdjacentPlaceholders(children)) {
52
+ newLayout = collapsePlaceholders(layoutRoot, targetParent as ReactElement);
53
+ // } else if (hasRedundantPlaceholders(children)){
54
+ /*
55
+ We may have redundany placeholders for example where we have a tower containing a Terrace and a placeholder
56
+ If all the components bordering on the lower placeholder are themselves placeholders, the lower placeholder
57
+ is redundant
58
+ */
59
+ } else {
60
+ break;
61
+ }
62
+ }
63
+ return newLayout;
64
+ // return removeChild(rootProps, {path: targetParent.props.path});
65
+ // return removeChildAndPlaceholder(rootProps, {path: targetParent.props.path});
66
+ } else {
67
+ return _removeChild(layoutRoot, target);
68
+ }
69
+ }
70
+
71
+ function _removeChild(container: ReactElement, child: ReactElement): ReactElement {
72
+ let { active, children: componentChildren, path, preserve } = getProps(container);
73
+ const { idx, finalStep } = nextStep(path, getProp(child, 'path'));
74
+ const type = typeOf(container) as string;
75
+ let children = componentChildren.slice() as ReactElement[];
76
+ if (finalStep) {
77
+ children.splice(idx, 1);
78
+ if (active !== undefined && active >= idx) {
79
+ active = Math.max(0, active - 1);
80
+ }
81
+
82
+ if (children.length === 1 && !preserve && path !== '0' && type.match(/Flexbox|Stack/)) {
83
+ return unwrap(container, children[0]);
84
+ }
85
+
86
+ // Not 100% sure we should do this, unless configured to
87
+ if (!children.some(isFlexible) && children.some(canBeMadeFlexible)) {
88
+ children = makeFlexible(children);
89
+ }
90
+ } else {
91
+ children[idx] = _removeChild(children[idx], child) as ReactElement;
92
+ }
93
+
94
+ children = children.map((child, i) => resetPath(child, `${path}.${i}`));
95
+ return React.cloneElement(container, { active }, children);
96
+ }
97
+
98
+ function unwrap(container: ReactElement, child: ReactElement) {
99
+ const type = typeOf(container);
100
+ const {
101
+ path,
102
+ style: { flexBasis, flexGrow, flexShrink, width, height }
103
+ } = getProps(container);
104
+
105
+ let unwrappedChild = resetPath(child, path);
106
+ if (path === '0') {
107
+ unwrappedChild = React.cloneElement(unwrappedChild, {
108
+ style: {
109
+ ...child.props.style,
110
+ width,
111
+ height
112
+ }
113
+ });
114
+ } else if (type === 'Flexbox') {
115
+ const dim = container.props.style.flexDirection === 'column' ? 'height' : 'width';
116
+ const {
117
+ // eslint-disable-next-line no-unused-vars
118
+ style: { [dim]: size, ...style }
119
+ } = unwrappedChild.props;
120
+ // Need to overwrite key
121
+ unwrappedChild = React.cloneElement(unwrappedChild, {
122
+ // Need to assign key
123
+ flexFill: undefined,
124
+ style: {
125
+ ...style,
126
+ // flexFill, if present described the childs relationship to the doomed flexbox,
127
+ // must not be applied to new parent
128
+ flexGrow,
129
+ flexShrink,
130
+ flexBasis,
131
+ width,
132
+ height
133
+ }
134
+ });
135
+ }
136
+ return unwrappedChild;
137
+ }
138
+
139
+ function isFlexible(element: ReactElement) {
140
+ return element.props.style.flexGrow > 0;
141
+ }
142
+
143
+ function canBeMadeFlexible(element: ReactElement) {
144
+ const { width, height, flexGrow } = element.props.style;
145
+ return flexGrow === 0 && typeof width !== 'number' && typeof height !== 'number';
146
+ }
147
+
148
+ function makeFlexible(children: ReactElement[]) {
149
+ return children.map((child) =>
150
+ canBeMadeFlexible(child)
151
+ ? React.cloneElement(child, {
152
+ style: {
153
+ ...child.props.style,
154
+ flexGrow: 1
155
+ }
156
+ })
157
+ : child
158
+ );
159
+ }
160
+
161
+ const hasAdjacentPlaceholders = (children: ReactElement[]) => {
162
+ if (children && children.length > 0) {
163
+ let wasPlaceholder = getProp(children[0], 'placeholder');
164
+ let isPlaceholder = false;
165
+ for (let i = 1; i < children.length; i++) {
166
+ isPlaceholder = getProp(children[i], 'placeholder');
167
+ if (wasPlaceholder && isPlaceholder) {
168
+ return true;
169
+ }
170
+ wasPlaceholder = isPlaceholder;
171
+ }
172
+ }
173
+ };
174
+
175
+ const collapsePlaceholders = (container: ReactElement, target: ReactElement) => {
176
+ let { children: componentChildren, path } = getProps(container);
177
+ const { idx, finalStep } = nextStep(path, getProp(target, 'path'));
178
+ let children = componentChildren.slice() as ReactElement[];
179
+ if (finalStep) {
180
+ children[idx] = _collapsePlaceHolders(target);
181
+ } else {
182
+ children[idx] = collapsePlaceholders(children[idx], target) as ReactElement;
183
+ }
184
+
185
+ children = children.map((child, i) => resetPath(child, `${path}.${i}`));
186
+ return React.cloneElement(container, undefined, children);
187
+ };
188
+
189
+ const _collapsePlaceHolders = (container: ReactElement) => {
190
+ const { children } = getProps(container);
191
+ const newChildren = [];
192
+ const placeholders: ReactElement[] = [];
193
+
194
+ for (let i = 0; i < children.length; i++) {
195
+ if (getProp(children[i], 'placeholder')) {
196
+ placeholders.push(children[i]);
197
+ } else {
198
+ if (placeholders.length === 1) {
199
+ newChildren.push(placeholders.pop());
200
+ } else if (placeholders.length > 0) {
201
+ newChildren.push(mergePlaceholders(placeholders));
202
+ placeholders.length = 0;
203
+ }
204
+ newChildren.push(children[i]);
205
+ }
206
+ }
207
+
208
+ if (placeholders.length === 1) {
209
+ newChildren.push(placeholders.pop());
210
+ } else if (placeholders.length > 0) {
211
+ newChildren.push(mergePlaceholders(placeholders));
212
+ }
213
+
214
+ const containerPath = getProp(container, 'path');
215
+ return React.cloneElement(
216
+ container,
217
+ undefined,
218
+ newChildren.map((child, i) => resetPath(child, `${containerPath}.${i}`))
219
+ );
220
+ };
221
+
222
+ const mergePlaceholders = ([placeholder, ...placeholders]: ReactElement[]) => {
223
+ const targetStyle = getProp(placeholder, 'style');
224
+ let { flexBasis, flexGrow, flexShrink } = targetStyle;
225
+ for (let {
226
+ props: { style }
227
+ } of placeholders) {
228
+ flexBasis += style.flexBasis;
229
+ flexGrow = Math.max(flexGrow, style.flexGrow);
230
+ flexShrink = Math.max(flexShrink, style.flexShrink);
231
+ }
232
+ return React.cloneElement(placeholder, {
233
+ style: { ...targetStyle, flexBasis, flexGrow, flexShrink }
234
+ });
235
+ };
236
+
237
+ const allOtherChildrenArePlaceholders = (children: ReactElement[], path?: string) =>
238
+ children.every(
239
+ (child) => getProp(child, 'placeholder') || (path && getProp(child, 'path') === path)
240
+ );
@@ -0,0 +1,118 @@
1
+ import React, { ReactElement } from 'react';
2
+ import { getProp, getProps, nextStep } from '../utils';
3
+ import { Action } from '../layout-action';
4
+ import { applyLayoutProps, LayoutProps } from './layoutUtils';
5
+ import { ReplaceAction } from './layoutTypes';
6
+
7
+ export function replaceChild(model: ReactElement, { target, replacement }: ReplaceAction) {
8
+ return _replaceChild(model, target, replacement);
9
+ }
10
+
11
+ export function _replaceChild(
12
+ model: ReactElement,
13
+ child: ReactElement,
14
+ replacement: ReactElement<LayoutProps>
15
+ ) {
16
+ const path = getProp(child, 'path');
17
+ const resizeable = getProp(child, 'resizeable');
18
+ const { style } = getProps(child);
19
+ const newChild =
20
+ // applyLayoutProps is a bit heavy here - it supports the scenario
21
+ // where we drop/replace a template. Might want to make it somehow
22
+ // an opt-in option
23
+ applyLayoutProps(
24
+ React.cloneElement(replacement, {
25
+ resizeable,
26
+ style: {
27
+ ...style,
28
+ ...replacement.props.style
29
+ }
30
+ }),
31
+ path
32
+ );
33
+
34
+ return swapChild(model, child, newChild);
35
+ }
36
+
37
+ export function swapChild(
38
+ model: ReactElement,
39
+ child: ReactElement,
40
+ replacement: ReactElement,
41
+ op?: 'maximize' | 'minimize' | 'restore'
42
+ ): ReactElement {
43
+ if (model === child) {
44
+ return replacement as any;
45
+ } else {
46
+ const { idx, finalStep } = nextStep(getProp(model, 'path'), getProp(child, 'path'));
47
+ const children = model.props.children.slice();
48
+ if (finalStep) {
49
+ if (!op) {
50
+ children[idx] = replacement;
51
+ } else if (op === Action.MINIMIZE) {
52
+ children[idx] = minimize(model, children[idx]);
53
+ } else if (op === Action.RESTORE) {
54
+ children[idx] = restore(children[idx]);
55
+ }
56
+ } else {
57
+ children[idx] = swapChild(children[idx], child, replacement, op);
58
+ }
59
+ return React.cloneElement(model, undefined, children);
60
+ }
61
+ }
62
+
63
+ function minimize(parent: ReactElement, child: ReactElement) {
64
+ // Right now, parent is always going to be a FLexbox, but might not always be the case
65
+ const { style: parentStyle } = getProps(parent);
66
+ const { style: childStyle } = getProps(child);
67
+
68
+ const { width, height, flexBasis, flexShrink, flexGrow, ...rest } = childStyle;
69
+
70
+ const restoreStyle = {
71
+ width,
72
+ height,
73
+ flexBasis,
74
+ flexShrink,
75
+ flexGrow
76
+ };
77
+
78
+ const style = {
79
+ ...rest,
80
+ flexBasis: 0,
81
+ flexGrow: 0,
82
+ flexShrink: 0
83
+ };
84
+ const collapsed =
85
+ parentStyle.flexDirection === 'row'
86
+ ? 'vertical'
87
+ : parentStyle.flexDirection === 'column'
88
+ ? 'horizontal'
89
+ : false;
90
+
91
+ if (collapsed) {
92
+ return React.cloneElement(child, {
93
+ collapsed,
94
+ restoreStyle,
95
+ style
96
+ });
97
+ } else {
98
+ return child;
99
+ }
100
+ }
101
+
102
+ function restore(child: ReactElement) {
103
+ // Right now, parent is always going to be a FLexbox, but might not always be the case
104
+ const { style: childStyle, restoreStyle } = getProps(child);
105
+
106
+ const { flexBasis, flexShrink, flexGrow, ...rest } = childStyle;
107
+
108
+ const style = {
109
+ ...rest,
110
+ ...restoreStyle
111
+ };
112
+
113
+ return React.cloneElement(child, {
114
+ collapsed: false,
115
+ style,
116
+ restoreStyle: undefined
117
+ });
118
+ }
@@ -0,0 +1,56 @@
1
+ import React, { CSSProperties, ReactElement } from 'react';
2
+ import { followPath, getProps } from '../utils';
3
+ import { swapChild } from './replace-layout-element';
4
+ import { SplitterResizeAction } from './layoutTypes';
5
+ import { dimension } from '../common-types';
6
+
7
+ export function resizeFlexChildren(
8
+ layoutRoot: ReactElement,
9
+ { path, sizes }: SplitterResizeAction
10
+ ) {
11
+ const target = followPath(layoutRoot, path, true);
12
+ const { children, style } = getProps(target);
13
+
14
+ const dimension = style.flexDirection === 'column' ? 'height' : 'width';
15
+ const replacementChildren = applySizesToChildren(children, sizes, dimension);
16
+
17
+ const replacement = React.cloneElement(target, undefined, replacementChildren);
18
+
19
+ return swapChild(layoutRoot, target, replacement);
20
+ }
21
+
22
+ function applySizesToChildren(
23
+ children: ReactElement[],
24
+ sizes: { currentSize: number; flexBasis: number }[],
25
+ dimension: dimension
26
+ ) {
27
+ return children.map((child, i) => {
28
+ const {
29
+ style: { [dimension]: size, flexBasis: actualFlexBasis }
30
+ } = getProps(child);
31
+ const meta = sizes[i];
32
+ let { currentSize, flexBasis } = meta;
33
+ const hasCurrentSize = currentSize !== undefined;
34
+ const newSize = hasCurrentSize ? meta.currentSize : flexBasis;
35
+
36
+ if (newSize === undefined || size === newSize || actualFlexBasis === newSize) {
37
+ return child;
38
+ } else {
39
+ return React.cloneElement(child, {
40
+ style: applySizeToChild(child.props.style, dimension, newSize)
41
+ });
42
+ }
43
+ });
44
+ }
45
+
46
+ function applySizeToChild(style: CSSProperties, dimension: dimension, newSize: number) {
47
+ const hasSize = typeof style[dimension] === 'number';
48
+ const { flexShrink = 1, flexGrow = 1 } = style;
49
+ return {
50
+ ...style,
51
+ [dimension]: hasSize ? newSize : 'auto',
52
+ flexBasis: hasSize ? 'auto' : newSize,
53
+ flexShrink,
54
+ flexGrow
55
+ };
56
+ }