@vuu-ui/vuu-layout 0.5.14 → 0.5.15

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 (114) hide show
  1. package/package.json +10 -13
  2. package/src/Component.css +0 -0
  3. package/src/Component.tsx +20 -0
  4. package/src/DraggableLayout.css +18 -0
  5. package/src/DraggableLayout.tsx +26 -0
  6. package/src/__tests__/flexbox-utils.spec.js +90 -0
  7. package/src/chest-of-drawers/Chest.css +36 -0
  8. package/src/chest-of-drawers/Chest.tsx +42 -0
  9. package/src/chest-of-drawers/Drawer.css +159 -0
  10. package/src/chest-of-drawers/Drawer.tsx +118 -0
  11. package/src/chest-of-drawers/index.ts +2 -0
  12. package/src/common-types.ts +9 -0
  13. package/src/debug.ts +16 -0
  14. package/src/drag-drop/BoxModel.ts +551 -0
  15. package/src/drag-drop/DragState.ts +219 -0
  16. package/src/drag-drop/Draggable.ts +282 -0
  17. package/src/drag-drop/DropMenu.css +71 -0
  18. package/src/drag-drop/DropMenu.tsx +61 -0
  19. package/src/drag-drop/DropTarget.ts +393 -0
  20. package/src/drag-drop/DropTargetRenderer.css +40 -0
  21. package/src/drag-drop/DropTargetRenderer.tsx +277 -0
  22. package/src/drag-drop/dragDropTypes.ts +47 -0
  23. package/src/drag-drop/index.ts +5 -0
  24. package/src/editable-label/EditableLabel.css +28 -0
  25. package/src/editable-label/EditableLabel.tsx +99 -0
  26. package/src/editable-label/index.ts +1 -0
  27. package/src/flexbox/Flexbox.css +45 -0
  28. package/src/flexbox/Flexbox.tsx +70 -0
  29. package/src/flexbox/FlexboxLayout.tsx +28 -0
  30. package/src/flexbox/FluidGrid.css +134 -0
  31. package/src/flexbox/FluidGrid.tsx +82 -0
  32. package/src/flexbox/FluidGridLayout.tsx +9 -0
  33. package/src/flexbox/Splitter.css +140 -0
  34. package/src/flexbox/Splitter.tsx +127 -0
  35. package/src/flexbox/flexbox-utils.ts +128 -0
  36. package/src/flexbox/flexboxTypes.ts +68 -0
  37. package/src/flexbox/index.ts +5 -0
  38. package/src/flexbox/useResponsiveSizing.ts +82 -0
  39. package/src/flexbox/useSplitterResizing.ts +270 -0
  40. package/src/index.ts +19 -0
  41. package/src/layout-action.ts +21 -0
  42. package/src/layout-header/ActionButton.tsx +23 -0
  43. package/src/layout-header/Header.css +8 -0
  44. package/src/layout-header/Header.tsx +216 -0
  45. package/src/layout-header/index.ts +1 -0
  46. package/src/layout-provider/LayoutProvider.tsx +161 -0
  47. package/src/layout-provider/LayoutProviderContext.ts +17 -0
  48. package/src/layout-provider/index.ts +3 -0
  49. package/src/layout-provider/useLayoutDragDrop.ts +210 -0
  50. package/src/layout-reducer/flexUtils.ts +276 -0
  51. package/src/layout-reducer/index.ts +5 -0
  52. package/src/layout-reducer/insert-layout-element.ts +365 -0
  53. package/src/layout-reducer/layout-reducer.ts +237 -0
  54. package/src/layout-reducer/layoutTypes.ts +159 -0
  55. package/src/layout-reducer/layoutUtils.ts +288 -0
  56. package/src/layout-reducer/remove-layout-element.ts +226 -0
  57. package/src/layout-reducer/replace-layout-element.ts +113 -0
  58. package/src/layout-reducer/resize-flex-children.ts +55 -0
  59. package/src/layout-reducer/wrap-layout-element.ts +307 -0
  60. package/src/layout-view/View.css +61 -0
  61. package/src/layout-view/View.tsx +143 -0
  62. package/src/layout-view/ViewContext.ts +30 -0
  63. package/src/layout-view/index.ts +5 -0
  64. package/src/layout-view/useView.tsx +104 -0
  65. package/src/layout-view/useViewActionDispatcher.ts +123 -0
  66. package/src/layout-view/useViewResize.ts +53 -0
  67. package/src/layout-view/viewTypes.ts +35 -0
  68. package/src/palette/Palette.css +33 -0
  69. package/src/palette/Palette.tsx +140 -0
  70. package/src/palette/PaletteSalt.css +9 -0
  71. package/src/palette/PaletteSalt.tsx +79 -0
  72. package/src/palette/index.ts +3 -0
  73. package/src/placeholder/Placeholder.css +10 -0
  74. package/src/placeholder/Placeholder.tsx +38 -0
  75. package/src/placeholder/index.ts +1 -0
  76. package/src/registry/ComponentRegistry.ts +44 -0
  77. package/src/registry/index.ts +1 -0
  78. package/src/responsive/breakpoints.ts +62 -0
  79. package/src/responsive/index.ts +3 -0
  80. package/src/responsive/measureMinimumNodeSize.ts +23 -0
  81. package/src/responsive/overflowUtils.js +14 -0
  82. package/src/responsive/use-breakpoints.ts +101 -0
  83. package/src/responsive/useResizeObserver.ts +154 -0
  84. package/src/responsive/utils.ts +37 -0
  85. package/src/stack/Stack.css +39 -0
  86. package/src/stack/Stack.tsx +173 -0
  87. package/src/stack/StackLayout.tsx +119 -0
  88. package/src/stack/index.ts +4 -0
  89. package/src/stack/stackTypes.ts +22 -0
  90. package/src/tabs/TabPanel.css +12 -0
  91. package/src/tabs/TabPanel.tsx +17 -0
  92. package/src/tabs/index.ts +1 -0
  93. package/src/tools/config-wrapper/ConfigWrapper.tsx +55 -0
  94. package/src/tools/config-wrapper/index.ts +1 -0
  95. package/src/tools/devtools-box/layout-configurator.css +112 -0
  96. package/src/tools/devtools-box/layout-configurator.jsx +369 -0
  97. package/src/tools/devtools-tree/layout-tree-viewer.css +15 -0
  98. package/src/tools/devtools-tree/layout-tree-viewer.jsx +36 -0
  99. package/src/tools/index.ts +4 -0
  100. package/src/use-persistent-state.ts +112 -0
  101. package/src/utils/index.ts +5 -0
  102. package/src/utils/pathUtils.ts +283 -0
  103. package/src/utils/propUtils.ts +26 -0
  104. package/src/utils/refUtils.ts +16 -0
  105. package/src/utils/styleUtils.ts +13 -0
  106. package/src/utils/typeOf.ts +25 -0
  107. package/tsconfig-emit-types.json +11 -0
  108. package/LICENSE +0 -201
  109. package/cjs/index.js +0 -20
  110. package/cjs/index.js.map +0 -7
  111. package/esm/index.js +0 -20
  112. package/esm/index.js.map +0 -7
  113. package/index.css +0 -2
  114. package/index.css.map +0 -7
