@vuu-ui/vuu-layout 0.5.4 → 0.5.6

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 (147) hide show
  1. package/README.md +1 -0
  2. package/cjs/index.js +4 -7379
  3. package/cjs/index.js.map +7 -0
  4. package/esm/index.js +20 -0
  5. package/esm/index.js.map +7 -0
  6. package/index.css +1 -951
  7. package/index.css.map +3 -3
  8. package/package.json +5 -9
  9. package/src/Component.css +0 -2
  10. package/src/Component.tsx +0 -20
  11. package/src/DraggableLayout.css +0 -18
  12. package/src/DraggableLayout.tsx +0 -29
  13. package/src/__tests__/flexbox-utils.spec.js +0 -90
  14. package/src/action-buttons/index.ts +0 -1
  15. package/src/chest-of-drawers/Chest.css +0 -36
  16. package/src/chest-of-drawers/Chest.tsx +0 -42
  17. package/src/chest-of-drawers/Drawer.css +0 -159
  18. package/src/chest-of-drawers/Drawer.tsx +0 -118
  19. package/src/chest-of-drawers/index.ts +0 -2
  20. package/src/common-types.ts +0 -9
  21. package/src/debug.ts +0 -16
  22. package/src/dialog/Dialog.css +0 -16
  23. package/src/dialog/Dialog.tsx +0 -59
  24. package/src/dialog/index.ts +0 -1
  25. package/src/drag-drop/BoxModel.ts +0 -546
  26. package/src/drag-drop/DragState.ts +0 -222
  27. package/src/drag-drop/Draggable.ts +0 -282
  28. package/src/drag-drop/DropMenu.css +0 -71
  29. package/src/drag-drop/DropMenu.tsx +0 -61
  30. package/src/drag-drop/DropTarget.ts +0 -392
  31. package/src/drag-drop/DropTargetRenderer.css +0 -40
  32. package/src/drag-drop/DropTargetRenderer.tsx +0 -279
  33. package/src/drag-drop/dragDropTypes.ts +0 -49
  34. package/src/drag-drop/index.ts +0 -4
  35. package/src/editable-label/EditableLabel.css +0 -28
  36. package/src/editable-label/EditableLabel.tsx +0 -99
  37. package/src/editable-label/index.ts +0 -1
  38. package/src/flexbox/Flexbox.css +0 -45
  39. package/src/flexbox/Flexbox.tsx +0 -70
  40. package/src/flexbox/FlexboxLayout.jsx +0 -26
  41. package/src/flexbox/FluidGrid.css +0 -134
  42. package/src/flexbox/FluidGrid.tsx +0 -84
  43. package/src/flexbox/FluidGridLayout.tsx +0 -10
  44. package/src/flexbox/Splitter.css +0 -140
  45. package/src/flexbox/Splitter.tsx +0 -135
  46. package/src/flexbox/flexbox-utils.ts +0 -128
  47. package/src/flexbox/flexboxTypes.ts +0 -63
  48. package/src/flexbox/index.ts +0 -4
  49. package/src/flexbox/useResponsiveSizing.ts +0 -85
  50. package/src/flexbox/useSplitterResizing.ts +0 -272
  51. package/src/index.ts +0 -21
  52. package/src/layout-action.ts +0 -21
  53. package/src/layout-header/ActionButton.tsx +0 -23
  54. package/src/layout-header/Header.css +0 -8
  55. package/src/layout-header/Header.tsx +0 -222
  56. package/src/layout-header/index.ts +0 -1
  57. package/src/layout-provider/LayoutProvider.tsx +0 -160
  58. package/src/layout-provider/LayoutProviderContext.ts +0 -17
  59. package/src/layout-provider/index.ts +0 -2
  60. package/src/layout-provider/useLayoutDragDrop.ts +0 -241
  61. package/src/layout-reducer/flexUtils.ts +0 -281
  62. package/src/layout-reducer/index.ts +0 -4
  63. package/src/layout-reducer/insert-layout-element.ts +0 -365
  64. package/src/layout-reducer/layout-reducer.ts +0 -255
  65. package/src/layout-reducer/layoutTypes.ts +0 -151
  66. package/src/layout-reducer/layoutUtils.ts +0 -302
  67. package/src/layout-reducer/remove-layout-element.ts +0 -240
  68. package/src/layout-reducer/replace-layout-element.ts +0 -118
  69. package/src/layout-reducer/resize-flex-children.ts +0 -56
  70. package/src/layout-reducer/wrap-layout-element.ts +0 -317
  71. package/src/layout-view/View.css +0 -61
  72. package/src/layout-view/View.tsx +0 -149
  73. package/src/layout-view/ViewContext.ts +0 -31
  74. package/src/layout-view/index.ts +0 -4
  75. package/src/layout-view/useView.tsx +0 -104
  76. package/src/layout-view/useViewActionDispatcher.ts +0 -133
  77. package/src/layout-view/useViewResize.ts +0 -53
  78. package/src/layout-view/viewTypes.ts +0 -37
  79. package/src/menu/ContextMenu.css +0 -22
  80. package/src/menu/ContextMenu.jsx +0 -121
  81. package/src/menu/MenuList.css +0 -150
  82. package/src/menu/MenuList.jsx +0 -179
  83. package/src/menu/aim/aim.js +0 -92
  84. package/src/menu/aim/corners.js +0 -114
  85. package/src/menu/aim/point-in-polygon.js +0 -25
  86. package/src/menu/aim/utils.js +0 -19
  87. package/src/menu/context-menu-provider.jsx +0 -135
  88. package/src/menu/index.js +0 -4
  89. package/src/menu/key-code.js +0 -61
  90. package/src/menu/list-dom-utils.js +0 -22
  91. package/src/menu/use-cascade.js +0 -292
  92. package/src/menu/use-click-away.js +0 -22
  93. package/src/menu/use-items-with-ids.js +0 -75
  94. package/src/menu/use-keyboard-navigation.js +0 -162
  95. package/src/menu/utils.js +0 -5
  96. package/src/palette/Palette.css +0 -37
  97. package/src/palette/Palette.tsx +0 -140
  98. package/src/palette/PaletteSalt.css +0 -9
  99. package/src/palette/PaletteSalt.tsx +0 -79
  100. package/src/palette/index.ts +0 -2
  101. package/src/placeholder/Placeholder.css +0 -10
  102. package/src/placeholder/Placeholder.tsx +0 -39
  103. package/src/placeholder/index.ts +0 -1
  104. package/src/popup/index.js +0 -2
  105. package/src/popup/popup-provider.js +0 -0
  106. package/src/popup/popup-service.css +0 -15
  107. package/src/popup/popup-service.js +0 -281
  108. package/src/portal/Portal.jsx +0 -50
  109. package/src/portal/index.ts +0 -3
  110. package/src/portal/render-portal.jsx +0 -68
  111. package/src/portal/utils.js +0 -16
  112. package/src/registry/ComponentRegistry.ts +0 -35
  113. package/src/registry/index.ts +0 -1
  114. package/src/responsive/OverflowMenu.css +0 -31
  115. package/src/responsive/OverflowMenu.jsx +0 -56
  116. package/src/responsive/breakpoints.ts +0 -62
  117. package/src/responsive/index.ts +0 -4
  118. package/src/responsive/measureMinimumNodeSize.ts +0 -23
  119. package/src/responsive/overflowUtils.js +0 -14
  120. package/src/responsive/use-breakpoints.ts +0 -100
  121. package/src/responsive/useOverflowObserver.ts +0 -606
  122. package/src/responsive/useResizeObserver.ts +0 -154
  123. package/src/responsive/utils.ts +0 -37
  124. package/src/stack/Stack.css +0 -39
  125. package/src/stack/Stack.tsx +0 -161
  126. package/src/stack/StackLayout.tsx +0 -137
  127. package/src/stack/index.ts +0 -3
  128. package/src/stack/stackTypes.ts +0 -19
  129. package/src/tabs/TabPanel.css +0 -12
  130. package/src/tabs/TabPanel.tsx +0 -17
  131. package/src/tabs/index.ts +0 -1
  132. package/src/tools/config-wrapper/ConfigWrapper.jsx +0 -53
  133. package/src/tools/config-wrapper/index.js +0 -1
  134. package/src/tools/devtools-box/layout-configurator.css +0 -112
  135. package/src/tools/devtools-box/layout-configurator.jsx +0 -369
  136. package/src/tools/devtools-tree/layout-tree-viewer.css +0 -15
  137. package/src/tools/devtools-tree/layout-tree-viewer.jsx +0 -36
  138. package/src/tools/index.js +0 -3
  139. package/src/use-persistent-state.ts +0 -115
  140. package/src/utils/apply-handlers.js +0 -15
  141. package/src/utils/componentFromLayout.tsx +0 -30
  142. package/src/utils/index.ts +0 -6
  143. package/src/utils/pathUtils.ts +0 -294
  144. package/src/utils/propUtils.ts +0 -24
  145. package/src/utils/refUtils.ts +0 -16
  146. package/src/utils/styleUtils.ts +0 -14
  147. package/src/utils/typeOf.ts +0 -22
