@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.
- package/README.md +1 -0
- package/package.json +30 -0
- package/src/Component.css +2 -0
- package/src/Component.tsx +20 -0
- package/src/DraggableLayout.css +18 -0
- package/src/DraggableLayout.tsx +29 -0
- package/src/__tests__/flexbox-utils.spec.js +90 -0
- package/src/action-buttons/action-buttons.css +12 -0
- package/src/action-buttons/action-buttons.tsx +30 -0
- package/src/action-buttons/index.ts +1 -0
- package/src/chest-of-drawers/Chest.css +36 -0
- package/src/chest-of-drawers/Chest.tsx +42 -0
- package/src/chest-of-drawers/Drawer.css +153 -0
- package/src/chest-of-drawers/Drawer.tsx +118 -0
- package/src/chest-of-drawers/index.ts +2 -0
- package/src/common-types.ts +9 -0
- package/src/debug.ts +16 -0
- package/src/dialog/Dialog.css +16 -0
- package/src/dialog/Dialog.tsx +59 -0
- package/src/dialog/index.ts +1 -0
- package/src/drag-drop/BoxModel.ts +546 -0
- package/src/drag-drop/DragState.ts +222 -0
- package/src/drag-drop/Draggable.ts +282 -0
- package/src/drag-drop/DropMenu.css +70 -0
- package/src/drag-drop/DropMenu.tsx +68 -0
- package/src/drag-drop/DropTarget.ts +392 -0
- package/src/drag-drop/DropTargetRenderer.css +40 -0
- package/src/drag-drop/DropTargetRenderer.tsx +284 -0
- package/src/drag-drop/dragDropTypes.ts +49 -0
- package/src/drag-drop/index.ts +4 -0
- package/src/editable-label/EditableLabel.css +28 -0
- package/src/editable-label/EditableLabel.tsx +99 -0
- package/src/editable-label/index.ts +1 -0
- package/src/flexbox/Flexbox.css +45 -0
- package/src/flexbox/Flexbox.tsx +70 -0
- package/src/flexbox/FlexboxLayout.jsx +26 -0
- package/src/flexbox/FluidGrid.css +134 -0
- package/src/flexbox/FluidGrid.tsx +84 -0
- package/src/flexbox/FluidGridLayout.tsx +10 -0
- package/src/flexbox/Splitter.css +140 -0
- package/src/flexbox/Splitter.tsx +135 -0
- package/src/flexbox/flexbox-utils.ts +128 -0
- package/src/flexbox/flexboxTypes.ts +63 -0
- package/src/flexbox/index.ts +4 -0
- package/src/flexbox/useResponsiveSizing.ts +85 -0
- package/src/flexbox/useSplitterResizing.ts +272 -0
- package/src/index.ts +20 -0
- package/src/layout-action.ts +21 -0
- package/src/layout-header/ActionButton.tsx +23 -0
- package/src/layout-header/Header.css +8 -0
- package/src/layout-header/Header.tsx +222 -0
- package/src/layout-header/index.ts +1 -0
- package/src/layout-provider/LayoutProvider.tsx +160 -0
- package/src/layout-provider/LayoutProviderContext.ts +17 -0
- package/src/layout-provider/index.ts +2 -0
- package/src/layout-provider/useLayoutDragDrop.ts +241 -0
- package/src/layout-reducer/flexUtils.ts +281 -0
- package/src/layout-reducer/index.ts +4 -0
- package/src/layout-reducer/insert-layout-element.ts +365 -0
- package/src/layout-reducer/layout-reducer.ts +255 -0
- package/src/layout-reducer/layoutTypes.ts +151 -0
- package/src/layout-reducer/layoutUtils.ts +302 -0
- package/src/layout-reducer/remove-layout-element.ts +240 -0
- package/src/layout-reducer/replace-layout-element.ts +118 -0
- package/src/layout-reducer/resize-flex-children.ts +56 -0
- package/src/layout-reducer/wrap-layout-element.ts +317 -0
- package/src/layout-view/View.css +58 -0
- package/src/layout-view/View.tsx +149 -0
- package/src/layout-view/ViewContext.ts +31 -0
- package/src/layout-view/index.ts +4 -0
- package/src/layout-view/useView.tsx +104 -0
- package/src/layout-view/useViewActionDispatcher.ts +133 -0
- package/src/layout-view/useViewResize.ts +53 -0
- package/src/layout-view/viewTypes.ts +37 -0
- package/src/palette/Palette.css +37 -0
- package/src/palette/Palette.tsx +140 -0
- package/src/palette/PaletteUitk.css +9 -0
- package/src/palette/PaletteUitk.tsx +79 -0
- package/src/palette/index.ts +2 -0
- package/src/placeholder/Placeholder.css +10 -0
- package/src/placeholder/Placeholder.tsx +39 -0
- package/src/placeholder/index.ts +1 -0
- package/src/registry/ComponentRegistry.ts +35 -0
- package/src/registry/index.ts +1 -0
- package/src/responsive/OverflowMenu.css +31 -0
- package/src/responsive/OverflowMenu.jsx +56 -0
- package/src/responsive/breakpoints.ts +48 -0
- package/src/responsive/index.ts +4 -0
- package/src/responsive/measureMinimumNodeSize.ts +23 -0
- package/src/responsive/overflowUtils.js +14 -0
- package/src/responsive/use-breakpoints.ts +100 -0
- package/src/responsive/useOverflowObserver.ts +606 -0
- package/src/responsive/useResizeObserver.ts +154 -0
- package/src/responsive/utils.ts +37 -0
- package/src/stack/Stack.css +39 -0
- package/src/stack/Stack.tsx +160 -0
- package/src/stack/StackLayout.tsx +137 -0
- package/src/stack/index.ts +3 -0
- package/src/stack/stackTypes.ts +19 -0
- package/src/tabs/TabPanel.css +12 -0
- package/src/tabs/TabPanel.tsx +17 -0
- package/src/tabs/index.ts +1 -0
- package/src/tools/config-wrapper/ConfigWrapper.jsx +53 -0
- package/src/tools/config-wrapper/index.js +1 -0
- package/src/tools/devtools-box/layout-configurator.css +112 -0
- package/src/tools/devtools-box/layout-configurator.jsx +369 -0
- package/src/tools/devtools-tree/layout-tree-viewer.css +15 -0
- package/src/tools/devtools-tree/layout-tree-viewer.jsx +36 -0
- package/src/tools/index.js +3 -0
- package/src/use-persistent-state.ts +115 -0
- package/src/utils/componentFromLayout.tsx +30 -0
- package/src/utils/index.ts +6 -0
- package/src/utils/pathUtils.ts +294 -0
- package/src/utils/propUtils.ts +24 -0
- package/src/utils/refUtils.ts +16 -0
- package/src/utils/styleUtils.ts +14 -0
- package/src/utils/typeOf.ts +22 -0
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
import { rect } from '../common-types';
|
|
2
|
+
import { Measurements, pointPositionWithinRect } from './BoxModel';
|
|
3
|
+
import { DragDropRect } from './dragDropTypes';
|
|
4
|
+
|
|
5
|
+
var SCALE_FACTOR = 0.4;
|
|
6
|
+
|
|
7
|
+
export type IntrinsicSizes = {
|
|
8
|
+
height?: number;
|
|
9
|
+
width?: number;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
interface ZoneRange {
|
|
13
|
+
hi: number;
|
|
14
|
+
lo: number;
|
|
15
|
+
}
|
|
16
|
+
type DragConstraint = {
|
|
17
|
+
zone: {
|
|
18
|
+
x: ZoneRange;
|
|
19
|
+
y: ZoneRange;
|
|
20
|
+
};
|
|
21
|
+
pos: {
|
|
22
|
+
x: ZoneRange;
|
|
23
|
+
y: ZoneRange;
|
|
24
|
+
};
|
|
25
|
+
mouse: {
|
|
26
|
+
x: ZoneRange;
|
|
27
|
+
y: ZoneRange;
|
|
28
|
+
};
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
interface ExtendedZoneRange {
|
|
32
|
+
lo: boolean;
|
|
33
|
+
hi: boolean;
|
|
34
|
+
mousePct: number;
|
|
35
|
+
mousePos: number;
|
|
36
|
+
pos: number;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export class DragState {
|
|
40
|
+
public constraint!: DragConstraint;
|
|
41
|
+
public x!: ExtendedZoneRange;
|
|
42
|
+
public y!: ExtendedZoneRange;
|
|
43
|
+
public intrinsicSize: IntrinsicSizes | undefined;
|
|
44
|
+
|
|
45
|
+
constructor(
|
|
46
|
+
zone: DragDropRect,
|
|
47
|
+
mouseX: number,
|
|
48
|
+
mouseY: number,
|
|
49
|
+
measurements: DragDropRect,
|
|
50
|
+
intrinsicSize?: IntrinsicSizes
|
|
51
|
+
) {
|
|
52
|
+
this.init(zone, mouseX, mouseY, measurements, intrinsicSize);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
init(
|
|
56
|
+
zone: DragDropRect,
|
|
57
|
+
mouseX: number,
|
|
58
|
+
mouseY: number,
|
|
59
|
+
rect: DragDropRect,
|
|
60
|
+
intrinsicSize?: IntrinsicSizes
|
|
61
|
+
) {
|
|
62
|
+
var { left: x, top: y } = rect;
|
|
63
|
+
|
|
64
|
+
var { pctX, pctY } = pointPositionWithinRect(mouseX, mouseY, rect);
|
|
65
|
+
|
|
66
|
+
// We are applying a scale factor of 0.4 to the draggee. This is purely a visual
|
|
67
|
+
// effect - the actual box size remains the original size. The 'leading' values
|
|
68
|
+
// represent the difference between the visual scaled down box and the actual box.
|
|
69
|
+
|
|
70
|
+
var scaleFactor = SCALE_FACTOR;
|
|
71
|
+
|
|
72
|
+
var leadX = pctX * rect.width;
|
|
73
|
+
var trailX = rect.width - leadX;
|
|
74
|
+
var leadY = pctY * rect.height;
|
|
75
|
+
var trailY = rect.height - leadY;
|
|
76
|
+
|
|
77
|
+
// When we assign position to rect using css. positioning units are applied to the
|
|
78
|
+
// unscaled shape, so we have to adjust values to take scaling into account.
|
|
79
|
+
var scaledWidth = rect.width * scaleFactor,
|
|
80
|
+
scaledHeight = rect.height * scaleFactor;
|
|
81
|
+
|
|
82
|
+
var scaleDiff = 1 - scaleFactor;
|
|
83
|
+
var leadXScaleDiff = leadX * scaleDiff;
|
|
84
|
+
var leadYScaleDiff = leadY * scaleDiff;
|
|
85
|
+
var trailXScaleDiff = trailX * scaleDiff;
|
|
86
|
+
var trailYScaleDiff = trailY * scaleDiff;
|
|
87
|
+
|
|
88
|
+
this.intrinsicSize = intrinsicSize;
|
|
89
|
+
|
|
90
|
+
this.constraint = {
|
|
91
|
+
zone: {
|
|
92
|
+
x: {
|
|
93
|
+
lo: zone.left,
|
|
94
|
+
hi: zone.right
|
|
95
|
+
},
|
|
96
|
+
y: {
|
|
97
|
+
lo: zone.top,
|
|
98
|
+
hi: zone.bottom
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
|
|
102
|
+
pos: {
|
|
103
|
+
x: {
|
|
104
|
+
lo: /* left */ zone.left - leadXScaleDiff,
|
|
105
|
+
hi: /* right */ zone.right - rect.width + trailXScaleDiff
|
|
106
|
+
},
|
|
107
|
+
y: {
|
|
108
|
+
lo: /* top */ zone.top - leadYScaleDiff,
|
|
109
|
+
hi: /* bottom */ zone.bottom - rect.height + trailYScaleDiff
|
|
110
|
+
}
|
|
111
|
+
},
|
|
112
|
+
mouse: {
|
|
113
|
+
x: {
|
|
114
|
+
lo: /* left */ zone.left + scaledWidth * pctX,
|
|
115
|
+
hi: /* right */ zone.right - scaledWidth * (1 - pctX)
|
|
116
|
+
},
|
|
117
|
+
y: {
|
|
118
|
+
lo: /* top */ zone.top + scaledHeight * pctY,
|
|
119
|
+
hi: /* bottom */ zone.bottom - scaledHeight * (1 - pctY)
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
// onsole.log(`DragState: constraint ${JSON.stringify(this.constraint, null, 2)}`);
|
|
125
|
+
|
|
126
|
+
this.x = {
|
|
127
|
+
pos: x,
|
|
128
|
+
lo: false,
|
|
129
|
+
hi: false,
|
|
130
|
+
mousePos: mouseX,
|
|
131
|
+
mousePct: pctX
|
|
132
|
+
};
|
|
133
|
+
this.y = {
|
|
134
|
+
pos: y,
|
|
135
|
+
lo: false,
|
|
136
|
+
hi: false,
|
|
137
|
+
mousePos: mouseY,
|
|
138
|
+
mousePct: pctY
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
outOfBounds() {
|
|
143
|
+
return this.x.lo || this.x.hi || this.y.lo || this.y.hi;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
inBounds() {
|
|
147
|
+
return !this.outOfBounds();
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
dropX() {
|
|
151
|
+
return this.dropXY('x');
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
dropY() {
|
|
155
|
+
return this.dropXY('y');
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
hasIntrinsicSize(): number | undefined {
|
|
159
|
+
return this?.intrinsicSize?.height && this?.intrinsicSize?.width;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/*
|
|
163
|
+
* diff = mouse movement, signed int
|
|
164
|
+
* xy = 'x' or 'y'
|
|
165
|
+
*/
|
|
166
|
+
//todo, diff can be calculated in here
|
|
167
|
+
update(xy: 'x' | 'y', mousePos: number) {
|
|
168
|
+
var state = this[xy],
|
|
169
|
+
mouseConstraint = this.constraint.mouse[xy],
|
|
170
|
+
posConstraint = this.constraint.pos[xy],
|
|
171
|
+
previousPos = state.pos;
|
|
172
|
+
|
|
173
|
+
var diff = mousePos - state.mousePos;
|
|
174
|
+
|
|
175
|
+
//xy==='x' && console.log(`update: state.lo=${state.lo}, mPos=${mousePos}, mC.lo=${mouseConstraint.lo}, prevPos=${previousPos}, diff=${diff} ` );
|
|
176
|
+
|
|
177
|
+
if (diff < 0) {
|
|
178
|
+
if (state.lo) {
|
|
179
|
+
/* do nothing */
|
|
180
|
+
} else if (mousePos < mouseConstraint.lo) {
|
|
181
|
+
state.lo = true;
|
|
182
|
+
state.pos = posConstraint.lo;
|
|
183
|
+
} else if (state.hi) {
|
|
184
|
+
if (mousePos < mouseConstraint.hi) {
|
|
185
|
+
state.hi = false;
|
|
186
|
+
state.pos += diff;
|
|
187
|
+
}
|
|
188
|
+
} else {
|
|
189
|
+
state.pos += diff;
|
|
190
|
+
}
|
|
191
|
+
} else if (diff > 0) {
|
|
192
|
+
if (state.hi) {
|
|
193
|
+
/* do nothing */
|
|
194
|
+
} else if (mousePos > mouseConstraint.hi) {
|
|
195
|
+
state.hi = true;
|
|
196
|
+
state.pos = posConstraint.hi;
|
|
197
|
+
} else if (state.lo) {
|
|
198
|
+
if (mousePos > mouseConstraint.lo) {
|
|
199
|
+
state.lo = false;
|
|
200
|
+
state.pos += diff;
|
|
201
|
+
}
|
|
202
|
+
} else {
|
|
203
|
+
state.pos += diff;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
state.mousePos = mousePos;
|
|
208
|
+
|
|
209
|
+
return previousPos !== state.pos;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
private dropXY(this: DragState, dir: 'x' | 'y') {
|
|
213
|
+
var pos = this[dir],
|
|
214
|
+
rect = this.constraint.zone[dir];
|
|
215
|
+
// why not do the rounding +/- 1 on the rect initially - this is all it is usef for
|
|
216
|
+
return pos.lo
|
|
217
|
+
? Math.max(rect.lo, pos.mousePos)
|
|
218
|
+
: pos.hi
|
|
219
|
+
? Math.min(pos.mousePos, Math.round(rect.hi) - 1)
|
|
220
|
+
: pos.mousePos;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
@@ -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 | null;
|
|
31
|
+
let _dragState: DragState;
|
|
32
|
+
let _dropTarget: DropTarget | null = null;
|
|
33
|
+
let _validDropTargetPaths: string[] | null;
|
|
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
|
+
var x = true;
|
|
120
|
+
var y = true;
|
|
121
|
+
|
|
122
|
+
let x_diff = x ? e.clientX - _dragStartX : 0;
|
|
123
|
+
let y_diff = y ? e.clientY - _dragStartY : 0;
|
|
124
|
+
let 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) {
|
|
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 = null;
|
|
278
|
+
_dropTargetRenderer.clear();
|
|
279
|
+
_validDropTargetPaths = null;
|
|
280
|
+
window.removeEventListener("mousemove", dragMousemoveHandler, false);
|
|
281
|
+
window.removeEventListener("mouseup", dragMouseupHandler, false);
|
|
282
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
.hwDropMenu {
|
|
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
|
+
.hwDropMenu.left,
|
|
14
|
+
.hwDropMenu.right {
|
|
15
|
+
flex-direction: column;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.hwDropMenu.bottom {
|
|
19
|
+
transform: translate(0, -30px);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.hwDropMenu.right {
|
|
23
|
+
transform: translate(-20px, 0);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.hwDropMenu-item {
|
|
27
|
+
width: 32px;
|
|
28
|
+
height: 32px;
|
|
29
|
+
background-color: var(--grey20);
|
|
30
|
+
border-bottom: solid 1px var(--grey40);
|
|
31
|
+
cursor: pointer;
|
|
32
|
+
display: flex;
|
|
33
|
+
align-items: center;
|
|
34
|
+
justify-content: center;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.hwDropMenu-item .Icon {
|
|
38
|
+
transform: scale(1.25);
|
|
39
|
+
transform-origin: center center;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.hwDropMenu.left .hwDropMenu-item .hwIcon {
|
|
43
|
+
transform: scale(1.25) rotate(180deg);
|
|
44
|
+
transform-origin: center center;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.hwDropMenu.top .hwDropMenu-item .hwIcon {
|
|
48
|
+
transform: scale(1.25) rotate(270deg);
|
|
49
|
+
transform-origin: center center;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.hwDropMenu.bottom .hwDropMenu-item .hwIcon {
|
|
53
|
+
transform: scale(1.25) rotate(90deg);
|
|
54
|
+
transform-origin: center center;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.hwDropMenu-item .hwIcon-path {
|
|
58
|
+
fill: grey;
|
|
59
|
+
}
|
|
60
|
+
.hwDropMenu-item:hover {
|
|
61
|
+
background-color: rgba(200, 200, 200, 0.5);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.hwDropMenu-item:hover .hwIcon-path-2 {
|
|
65
|
+
fill: blue;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.hwDropMenu-item:last-child {
|
|
69
|
+
border-bottom: none;
|
|
70
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import React, { HTMLAttributes } from "react";
|
|
2
|
+
import cx from "classnames";
|
|
3
|
+
import { Column2AIcon, Column2BIcon } from "@vuu-ui/ui-controls";
|
|
4
|
+
import "./DropMenu.css";
|
|
5
|
+
import { DropTarget } from "./DropTarget";
|
|
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 getIcon = (i: number) => {
|
|
29
|
+
if (i === 0) {
|
|
30
|
+
return <Column2AIcon />;
|
|
31
|
+
} else {
|
|
32
|
+
return <Column2BIcon />;
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export interface DropMenuProps extends HTMLAttributes<HTMLDivElement> {
|
|
37
|
+
dropTarget: DropTarget;
|
|
38
|
+
onHover: (target: DropTarget | null) => void;
|
|
39
|
+
orientation?: "left" | "top" | "right" | "bottom";
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export const DropMenu = ({
|
|
43
|
+
className,
|
|
44
|
+
dropTarget,
|
|
45
|
+
onHover,
|
|
46
|
+
orientation,
|
|
47
|
+
}: DropMenuProps) => {
|
|
48
|
+
const dropTargets = dropTarget.toArray();
|
|
49
|
+
// TODO we have all the information here to draw a mini target map
|
|
50
|
+
// but maybe thats overkill ...
|
|
51
|
+
|
|
52
|
+
return (
|
|
53
|
+
<div
|
|
54
|
+
className={cx("hwDropMenu", className, orientation)}
|
|
55
|
+
onMouseLeave={() => onHover(null)}
|
|
56
|
+
>
|
|
57
|
+
{dropTargets.map((target, i) => (
|
|
58
|
+
<div
|
|
59
|
+
key={i}
|
|
60
|
+
className="hwDropMenu-item"
|
|
61
|
+
onMouseEnter={() => onHover(target)}
|
|
62
|
+
>
|
|
63
|
+
{getIcon(i)}
|
|
64
|
+
</div>
|
|
65
|
+
))}
|
|
66
|
+
</div>
|
|
67
|
+
);
|
|
68
|
+
};
|