wunderbaum 0.0.4 → 0.0.7
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 +562 -306
- package/dist/wunderbaum.esm.js +995 -473
- package/dist/wunderbaum.esm.min.js +34 -27
- package/dist/wunderbaum.esm.min.js.map +1 -1
- package/dist/wunderbaum.umd.js +995 -473
- package/dist/wunderbaum.umd.min.js +38 -32
- package/dist/wunderbaum.umd.min.js.map +1 -1
- package/package.json +4 -4
- package/src/common.ts +50 -147
- package/src/types.ts +470 -0
- package/src/util.ts +30 -11
- package/src/wb_ext_dnd.ts +12 -163
- package/src/wb_ext_edit.ts +15 -15
- package/src/wb_ext_filter.ts +17 -5
- package/src/wb_ext_keynav.ts +140 -38
- package/src/wb_ext_logger.ts +1 -1
- package/src/wb_node.ts +224 -123
- package/src/wb_options.ts +54 -18
- package/src/wunderbaum.scss +172 -84
- package/src/wunderbaum.ts +504 -243
package/src/wb_node.ts
CHANGED
|
@@ -9,25 +9,31 @@ import * as util from "./util";
|
|
|
9
9
|
|
|
10
10
|
import { Wunderbaum } from "./wunderbaum";
|
|
11
11
|
import {
|
|
12
|
-
|
|
12
|
+
AddNodeType,
|
|
13
|
+
ApplyCommandType,
|
|
13
14
|
ChangeType,
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
KEY_TO_ACTION_DICT,
|
|
17
|
-
makeNodeTitleMatcher,
|
|
15
|
+
ColumnEventInfos,
|
|
16
|
+
MakeVisibleOptions,
|
|
18
17
|
MatcherType,
|
|
19
18
|
NodeAnyCallback,
|
|
20
19
|
NodeStatusType,
|
|
21
20
|
NodeVisitCallback,
|
|
22
21
|
NodeVisitResponse,
|
|
23
|
-
|
|
24
|
-
ROW_HEIGHT,
|
|
25
|
-
TEST_IMG,
|
|
26
|
-
ApplyCommandType,
|
|
27
|
-
AddNodeType,
|
|
22
|
+
ScrollIntoViewOptions,
|
|
28
23
|
SetActiveOptions,
|
|
29
24
|
SetExpandedOptions,
|
|
30
25
|
SetSelectedOptions,
|
|
26
|
+
SetStatusOptions,
|
|
27
|
+
} from "./types";
|
|
28
|
+
import {
|
|
29
|
+
iconMap,
|
|
30
|
+
ICON_WIDTH,
|
|
31
|
+
KEY_TO_ACTION_DICT,
|
|
32
|
+
makeNodeTitleMatcher,
|
|
33
|
+
RESERVED_TREE_SOURCE_KEYS,
|
|
34
|
+
TITLE_SPAN_PAD_Y,
|
|
35
|
+
ROW_HEIGHT,
|
|
36
|
+
TEST_IMG,
|
|
31
37
|
} from "./common";
|
|
32
38
|
import { Deferred } from "./deferred";
|
|
33
39
|
import { WbNodeData } from "./wb_options";
|
|
@@ -50,7 +56,7 @@ const NODE_PROPS = new Set<string>([
|
|
|
50
56
|
const NODE_ATTRS = new Set<string>([
|
|
51
57
|
"checkbox",
|
|
52
58
|
"expanded",
|
|
53
|
-
"
|
|
59
|
+
"classes",
|
|
54
60
|
"folder",
|
|
55
61
|
"icon",
|
|
56
62
|
"iconTooltip",
|
|
@@ -96,6 +102,7 @@ export class WunderbaumNode {
|
|
|
96
102
|
public readonly refKey: string | undefined = undefined;
|
|
97
103
|
public children: WunderbaumNode[] | null = null;
|
|
98
104
|
public checkbox?: boolean;
|
|
105
|
+
/** If true, (in grid mode) no cells are rendered, except for the node title.*/
|
|
99
106
|
public colspan?: boolean;
|
|
100
107
|
public icon?: boolean | string;
|
|
101
108
|
public lazy: boolean = false;
|
|
@@ -108,8 +115,8 @@ export class WunderbaumNode {
|
|
|
108
115
|
public type?: string;
|
|
109
116
|
public tooltip?: string;
|
|
110
117
|
/** Additional classes added to `div.wb-row`.
|
|
111
|
-
* @see {@link
|
|
112
|
-
public
|
|
118
|
+
* @see {@link hasClass}, {@link setClass}. */
|
|
119
|
+
public classes: Set<string> | null = null; //new Set<string>();
|
|
113
120
|
/** Custom data that was passed to the constructor */
|
|
114
121
|
public data: any = {};
|
|
115
122
|
// --- Node Status ---
|
|
@@ -150,9 +157,7 @@ export class WunderbaumNode {
|
|
|
150
157
|
this.lazy = data.lazy === true;
|
|
151
158
|
this.selected = data.selected === true;
|
|
152
159
|
if (data.classes) {
|
|
153
|
-
|
|
154
|
-
this.extraClasses.add(c.trim());
|
|
155
|
-
}
|
|
160
|
+
this.setClass(data.classes);
|
|
156
161
|
}
|
|
157
162
|
// Store custom fields as `node.data`
|
|
158
163
|
for (const [key, value] of Object.entries(data)) {
|
|
@@ -172,7 +177,7 @@ export class WunderbaumNode {
|
|
|
172
177
|
* @internal
|
|
173
178
|
*/
|
|
174
179
|
toString() {
|
|
175
|
-
return
|
|
180
|
+
return `WunderbaumNode@${this.key}<'${this.title}'>`;
|
|
176
181
|
}
|
|
177
182
|
|
|
178
183
|
// /** Return an option value. */
|
|
@@ -220,6 +225,8 @@ export class WunderbaumNode {
|
|
|
220
225
|
* @returns first child added
|
|
221
226
|
*/
|
|
222
227
|
addChildren(nodeData: any, options?: any): WunderbaumNode {
|
|
228
|
+
const tree = this.tree;
|
|
229
|
+
const level = options ? options.level : this.getLevel();
|
|
223
230
|
let insertBefore: WunderbaumNode | string | number = options
|
|
224
231
|
? options.before
|
|
225
232
|
: null,
|
|
@@ -227,19 +234,21 @@ export class WunderbaumNode {
|
|
|
227
234
|
nodeList = [];
|
|
228
235
|
|
|
229
236
|
try {
|
|
230
|
-
|
|
237
|
+
tree.enableUpdate(false);
|
|
231
238
|
|
|
232
239
|
if (util.isPlainObject(nodeData)) {
|
|
233
240
|
nodeData = [nodeData];
|
|
234
241
|
}
|
|
242
|
+
const forceExpand = level < tree.options.minExpandLevel!;
|
|
235
243
|
for (let child of nodeData) {
|
|
236
244
|
let subChildren = child.children;
|
|
237
245
|
delete child.children;
|
|
238
246
|
|
|
239
|
-
let n = new WunderbaumNode(
|
|
247
|
+
let n = new WunderbaumNode(tree, this, child);
|
|
248
|
+
if (forceExpand && !n.lazy) n.expanded = true;
|
|
240
249
|
nodeList.push(n);
|
|
241
250
|
if (subChildren) {
|
|
242
|
-
n.addChildren(subChildren, { redraw: false });
|
|
251
|
+
n.addChildren(subChildren, { redraw: false, level: level + 1 });
|
|
243
252
|
}
|
|
244
253
|
}
|
|
245
254
|
|
|
@@ -256,14 +265,14 @@ export class WunderbaumNode {
|
|
|
256
265
|
this.children.splice(pos, 0, ...nodeList);
|
|
257
266
|
}
|
|
258
267
|
// TODO:
|
|
259
|
-
// if (
|
|
268
|
+
// if (tree.options.selectMode === 3) {
|
|
260
269
|
// this.fixSelection3FromEndNodes();
|
|
261
270
|
// }
|
|
262
271
|
// this.triggerModifyChild("add", nodeList.length === 1 ? nodeList[0] : null);
|
|
263
|
-
|
|
272
|
+
tree.setModified(ChangeType.structure);
|
|
264
273
|
return nodeList[0];
|
|
265
274
|
} finally {
|
|
266
|
-
|
|
275
|
+
tree.enableUpdate(true);
|
|
267
276
|
}
|
|
268
277
|
}
|
|
269
278
|
|
|
@@ -307,31 +316,42 @@ export class WunderbaumNode {
|
|
|
307
316
|
return this.tree.applyCommand(cmd, this, opts);
|
|
308
317
|
}
|
|
309
318
|
|
|
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) {
|
|
319
|
+
/**
|
|
320
|
+
* Add/remove one or more classes to `<div class='wb-row'>`.
|
|
321
|
+
*
|
|
322
|
+
* This also maintains `node.classes`, so the class will survive a re-render.
|
|
323
|
+
*
|
|
324
|
+
* @param className one or more class names. Multiple classes can be passed
|
|
325
|
+
* as space-separated string, array of strings, or set of strings.
|
|
326
|
+
*/
|
|
327
|
+
setClass(
|
|
328
|
+
className: string | string[] | Set<string>,
|
|
329
|
+
flag: boolean = true
|
|
330
|
+
): void {
|
|
327
331
|
const cnSet = util.toSet(className);
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
+
if (flag) {
|
|
333
|
+
if (this.classes === null) {
|
|
334
|
+
this.classes = new Set<string>();
|
|
335
|
+
}
|
|
336
|
+
cnSet.forEach((cn) => {
|
|
337
|
+
this.classes!.add(cn);
|
|
338
|
+
this._rowElem?.classList.toggle(cn, flag);
|
|
339
|
+
});
|
|
340
|
+
} else {
|
|
341
|
+
if (this.classes === null) {
|
|
342
|
+
return;
|
|
343
|
+
}
|
|
344
|
+
cnSet.forEach((cn) => {
|
|
345
|
+
this.classes!.delete(cn);
|
|
346
|
+
this._rowElem?.classList.toggle(cn, flag);
|
|
347
|
+
});
|
|
348
|
+
if (this.classes.size === 0) {
|
|
349
|
+
this.classes = null;
|
|
350
|
+
}
|
|
351
|
+
}
|
|
332
352
|
}
|
|
333
353
|
|
|
334
|
-
/** */
|
|
354
|
+
/** Call `setExpanded()` on al child nodes*/
|
|
335
355
|
async expandAll(flag: boolean = true) {
|
|
336
356
|
this.visit((node) => {
|
|
337
357
|
node.setExpanded(flag);
|
|
@@ -527,6 +547,11 @@ export class WunderbaumNode {
|
|
|
527
547
|
return !!(this.children && this.children.length);
|
|
528
548
|
}
|
|
529
549
|
|
|
550
|
+
/** Return true if node has className set. */
|
|
551
|
+
hasClass(className: string): boolean {
|
|
552
|
+
return this.classes ? this.classes.has(className) : false;
|
|
553
|
+
}
|
|
554
|
+
|
|
530
555
|
/** Return true if this node is the currently active tree node. */
|
|
531
556
|
isActive() {
|
|
532
557
|
return this.tree.activeNode === this;
|
|
@@ -539,6 +564,13 @@ export class WunderbaumNode {
|
|
|
539
564
|
return this.parent && this.parent === other;
|
|
540
565
|
}
|
|
541
566
|
|
|
567
|
+
/** Return true if this node's title spans all columns, i.e. the node has no
|
|
568
|
+
* grid cells.
|
|
569
|
+
*/
|
|
570
|
+
isColspan() {
|
|
571
|
+
return !!this.getOption("colspan");
|
|
572
|
+
}
|
|
573
|
+
|
|
542
574
|
/** Return true if this node is a direct or indirect sub node of `other`.
|
|
543
575
|
* (See also [[isChildOf]].)
|
|
544
576
|
*/
|
|
@@ -690,9 +722,11 @@ export class WunderbaumNode {
|
|
|
690
722
|
return true;
|
|
691
723
|
}
|
|
692
724
|
|
|
693
|
-
protected _loadSourceObject(source: any) {
|
|
725
|
+
protected _loadSourceObject(source: any, level?: number) {
|
|
694
726
|
const tree = this.tree;
|
|
695
727
|
|
|
728
|
+
level ??= this.getLevel();
|
|
729
|
+
|
|
696
730
|
// Let caller modify the parsed JSON response:
|
|
697
731
|
this._callEvent("receive", { response: source });
|
|
698
732
|
|
|
@@ -705,21 +739,40 @@ export class WunderbaumNode {
|
|
|
705
739
|
"If `source` is an object, it must have a `children` property"
|
|
706
740
|
);
|
|
707
741
|
if (source.types) {
|
|
708
|
-
|
|
709
|
-
|
|
742
|
+
tree.logInfo("Redefine types", source.columns);
|
|
743
|
+
tree.setTypes(source.types, false);
|
|
744
|
+
delete source.types;
|
|
745
|
+
}
|
|
746
|
+
if (source.columns) {
|
|
747
|
+
tree.logInfo("Redefine columns", source.columns);
|
|
748
|
+
tree.columns = source.columns;
|
|
749
|
+
delete source.columns;
|
|
750
|
+
tree.updateColumns({ calculateCols: false });
|
|
710
751
|
}
|
|
752
|
+
|
|
711
753
|
this.addChildren(source.children);
|
|
712
754
|
|
|
755
|
+
// Add extra data to `tree.data`
|
|
756
|
+
for (const [key, value] of Object.entries(source)) {
|
|
757
|
+
if (!RESERVED_TREE_SOURCE_KEYS.has(key)) {
|
|
758
|
+
tree.data[key] = value;
|
|
759
|
+
tree.logDebug(`Add source.${key} to tree.data.${key}`);
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
|
|
713
763
|
this._callEvent("load");
|
|
714
764
|
}
|
|
715
765
|
|
|
716
766
|
/** Download data from the cloud, then call `.update()`. */
|
|
717
767
|
async load(source: any) {
|
|
718
768
|
const tree = this.tree;
|
|
719
|
-
// const opts = tree.options;
|
|
720
769
|
const requestId = Date.now();
|
|
721
770
|
const prevParent = this.parent;
|
|
722
771
|
const url = typeof source === "string" ? source : source.url;
|
|
772
|
+
const start = Date.now();
|
|
773
|
+
let elap = 0,
|
|
774
|
+
elapLoad = 0,
|
|
775
|
+
elapProcess = 0;
|
|
723
776
|
|
|
724
777
|
// Check for overlapping requests
|
|
725
778
|
if (this._requestId) {
|
|
@@ -730,11 +783,12 @@ export class WunderbaumNode {
|
|
|
730
783
|
}
|
|
731
784
|
this._requestId = requestId;
|
|
732
785
|
|
|
733
|
-
const timerLabel = tree.logTime(this + ".load()");
|
|
786
|
+
// const timerLabel = tree.logTime(this + ".load()");
|
|
734
787
|
|
|
735
788
|
try {
|
|
736
789
|
if (!url) {
|
|
737
790
|
this._loadSourceObject(source);
|
|
791
|
+
elapProcess = Date.now() - start;
|
|
738
792
|
} else {
|
|
739
793
|
this.setStatus(NodeStatusType.loading);
|
|
740
794
|
const response = await fetch(url, { method: "GET" });
|
|
@@ -742,6 +796,7 @@ export class WunderbaumNode {
|
|
|
742
796
|
util.error(`GET ${url} returned ${response.status}, ${response}`);
|
|
743
797
|
}
|
|
744
798
|
const data = await response.json();
|
|
799
|
+
elapLoad = Date.now() - start;
|
|
745
800
|
|
|
746
801
|
if (this._requestId && this._requestId > requestId) {
|
|
747
802
|
this.logWarn(
|
|
@@ -758,23 +813,32 @@ export class WunderbaumNode {
|
|
|
758
813
|
return;
|
|
759
814
|
}
|
|
760
815
|
this.setStatus(NodeStatusType.ok);
|
|
761
|
-
if (data.columns) {
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
}
|
|
816
|
+
// if (data.columns) {
|
|
817
|
+
// tree.logInfo("Re-define columns", data.columns);
|
|
818
|
+
// util.assert(!this.parent);
|
|
819
|
+
// tree.columns = data.columns;
|
|
820
|
+
// delete data.columns;
|
|
821
|
+
// tree.updateColumns({ calculateCols: false });
|
|
822
|
+
// }
|
|
823
|
+
const startProcess = Date.now();
|
|
768
824
|
this._loadSourceObject(data);
|
|
825
|
+
elapProcess = Date.now() - startProcess;
|
|
769
826
|
}
|
|
770
827
|
} catch (error) {
|
|
771
828
|
this.logError("Error during load()", source, error);
|
|
772
829
|
this._callEvent("error", { error: error });
|
|
773
|
-
this.setStatus(NodeStatusType.error, "" + error);
|
|
830
|
+
this.setStatus(NodeStatusType.error, { message: "" + error });
|
|
774
831
|
throw error;
|
|
775
832
|
} finally {
|
|
776
833
|
this._requestId = 0;
|
|
777
|
-
|
|
834
|
+
elap = Date.now() - start;
|
|
835
|
+
if (tree.options.debugLevel >= 3) {
|
|
836
|
+
tree.logInfo(
|
|
837
|
+
`Load source took ${elap / 1000} seconds (transfer: ${
|
|
838
|
+
elapLoad / 1000
|
|
839
|
+
}s, processing: ${elapProcess / 1000}s)`
|
|
840
|
+
);
|
|
841
|
+
}
|
|
778
842
|
}
|
|
779
843
|
}
|
|
780
844
|
|
|
@@ -815,7 +879,7 @@ export class WunderbaumNode {
|
|
|
815
879
|
} catch (e) {
|
|
816
880
|
this.logError("Error during loadLazy()", e);
|
|
817
881
|
this._callEvent("error", { error: e });
|
|
818
|
-
this.setStatus(NodeStatusType.error, "" + e);
|
|
882
|
+
this.setStatus(NodeStatusType.error, { message: "" + e });
|
|
819
883
|
}
|
|
820
884
|
return;
|
|
821
885
|
}
|
|
@@ -862,25 +926,29 @@ export class WunderbaumNode {
|
|
|
862
926
|
* @param {object} [opts] passed to `setExpanded()`.
|
|
863
927
|
* Defaults to {noAnimation: false, noEvents: false, scrollIntoView: true}
|
|
864
928
|
*/
|
|
865
|
-
async makeVisible(opts
|
|
929
|
+
async makeVisible(opts?: MakeVisibleOptions) {
|
|
866
930
|
let i,
|
|
867
931
|
dfd = new Deferred(),
|
|
868
932
|
deferreds = [],
|
|
869
933
|
parents = this.getParentList(false, false),
|
|
870
934
|
len = parents.length,
|
|
871
|
-
effects = !(opts && opts.noAnimation === true),
|
|
935
|
+
// effects = !(opts && opts.noAnimation === true),
|
|
872
936
|
scroll = !(opts && opts.scrollIntoView === false);
|
|
873
937
|
|
|
874
938
|
// Expand bottom-up, so only the top node is animated
|
|
875
939
|
for (i = len - 1; i >= 0; i--) {
|
|
876
940
|
// self.debug("pushexpand" + parents[i]);
|
|
877
|
-
|
|
941
|
+
const seOpts = { noAnimation: opts?.noAnimation };
|
|
942
|
+
deferreds.push(parents[i].setExpanded(true, seOpts));
|
|
878
943
|
}
|
|
879
944
|
Promise.all(deferreds).then(() => {
|
|
880
945
|
// All expands have finished
|
|
881
946
|
// self.debug("expand DONE", scroll);
|
|
882
|
-
|
|
883
|
-
|
|
947
|
+
// Note: this.tree may be none when switching demo trees
|
|
948
|
+
if (scroll && this.tree) {
|
|
949
|
+
// Make sure markup and _rowIdx is updated before we do the scroll calculations
|
|
950
|
+
this.tree.updatePendingModifications();
|
|
951
|
+
this.scrollIntoView().then(() => {
|
|
884
952
|
// self.debug("scroll DONE");
|
|
885
953
|
dfd.resolve();
|
|
886
954
|
});
|
|
@@ -1074,25 +1142,35 @@ export class WunderbaumNode {
|
|
|
1074
1142
|
}
|
|
1075
1143
|
}
|
|
1076
1144
|
|
|
1077
|
-
protected _getRenderInfo() {
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1145
|
+
protected _getRenderInfo(): any {
|
|
1146
|
+
const allColInfosById: ColumnEventInfos = {};
|
|
1147
|
+
const renderColInfosById: ColumnEventInfos = {};
|
|
1148
|
+
const isColspan = this.isColspan();
|
|
1149
|
+
|
|
1150
|
+
const colElems = this._rowElem
|
|
1081
1151
|
? ((<unknown>(
|
|
1082
1152
|
this._rowElem.querySelectorAll("span.wb-col")
|
|
1083
|
-
)) as
|
|
1153
|
+
)) as HTMLSpanElement[])
|
|
1084
1154
|
: null;
|
|
1085
1155
|
|
|
1156
|
+
let idx = 0;
|
|
1086
1157
|
for (let col of this.tree.columns) {
|
|
1087
|
-
|
|
1158
|
+
allColInfosById[col.id] = {
|
|
1088
1159
|
id: col.id,
|
|
1089
1160
|
idx: idx,
|
|
1090
1161
|
elem: colElems ? colElems[idx] : null,
|
|
1091
1162
|
info: col,
|
|
1092
1163
|
};
|
|
1164
|
+
// renderColInfosById only contains columns that need rendering:
|
|
1165
|
+
if (!isColspan && col.id !== "*") {
|
|
1166
|
+
renderColInfosById[col.id] = allColInfosById[col.id];
|
|
1167
|
+
}
|
|
1093
1168
|
idx++;
|
|
1094
1169
|
}
|
|
1095
|
-
return
|
|
1170
|
+
return {
|
|
1171
|
+
allColInfosById: allColInfosById,
|
|
1172
|
+
renderColInfosById: renderColInfosById,
|
|
1173
|
+
};
|
|
1096
1174
|
}
|
|
1097
1175
|
|
|
1098
1176
|
protected _createIcon(
|
|
@@ -1118,6 +1196,8 @@ export class WunderbaumNode {
|
|
|
1118
1196
|
icon = iconMap.folderOpen;
|
|
1119
1197
|
} else if (this.children) {
|
|
1120
1198
|
icon = iconMap.folder;
|
|
1199
|
+
} else if (this.lazy) {
|
|
1200
|
+
icon = iconMap.folderLazy;
|
|
1121
1201
|
} else {
|
|
1122
1202
|
icon = iconMap.doc;
|
|
1123
1203
|
}
|
|
@@ -1145,7 +1225,7 @@ export class WunderbaumNode {
|
|
|
1145
1225
|
|
|
1146
1226
|
/**
|
|
1147
1227
|
* Create a whole new `<div class="wb-row">` element.
|
|
1148
|
-
* @see {@link
|
|
1228
|
+
* @see {@link WunderbaumNode.render}
|
|
1149
1229
|
*/
|
|
1150
1230
|
protected _render_markup(opts: any) {
|
|
1151
1231
|
const tree = this.tree;
|
|
@@ -1160,8 +1240,7 @@ export class WunderbaumNode {
|
|
|
1160
1240
|
let checkboxSpan: HTMLElement | null = null;
|
|
1161
1241
|
let iconSpan: HTMLElement | null;
|
|
1162
1242
|
let expanderSpan: HTMLElement | null = null;
|
|
1163
|
-
const activeColIdx =
|
|
1164
|
-
tree.navMode === NavigationMode.row ? null : tree.activeColIdx;
|
|
1243
|
+
const activeColIdx = tree.isRowNav() ? null : tree.activeColIdx;
|
|
1165
1244
|
|
|
1166
1245
|
const isNew = !rowDiv;
|
|
1167
1246
|
util.assert(isNew);
|
|
@@ -1229,7 +1308,9 @@ export class WunderbaumNode {
|
|
|
1229
1308
|
}
|
|
1230
1309
|
|
|
1231
1310
|
// Render columns
|
|
1232
|
-
|
|
1311
|
+
const isColspan = this.isColspan();
|
|
1312
|
+
|
|
1313
|
+
if (!isColspan && columns.length > 1) {
|
|
1233
1314
|
let colIdx = 0;
|
|
1234
1315
|
for (let col of columns) {
|
|
1235
1316
|
colIdx++;
|
|
@@ -1257,11 +1338,6 @@ export class WunderbaumNode {
|
|
|
1257
1338
|
}
|
|
1258
1339
|
}
|
|
1259
1340
|
}
|
|
1260
|
-
|
|
1261
|
-
// Now go on and fill in data and update classes
|
|
1262
|
-
opts.isNew = true;
|
|
1263
|
-
this._render_data(opts);
|
|
1264
|
-
|
|
1265
1341
|
// Attach to DOM as late as possible
|
|
1266
1342
|
const after = opts ? opts.after : "last";
|
|
1267
1343
|
switch (after) {
|
|
@@ -1274,12 +1350,15 @@ export class WunderbaumNode {
|
|
|
1274
1350
|
default:
|
|
1275
1351
|
opts.after.after(rowDiv);
|
|
1276
1352
|
}
|
|
1353
|
+
// Now go on and fill in data and update classes
|
|
1354
|
+
opts.isNew = true;
|
|
1355
|
+
this._render_data(opts);
|
|
1277
1356
|
}
|
|
1278
1357
|
|
|
1279
1358
|
/**
|
|
1280
1359
|
* Render `node.title`, `.icon` into an existing row.
|
|
1281
1360
|
*
|
|
1282
|
-
* @see {@link
|
|
1361
|
+
* @see {@link WunderbaumNode.render}
|
|
1283
1362
|
*/
|
|
1284
1363
|
protected _render_data(opts: any) {
|
|
1285
1364
|
util.assert(this._rowElem);
|
|
@@ -1289,7 +1368,7 @@ export class WunderbaumNode {
|
|
|
1289
1368
|
const rowDiv = this._rowElem!;
|
|
1290
1369
|
const isNew = !!opts.isNew; // Called by _render_markup()?
|
|
1291
1370
|
const columns = tree.columns;
|
|
1292
|
-
const
|
|
1371
|
+
const isColspan = this.isColspan();
|
|
1293
1372
|
|
|
1294
1373
|
// Row markup already exists
|
|
1295
1374
|
const nodeElem = rowDiv.querySelector("span.wb-node") as HTMLSpanElement;
|
|
@@ -1305,15 +1384,15 @@ export class WunderbaumNode {
|
|
|
1305
1384
|
|
|
1306
1385
|
// Set the width of the title span, so overflow ellipsis work
|
|
1307
1386
|
if (!treeOptions.skeleton) {
|
|
1308
|
-
if (
|
|
1387
|
+
if (isColspan) {
|
|
1309
1388
|
let vpWidth = tree.element.clientWidth;
|
|
1310
1389
|
titleSpan.style.width =
|
|
1311
|
-
vpWidth - (<any>nodeElem)._ofsTitlePx -
|
|
1390
|
+
vpWidth - (<any>nodeElem)._ofsTitlePx - TITLE_SPAN_PAD_Y + "px";
|
|
1312
1391
|
} else {
|
|
1313
1392
|
titleSpan.style.width =
|
|
1314
|
-
columns[0]._widthPx -
|
|
1393
|
+
columns[0]._widthPx! -
|
|
1315
1394
|
(<any>nodeElem)._ofsTitlePx -
|
|
1316
|
-
|
|
1395
|
+
TITLE_SPAN_PAD_Y +
|
|
1317
1396
|
"px";
|
|
1318
1397
|
}
|
|
1319
1398
|
}
|
|
@@ -1330,19 +1409,22 @@ export class WunderbaumNode {
|
|
|
1330
1409
|
});
|
|
1331
1410
|
} else if (this.parent) {
|
|
1332
1411
|
// Skip root node
|
|
1412
|
+
const renderInfo = this._getRenderInfo();
|
|
1413
|
+
|
|
1333
1414
|
this._callEvent("render", {
|
|
1334
1415
|
isNew: isNew,
|
|
1335
|
-
|
|
1416
|
+
isColspan: isColspan,
|
|
1417
|
+
// isDataChange: true,
|
|
1336
1418
|
nodeElem: nodeElem,
|
|
1337
|
-
|
|
1338
|
-
|
|
1419
|
+
allColInfosById: renderInfo.allColInfosById,
|
|
1420
|
+
renderColInfosById: renderInfo.renderColInfosById,
|
|
1339
1421
|
});
|
|
1340
1422
|
}
|
|
1341
1423
|
}
|
|
1342
1424
|
|
|
1343
1425
|
/**
|
|
1344
1426
|
* Update row classes to reflect active, focuses, etc.
|
|
1345
|
-
* @see {@link
|
|
1427
|
+
* @see {@link WunderbaumNode.render}
|
|
1346
1428
|
*/
|
|
1347
1429
|
protected _render_status(opts: any) {
|
|
1348
1430
|
// this.log("_render_status", opts);
|
|
@@ -1359,8 +1441,6 @@ export class WunderbaumNode {
|
|
|
1359
1441
|
const checkboxSpan = nodeElem.querySelector(
|
|
1360
1442
|
"i.wb-checkbox"
|
|
1361
1443
|
) as HTMLLIElement;
|
|
1362
|
-
// TODO: update icon (if not opts.isNew)
|
|
1363
|
-
// const iconSpan = nodeElem.querySelector("i.wb-icon") as HTMLElement;
|
|
1364
1444
|
|
|
1365
1445
|
let rowClasses = ["wb-row"];
|
|
1366
1446
|
this.expanded ? rowClasses.push("wb-expanded") : 0;
|
|
@@ -1370,6 +1450,7 @@ export class WunderbaumNode {
|
|
|
1370
1450
|
this === tree.focusNode ? rowClasses.push("wb-focus") : 0;
|
|
1371
1451
|
this._errorInfo ? rowClasses.push("wb-error") : 0;
|
|
1372
1452
|
this._isLoading ? rowClasses.push("wb-loading") : 0;
|
|
1453
|
+
this.isColspan() ? rowClasses.push("wb-colspan") : 0;
|
|
1373
1454
|
this.statusNodeType
|
|
1374
1455
|
? rowClasses.push("wb-status-" + this.statusNodeType)
|
|
1375
1456
|
: 0;
|
|
@@ -1381,8 +1462,8 @@ export class WunderbaumNode {
|
|
|
1381
1462
|
// Replace previous classes:
|
|
1382
1463
|
rowDiv.className = rowClasses.join(" ");
|
|
1383
1464
|
|
|
1384
|
-
// Add classes from `node.
|
|
1385
|
-
rowDiv.classList.add(...this.
|
|
1465
|
+
// Add classes from `node.classes`
|
|
1466
|
+
this.classes ? rowDiv.classList.add(...this.classes) : 0;
|
|
1386
1467
|
|
|
1387
1468
|
// Add classes from `tree.types[node.type]`
|
|
1388
1469
|
if (typeInfo && typeInfo.classes) {
|
|
@@ -1417,6 +1498,11 @@ export class WunderbaumNode {
|
|
|
1417
1498
|
for (let colSpan of rowDiv.children) {
|
|
1418
1499
|
colSpan.classList.toggle("wb-active", i++ === tree.activeColIdx);
|
|
1419
1500
|
}
|
|
1501
|
+
// Update icon (if not opts.isNew, which would rebuild markup anyway)
|
|
1502
|
+
const iconSpan = nodeElem.querySelector("i.wb-icon") as HTMLElement;
|
|
1503
|
+
if (iconSpan) {
|
|
1504
|
+
this._createIcon(nodeElem, iconSpan);
|
|
1505
|
+
}
|
|
1420
1506
|
}
|
|
1421
1507
|
}
|
|
1422
1508
|
|
|
@@ -1574,8 +1660,9 @@ export class WunderbaumNode {
|
|
|
1574
1660
|
/** Make sure that this node is visible in the viewport.
|
|
1575
1661
|
* @see {@link Wunderbaum.scrollTo|Wunderbaum.scrollTo()}
|
|
1576
1662
|
*/
|
|
1577
|
-
async scrollIntoView(options?:
|
|
1578
|
-
|
|
1663
|
+
async scrollIntoView(options?: ScrollIntoViewOptions) {
|
|
1664
|
+
const opts = Object.assign({ node: this }, options);
|
|
1665
|
+
return this.tree.scrollTo(opts);
|
|
1579
1666
|
}
|
|
1580
1667
|
|
|
1581
1668
|
/**
|
|
@@ -1584,31 +1671,29 @@ export class WunderbaumNode {
|
|
|
1584
1671
|
async setActive(flag: boolean = true, options?: SetActiveOptions) {
|
|
1585
1672
|
const tree = this.tree;
|
|
1586
1673
|
const prev = tree.activeNode;
|
|
1587
|
-
const retrigger = options?.retrigger;
|
|
1588
|
-
const
|
|
1674
|
+
const retrigger = options?.retrigger; // Default: false
|
|
1675
|
+
const focusTree = options?.focusTree; // Default: false
|
|
1676
|
+
const focusNode = options?.focusNode !== false; // Default: true
|
|
1677
|
+
const noEvents = options?.noEvents; // Default: false
|
|
1678
|
+
const orgEvent = options?.event; // Default: false
|
|
1589
1679
|
|
|
1590
1680
|
if (!noEvents) {
|
|
1591
|
-
let orgEvent = options?.event;
|
|
1592
1681
|
if (flag) {
|
|
1593
1682
|
if (prev !== this || retrigger) {
|
|
1594
1683
|
if (
|
|
1595
1684
|
prev?._callEvent("deactivate", {
|
|
1596
1685
|
nextNode: this,
|
|
1597
1686
|
orgEvent: orgEvent,
|
|
1598
|
-
}) === false
|
|
1599
|
-
|
|
1600
|
-
return;
|
|
1601
|
-
}
|
|
1602
|
-
if (
|
|
1603
|
-
this._callEvent("activate", {
|
|
1687
|
+
}) === false ||
|
|
1688
|
+
this._callEvent("beforeActivate", {
|
|
1604
1689
|
prevNode: prev,
|
|
1605
1690
|
orgEvent: orgEvent,
|
|
1606
1691
|
}) === false
|
|
1607
1692
|
) {
|
|
1608
|
-
tree.activeNode = null;
|
|
1609
|
-
prev?.setModified();
|
|
1610
1693
|
return;
|
|
1611
1694
|
}
|
|
1695
|
+
tree.activeNode = null;
|
|
1696
|
+
prev?.setModified(ChangeType.status);
|
|
1612
1697
|
}
|
|
1613
1698
|
} else if (prev === this || retrigger) {
|
|
1614
1699
|
this._callEvent("deactivate", { nextNode: null, orgEvent: orgEvent });
|
|
@@ -1616,7 +1701,11 @@ export class WunderbaumNode {
|
|
|
1616
1701
|
}
|
|
1617
1702
|
|
|
1618
1703
|
if (prev !== this) {
|
|
1619
|
-
|
|
1704
|
+
if (flag) {
|
|
1705
|
+
tree.activeNode = this;
|
|
1706
|
+
if (focusNode || focusTree) tree.focusNode = this;
|
|
1707
|
+
if (focusTree) tree.setFocus();
|
|
1708
|
+
}
|
|
1620
1709
|
prev?.setModified(ChangeType.status);
|
|
1621
1710
|
this.setModified(ChangeType.status);
|
|
1622
1711
|
}
|
|
@@ -1624,42 +1713,52 @@ export class WunderbaumNode {
|
|
|
1624
1713
|
options &&
|
|
1625
1714
|
options.colIdx != null &&
|
|
1626
1715
|
options.colIdx !== tree.activeColIdx &&
|
|
1627
|
-
tree.
|
|
1716
|
+
tree.isCellNav()
|
|
1628
1717
|
) {
|
|
1629
1718
|
tree.setColumn(options.colIdx);
|
|
1630
1719
|
}
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
return this.
|
|
1720
|
+
if (flag && !noEvents) {
|
|
1721
|
+
this._callEvent("activate", { prevNode: prev, orgEvent: orgEvent });
|
|
1722
|
+
}
|
|
1723
|
+
return this.makeVisible();
|
|
1635
1724
|
}
|
|
1636
1725
|
|
|
1637
1726
|
/**
|
|
1638
1727
|
* Expand or collapse this node.
|
|
1639
1728
|
*/
|
|
1640
1729
|
async setExpanded(flag: boolean = true, options?: SetExpandedOptions) {
|
|
1641
|
-
// alert("" + this.getLevel() + ", "+ this.getOption("minExpandLevel");
|
|
1642
1730
|
if (
|
|
1643
1731
|
!flag &&
|
|
1644
1732
|
this.isExpanded() &&
|
|
1645
|
-
this.getLevel()
|
|
1733
|
+
this.getLevel() <= this.tree.getOption("minExpandLevel") &&
|
|
1646
1734
|
!util.getOption(options, "force")
|
|
1647
1735
|
) {
|
|
1648
1736
|
this.logDebug("Ignored collapse request below expandLevel.");
|
|
1649
1737
|
return;
|
|
1650
1738
|
}
|
|
1739
|
+
if (!flag === !this.expanded) {
|
|
1740
|
+
return; // Nothing to do
|
|
1741
|
+
}
|
|
1651
1742
|
if (flag && this.lazy && this.children == null) {
|
|
1652
1743
|
await this.loadLazy();
|
|
1653
1744
|
}
|
|
1654
1745
|
this.expanded = flag;
|
|
1655
|
-
|
|
1746
|
+
const updateOpts = { immediate: !!util.getOption(options, "immediate") };
|
|
1747
|
+
this.tree.setModified(ChangeType.structure, updateOpts);
|
|
1748
|
+
if (util.getOption(options, "scrollIntoView") !== false) {
|
|
1749
|
+
const lastChild = this.getLastChild();
|
|
1750
|
+
if (lastChild) {
|
|
1751
|
+
lastChild.scrollIntoView({ topNode: this });
|
|
1752
|
+
}
|
|
1753
|
+
}
|
|
1656
1754
|
}
|
|
1657
1755
|
|
|
1658
1756
|
/**
|
|
1659
1757
|
* Set keyboard focus here.
|
|
1660
1758
|
* @see {@link setActive}
|
|
1661
1759
|
*/
|
|
1662
|
-
setFocus(flag: boolean = true
|
|
1760
|
+
setFocus(flag: boolean = true) {
|
|
1761
|
+
util.assert(!!flag, "blur is not yet implemented");
|
|
1663
1762
|
const prev = this.tree.focusNode;
|
|
1664
1763
|
this.tree.focusNode = this;
|
|
1665
1764
|
prev?.setModified();
|
|
@@ -1702,10 +1801,12 @@ export class WunderbaumNode {
|
|
|
1702
1801
|
/** Display node status (ok, loading, error, noData) using styles and a dummy child node. */
|
|
1703
1802
|
setStatus(
|
|
1704
1803
|
status: NodeStatusType,
|
|
1705
|
-
|
|
1706
|
-
details?: string
|
|
1804
|
+
options?: SetStatusOptions
|
|
1707
1805
|
): WunderbaumNode | null {
|
|
1708
1806
|
const tree = this.tree;
|
|
1807
|
+
const message = options?.message;
|
|
1808
|
+
const details = options?.details;
|
|
1809
|
+
|
|
1709
1810
|
let statusNode: WunderbaumNode | null = null;
|
|
1710
1811
|
|
|
1711
1812
|
const _clearStatusNode = () => {
|
|
@@ -1823,9 +1924,9 @@ export class WunderbaumNode {
|
|
|
1823
1924
|
* @param {object} [extra]
|
|
1824
1925
|
*/
|
|
1825
1926
|
triggerModify(operation: string, extra?: any) {
|
|
1826
|
-
if (!this.parent) {
|
|
1827
|
-
|
|
1828
|
-
}
|
|
1927
|
+
// if (!this.parent) {
|
|
1928
|
+
// return;
|
|
1929
|
+
// }
|
|
1829
1930
|
this.parent.triggerModifyChild(operation, this, extra);
|
|
1830
1931
|
}
|
|
1831
1932
|
|