@vuu-ui/vuu-layout 2.1.19-beta.1 → 2.1.19-beta.2

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 (89) hide show
  1. package/package.json +12 -13
  2. package/src/Component.js +14 -0
  3. package/src/LayoutContainer.css.js +24 -0
  4. package/src/LayoutContainer.js +26 -0
  5. package/src/debug.js +16 -0
  6. package/src/dock-layout/DockLayout.css.js +36 -0
  7. package/src/dock-layout/DockLayout.js +43 -0
  8. package/src/dock-layout/Drawer.css.js +150 -0
  9. package/src/dock-layout/Drawer.js +82 -0
  10. package/src/dock-layout/index.js +2 -0
  11. package/src/drag-drop/BoxModel.js +337 -0
  12. package/src/drag-drop/DragState.js +119 -0
  13. package/src/drag-drop/Draggable.js +140 -0
  14. package/src/drag-drop/DropTarget.js +286 -0
  15. package/src/drag-drop/DropTargetRenderer.js +275 -0
  16. package/src/drag-drop/dragDropTypes.js +0 -0
  17. package/src/drag-drop/index.js +3 -0
  18. package/src/flexbox/Flexbox.css.js +45 -0
  19. package/src/flexbox/Flexbox.js +45 -0
  20. package/src/flexbox/FlexboxLayout.js +27 -0
  21. package/src/flexbox/Splitter.css.js +57 -0
  22. package/src/flexbox/Splitter.js +112 -0
  23. package/src/flexbox/flexbox-utils.js +92 -0
  24. package/src/flexbox/flexboxTypes.js +0 -0
  25. package/src/flexbox/index.js +2 -0
  26. package/src/flexbox/useSplitterResizing.js +181 -0
  27. package/src/index.js +20 -0
  28. package/src/layout-action.js +20 -0
  29. package/src/layout-header/ActionButton.js +15 -0
  30. package/src/layout-header/Header.css.js +32 -0
  31. package/src/layout-header/Header.js +99 -0
  32. package/src/layout-header/index.js +1 -0
  33. package/src/layout-header/useHeader.js +76 -0
  34. package/src/layout-provider/LayoutProvider.js +242 -0
  35. package/src/layout-provider/LayoutProviderContext.js +13 -0
  36. package/src/layout-provider/index.js +2 -0
  37. package/src/layout-provider/useLayoutDragDrop.js +147 -0
  38. package/src/layout-reducer/flexUtils.js +186 -0
  39. package/src/layout-reducer/index.js +4 -0
  40. package/src/layout-reducer/insert-layout-element.js +179 -0
  41. package/src/layout-reducer/layout-reducer.js +121 -0
  42. package/src/layout-reducer/layoutTypes.js +34 -0
  43. package/src/layout-reducer/layoutUtils.js +178 -0
  44. package/src/layout-reducer/move-layout-element.js +18 -0
  45. package/src/layout-reducer/remove-layout-element.js +161 -0
  46. package/src/layout-reducer/replace-layout-element.js +70 -0
  47. package/src/layout-reducer/resize-flex-children.js +48 -0
  48. package/src/layout-reducer/wrap-layout-element.js +134 -0
  49. package/src/layout-view/View.css.js +76 -0
  50. package/src/layout-view/View.js +116 -0
  51. package/src/layout-view/useView.js +69 -0
  52. package/src/layout-view/useViewBroadcastChannel.js +29 -0
  53. package/src/layout-view/useViewResize.js +27 -0
  54. package/src/layout-view/viewTypes.js +0 -0
  55. package/src/layout-view-actions/ViewContext.js +11 -0
  56. package/src/layout-view-actions/useViewActionDispatcher.js +116 -0
  57. package/src/layout-view-actions/useViewContributions.js +31 -0
  58. package/src/palette/Palette.css.js +31 -0
  59. package/src/palette/Palette.js +85 -0
  60. package/src/palette/index.js +1 -0
  61. package/src/placeholder/LayoutStartPanel.css.js +35 -0
  62. package/src/placeholder/LayoutStartPanel.js +57 -0
  63. package/src/placeholder/Placeholder.css.js +20 -0
  64. package/src/placeholder/Placeholder.js +33 -0
  65. package/src/placeholder/index.js +1 -0
  66. package/src/responsive/index.js +2 -0
  67. package/src/responsive/measureMinimumNodeSize.js +24 -0
  68. package/src/responsive/useResizeObserver.js +109 -0
  69. package/src/responsive/utils.js +27 -0
  70. package/src/stack/Stack.css.js +44 -0
  71. package/src/stack/Stack.js +98 -0
  72. package/src/stack/StackLayout.js +116 -0
  73. package/src/stack/index.js +3 -0
  74. package/src/stack/stackTypes.js +0 -0
  75. package/src/tabs/TabPanel.css.js +17 -0
  76. package/src/tabs/TabPanel.js +11 -0
  77. package/src/tabs/index.js +1 -0
  78. package/src/tools/config-wrapper/ConfigWrapper.js +55 -0
  79. package/src/tools/config-wrapper/index.js +1 -0
  80. package/src/tools/devtools-box/layout-configurator.css.js +113 -0
  81. package/src/tools/devtools-tree/layout-tree-viewer.css.js +17 -0
  82. package/src/tools/index.js +3 -0
  83. package/src/use-persistent-state.js +43 -0
  84. package/src/utils/index.js +5 -0
  85. package/src/utils/pathUtils.js +212 -0
  86. package/src/utils/propUtils.js +15 -0
  87. package/src/utils/refUtils.js +5 -0
  88. package/src/utils/styleUtils.js +9 -0
  89. package/src/utils/typeOf.js +15 -0
