wunderbaum 0.12.0 → 0.13.0
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/dist/wunderbaum.css +10 -1
- package/dist/wunderbaum.css.map +1 -1
- package/dist/wunderbaum.d.ts +214 -59
- package/dist/wunderbaum.esm.js +417 -222
- package/dist/wunderbaum.esm.min.js +42 -42
- package/dist/wunderbaum.esm.min.js.map +1 -1
- package/dist/wunderbaum.umd.js +417 -222
- package/dist/wunderbaum.umd.min.js +45 -45
- package/dist/wunderbaum.umd.min.js.map +1 -1
- package/package.json +30 -28
- package/src/common.ts +58 -16
- package/src/debounce.ts +5 -0
- package/src/deferred.ts +1 -1
- package/src/drag_observer.ts +1 -1
- package/src/types.ts +163 -21
- package/src/util.ts +1 -14
- package/src/wb_ext_dnd.ts +3 -3
- package/src/wb_ext_edit.ts +2 -2
- package/src/wb_ext_filter.ts +119 -44
- package/src/wb_ext_grid.ts +1 -1
- package/src/wb_ext_keynav.ts +1 -1
- package/src/wb_ext_logger.ts +1 -1
- package/src/wb_extension_base.ts +4 -3
- package/src/wb_node.ts +27 -98
- package/src/wb_options.ts +7 -5
- package/src/wunderbaum.scss +12 -4
- package/src/wunderbaum.ts +272 -48
package/src/wb_ext_filter.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/*!
|
|
2
2
|
* Wunderbaum - ext-filter
|
|
3
|
-
* Copyright (c) 2021-
|
|
3
|
+
* Copyright (c) 2021-2025, Martin Wendt. Released under the MIT license.
|
|
4
4
|
* @VERSION, @DATE (https://github.com/mar10/wunderbaum)
|
|
5
5
|
*/
|
|
6
6
|
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
onEvent,
|
|
14
14
|
} from "./util";
|
|
15
15
|
import {
|
|
16
|
+
FilterConnectType,
|
|
16
17
|
FilterNodesOptions,
|
|
17
18
|
FilterOptionsType,
|
|
18
19
|
NodeFilterCallback,
|
|
@@ -29,7 +30,11 @@ const RE_START_MARKER = new RegExp(escapeRegex(START_MARKER), "g");
|
|
|
29
30
|
const RE_END_MARTKER = new RegExp(escapeRegex(END_MARKER), "g");
|
|
30
31
|
|
|
31
32
|
export class FilterExtension extends WunderbaumExtension<FilterOptionsType> {
|
|
32
|
-
public queryInput
|
|
33
|
+
public queryInput: HTMLInputElement | null = null;
|
|
34
|
+
public prevButton: HTMLElement | HTMLAnchorElement | null = null;
|
|
35
|
+
public nextButton: HTMLElement | HTMLAnchorElement | null = null;
|
|
36
|
+
public modeButton: HTMLButtonElement | null = null;
|
|
37
|
+
public matchInfoElem: HTMLElement | null = null;
|
|
33
38
|
public lastFilterArgs: IArguments | null = null;
|
|
34
39
|
|
|
35
40
|
constructor(tree: Wunderbaum) {
|
|
@@ -37,7 +42,7 @@ export class FilterExtension extends WunderbaumExtension<FilterOptionsType> {
|
|
|
37
42
|
autoApply: true, // Re-apply last filter if lazy data is loaded
|
|
38
43
|
autoExpand: false, // Expand all branches that contain matches while filtered
|
|
39
44
|
matchBranch: false, // Whether to implicitly match all children of matched nodes
|
|
40
|
-
|
|
45
|
+
connect: null, // Element or selector of an input control for filter query strings
|
|
41
46
|
fuzzy: false, // Match single characters in order, e.g. 'fb' will match 'FooBar'
|
|
42
47
|
hideExpanders: false, // Hide expanders if all child nodes are hidden by filter
|
|
43
48
|
highlight: true, // Highlight matches by wrapping inside <mark> tags
|
|
@@ -49,35 +54,118 @@ export class FilterExtension extends WunderbaumExtension<FilterOptionsType> {
|
|
|
49
54
|
|
|
50
55
|
init() {
|
|
51
56
|
super.init();
|
|
52
|
-
const
|
|
53
|
-
if (
|
|
54
|
-
this.
|
|
55
|
-
assert(
|
|
56
|
-
this.queryInput,
|
|
57
|
-
`Invalid 'filter.connectInput' option: ${connectInput}.`
|
|
58
|
-
);
|
|
59
|
-
onEvent(
|
|
60
|
-
this.queryInput,
|
|
61
|
-
"input",
|
|
62
|
-
debounce((e) => {
|
|
63
|
-
// this.tree.log("query", e);
|
|
64
|
-
this.filterNodes(this.queryInput!.value.trim(), {});
|
|
65
|
-
}, 700)
|
|
66
|
-
);
|
|
57
|
+
const connect: FilterConnectType = this.getPluginOption("connect");
|
|
58
|
+
if (connect) {
|
|
59
|
+
this._connectControls();
|
|
67
60
|
}
|
|
68
61
|
}
|
|
69
62
|
|
|
70
63
|
setPluginOption(name: string, value: any): void {
|
|
71
|
-
// alert("filter opt=" + name + ", " + value)
|
|
72
64
|
super.setPluginOption(name, value);
|
|
73
65
|
switch (name) {
|
|
74
66
|
case "mode":
|
|
75
|
-
this.tree.filterMode =
|
|
67
|
+
this.tree.filterMode =
|
|
68
|
+
value === "hide" ? "hide" : value === "mark" ? "mark" : "dim";
|
|
76
69
|
this.tree.updateFilter();
|
|
77
70
|
break;
|
|
78
71
|
}
|
|
79
72
|
}
|
|
80
73
|
|
|
74
|
+
_updatedConnectedControls() {
|
|
75
|
+
const filterActive = this.tree.filterMode !== null;
|
|
76
|
+
const activeNode = this.tree.getActiveNode();
|
|
77
|
+
const matchCount = filterActive ? this.countMatches() : 0;
|
|
78
|
+
const strings = this.treeOpts.strings!;
|
|
79
|
+
let matchIdx: string | number = "?";
|
|
80
|
+
|
|
81
|
+
if (this.matchInfoElem) {
|
|
82
|
+
if (filterActive) {
|
|
83
|
+
let info;
|
|
84
|
+
if (matchCount === 0) {
|
|
85
|
+
info = strings.noMatch;
|
|
86
|
+
} else if (activeNode && activeNode.match! >= 1) {
|
|
87
|
+
matchIdx = activeNode.match ?? "?";
|
|
88
|
+
info = strings.matchIndex;
|
|
89
|
+
} else {
|
|
90
|
+
info = strings.queryResult;
|
|
91
|
+
}
|
|
92
|
+
info = info
|
|
93
|
+
.replace("${count}", this.tree.count().toLocaleString())
|
|
94
|
+
.replace("${match}", "" + matchIdx)
|
|
95
|
+
.replace("${matches}", matchCount.toLocaleString());
|
|
96
|
+
this.matchInfoElem.textContent = info;
|
|
97
|
+
} else {
|
|
98
|
+
this.matchInfoElem.textContent = "";
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (this.nextButton instanceof HTMLButtonElement) {
|
|
103
|
+
this.nextButton.disabled = !matchCount;
|
|
104
|
+
}
|
|
105
|
+
if (this.prevButton instanceof HTMLButtonElement) {
|
|
106
|
+
this.prevButton.disabled = !matchCount;
|
|
107
|
+
}
|
|
108
|
+
if (this.modeButton) {
|
|
109
|
+
this.modeButton.disabled = !filterActive;
|
|
110
|
+
this.modeButton.classList.toggle(
|
|
111
|
+
"wb-filter-hide",
|
|
112
|
+
this.tree.filterMode === "hide"
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
_connectControls() {
|
|
117
|
+
const tree = this.tree;
|
|
118
|
+
const connect: FilterConnectType = this.getPluginOption("connect");
|
|
119
|
+
if (!connect) {
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
this.queryInput = elemFromSelector(connect.inputElem);
|
|
123
|
+
if (!this.queryInput) {
|
|
124
|
+
throw new Error(`Invalid 'filter.connect' option: ${connect.inputElem}.`);
|
|
125
|
+
}
|
|
126
|
+
this.prevButton = elemFromSelector(connect.prevButton!);
|
|
127
|
+
this.nextButton = elemFromSelector(connect.nextButton!);
|
|
128
|
+
this.modeButton = elemFromSelector(connect.modeButton!);
|
|
129
|
+
this.matchInfoElem = elemFromSelector(connect.matchInfoElem!);
|
|
130
|
+
if (this.prevButton) {
|
|
131
|
+
onEvent(this.prevButton, "click", () => {
|
|
132
|
+
tree.findRelatedNode(
|
|
133
|
+
tree.getActiveNode() || tree.getFirstChild()!,
|
|
134
|
+
"prevMatch"
|
|
135
|
+
);
|
|
136
|
+
this._updatedConnectedControls();
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
if (this.nextButton) {
|
|
140
|
+
onEvent(this.nextButton, "click", () => {
|
|
141
|
+
tree.findRelatedNode(
|
|
142
|
+
tree.getActiveNode() || tree.getFirstChild()!,
|
|
143
|
+
"nextMatch"
|
|
144
|
+
);
|
|
145
|
+
this._updatedConnectedControls();
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
if (this.modeButton) {
|
|
149
|
+
onEvent(this.modeButton, "click", (e) => {
|
|
150
|
+
if (!this.tree.filterMode) {
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
this.setPluginOption(
|
|
154
|
+
"mode",
|
|
155
|
+
tree.filterMode === "dim" ? "hide" : "dim"
|
|
156
|
+
);
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
onEvent(
|
|
160
|
+
this.queryInput,
|
|
161
|
+
"input",
|
|
162
|
+
debounce((e) => {
|
|
163
|
+
this.filterNodes(this.queryInput!.value.trim(), {});
|
|
164
|
+
}, 700)
|
|
165
|
+
);
|
|
166
|
+
this._updatedConnectedControls();
|
|
167
|
+
}
|
|
168
|
+
|
|
81
169
|
_applyFilterNoUpdate(
|
|
82
170
|
filter: string | RegExp | NodeFilterCallback,
|
|
83
171
|
_opts: FilterNodesOptions
|
|
@@ -98,7 +186,7 @@ export class FilterExtension extends WunderbaumExtension<FilterOptionsType> {
|
|
|
98
186
|
const treeOpts = tree.options;
|
|
99
187
|
const prevAutoCollapse = treeOpts.autoCollapse;
|
|
100
188
|
// Use default options from `tree.options.filter`, but allow to override them
|
|
101
|
-
const opts = extend({}, treeOpts.filter, _opts);
|
|
189
|
+
const opts: FilterOptionsType = extend({}, treeOpts.filter, _opts);
|
|
102
190
|
const hideMode = opts.mode === "hide";
|
|
103
191
|
const matchBranch = !!opts.matchBranch;
|
|
104
192
|
const leavesOnly = !!opts.leavesOnly && !matchBranch;
|
|
@@ -176,12 +264,12 @@ export class FilterExtension extends WunderbaumExtension<FilterOptionsType> {
|
|
|
176
264
|
};
|
|
177
265
|
}
|
|
178
266
|
|
|
179
|
-
tree.filterMode = opts.mode;
|
|
180
|
-
// eslint-disable-next-line prefer-rest-params
|
|
267
|
+
tree.filterMode = opts.mode ?? "dim";
|
|
268
|
+
// eslint-disable-next-line prefer-rest-params
|
|
181
269
|
this.lastFilterArgs = arguments;
|
|
182
270
|
|
|
183
271
|
tree.element.classList.toggle("wb-ext-filter-hide", !!hideMode);
|
|
184
|
-
tree.element.classList.toggle("wb-ext-filter-dim",
|
|
272
|
+
tree.element.classList.toggle("wb-ext-filter-dim", opts.mode === "dim");
|
|
185
273
|
tree.element.classList.toggle(
|
|
186
274
|
"wb-ext-filter-hide-expanders",
|
|
187
275
|
!!opts.hideExpanders
|
|
@@ -193,10 +281,6 @@ export class FilterExtension extends WunderbaumExtension<FilterOptionsType> {
|
|
|
193
281
|
delete node.titleWithHighlight;
|
|
194
282
|
node.subMatchCount = 0;
|
|
195
283
|
});
|
|
196
|
-
// statusNode = tree.root.findDirectChild(KEY_NODATA);
|
|
197
|
-
// if (statusNode) {
|
|
198
|
-
// statusNode.remove();
|
|
199
|
-
// }
|
|
200
284
|
tree.setStatus(NodeStatusType.ok);
|
|
201
285
|
|
|
202
286
|
// Adjust node.hide, .match, and .subMatchCount properties
|
|
@@ -210,7 +294,7 @@ export class FilterExtension extends WunderbaumExtension<FilterOptionsType> {
|
|
|
210
294
|
|
|
211
295
|
if (res === "skip") {
|
|
212
296
|
node.visit(function (c) {
|
|
213
|
-
c.match =
|
|
297
|
+
c.match = undefined;
|
|
214
298
|
}, true);
|
|
215
299
|
return "skip";
|
|
216
300
|
}
|
|
@@ -223,7 +307,7 @@ export class FilterExtension extends WunderbaumExtension<FilterOptionsType> {
|
|
|
223
307
|
|
|
224
308
|
if (res) {
|
|
225
309
|
count++;
|
|
226
|
-
node.match =
|
|
310
|
+
node.match = count;
|
|
227
311
|
node.visitParents((p) => {
|
|
228
312
|
if (p !== node) {
|
|
229
313
|
p.subMatchCount! += 1;
|
|
@@ -252,6 +336,8 @@ export class FilterExtension extends WunderbaumExtension<FilterOptionsType> {
|
|
|
252
336
|
tree.logDebug(
|
|
253
337
|
`Filter '${filter}' found ${count} nodes in ${Date.now() - start} ms.`
|
|
254
338
|
);
|
|
339
|
+
this._updatedConnectedControls();
|
|
340
|
+
|
|
255
341
|
return count;
|
|
256
342
|
}
|
|
257
343
|
|
|
@@ -309,6 +395,7 @@ export class FilterExtension extends WunderbaumExtension<FilterOptionsType> {
|
|
|
309
395
|
} else {
|
|
310
396
|
tree.logWarn("updateFilter(): no filter active.");
|
|
311
397
|
}
|
|
398
|
+
this._updatedConnectedControls();
|
|
312
399
|
}
|
|
313
400
|
|
|
314
401
|
/**
|
|
@@ -316,30 +403,17 @@ export class FilterExtension extends WunderbaumExtension<FilterOptionsType> {
|
|
|
316
403
|
*/
|
|
317
404
|
clearFilter() {
|
|
318
405
|
const tree = this.tree;
|
|
319
|
-
// statusNode = tree.root.findDirectChild(KEY_NODATA),
|
|
320
|
-
// escapeTitles = tree.options.escapeTitles;
|
|
321
406
|
tree.enableUpdate(false);
|
|
322
407
|
|
|
323
|
-
// if (statusNode) {
|
|
324
|
-
// statusNode.remove();
|
|
325
|
-
// }
|
|
326
408
|
tree.setStatus(NodeStatusType.ok);
|
|
327
409
|
// we also counted root node's subMatchCount
|
|
328
410
|
delete tree.root.match;
|
|
329
411
|
delete tree.root.subMatchCount;
|
|
330
412
|
|
|
331
413
|
tree.visit((node) => {
|
|
332
|
-
// if (node.match && node._rowElem) {
|
|
333
|
-
// let titleElem = node._rowElem.querySelector("span.wb-title")!;
|
|
334
|
-
// node._callEvent("enhanceTitle", { titleElem: titleElem });
|
|
335
|
-
// }
|
|
336
414
|
delete node.match;
|
|
337
415
|
delete node.subMatchCount;
|
|
338
416
|
delete node.titleWithHighlight;
|
|
339
|
-
// if (node.subMatchBadge) {
|
|
340
|
-
// node.subMatchBadge.remove();
|
|
341
|
-
// delete node.subMatchBadge;
|
|
342
|
-
// }
|
|
343
417
|
if (node._filterAutoExpanded && node.expanded) {
|
|
344
418
|
node.setExpanded(false, {
|
|
345
419
|
noAnimation: true,
|
|
@@ -355,7 +429,8 @@ export class FilterExtension extends WunderbaumExtension<FilterOptionsType> {
|
|
|
355
429
|
"wb-ext-filter-dim",
|
|
356
430
|
"wb-ext-filter-hide"
|
|
357
431
|
);
|
|
358
|
-
|
|
432
|
+
this._updatedConnectedControls();
|
|
433
|
+
|
|
359
434
|
tree.enableUpdate(true);
|
|
360
435
|
}
|
|
361
436
|
}
|
package/src/wb_ext_grid.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/*!
|
|
2
2
|
* Wunderbaum - ext-grid
|
|
3
|
-
* Copyright (c) 2021-
|
|
3
|
+
* Copyright (c) 2021-2025, Martin Wendt. Released under the MIT license.
|
|
4
4
|
* @VERSION, @DATE (https://github.com/mar10/wunderbaum)
|
|
5
5
|
*/
|
|
6
6
|
import { Wunderbaum } from "./wunderbaum";
|
package/src/wb_ext_keynav.ts
CHANGED
package/src/wb_ext_logger.ts
CHANGED
package/src/wb_extension_base.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
/*!
|
|
2
2
|
* Wunderbaum - wb_extension_base
|
|
3
|
-
* Copyright (c) 2021-
|
|
3
|
+
* Copyright (c) 2021-2025, Martin Wendt. Released under the MIT license.
|
|
4
4
|
* @VERSION, @DATE (https://github.com/mar10/wunderbaum)
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import * as util from "./util";
|
|
8
|
+
import { WunderbaumOptions } from "./wb_options";
|
|
8
9
|
import { Wunderbaum } from "./wunderbaum";
|
|
9
10
|
|
|
10
11
|
export type ExtensionsDict = { [key: string]: WunderbaumExtension<any> };
|
|
@@ -13,7 +14,7 @@ export abstract class WunderbaumExtension<TOptions> {
|
|
|
13
14
|
public enabled = true;
|
|
14
15
|
readonly id: string;
|
|
15
16
|
readonly tree: Wunderbaum;
|
|
16
|
-
readonly treeOpts:
|
|
17
|
+
readonly treeOpts: WunderbaumOptions;
|
|
17
18
|
readonly extensionOpts: any;
|
|
18
19
|
|
|
19
20
|
constructor(tree: Wunderbaum, id: string, defaults: TOptions) {
|
|
@@ -23,7 +24,7 @@ export abstract class WunderbaumExtension<TOptions> {
|
|
|
23
24
|
|
|
24
25
|
const opts = tree.options as any;
|
|
25
26
|
|
|
26
|
-
if (this.treeOpts[id] === undefined) {
|
|
27
|
+
if ((<any>this.treeOpts)[id] === undefined) {
|
|
27
28
|
opts[id] = this.extensionOpts = util.extend({}, defaults);
|
|
28
29
|
} else {
|
|
29
30
|
// TODO: do we break existing object instance references here?
|
package/src/wb_node.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/*!
|
|
2
2
|
* Wunderbaum - wunderbaum_node
|
|
3
|
-
* Copyright (c) 2021-
|
|
3
|
+
* Copyright (c) 2021-2025, Martin Wendt. Released under the MIT license.
|
|
4
4
|
* @VERSION, @DATE (https://github.com/mar10/wunderbaum)
|
|
5
5
|
*/
|
|
6
6
|
|
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
MakeVisibleOptions,
|
|
22
22
|
MatcherCallback,
|
|
23
23
|
NavigateOptions,
|
|
24
|
+
NavigationType,
|
|
24
25
|
NodeAnyCallback,
|
|
25
26
|
NodeStatusType,
|
|
26
27
|
NodeStringCallback,
|
|
@@ -46,7 +47,7 @@ import {
|
|
|
46
47
|
import {
|
|
47
48
|
decompressSourceData,
|
|
48
49
|
ICON_WIDTH,
|
|
49
|
-
|
|
50
|
+
KEY_TO_NAVIGATION_MAP,
|
|
50
51
|
makeNodeTitleMatcher,
|
|
51
52
|
nodeTitleSorter,
|
|
52
53
|
RESERVED_TREE_SOURCE_KEYS,
|
|
@@ -171,7 +172,11 @@ export class WunderbaumNode {
|
|
|
171
172
|
_partsel = false;
|
|
172
173
|
_partload = false;
|
|
173
174
|
// --- FILTER ---
|
|
174
|
-
|
|
175
|
+
/**
|
|
176
|
+
* > 0 if matched (-1 to keep system nodes visible);
|
|
177
|
+
* Added and removed by filter code.
|
|
178
|
+
*/
|
|
179
|
+
public match?: number;
|
|
175
180
|
public subMatchCount?: number = 0;
|
|
176
181
|
// public subMatchBadge?: HTMLElement;
|
|
177
182
|
/** @internal */
|
|
@@ -656,7 +661,7 @@ export class WunderbaumNode {
|
|
|
656
661
|
*
|
|
657
662
|
* @see {@link Wunderbaum.findRelatedNode|tree.findRelatedNode()}
|
|
658
663
|
*/
|
|
659
|
-
findRelatedNode(where:
|
|
664
|
+
findRelatedNode(where: NavigationType, includeHidden = false) {
|
|
660
665
|
return this.tree.findRelatedNode(this, where, includeHidden);
|
|
661
666
|
}
|
|
662
667
|
|
|
@@ -987,7 +992,7 @@ export class WunderbaumNode {
|
|
|
987
992
|
return other && other.parent === this;
|
|
988
993
|
}
|
|
989
994
|
|
|
990
|
-
/**
|
|
995
|
+
/** Return true if this node is partially loaded. @experimental */
|
|
991
996
|
isPartload(): boolean {
|
|
992
997
|
return !!this._partload;
|
|
993
998
|
}
|
|
@@ -1509,12 +1514,12 @@ export class WunderbaumNode {
|
|
|
1509
1514
|
* e.g. `ArrowLeft` = 'left'.
|
|
1510
1515
|
* @param options
|
|
1511
1516
|
*/
|
|
1512
|
-
async navigate(where: string, options?: NavigateOptions) {
|
|
1517
|
+
async navigate(where: NavigationType | string, options?: NavigateOptions) {
|
|
1513
1518
|
// Allow to pass 'ArrowLeft' instead of 'left'
|
|
1514
|
-
|
|
1519
|
+
const navType = (KEY_TO_NAVIGATION_MAP[where] ?? where) as NavigationType;
|
|
1515
1520
|
|
|
1516
1521
|
// Otherwise activate or focus the related node
|
|
1517
|
-
const node = this.findRelatedNode(
|
|
1522
|
+
const node = this.findRelatedNode(navType);
|
|
1518
1523
|
if (!node) {
|
|
1519
1524
|
this.logWarn(`Could not find related node '${where}'.`);
|
|
1520
1525
|
return Promise.resolve(this);
|
|
@@ -1618,91 +1623,19 @@ export class WunderbaumNode {
|
|
|
1618
1623
|
}
|
|
1619
1624
|
|
|
1620
1625
|
protected _createIcon(
|
|
1621
|
-
iconMap: any,
|
|
1622
1626
|
parentElem: HTMLElement,
|
|
1623
1627
|
replaceChild: HTMLElement | null,
|
|
1624
1628
|
showLoading: boolean
|
|
1625
1629
|
): HTMLElement | null {
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
} else if (this._isLoading && showLoading) {
|
|
1631
|
-
// Status nodes, or nodes without expander (< minExpandLevel) should
|
|
1632
|
-
// display the 'loading' status with the i.wb-icon span
|
|
1633
|
-
icon = iconMap.loading;
|
|
1634
|
-
}
|
|
1635
|
-
if (icon === false) {
|
|
1636
|
-
return null; // explicitly disabled: don't try default icons
|
|
1637
|
-
}
|
|
1638
|
-
if (typeof icon === "string") {
|
|
1639
|
-
// Callback returned an icon definition
|
|
1640
|
-
// icon = icon.trim()
|
|
1641
|
-
} else if (this.statusNodeType) {
|
|
1642
|
-
icon = (<any>iconMap)[this.statusNodeType];
|
|
1643
|
-
} else if (this.expanded) {
|
|
1644
|
-
icon = iconMap.folderOpen;
|
|
1645
|
-
} else if (this.children) {
|
|
1646
|
-
icon = iconMap.folder;
|
|
1647
|
-
} else if (this.lazy) {
|
|
1648
|
-
icon = iconMap.folderLazy;
|
|
1649
|
-
} else {
|
|
1650
|
-
icon = iconMap.doc;
|
|
1651
|
-
}
|
|
1652
|
-
|
|
1653
|
-
// this.log("_createIcon: " + icon);
|
|
1654
|
-
if (!icon) {
|
|
1655
|
-
iconSpan = document.createElement("i");
|
|
1656
|
-
iconSpan.className = "wb-icon";
|
|
1657
|
-
} else if (icon.indexOf("<") >= 0) {
|
|
1658
|
-
// HTML
|
|
1659
|
-
iconSpan = util.elemFromHtml(icon);
|
|
1660
|
-
} else if (TEST_IMG.test(icon)) {
|
|
1661
|
-
// Image URL
|
|
1662
|
-
iconSpan = util.elemFromHtml(
|
|
1663
|
-
`<i class="wb-icon" style="background-image: url('${icon}');">`
|
|
1664
|
-
);
|
|
1665
|
-
} else {
|
|
1666
|
-
// Class name
|
|
1667
|
-
iconSpan = document.createElement("i");
|
|
1668
|
-
iconSpan.className = "wb-icon " + icon;
|
|
1669
|
-
}
|
|
1670
|
-
if (replaceChild) {
|
|
1671
|
-
parentElem.replaceChild(iconSpan, replaceChild);
|
|
1672
|
-
} else {
|
|
1673
|
-
parentElem.appendChild(iconSpan);
|
|
1674
|
-
}
|
|
1675
|
-
|
|
1676
|
-
// Event handler `tree.iconBadge` can return a badge text or HTMLSpanElement
|
|
1677
|
-
|
|
1678
|
-
const cbRes = this._callEvent("iconBadge", { iconSpan: iconSpan });
|
|
1679
|
-
let badge = null;
|
|
1680
|
-
if (cbRes != null && cbRes !== false) {
|
|
1681
|
-
let classes = "";
|
|
1682
|
-
let tooltip = "";
|
|
1683
|
-
if (util.isPlainObject(cbRes)) {
|
|
1684
|
-
badge = "" + cbRes.badge;
|
|
1685
|
-
classes = cbRes.badgeClass ? " " + cbRes.badgeClass : "";
|
|
1686
|
-
tooltip = cbRes.badgeTooltip ? ` title="${cbRes.badgeTooltip}"` : "";
|
|
1687
|
-
} else if (typeof cbRes === "number") {
|
|
1688
|
-
badge = "" + cbRes;
|
|
1630
|
+
const iconElem = this.tree._createNodeIcon(this, showLoading, true);
|
|
1631
|
+
if (iconElem) {
|
|
1632
|
+
if (replaceChild) {
|
|
1633
|
+
parentElem.replaceChild(iconElem, replaceChild);
|
|
1689
1634
|
} else {
|
|
1690
|
-
|
|
1691
|
-
}
|
|
1692
|
-
if (typeof badge === "string") {
|
|
1693
|
-
badge = util.elemFromHtml(
|
|
1694
|
-
`<span class="wb-badge${classes}"${tooltip}>${util.escapeHtml(
|
|
1695
|
-
badge
|
|
1696
|
-
)}</span>`
|
|
1697
|
-
);
|
|
1698
|
-
}
|
|
1699
|
-
if (badge) {
|
|
1700
|
-
iconSpan.append(<HTMLSpanElement>badge);
|
|
1635
|
+
parentElem.appendChild(iconElem);
|
|
1701
1636
|
}
|
|
1702
1637
|
}
|
|
1703
|
-
|
|
1704
|
-
// this.log("_createIcon: ", iconSpan);
|
|
1705
|
-
return iconSpan;
|
|
1638
|
+
return iconElem;
|
|
1706
1639
|
}
|
|
1707
1640
|
|
|
1708
1641
|
/**
|
|
@@ -1773,12 +1706,7 @@ export class WunderbaumNode {
|
|
|
1773
1706
|
|
|
1774
1707
|
// Render the icon (show a 'loading' icon if we do not have an expander that
|
|
1775
1708
|
// we would prefer).
|
|
1776
|
-
const iconSpan = this._createIcon(
|
|
1777
|
-
tree.iconMap,
|
|
1778
|
-
nodeElem,
|
|
1779
|
-
null,
|
|
1780
|
-
!expanderSpan
|
|
1781
|
-
);
|
|
1709
|
+
const iconSpan = this._createIcon(nodeElem, null, !expanderSpan);
|
|
1782
1710
|
if (iconSpan) {
|
|
1783
1711
|
ofsTitlePx += ICON_WIDTH;
|
|
1784
1712
|
}
|
|
@@ -2029,7 +1957,7 @@ export class WunderbaumNode {
|
|
|
2029
1957
|
// Update icon (if not opts.isNew, which would rebuild markup anyway)
|
|
2030
1958
|
const iconSpan = nodeElem.querySelector("i.wb-icon") as HTMLElement;
|
|
2031
1959
|
if (iconSpan) {
|
|
2032
|
-
this._createIcon(
|
|
1960
|
+
this._createIcon(nodeElem, iconSpan, !expanderSpan);
|
|
2033
1961
|
}
|
|
2034
1962
|
}
|
|
2035
1963
|
// Adjust column width
|
|
@@ -2437,7 +2365,8 @@ export class WunderbaumNode {
|
|
|
2437
2365
|
case undefined:
|
|
2438
2366
|
changed = this.selected || !this._partsel;
|
|
2439
2367
|
this.selected = false;
|
|
2440
|
-
|
|
2368
|
+
// #110: end nodess cannot have a `_partsel` flag
|
|
2369
|
+
this._partsel = this.hasChildren() ? true : false;
|
|
2441
2370
|
break;
|
|
2442
2371
|
default:
|
|
2443
2372
|
util.error(`Invalid state: ${state}`);
|
|
@@ -2640,7 +2569,7 @@ export class WunderbaumNode {
|
|
|
2640
2569
|
);
|
|
2641
2570
|
|
|
2642
2571
|
statusNode = this.addNode(data, "prependChild");
|
|
2643
|
-
statusNode.match =
|
|
2572
|
+
statusNode.match = -1; // Mark as 'match' to avoid hiding
|
|
2644
2573
|
tree.update(ChangeType.structure);
|
|
2645
2574
|
|
|
2646
2575
|
return statusNode;
|
|
@@ -2663,7 +2592,7 @@ export class WunderbaumNode {
|
|
|
2663
2592
|
_setStatusNode({
|
|
2664
2593
|
statusNodeType: status,
|
|
2665
2594
|
title:
|
|
2666
|
-
tree.options.strings
|
|
2595
|
+
tree.options.strings!.loading +
|
|
2667
2596
|
(message ? " (" + message + ")" : ""),
|
|
2668
2597
|
checkbox: false,
|
|
2669
2598
|
colspan: true,
|
|
@@ -2676,7 +2605,7 @@ export class WunderbaumNode {
|
|
|
2676
2605
|
_setStatusNode({
|
|
2677
2606
|
statusNodeType: status,
|
|
2678
2607
|
title:
|
|
2679
|
-
tree.options.strings
|
|
2608
|
+
tree.options.strings!.loadError +
|
|
2680
2609
|
(message ? " (" + message + ")" : ""),
|
|
2681
2610
|
checkbox: false,
|
|
2682
2611
|
colspan: true,
|
|
@@ -2689,7 +2618,7 @@ export class WunderbaumNode {
|
|
|
2689
2618
|
case "noData":
|
|
2690
2619
|
_setStatusNode({
|
|
2691
2620
|
statusNodeType: status,
|
|
2692
|
-
title: message || tree.options.strings
|
|
2621
|
+
title: message || tree.options.strings!.noData,
|
|
2693
2622
|
checkbox: false,
|
|
2694
2623
|
colspan: true,
|
|
2695
2624
|
tooltip: details,
|
package/src/wb_options.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/*!
|
|
2
2
|
* Wunderbaum - utils
|
|
3
|
-
* Copyright (c) 2021-
|
|
3
|
+
* Copyright (c) 2021-2025, Martin Wendt. Released under the MIT license.
|
|
4
4
|
* @VERSION, @DATE (https://github.com/mar10/wunderbaum)
|
|
5
5
|
*/
|
|
6
6
|
|
|
@@ -13,12 +13,14 @@ import {
|
|
|
13
13
|
DynamicIconOption,
|
|
14
14
|
EditOptionsType,
|
|
15
15
|
FilterOptionsType,
|
|
16
|
+
IconMapType,
|
|
16
17
|
// GridOptionsType,
|
|
17
18
|
// KeynavOptionsType,
|
|
18
19
|
// LoggerOptionsType,
|
|
19
20
|
NavModeEnum,
|
|
20
21
|
NodeTypeDefinitionMap,
|
|
21
22
|
SelectModeType,
|
|
23
|
+
TranslationsType,
|
|
22
24
|
WbActivateEventType,
|
|
23
25
|
WbButtonClickEventType,
|
|
24
26
|
WbCancelableEventResultType,
|
|
@@ -114,7 +116,7 @@ export interface WunderbaumOptions {
|
|
|
114
116
|
/**
|
|
115
117
|
* Translation map for some system messages.
|
|
116
118
|
*/
|
|
117
|
-
strings?:
|
|
119
|
+
strings?: TranslationsType;
|
|
118
120
|
/**
|
|
119
121
|
* 0:quiet, 1:errors, 2:warnings, 3:info, 4:verbose
|
|
120
122
|
* Default: 3 (4 in local debug environment)
|
|
@@ -149,7 +151,7 @@ export interface WunderbaumOptions {
|
|
|
149
151
|
* Note: the icon font must be loaded separately.
|
|
150
152
|
* Default: "bootstrap"
|
|
151
153
|
*/
|
|
152
|
-
iconMap?: string |
|
|
154
|
+
iconMap?: string | IconMapType;
|
|
153
155
|
/**
|
|
154
156
|
* Collapse siblings when a node is expanded.
|
|
155
157
|
* Default: false
|
|
@@ -170,10 +172,10 @@ export interface WunderbaumOptions {
|
|
|
170
172
|
*/
|
|
171
173
|
adjustHeight?: boolean;
|
|
172
174
|
/**
|
|
173
|
-
* HTMLElement that receives the top nodes breadcrumb.
|
|
175
|
+
* HTMLElement or selector that receives the top nodes breadcrumb.
|
|
174
176
|
* Default: undefined
|
|
175
177
|
*/
|
|
176
|
-
connectTopBreadcrumb?: HTMLElement;
|
|
178
|
+
connectTopBreadcrumb?: HTMLElement | string;
|
|
177
179
|
/**
|
|
178
180
|
* Default: NavModeEnum.startRow
|
|
179
181
|
*/
|
package/src/wunderbaum.scss
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/*!
|
|
2
2
|
* Wunderbaum style sheet (generated from wunderbaum.scss)
|
|
3
|
-
* Copyright (c) 2021-
|
|
3
|
+
* Copyright (c) 2021-2025, Martin Wendt. Released under the MIT license.
|
|
4
4
|
* @VERSION, @DATE (https://github.com/mar10/wunderbaum)
|
|
5
5
|
*/
|
|
6
6
|
// @use "sass:meta";
|
|
@@ -69,8 +69,8 @@ $header-height: $row-outer-height;
|
|
|
69
69
|
// $level-rainbow: rgba(255, 255, 64, 0.07), rgba(127, 255, 127, 0.07),
|
|
70
70
|
// rgba(255, 127, 255, 0.07), rgba(79, 236, 236, 0.07);
|
|
71
71
|
// Slightly stronger*
|
|
72
|
-
$level-rainbow:
|
|
73
|
-
rgb(204, 250, 250);
|
|
72
|
+
$level-rainbow:
|
|
73
|
+
rgb(255, 255, 201), rgb(218, 255, 218), rgb(255, 217, 254), rgb(204, 250, 250);
|
|
74
74
|
|
|
75
75
|
// ----------------------------------------------------------------------------
|
|
76
76
|
// --- Define CSS variables with calculated default values
|
|
@@ -759,6 +759,10 @@ div.wunderbaum {
|
|
|
759
759
|
}
|
|
760
760
|
|
|
761
761
|
/* --- TOOL CLASSES --- */
|
|
762
|
+
a.wb-breadcrumb {
|
|
763
|
+
cursor: pointer;
|
|
764
|
+
text-decoration: none;
|
|
765
|
+
}
|
|
762
766
|
|
|
763
767
|
.wb-helper-center {
|
|
764
768
|
text-align: center;
|
|
@@ -797,7 +801,11 @@ div.wunderbaum {
|
|
|
797
801
|
// .wb-helper-edit-text {
|
|
798
802
|
// // content-editable: true;
|
|
799
803
|
// }
|
|
800
|
-
|
|
804
|
+
button.wb-filter-hide {
|
|
805
|
+
font-weight: bolder;
|
|
806
|
+
// background-color: var(--wb-active-color);
|
|
807
|
+
// -webkit-appearance: auto; /* Removes default Safari styles */
|
|
808
|
+
}
|
|
801
809
|
/* RTL support */
|
|
802
810
|
.wb-helper-start,
|
|
803
811
|
.wb-helper-start > input {
|