wunderbaum 0.0.4 → 0.0.5
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/README.md +2 -0
- package/dist/wunderbaum.css +1 -1
- package/dist/wunderbaum.d.ts +141 -45
- package/dist/wunderbaum.esm.js +439 -184
- package/dist/wunderbaum.esm.min.js +25 -24
- package/dist/wunderbaum.esm.min.js.map +1 -1
- package/dist/wunderbaum.umd.js +439 -184
- package/dist/wunderbaum.umd.min.js +30 -29
- package/dist/wunderbaum.umd.min.js.map +1 -1
- package/package.json +2 -2
- package/src/common.ts +35 -27
- package/src/util.ts +11 -9
- package/src/wb_ext_dnd.ts +10 -9
- package/src/wb_ext_edit.ts +9 -10
- package/src/wb_ext_filter.ts +14 -2
- package/src/wb_ext_keynav.ts +80 -13
- package/src/wb_node.ts +53 -35
- package/src/wb_options.ts +59 -5
- package/src/wunderbaum.scss +91 -23
- package/src/wunderbaum.ts +276 -100
package/src/util.ts
CHANGED
|
@@ -198,7 +198,7 @@ export function extractHtmlText(s: string) {
|
|
|
198
198
|
*
|
|
199
199
|
* If a `<span class="wb-col">` is passed, the first child input is used.
|
|
200
200
|
* Depending on the target element type, `value` is interpreted accordingly.
|
|
201
|
-
* For example for a checkbox, a value of true, false, or null is returned if
|
|
201
|
+
* For example for a checkbox, a value of true, false, or null is returned if
|
|
202
202
|
* the element is checked, unchecked, or indeterminate.
|
|
203
203
|
* For datetime input control a numerical value is assumed, etc.
|
|
204
204
|
*
|
|
@@ -300,7 +300,9 @@ export function setValueToElem(elem: HTMLElement, value: any): void {
|
|
|
300
300
|
|
|
301
301
|
switch (type) {
|
|
302
302
|
case "checkbox":
|
|
303
|
-
|
|
303
|
+
// An explicit `null` value is interpreted as 'indeterminate'.
|
|
304
|
+
// `undefined` is interpreted as 'unchecked'
|
|
305
|
+
input.indeterminate = value === null;
|
|
304
306
|
input.checked = !!value;
|
|
305
307
|
break;
|
|
306
308
|
case "date":
|
|
@@ -682,7 +684,7 @@ export function toSet(val: any): Set<string> {
|
|
|
682
684
|
throw new Error("Cannot convert to Set<string>: " + val);
|
|
683
685
|
}
|
|
684
686
|
|
|
685
|
-
/**Return a canonical string representation for an object's type (e.g. 'array', 'number', ...) */
|
|
687
|
+
/** Return a canonical string representation for an object's type (e.g. 'array', 'number', ...). */
|
|
686
688
|
export function type(obj: any): string {
|
|
687
689
|
return Object.prototype.toString
|
|
688
690
|
.call(obj)
|
|
@@ -697,12 +699,12 @@ export function type(obj: any): string {
|
|
|
697
699
|
* previous call.
|
|
698
700
|
* Example:
|
|
699
701
|
* ```js
|
|
700
|
-
* throttledFoo = util.
|
|
702
|
+
* throttledFoo = util.adaptiveThrottle(foo.bind(this), {});
|
|
701
703
|
* throttledFoo();
|
|
702
704
|
* throttledFoo();
|
|
703
705
|
* ```
|
|
704
706
|
*/
|
|
705
|
-
export function
|
|
707
|
+
export function adaptiveThrottle(
|
|
706
708
|
this: unknown,
|
|
707
709
|
callback: (...args: any[]) => void,
|
|
708
710
|
options: any
|
|
@@ -724,7 +726,7 @@ export function addaptiveThrottle(
|
|
|
724
726
|
const throttledFn = (...args: any[]) => {
|
|
725
727
|
if (waiting) {
|
|
726
728
|
pendingArgs = args;
|
|
727
|
-
// console.log(`
|
|
729
|
+
// console.log(`adaptiveThrottle() queing request #${waiting}...`, args);
|
|
728
730
|
waiting += 1;
|
|
729
731
|
} else {
|
|
730
732
|
// Prevent invocations while running or blocking
|
|
@@ -732,7 +734,7 @@ export function addaptiveThrottle(
|
|
|
732
734
|
const useArgs = args; // pendingArgs || args;
|
|
733
735
|
pendingArgs = null;
|
|
734
736
|
|
|
735
|
-
// console.log(`
|
|
737
|
+
// console.log(`adaptiveThrottle() execute...`, useArgs);
|
|
736
738
|
const start = Date.now();
|
|
737
739
|
try {
|
|
738
740
|
callback.apply(this, useArgs);
|
|
@@ -747,7 +749,7 @@ export function addaptiveThrottle(
|
|
|
747
749
|
);
|
|
748
750
|
const useDelay = Math.max(minDelay, curDelay - elap);
|
|
749
751
|
// console.log(
|
|
750
|
-
// `
|
|
752
|
+
// `adaptiveThrottle() calling worker took ${elap}ms. delay = ${curDelay}ms, using ${useDelay}ms`,
|
|
751
753
|
// pendingArgs
|
|
752
754
|
// );
|
|
753
755
|
setTimeout(() => {
|
|
@@ -757,7 +759,7 @@ export function addaptiveThrottle(
|
|
|
757
759
|
if (pendingArgs != null) {
|
|
758
760
|
// There was another request while running or waiting
|
|
759
761
|
// console.log(
|
|
760
|
-
// `
|
|
762
|
+
// `adaptiveThrottle() re-trigger (missed ${skipped})...`,
|
|
761
763
|
// pendingArgs
|
|
762
764
|
// );
|
|
763
765
|
throttledFn.apply(this, pendingArgs);
|
package/src/wb_ext_dnd.ts
CHANGED
|
@@ -245,8 +245,9 @@ export class DndExtension extends WunderbaumExtension {
|
|
|
245
245
|
const ltn = this.lastTargetNode;
|
|
246
246
|
this.lastEnterStamp = 0;
|
|
247
247
|
if (ltn) {
|
|
248
|
-
ltn.
|
|
249
|
-
"wb-drop-target wb-drop-over wb-drop-after wb-drop-before"
|
|
248
|
+
ltn.setClass(
|
|
249
|
+
"wb-drop-target wb-drop-over wb-drop-after wb-drop-before",
|
|
250
|
+
false
|
|
250
251
|
);
|
|
251
252
|
this.lastTargetNode = null;
|
|
252
253
|
}
|
|
@@ -295,7 +296,7 @@ export class DndExtension extends WunderbaumExtension {
|
|
|
295
296
|
protected autoScroll(event: DragEvent): number {
|
|
296
297
|
let tree = this.tree,
|
|
297
298
|
dndOpts = tree.options.dnd!,
|
|
298
|
-
sp = tree.
|
|
299
|
+
sp = tree.scrollContainerElement,
|
|
299
300
|
sensitivity = dndOpts.scrollSensitivity,
|
|
300
301
|
speed = dndOpts.scrollSpeed,
|
|
301
302
|
scrolled = 0;
|
|
@@ -352,7 +353,7 @@ export class DndExtension extends WunderbaumExtension {
|
|
|
352
353
|
setTimeout(() => {
|
|
353
354
|
// Decouple this call, so the CSS is applied to the node, but not to
|
|
354
355
|
// the system generated drag image
|
|
355
|
-
srcNode.
|
|
356
|
+
srcNode.setClass("wb-drag-source");
|
|
356
357
|
}, 0);
|
|
357
358
|
|
|
358
359
|
// --- drag ---
|
|
@@ -360,7 +361,7 @@ export class DndExtension extends WunderbaumExtension {
|
|
|
360
361
|
// This event occurs very often...
|
|
361
362
|
// --- dragend ---
|
|
362
363
|
} else if (e.type === "dragend") {
|
|
363
|
-
srcNode.
|
|
364
|
+
srcNode.setClass("wb-drag-source", false);
|
|
364
365
|
this.srcNode = null;
|
|
365
366
|
if (this.lastTargetNode) {
|
|
366
367
|
this._leaveNode();
|
|
@@ -424,7 +425,7 @@ export class DndExtension extends WunderbaumExtension {
|
|
|
424
425
|
}
|
|
425
426
|
this.lastAllowedDropRegions = regionSet;
|
|
426
427
|
this.lastDropEffect = dt.dropEffect;
|
|
427
|
-
targetNode.
|
|
428
|
+
targetNode.setClass("wb-drop-target");
|
|
428
429
|
|
|
429
430
|
e.preventDefault(); // Allow drop (Drop operation is denied by default)
|
|
430
431
|
return false;
|
|
@@ -450,9 +451,9 @@ export class DndExtension extends WunderbaumExtension {
|
|
|
450
451
|
if (!region) {
|
|
451
452
|
return; // We already rejected in dragenter
|
|
452
453
|
}
|
|
453
|
-
targetNode.
|
|
454
|
-
targetNode.
|
|
455
|
-
targetNode.
|
|
454
|
+
targetNode.setClass("wb-drop-over", region === "over");
|
|
455
|
+
targetNode.setClass("wb-drop-before", region === "before");
|
|
456
|
+
targetNode.setClass("wb-drop-after", region === "after");
|
|
456
457
|
// console.log("dragover", e);
|
|
457
458
|
|
|
458
459
|
// dt.dropEffect = this.lastDropEffect!;
|
package/src/wb_ext_edit.ts
CHANGED
|
@@ -63,14 +63,14 @@ export class EditExtension extends WunderbaumExtension {
|
|
|
63
63
|
|
|
64
64
|
node.log(`_applyChange(${eventName})`, extra);
|
|
65
65
|
|
|
66
|
-
colElem.classList.add("wb-
|
|
66
|
+
colElem.classList.add("wb-busy");
|
|
67
67
|
colElem.classList.remove("wb-error");
|
|
68
68
|
try {
|
|
69
69
|
res = node._callEvent(eventName, extra);
|
|
70
70
|
} catch (err) {
|
|
71
71
|
node.logError(`Error in ${eventName} event handler`, err);
|
|
72
72
|
colElem.classList.add("wb-error");
|
|
73
|
-
colElem.classList.remove("wb-
|
|
73
|
+
colElem.classList.remove("wb-busy");
|
|
74
74
|
}
|
|
75
75
|
// Convert scalar return value to a resolved promise
|
|
76
76
|
if (!(res instanceof Promise)) {
|
|
@@ -82,7 +82,7 @@ export class EditExtension extends WunderbaumExtension {
|
|
|
82
82
|
colElem.classList.add("wb-error");
|
|
83
83
|
})
|
|
84
84
|
.finally(() => {
|
|
85
|
-
colElem.classList.remove("wb-
|
|
85
|
+
colElem.classList.remove("wb-busy");
|
|
86
86
|
});
|
|
87
87
|
return res;
|
|
88
88
|
}
|
|
@@ -130,15 +130,14 @@ export class EditExtension extends WunderbaumExtension {
|
|
|
130
130
|
const eventName = eventToString(event);
|
|
131
131
|
const tree = this.tree;
|
|
132
132
|
const trigger = this.getPluginOption("trigger");
|
|
133
|
-
const inputElem =
|
|
134
|
-
|
|
135
|
-
// let handled = true;
|
|
133
|
+
// const inputElem =
|
|
134
|
+
// event.target && event.target.closest("input,[contenteditable]");
|
|
136
135
|
|
|
137
136
|
tree.logDebug(`_preprocessKeyEvent: ${eventName}`);
|
|
138
137
|
|
|
139
138
|
// --- Title editing: apply/discard ---
|
|
140
|
-
if (inputElem) {
|
|
141
|
-
|
|
139
|
+
// if (inputElem) {
|
|
140
|
+
if (this.isEditingTitle()) {
|
|
142
141
|
switch (eventName) {
|
|
143
142
|
case "Enter":
|
|
144
143
|
this._stopEditTitle(true, { event: event });
|
|
@@ -205,7 +204,7 @@ export class EditExtension extends WunderbaumExtension {
|
|
|
205
204
|
titleSpan.innerHTML = inputHtml;
|
|
206
205
|
const inputElem = titleSpan.firstElementChild as HTMLInputElement;
|
|
207
206
|
if (validity) {
|
|
208
|
-
// Permanently apply
|
|
207
|
+
// Permanently apply input validations (CSS and tooltip)
|
|
209
208
|
inputElem.addEventListener("keydown", (e) => {
|
|
210
209
|
inputElem.setCustomValidity("");
|
|
211
210
|
if (!inputElem.reportValidity()) {
|
|
@@ -336,7 +335,7 @@ export class EditExtension extends WunderbaumExtension {
|
|
|
336
335
|
return;
|
|
337
336
|
}
|
|
338
337
|
const newNode = node.addNode(init, mode);
|
|
339
|
-
newNode.
|
|
338
|
+
newNode.setClass("wb-edit-new");
|
|
340
339
|
this.relatedNode = node;
|
|
341
340
|
|
|
342
341
|
// Don't filter new nodes:
|
package/src/wb_ext_filter.ts
CHANGED
|
@@ -28,6 +28,7 @@ export class FilterExtension extends WunderbaumExtension {
|
|
|
28
28
|
|
|
29
29
|
constructor(tree: Wunderbaum) {
|
|
30
30
|
super(tree, "filter", {
|
|
31
|
+
attachInput: null, // Element or selector of an input control for filter query strings
|
|
31
32
|
autoApply: true, // Re-apply last filter if lazy data is loaded
|
|
32
33
|
autoExpand: false, // Expand all branches that contain matches while filtered
|
|
33
34
|
counter: true, // Show a badge with number of matching child nodes near parent icons
|
|
@@ -36,14 +37,14 @@ export class FilterExtension extends WunderbaumExtension {
|
|
|
36
37
|
hideExpanders: false, // Hide expanders if all child nodes are hidden by filter
|
|
37
38
|
highlight: true, // Highlight matches by wrapping inside <mark> tags
|
|
38
39
|
leavesOnly: false, // Match end nodes only
|
|
39
|
-
mode: "
|
|
40
|
+
mode: "dim", // Grayout unmatched nodes (pass "hide" to remove unmatched node instead)
|
|
40
41
|
noData: true, // Display a 'no data' status node if result is empty
|
|
41
42
|
});
|
|
42
43
|
}
|
|
43
44
|
|
|
44
45
|
init() {
|
|
45
46
|
super.init();
|
|
46
|
-
|
|
47
|
+
const attachInput = this.getPluginOption("attachInput");
|
|
47
48
|
if (attachInput) {
|
|
48
49
|
this.queryInput = elemFromSelector(attachInput) as HTMLInputElement;
|
|
49
50
|
onEvent(
|
|
@@ -57,6 +58,17 @@ export class FilterExtension extends WunderbaumExtension {
|
|
|
57
58
|
}
|
|
58
59
|
}
|
|
59
60
|
|
|
61
|
+
setPluginOption(name: string, value: any): void {
|
|
62
|
+
// alert("filter opt=" + name + ", " + value)
|
|
63
|
+
super.setPluginOption(name, value);
|
|
64
|
+
switch (name) {
|
|
65
|
+
case "mode":
|
|
66
|
+
this.tree.filterMode = value === "hide" ? "hide" : "dim";
|
|
67
|
+
this.tree.updateFilter();
|
|
68
|
+
break;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
60
72
|
_applyFilterNoUpdate(
|
|
61
73
|
filter: string | NodeFilterCallback,
|
|
62
74
|
branchMode: boolean,
|
package/src/wb_ext_keynav.ts
CHANGED
|
@@ -4,7 +4,11 @@
|
|
|
4
4
|
* @VERSION, @DATE (https://github.com/mar10/wunderbaum)
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
// NAVIGATE_IN_INPUT_KEYS,
|
|
9
|
+
NavigationMode,
|
|
10
|
+
NavigationModeOption,
|
|
11
|
+
} from "./common";
|
|
8
12
|
import { eventToString } from "./util";
|
|
9
13
|
import { Wunderbaum } from "./wunderbaum";
|
|
10
14
|
import { WunderbaumNode } from "./wb_node";
|
|
@@ -15,18 +19,47 @@ export class KeynavExtension extends WunderbaumExtension {
|
|
|
15
19
|
super(tree, "keynav", {});
|
|
16
20
|
}
|
|
17
21
|
|
|
22
|
+
protected _getEmbeddedInputElem(
|
|
23
|
+
elem: any,
|
|
24
|
+
setFocus = false
|
|
25
|
+
): HTMLInputElement | null {
|
|
26
|
+
let input = null;
|
|
27
|
+
|
|
28
|
+
if (elem && elem.type != null) {
|
|
29
|
+
input = elem;
|
|
30
|
+
} else {
|
|
31
|
+
// ,[contenteditable]
|
|
32
|
+
const ace = this.tree.getActiveColElem()?.querySelector("input,select");
|
|
33
|
+
if (ace) {
|
|
34
|
+
input = ace as HTMLInputElement;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
if (setFocus && input) {
|
|
38
|
+
this.tree.log("focus", input);
|
|
39
|
+
input.focus();
|
|
40
|
+
}
|
|
41
|
+
return input;
|
|
42
|
+
}
|
|
43
|
+
|
|
18
44
|
onKeyEvent(data: any): boolean | undefined {
|
|
19
|
-
|
|
20
|
-
eventName = eventToString(event),
|
|
21
|
-
focusNode,
|
|
22
|
-
node = data.node as WunderbaumNode,
|
|
45
|
+
const event = data.event,
|
|
23
46
|
tree = this.tree,
|
|
24
47
|
opts = data.options,
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
48
|
+
activate = !event.ctrlKey || opts.autoActivate,
|
|
49
|
+
curInput = this._getEmbeddedInputElem(event.target),
|
|
50
|
+
navModeOption = opts.navigationMode as NavigationModeOption,
|
|
51
|
+
isCellEditMode = tree.navMode === NavigationMode.cellEdit;
|
|
28
52
|
|
|
29
|
-
|
|
53
|
+
let focusNode,
|
|
54
|
+
eventName = eventToString(event),
|
|
55
|
+
node = data.node as WunderbaumNode,
|
|
56
|
+
handled = true;
|
|
57
|
+
|
|
58
|
+
tree.log(`onKeyEvent: ${eventName}, curInput`, curInput);
|
|
59
|
+
if (!tree.isEnabled()) {
|
|
60
|
+
// tree.logDebug(`onKeyEvent ignored for disabled tree: ${eventName}`);
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
30
63
|
|
|
31
64
|
// Let callback prevent default processing
|
|
32
65
|
if (tree._callEvent("keydown", data) === false) {
|
|
@@ -58,12 +91,15 @@ export class KeynavExtension extends WunderbaumExtension {
|
|
|
58
91
|
}
|
|
59
92
|
|
|
60
93
|
if (tree.navMode === NavigationMode.row) {
|
|
94
|
+
// -----------------------------------------------------------------------
|
|
95
|
+
// --- Row Mode ---
|
|
96
|
+
// -----------------------------------------------------------------------
|
|
61
97
|
// --- Quick-Search
|
|
62
98
|
if (
|
|
63
99
|
opts.quicksearch &&
|
|
64
100
|
eventName.length === 1 &&
|
|
65
|
-
/^\w$/.test(eventName)
|
|
66
|
-
|
|
101
|
+
/^\w$/.test(eventName) &&
|
|
102
|
+
!curInput
|
|
67
103
|
) {
|
|
68
104
|
// Allow to search for longer streaks if typed in quickly
|
|
69
105
|
const stamp = Date.now();
|
|
@@ -143,7 +179,17 @@ export class KeynavExtension extends WunderbaumExtension {
|
|
|
143
179
|
handled = false;
|
|
144
180
|
}
|
|
145
181
|
} else {
|
|
146
|
-
//
|
|
182
|
+
// -----------------------------------------------------------------------
|
|
183
|
+
// --- Cell Mode ---
|
|
184
|
+
// -----------------------------------------------------------------------
|
|
185
|
+
// // Standard navigation (cell mode)
|
|
186
|
+
// if (isCellEditMode && NAVIGATE_IN_INPUT_KEYS.has(eventName)) {
|
|
187
|
+
// }
|
|
188
|
+
if (eventName === "Tab") {
|
|
189
|
+
eventName = "ArrowRight";
|
|
190
|
+
} else if (eventName === "Shift+Tab") {
|
|
191
|
+
eventName = tree.activeColIdx > 0 ? "ArrowLeft" : "";
|
|
192
|
+
}
|
|
147
193
|
switch (eventName) {
|
|
148
194
|
case " ":
|
|
149
195
|
if (tree.activeColIdx === 0 && node.getOption("checkbox")) {
|
|
@@ -162,13 +208,24 @@ export class KeynavExtension extends WunderbaumExtension {
|
|
|
162
208
|
if (tree.activeColIdx === 0 && node.isExpandable()) {
|
|
163
209
|
node.setExpanded(!node.isExpanded());
|
|
164
210
|
handled = true;
|
|
211
|
+
} else if (
|
|
212
|
+
!isCellEditMode &&
|
|
213
|
+
(navModeOption === NavigationModeOption.startCell ||
|
|
214
|
+
navModeOption === NavigationModeOption.startRow)
|
|
215
|
+
) {
|
|
216
|
+
tree.setNavigationMode(NavigationMode.cellEdit);
|
|
217
|
+
this._getEmbeddedInputElem(null, true); // set focus to input
|
|
218
|
+
handled = true;
|
|
165
219
|
}
|
|
166
220
|
break;
|
|
167
221
|
case "Escape":
|
|
168
222
|
if (tree.navMode === NavigationMode.cellEdit) {
|
|
169
223
|
tree.setNavigationMode(NavigationMode.cellNav);
|
|
170
224
|
handled = true;
|
|
171
|
-
} else if (
|
|
225
|
+
} else if (
|
|
226
|
+
tree.navMode === NavigationMode.cellNav &&
|
|
227
|
+
navModeOption !== NavigationModeOption.cell
|
|
228
|
+
) {
|
|
172
229
|
tree.setNavigationMode(NavigationMode.row);
|
|
173
230
|
handled = true;
|
|
174
231
|
}
|
|
@@ -176,6 +233,9 @@ export class KeynavExtension extends WunderbaumExtension {
|
|
|
176
233
|
case "ArrowLeft":
|
|
177
234
|
if (tree.activeColIdx > 0) {
|
|
178
235
|
tree.setColumn(tree.activeColIdx - 1);
|
|
236
|
+
if (isCellEditMode) {
|
|
237
|
+
this._getEmbeddedInputElem(null, true); // set focus to input
|
|
238
|
+
}
|
|
179
239
|
handled = true;
|
|
180
240
|
} else if (navModeOption !== NavigationModeOption.cell) {
|
|
181
241
|
tree.setNavigationMode(NavigationMode.row);
|
|
@@ -185,6 +245,9 @@ export class KeynavExtension extends WunderbaumExtension {
|
|
|
185
245
|
case "ArrowRight":
|
|
186
246
|
if (tree.activeColIdx < tree.columns.length - 1) {
|
|
187
247
|
tree.setColumn(tree.activeColIdx + 1);
|
|
248
|
+
if (isCellEditMode) {
|
|
249
|
+
this._getEmbeddedInputElem(null, true); // set focus to input
|
|
250
|
+
}
|
|
188
251
|
handled = true;
|
|
189
252
|
}
|
|
190
253
|
break;
|
|
@@ -200,6 +263,10 @@ export class KeynavExtension extends WunderbaumExtension {
|
|
|
200
263
|
case "PageDown":
|
|
201
264
|
case "PageUp":
|
|
202
265
|
node.navigate(eventName, { activate: activate, event: event });
|
|
266
|
+
if (isCellEditMode) {
|
|
267
|
+
this._getEmbeddedInputElem(null, true); // set focus to input
|
|
268
|
+
}
|
|
269
|
+
handled = true;
|
|
203
270
|
break;
|
|
204
271
|
default:
|
|
205
272
|
handled = false;
|
package/src/wb_node.ts
CHANGED
|
@@ -50,7 +50,7 @@ const NODE_PROPS = new Set<string>([
|
|
|
50
50
|
const NODE_ATTRS = new Set<string>([
|
|
51
51
|
"checkbox",
|
|
52
52
|
"expanded",
|
|
53
|
-
"
|
|
53
|
+
"classes",
|
|
54
54
|
"folder",
|
|
55
55
|
"icon",
|
|
56
56
|
"iconTooltip",
|
|
@@ -108,8 +108,8 @@ export class WunderbaumNode {
|
|
|
108
108
|
public type?: string;
|
|
109
109
|
public tooltip?: string;
|
|
110
110
|
/** Additional classes added to `div.wb-row`.
|
|
111
|
-
* @see {@link
|
|
112
|
-
public
|
|
111
|
+
* @see {@link hasClass}, {@link setClass}. */
|
|
112
|
+
public classes: Set<string> | null = null; //new Set<string>();
|
|
113
113
|
/** Custom data that was passed to the constructor */
|
|
114
114
|
public data: any = {};
|
|
115
115
|
// --- Node Status ---
|
|
@@ -150,9 +150,7 @@ export class WunderbaumNode {
|
|
|
150
150
|
this.lazy = data.lazy === true;
|
|
151
151
|
this.selected = data.selected === true;
|
|
152
152
|
if (data.classes) {
|
|
153
|
-
|
|
154
|
-
this.extraClasses.add(c.trim());
|
|
155
|
-
}
|
|
153
|
+
this.setClass(data.classes);
|
|
156
154
|
}
|
|
157
155
|
// Store custom fields as `node.data`
|
|
158
156
|
for (const [key, value] of Object.entries(data)) {
|
|
@@ -172,7 +170,7 @@ export class WunderbaumNode {
|
|
|
172
170
|
* @internal
|
|
173
171
|
*/
|
|
174
172
|
toString() {
|
|
175
|
-
return
|
|
173
|
+
return `WunderbaumNode@${this.key}<'${this.title}'>`;
|
|
176
174
|
}
|
|
177
175
|
|
|
178
176
|
// /** Return an option value. */
|
|
@@ -307,28 +305,39 @@ export class WunderbaumNode {
|
|
|
307
305
|
return this.tree.applyCommand(cmd, this, opts);
|
|
308
306
|
}
|
|
309
307
|
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
this._rowElem?.classList.remove(cn);
|
|
323
|
-
});
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
toggleClass(className: string | string[] | Set<string>, flag: boolean) {
|
|
308
|
+
/**
|
|
309
|
+
* Add/remove one or more classes to `<div class='wb-row'>`.
|
|
310
|
+
*
|
|
311
|
+
* This also maintains `node.classes`, so the class will survive a re-render.
|
|
312
|
+
*
|
|
313
|
+
* @param className one or more class names. Multiple classes can be passed
|
|
314
|
+
* as space-separated string, array of strings, or set of strings.
|
|
315
|
+
*/
|
|
316
|
+
setClass(
|
|
317
|
+
className: string | string[] | Set<string>,
|
|
318
|
+
flag: boolean = true
|
|
319
|
+
): void {
|
|
327
320
|
const cnSet = util.toSet(className);
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
321
|
+
if (flag) {
|
|
322
|
+
if (this.classes === null) {
|
|
323
|
+
this.classes = new Set<string>();
|
|
324
|
+
}
|
|
325
|
+
cnSet.forEach((cn) => {
|
|
326
|
+
this.classes!.add(cn);
|
|
327
|
+
this._rowElem?.classList.toggle(cn, flag);
|
|
328
|
+
});
|
|
329
|
+
} else {
|
|
330
|
+
if (this.classes === null) {
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
cnSet.forEach((cn) => {
|
|
334
|
+
this.classes!.delete(cn);
|
|
335
|
+
this._rowElem?.classList.toggle(cn, flag);
|
|
336
|
+
});
|
|
337
|
+
if (this.classes.size === 0) {
|
|
338
|
+
this.classes = null;
|
|
339
|
+
}
|
|
340
|
+
}
|
|
332
341
|
}
|
|
333
342
|
|
|
334
343
|
/** */
|
|
@@ -527,6 +536,11 @@ export class WunderbaumNode {
|
|
|
527
536
|
return !!(this.children && this.children.length);
|
|
528
537
|
}
|
|
529
538
|
|
|
539
|
+
/** Return true if node has className set. */
|
|
540
|
+
hasClass(className: string): boolean {
|
|
541
|
+
return this.classes ? this.classes.has(className) : false;
|
|
542
|
+
}
|
|
543
|
+
|
|
530
544
|
/** Return true if this node is the currently active tree node. */
|
|
531
545
|
isActive() {
|
|
532
546
|
return this.tree.activeNode === this;
|
|
@@ -705,9 +719,13 @@ export class WunderbaumNode {
|
|
|
705
719
|
"If `source` is an object, it must have a `children` property"
|
|
706
720
|
);
|
|
707
721
|
if (source.types) {
|
|
708
|
-
|
|
709
|
-
|
|
722
|
+
tree.setTypes(source.types, false);
|
|
723
|
+
}
|
|
724
|
+
if (source.columns) {
|
|
725
|
+
tree.columns = source.columns;
|
|
726
|
+
tree.updateColumns({ calculateCols: false });
|
|
710
727
|
}
|
|
728
|
+
|
|
711
729
|
this.addChildren(source.children);
|
|
712
730
|
|
|
713
731
|
this._callEvent("load");
|
|
@@ -1381,8 +1399,8 @@ export class WunderbaumNode {
|
|
|
1381
1399
|
// Replace previous classes:
|
|
1382
1400
|
rowDiv.className = rowClasses.join(" ");
|
|
1383
1401
|
|
|
1384
|
-
// Add classes from `node.
|
|
1385
|
-
rowDiv.classList.add(...this.
|
|
1402
|
+
// Add classes from `node.classes`
|
|
1403
|
+
this.classes ? rowDiv.classList.add(...this.classes) : 0;
|
|
1386
1404
|
|
|
1387
1405
|
// Add classes from `tree.types[node.type]`
|
|
1388
1406
|
if (typeInfo && typeInfo.classes) {
|
|
@@ -1823,9 +1841,9 @@ export class WunderbaumNode {
|
|
|
1823
1841
|
* @param {object} [extra]
|
|
1824
1842
|
*/
|
|
1825
1843
|
triggerModify(operation: string, extra?: any) {
|
|
1826
|
-
if (!this.parent) {
|
|
1827
|
-
|
|
1828
|
-
}
|
|
1844
|
+
// if (!this.parent) {
|
|
1845
|
+
// return;
|
|
1846
|
+
// }
|
|
1829
1847
|
this.parent.triggerModifyChild(operation, this, extra);
|
|
1830
1848
|
}
|
|
1831
1849
|
|
package/src/wb_options.ts
CHANGED
|
@@ -23,6 +23,43 @@ export interface WbNodeData {
|
|
|
23
23
|
// ...any?: Any;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
export interface ColumnDefinition {
|
|
27
|
+
/** Column ID (pass "*" for the main tree nodes column ) */
|
|
28
|
+
id: string;
|
|
29
|
+
/** Column header (defaults to id) */
|
|
30
|
+
title: string;
|
|
31
|
+
/** Column width or weight.
|
|
32
|
+
* Either an absolute pixel value (e.g. `"50px"`) or a relative weight (e.g. `1`)
|
|
33
|
+
* that is used to calculate the width inside the remaining available space.
|
|
34
|
+
* Default: `"*"`, which is interpreted as `1`.
|
|
35
|
+
*/
|
|
36
|
+
width?: string | number;
|
|
37
|
+
/** Only used for columns with a relative weight.
|
|
38
|
+
* Default: `4px`.
|
|
39
|
+
*/
|
|
40
|
+
minWidth?: string | number;
|
|
41
|
+
/** Optional class names that are added to all `span.wb-col` elements of that column.*/
|
|
42
|
+
classes?: string;
|
|
43
|
+
/** Optional HTML content that is rendered into all `span.wb-col` elements of that column.*/
|
|
44
|
+
html: string;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface TypeDefinition {
|
|
48
|
+
// /** Type ID that matches `node.type`. */
|
|
49
|
+
// id: string;
|
|
50
|
+
/** En/disable checkbox for matching nodes.*/
|
|
51
|
+
checkbox?: boolean | BoolOptionResolver;
|
|
52
|
+
/** Optional class names that are added to all `div.wb-row` elements of matching nodes.*/
|
|
53
|
+
classes?: string;
|
|
54
|
+
/**Default icon for matching nodes.*/
|
|
55
|
+
icon?: boolean | string | BoolOptionResolver;
|
|
56
|
+
/**
|
|
57
|
+
* See also {@link WunderbaumNode.getOption|WunderbaumNode.getOption()}
|
|
58
|
+
* to evaluate `node.NAME` setting and `tree.types[node.type].NAME`.
|
|
59
|
+
*/
|
|
60
|
+
_any: any;
|
|
61
|
+
}
|
|
62
|
+
|
|
26
63
|
/**
|
|
27
64
|
* Available options for [[Wunderbaum]].
|
|
28
65
|
*
|
|
@@ -79,7 +116,7 @@ export interface WunderbaumOptions {
|
|
|
79
116
|
*
|
|
80
117
|
* Default: `{}`.
|
|
81
118
|
*/
|
|
82
|
-
types?:
|
|
119
|
+
types?: { [key: string]: TypeDefinition };
|
|
83
120
|
/**
|
|
84
121
|
* A list of maps that define column headers. If this option is set,
|
|
85
122
|
* Wunderbaum becomes a treegrid control instead of a plain tree.
|
|
@@ -87,7 +124,7 @@ export interface WunderbaumOptions {
|
|
|
87
124
|
* response.
|
|
88
125
|
* Default: `[]` meaning this is a plain tree.
|
|
89
126
|
*/
|
|
90
|
-
columns?: Array<
|
|
127
|
+
columns?: Array<ColumnDefinition>;
|
|
91
128
|
/**
|
|
92
129
|
* If true, add a `wb-skeleton` class to all nodes, that will result in a
|
|
93
130
|
* 'glow' effect. Typically used with initial dummy nodes, while loading the
|
|
@@ -106,7 +143,7 @@ export interface WunderbaumOptions {
|
|
|
106
143
|
debugLevel: number;
|
|
107
144
|
/**
|
|
108
145
|
* Number of levels that are forced to be expanded, and have no expander icon.
|
|
109
|
-
*
|
|
146
|
+
* Default: 0
|
|
110
147
|
*/
|
|
111
148
|
minExpandLevel?: number;
|
|
112
149
|
// escapeTitles: boolean;
|
|
@@ -125,6 +162,11 @@ export interface WunderbaumOptions {
|
|
|
125
162
|
* Default: false
|
|
126
163
|
*/
|
|
127
164
|
autoCollapse?: boolean;
|
|
165
|
+
/**
|
|
166
|
+
* HTMLElement that receives the top nodes breadcrumb.
|
|
167
|
+
* Default: undefined
|
|
168
|
+
*/
|
|
169
|
+
attachBreadcrumb?: HTMLElement;
|
|
128
170
|
/**
|
|
129
171
|
* Default: NavigationModeOption.startRow
|
|
130
172
|
*/
|
|
@@ -145,17 +187,29 @@ export interface WunderbaumOptions {
|
|
|
145
187
|
* Default: 200
|
|
146
188
|
*/
|
|
147
189
|
updateThrottleWait?: number;
|
|
190
|
+
/**
|
|
191
|
+
* Default: true
|
|
192
|
+
*/
|
|
193
|
+
enabled?: boolean;
|
|
194
|
+
/**
|
|
195
|
+
* Default: false
|
|
196
|
+
*/
|
|
197
|
+
fixedCol?: boolean;
|
|
198
|
+
|
|
148
199
|
// --- KeyNav ---
|
|
149
200
|
/**
|
|
150
201
|
* Default: true
|
|
151
202
|
*/
|
|
152
203
|
quicksearch?: boolean;
|
|
153
204
|
|
|
154
|
-
// --- Extensions
|
|
205
|
+
// --- Extensions ------------------------------------------------------------
|
|
155
206
|
dnd?: DndOptionsType; // = {};
|
|
207
|
+
edit: any; // = {};
|
|
156
208
|
filter: any; // = {};
|
|
157
209
|
grid: any; // = {};
|
|
158
|
-
|
|
210
|
+
|
|
211
|
+
// --- Events ----------------------------------------------------------------
|
|
212
|
+
|
|
159
213
|
/**
|
|
160
214
|
*
|
|
161
215
|
* @category Callback
|