Skip to content

Commit ac4e8ee

Browse files
committed
WIP
1 parent 1b3943c commit ac4e8ee

17 files changed

+628
-272
lines changed

vuu-ui/packages/vuu-layout/src/drag-drop-next/DragContextNext.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ export type DropHandler<T extends DragSource = DragSource> = (
4040
export class DragContext {
4141
#dragElementHeight?: number;
4242
#dragElementWidth?: number;
43+
#dragLabelWidth?: number;
4344
#dragSource?: DragSource;
4445
#dragSources: Map<string, DragSourceDescriptor> = new Map();
4546
#detachTabHandler: DetachTabHandler = () =>

vuu-ui/packages/vuu-layout/src/drag-drop-next/DragDropProviderNext.css

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@
4444
}
4545

4646
.vuuDraggableItem-animating {
47-
transition: transform .3s ease-in-out;
47+
transition: transform .2s ease-in-out;
4848
}
4949

5050
.SpaceMan {

vuu-ui/packages/vuu-layout/src/drag-drop-next/SpaceMan.tsx

Lines changed: 11 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ export class SpaceMan {
1818
#sizeProperty: "height" | "width";
1919
#spacer1 = this.createDragSpacer();
2020
#spacer2 = this.createDragSpacer();
21+
// TODO do we still need this state
2122
#state: State = "initial";
2223
#toDirection: Direction | undefined;
2324
#toIndex: string | number | undefined = undefined;
@@ -33,22 +34,15 @@ export class SpaceMan {
3334
this.#sizeProperty = orientation === "horizontal" ? "width" : "height";
3435
}
3536

36-
get fromIndex() {
37-
return asInteger(this.#fromIndex);
38-
}
39-
get toIndex() {
40-
return asInteger(this.#toIndex);
41-
}
42-
4337
get mouseOffset() {
4438
return this.#mouseOffset;
4539
}
4640
set mouseOffset(offset: { x: number; y: number }) {
4741
this.#mouseOffset = offset;
4842
}
4943

50-
get dropPosition(): DropPosition {
51-
console.log("[SpaceMan] get dropPosition");
44+
get dropPosition(): DropPosition | undefined {
45+
// console.log("[SpaceMan] get dropPosition");
5246
const dropTargetSpacer = this.#dragContainer?.querySelector(
5347
'[data-drop-target="true"]',
5448
);
@@ -70,10 +64,8 @@ export class SpaceMan {
7064
}
7165

7266
throw Error(
73-
`[[SpaceMan] dropPosition] no dropTarget with data-drop-target attribute found`,
67+
`[[SpaceMan] (getter) dropPosition] no dropTarget with data-drop-target attribute found`,
7468
);
75-
} else {
76-
throw Error(`[SpaceMan] dropPosition no dropTarget spacer found`);
7769
}
7870
}
7971

@@ -99,10 +91,10 @@ export class SpaceMan {
9991
this.#withinDragContainer = false;
10092
this.#state = "away";
10193
this.setSpacerSizes(0, 0);
102-
//TODO don't think we really want a timeout here
94+
//TODO is the timeout just to allow the spacer rezuse to animate ?
95+
// seems to work without it
10396
setTimeout(() => {
10497
// TODO deal with situation where item re-enters before this happens
105-
console.log(`[SPaceMan timeout:300 leaveDragContainer]`);
10698
this.clearSpacers();
10799
}, 200);
108100
}
@@ -259,8 +251,8 @@ export class SpaceMan {
259251
this.#spacer2.style[propertyName] = "0px";
260252
}
261253

262-
private cleanup() {
263-
console.log("[SpaceMan] cleanup");
254+
cleanup() {
255+
console.log(`[SpaceMan#${this.id}] cleanup`);
264256
this.clearSpacers();
265257

266258
if (this.#dragItem) {
@@ -275,6 +267,9 @@ export class SpaceMan {
275267

276268
drop(x: number, y: number): Promise<void> {
277269
// TODO dragItem should be passed in
270+
console.log(`[SpaceMan#${this.id}] drop, returns a promise`, {
271+
dragItem: this.#dragItem,
272+
});
278273
return new Promise((resolve) => {
279274
if (this.#dragItem) {
280275
const dragItem = this.#dragItem;

vuu-ui/packages/vuu-layout/src/drag-drop-next/drag-drop-listeners.ts

Lines changed: 81 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,11 @@ export function initializeDragContainer(
6262

6363
if (element) {
6464
const tabsContainer = queryClosest(e.target, ".vuuDragContainer", true);
65+
const gridLayoutItem = queryClosest(
66+
tabsContainer,
67+
".vuuGridLayoutItem",
68+
true,
69+
);
6570
const tabIndex = getDataIndex(element);
6671
const label = getDataLabel(element);
6772
const isSelectedTab =
@@ -70,44 +75,57 @@ export function initializeDragContainer(
7075
const { gridLayoutItemId } = element.dataset;
7176
e.stopPropagation();
7277
dragContext.beginDrag(e, {
73-
id: gridLayoutItemId ?? "",
7478
element,
7579
isSelectedTab,
76-
label,
80+
tab: { id: gridLayoutItemId ?? "", label },
7781
tabIndex,
78-
tabsId: tabsContainer.id,
82+
tabsId: gridLayoutItem.id,
7983
type: "tabbed-component",
8084
});
8185

8286
requestAnimationFrame(() => {
8387
spaceMan.dragStart(tabIndex);
84-
const gridLayoutItem = queryClosest(
85-
tabsContainer,
86-
".vuuGridLayoutItem",
87-
);
88-
if (gridLayoutItem) {
89-
dragContext.detachTab(gridLayoutItem.id, tabIndex, label);
90-
}
88+
dragContext.detachTab(gridLayoutItem.id, tabIndex, label);
9189
});
9290
}
9391
};
9492

9593
const onDragEnter = (e: DragEvent) => {
96-
// console.log(`[drag-drop-listeners] onDragEnter`);
97-
// don't think we need this check. DragEnter wouldn't be firing
98-
// if we weren't in a drop zone
99-
e.stopPropagation();
10094
// we should really mark drop targets
10195
const dropTarget = getDraggableEl(e.target);
96+
console.log(`[drag-drop-listeners] onDragEnter`, {
97+
target: e.target,
98+
dropTarget,
99+
});
100+
// We always revent default here, that way useAsDropItem will know that another drag handler
101+
// is responsible for this area
102+
e.preventDefault();
102103
const { dragSource, x, y } = dragContext;
103-
if (dropTarget && sourceIsTabbedComponent(dragSource)) {
104+
if (dropTarget) {
105+
console.log(
106+
`[drag-drop-listeners] onDragEnter ${dropTarget.className} preventDefault`,
107+
);
104108
const indexOfDropTarget = getDataIndex(dropTarget);
109+
if (sourceIsTabbedComponent(dragSource)) {
110+
if (
111+
indexOfDropTarget !== -1 &&
112+
(indexOfDropTarget !== dragSource?.tabIndex ||
113+
isRemoteContainer(dragSource.element, dropTarget))
114+
) {
115+
const direction =
116+
orientation === "horizontal"
117+
? e.clientX > x
118+
? "fwd"
119+
: "bwd"
120+
: e.clientY > y
121+
? "fwd"
122+
: "bwd";
123+
124+
spaceMan.dragEnter(indexOfDropTarget, direction);
125+
}
126+
} else {
127+
console.log(`draging a componnet over a tabstrip`);
105128

106-
if (
107-
indexOfDropTarget !== -1 &&
108-
(indexOfDropTarget !== dragSource?.tabIndex ||
109-
isRemoteContainer(dragSource.element, dropTarget))
110-
) {
111129
const direction =
112130
orientation === "horizontal"
113131
? e.clientX > x
@@ -129,35 +147,55 @@ export function initializeDragContainer(
129147
};
130148

131149
const onDragLeave = (e: DragEvent) => {
132-
// TODO include container #id in query
133-
const container = queryClosest(e.relatedTarget, ".vuuDragContainer");
150+
console.log(`[drag-drop-listeners] onDragleave ${spaceMan.id}`);
151+
// Have we dragged the draggable item right out of the parent drag container
152+
const container = queryClosest(e.relatedTarget, `#${spaceMan.id}`);
134153
if (container === null) {
135154
spaceMan.leaveDragContainer();
136155
}
137156
};
138157

139158
const onDrop = async (e: DragEvent) => {
140-
const { clientX, clientY } = e;
141-
e.stopPropagation();
142-
// console.log(
143-
// `[drag-drop-listeners] onDrop toIndex: ${spaceMan.toIndex}
144-
// ${dropPosition.position} targetTabId ${dropPosition.target}`,
145-
// );
146-
// important we capture this before calling spaceMan.drop
147-
const { dropPosition } = spaceMan;
148-
await spaceMan.drop(clientX, clientY);
149-
dragContext.drop({
150-
toId: dragId,
151-
tabsId: dragId,
152-
dropPosition,
153-
});
159+
if (e.defaultPrevented) {
160+
console.log(`[drag-drop-listeners] onDrop ${dragId} - already handled`);
161+
spaceMan.cleanup();
162+
} else {
163+
const { clientX, clientY } = e;
164+
e.stopPropagation();
154165

155-
if (dragContext.dragSource) {
156-
focusDroppedTab(dragId, dragContext.dragSource.label);
166+
// important we capture this before calling spaceMan.drop
167+
const { dropPosition } = spaceMan;
168+
169+
console.log(
170+
`[drag-drop-listeners] onDrop #${dragId} ${dropPosition?.position} targetTabId ${dropPosition?.target}`,
171+
);
172+
if (dropPosition) {
173+
await spaceMan.drop(clientX, clientY);
174+
dragContext.drop({
175+
toId: dragId,
176+
tabsId: dragId,
177+
dropPosition,
178+
});
179+
180+
if (dragContext.dragSource) {
181+
focusDroppedTab(dragId, dragContext.dragSource.label);
182+
}
183+
} else {
184+
console.log(`[drag-drop-listeners] onDrop, drop is elsewhere `);
185+
spaceMan.cleanup();
186+
}
157187
}
158188
};
159189
const onDragEnd = () => {
160-
console.log("[drag-drop-listeners] onDragEnd");
190+
console.log(`[drag-drop-listeners#${dragId}] onDragEnd`, {
191+
dragSource: dragContext.dragSource,
192+
});
193+
if (
194+
sourceIsTabbedComponent(dragContext.dragSource) &&
195+
dragContext.dragSource.tabsId === dragId
196+
) {
197+
console.log(`[drag-drop-listeners#${dragId}] do we need to cleanup`);
198+
}
161199
if (!dragContext.dropped) {
162200
spaceMan.dragEnd();
163201
}
@@ -180,17 +218,17 @@ export function initializeDragContainer(
180218
containerEl?.addEventListener("dragenter", onDragEnter);
181219
containerEl?.addEventListener("dragleave", onDragLeave);
182220
containerEl?.addEventListener("dragover", onDragOver);
183-
containerEl?.addEventListener("drop", onDrop);
184-
containerEl?.addEventListener("dragend", onDragEnd);
221+
document.body.addEventListener("drop", onDrop);
222+
document.body.addEventListener("dragend", onDragEnd);
185223

186224
function cleanUp() {
187225
containerEl?.removeEventListener("mousedown", onMouseDown);
188226
containerEl?.removeEventListener("dragstart", onDragStart);
189227
containerEl?.removeEventListener("dragenter", onDragEnter);
190228
containerEl?.removeEventListener("dragleave", onDragLeave);
191229
containerEl?.removeEventListener("dragover", onDragOver);
192-
containerEl?.removeEventListener("drop", onDrop);
193-
containerEl?.removeEventListener("dragend", onDragEnd);
230+
document.body.removeEventListener("drop", onDrop);
231+
document.body.removeEventListener("dragend", onDragEnd);
194232
}
195233

196234
return cleanUp;

vuu-ui/packages/vuu-layout/src/grid-layout/GridLayout.css

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,10 @@
4949
cursor: grab;
5050
display: flex;
5151
flex: 0 0 var(--vuu-grid-header-height);
52-
inset: var(--vuu-grid-gap) var(--vuu-grid-gap) auto var(--vuu-grid-gap);
52+
/* inset: var(--vuu-grid-gap) var(--vuu-grid-gap) auto var(--vuu-grid-gap); */
5353
height: var(--vuu-grid-header-height);
5454
padding: 0 var(--salt-spacing-100) 0 0;
55+
position: relative;
5556

5657
[data-align="right"] {
5758
margin-left: auto;
@@ -77,7 +78,7 @@
7778
}
7879

7980
.vuuGridLayoutItem-stacked {
80-
margin-top: var(--vuu-layout-tabs-height);
81+
margin-top: var(--vuu-layout-tabs-height)
8182
}
8283

8384
.vuuGridLayoutItemHeader-close {
@@ -161,4 +162,16 @@
161162
transition-timing-function: ease-in-out;
162163
z-index: 100;
163164
}
165+
.vuuDropTarget-header:after {
166+
background-color: cornflowerblue;
167+
content: "";
168+
inset: 0px;
169+
opacity: 0.3;
170+
pointer-events: none;
171+
position: absolute;
172+
transition-property: top, left, right, bottom;
173+
transition-duration: 0.3s;
174+
transition-timing-function: ease-in-out;
175+
z-index: 100;
176+
}
164177

vuu-ui/packages/vuu-layout/src/grid-layout/GridLayout.tsx

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,7 @@ import { useIdMemo } from "@salt-ds/core";
88
import { useComponentCssInjection } from "@salt-ds/styles";
99
import { useWindow } from "@salt-ds/window";
1010
import cx from "clsx";
11-
import {
12-
CSSProperties,
13-
HTMLAttributes,
14-
ReactElement,
15-
useCallback,
16-
} from "react";
11+
import { CSSProperties, HTMLAttributes, ReactElement } from "react";
1712
import { DragDropProviderNext } from "../drag-drop-next/DragDropProviderNext";
1813
import gridLayoutCss from "./GridLayout.css";
1914
import { GridLayoutContext } from "./GridLayoutContext";
@@ -122,6 +117,7 @@ export const GridLayout = ({
122117

123118
console.log(`GridLayout #${id}`, {
124119
children,
120+
stackedItems,
125121
});
126122

127123
return (

vuu-ui/packages/vuu-layout/src/grid-layout/GridLayoutContext.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import { createContext, Dispatch, useContext } from "react";
1+
import { createContext, Dispatch, DragEvent, useContext } from "react";
22
import { GridLayoutDragEndHandler } from "./GridLayoutProvider";
3-
import { GridModel } from "./GridModel";
3+
import { GridModel, TabStateTab } from "./GridModel";
44
import { GridLayoutDragStartHandler } from "./useDraggable";
55
import { GridLayoutModel } from "./GridLayoutModel";
66
import { GridLayoutDropPosition } from "@finos/vuu-utils";
@@ -39,9 +39,9 @@ const unconfiguredGridLayoutDispatch: GridLayoutDispatch = (action) =>
3939
*/
4040
export interface TabbedComponentDragSource {
4141
element: HTMLElement;
42-
id: string;
4342
isSelectedTab: boolean;
44-
label: string;
43+
tab: TabStateTab;
44+
/** deprecated */
4545
tabIndex: number;
4646
tabsId: string;
4747
type: "tabbed-component";
@@ -51,6 +51,7 @@ export interface TabbedComponentDragSource {
5151
*/
5252
export interface ComponentDragSource {
5353
element: HTMLElement;
54+
dragElement?: HTMLElement;
5455
id: string;
5556
label: string;
5657
type: "component";
@@ -66,6 +67,8 @@ export interface TemplateDragSource {
6667
type: "template";
6768
}
6869

70+
export type DragSourceProvider = (evt: DragEvent<Element>) => DragSource;
71+
6972
export type DragSource =
7073
| ComponentDragSource
7174
| TemplateDragSource

0 commit comments

Comments
 (0)