@@ -0,0 +1,337 @@
1
+ import { boxContainsPoint, isContainer } from "@vuu-ui/vuu-utils";
2
+ import { getProps, typeOf } from "../utils/index.js";
3
+ const positionValues = {
4
+ north: 1,
5
+ east: 2,
6
+ south: 4,
7
+ west: 8,
8
+ header: 16,
9
+ centre: 32,
10
+ absolute: 64
11
+ };
12
+ const RelativeDropPosition = {
13
+ AFTER: "after",
14
+ BEFORE: "before"
15
+ };
16
+ const Position = Object.freeze({
17
+ North: _position("north"),
18
+ East: _position("east"),
19
+ South: _position("south"),
20
+ West: _position("west"),
21
+ Header: _position("header"),
22
+ Centre: _position("centre"),
23
+ Absolute: _position("absolute")
24
+ });
25
+ function _position(str) {
26
+ return Object.freeze({
27
+ offset: "north" === str || "west" === str ? 0 : "south" === str || "east" === str ? 1 : NaN,
28
+ valueOf: function() {
29
+ return positionValues[str];
30
+ },
31
+ toString: function() {
32
+ return str;
33
+ },
34
+ North: "north" === str,
35
+ South: "south" === str,
36
+ East: "east" === str,
37
+ West: "west" === str,
38
+ Header: "header" === str,
39
+ Centre: "centre" === str,
40
+ NorthOrSouth: "north" === str || "south" === str,
41
+ EastOrWest: "east" === str || "west" === str,
42
+ NorthOrWest: "north" === str || "west" === str,
43
+ SouthOrEast: "east" === str || "south" === str,
44
+ Absolute: "absolute" === str
45
+ });
46
+ }
47
+ const NORTH = Position.North, SOUTH = Position.South, EAST = Position.East, WEST = Position.West, HEADER = Position.Header, CENTRE = Position.Centre;
48
+ class BoxModel {
49
+ static measure(model, dropTargetPaths = []) {
50
+ const measurements = {};
51
+ measureRootComponent(model, measurements, dropTargetPaths);
52
+ return measurements;
53
+ }
54
+ static allBoxesContainingPoint(layout, measurements, x, y, validDropTargets) {
55
+ return allBoxesContainingPoint(layout, measurements, x, y, validDropTargets).reverse();
56
+ }
57
+ }
58
+ function pointPositionWithinRect(x, y, rect, borderZone = 30) {
59
+ const width = rect.right - rect.left;
60
+ const height = rect.bottom - rect.top;
61
+ const posX = x - rect.left;
62
+ const posY = y - rect.top;
63
+ let closeToTheEdge = 0;
64
+ if (posX < borderZone) closeToTheEdge += 8;
65
+ if (posX > width - borderZone) closeToTheEdge += 2;
66
+ if (posY < borderZone) closeToTheEdge += 1;
67
+ if (posY > height - borderZone) closeToTheEdge += 4;
68
+ return {
69
+ pctX: posX / width,
70
+ pctY: posY / height,
71
+ closeToTheEdge
72
+ };
73
+ }
74
+ function getPosition(x, y, rect, targetOrientation) {
75
+ const { BEFORE, AFTER } = RelativeDropPosition;
76
+ const { pctX, pctY, closeToTheEdge } = pointPositionWithinRect(x, y, rect);
77
+ let position;
78
+ let tab;
79
+ if ("row" === targetOrientation) position = pctX < 0.5 ? WEST : EAST;
80
+ else if (rect.header && boxContainsPoint(rect.header, x, y)) {
81
+ position = HEADER;
82
+ if (rect.Stack) {
83
+ const tabCount = rect.Stack.length;
84
+ if (0 === tabCount) tab = {
85
+ index: -1,
86
+ left: rect.left,
87
+ positionRelativeToTab: AFTER,
88
+ width: 0
89
+ };
90
+ else {
91
+ const targetTab = rect.Stack.find(({ left, right })=>x >= left && x <= right);
92
+ if (targetTab) {
93
+ const tabWidth = targetTab.right - targetTab.left;
94
+ tab = {
95
+ index: rect.Stack.indexOf(targetTab),
96
+ left: targetTab.left,
97
+ positionRelativeToTab: (x - targetTab.left) / tabWidth < 0.5 ? BEFORE : AFTER,
98
+ width: tabWidth
99
+ };
100
+ } else {
101
+ const lastTab = rect.Stack[tabCount - 1];
102
+ tab = {
103
+ left: lastTab.right,
104
+ width: 0,
105
+ index: tabCount,
106
+ positionRelativeToTab: AFTER
107
+ };
108
+ }
109
+ }
110
+ } else if (rect.header.titleWidth) {
111
+ const tabWidth = rect.header.titleWidth;
112
+ tab = {
113
+ index: -1,
114
+ left: rect.left,
115
+ positionRelativeToTab: (x - rect.left) / tabWidth < 0.5 ? BEFORE : AFTER,
116
+ width: tabWidth
117
+ };
118
+ } else tab = {
119
+ left: rect.left,
120
+ width: 0,
121
+ positionRelativeToTab: BEFORE,
122
+ index: -1
123
+ };
124
+ } else position = getPositionWithinBox(x, y, rect, pctX, pctY);
125
+ return {
126
+ position: position,
127
+ x,
128
+ y,
129
+ closeToTheEdge,
130
+ tab
131
+ };
132
+ }
133
+ function getPositionWithinBox(x, y, rect, pctX, pctY) {
134
+ const centerBox = getCenteredBox(rect, 0.2);
135
+ if (boxContainsPoint(centerBox, x, y)) return CENTRE;
136
+ {
137
+ const quadrant = `${pctY < 0.5 ? "north" : "south"}${pctX < 0.5 ? "west" : "east"}`;
138
+ switch(quadrant){
139
+ case "northwest":
140
+ return pctX > pctY ? NORTH : WEST;
141
+ case "northeast":
142
+ return 1 - pctX > pctY ? NORTH : EAST;
143
+ case "southeast":
144
+ return pctX > pctY ? EAST : SOUTH;
145
+ case "southwest":
146
+ return 1 - pctX > pctY ? WEST : SOUTH;
147
+ default:
148
+ }
149
+ }
150
+ }
151
+ function getCenteredBox({ right, left, top, bottom }, pctSize) {
152
+ const pctOffset = (1 - pctSize) / 2;
153
+ const w = (right - left) * pctOffset;
154
+ const h = (bottom - top) * pctOffset;
155
+ return {
156
+ left: left + w,
157
+ top: top + h,
158
+ right: right - w,
159
+ bottom: bottom - h
160
+ };
161
+ }
162
+ function measureRootComponent(rootComponent, measurements, dropTargets) {
163
+ const { id, "data-path": dataPath, path = dataPath } = getProps(rootComponent);
164
+ const type = typeOf(rootComponent);
165
+ if (id && path) {
166
+ const [rect, el] = measureComponentDomElement(rootComponent);
167
+ measureComponent(rootComponent, rect, el, measurements);
168
+ if (isContainer(type)) collectChildMeasurements(rootComponent, measurements, dropTargets);
169
+ }
170
+ }
171
+ function measureComponent(component, rect, el, measurements) {
172
+ const { "data-path": dataPath, path = dataPath, header } = getProps(component);
173
+ measurements[path] = rect;
174
+ const type = typeOf(component);
175
+ if (header || "Stack" === type) {
176
+ const query = "Stack" === type ? ".vuuTabstrip" : ".vuuHeader";
177
+ const headerEl = el.querySelector(query);
178
+ if (headerEl) {
179
+ const { top, left, right, bottom } = headerEl.getBoundingClientRect();
180
+ measurements[path].header = {
181
+ top: Math.round(top),
182
+ left: Math.round(left),
183
+ right: Math.round(right),
184
+ bottom: Math.round(bottom)
185
+ };
186
+ if ("Stack" === type) measurements[path].Stack = Array.from(headerEl.querySelectorAll(".vuuTab")).map((tab)=>tab.getBoundingClientRect()).map(({ left, right })=>({
187
+ left,
188
+ right
189
+ }));
190
+ else {
191
+ const titleEl = headerEl.querySelector('[class^="vuuHeader-title"]');
192
+ const { header } = measurements[path];
193
+ if (titleEl && header) header.titleWidth = titleEl.clientWidth;
194
+ }
195
+ }
196
+ }
197
+ return measurements[path];
198
+ }
199
+ function collectChildMeasurements(component, measurements, dropTargets, preX = 0, posX = 0, preY = 0, posY = 0) {
200
+ const { children, "data-path": dataPath, path = dataPath, style, active = 0 } = getProps(component);
201
+ const type = typeOf(component);
202
+ const isFlexbox = "Flexbox" === type;
203
+ const isStack = "Stack" === type;
204
+ const isTower = isFlexbox && "column" === style.flexDirection;
205
+ const isTerrace = isFlexbox && "row" === style.flexDirection;
206
+ const childrenToMeasure = isStack ? children.filter((_child, idx)=>idx === active) : children.filter(omitDragging);
207
+ const childMeasurements = childrenToMeasure.map((child)=>{
208
+ const [rect, el] = measureComponentDomElement(child);
209
+ return [
210
+ {
211
+ ...rect,
212
+ top: rect.top - preY,
213
+ right: rect.right + posX,
214
+ bottom: rect.bottom + posY,
215
+ left: rect.left - preX
216
+ },
217
+ el,
218
+ child
219
+ ];
220
+ });
221
+ const expandedMeasurements = childMeasurements.map(([rect, el, child], i, all)=>{
222
+ let localPreX;
223
+ let localPosX;
224
+ let localPreY;
225
+ let localPosY;
226
+ let gapPre;
227
+ let gapPos;
228
+ const n = all.length - 1;
229
+ if (isTerrace) {
230
+ gapPre = 0 === i ? 0 : rect.left - all[i - 1][0].right;
231
+ gapPos = i === n ? 0 : all[i + 1][0].left - rect.right;
232
+ localPreX = 0 === i ? 0 : 0 === gapPre ? 0 : gapPre;
233
+ localPosX = i === n ? 0 : 0 === gapPos ? 0 : gapPos - gapPos / 2;
234
+ rect.left -= localPreX;
235
+ rect.right += localPosX;
236
+ localPreY = preY;
237
+ localPosY = posY;
238
+ } else if (isTower) {
239
+ gapPre = 0 === i ? 0 : rect.top - all[i - 1][0].bottom;
240
+ gapPos = i === n ? 0 : all[i + 1][0].top - rect.bottom;
241
+ localPreY = 0 === i ? 0 : 0 === gapPre ? 0 : gapPre;
242
+ localPosY = i === n ? 0 : 0 === gapPos ? 0 : gapPos - gapPos / 2;
243
+ rect.top -= localPreY;
244
+ rect.bottom += localPosY;
245
+ localPreX = preX;
246
+ localPosX = posX;
247
+ }
248
+ const componentMeasurements = measureComponent(child, rect, el, measurements);
249
+ const childType = typeOf(child);
250
+ if (isContainer(childType)) collectChildMeasurements(child, measurements, dropTargets, localPreX, localPosX, localPreY, localPosY);
251
+ return componentMeasurements;
252
+ });
253
+ if (childMeasurements.length) measurements[path].children = expandedMeasurements;
254
+ }
255
+ function omitDragging(component) {
256
+ const { id } = getProps(component);
257
+ const el = document.getElementById(id);
258
+ if (el) return "true" !== el.dataset.dragging;
259
+ console.warn(`BoxModel: no element found with id #${id}`);
260
+ return false;
261
+ }
262
+ function measureComponentDomElement(component) {
263
+ const { id } = getProps(component);
264
+ if (void 0 === id) throw Error("`BoxModel.measureComponentElement, component has no id");
265
+ const el = document.getElementById(id);
266
+ if (!el) throw Error("BoxModel.measureComponentElement, no DOM element found for component");
267
+ const { top, left, right, bottom, height, width } = el.getBoundingClientRect();
268
+ let scrolling;
269
+ const type = typeOf(component);
270
+ if (isContainer(type)) {
271
+ const scrollHeight = el.scrollHeight;
272
+ if (scrollHeight > height) scrolling = {
273
+ id,
274
+ scrollHeight,
275
+ scrollTop: el.scrollTop
276
+ };
277
+ }
278
+ return [
279
+ {
280
+ top: Math.round(top),
281
+ left: Math.round(left),
282
+ right: Math.round(right),
283
+ bottom: Math.round(bottom),
284
+ height: Math.round(height),
285
+ width: Math.round(width),
286
+ scrolling
287
+ },
288
+ el,
289
+ component
290
+ ];
291
+ }
292
+ function allBoxesContainingPoint(component, measurements, x, y, dropTargets, boxes = []) {
293
+ const { children, "data-path": dataPath, path = dataPath } = getProps(component);
294
+ const type = typeOf(component);
295
+ const rect = measurements[path];
296
+ if (!boxContainsPoint(rect, x, y)) return boxes;
297
+ if (dropTargets && dropTargets.length) {
298
+ if (dropTargets.includes(path)) boxes.push(component);
299
+ else if (!dropTargets.some((dropTargetPath)=>dropTargetPath.startsWith(path))) return boxes;
300
+ } else boxes.push(component);
301
+ if (!isContainer(type)) return boxes;
302
+ if (rect.header && boxContainsPoint(rect.header, x, y)) return boxes;
303
+ if (rect.scrolling) scrollIntoViewIfNeccesary(rect, x, y);
304
+ for(let i = 0; i < children.length; i++){
305
+ if ("Stack" === type && component.props.active !== i) continue;
306
+ const nestedBoxes = allBoxesContainingPoint(children[i], measurements, x, y, dropTargets);
307
+ if (nestedBoxes.length) return boxes.concat(nestedBoxes);
308
+ }
309
+ return boxes;
310
+ }
311
+ function scrollIntoViewIfNeccesary({ top, bottom, scrolling }, x, y) {
312
+ if (!scrolling) return false;
313
+ {
314
+ const { id, scrollTop, scrollHeight } = scrolling;
315
+ const height = bottom - top;
316
+ if (0 === scrollTop && bottom - y < 50) {
317
+ const scrollMax = scrollHeight - height;
318
+ const el = document.getElementById(id);
319
+ el.scrollTo({
320
+ left: 0,
321
+ top: scrollMax,
322
+ behavior: "smooth"
323
+ });
324
+ scrolling.scrollTop = scrollMax;
325
+ } else {
326
+ if (!(scrollTop > 0) || !(y - top < 50)) return false;
327
+ const el = document.getElementById(id);
328
+ el.scrollTo({
329
+ left: 0,
330
+ top: 0,
331
+ behavior: "smooth"
332
+ });
333
+ scrolling.scrollTop = 0;
334
+ }
335
+ }
336
+ }
337
+ export { BoxModel, Position, RelativeDropPosition, getPosition, pointPositionWithinRect, positionValues };
@@ -0,0 +1,119 @@
1
+ import { pointPositionWithinRect } from "./BoxModel.js";
2
+ const SCALE_FACTOR = 0.4;
3
+ class DragState {
4
+ constraint;
5
+ x;
6
+ y;
7
+ intrinsicSize;
8
+ constructor(zone, mouseX, mouseY, measurements, intrinsicSize){
9
+ this.init(zone, mouseX, mouseY, measurements, intrinsicSize);
10
+ }
11
+ init(zone, mouseX, mouseY, rect, intrinsicSize) {
12
+ const { left: x, top: y } = rect;
13
+ const { pctX, pctY } = pointPositionWithinRect(mouseX, mouseY, rect);
14
+ const scaleFactor = SCALE_FACTOR;
15
+ const leadX = pctX * rect.width;
16
+ const trailX = rect.width - leadX;
17
+ const leadY = pctY * rect.height;
18
+ const trailY = rect.height - leadY;
19
+ const scaledWidth = rect.width * scaleFactor, scaledHeight = rect.height * scaleFactor;
20
+ const scaleDiff = 1 - scaleFactor;
21
+ const leadXScaleDiff = leadX * scaleDiff;
22
+ const leadYScaleDiff = leadY * scaleDiff;
23
+ const trailXScaleDiff = trailX * scaleDiff;
24
+ const trailYScaleDiff = trailY * scaleDiff;
25
+ this.intrinsicSize = intrinsicSize;
26
+ this.constraint = {
27
+ zone: {
28
+ x: {
29
+ lo: zone.left,
30
+ hi: zone.right
31
+ },
32
+ y: {
33
+ lo: zone.top,
34
+ hi: zone.bottom
35
+ }
36
+ },
37
+ pos: {
38
+ x: {
39
+ lo: zone.left - leadXScaleDiff,
40
+ hi: zone.right - rect.width + trailXScaleDiff
41
+ },
42
+ y: {
43
+ lo: zone.top - leadYScaleDiff,
44
+ hi: zone.bottom - rect.height + trailYScaleDiff
45
+ }
46
+ },
47
+ mouse: {
48
+ x: {
49
+ lo: zone.left + scaledWidth * pctX,
50
+ hi: zone.right - scaledWidth * (1 - pctX)
51
+ },
52
+ y: {
53
+ lo: zone.top + scaledHeight * pctY,
54
+ hi: zone.bottom - scaledHeight * (1 - pctY)
55
+ }
56
+ }
57
+ };
58
+ this.x = {
59
+ pos: x,
60
+ lo: false,
61
+ hi: false,
62
+ mousePos: mouseX,
63
+ mousePct: pctX
64
+ };
65
+ this.y = {
66
+ pos: y,
67
+ lo: false,
68
+ hi: false,
69
+ mousePos: mouseY,
70
+ mousePct: pctY
71
+ };
72
+ }
73
+ outOfBounds() {
74
+ return this.x.lo || this.x.hi || this.y.lo || this.y.hi;
75
+ }
76
+ inBounds() {
77
+ return !this.outOfBounds();
78
+ }
79
+ dropX() {
80
+ return this.dropXY('x');
81
+ }
82
+ dropY() {
83
+ return this.dropXY('y');
84
+ }
85
+ hasIntrinsicSize() {
86
+ return this?.intrinsicSize?.height && this?.intrinsicSize?.width;
87
+ }
88
+ update(xy, mousePos) {
89
+ const state = this[xy], mouseConstraint = this.constraint.mouse[xy], posConstraint = this.constraint.pos[xy], previousPos = state.pos;
90
+ const diff = mousePos - state.mousePos;
91
+ if (diff < 0) if (state.lo) ;
92
+ else if (mousePos < mouseConstraint.lo) {
93
+ state.lo = true;
94
+ state.pos = posConstraint.lo;
95
+ } else if (state.hi) {
96
+ if (mousePos < mouseConstraint.hi) {
97
+ state.hi = false;
98
+ state.pos += diff;
99
+ }
100
+ } else state.pos += diff;
101
+ else if (diff > 0) if (state.hi) ;
102
+ else if (mousePos > mouseConstraint.hi) {
103
+ state.hi = true;
104
+ state.pos = posConstraint.hi;
105
+ } else if (state.lo) {
106
+ if (mousePos > mouseConstraint.lo) {
107
+ state.lo = false;
108
+ state.pos += diff;
109
+ }
110
+ } else state.pos += diff;
111
+ state.mousePos = mousePos;
112
+ return previousPos !== state.pos;
113
+ }
114
+ dropXY(dir) {
115
+ const pos = this[dir], rect = this.constraint.zone[dir];
116
+ return pos.lo ? Math.max(rect.lo, pos.mousePos) : pos.hi ? Math.min(pos.mousePos, Math.round(rect.hi) - 1) : pos.mousePos;
117
+ }
118
+ }
119
+ export { DragState };
@@ -0,0 +1,140 @@
1
+ import { findTarget, followPath, getProps } from "../utils/index.js";
2
+ import { BoxModel } from "./BoxModel.js";
3
+ import { DragState } from "./DragState.js";
4
+ import { DropTarget, identifyDropTarget } from "./DropTarget.js";
5
+ import DropTargetRenderer from "./DropTargetRenderer.js";
6
+ let _dragStartCallback;
7
+ let _dragMoveCallback;
8
+ let _dragEndCallback;
9
+ let _dragStartX;
10
+ let _dragStartY;
11
+ let _dragContainer;
12
+ let _dragState;
13
+ let _dropTarget = null;
14
+ let _validDropTargetPaths;
15
+ let _dragInstructions;
16
+ let _measurements;
17
+ let _simpleDrag;
18
+ let _dragThreshold;
19
+ let _mouseDownTimer = null;
20
+ const DEFAULT_DRAG_THRESHOLD = 3;
21
+ const _dropTargetRenderer = new DropTargetRenderer();
22
+ const SCALE_FACTOR = 0.4;
23
+ function getDragContainer(rootContainer, dragContainerPath) {
24
+ if (dragContainerPath) return followPath(rootContainer, dragContainerPath);
25
+ return findTarget(rootContainer, (props)=>props.dropTarget);
26
+ }
27
+ const Draggable = {
28
+ handleMousedown (e, dragStartCallback, dragInstructions = {}) {
29
+ _dragStartCallback = dragStartCallback;
30
+ _dragInstructions = dragInstructions;
31
+ _dragStartX = e.clientX;
32
+ _dragStartY = e.clientY;
33
+ _dragThreshold = void 0 === dragInstructions.dragThreshold ? DEFAULT_DRAG_THRESHOLD : dragInstructions.dragThreshold;
34
+ if (0 === _dragThreshold) _dragStartCallback(e, 0, 0);
35
+ else {
36
+ window.addEventListener("mousemove", preDragMousemoveHandler, false);
37
+ window.addEventListener("mouseup", preDragMouseupHandler, false);
38
+ _mouseDownTimer = window.setTimeout(()=>{
39
+ window.removeEventListener("mousemove", preDragMousemoveHandler, false);
40
+ window.removeEventListener("mouseup", preDragMouseupHandler, false);
41
+ _dragStartCallback?.(e, 0, 0);
42
+ }, 500);
43
+ }
44
+ e.preventDefault();
45
+ },
46
+ initDrag (rootContainer, dragContainerPath, { top, left, right, bottom }, dragPos, dragHandler, intrinsicSize, dropTargets) {
47
+ ({ drag: _dragMoveCallback, drop: _dragEndCallback } = dragHandler);
48
+ return initDrag(rootContainer, dragContainerPath, {
49
+ top,
50
+ left,
51
+ right,
52
+ bottom
53
+ }, dragPos, intrinsicSize, dropTargets);
54
+ }
55
+ };
56
+ function preDragMousemoveHandler(e) {
57
+ const x = true;
58
+ const y = true;
59
+ const x_diff = x ? e.clientX - _dragStartX : 0;
60
+ const y_diff = y ? e.clientY - _dragStartY : 0;
61
+ const mouseMoveDistance = Math.max(Math.abs(x_diff), Math.abs(y_diff));
62
+ if (mouseMoveDistance > _dragThreshold) {
63
+ window.removeEventListener("mousemove", preDragMousemoveHandler, false);
64
+ window.removeEventListener("mouseup", preDragMouseupHandler, false);
65
+ _dragStartCallback?.(e, x_diff, y_diff);
66
+ _dragStartCallback = null;
67
+ }
68
+ }
69
+ function preDragMouseupHandler() {
70
+ if (_mouseDownTimer) {
71
+ window.clearTimeout(_mouseDownTimer);
72
+ _mouseDownTimer = null;
73
+ }
74
+ window.removeEventListener("mousemove", preDragMousemoveHandler, false);
75
+ window.removeEventListener("mouseup", preDragMouseupHandler, false);
76
+ }
77
+ function initDrag(rootContainer, dragContainerPath, dragRect, dragPos, intrinsicSize, dropTargets) {
78
+ _dragContainer = getDragContainer(rootContainer, dragContainerPath);
79
+ const { "data-path": dataPath, path = dataPath } = getProps(_dragContainer);
80
+ if (dropTargets) {
81
+ const dropPaths = dropTargets.map((id)=>findTarget(rootContainer, (props)=>props.id === id)).map((target)=>target.props.path);
82
+ _validDropTargetPaths = dropPaths;
83
+ }
84
+ _measurements = BoxModel.measure(_dragContainer, dropTargets);
85
+ const dragZone = _measurements[path];
86
+ _dragState = new DragState(dragZone, dragPos.x, dragPos.y, dragRect, intrinsicSize);
87
+ const pctX = Math.round(100 * _dragState.x.mousePct);
88
+ const pctY = Math.round(100 * _dragState.y.mousePct);
89
+ window.addEventListener("mousemove", dragMousemoveHandler, false);
90
+ window.addEventListener("mouseup", dragMouseupHandler, false);
91
+ window.addEventListener("keydown", dragKeydownHandler, false);
92
+ _simpleDrag = false;
93
+ _dropTargetRenderer.prepare(dragRect, "tab-only");
94
+ return _dragInstructions.DoNotTransform ? "transform:none" : `transform:scale(${SCALE_FACTOR},${SCALE_FACTOR});transform-origin:${pctX}% ${pctY}%;`;
95
+ }
96
+ function dragMousemoveHandler(evt) {
97
+ const x = evt.clientX;
98
+ const y = evt.clientY;
99
+ const dragState = _dragState;
100
+ const currentDropTarget = _dropTarget;
101
+ let dropTarget;
102
+ let newX, newY;
103
+ if (dragState.update("x", x)) newX = dragState.x.pos;
104
+ if (dragState.update("y", y)) newY = dragState.y.pos;
105
+ void 0 === newX && void 0 === newY || _dragMoveCallback?.(newX, newY);
106
+ if (_simpleDrag || void 0 === _dragContainer) return;
107
+ dropTarget = dragState.inBounds() ? identifyDropTarget(x, y, _dragContainer, _measurements, dragState.hasIntrinsicSize(), _validDropTargetPaths) : identifyDropTarget(dragState.dropX(), dragState.dropY(), _dragContainer, _measurements);
108
+ if (currentDropTarget) {
109
+ if (null == dropTarget || dropTarget.box !== currentDropTarget.box) _dropTarget = null;
110
+ }
111
+ if (dropTarget) {
112
+ _dropTargetRenderer.draw(dropTarget, dragState);
113
+ _dropTarget = dropTarget;
114
+ }
115
+ }
116
+ function dragKeydownHandler(evt) {
117
+ if ("Escape" === evt.key) {
118
+ _dropTarget = null;
119
+ onDragEnd();
120
+ }
121
+ }
122
+ function dragMouseupHandler() {
123
+ onDragEnd();
124
+ }
125
+ function onDragEnd() {
126
+ if (_dropTarget) {
127
+ const dropTarget = _dropTargetRenderer.hoverDropTarget || DropTarget.getActiveDropTarget(_dropTarget);
128
+ _dragEndCallback?.(dropTarget);
129
+ _dropTarget = null;
130
+ } else _dragEndCallback?.();
131
+ _dragMoveCallback = null;
132
+ _dragEndCallback = null;
133
+ _dragContainer = void 0;
134
+ _dropTargetRenderer.clear();
135
+ _validDropTargetPaths = void 0;
136
+ window.removeEventListener("mousemove", dragMousemoveHandler, false);
137
+ window.removeEventListener("mouseup", dragMouseupHandler, false);
138
+ window.removeEventListener("keydown", dragKeydownHandler, false);
139
+ }
140
+ export { Draggable };