@@ -1,392 +0,0 @@
1
- import {
2
- BoxModel,
3
- positionValues,
4
- getPosition,
5
- Position,
6
- RelativeDropPosition,
7
- Measurements,
8
- } from "./BoxModel";
9
- import { getProps, typeOf } from "../utils";
10
- import { DragDropRect, DropPos, DropPosTab } from "./dragDropTypes";
11
- import { LayoutModel } from "../layout-reducer";
12
- import { DragState } from "./DragState";
13
- import { rect, rectTuple } from "../common-types";
14
-
15
- export const isTabstrip = (dropTarget: DropTarget) =>
16
- dropTarget.pos.tab &&
17
- typeOf(dropTarget.component) === "Stack" &&
18
- dropTarget.pos.position.Header;
19
-
20
- const { north, south, east, west } = positionValues;
21
- const eastwest = east + west;
22
- const northsouth = north + south;
23
-
24
- export interface DropTargetProps {
25
- component: LayoutModel;
26
- pos: DropPos;
27
- clientRect: DragDropRect;
28
- nextDropTarget: DropTarget | null;
29
- }
30
-
31
- export type GuideLine = [
32
- number,
33
- number,
34
- number,
35
- number,
36
- number,
37
- number,
38
- number,
39
- number
40
- ];
41
- export interface TargetDropOutline {
42
- l: number;
43
- r: number;
44
- t: number;
45
- b: number;
46
- tabLeft?: number;
47
- tabWidth?: number;
48
- tabHeight?: number;
49
- guideLines?: GuideLine;
50
- }
51
-
52
- export class DropTarget {
53
- private active: boolean;
54
-
55
- public box: any;
56
- public clientRect: DragDropRect;
57
- public component: LayoutModel;
58
- public dropRect: rectTuple | undefined;
59
- public nextDropTarget: DropTarget | null;
60
- public pos: DropPos;
61
-
62
- constructor({
63
- component,
64
- pos,
65
- clientRect /*, closeToTheEdge*/,
66
- nextDropTarget,
67
- }: DropTargetProps) {
68
- this.component = component;
69
- this.pos = pos;
70
- this.clientRect = clientRect;
71
- this.nextDropTarget = nextDropTarget;
72
- this.active = false;
73
- this.dropRect = undefined;
74
- }
75
-
76
- targetTabPos(tab: DropPosTab) {
77
- const { left: tabLeft, width: tabWidth, positionRelativeToTab } = tab;
78
- return positionRelativeToTab === RelativeDropPosition.BEFORE
79
- ? tabLeft
80
- : tabLeft + tabWidth;
81
- }
82
-
83
- /**
84
- * Determine what will be rendered by the dropTargetRenderer
85
- *
86
- * @param {*} lineWidth
87
- * @param {*} dragState
88
- * @returns {l, t, r, b, tabLeft, tabWidth, tabHeight}
89
- */
90
- getTargetDropOutline(
91
- lineWidth: number,
92
- dragState?: DragState
93
- ): TargetDropOutline {
94
- if (this.pos.tab) {
95
- return this.getDropTabOutline(lineWidth, this.pos.tab);
96
- } else if (dragState && dragState.hasIntrinsicSize()) {
97
- return this.getIntrinsicDropRect(dragState);
98
- } else {
99
- const [l, t, r, b] = this.getDropRectOutline(
100
- lineWidth,
101
- dragState
102
- ) as rectTuple;
103
- return { l, t, r, b };
104
- }
105
- }
106
-
107
- getDropTabOutline(lineWidth: number, tab: DropPosTab): TargetDropOutline {
108
- const {
109
- clientRect: { top, left, right, bottom, header },
110
- } = this;
111
-
112
- const inset = 0;
113
- const gap = Math.round(lineWidth / 2) + inset;
114
-
115
- const t = Math.round(top);
116
- const l = Math.round(left + gap);
117
- const r = Math.round(right - gap);
118
- const b = Math.round(bottom - gap);
119
- const tabLeft = this.targetTabPos(tab);
120
- const tabWidth = 60; // should really measure text
121
- const tabHeight = header!.bottom - header!.top;
122
- return { l, t, r, b, tabLeft, tabWidth, tabHeight };
123
- }
124
-
125
- getIntrinsicDropRect(dragState: DragState): TargetDropOutline {
126
- const { pos, clientRect: rect } = this;
127
-
128
- let { x, y } = dragState;
129
-
130
- let height = dragState.intrinsicSize?.height ?? 0;
131
- let width = dragState.intrinsicSize?.height ?? 0;
132
-
133
- if (height && height > rect.height) {
134
- console.log(`DropTarget: we're going to blow the gaff`);
135
- height = rect.height;
136
- } else if (width && width > rect.width) {
137
- console.log(`DropTarget: we're going to blow the gaff`);
138
- width = rect.width;
139
- }
140
-
141
- const left = Math.min(
142
- rect.right - width,
143
- Math.max(rect.left, Math.round(pos.x - x.mousePct * width))
144
- );
145
- const top = Math.min(
146
- rect.bottom - height,
147
- Math.max(rect.top, Math.round(pos.y - y.mousePct * height))
148
- );
149
- const [l, t, r, b] = (this.dropRect = [
150
- left,
151
- top,
152
- left + width,
153
- top + height,
154
- ]);
155
-
156
- const guideLines: GuideLine = pos.position.EastOrWest
157
- ? [l, rect.top, l, rect.bottom, r, rect.top, r, rect.bottom]
158
- : [rect.left, t, rect.right, t, rect.left, b, rect.right, b];
159
-
160
- return { l, r, t, b, guideLines };
161
- }
162
-
163
- /**
164
- * @returns [left, top, right, bottom]
165
- */
166
- getDropRectOutline(lineWidth: number, dragState?: DragState) {
167
- const { pos, clientRect: rect } = this;
168
- const { width: suggestedWidth, height: suggestedHeight, position } = pos;
169
-
170
- const { width: intrinsicWidth, height: intrinsicHeight } =
171
- dragState?.intrinsicSize ?? {};
172
- const sizeHeight = intrinsicHeight ?? suggestedHeight ?? 0;
173
- const sizeWidth = intrinsicWidth ?? suggestedWidth ?? 0;
174
-
175
- this.dropRect = undefined;
176
-
177
- const { top: t, left: l, right: r, bottom: b } = rect;
178
-
179
- const inset = 0;
180
- const gap = Math.round(lineWidth / 2) + inset;
181
-
182
- switch (position) {
183
- case Position.North:
184
- case Position.Header: {
185
- const halfHeight = Math.round((b - t) / 2);
186
- const height = sizeHeight
187
- ? Math.min(halfHeight, Math.round(sizeHeight))
188
- : halfHeight;
189
- return sizeWidth && l + sizeWidth < r
190
- ? [l + gap, t + gap, l + sizeWidth - gap, t + gap + height] // need flex direction indicator
191
- : [l + gap, t + gap, r - gap, t + gap + height];
192
- }
193
- case Position.West: {
194
- const halfWidth = Math.round((r - l) / 2);
195
- const width = sizeWidth
196
- ? Math.min(halfWidth, Math.round(sizeWidth))
197
- : halfWidth;
198
- return sizeHeight && t + sizeHeight < b
199
- ? [l + gap, t + gap, l - gap + width, t + sizeHeight + gap] // need flex direction indicator
200
- : [l + gap, t + gap, l - gap + width, b - gap];
201
- }
202
- case Position.East: {
203
- const halfWidth = Math.round((r - l) / 2);
204
- const width = sizeWidth
205
- ? Math.min(halfWidth, Math.round(sizeWidth))
206
- : halfWidth;
207
- return sizeHeight && t + sizeHeight < b
208
- ? [r - gap - width, t + gap, r - gap, t + sizeHeight + gap] // need flex direction indicator
209
- : [r - gap - width, t + gap, r - gap, b - gap];
210
- }
211
- case Position.South: {
212
- const halfHeight = Math.round((b - t) / 2);
213
- const height = sizeHeight
214
- ? Math.min(halfHeight, Math.round(sizeHeight))
215
- : halfHeight;
216
-
217
- return sizeWidth && l + sizeWidth < r
218
- ? [l + gap, b - gap - height, l + sizeWidth - gap, b - gap] // need flex direction indicator
219
- : [l + gap, b - gap - height, r - gap, b - gap];
220
- }
221
- case Position.Centre: {
222
- return [l + gap, t + gap, r - gap, b - gap];
223
- }
224
- default:
225
- console.warn(`DropTarget does not recognize position ${position}`);
226
- return null;
227
- }
228
- }
229
-
230
- activate() {
231
- this.active = true;
232
- return this;
233
- }
234
-
235
- toArray(this: DropTarget) {
236
- let dropTarget: DropTarget | null = this;
237
- const dropTargets = [dropTarget];
238
- // eslint-disable-next-line no-cond-assign
239
- while ((dropTarget = dropTarget.nextDropTarget)) {
240
- dropTargets.push(dropTarget);
241
- }
242
- return dropTargets;
243
- }
244
-
245
- static getActiveDropTarget(dropTarget: DropTarget | null): DropTarget | null {
246
- return dropTarget === null
247
- ? null
248
- : dropTarget?.active
249
- ? dropTarget
250
- : DropTarget.getActiveDropTarget(dropTarget.nextDropTarget);
251
- }
252
- }
253
-
254
- // Initial entry to this method is always via the app (may be it should be *on* the app)
255
- export function identifyDropTarget(
256
- x: number,
257
- y: number,
258
- rootLayout: LayoutModel,
259
- measurements: Measurements,
260
- intrinsicSize?: number,
261
- validDropTargets?: string[]
262
- ) {
263
- let dropTarget = null;
264
-
265
- const allBoxesContainingPoint = BoxModel.allBoxesContainingPoint(
266
- rootLayout,
267
- measurements,
268
- x,
269
- y,
270
- validDropTargets
271
- );
272
-
273
- if (allBoxesContainingPoint.length) {
274
- const [component, ...containers] = allBoxesContainingPoint;
275
- const {
276
- "data-path": dataPath,
277
- path = dataPath,
278
- "data-row-placeholder": isRowPlaceholder,
279
- } = getProps(component);
280
- const clientRect = measurements[path];
281
- const placeholderOrientation =
282
- intrinsicSize && isRowPlaceholder ? "row" : undefined;
283
- const pos = getPosition(x, y, clientRect, placeholderOrientation);
284
- const box = measurements[path];
285
-
286
- const nextDropTarget = ([nextTarget, ...targets]: LayoutModel[]):
287
- | DropTarget
288
- | undefined => {
289
- if (pos.position?.Header || pos.closeToTheEdge) {
290
- const targetPosition = getTargetPosition(
291
- nextTarget,
292
- pos,
293
- box,
294
- measurements,
295
- x,
296
- y
297
- );
298
- if (targetPosition) {
299
- const [containerPos, clientRect] = targetPosition;
300
-
301
- return new DropTarget({
302
- component: nextTarget,
303
- pos: containerPos,
304
- clientRect,
305
- nextDropTarget: nextDropTarget(targets) ?? null,
306
- });
307
- } else if (targets.length) {
308
- return nextDropTarget(targets);
309
- }
310
- }
311
- };
312
- dropTarget = new DropTarget({
313
- component,
314
- pos,
315
- clientRect,
316
- nextDropTarget: nextDropTarget(containers) ?? null,
317
- }).activate();
318
- }
319
-
320
- return dropTarget;
321
- }
322
-
323
- function getTargetPosition(
324
- container: LayoutModel,
325
- { closeToTheEdge, position }: DropPos,
326
- box: rect,
327
- measurements: Measurements,
328
- x: number,
329
- y: number
330
- ): [DropPos, DragDropRect] | undefined {
331
- if (!container || container.type === "DraggableLayout") {
332
- return;
333
- }
334
-
335
- const containingBox = measurements[container.props.path];
336
- const closeToTop = closeToTheEdge & positionValues.north;
337
- const closeToRight = closeToTheEdge & positionValues.east;
338
- const closeToBottom = closeToTheEdge & positionValues.south;
339
- const closeToLeft = closeToTheEdge & positionValues.west;
340
-
341
- const atTop =
342
- (closeToTop || position.Header) &&
343
- Math.round(box.top) === Math.round(containingBox.top);
344
- const atRight =
345
- closeToRight && Math.round(box.right) === Math.round(containingBox.right);
346
- const atBottom =
347
- closeToBottom &&
348
- Math.round(box.bottom) === Math.round(containingBox.bottom);
349
- const atLeft =
350
- closeToLeft && Math.round(box.left) === Math.round(containingBox.left);
351
-
352
- if (atTop || atRight || atBottom || atLeft) {
353
- const { "data-path": dataPath, path = dataPath } = container.props;
354
- const clientRect = measurements[path];
355
- const containerPos = getPosition(x, y, clientRect);
356
-
357
- // if its a VBox and we're close to left or right ...
358
- if (
359
- (isVBox(container) || isTabbedContainer(container)) &&
360
- closeToTheEdge & eastwest
361
- ) {
362
- containerPos.width = 120;
363
- return [containerPos, clientRect];
364
- }
365
- // if it's a HBox and we're close to top or bottom ...
366
- else if (
367
- (isHBox(container) || isTabbedContainer(container)) &&
368
- (position.Header || closeToTheEdge & northsouth)
369
- ) {
370
- containerPos.height = 120;
371
- return [containerPos, clientRect];
372
- }
373
- }
374
- }
375
-
376
- function isTabbedContainer(component: LayoutModel) {
377
- return typeOf(component) === "Stack";
378
- }
379
-
380
- function isVBox(component: LayoutModel) {
381
- return (
382
- typeOf(component) === "Flexbox" &&
383
- component.props.style.flexDirection === "column"
384
- );
385
- }
386
-
387
- function isHBox(component: LayoutModel) {
388
- return (
389
- typeOf(component) === "Flexbox" &&
390
- component.props.style.flexDirection === "row"
391
- );
392
- }
@@ -1,40 +0,0 @@
1
- #hw-drag-canvas {
2
- visibility: hidden;
3
- z-index: 1;
4
- position: absolute;
5
- top: 0px;
6
- left: 0;
7
- right: 0;
8
- bottom: 0;
9
- background-color: transparent;
10
- }
11
-
12
- #hw-drag-canvas > svg {
13
- position: absolute;
14
- }
15
-
16
- .drawing #hw-drag-canvas {
17
- visibility: visible;
18
- }
19
-
20
- path.drop-target {
21
- stroke: blue;
22
- stroke-width: 4px;
23
- fill: transparent;
24
- }
25
-
26
- path.drop-target.centre {
27
- stroke: red;
28
- }
29
-
30
- #vuu-drop-outline {
31
- fill: rgba(0,0,255,.3);
32
- stroke: none;
33
- stroke-dasharray: 4 2;
34
- }
35
-
36
- #hw-drop-guides {
37
- fill: none;
38
- stroke: rgba(0, 0, 0, 0.3);
39
- stroke-dasharray: 2 3;
40
- }
@@ -1,279 +0,0 @@
1
- import { PopupService } from "../popup";
2
- import { RelativeDropPosition } from "./BoxModel";
3
- import { DragDropRect } from "./dragDropTypes";
4
- import { DragState } from "./DragState";
5
- import { computeMenuPosition, DropMenu } from "./DropMenu";
6
- import { DropTarget, GuideLine } from "./DropTarget";
7
-
8
- import "./DropTargetRenderer.css";
9
-
10
- type Point = [number, number];
11
- type TabMode = "full-view" | "tab-only";
12
-
13
- let _multiDropOptions = false;
14
- let _hoverDropTarget: DropTarget | null = null;
15
- let _shiftedTab: HTMLElement | null = null;
16
-
17
- const onHoverDropTarget = (dropTarget: DropTarget | null) =>
18
- (_hoverDropTarget = dropTarget);
19
-
20
- const start = ([x, y]: Point) => `M${x},${y}`;
21
- const point = ([x, y]: Point) => `L${x},${y}`;
22
- const pathFromPoints = ([p1, ...points]: Point[]) =>
23
- `${start(p1)} ${points.map(point)}Z`;
24
-
25
- const pathFromGuideLines = (guideLines?: GuideLine) => {
26
- if (guideLines) {
27
- const [x1, y1, x2, y2, x3, y3, x4, y4] = guideLines;
28
- return `M${x1},${y1} L${x2},${y2} M${x3},${y3} L${x4},${y4}`;
29
- } else {
30
- return "";
31
- }
32
- };
33
-
34
- function insertSVGRoot() {
35
- if (document.getElementById("hw-drag-canvas") === null) {
36
- const root = document.getElementById("root");
37
- const container = document.createElement("div");
38
- container.id = "hw-drag-canvas";
39
- container.innerHTML = `
40
- <svg width="100%" height="100%">
41
- <path id="hw-drop-guides" />
42
- <path
43
- id="vuu-drop-outline"
44
- d="M300,132 L380,132 L380,100 L460,100 L460,132, L550,132 L550,350 L300,350z">
45
- <animate
46
- attributeName="d"
47
- id="hw-drop-outline-animate"
48
- begin="indefinite"
49
- dur="300ms"
50
- fill="freeze"
51
- to="M255,33 L255,33,L255,1,L315,1,L315,1,L794,1,L794,164,L255,164Z"
52
- />
53
- </path>
54
- </svg>
55
- `;
56
- document.body.insertBefore(container, root);
57
- }
58
- }
59
- export default class DropTargetCanvas {
60
- private currentPath: string | null = null;
61
- private tabMode: TabMode | null = null;
62
-
63
- constructor() {
64
- insertSVGRoot();
65
- }
66
-
67
- prepare(dragRect: DragDropRect, tabMode: TabMode = "full-view") {
68
- // don't do this on body
69
- document.body.classList.add("drawing");
70
- this.currentPath = null;
71
- this.tabMode = tabMode;
72
-
73
- const points = this.getPoints(0, 0, 0, 0);
74
- // const points = this.getPoints(left, top, width, height);
75
- const d = pathFromPoints(points);
76
-
77
- const dropOutlinePath = document.getElementById("vuu-drop-outline");
78
- dropOutlinePath?.setAttribute("d", d);
79
- this.currentPath = d;
80
- }
81
-
82
- clear() {
83
- // don't do this on body
84
- _hoverDropTarget = null;
85
- clearShiftedTab();
86
- document.body.classList.remove("drawing");
87
- PopupService.hidePopup();
88
- }
89
-
90
- get hoverDropTarget() {
91
- return _hoverDropTarget;
92
- }
93
-
94
- getPoints(
95
- x: number,
96
- y: number,
97
- width: number,
98
- height: number,
99
- tabLeft = 0,
100
- tabWidth = 0,
101
- tabHeight = 0
102
- ): Point[] {
103
- const tabOnly = this.tabMode === "tab-only";
104
- if (tabWidth === 0) {
105
- return [
106
- [x, y + tabHeight],
107
- [x, y + tabHeight],
108
- [x, y],
109
- [x + tabWidth, y],
110
- [x + tabWidth, y],
111
- [x + width, y],
112
- [x + width, y + height],
113
- [x, y + height],
114
- ];
115
- } else if (tabOnly) {
116
- const left = tabLeft;
117
- return [
118
- [left, y],
119
- [left, y],
120
- [left + tabWidth, y],
121
- [left + tabWidth, y],
122
- [left + tabWidth, y + tabHeight],
123
- [left + tabWidth, y + tabHeight],
124
- [left, y + tabHeight],
125
- [left, y + tabHeight],
126
- ];
127
- } else if (tabLeft === 0) {
128
- return [
129
- [x, y + tabHeight],
130
- [x, y + tabHeight],
131
- [x, y],
132
- [x + tabWidth, y],
133
- [x + tabWidth, y + tabHeight],
134
- [x + width, y + tabHeight],
135
- [x + width, y + height],
136
- [x, y + height],
137
- ];
138
- } else {
139
- return [
140
- [x, y + tabHeight],
141
- [x + tabLeft, y + tabHeight],
142
- [x + tabLeft, y],
143
- [x + tabLeft, y],
144
- [x + tabLeft, y + tabHeight],
145
- [x + width, y + tabHeight],
146
- [x + width, y + height],
147
- [x, y + height],
148
- ];
149
- }
150
- }
151
-
152
- draw(dropTarget: DropTarget, dragState: DragState) {
153
- const sameDropTarget = false;
154
- const wasMultiDrop = _multiDropOptions;
155
-
156
- if (_hoverDropTarget !== null) {
157
- this.drawTarget(_hoverDropTarget);
158
- } else {
159
- if (sameDropTarget === false) {
160
- _multiDropOptions = dropTarget.nextDropTarget != null;
161
- if (dropTarget.pos.tab) {
162
- moveExistingTabs(dropTarget);
163
- } else if (_shiftedTab) {
164
- clearShiftedTab();
165
- }
166
- this.drawTarget(dropTarget, dragState);
167
- }
168
-
169
- if (_multiDropOptions) {
170
- const [left, top, orientation] = computeMenuPosition(dropTarget);
171
- if (!wasMultiDrop || !sameDropTarget) {
172
- const component = (
173
- <DropMenu
174
- dropTarget={dropTarget}
175
- onHover={onHoverDropTarget}
176
- orientation={orientation}
177
- />
178
- );
179
- PopupService.showPopup({
180
- left,
181
- top,
182
- component,
183
- });
184
- } else {
185
- PopupService.movePopupTo(left, top);
186
- }
187
- } else {
188
- PopupService.hidePopup();
189
- }
190
- }
191
- }
192
-
193
- drawTarget(dropTarget: DropTarget, dragState?: DragState) {
194
- const lineWidth = 6;
195
-
196
- const targetDropOutline = dropTarget.getTargetDropOutline(
197
- lineWidth,
198
- dragState
199
- );
200
-
201
- if (targetDropOutline) {
202
- const { l, t, r, b, tabLeft, tabWidth, tabHeight, guideLines } =
203
- targetDropOutline;
204
- const w = r - l;
205
- const h = b - t;
206
-
207
- if (this.currentPath) {
208
- const path = document.getElementById("vuu-drop-outline");
209
- path?.setAttribute("d", this.currentPath);
210
- }
211
-
212
- const points = this.getPoints(l, t, w, h, tabLeft, tabWidth, tabHeight);
213
- const path = pathFromPoints(points);
214
- const animation = document.getElementById(
215
- "hw-drop-outline-animate"
216
- ) as unknown as SVGAnimateElement;
217
- animation?.setAttribute("to", path);
218
- animation?.beginElement();
219
- this.currentPath = path;
220
-
221
- const dropGuidePath = document.getElementById("hw-drop-guides");
222
- dropGuidePath?.setAttribute("d", pathFromGuideLines(guideLines));
223
- }
224
- }
225
- }
226
-
227
- const cssShiftRight = "transition:margin-left .4s ease-out;margin-left: 63px";
228
- const cssShiftBack = "transition:margin-left .4s ease-out;margin-left: 0px";
229
-
230
- function moveExistingTabs(dropTarget: DropTarget) {
231
- const { AFTER, BEFORE } = RelativeDropPosition;
232
- const {
233
- clientRect: { Stack },
234
- pos: {
235
- // tab: { index: tabIndex, positionRelativeToTab }
236
- tab,
237
- },
238
- } = dropTarget;
239
-
240
- const { id } = dropTarget.component.props;
241
- let tabEl = null;
242
- // console.log(`tabPos = ${tabPos} (width=${tabWidth}) x=${x}`)
243
- if (Stack && tab && tab.positionRelativeToTab !== AFTER) {
244
- const tabOffset = tab.positionRelativeToTab === BEFORE ? 1 : 2;
245
- const selector = `:scope .hwTabstrip > .hwTabstrip-inner > .hwTab:nth-child(${
246
- tab.index + tabOffset
247
- })`;
248
- tabEl = document.getElementById(id)?.querySelector(selector) as HTMLElement;
249
- if (tabEl) {
250
- if (_shiftedTab === null || _shiftedTab !== tabEl) {
251
- tabEl.style.cssText = cssShiftRight;
252
- if (_shiftedTab) {
253
- _shiftedTab.style.cssText = cssShiftBack;
254
- }
255
- _shiftedTab = tabEl;
256
- }
257
- } else {
258
- clearShiftedTab();
259
- }
260
- } else if (tab?.positionRelativeToTab === BEFORE) {
261
- if (_shiftedTab === null) {
262
- const selector = ".vuuHeader-title";
263
- tabEl = document
264
- .getElementById(id)
265
- ?.querySelector(selector) as HTMLElement;
266
- tabEl.style.cssText = cssShiftRight;
267
- _shiftedTab = tabEl;
268
- }
269
- } else {
270
- clearShiftedTab();
271
- }
272
- }
273
-
274
- function clearShiftedTab() {
275
- if (_shiftedTab) {
276
- _shiftedTab.style.cssText = cssShiftBack;
277
- _shiftedTab = null;
278
- }
279
- }