jqtree 1.8.4 → 1.8.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.
- package/.tool-versions +2 -0
- package/README.md +1 -1
- package/bower.json +1 -1
- package/eslint.config.mjs +69 -0
- package/package.json +27 -29
- package/src/dataLoader.ts +62 -62
- package/src/dragAndDropHandler/dragElement.ts +10 -10
- package/src/dragAndDropHandler/generateHitAreas.ts +5 -5
- package/src/dragAndDropHandler/index.ts +250 -250
- package/src/dragAndDropHandler/types.ts +1 -1
- package/src/elementsRenderer.ts +124 -125
- package/src/jqtreeMethodTypes.ts +1 -1
- package/src/jqtreeOptions.ts +5 -5
- package/src/keyHandler.ts +66 -58
- package/src/mouseHandler.ts +211 -215
- package/src/node.ts +390 -392
- package/src/nodeElement/folderElement.ts +48 -48
- package/src/nodeElement/ghostDropHint.ts +4 -4
- package/src/nodeElement/index.ts +39 -39
- package/src/nodeUtils.ts +1 -1
- package/src/position.ts +1 -1
- package/src/saveStateHandler.ts +135 -137
- package/src/scrollHandler/containerScrollParent.ts +70 -69
- package/src/scrollHandler/createScrollParent.ts +1 -0
- package/src/scrollHandler/documentScrollParent.ts +88 -87
- package/src/scrollHandler.ts +20 -20
- package/src/selectNodeHandler.ts +16 -16
- package/src/simple.widget.ts +16 -15
- package/src/tree.jquery.d.ts +25 -27
- package/src/tree.jquery.ts +849 -843
- package/src/version.ts +1 -1
- package/tree.jquery.debug.js +2533 -2523
- package/tree.jquery.debug.js.map +1 -1
- package/tree.jquery.js +2 -2
- package/tree.jquery.js.map +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import { OnFinishOpenNode, TriggerEvent } from "../jqtreeMethodTypes";
|
|
1
2
|
import { Position } from "../position";
|
|
2
3
|
import NodeElement, { NodeElementParams } from "./index";
|
|
3
|
-
import { OnFinishOpenNode, TriggerEvent } from "../jqtreeMethodTypes";
|
|
4
4
|
|
|
5
5
|
interface FolderElementParams extends NodeElementParams {
|
|
6
6
|
closedIconElement?: HTMLElement | Text;
|
|
@@ -14,19 +14,19 @@ class FolderElement extends NodeElement {
|
|
|
14
14
|
private triggerEvent: TriggerEvent;
|
|
15
15
|
|
|
16
16
|
constructor({
|
|
17
|
+
$treeElement,
|
|
17
18
|
closedIconElement,
|
|
18
19
|
getScrollLeft,
|
|
19
20
|
node,
|
|
20
21
|
openedIconElement,
|
|
21
22
|
tabIndex,
|
|
22
|
-
$treeElement,
|
|
23
23
|
triggerEvent,
|
|
24
24
|
}: FolderElementParams) {
|
|
25
25
|
super({
|
|
26
|
+
$treeElement,
|
|
26
27
|
getScrollLeft,
|
|
27
28
|
node,
|
|
28
29
|
tabIndex,
|
|
29
|
-
$treeElement,
|
|
30
30
|
});
|
|
31
31
|
|
|
32
32
|
this.closedIconElement = closedIconElement;
|
|
@@ -34,100 +34,100 @@ class FolderElement extends NodeElement {
|
|
|
34
34
|
this.triggerEvent = triggerEvent;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
|
|
38
|
-
|
|
37
|
+
protected mustShowBorderDropHint(position: Position): boolean {
|
|
38
|
+
return !this.node.is_open && position === Position.Inside;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
private getButton(): HTMLLinkElement {
|
|
42
|
+
return this.element.querySelector(
|
|
43
|
+
":scope > .jqtree-element > a.jqtree-toggler",
|
|
44
|
+
) as HTMLLinkElement;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
public close(
|
|
39
48
|
slide = true,
|
|
40
|
-
animationSpeed: JQuery.Duration = "fast",
|
|
49
|
+
animationSpeed: JQuery.Duration | undefined = "fast",
|
|
41
50
|
): void {
|
|
42
|
-
if (this.node.is_open) {
|
|
51
|
+
if (!this.node.is_open) {
|
|
43
52
|
return;
|
|
44
53
|
}
|
|
45
54
|
|
|
46
|
-
this.node.is_open =
|
|
55
|
+
this.node.is_open = false;
|
|
47
56
|
|
|
48
57
|
const button = this.getButton();
|
|
49
|
-
button.classList.
|
|
58
|
+
button.classList.add("jqtree-closed");
|
|
50
59
|
button.innerHTML = "";
|
|
51
60
|
|
|
52
|
-
const
|
|
61
|
+
const closedIconElement = this.closedIconElement;
|
|
53
62
|
|
|
54
|
-
if (
|
|
55
|
-
const icon =
|
|
63
|
+
if (closedIconElement) {
|
|
64
|
+
const icon = closedIconElement.cloneNode(true);
|
|
56
65
|
button.appendChild(icon);
|
|
57
66
|
}
|
|
58
67
|
|
|
59
|
-
const
|
|
60
|
-
this.element.classList.
|
|
68
|
+
const doClose = (): void => {
|
|
69
|
+
this.element.classList.add("jqtree-closed");
|
|
61
70
|
|
|
62
71
|
const titleSpan = this.getTitleSpan();
|
|
63
|
-
titleSpan.setAttribute("aria-expanded", "
|
|
64
|
-
|
|
65
|
-
if (onFinished) {
|
|
66
|
-
onFinished(this.node);
|
|
67
|
-
}
|
|
72
|
+
titleSpan.setAttribute("aria-expanded", "false");
|
|
68
73
|
|
|
69
|
-
this.triggerEvent("tree.
|
|
74
|
+
this.triggerEvent("tree.close", {
|
|
70
75
|
node: this.node,
|
|
71
76
|
});
|
|
72
77
|
};
|
|
73
78
|
|
|
74
79
|
if (slide) {
|
|
75
|
-
jQuery(this.getUl()).
|
|
80
|
+
jQuery(this.getUl()).slideUp(animationSpeed, doClose);
|
|
76
81
|
} else {
|
|
77
|
-
jQuery(this.getUl()).
|
|
78
|
-
|
|
82
|
+
jQuery(this.getUl()).hide();
|
|
83
|
+
doClose();
|
|
79
84
|
}
|
|
80
85
|
}
|
|
81
86
|
|
|
82
|
-
public
|
|
87
|
+
public open(
|
|
88
|
+
onFinished: OnFinishOpenNode | undefined,
|
|
83
89
|
slide = true,
|
|
84
|
-
animationSpeed: JQuery.Duration
|
|
90
|
+
animationSpeed: JQuery.Duration = "fast",
|
|
85
91
|
): void {
|
|
86
|
-
if (
|
|
92
|
+
if (this.node.is_open) {
|
|
87
93
|
return;
|
|
88
94
|
}
|
|
89
95
|
|
|
90
|
-
this.node.is_open =
|
|
96
|
+
this.node.is_open = true;
|
|
91
97
|
|
|
92
98
|
const button = this.getButton();
|
|
93
|
-
button.classList.
|
|
99
|
+
button.classList.remove("jqtree-closed");
|
|
94
100
|
button.innerHTML = "";
|
|
95
101
|
|
|
96
|
-
const
|
|
102
|
+
const openedIconElement = this.openedIconElement;
|
|
97
103
|
|
|
98
|
-
if (
|
|
99
|
-
const icon =
|
|
104
|
+
if (openedIconElement) {
|
|
105
|
+
const icon = openedIconElement.cloneNode(true);
|
|
100
106
|
button.appendChild(icon);
|
|
101
107
|
}
|
|
102
108
|
|
|
103
|
-
const
|
|
104
|
-
this.element.classList.
|
|
109
|
+
const doOpen = (): void => {
|
|
110
|
+
this.element.classList.remove("jqtree-closed");
|
|
105
111
|
|
|
106
112
|
const titleSpan = this.getTitleSpan();
|
|
107
|
-
titleSpan.setAttribute("aria-expanded", "
|
|
113
|
+
titleSpan.setAttribute("aria-expanded", "true");
|
|
108
114
|
|
|
109
|
-
|
|
115
|
+
if (onFinished) {
|
|
116
|
+
onFinished(this.node);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
this.triggerEvent("tree.open", {
|
|
110
120
|
node: this.node,
|
|
111
121
|
});
|
|
112
122
|
};
|
|
113
123
|
|
|
114
124
|
if (slide) {
|
|
115
|
-
jQuery(this.getUl()).
|
|
125
|
+
jQuery(this.getUl()).slideDown(animationSpeed, doOpen);
|
|
116
126
|
} else {
|
|
117
|
-
jQuery(this.getUl()).
|
|
118
|
-
|
|
127
|
+
jQuery(this.getUl()).show();
|
|
128
|
+
doOpen();
|
|
119
129
|
}
|
|
120
130
|
}
|
|
121
|
-
|
|
122
|
-
protected mustShowBorderDropHint(position: Position): boolean {
|
|
123
|
-
return !this.node.is_open && position === Position.Inside;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
private getButton(): HTMLLinkElement {
|
|
127
|
-
return this.element.querySelector(
|
|
128
|
-
":scope > .jqtree-element > a.jqtree-toggler",
|
|
129
|
-
) as HTMLLinkElement;
|
|
130
|
-
}
|
|
131
131
|
}
|
|
132
132
|
|
|
133
133
|
export default FolderElement;
|
|
@@ -12,10 +12,6 @@ class GhostDropHint implements DropHint {
|
|
|
12
12
|
this.ghost.classList.add("jqtree-inside");
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
public remove(): void {
|
|
16
|
-
this.ghost.remove();
|
|
17
|
-
}
|
|
18
|
-
|
|
19
15
|
private createGhostElement() {
|
|
20
16
|
const ghost = document.createElement("li");
|
|
21
17
|
ghost.className = "jqtree_common jqtree-ghost";
|
|
@@ -30,6 +26,10 @@ class GhostDropHint implements DropHint {
|
|
|
30
26
|
|
|
31
27
|
return ghost;
|
|
32
28
|
}
|
|
29
|
+
|
|
30
|
+
public remove(): void {
|
|
31
|
+
this.ghost.remove();
|
|
32
|
+
}
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
export default GhostDropHint;
|
package/src/nodeElement/index.ts
CHANGED
|
@@ -1,29 +1,29 @@
|
|
|
1
|
+
import { DropHint } from "../dragAndDropHandler/types";
|
|
2
|
+
import { GetScrollLeft } from "../jqtreeMethodTypes";
|
|
1
3
|
import { Node } from "../node";
|
|
2
4
|
import { Position } from "../position";
|
|
3
|
-
import { DropHint } from "../dragAndDropHandler/types";
|
|
4
5
|
import BorderDropHint from "./borderDropHint";
|
|
5
6
|
import GhostDropHint from "./ghostDropHint";
|
|
6
|
-
import { GetScrollLeft } from "../jqtreeMethodTypes";
|
|
7
7
|
|
|
8
8
|
export interface NodeElementParams {
|
|
9
|
+
$treeElement: JQuery;
|
|
9
10
|
getScrollLeft: GetScrollLeft;
|
|
10
11
|
node: Node;
|
|
11
12
|
tabIndex?: number;
|
|
12
|
-
$treeElement: JQuery<HTMLElement>;
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
class NodeElement {
|
|
16
|
-
|
|
17
|
-
public element: HTMLElement;
|
|
16
|
+
private $treeElement: JQuery;
|
|
18
17
|
private getScrollLeft: GetScrollLeft;
|
|
19
18
|
private tabIndex?: number;
|
|
20
|
-
|
|
19
|
+
public element: HTMLElement;
|
|
20
|
+
public node: Node;
|
|
21
21
|
|
|
22
22
|
constructor({
|
|
23
|
+
$treeElement,
|
|
23
24
|
getScrollLeft,
|
|
24
25
|
node,
|
|
25
26
|
tabIndex,
|
|
26
|
-
$treeElement,
|
|
27
27
|
}: NodeElementParams) {
|
|
28
28
|
this.getScrollLeft = getScrollLeft;
|
|
29
29
|
this.tabIndex = tabIndex;
|
|
@@ -32,6 +32,38 @@ class NodeElement {
|
|
|
32
32
|
this.init(node);
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
+
protected getTitleSpan(): HTMLSpanElement {
|
|
36
|
+
return this.element.querySelector(
|
|
37
|
+
":scope > .jqtree-element > span.jqtree-title",
|
|
38
|
+
) as HTMLSpanElement;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
protected getUl(): HTMLUListElement {
|
|
42
|
+
return this.element.querySelector(":scope > ul") as HTMLUListElement;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
protected mustShowBorderDropHint(position: Position): boolean {
|
|
46
|
+
return position === Position.Inside;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
public addDropHint(position: number): DropHint {
|
|
50
|
+
if (this.mustShowBorderDropHint(position)) {
|
|
51
|
+
return new BorderDropHint(this.element, this.getScrollLeft());
|
|
52
|
+
} else {
|
|
53
|
+
return new GhostDropHint(this.element);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
public deselect(): void {
|
|
58
|
+
this.element.classList.remove("jqtree-selected");
|
|
59
|
+
|
|
60
|
+
const titleSpan = this.getTitleSpan();
|
|
61
|
+
titleSpan.removeAttribute("tabindex");
|
|
62
|
+
titleSpan.setAttribute("aria-selected", "false");
|
|
63
|
+
|
|
64
|
+
titleSpan.blur();
|
|
65
|
+
}
|
|
66
|
+
|
|
35
67
|
public init(node: Node): void {
|
|
36
68
|
this.node = node;
|
|
37
69
|
|
|
@@ -48,14 +80,6 @@ class NodeElement {
|
|
|
48
80
|
}
|
|
49
81
|
}
|
|
50
82
|
|
|
51
|
-
public addDropHint(position: number): DropHint {
|
|
52
|
-
if (this.mustShowBorderDropHint(position)) {
|
|
53
|
-
return new BorderDropHint(this.element, this.getScrollLeft());
|
|
54
|
-
} else {
|
|
55
|
-
return new GhostDropHint(this.element);
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
83
|
public select(mustSetFocus: boolean): void {
|
|
60
84
|
this.element.classList.add("jqtree-selected");
|
|
61
85
|
|
|
@@ -73,30 +97,6 @@ class NodeElement {
|
|
|
73
97
|
titleSpan.focus();
|
|
74
98
|
}
|
|
75
99
|
}
|
|
76
|
-
|
|
77
|
-
public deselect(): void {
|
|
78
|
-
this.element.classList.remove("jqtree-selected");
|
|
79
|
-
|
|
80
|
-
const titleSpan = this.getTitleSpan();
|
|
81
|
-
titleSpan.removeAttribute("tabindex");
|
|
82
|
-
titleSpan.setAttribute("aria-selected", "false");
|
|
83
|
-
|
|
84
|
-
titleSpan.blur();
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
protected getUl(): HTMLUListElement {
|
|
88
|
-
return this.element.querySelector(":scope > ul") as HTMLUListElement;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
protected getTitleSpan(): HTMLSpanElement {
|
|
92
|
-
return this.element.querySelector(
|
|
93
|
-
":scope > .jqtree-element > span.jqtree-title",
|
|
94
|
-
) as HTMLSpanElement;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
protected mustShowBorderDropHint(position: Position): boolean {
|
|
98
|
-
return position === Position.Inside;
|
|
99
|
-
}
|
|
100
100
|
}
|
|
101
101
|
|
|
102
102
|
export default NodeElement;
|
package/src/nodeUtils.ts
CHANGED
package/src/position.ts
CHANGED
package/src/saveStateHandler.ts
CHANGED
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
import { isInt } from "./util";
|
|
2
|
-
import { Node } from "./node";
|
|
3
|
-
import { OnGetStateFromStorage, OnSetStateFromStorage } from "./jqtreeOptions";
|
|
4
1
|
import {
|
|
5
2
|
AddToSelection,
|
|
6
3
|
GetNodeById,
|
|
@@ -10,10 +7,13 @@ import {
|
|
|
10
7
|
RefreshElements,
|
|
11
8
|
RemoveFromSelection,
|
|
12
9
|
} from "./jqtreeMethodTypes";
|
|
10
|
+
import { OnGetStateFromStorage, OnSetStateFromStorage } from "./jqtreeOptions";
|
|
11
|
+
import { Node } from "./node";
|
|
12
|
+
import { isInt } from "./util";
|
|
13
13
|
|
|
14
14
|
export interface SavedState {
|
|
15
|
-
open_nodes
|
|
16
|
-
selected_node
|
|
15
|
+
open_nodes?: NodeId[];
|
|
16
|
+
selected_node?: NodeId[];
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
interface SaveStateHandlerParams {
|
|
@@ -30,6 +30,7 @@ interface SaveStateHandlerParams {
|
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
export default class SaveStateHandler {
|
|
33
|
+
private _supportsLocalStorage: boolean | null;
|
|
33
34
|
private addToSelection: AddToSelection;
|
|
34
35
|
private getNodeById: GetNodeById;
|
|
35
36
|
private getSelectedNodes: GetSelectedNodes;
|
|
@@ -40,7 +41,6 @@ export default class SaveStateHandler {
|
|
|
40
41
|
private refreshElements: RefreshElements;
|
|
41
42
|
private removeFromSelection: RemoveFromSelection;
|
|
42
43
|
private saveStateOption: boolean | string;
|
|
43
|
-
private _supportsLocalStorage: boolean | null;
|
|
44
44
|
|
|
45
45
|
constructor({
|
|
46
46
|
addToSelection,
|
|
@@ -66,21 +66,104 @@ export default class SaveStateHandler {
|
|
|
66
66
|
this.saveStateOption = saveState;
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
-
|
|
70
|
-
|
|
69
|
+
private getKeyName(): string {
|
|
70
|
+
if (typeof this.saveStateOption === "string") {
|
|
71
|
+
return this.saveStateOption;
|
|
72
|
+
} else {
|
|
73
|
+
return "tree";
|
|
74
|
+
}
|
|
75
|
+
}
|
|
71
76
|
|
|
72
|
-
|
|
73
|
-
|
|
77
|
+
private loadFromStorage(): null | string {
|
|
78
|
+
if (this.onGetStateFromStorage) {
|
|
79
|
+
return this.onGetStateFromStorage();
|
|
74
80
|
} else if (this.supportsLocalStorage()) {
|
|
75
|
-
localStorage.
|
|
81
|
+
return localStorage.getItem(this.getKeyName());
|
|
82
|
+
} else {
|
|
83
|
+
return null;
|
|
76
84
|
}
|
|
77
85
|
}
|
|
78
86
|
|
|
79
|
-
|
|
80
|
-
|
|
87
|
+
private openInitialNodes(nodeIds: NodeId[]): boolean {
|
|
88
|
+
let mustLoadOnDemand = false;
|
|
81
89
|
|
|
82
|
-
|
|
83
|
-
|
|
90
|
+
for (const nodeId of nodeIds) {
|
|
91
|
+
const node = this.getNodeById(nodeId);
|
|
92
|
+
|
|
93
|
+
if (node) {
|
|
94
|
+
if (!node.load_on_demand) {
|
|
95
|
+
node.is_open = true;
|
|
96
|
+
} else {
|
|
97
|
+
mustLoadOnDemand = true;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return mustLoadOnDemand;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
private parseState(jsonData: string): SavedState {
|
|
106
|
+
const state = JSON.parse(jsonData) as Record<string, unknown>;
|
|
107
|
+
|
|
108
|
+
// Check if selected_node is an int (instead of an array)
|
|
109
|
+
if (state.selected_node && isInt(state.selected_node)) {
|
|
110
|
+
// Convert to array
|
|
111
|
+
state.selected_node = [state.selected_node];
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return state as unknown as SavedState;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
private resetSelection(): void {
|
|
118
|
+
const selectedNodes = this.getSelectedNodes();
|
|
119
|
+
|
|
120
|
+
selectedNodes.forEach((node) => {
|
|
121
|
+
this.removeFromSelection(node);
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
private selectInitialNodes(nodeIds: NodeId[]): boolean {
|
|
126
|
+
let selectCount = 0;
|
|
127
|
+
|
|
128
|
+
for (const nodeId of nodeIds) {
|
|
129
|
+
const node = this.getNodeById(nodeId);
|
|
130
|
+
|
|
131
|
+
if (node) {
|
|
132
|
+
selectCount += 1;
|
|
133
|
+
|
|
134
|
+
this.addToSelection(node);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return selectCount !== 0;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
private supportsLocalStorage(): boolean {
|
|
142
|
+
const testSupport = (): boolean => {
|
|
143
|
+
// Check if it's possible to store an item. Safari does not allow this in private browsing mode.
|
|
144
|
+
try {
|
|
145
|
+
const key = "_storage_test";
|
|
146
|
+
sessionStorage.setItem(key, "value");
|
|
147
|
+
sessionStorage.removeItem(key);
|
|
148
|
+
} catch {
|
|
149
|
+
return false;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return true;
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
if (this._supportsLocalStorage == null) {
|
|
156
|
+
this._supportsLocalStorage = testSupport();
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return this._supportsLocalStorage;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
public getNodeIdToBeSelected(): NodeId | null {
|
|
163
|
+
const state = this.getStateFromStorage();
|
|
164
|
+
|
|
165
|
+
if (state?.selected_node) {
|
|
166
|
+
return state.selected_node[0] ?? null;
|
|
84
167
|
} else {
|
|
85
168
|
return null;
|
|
86
169
|
}
|
|
@@ -118,6 +201,26 @@ export default class SaveStateHandler {
|
|
|
118
201
|
};
|
|
119
202
|
}
|
|
120
203
|
|
|
204
|
+
public getStateFromStorage(): null | SavedState {
|
|
205
|
+
const jsonData = this.loadFromStorage();
|
|
206
|
+
|
|
207
|
+
if (jsonData) {
|
|
208
|
+
return this.parseState(jsonData) as unknown as SavedState;
|
|
209
|
+
} else {
|
|
210
|
+
return null;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
public saveState(): void {
|
|
215
|
+
const state = JSON.stringify(this.getState());
|
|
216
|
+
|
|
217
|
+
if (this.onSetStateFromStorage) {
|
|
218
|
+
this.onSetStateFromStorage(state);
|
|
219
|
+
} else if (this.supportsLocalStorage()) {
|
|
220
|
+
localStorage.setItem(this.getKeyName(), state);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
121
224
|
/*
|
|
122
225
|
Set initial state
|
|
123
226
|
Don't handle nodes that are loaded on demand
|
|
@@ -125,22 +228,19 @@ export default class SaveStateHandler {
|
|
|
125
228
|
result: must load on demand
|
|
126
229
|
*/
|
|
127
230
|
public setInitialState(state: SavedState): boolean {
|
|
128
|
-
|
|
129
|
-
return false;
|
|
130
|
-
} else {
|
|
131
|
-
let mustLoadOnDemand = false;
|
|
231
|
+
let mustLoadOnDemand = false;
|
|
132
232
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
233
|
+
if (state.open_nodes) {
|
|
234
|
+
mustLoadOnDemand = this.openInitialNodes(state.open_nodes);
|
|
235
|
+
}
|
|
136
236
|
|
|
137
|
-
|
|
138
|
-
this.resetSelection();
|
|
139
|
-
this.selectInitialNodes(state.selected_node);
|
|
140
|
-
}
|
|
237
|
+
this.resetSelection();
|
|
141
238
|
|
|
142
|
-
|
|
239
|
+
if (state.selected_node) {
|
|
240
|
+
this.selectInitialNodes(state.selected_node);
|
|
143
241
|
}
|
|
242
|
+
|
|
243
|
+
return mustLoadOnDemand;
|
|
144
244
|
}
|
|
145
245
|
|
|
146
246
|
public setInitialStateOnDemand(
|
|
@@ -151,6 +251,10 @@ export default class SaveStateHandler {
|
|
|
151
251
|
let nodeIds = state.open_nodes;
|
|
152
252
|
|
|
153
253
|
const openNodes = (): void => {
|
|
254
|
+
if (!nodeIds) {
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
|
|
154
258
|
const newNodesIds = [];
|
|
155
259
|
|
|
156
260
|
for (const nodeId of nodeIds) {
|
|
@@ -171,8 +275,10 @@ export default class SaveStateHandler {
|
|
|
171
275
|
|
|
172
276
|
nodeIds = newNodesIds;
|
|
173
277
|
|
|
174
|
-
if (
|
|
175
|
-
this.
|
|
278
|
+
if (state.selected_node) {
|
|
279
|
+
if (this.selectInitialNodes(state.selected_node)) {
|
|
280
|
+
this.refreshElements(null);
|
|
281
|
+
}
|
|
176
282
|
}
|
|
177
283
|
|
|
178
284
|
if (loadingCount === 0) {
|
|
@@ -190,112 +296,4 @@ export default class SaveStateHandler {
|
|
|
190
296
|
|
|
191
297
|
openNodes();
|
|
192
298
|
}
|
|
193
|
-
|
|
194
|
-
public getNodeIdToBeSelected(): NodeId | null {
|
|
195
|
-
const state = this.getStateFromStorage();
|
|
196
|
-
|
|
197
|
-
if (state?.selected_node) {
|
|
198
|
-
return state.selected_node[0] || null;
|
|
199
|
-
} else {
|
|
200
|
-
return null;
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
private parseState(jsonData: string): SavedState {
|
|
205
|
-
const state = JSON.parse(jsonData) as Record<string, unknown>;
|
|
206
|
-
|
|
207
|
-
// Check if selected_node is an int (instead of an array)
|
|
208
|
-
if (state && state.selected_node && isInt(state.selected_node)) {
|
|
209
|
-
// Convert to array
|
|
210
|
-
state.selected_node = [state.selected_node];
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
return state as unknown as SavedState;
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
private loadFromStorage(): string | null {
|
|
217
|
-
if (this.onGetStateFromStorage) {
|
|
218
|
-
return this.onGetStateFromStorage();
|
|
219
|
-
} else if (this.supportsLocalStorage()) {
|
|
220
|
-
return localStorage.getItem(this.getKeyName());
|
|
221
|
-
} else {
|
|
222
|
-
return null;
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
private openInitialNodes(nodeIds: NodeId[]): boolean {
|
|
227
|
-
let mustLoadOnDemand = false;
|
|
228
|
-
|
|
229
|
-
for (const nodeId of nodeIds) {
|
|
230
|
-
const node = this.getNodeById(nodeId);
|
|
231
|
-
|
|
232
|
-
if (node) {
|
|
233
|
-
if (!node.load_on_demand) {
|
|
234
|
-
node.is_open = true;
|
|
235
|
-
} else {
|
|
236
|
-
mustLoadOnDemand = true;
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
return mustLoadOnDemand;
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
private selectInitialNodes(nodeIds: NodeId[]): boolean {
|
|
245
|
-
let selectCount = 0;
|
|
246
|
-
|
|
247
|
-
for (const nodeId of nodeIds) {
|
|
248
|
-
const node = this.getNodeById(nodeId);
|
|
249
|
-
|
|
250
|
-
if (node) {
|
|
251
|
-
selectCount += 1;
|
|
252
|
-
|
|
253
|
-
this.addToSelection(node);
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
return selectCount !== 0;
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
private resetSelection(): void {
|
|
261
|
-
const selectedNodes = this.getSelectedNodes();
|
|
262
|
-
|
|
263
|
-
selectedNodes.forEach((node) => {
|
|
264
|
-
this.removeFromSelection(node);
|
|
265
|
-
});
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
private getKeyName(): string {
|
|
269
|
-
if (typeof this.saveStateOption === "string") {
|
|
270
|
-
return this.saveStateOption;
|
|
271
|
-
} else {
|
|
272
|
-
return "tree";
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
private supportsLocalStorage(): boolean {
|
|
277
|
-
const testSupport = (): boolean => {
|
|
278
|
-
// Is local storage supported?
|
|
279
|
-
if (localStorage == null) {
|
|
280
|
-
return false;
|
|
281
|
-
} else {
|
|
282
|
-
// Check if it's possible to store an item. Safari does not allow this in private browsing mode.
|
|
283
|
-
try {
|
|
284
|
-
const key = "_storage_test";
|
|
285
|
-
sessionStorage.setItem(key, "value");
|
|
286
|
-
sessionStorage.removeItem(key);
|
|
287
|
-
} catch (error) {
|
|
288
|
-
return false;
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
return true;
|
|
292
|
-
}
|
|
293
|
-
};
|
|
294
|
-
|
|
295
|
-
if (this._supportsLocalStorage == null) {
|
|
296
|
-
this._supportsLocalStorage = testSupport();
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
return this._supportsLocalStorage;
|
|
300
|
-
}
|
|
301
299
|
}
|