jqtree 1.7.5 → 1.8.1
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/.eslintrc +13 -3
- package/.github/workflows/ci.yml +6 -6
- package/.github/workflows/codeql-analysis.yml +4 -4
- package/.github/workflows/size.yml +3 -3
- package/.github/workflows/static.yml +1 -1
- package/bower.json +1 -1
- package/config/babel.config.json +1 -1
- package/config/jest.config.js +4 -0
- package/config/jest.polyfills.js +14 -0
- package/config/production +2 -0
- package/devserver/devserver_scroll.js +8 -0
- package/devserver/test_index.html +9 -0
- package/devserver/test_scroll.html +28 -0
- package/devserver/test_scroll_container.html +39 -0
- package/docs/.ruby-version +1 -1
- package/docs/_config.yml +1 -1
- package/docs/_entries/general/changelog.md +11 -0
- package/docs/_entries/multiple_selection/get-selected-nodes.md +1 -1
- package/docs/_entries/node/getnextnode.md +3 -6
- package/docs/_entries/node/getnextsibling.md +1 -1
- package/docs/_entries/node/getnextvisiblenode.md +8 -5
- package/docs/_entries/node/getpreviousnode.md +12 -0
- package/docs/_entries/node/getprevioussibling.md +1 -1
- package/docs/_entries/node/getpreviousvisiblenode.md +6 -5
- package/package.json +35 -29
- package/src/dataLoader.ts +57 -34
- package/src/dragAndDropHandler/dragElement.ts +54 -0
- package/src/dragAndDropHandler/generateHitAreas.ts +176 -0
- package/src/dragAndDropHandler/index.ts +454 -0
- package/src/dragAndDropHandler/iterateVisibleNodes.ts +91 -0
- package/src/dragAndDropHandler/types.ts +13 -0
- package/src/elementsRenderer.ts +75 -40
- package/src/jqtreeMethodTypes.ts +40 -0
- package/src/jqtreeOptions.ts +43 -25
- package/src/keyHandler.ts +59 -30
- package/src/mouseHandler.ts +385 -0
- package/src/mouseUtils.ts +23 -0
- package/src/node.ts +1 -29
- package/src/nodeElement/borderDropHint.ts +32 -0
- package/src/nodeElement/folderElement.ts +133 -0
- package/src/nodeElement/ghostDropHint.ts +69 -0
- package/src/nodeElement/index.ts +102 -0
- package/src/playwright/coverage.ts +4 -7
- package/src/playwright/playwright.test.ts +150 -53
- package/src/playwright/testUtils.ts +28 -5
- package/src/position.ts +28 -0
- package/src/saveStateHandler.ts +75 -26
- package/src/scrollHandler/containerScrollParent.ts +13 -23
- package/src/scrollHandler/createScrollParent.ts +22 -22
- package/src/scrollHandler/documentScrollParent.ts +16 -13
- package/src/scrollHandler.ts +13 -15
- package/src/selectNodeHandler.ts +10 -16
- package/src/test/jqTree/events.test.ts +97 -30
- package/src/test/jqTree/keyboard.test.ts +18 -23
- package/src/test/jqTree/loadOnDemand.test.ts +22 -15
- package/src/test/jqTree/methods.test.ts +40 -14
- package/src/test/jqTree/mouse.test.ts +82 -0
- package/src/test/jqTree/options.test.ts +24 -12
- package/src/test/node.test.ts +3 -2
- package/src/test/{nodeUtil.test.ts → position.test.ts} +1 -1
- package/src/tree.jquery.ts +314 -208
- package/src/util.ts +12 -0
- package/src/version.ts +1 -1
- package/tree.jquery.debug.js +2594 -3419
- package/tree.jquery.debug.js.map +1 -1
- package/tree.jquery.js +3 -3
- package/tree.jquery.js.map +1 -1
- package/tsconfig.json +5 -3
- package/docs/_entries/functions/get-selected-nodes.md +0 -10
- package/lib/dataLoader.js +0 -123
- package/lib/dragAndDropHandler.js +0 -588
- package/lib/elementsRenderer.js +0 -267
- package/lib/jqtreeOptions.js +0 -1
- package/lib/keyHandler.js +0 -111
- package/lib/mouse.widget.js +0 -255
- package/lib/node.js +0 -708
- package/lib/nodeElement.js +0 -274
- package/lib/nodeUtils.js +0 -10
- package/lib/playwright/coverage.js +0 -99
- package/lib/playwright/playwright.test.js +0 -606
- package/lib/playwright/testUtils.js +0 -210
- package/lib/saveStateHandler.js +0 -277
- package/lib/scrollHandler/containerScrollParent.js +0 -160
- package/lib/scrollHandler/createScrollParent.js +0 -57
- package/lib/scrollHandler/documentScrollParent.js +0 -169
- package/lib/scrollHandler/scrollParent.js +0 -58
- package/lib/scrollHandler/types.js +0 -1
- package/lib/scrollHandler.js +0 -71
- package/lib/selectNodeHandler.js +0 -128
- package/lib/simple.widget.js +0 -158
- package/lib/test/global.d.js +0 -3
- package/lib/test/jqTree/accessibility.test.js +0 -37
- package/lib/test/jqTree/create.test.js +0 -48
- package/lib/test/jqTree/events.test.js +0 -210
- package/lib/test/jqTree/keyboard.test.js +0 -225
- package/lib/test/jqTree/loadOnDemand.test.js +0 -218
- package/lib/test/jqTree/methods.test.js +0 -1348
- package/lib/test/jqTree/options.test.js +0 -548
- package/lib/test/jqTree/scrollHandler/containerScrollParent.test.js +0 -94
- package/lib/test/node.test.js +0 -1202
- package/lib/test/nodeUtil.test.js +0 -27
- package/lib/test/nodeUtils.test.js +0 -20
- package/lib/test/support/exampleData.js +0 -35
- package/lib/test/support/jqTreeMatchers.js +0 -70
- package/lib/test/support/matchers.d.js +0 -1
- package/lib/test/support/setupTests.js +0 -7
- package/lib/test/support/testUtil.js +0 -29
- package/lib/test/support/treeStructure.js +0 -38
- package/lib/test/util.test.js +0 -26
- package/lib/tree.jquery.d.js +0 -1
- package/lib/tree.jquery.js +0 -1105
- package/lib/types.js +0 -1
- package/lib/typings.d.js +0 -2
- package/lib/util.js +0 -15
- package/lib/version.js +0 -8
- package/src/dragAndDropHandler.ts +0 -713
- package/src/mouse.widget.ts +0 -266
- package/src/nodeElement.ts +0 -272
- package/src/types.ts +0 -19
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
interface DragElementParams {
|
|
2
|
+
autoEscape: boolean;
|
|
3
|
+
nodeName: string;
|
|
4
|
+
offsetX: number;
|
|
5
|
+
offsetY: number;
|
|
6
|
+
treeElement: HTMLElement;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
class DragElement {
|
|
10
|
+
private offsetX: number;
|
|
11
|
+
private offsetY: number;
|
|
12
|
+
private element: HTMLElement;
|
|
13
|
+
|
|
14
|
+
constructor({
|
|
15
|
+
autoEscape,
|
|
16
|
+
nodeName,
|
|
17
|
+
offsetX,
|
|
18
|
+
offsetY,
|
|
19
|
+
treeElement,
|
|
20
|
+
}: DragElementParams) {
|
|
21
|
+
this.offsetX = offsetX;
|
|
22
|
+
this.offsetY = offsetY;
|
|
23
|
+
|
|
24
|
+
this.element = this.createElement(nodeName, autoEscape);
|
|
25
|
+
|
|
26
|
+
treeElement.appendChild(this.element);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
public move(pageX: number, pageY: number): void {
|
|
30
|
+
this.element.style.left = `${pageX - this.offsetX}px`;
|
|
31
|
+
this.element.style.top = `${pageY - this.offsetY}px`;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
public remove(): void {
|
|
35
|
+
this.element.remove();
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
private createElement(nodeName: string, autoEscape: boolean) {
|
|
39
|
+
const element = document.createElement("span");
|
|
40
|
+
element.classList.add("jqtree-title", "jqtree-dragging");
|
|
41
|
+
|
|
42
|
+
if (autoEscape) {
|
|
43
|
+
element.textContent = nodeName;
|
|
44
|
+
} else {
|
|
45
|
+
element.innerHTML = nodeName;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
element.style.position = "absolute";
|
|
49
|
+
|
|
50
|
+
return element;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export default DragElement;
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import { HitArea } from "./types";
|
|
2
|
+
import { Node } from "../node";
|
|
3
|
+
import { Position } from "../position";
|
|
4
|
+
import { getOffsetTop } from "../util";
|
|
5
|
+
import iterateVisibleNodes from "./iterateVisibleNodes";
|
|
6
|
+
|
|
7
|
+
const generatePositions = (tree: Node, currentNode: Node): HitArea[] => {
|
|
8
|
+
const positions: HitArea[] = [];
|
|
9
|
+
let lastTop = 0;
|
|
10
|
+
|
|
11
|
+
const addPosition = (node: Node, position: number, top: number) => {
|
|
12
|
+
const area = {
|
|
13
|
+
top,
|
|
14
|
+
bottom: 0,
|
|
15
|
+
node,
|
|
16
|
+
position,
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
positions.push(area);
|
|
20
|
+
lastTop = top;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const handleAfterOpenFolder = (node: Node, nextNode: Node | null) => {
|
|
24
|
+
if (node === currentNode || nextNode === currentNode) {
|
|
25
|
+
// Cannot move before or after current item
|
|
26
|
+
addPosition(node, Position.None, lastTop);
|
|
27
|
+
} else {
|
|
28
|
+
addPosition(node, Position.After, lastTop);
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const handleClosedFolder = (
|
|
33
|
+
node: Node,
|
|
34
|
+
nextNode: Node | null,
|
|
35
|
+
element: HTMLElement,
|
|
36
|
+
) => {
|
|
37
|
+
const top = getOffsetTop(element);
|
|
38
|
+
|
|
39
|
+
if (node === currentNode) {
|
|
40
|
+
// Cannot move after current item
|
|
41
|
+
addPosition(node, Position.None, top);
|
|
42
|
+
} else {
|
|
43
|
+
addPosition(node, Position.Inside, top);
|
|
44
|
+
|
|
45
|
+
// Cannot move before current item
|
|
46
|
+
if (nextNode !== currentNode) {
|
|
47
|
+
addPosition(node, Position.After, top);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const handleFirstNode = (node: Node) => {
|
|
53
|
+
if (node !== currentNode) {
|
|
54
|
+
addPosition(node, Position.Before, getOffsetTop(node.element));
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const handleNode = (
|
|
59
|
+
node: Node,
|
|
60
|
+
nextNode: Node | null,
|
|
61
|
+
element: HTMLElement,
|
|
62
|
+
) => {
|
|
63
|
+
const top = getOffsetTop(element);
|
|
64
|
+
|
|
65
|
+
if (node === currentNode) {
|
|
66
|
+
// Cannot move inside current item
|
|
67
|
+
addPosition(node, Position.None, top);
|
|
68
|
+
} else {
|
|
69
|
+
addPosition(node, Position.Inside, top);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (nextNode === currentNode || node === currentNode) {
|
|
73
|
+
// Cannot move before or after current item
|
|
74
|
+
addPosition(node, Position.None, top);
|
|
75
|
+
} else {
|
|
76
|
+
addPosition(node, Position.After, top);
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
const handleOpenFolder = (node: Node, element: HTMLElement) => {
|
|
81
|
+
if (node === currentNode) {
|
|
82
|
+
// Cannot move inside current item
|
|
83
|
+
// Stop iterating
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Cannot move before current item
|
|
88
|
+
if (node.children[0] !== currentNode) {
|
|
89
|
+
addPosition(node, Position.Inside, getOffsetTop(element));
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Continue iterating
|
|
93
|
+
return true;
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
iterateVisibleNodes(tree, {
|
|
97
|
+
handleAfterOpenFolder,
|
|
98
|
+
handleClosedFolder,
|
|
99
|
+
handleFirstNode,
|
|
100
|
+
handleNode,
|
|
101
|
+
handleOpenFolder,
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
return positions;
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
const generateHitAreasForGroup = (
|
|
108
|
+
hitAreas: HitArea[],
|
|
109
|
+
positionsInGroup: HitArea[],
|
|
110
|
+
top: number,
|
|
111
|
+
bottom: number,
|
|
112
|
+
) => {
|
|
113
|
+
// limit positions in group
|
|
114
|
+
const positionCount = Math.min(positionsInGroup.length, 4);
|
|
115
|
+
|
|
116
|
+
const areaHeight = Math.round((bottom - top) / positionCount);
|
|
117
|
+
let areaTop = top;
|
|
118
|
+
|
|
119
|
+
let i = 0;
|
|
120
|
+
while (i < positionCount) {
|
|
121
|
+
const position = positionsInGroup[i];
|
|
122
|
+
|
|
123
|
+
if (position) {
|
|
124
|
+
hitAreas.push({
|
|
125
|
+
top: areaTop,
|
|
126
|
+
bottom: areaTop + areaHeight,
|
|
127
|
+
node: position.node,
|
|
128
|
+
position: position.position,
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
areaTop += areaHeight;
|
|
133
|
+
i += 1;
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
const generateHitAreasFromPositions = (
|
|
138
|
+
positions: HitArea[],
|
|
139
|
+
treeBottom: number,
|
|
140
|
+
): HitArea[] => {
|
|
141
|
+
let previousTop = positions[0]?.top ?? 0;
|
|
142
|
+
let group = [];
|
|
143
|
+
const hitAreas: HitArea[] = [];
|
|
144
|
+
|
|
145
|
+
for (const position of positions) {
|
|
146
|
+
if (position.top !== previousTop && group.length) {
|
|
147
|
+
generateHitAreasForGroup(
|
|
148
|
+
hitAreas,
|
|
149
|
+
group,
|
|
150
|
+
previousTop,
|
|
151
|
+
position.top,
|
|
152
|
+
);
|
|
153
|
+
|
|
154
|
+
previousTop = position.top;
|
|
155
|
+
group = [];
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
group.push(position);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
generateHitAreasForGroup(hitAreas, group, previousTop, treeBottom);
|
|
162
|
+
|
|
163
|
+
return hitAreas;
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
const generateHitAreas = (
|
|
167
|
+
tree: Node,
|
|
168
|
+
currentNode: Node,
|
|
169
|
+
treeBottom: number,
|
|
170
|
+
) => {
|
|
171
|
+
const positions = generatePositions(tree, currentNode);
|
|
172
|
+
|
|
173
|
+
return generateHitAreasFromPositions(positions, treeBottom);
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
export default generateHitAreas;
|
|
@@ -0,0 +1,454 @@
|
|
|
1
|
+
import { Node } from "../node";
|
|
2
|
+
import { getPositionName, Position } from "../position";
|
|
3
|
+
import { DropHint, HitArea } from "./types";
|
|
4
|
+
import { PositionInfo } from "../mouseUtils";
|
|
5
|
+
import NodeElement from "../nodeElement";
|
|
6
|
+
import DragElement from "./dragElement";
|
|
7
|
+
import generateHitAreas from "./generateHitAreas";
|
|
8
|
+
import { getElementPosition } from "../util";
|
|
9
|
+
import {
|
|
10
|
+
OnCanMove,
|
|
11
|
+
OnCanMoveTo,
|
|
12
|
+
OnIsMoveHandle,
|
|
13
|
+
DragMethod,
|
|
14
|
+
} from "../jqtreeOptions";
|
|
15
|
+
import {
|
|
16
|
+
GetScrollLeft,
|
|
17
|
+
GetTree,
|
|
18
|
+
OpenNode,
|
|
19
|
+
RefreshElements,
|
|
20
|
+
TriggerEvent,
|
|
21
|
+
} from "../jqtreeMethodTypes";
|
|
22
|
+
|
|
23
|
+
interface Dimensions {
|
|
24
|
+
left: number;
|
|
25
|
+
top: number;
|
|
26
|
+
right: number;
|
|
27
|
+
bottom: number;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
type GetNodeElement = (element: HTMLElement) => NodeElement | null;
|
|
31
|
+
type GetNodeElementForNode = (node: Node) => NodeElement;
|
|
32
|
+
|
|
33
|
+
interface DragAndDropHandlerParams {
|
|
34
|
+
autoEscape?: boolean;
|
|
35
|
+
getNodeElement: GetNodeElement;
|
|
36
|
+
getNodeElementForNode: GetNodeElementForNode;
|
|
37
|
+
getScrollLeft: GetScrollLeft;
|
|
38
|
+
getTree: GetTree;
|
|
39
|
+
onCanMove?: OnCanMove;
|
|
40
|
+
onCanMoveTo?: OnCanMoveTo;
|
|
41
|
+
onDragMove?: DragMethod;
|
|
42
|
+
onDragStop?: DragMethod;
|
|
43
|
+
onIsMoveHandle?: OnIsMoveHandle;
|
|
44
|
+
openFolderDelay: number | false;
|
|
45
|
+
openNode: OpenNode;
|
|
46
|
+
refreshElements: RefreshElements;
|
|
47
|
+
slide: boolean;
|
|
48
|
+
treeElement: HTMLElement;
|
|
49
|
+
triggerEvent: TriggerEvent;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export class DragAndDropHandler {
|
|
53
|
+
public hitAreas: HitArea[];
|
|
54
|
+
public isDragging: boolean;
|
|
55
|
+
public currentItem: NodeElement | null;
|
|
56
|
+
public hoveredArea: HitArea | null;
|
|
57
|
+
|
|
58
|
+
private autoEscape?: boolean;
|
|
59
|
+
private dragElement: DragElement | null;
|
|
60
|
+
private getNodeElement: GetNodeElement;
|
|
61
|
+
private getNodeElementForNode: GetNodeElementForNode;
|
|
62
|
+
private getScrollLeft: GetScrollLeft;
|
|
63
|
+
private getTree: GetTree;
|
|
64
|
+
private onCanMove?: OnCanMove;
|
|
65
|
+
private onCanMoveTo?: OnCanMoveTo;
|
|
66
|
+
private onDragMove?: DragMethod;
|
|
67
|
+
private onDragStop?: DragMethod;
|
|
68
|
+
private onIsMoveHandle?: OnIsMoveHandle;
|
|
69
|
+
private openFolderDelay: number | false;
|
|
70
|
+
private openFolderTimer: number | null;
|
|
71
|
+
private openNode: OpenNode;
|
|
72
|
+
private previousGhost: DropHint | null;
|
|
73
|
+
private refreshElements: RefreshElements;
|
|
74
|
+
private slide: boolean;
|
|
75
|
+
private treeElement: HTMLElement;
|
|
76
|
+
private triggerEvent: TriggerEvent;
|
|
77
|
+
|
|
78
|
+
constructor({
|
|
79
|
+
autoEscape,
|
|
80
|
+
getNodeElement,
|
|
81
|
+
getNodeElementForNode,
|
|
82
|
+
getScrollLeft,
|
|
83
|
+
getTree,
|
|
84
|
+
onCanMove,
|
|
85
|
+
onCanMoveTo,
|
|
86
|
+
onDragMove,
|
|
87
|
+
onDragStop,
|
|
88
|
+
onIsMoveHandle,
|
|
89
|
+
openNode,
|
|
90
|
+
refreshElements,
|
|
91
|
+
slide,
|
|
92
|
+
treeElement,
|
|
93
|
+
triggerEvent,
|
|
94
|
+
}: DragAndDropHandlerParams) {
|
|
95
|
+
this.autoEscape = autoEscape;
|
|
96
|
+
this.getNodeElement = getNodeElement;
|
|
97
|
+
this.getNodeElementForNode = getNodeElementForNode;
|
|
98
|
+
this.getScrollLeft = getScrollLeft;
|
|
99
|
+
this.getTree = getTree;
|
|
100
|
+
this.onCanMove = onCanMove;
|
|
101
|
+
this.onCanMoveTo = onCanMoveTo;
|
|
102
|
+
this.onDragMove = onDragMove;
|
|
103
|
+
this.onDragStop = onDragStop;
|
|
104
|
+
this.onIsMoveHandle = onIsMoveHandle;
|
|
105
|
+
this.openNode = openNode;
|
|
106
|
+
this.refreshElements = refreshElements;
|
|
107
|
+
this.slide = slide;
|
|
108
|
+
this.treeElement = treeElement;
|
|
109
|
+
this.triggerEvent = triggerEvent;
|
|
110
|
+
|
|
111
|
+
this.hoveredArea = null;
|
|
112
|
+
this.hitAreas = [];
|
|
113
|
+
this.isDragging = false;
|
|
114
|
+
this.currentItem = null;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
public mouseCapture(positionInfo: PositionInfo): boolean | null {
|
|
118
|
+
const element = positionInfo.target;
|
|
119
|
+
|
|
120
|
+
if (!this.mustCaptureElement(element)) {
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (this.onIsMoveHandle && !this.onIsMoveHandle(jQuery(element))) {
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
let nodeElement = this.getNodeElement(element);
|
|
129
|
+
|
|
130
|
+
if (nodeElement && this.onCanMove) {
|
|
131
|
+
if (!this.onCanMove(nodeElement.node)) {
|
|
132
|
+
nodeElement = null;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
this.currentItem = nodeElement;
|
|
137
|
+
return this.currentItem != null;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
public mouseStart(positionInfo: PositionInfo): boolean {
|
|
141
|
+
if (!this.currentItem) {
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
this.refresh();
|
|
146
|
+
|
|
147
|
+
const { left, top } = getElementPosition(positionInfo.target);
|
|
148
|
+
|
|
149
|
+
const node = this.currentItem.node;
|
|
150
|
+
|
|
151
|
+
this.dragElement = new DragElement({
|
|
152
|
+
autoEscape: this.autoEscape ?? true,
|
|
153
|
+
nodeName: node.name,
|
|
154
|
+
offsetX: positionInfo.pageX - left,
|
|
155
|
+
offsetY: positionInfo.pageY - top,
|
|
156
|
+
treeElement: this.treeElement,
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
this.isDragging = true;
|
|
160
|
+
this.currentItem.element.classList.add("jqtree-moving");
|
|
161
|
+
|
|
162
|
+
return true;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
public mouseDrag(positionInfo: PositionInfo): boolean {
|
|
166
|
+
if (!this.currentItem || !this.dragElement) {
|
|
167
|
+
return false;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
this.dragElement.move(positionInfo.pageX, positionInfo.pageY);
|
|
171
|
+
|
|
172
|
+
const area = this.findHoveredArea(
|
|
173
|
+
positionInfo.pageX,
|
|
174
|
+
positionInfo.pageY,
|
|
175
|
+
);
|
|
176
|
+
|
|
177
|
+
if (area && this.canMoveToArea(area)) {
|
|
178
|
+
if (!area.node.isFolder()) {
|
|
179
|
+
this.stopOpenFolderTimer();
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (this.hoveredArea !== area) {
|
|
183
|
+
this.hoveredArea = area;
|
|
184
|
+
|
|
185
|
+
// If this is a closed folder, start timer to open it
|
|
186
|
+
if (this.mustOpenFolderTimer(area)) {
|
|
187
|
+
this.startOpenFolderTimer(area.node);
|
|
188
|
+
} else {
|
|
189
|
+
this.stopOpenFolderTimer();
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
this.updateDropHint();
|
|
193
|
+
}
|
|
194
|
+
} else {
|
|
195
|
+
this.removeDropHint();
|
|
196
|
+
this.stopOpenFolderTimer();
|
|
197
|
+
this.hoveredArea = area;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
if (!area) {
|
|
201
|
+
if (this.onDragMove) {
|
|
202
|
+
this.onDragMove(
|
|
203
|
+
this.currentItem.node,
|
|
204
|
+
positionInfo.originalEvent,
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
return true;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
public mouseStop(positionInfo: PositionInfo): boolean {
|
|
213
|
+
this.moveItem(positionInfo);
|
|
214
|
+
this.clear();
|
|
215
|
+
this.removeHover();
|
|
216
|
+
this.removeDropHint();
|
|
217
|
+
this.removeHitAreas();
|
|
218
|
+
|
|
219
|
+
const currentItem = this.currentItem;
|
|
220
|
+
|
|
221
|
+
if (this.currentItem) {
|
|
222
|
+
this.currentItem.element.classList.remove("jqtree-moving");
|
|
223
|
+
this.currentItem = null;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
this.isDragging = false;
|
|
227
|
+
|
|
228
|
+
if (!this.hoveredArea && currentItem) {
|
|
229
|
+
if (this.onDragStop) {
|
|
230
|
+
this.onDragStop(currentItem.node, positionInfo.originalEvent);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
return false;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
public refresh(): void {
|
|
238
|
+
this.removeHitAreas();
|
|
239
|
+
|
|
240
|
+
if (this.currentItem) {
|
|
241
|
+
this.generateHitAreas();
|
|
242
|
+
|
|
243
|
+
this.currentItem = this.getNodeElementForNode(
|
|
244
|
+
this.currentItem.node,
|
|
245
|
+
);
|
|
246
|
+
|
|
247
|
+
if (this.isDragging) {
|
|
248
|
+
this.currentItem.element.classList.add("jqtree-moving");
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
private generateHitAreas(): void {
|
|
254
|
+
const tree = this.getTree();
|
|
255
|
+
|
|
256
|
+
if (!this.currentItem || !tree) {
|
|
257
|
+
this.hitAreas = [];
|
|
258
|
+
} else {
|
|
259
|
+
this.hitAreas = generateHitAreas(
|
|
260
|
+
tree,
|
|
261
|
+
this.currentItem.node,
|
|
262
|
+
this.getTreeDimensions().bottom,
|
|
263
|
+
);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
private mustCaptureElement(element: HTMLElement): boolean {
|
|
268
|
+
const nodeName = element.nodeName;
|
|
269
|
+
|
|
270
|
+
return (
|
|
271
|
+
nodeName !== "INPUT" &&
|
|
272
|
+
nodeName !== "SELECT" &&
|
|
273
|
+
nodeName !== "TEXTAREA"
|
|
274
|
+
);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
private canMoveToArea(area: HitArea): boolean {
|
|
278
|
+
if (!this.onCanMoveTo) {
|
|
279
|
+
return true;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
if (!this.currentItem) {
|
|
283
|
+
return false;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
const positionName = getPositionName(area.position);
|
|
287
|
+
|
|
288
|
+
return this.onCanMoveTo(this.currentItem.node, area.node, positionName);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
private removeHitAreas(): void {
|
|
292
|
+
this.hitAreas = [];
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
private clear(): void {
|
|
296
|
+
if (this.dragElement) {
|
|
297
|
+
this.dragElement.remove();
|
|
298
|
+
this.dragElement = null;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
private removeDropHint(): void {
|
|
303
|
+
if (this.previousGhost) {
|
|
304
|
+
this.previousGhost.remove();
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
private removeHover(): void {
|
|
309
|
+
this.hoveredArea = null;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
private findHoveredArea(x: number, y: number): HitArea | null {
|
|
313
|
+
const dimensions = this.getTreeDimensions();
|
|
314
|
+
|
|
315
|
+
if (
|
|
316
|
+
x < dimensions.left ||
|
|
317
|
+
y < dimensions.top ||
|
|
318
|
+
x > dimensions.right ||
|
|
319
|
+
y > dimensions.bottom
|
|
320
|
+
) {
|
|
321
|
+
return null;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
let low = 0;
|
|
325
|
+
let high = this.hitAreas.length;
|
|
326
|
+
while (low < high) {
|
|
327
|
+
const mid = (low + high) >> 1;
|
|
328
|
+
const area = this.hitAreas[mid];
|
|
329
|
+
|
|
330
|
+
if (!area) {
|
|
331
|
+
return null;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
if (y < area.top) {
|
|
335
|
+
high = mid;
|
|
336
|
+
} else if (y > area.bottom) {
|
|
337
|
+
low = mid + 1;
|
|
338
|
+
} else {
|
|
339
|
+
return area;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
return null;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
private mustOpenFolderTimer(area: HitArea): boolean {
|
|
347
|
+
const node = area.node;
|
|
348
|
+
|
|
349
|
+
return (
|
|
350
|
+
node.isFolder() &&
|
|
351
|
+
!node.is_open &&
|
|
352
|
+
area.position === Position.Inside
|
|
353
|
+
);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
private updateDropHint(): void {
|
|
357
|
+
if (!this.hoveredArea) {
|
|
358
|
+
return;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// remove previous drop hint
|
|
362
|
+
this.removeDropHint();
|
|
363
|
+
|
|
364
|
+
// add new drop hint
|
|
365
|
+
const nodeElement = this.getNodeElementForNode(this.hoveredArea.node);
|
|
366
|
+
this.previousGhost = nodeElement.addDropHint(this.hoveredArea.position);
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
private startOpenFolderTimer(folder: Node): void {
|
|
370
|
+
const openFolder = (): void => {
|
|
371
|
+
this.openNode(folder, this.slide, () => {
|
|
372
|
+
this.refresh();
|
|
373
|
+
this.updateDropHint();
|
|
374
|
+
});
|
|
375
|
+
};
|
|
376
|
+
|
|
377
|
+
this.stopOpenFolderTimer();
|
|
378
|
+
|
|
379
|
+
const openFolderDelay = this.openFolderDelay;
|
|
380
|
+
|
|
381
|
+
if (openFolderDelay !== false) {
|
|
382
|
+
this.openFolderTimer = window.setTimeout(
|
|
383
|
+
openFolder,
|
|
384
|
+
openFolderDelay,
|
|
385
|
+
);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
private stopOpenFolderTimer(): void {
|
|
390
|
+
if (this.openFolderTimer) {
|
|
391
|
+
clearTimeout(this.openFolderTimer);
|
|
392
|
+
this.openFolderTimer = null;
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
private moveItem(positionInfo: PositionInfo): void {
|
|
397
|
+
if (
|
|
398
|
+
this.currentItem &&
|
|
399
|
+
this.hoveredArea &&
|
|
400
|
+
this.hoveredArea.position !== Position.None &&
|
|
401
|
+
this.canMoveToArea(this.hoveredArea)
|
|
402
|
+
) {
|
|
403
|
+
const movedNode = this.currentItem.node;
|
|
404
|
+
const targetNode = this.hoveredArea.node;
|
|
405
|
+
const position = this.hoveredArea.position;
|
|
406
|
+
const previousParent = movedNode.parent;
|
|
407
|
+
|
|
408
|
+
if (position === Position.Inside) {
|
|
409
|
+
this.hoveredArea.node.is_open = true;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
const doMove = (): void => {
|
|
413
|
+
const tree = this.getTree();
|
|
414
|
+
|
|
415
|
+
if (tree) {
|
|
416
|
+
tree.moveNode(movedNode, targetNode, position);
|
|
417
|
+
|
|
418
|
+
this.treeElement.textContent = "";
|
|
419
|
+
this.refreshElements(null);
|
|
420
|
+
}
|
|
421
|
+
};
|
|
422
|
+
|
|
423
|
+
const event = this.triggerEvent("tree.move", {
|
|
424
|
+
move_info: {
|
|
425
|
+
moved_node: movedNode,
|
|
426
|
+
target_node: targetNode,
|
|
427
|
+
position: getPositionName(position),
|
|
428
|
+
previous_parent: previousParent,
|
|
429
|
+
do_move: doMove,
|
|
430
|
+
original_event: positionInfo.originalEvent,
|
|
431
|
+
},
|
|
432
|
+
});
|
|
433
|
+
|
|
434
|
+
if (!event.isDefaultPrevented()) {
|
|
435
|
+
doMove();
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
private getTreeDimensions(): Dimensions {
|
|
441
|
+
// Return the dimensions of the tree. Add a margin to the bottom to allow
|
|
442
|
+
// to drag-and-drop after the last element.
|
|
443
|
+
const treePosition = getElementPosition(this.treeElement);
|
|
444
|
+
const left = treePosition.left + this.getScrollLeft();
|
|
445
|
+
const top = treePosition.top;
|
|
446
|
+
|
|
447
|
+
return {
|
|
448
|
+
left,
|
|
449
|
+
top,
|
|
450
|
+
right: left + this.treeElement.clientWidth,
|
|
451
|
+
bottom: top + this.treeElement.clientHeight + 16,
|
|
452
|
+
};
|
|
453
|
+
}
|
|
454
|
+
}
|