wunderbaum 0.12.1 → 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 +9 -0
- package/dist/wunderbaum.css.map +1 -1
- package/dist/wunderbaum.d.ts +147 -44
- package/dist/wunderbaum.esm.js +353 -187
- package/dist/wunderbaum.esm.min.js +26 -26
- package/dist/wunderbaum.esm.min.js.map +1 -1
- package/dist/wunderbaum.umd.js +353 -187
- package/dist/wunderbaum.umd.min.js +29 -29
- package/dist/wunderbaum.umd.min.js.map +1 -1
- package/package.json +1 -1
- package/src/common.ts +27 -3
- package/src/types.ts +115 -12
- package/src/util.ts +0 -13
- package/src/wb_ext_edit.ts +1 -1
- package/src/wb_ext_filter.ts +117 -42
- package/src/wb_extension_base.ts +3 -2
- package/src/wb_node.ts +24 -96
- package/src/wb_options.ts +6 -12
- package/src/wunderbaum.scss +9 -1
- package/src/wunderbaum.ts +264 -40
package/package.json
CHANGED
package/src/common.ts
CHANGED
|
@@ -4,7 +4,14 @@
|
|
|
4
4
|
* @VERSION, @DATE (https://github.com/mar10/wunderbaum)
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
ApplyCommandType,
|
|
9
|
+
NavigationType,
|
|
10
|
+
SourceListType,
|
|
11
|
+
SourceObjectType,
|
|
12
|
+
IconMapType,
|
|
13
|
+
MatcherCallback,
|
|
14
|
+
} from "./types";
|
|
8
15
|
import * as util from "./util";
|
|
9
16
|
import { WunderbaumNode } from "./wb_node";
|
|
10
17
|
|
|
@@ -42,7 +49,7 @@ export const TEST_IMG = new RegExp(/\.|\//);
|
|
|
42
49
|
* - 'fontawesome6' {@link https://fontawesome.com/icons}
|
|
43
50
|
*
|
|
44
51
|
*/
|
|
45
|
-
export const iconMaps: { [key: string]:
|
|
52
|
+
export const iconMaps: { [key: string]: IconMapType } = {
|
|
46
53
|
bootstrap: {
|
|
47
54
|
error: "bi bi-exclamation-triangle",
|
|
48
55
|
// loading: "bi bi-hourglass-split wb-busy",
|
|
@@ -140,7 +147,24 @@ export const RESERVED_TREE_SOURCE_KEYS: Set<string> = new Set([
|
|
|
140
147
|
// ]);
|
|
141
148
|
|
|
142
149
|
/** Map `KeyEvent.key` to navigation action. */
|
|
143
|
-
export const
|
|
150
|
+
export const KEY_TO_NAVIGATION_MAP: { [key: string]: NavigationType } = {
|
|
151
|
+
ArrowDown: "down",
|
|
152
|
+
ArrowLeft: "left",
|
|
153
|
+
ArrowRight: "right",
|
|
154
|
+
ArrowUp: "up",
|
|
155
|
+
Backspace: "parent",
|
|
156
|
+
End: "lastCol",
|
|
157
|
+
Home: "firstCol",
|
|
158
|
+
"Control+End": "last",
|
|
159
|
+
"Control+Home": "first",
|
|
160
|
+
"Meta+ArrowDown": "last", // macOs
|
|
161
|
+
"Meta+ArrowUp": "first", // macOs
|
|
162
|
+
PageDown: "pageDown",
|
|
163
|
+
PageUp: "pageUp",
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
/** Map `KeyEvent.key` to navigation action. */
|
|
167
|
+
export const KEY_TO_COMMAND_MAP: { [key: string]: ApplyCommandType } = {
|
|
144
168
|
" ": "toggleSelect",
|
|
145
169
|
"+": "expand",
|
|
146
170
|
Add: "expand",
|
package/src/types.ts
CHANGED
|
@@ -173,6 +173,32 @@ export interface WbNodeData {
|
|
|
173
173
|
[key: string]: unknown;
|
|
174
174
|
}
|
|
175
175
|
|
|
176
|
+
/** A plain object (dictionary) that defines node icons. */
|
|
177
|
+
export interface IconMapType {
|
|
178
|
+
error: string;
|
|
179
|
+
loading: string;
|
|
180
|
+
noData: string;
|
|
181
|
+
expanderExpanded: string;
|
|
182
|
+
expanderCollapsed: string;
|
|
183
|
+
expanderLazy: string;
|
|
184
|
+
checkChecked: string;
|
|
185
|
+
checkUnchecked: string;
|
|
186
|
+
checkUnknown: string;
|
|
187
|
+
radioChecked: string;
|
|
188
|
+
radioUnchecked: string;
|
|
189
|
+
radioUnknown: string;
|
|
190
|
+
folder: string;
|
|
191
|
+
folderOpen: string;
|
|
192
|
+
folderLazy: string;
|
|
193
|
+
doc: string;
|
|
194
|
+
colSortable: string;
|
|
195
|
+
colSortAsc: string;
|
|
196
|
+
colSortDesc: string;
|
|
197
|
+
colFilter: string;
|
|
198
|
+
colFilterActive: string;
|
|
199
|
+
colMenu: string;
|
|
200
|
+
[key: string]: string;
|
|
201
|
+
}
|
|
176
202
|
/* -----------------------------------------------------------------------------
|
|
177
203
|
* EVENT CALLBACK TYPES
|
|
178
204
|
* ---------------------------------------------------------------------------*/
|
|
@@ -471,6 +497,21 @@ export interface ColumnDefinition {
|
|
|
471
497
|
|
|
472
498
|
export type ColumnDefinitionList = Array<ColumnDefinition>;
|
|
473
499
|
|
|
500
|
+
/**
|
|
501
|
+
* Used by {@link Wunderbaum.getState} and {@link Wunderbaum.setState}.
|
|
502
|
+
*/
|
|
503
|
+
export interface TreeStateDefinition {
|
|
504
|
+
/** The active node's key if any. */
|
|
505
|
+
activeKey: string | null;
|
|
506
|
+
/** The active column index if any. */
|
|
507
|
+
activeColIdx: number | null;
|
|
508
|
+
/** List of selected node's keys. */
|
|
509
|
+
selectedKeys: Array<string> | undefined;
|
|
510
|
+
/** List of expanded node's keys. */
|
|
511
|
+
expandedKeys: Array<string> | undefined;
|
|
512
|
+
/** List of checked node's keys. */
|
|
513
|
+
}
|
|
514
|
+
|
|
474
515
|
/**
|
|
475
516
|
* Column information (passed to the `render` event).
|
|
476
517
|
*/
|
|
@@ -518,29 +559,43 @@ export interface WbEventInfo {
|
|
|
518
559
|
// export type WbNodeCallbackType = (e: WbNodeEventType) => any;
|
|
519
560
|
// export type WbRenderCallbackType = (e: WbRenderEventType) => void;
|
|
520
561
|
|
|
521
|
-
export type FilterModeType = null | "dim" | "hide";
|
|
562
|
+
export type FilterModeType = null | "mark" | "dim" | "hide";
|
|
522
563
|
export type SelectModeType = "single" | "multi" | "hier";
|
|
564
|
+
|
|
565
|
+
export type NavigationType =
|
|
566
|
+
| "down"
|
|
567
|
+
| "first"
|
|
568
|
+
| "firstCol"
|
|
569
|
+
| "last"
|
|
570
|
+
| "lastCol"
|
|
571
|
+
| "left"
|
|
572
|
+
| "nextMatch"
|
|
573
|
+
| "pageDown"
|
|
574
|
+
| "pageUp"
|
|
575
|
+
| "parent"
|
|
576
|
+
| "prevMatch"
|
|
577
|
+
| "right"
|
|
578
|
+
| "up";
|
|
579
|
+
|
|
523
580
|
export type ApplyCommandType =
|
|
581
|
+
| NavigationType
|
|
524
582
|
| "addChild"
|
|
525
583
|
| "addSibling"
|
|
584
|
+
| "collapse"
|
|
585
|
+
| "collapseAll"
|
|
526
586
|
| "copy"
|
|
527
587
|
| "cut"
|
|
528
|
-
| "
|
|
529
|
-
| "
|
|
588
|
+
| "edit"
|
|
589
|
+
| "expand"
|
|
590
|
+
| "expandAll"
|
|
530
591
|
| "indent"
|
|
531
|
-
| "last"
|
|
532
|
-
| "left"
|
|
533
592
|
| "moveDown"
|
|
534
593
|
| "moveUp"
|
|
535
594
|
| "outdent"
|
|
536
|
-
| "pageDown"
|
|
537
|
-
| "pageUp"
|
|
538
|
-
| "parent"
|
|
539
595
|
| "paste"
|
|
540
596
|
| "remove"
|
|
541
597
|
| "rename"
|
|
542
|
-
| "
|
|
543
|
-
| "up";
|
|
598
|
+
| "toggleSelect";
|
|
544
599
|
|
|
545
600
|
export type NodeFilterResponse = "skip" | "branch" | boolean | void;
|
|
546
601
|
export type NodeFilterCallback = (node: WunderbaumNode) => NodeFilterResponse;
|
|
@@ -607,6 +662,23 @@ export enum NavModeEnum {
|
|
|
607
662
|
row = "row",
|
|
608
663
|
}
|
|
609
664
|
|
|
665
|
+
/** Translatable strings. */
|
|
666
|
+
export type TranslationsType = {
|
|
667
|
+
/** @default "Loading..." */
|
|
668
|
+
loading: string;
|
|
669
|
+
/** @default "Error" */
|
|
670
|
+
loadError: string;
|
|
671
|
+
/** @default "No data" */
|
|
672
|
+
noData: string;
|
|
673
|
+
/** @default " » " */
|
|
674
|
+
breadcrumbDelimiter: string;
|
|
675
|
+
/** @default "Found ${matches} of ${count}" */
|
|
676
|
+
queryResult: string;
|
|
677
|
+
/** @default "No result" */
|
|
678
|
+
noMatch: string;
|
|
679
|
+
/** @default "${match} of ${matches}" */
|
|
680
|
+
matchIndex: string;
|
|
681
|
+
};
|
|
610
682
|
/* -----------------------------------------------------------------------------
|
|
611
683
|
* METHOD OPTIONS TYPES
|
|
612
684
|
* ---------------------------------------------------------------------------*/
|
|
@@ -693,6 +765,22 @@ export interface FilterNodesOptions {
|
|
|
693
765
|
noData?: boolean | string;
|
|
694
766
|
}
|
|
695
767
|
|
|
768
|
+
/** Possible values for {@link Wunderbaum.getState}. */
|
|
769
|
+
export interface GetStateOptions {
|
|
770
|
+
// /** Include the activated key. @default true */
|
|
771
|
+
// activeKey?: boolean;
|
|
772
|
+
/** Include the expanded keys. @default true */
|
|
773
|
+
expandedKeys?: boolean;
|
|
774
|
+
/** Include the selected keys. @default true */
|
|
775
|
+
selectedKeys?: boolean;
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
/** Possible values for {@link Wunderbaum.setState}. */
|
|
779
|
+
export interface SetStateOptions {
|
|
780
|
+
/** Recursively load lazy nodes as needed. @default false */
|
|
781
|
+
expandLazy?: boolean;
|
|
782
|
+
}
|
|
783
|
+
|
|
696
784
|
/** Possible values for {@link WunderbaumNode.makeVisible}. */
|
|
697
785
|
export interface MakeVisibleOptions {
|
|
698
786
|
/** Do not animate expand (currently not implemented). @default false */
|
|
@@ -897,6 +985,19 @@ export interface VisitRowsOptions {
|
|
|
897
985
|
/* -----------------------------------------------------------------------------
|
|
898
986
|
* wb_ext_filter
|
|
899
987
|
* ---------------------------------------------------------------------------*/
|
|
988
|
+
|
|
989
|
+
/**
|
|
990
|
+
* Passed as tree option.filer.connect to configure automatic integration of
|
|
991
|
+
* filter UI controls. @experimental
|
|
992
|
+
*/
|
|
993
|
+
export interface FilterConnectType {
|
|
994
|
+
inputElem: string | HTMLInputElement | null;
|
|
995
|
+
modeButton?: string | HTMLButtonElement | null;
|
|
996
|
+
nextButton?: string | HTMLButtonElement | HTMLAnchorElement | null;
|
|
997
|
+
prevButton?: string | HTMLButtonElement | HTMLAnchorElement | null;
|
|
998
|
+
matchInfoElem?: string | HTMLElement | null;
|
|
999
|
+
}
|
|
1000
|
+
|
|
900
1001
|
/**
|
|
901
1002
|
* Passed as tree options to configure default filtering behavior.
|
|
902
1003
|
*
|
|
@@ -905,10 +1006,12 @@ export interface VisitRowsOptions {
|
|
|
905
1006
|
*/
|
|
906
1007
|
export type FilterOptionsType = {
|
|
907
1008
|
/**
|
|
908
|
-
* Element or selector of
|
|
1009
|
+
* Element or selector of input controls and buttons for filter query strings.
|
|
1010
|
+
* @experimental
|
|
1011
|
+
* @since 0.13
|
|
909
1012
|
* @default null
|
|
910
1013
|
*/
|
|
911
|
-
|
|
1014
|
+
connect?: null | FilterConnectType;
|
|
912
1015
|
/**
|
|
913
1016
|
* Re-apply last filter if lazy data is loaded
|
|
914
1017
|
* @default true
|
package/src/util.ts
CHANGED
|
@@ -424,19 +424,6 @@ export function elemFromSelector<T = HTMLElement>(obj: string | T): T | null {
|
|
|
424
424
|
return obj as T;
|
|
425
425
|
}
|
|
426
426
|
|
|
427
|
-
// /** Return a EventTarget from selector or cast an existing element. */
|
|
428
|
-
// export function eventTargetFromSelector(
|
|
429
|
-
// obj: string | EventTarget
|
|
430
|
-
// ): EventTarget | null {
|
|
431
|
-
// if (!obj) {
|
|
432
|
-
// return null;
|
|
433
|
-
// }
|
|
434
|
-
// if (typeof obj === "string") {
|
|
435
|
-
// return document.querySelector(obj) as EventTarget;
|
|
436
|
-
// }
|
|
437
|
-
// return obj as EventTarget;
|
|
438
|
-
// }
|
|
439
|
-
|
|
440
427
|
/**
|
|
441
428
|
* Return a canonical descriptive string for a keyboard or mouse event.
|
|
442
429
|
*
|
package/src/wb_ext_edit.ts
CHANGED
|
@@ -381,7 +381,7 @@ export class EditExtension extends WunderbaumExtension<EditOptionsType> {
|
|
|
381
381
|
this.relatedNode = node;
|
|
382
382
|
|
|
383
383
|
// Don't filter new nodes:
|
|
384
|
-
newNode.match =
|
|
384
|
+
newNode.match = -1;
|
|
385
385
|
|
|
386
386
|
newNode.makeVisible({ noAnimation: true }).then(() => {
|
|
387
387
|
this.startEditTitle(newNode);
|
package/src/wb_ext_filter.ts
CHANGED
|
@@ -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;
|
|
267
|
+
tree.filterMode = opts.mode ?? "dim";
|
|
180
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_extension_base.ts
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
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?
|