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.
@@ -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
- public open(
38
- onFinished: OnFinishOpenNode | undefined,
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 = true;
55
+ this.node.is_open = false;
47
56
 
48
57
  const button = this.getButton();
49
- button.classList.remove("jqtree-closed");
58
+ button.classList.add("jqtree-closed");
50
59
  button.innerHTML = "";
51
60
 
52
- const openedIconElement = this.openedIconElement;
61
+ const closedIconElement = this.closedIconElement;
53
62
 
54
- if (openedIconElement) {
55
- const icon = openedIconElement.cloneNode(true);
63
+ if (closedIconElement) {
64
+ const icon = closedIconElement.cloneNode(true);
56
65
  button.appendChild(icon);
57
66
  }
58
67
 
59
- const doOpen = (): void => {
60
- this.element.classList.remove("jqtree-closed");
68
+ const doClose = (): void => {
69
+ this.element.classList.add("jqtree-closed");
61
70
 
62
71
  const titleSpan = this.getTitleSpan();
63
- titleSpan.setAttribute("aria-expanded", "true");
64
-
65
- if (onFinished) {
66
- onFinished(this.node);
67
- }
72
+ titleSpan.setAttribute("aria-expanded", "false");
68
73
 
69
- this.triggerEvent("tree.open", {
74
+ this.triggerEvent("tree.close", {
70
75
  node: this.node,
71
76
  });
72
77
  };
73
78
 
74
79
  if (slide) {
75
- jQuery(this.getUl()).slideDown(animationSpeed, doOpen);
80
+ jQuery(this.getUl()).slideUp(animationSpeed, doClose);
76
81
  } else {
77
- jQuery(this.getUl()).show();
78
- doOpen();
82
+ jQuery(this.getUl()).hide();
83
+ doClose();
79
84
  }
80
85
  }
81
86
 
82
- public close(
87
+ public open(
88
+ onFinished: OnFinishOpenNode | undefined,
83
89
  slide = true,
84
- animationSpeed: JQuery.Duration | undefined = "fast",
90
+ animationSpeed: JQuery.Duration = "fast",
85
91
  ): void {
86
- if (!this.node.is_open) {
92
+ if (this.node.is_open) {
87
93
  return;
88
94
  }
89
95
 
90
- this.node.is_open = false;
96
+ this.node.is_open = true;
91
97
 
92
98
  const button = this.getButton();
93
- button.classList.add("jqtree-closed");
99
+ button.classList.remove("jqtree-closed");
94
100
  button.innerHTML = "";
95
101
 
96
- const closedIconElement = this.closedIconElement;
102
+ const openedIconElement = this.openedIconElement;
97
103
 
98
- if (closedIconElement) {
99
- const icon = closedIconElement.cloneNode(true);
104
+ if (openedIconElement) {
105
+ const icon = openedIconElement.cloneNode(true);
100
106
  button.appendChild(icon);
101
107
  }
102
108
 
103
- const doClose = (): void => {
104
- this.element.classList.add("jqtree-closed");
109
+ const doOpen = (): void => {
110
+ this.element.classList.remove("jqtree-closed");
105
111
 
106
112
  const titleSpan = this.getTitleSpan();
107
- titleSpan.setAttribute("aria-expanded", "false");
113
+ titleSpan.setAttribute("aria-expanded", "true");
108
114
 
109
- this.triggerEvent("tree.close", {
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()).slideUp(animationSpeed, doClose);
125
+ jQuery(this.getUl()).slideDown(animationSpeed, doOpen);
116
126
  } else {
117
- jQuery(this.getUl()).hide();
118
- doClose();
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;
@@ -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
- public node: Node;
17
- public element: HTMLElement;
16
+ private $treeElement: JQuery;
18
17
  private getScrollLeft: GetScrollLeft;
19
18
  private tabIndex?: number;
20
- private $treeElement: JQuery<HTMLElement>;
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
@@ -7,4 +7,4 @@ export const isNodeRecordWithChildren = (
7
7
  ): data is NodeRecordWithChildren =>
8
8
  typeof data === "object" &&
9
9
  "children" in data &&
10
- data["children"] instanceof Array;
10
+ data.children instanceof Array;
package/src/position.ts CHANGED
@@ -6,8 +6,8 @@ export enum Position {
6
6
  }
7
7
 
8
8
  const positionNames: Record<string, Position> = {
9
- before: Position.Before,
10
9
  after: Position.After,
10
+ before: Position.Before,
11
11
  inside: Position.Inside,
12
12
  none: Position.None,
13
13
  };
@@ -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: NodeId[];
16
- selected_node: NodeId[];
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
- public saveState(): void {
70
- const state = JSON.stringify(this.getState());
69
+ private getKeyName(): string {
70
+ if (typeof this.saveStateOption === "string") {
71
+ return this.saveStateOption;
72
+ } else {
73
+ return "tree";
74
+ }
75
+ }
71
76
 
72
- if (this.onSetStateFromStorage) {
73
- this.onSetStateFromStorage(state);
77
+ private loadFromStorage(): null | string {
78
+ if (this.onGetStateFromStorage) {
79
+ return this.onGetStateFromStorage();
74
80
  } else if (this.supportsLocalStorage()) {
75
- localStorage.setItem(this.getKeyName(), state);
81
+ return localStorage.getItem(this.getKeyName());
82
+ } else {
83
+ return null;
76
84
  }
77
85
  }
78
86
 
79
- public getStateFromStorage(): SavedState | null {
80
- const jsonData = this.loadFromStorage();
87
+ private openInitialNodes(nodeIds: NodeId[]): boolean {
88
+ let mustLoadOnDemand = false;
81
89
 
82
- if (jsonData) {
83
- return this.parseState(jsonData) as unknown as SavedState;
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
- if (!state) {
129
- return false;
130
- } else {
131
- let mustLoadOnDemand = false;
231
+ let mustLoadOnDemand = false;
132
232
 
133
- if (state.open_nodes) {
134
- mustLoadOnDemand = this.openInitialNodes(state.open_nodes);
135
- }
233
+ if (state.open_nodes) {
234
+ mustLoadOnDemand = this.openInitialNodes(state.open_nodes);
235
+ }
136
236
 
137
- if (state.selected_node) {
138
- this.resetSelection();
139
- this.selectInitialNodes(state.selected_node);
140
- }
237
+ this.resetSelection();
141
238
 
142
- return mustLoadOnDemand;
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 (this.selectInitialNodes(state.selected_node)) {
175
- this.refreshElements(null);
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
  }