@@ -0,0 +1,219 @@
1
+ import { pointPositionWithinRect } from './BoxModel';
2
+ import { DragDropRect } from './dragDropTypes';
3
+
4
+ const SCALE_FACTOR = 0.4;
5
+
6
+ export type IntrinsicSizes = {
7
+ height?: number;
8
+ width?: number;
9
+ };
10
+
11
+ interface ZoneRange {
12
+ hi: number;
13
+ lo: number;
14
+ }
15
+ type DragConstraint = {
16
+ zone: {
17
+ x: ZoneRange;
18
+ y: ZoneRange;
19
+ };
20
+ pos: {
21
+ x: ZoneRange;
22
+ y: ZoneRange;
23
+ };
24
+ mouse: {
25
+ x: ZoneRange;
26
+ y: ZoneRange;
27
+ };
28
+ };
29
+
30
+ interface ExtendedZoneRange {
31
+ lo: boolean;
32
+ hi: boolean;
33
+ mousePct: number;
34
+ mousePos: number;
35
+ pos: number;
36
+ }
37
+
38
+ export class DragState {
39
+ public constraint!: DragConstraint;
40
+ public x!: ExtendedZoneRange;
41
+ public y!: ExtendedZoneRange;
42
+ public intrinsicSize: IntrinsicSizes | undefined;
43
+
44
+ constructor(
45
+ zone: DragDropRect,
46
+ mouseX: number,
47
+ mouseY: number,
48
+ measurements: DragDropRect,
49
+ intrinsicSize?: IntrinsicSizes
50
+ ) {
51
+ this.init(zone, mouseX, mouseY, measurements, intrinsicSize);
52
+ }
53
+
54
+ init(
55
+ zone: DragDropRect,
56
+ mouseX: number,
57
+ mouseY: number,
58
+ rect: DragDropRect,
59
+ intrinsicSize?: IntrinsicSizes
60
+ ) {
61
+ const { left: x, top: y } = rect;
62
+
63
+ const { pctX, pctY } = pointPositionWithinRect(mouseX, mouseY, rect);
64
+
65
+ // We are applying a scale factor of 0.4 to the draggee. This is purely a visual
66
+ // effect - the actual box size remains the original size. The 'leading' values
67
+ // represent the difference between the visual scaled down box and the actual box.
68
+
69
+ const scaleFactor = SCALE_FACTOR;
70
+
71
+ const leadX = pctX * rect.width;
72
+ const trailX = rect.width - leadX;
73
+ const leadY = pctY * rect.height;
74
+ const trailY = rect.height - leadY;
75
+
76
+ // When we assign position to rect using css. positioning units are applied to the
77
+ // unscaled shape, so we have to adjust values to take scaling into account.
78
+ const scaledWidth = rect.width * scaleFactor,
79
+ scaledHeight = rect.height * scaleFactor;
80
+
81
+ const scaleDiff = 1 - scaleFactor;
82
+ const leadXScaleDiff = leadX * scaleDiff;
83
+ const leadYScaleDiff = leadY * scaleDiff;
84
+ const trailXScaleDiff = trailX * scaleDiff;
85
+ const trailYScaleDiff = trailY * scaleDiff;
86
+
87
+ this.intrinsicSize = intrinsicSize;
88
+
89
+ this.constraint = {
90
+ zone: {
91
+ x: {
92
+ lo: zone.left,
93
+ hi: zone.right
94
+ },
95
+ y: {
96
+ lo: zone.top,
97
+ hi: zone.bottom
98
+ }
99
+ },
100
+
101
+ pos: {
102
+ x: {
103
+ lo: /* left */ zone.left - leadXScaleDiff,
104
+ hi: /* right */ zone.right - rect.width + trailXScaleDiff
105
+ },
106
+ y: {
107
+ lo: /* top */ zone.top - leadYScaleDiff,
108
+ hi: /* bottom */ zone.bottom - rect.height + trailYScaleDiff
109
+ }
110
+ },
111
+ mouse: {
112
+ x: {
113
+ lo: /* left */ zone.left + scaledWidth * pctX,
114
+ hi: /* right */ zone.right - scaledWidth * (1 - pctX)
115
+ },
116
+ y: {
117
+ lo: /* top */ zone.top + scaledHeight * pctY,
118
+ hi: /* bottom */ zone.bottom - scaledHeight * (1 - pctY)
119
+ }
120
+ }
121
+ };
122
+
123
+ this.x = {
124
+ pos: x,
125
+ lo: false,
126
+ hi: false,
127
+ mousePos: mouseX,
128
+ mousePct: pctX
129
+ };
130
+ this.y = {
131
+ pos: y,
132
+ lo: false,
133
+ hi: false,
134
+ mousePos: mouseY,
135
+ mousePct: pctY
136
+ };
137
+ }
138
+
139
+ outOfBounds() {
140
+ return this.x.lo || this.x.hi || this.y.lo || this.y.hi;
141
+ }
142
+
143
+ inBounds() {
144
+ return !this.outOfBounds();
145
+ }
146
+
147
+ dropX() {
148
+ return this.dropXY('x');
149
+ }
150
+
151
+ dropY() {
152
+ return this.dropXY('y');
153
+ }
154
+
155
+ hasIntrinsicSize(): number | undefined {
156
+ return this?.intrinsicSize?.height && this?.intrinsicSize?.width;
157
+ }
158
+
159
+ /*
160
+ * diff = mouse movement, signed int
161
+ * xy = 'x' or 'y'
162
+ */
163
+ //todo, diff can be calculated in here
164
+ update(xy: 'x' | 'y', mousePos: number) {
165
+ const state = this[xy],
166
+ mouseConstraint = this.constraint.mouse[xy],
167
+ posConstraint = this.constraint.pos[xy],
168
+ previousPos = state.pos;
169
+
170
+ const diff = mousePos - state.mousePos;
171
+
172
+ //xy==='x' && console.log(`update: state.lo=${state.lo}, mPos=${mousePos}, mC.lo=${mouseConstraint.lo}, prevPos=${previousPos}, diff=${diff} ` );
173
+
174
+ if (diff < 0) {
175
+ if (state.lo) {
176
+ /* do nothing */
177
+ } else if (mousePos < mouseConstraint.lo) {
178
+ state.lo = true;
179
+ state.pos = posConstraint.lo;
180
+ } else if (state.hi) {
181
+ if (mousePos < mouseConstraint.hi) {
182
+ state.hi = false;
183
+ state.pos += diff;
184
+ }
185
+ } else {
186
+ state.pos += diff;
187
+ }
188
+ } else if (diff > 0) {
189
+ if (state.hi) {
190
+ /* do nothing */
191
+ } else if (mousePos > mouseConstraint.hi) {
192
+ state.hi = true;
193
+ state.pos = posConstraint.hi;
194
+ } else if (state.lo) {
195
+ if (mousePos > mouseConstraint.lo) {
196
+ state.lo = false;
197
+ state.pos += diff;
198
+ }
199
+ } else {
200
+ state.pos += diff;
201
+ }
202
+ }
203
+
204
+ state.mousePos = mousePos;
205
+
206
+ return previousPos !== state.pos;
207
+ }
208
+
209
+ private dropXY(this: DragState, dir: 'x' | 'y') {
210
+ const pos = this[dir],
211
+ rect = this.constraint.zone[dir];
212
+ // why not do the rounding +/- 1 on the rect initially - this is all it is usef for
213
+ return pos.lo
214
+ ? Math.max(rect.lo, pos.mousePos)
215
+ : pos.hi
216
+ ? Math.min(pos.mousePos, Math.round(rect.hi) - 1)
217
+ : pos.mousePos;
218
+ }
219
+ }
@@ -0,0 +1,282 @@
1
+ import { ReactElement } from "react";
2
+ import { rect } from "../common-types";
3
+ import { LayoutModel } from "../layout-reducer";
4
+ import { findTarget, followPath, getProps } from "../utils";
5
+ import { BoxModel, Measurements, Position } from "./BoxModel";
6
+ import { DragDropRect } from "./dragDropTypes";
7
+ import { DragState, IntrinsicSizes } from "./DragState";
8
+ import { DropTarget, identifyDropTarget } from "./DropTarget";
9
+ import DropTargetRenderer from "./DropTargetRenderer";
10
+
11
+ export type DragStartCallback = (e: MouseEvent, x: number, y: number) => void;
12
+ export type DragMoveCallback = (
13
+ x: number | undefined,
14
+ y: number | undefined
15
+ ) => void;
16
+ export type DragEndCallback = (droppedTarget: Partial<DropTarget>) => void;
17
+ export type DragInstructions = {
18
+ DoNotRemove?: boolean;
19
+ DoNotTransform?: boolean;
20
+ dragThreshold?: number;
21
+ RemoveDraggableOnDragEnd?: boolean;
22
+ };
23
+
24
+ let _dragStartCallback: DragStartCallback | null;
25
+ let _dragMoveCallback: DragMoveCallback | null;
26
+ let _dragEndCallback: DragEndCallback | null;
27
+
28
+ let _dragStartX: number;
29
+ let _dragStartY: number;
30
+ let _dragContainer: ReactElement | undefined;
31
+ let _dragState: DragState;
32
+ let _dropTarget: DropTarget | null = null;
33
+ let _validDropTargetPaths: string[] | undefined;
34
+ let _dragInstructions: DragInstructions;
35
+ let _measurements: Measurements;
36
+ let _simpleDrag: boolean;
37
+ let _dragThreshold: number;
38
+ let _mouseDownTimer: number | null = null;
39
+
40
+ const DEFAULT_DRAG_THRESHOLD = 3;
41
+ const _dropTargetRenderer = new DropTargetRenderer();
42
+ const SCALE_FACTOR = 0.4;
43
+
44
+ function getDragContainer(
45
+ rootContainer: ReactElement,
46
+ dragContainerPath: string
47
+ ) {
48
+ if (dragContainerPath) {
49
+ return followPath(rootContainer, dragContainerPath) as ReactElement;
50
+ } else {
51
+ return findTarget(
52
+ rootContainer,
53
+ (props) => props.dropTarget
54
+ ) as ReactElement;
55
+ }
56
+ }
57
+
58
+ export const Draggable = {
59
+ handleMousedown(
60
+ e: MouseEvent,
61
+ dragStartCallback: DragStartCallback,
62
+ dragInstructions: DragInstructions = {}
63
+ ) {
64
+ _dragStartCallback = dragStartCallback;
65
+ _dragInstructions = dragInstructions;
66
+
67
+ _dragStartX = e.clientX;
68
+ _dragStartY = e.clientY;
69
+
70
+ _dragThreshold =
71
+ dragInstructions.dragThreshold === undefined
72
+ ? DEFAULT_DRAG_THRESHOLD
73
+ : dragInstructions.dragThreshold;
74
+
75
+ if (_dragThreshold === 0) {
76
+ // maybe this should be -1
77
+ _dragStartCallback(e, 0, 0);
78
+ } else {
79
+ window.addEventListener("mousemove", preDragMousemoveHandler, false);
80
+ window.addEventListener("mouseup", preDragMouseupHandler, false);
81
+
82
+ _mouseDownTimer = window.setTimeout(() => {
83
+ console.log("mousedownTimer fires");
84
+ window.removeEventListener("mousemove", preDragMousemoveHandler, false);
85
+ window.removeEventListener("mouseup", preDragMouseupHandler, false);
86
+ _dragStartCallback?.(e, 0, 0);
87
+ }, 500);
88
+ }
89
+
90
+ e.preventDefault();
91
+ },
92
+
93
+ // called from handleDragStart (_dragCallback)
94
+ initDrag(
95
+ rootContainer: ReactElement,
96
+ dragContainerPath: string,
97
+ { top, left, right, bottom }: rect,
98
+ dragPos: { x: number; y: number },
99
+ dragHandler: {
100
+ drag: DragMoveCallback;
101
+ drop: DragEndCallback;
102
+ },
103
+ intrinsicSize?: IntrinsicSizes,
104
+ dropTargets?: string[]
105
+ ) {
106
+ ({ drag: _dragMoveCallback, drop: _dragEndCallback } = dragHandler);
107
+ return initDrag(
108
+ rootContainer,
109
+ dragContainerPath,
110
+ { top, left, right, bottom } as DragDropRect,
111
+ dragPos,
112
+ intrinsicSize,
113
+ dropTargets
114
+ );
115
+ },
116
+ };
117
+
118
+ function preDragMousemoveHandler(e: MouseEvent) {
119
+ const x = true;
120
+ const y = true;
121
+
122
+ const x_diff = x ? e.clientX - _dragStartX : 0;
123
+ const y_diff = y ? e.clientY - _dragStartY : 0;
124
+ const mouseMoveDistance = Math.max(Math.abs(x_diff), Math.abs(y_diff));
125
+
126
+ // when we do finally move the draggee, we are going to 'jump' by the amount of the drag threshold, should we
127
+ // attempt to animate this ?
128
+ if (mouseMoveDistance > _dragThreshold) {
129
+ window.removeEventListener("mousemove", preDragMousemoveHandler, false);
130
+ window.removeEventListener("mouseup", preDragMouseupHandler, false);
131
+ _dragStartCallback?.(e, x_diff, y_diff);
132
+ _dragStartCallback = null;
133
+ }
134
+ }
135
+
136
+ function preDragMouseupHandler() {
137
+ if (_mouseDownTimer) {
138
+ window.clearTimeout(_mouseDownTimer);
139
+ _mouseDownTimer = null;
140
+ }
141
+ window.removeEventListener("mousemove", preDragMousemoveHandler, false);
142
+ window.removeEventListener("mouseup", preDragMouseupHandler, false);
143
+ }
144
+
145
+ function initDrag(
146
+ rootContainer: ReactElement,
147
+ dragContainerPath: string,
148
+ dragRect: DragDropRect,
149
+ dragPos: { x: number; y: number },
150
+ intrinsicSize?: IntrinsicSizes,
151
+ dropTargets?: string[]
152
+ ) {
153
+ _dragContainer = getDragContainer(rootContainer, dragContainerPath);
154
+ const { "data-path": dataPath, path = dataPath } = getProps(_dragContainer);
155
+
156
+ if (dropTargets) {
157
+ const dropPaths = dropTargets
158
+ .map((id) => findTarget(rootContainer, (props) => props.id === id))
159
+ .map((target) => (target as LayoutModel).props.path);
160
+ _validDropTargetPaths = dropPaths;
161
+ }
162
+
163
+ // var start = window.performance.now();
164
+ // translate the layout $position to drag-oriented co-ordinates, ignoring splitters
165
+ _measurements = BoxModel.measure(_dragContainer, dropTargets);
166
+ console.log({ _measurements });
167
+ // onsole.log({ measurements: _measurements });
168
+ // var end = window.performance.now();
169
+ // onsole.log(`[Draggable] measurements took ${end - start}ms`, _measurements);
170
+
171
+ const dragZone = _measurements[path];
172
+
173
+ _dragState = new DragState(
174
+ dragZone,
175
+ dragPos.x,
176
+ dragPos.y,
177
+ dragRect,
178
+ intrinsicSize
179
+ );
180
+
181
+ const pctX = Math.round(_dragState.x.mousePct * 100);
182
+ const pctY = Math.round(_dragState.y.mousePct * 100);
183
+
184
+ window.addEventListener("mousemove", dragMousemoveHandler, false);
185
+ window.addEventListener("mouseup", dragMouseupHandler, false);
186
+
187
+ _simpleDrag = false;
188
+
189
+ _dropTargetRenderer.prepare(dragRect, "tab-only");
190
+
191
+ return _dragInstructions.DoNotTransform
192
+ ? "transform:none"
193
+ : // scale factor should be applied in caller, not here
194
+ `transform:scale(${SCALE_FACTOR},${SCALE_FACTOR});transform-origin:${pctX}% ${pctY}%;`;
195
+ }
196
+
197
+ function dragMousemoveHandler(evt: MouseEvent) {
198
+ const x = evt.clientX;
199
+ const y = evt.clientY;
200
+ const dragState = _dragState;
201
+ const currentDropTarget = _dropTarget;
202
+ let dropTarget;
203
+ let newX, newY;
204
+
205
+ if (dragState.update("x", x)) {
206
+ newX = dragState.x.pos;
207
+ }
208
+
209
+ if (dragState.update("y", y)) {
210
+ newY = dragState.y.pos;
211
+ }
212
+
213
+ if (newX === undefined && newY === undefined) {
214
+ //onsole.log('both x and y are unchanged');
215
+ } else {
216
+ _dragMoveCallback?.(newX, newY);
217
+ }
218
+
219
+ if (_simpleDrag || _dragContainer === undefined) {
220
+ return;
221
+ }
222
+
223
+ if (dragState.inBounds()) {
224
+ dropTarget = identifyDropTarget(
225
+ x,
226
+ y,
227
+ _dragContainer,
228
+ _measurements,
229
+ dragState.hasIntrinsicSize(),
230
+ _validDropTargetPaths
231
+ );
232
+ } else {
233
+ dropTarget = identifyDropTarget(
234
+ dragState.dropX(),
235
+ dragState.dropY(),
236
+ _dragContainer,
237
+ _measurements
238
+ );
239
+ }
240
+
241
+ // did we have an existing droptarget which is no longer such ...
242
+ if (currentDropTarget) {
243
+ if (dropTarget == null || dropTarget.box !== currentDropTarget.box) {
244
+ _dropTarget = null;
245
+ }
246
+ }
247
+
248
+ if (dropTarget) {
249
+ _dropTargetRenderer.draw(dropTarget, dragState);
250
+ _dropTarget = dropTarget;
251
+ }
252
+ }
253
+
254
+ function dragMouseupHandler() {
255
+ onDragEnd();
256
+ }
257
+
258
+ function onDragEnd() {
259
+ if (_dropTarget) {
260
+ const dropTarget =
261
+ _dropTargetRenderer.hoverDropTarget ||
262
+ DropTarget.getActiveDropTarget(_dropTarget);
263
+
264
+ _dragEndCallback?.(dropTarget as DropTarget);
265
+
266
+ _dropTarget = null;
267
+ } else {
268
+ _dragEndCallback?.({
269
+ component: _dragContainer,
270
+ pos: { position: Position.Absolute } as any,
271
+ });
272
+ }
273
+
274
+ _dragMoveCallback = null;
275
+ _dragEndCallback = null;
276
+
277
+ _dragContainer = undefined;
278
+ _dropTargetRenderer.clear();
279
+ _validDropTargetPaths = undefined;
280
+ window.removeEventListener("mousemove", dragMousemoveHandler, false);
281
+ window.removeEventListener("mouseup", dragMouseupHandler, false);
282
+ }
@@ -0,0 +1,71 @@
1
+ .vuuDropMenu {
2
+ margin-left: -50%;
3
+ margin-bottom: -50%;
4
+ background-color: white;
5
+ border: solid 1px var(--grey40);
6
+ display: inline-flex;
7
+ justify-content: center;
8
+ align-items: center;
9
+ padding: 3px;
10
+ border-radius: 3px;
11
+ }
12
+
13
+ .vuuDropMenu-left,
14
+ .vuuDropMenu-right {
15
+ flex-direction: column;
16
+ }
17
+
18
+ .vuuDropMenu-bottom {
19
+ transform: translate(0, -30px);
20
+ }
21
+
22
+ .vuuDropMenu-right {
23
+ transform: translate(-20px, 0);
24
+ }
25
+
26
+ .vuuDropMenu-item {
27
+ --vuu-icon-size: 20px;
28
+ width: 32px;
29
+ height: 32px;
30
+ background-color: var(--grey20);
31
+ border-bottom: solid 1px var(--grey40);
32
+ cursor: pointer;
33
+ display: flex;
34
+ align-items: center;
35
+ justify-content: center;
36
+ }
37
+
38
+ .vuuDropMenu-item .Icon {
39
+ transform: scale(1.25);
40
+ transform-origin: center center;
41
+ }
42
+
43
+ .vuuDropMenu-left .vuuDropMenu-item .hwIcon {
44
+ transform: scale(1.25) rotate(180deg);
45
+ transform-origin: center center;
46
+ }
47
+
48
+ .vuuDropMenu-top .vuuDropMenu-item .hwIcon {
49
+ transform: scale(1.25) rotate(270deg);
50
+ transform-origin: center center;
51
+ }
52
+
53
+ .vuuDropMenu-bottom .vuuDropMenu-item .hwIcon {
54
+ transform: scale(1.25) rotate(90deg);
55
+ transform-origin: center center;
56
+ }
57
+
58
+ .vuuDropMenu-item .hwIcon-path {
59
+ fill: grey;
60
+ }
61
+ .vuuDropMenu-item:hover {
62
+ background-color: rgba(200, 200, 200, 0.5);
63
+ }
64
+
65
+ .vuuDropMenu-item:hover .hwIcon-path-2 {
66
+ fill: blue;
67
+ }
68
+
69
+ .vuuDropMenu-item:last-child {
70
+ border-bottom: none;
71
+ }
@@ -0,0 +1,61 @@
1
+ import cx from "classnames";
2
+ import { HTMLAttributes } from "react";
3
+ import { DropTarget } from "./DropTarget";
4
+
5
+ import "./DropMenu.css";
6
+
7
+ export function computeMenuPosition(
8
+ dropTarget: DropTarget,
9
+ offsetTop = 0,
10
+ offsetLeft = 0
11
+ ): [number, number, "left" | "bottom" | "right" | "top"] {
12
+ const { pos, clientRect: box } = dropTarget;
13
+ const menuOffset = 20;
14
+
15
+ return pos.position.West
16
+ ? [box.left - offsetLeft + menuOffset, pos.y - offsetTop, "left"]
17
+ : pos.position.South
18
+ ? [pos.x - offsetLeft, box.bottom - offsetTop - menuOffset, "bottom"]
19
+ : pos.position.East
20
+ ? [box.right - offsetLeft - menuOffset, pos.y - offsetTop, "right"]
21
+ : /* North | Header*/ [
22
+ pos.x - offsetLeft,
23
+ box.top - offsetTop + menuOffset,
24
+ "top",
25
+ ];
26
+ }
27
+
28
+ const classBase = "vuuDropMenu";
29
+
30
+ export interface DropMenuProps extends HTMLAttributes<HTMLDivElement> {
31
+ dropTarget: DropTarget;
32
+ onHover: (target: DropTarget | null) => void;
33
+ orientation?: "left" | "top" | "right" | "bottom";
34
+ }
35
+
36
+ export const DropMenu = ({
37
+ className,
38
+ dropTarget,
39
+ onHover,
40
+ orientation,
41
+ }: DropMenuProps) => {
42
+ const dropTargets = dropTarget.toArray();
43
+ // TODO we have all the information here to draw a mini target map
44
+ // but maybe thats overkill ...
45
+
46
+ return (
47
+ <div
48
+ className={cx(classBase, className, `${classBase}-${orientation}`)}
49
+ onMouseLeave={() => onHover(null)}
50
+ >
51
+ {dropTargets.map((target, i) => (
52
+ <div
53
+ key={i}
54
+ className={`${classBase}-item`}
55
+ data-icon={i === 0 ? "column-2A" : "column-2B"}
56
+ onMouseEnter={() => onHover(target)}
57
+ />
58
+ ))}
59
+ </div>
60
+ );
61
+ };