wunderbaum 0.0.1-0 → 0.0.3
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 +6 -5
- package/dist/wunderbaum.css +1 -1
- package/dist/wunderbaum.d.ts +634 -171
- package/dist/wunderbaum.esm.js +818 -436
- package/dist/wunderbaum.esm.min.js +31 -21
- package/dist/wunderbaum.esm.min.js.map +1 -1
- package/dist/wunderbaum.umd.js +820 -438
- package/dist/wunderbaum.umd.min.js +34 -24
- package/dist/wunderbaum.umd.min.js.map +1 -1
- package/package.json +35 -32
- package/src/common.ts +37 -5
- package/src/drag_observer.ts +169 -0
- package/src/util.ts +48 -13
- package/src/wb_ext_dnd.ts +145 -4
- package/src/wb_ext_edit.ts +10 -1
- package/src/wb_ext_filter.ts +35 -40
- package/src/wb_ext_grid.ts +45 -0
- package/src/wb_ext_keynav.ts +8 -4
- package/src/wb_node.ts +142 -78
- package/src/wb_options.ts +138 -25
- package/src/wunderbaum.scss +28 -5
- package/src/wunderbaum.ts +481 -321
package/dist/wunderbaum.esm.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
/*!
|
|
2
2
|
* Wunderbaum - util
|
|
3
3
|
* Copyright (c) 2021-2022, Martin Wendt. Released under the MIT license.
|
|
4
|
-
* v0.0.
|
|
4
|
+
* v0.0.3, Mon, 18 Apr 2022 11:52:44 GMT (https://github.com/mar10/wunderbaum)
|
|
5
5
|
*/
|
|
6
|
+
/** @module util */
|
|
6
7
|
/** Readable names for `MouseEvent.button` */
|
|
7
8
|
const MOUSE_BUTTONS = {
|
|
8
9
|
0: "",
|
|
@@ -14,7 +15,7 @@ const MOUSE_BUTTONS = {
|
|
|
14
15
|
};
|
|
15
16
|
const MAX_INT = 9007199254740991;
|
|
16
17
|
const userInfo = _getUserInfo();
|
|
17
|
-
/**True if the
|
|
18
|
+
/**True if the client is using a macOS platform. */
|
|
18
19
|
const isMac = userInfo.isMac;
|
|
19
20
|
const REX_HTML = /[&<>"'/]/g; // Escape those characters
|
|
20
21
|
const REX_TOOLTIP = /[<>"'/]/g; // Don't escape `&` in tooltips
|
|
@@ -162,7 +163,7 @@ function escapeTooltip(s) {
|
|
|
162
163
|
/** TODO */
|
|
163
164
|
function extractHtmlText(s) {
|
|
164
165
|
if (s.indexOf(">") >= 0) {
|
|
165
|
-
error("
|
|
166
|
+
error("Not implemented");
|
|
166
167
|
// return $("<div/>").html(s).text();
|
|
167
168
|
}
|
|
168
169
|
return s;
|
|
@@ -284,7 +285,7 @@ function setValueToElem(elem, value) {
|
|
|
284
285
|
input.valueAsNumber = value;
|
|
285
286
|
break;
|
|
286
287
|
case "radio":
|
|
287
|
-
|
|
288
|
+
error("Not implemented");
|
|
288
289
|
// const name = input.name;
|
|
289
290
|
// const checked = input.parentElement!.querySelector(
|
|
290
291
|
// `input[name="${name}"]:checked`
|
|
@@ -307,7 +308,7 @@ function setValueToElem(elem, value) {
|
|
|
307
308
|
}
|
|
308
309
|
// return value;
|
|
309
310
|
}
|
|
310
|
-
/**
|
|
311
|
+
/** Create and return an unconnected `HTMLElement` from a HTML string. */
|
|
311
312
|
function elemFromHtml(html) {
|
|
312
313
|
const t = document.createElement("template");
|
|
313
314
|
t.innerHTML = html.trim();
|
|
@@ -324,11 +325,23 @@ function elemFromSelector(obj) {
|
|
|
324
325
|
}
|
|
325
326
|
return obj;
|
|
326
327
|
}
|
|
328
|
+
/** Return a EventTarget from selector or cast an existing element. */
|
|
329
|
+
function eventTargetFromSelector(obj) {
|
|
330
|
+
if (!obj) {
|
|
331
|
+
return null;
|
|
332
|
+
}
|
|
333
|
+
if (typeof obj === "string") {
|
|
334
|
+
return document.querySelector(obj);
|
|
335
|
+
}
|
|
336
|
+
return obj;
|
|
337
|
+
}
|
|
327
338
|
/**
|
|
328
|
-
* Return a descriptive string for a keyboard or mouse event.
|
|
339
|
+
* Return a canonical descriptive string for a keyboard or mouse event.
|
|
329
340
|
*
|
|
330
341
|
* The result also contains a prefix for modifiers if any, for example
|
|
331
342
|
* `"x"`, `"F2"`, `"Control+Home"`, or `"Shift+clickright"`.
|
|
343
|
+
* This is especially useful in `switch` statements, to make sure that modifier
|
|
344
|
+
* keys are considered and handled correctly.
|
|
332
345
|
*/
|
|
333
346
|
function eventToString(event) {
|
|
334
347
|
let key = event.key, et = event.type, s = [];
|
|
@@ -360,6 +373,13 @@ function eventToString(event) {
|
|
|
360
373
|
}
|
|
361
374
|
return s.join("+");
|
|
362
375
|
}
|
|
376
|
+
/**
|
|
377
|
+
* Copy allproperties from one or more source objects to a target object.
|
|
378
|
+
*
|
|
379
|
+
* @returns the modified target object.
|
|
380
|
+
*/
|
|
381
|
+
// TODO: use Object.assign()? --> https://stackoverflow.com/a/42740894
|
|
382
|
+
// TODO: support deep merge --> https://stackoverflow.com/a/42740894
|
|
363
383
|
function extend(...args) {
|
|
364
384
|
for (let i = 1; i < args.length; i++) {
|
|
365
385
|
let arg = args[i];
|
|
@@ -374,23 +394,27 @@ function extend(...args) {
|
|
|
374
394
|
}
|
|
375
395
|
return args[0];
|
|
376
396
|
}
|
|
397
|
+
/** Return true if `obj` is of type `array`. */
|
|
377
398
|
function isArray(obj) {
|
|
378
399
|
return Array.isArray(obj);
|
|
379
400
|
}
|
|
401
|
+
/** Return true if `obj` is of type `Object` and has no propertied. */
|
|
380
402
|
function isEmptyObject(obj) {
|
|
381
403
|
return Object.keys(obj).length === 0 && obj.constructor === Object;
|
|
382
404
|
}
|
|
405
|
+
/** Return true if `obj` is of type `function`. */
|
|
383
406
|
function isFunction(obj) {
|
|
384
407
|
return typeof obj === "function";
|
|
385
408
|
}
|
|
409
|
+
/** Return true if `obj` is of type `Object`. */
|
|
386
410
|
function isPlainObject(obj) {
|
|
387
411
|
return Object.prototype.toString.call(obj) === "[object Object]";
|
|
388
412
|
}
|
|
389
413
|
/** A dummy function that does nothing ('no operation'). */
|
|
390
414
|
function noop(...args) { }
|
|
391
|
-
function onEvent(
|
|
415
|
+
function onEvent(rootTarget, eventNames, selectorOrHandler, handlerOrNone) {
|
|
392
416
|
let selector, handler;
|
|
393
|
-
|
|
417
|
+
rootTarget = eventTargetFromSelector(rootTarget);
|
|
394
418
|
if (handlerOrNone) {
|
|
395
419
|
selector = selectorOrHandler;
|
|
396
420
|
handler = handlerOrNone;
|
|
@@ -400,7 +424,7 @@ function onEvent(rootElem, eventNames, selectorOrHandler, handlerOrNone) {
|
|
|
400
424
|
handler = selectorOrHandler;
|
|
401
425
|
}
|
|
402
426
|
eventNames.split(" ").forEach((evn) => {
|
|
403
|
-
|
|
427
|
+
rootTarget.addEventListener(evn, function (e) {
|
|
404
428
|
if (!selector) {
|
|
405
429
|
return handler(e); // no event delegation
|
|
406
430
|
}
|
|
@@ -478,6 +502,13 @@ async function sleep(ms) {
|
|
|
478
502
|
}
|
|
479
503
|
/**
|
|
480
504
|
* Set or rotate checkbox status with support for tri-state.
|
|
505
|
+
*
|
|
506
|
+
* An initial 'indeterminate' state becomes 'checked' on the first call.
|
|
507
|
+
*
|
|
508
|
+
* If the input element has the class 'wb-tristate' assigned, the sequence is:<br>
|
|
509
|
+
* 'indeterminate' -> 'checked' -> 'unchecked' -> 'indeterminate' -> ...<br>
|
|
510
|
+
* Otherwise we toggle like <br>
|
|
511
|
+
* 'checked' -> 'unchecked' -> 'checked' -> ...
|
|
481
512
|
*/
|
|
482
513
|
function toggleCheckbox(element, value, tristate) {
|
|
483
514
|
const input = elemFromSelector(element);
|
|
@@ -535,6 +566,7 @@ function toSet(val) {
|
|
|
535
566
|
}
|
|
536
567
|
throw new Error("Cannot convert to Set<string>: " + val);
|
|
537
568
|
}
|
|
569
|
+
/**Return a canonical string representation for an object's type (e.g. 'array', 'number', ...) */
|
|
538
570
|
function type(obj) {
|
|
539
571
|
return Object.prototype.toString
|
|
540
572
|
.call(obj)
|
|
@@ -561,6 +593,7 @@ var util = /*#__PURE__*/Object.freeze({
|
|
|
561
593
|
setValueToElem: setValueToElem,
|
|
562
594
|
elemFromHtml: elemFromHtml,
|
|
563
595
|
elemFromSelector: elemFromSelector,
|
|
596
|
+
eventTargetFromSelector: eventTargetFromSelector,
|
|
564
597
|
eventToString: eventToString,
|
|
565
598
|
extend: extend,
|
|
566
599
|
isArray: isArray,
|
|
@@ -581,7 +614,7 @@ var util = /*#__PURE__*/Object.freeze({
|
|
|
581
614
|
/*!
|
|
582
615
|
* Wunderbaum - common
|
|
583
616
|
* Copyright (c) 2021-2022, Martin Wendt. Released under the MIT license.
|
|
584
|
-
* v0.0.
|
|
617
|
+
* v0.0.3, Mon, 18 Apr 2022 11:52:44 GMT (https://github.com/mar10/wunderbaum)
|
|
585
618
|
*/
|
|
586
619
|
const DEFAULT_DEBUGLEVEL = 4; // Replaced by rollup script
|
|
587
620
|
const ROW_HEIGHT = 22;
|
|
@@ -595,6 +628,8 @@ var ChangeType;
|
|
|
595
628
|
ChangeType["row"] = "row";
|
|
596
629
|
ChangeType["structure"] = "structure";
|
|
597
630
|
ChangeType["status"] = "status";
|
|
631
|
+
ChangeType["vscroll"] = "vscroll";
|
|
632
|
+
ChangeType["header"] = "header";
|
|
598
633
|
})(ChangeType || (ChangeType = {}));
|
|
599
634
|
var NodeStatusType;
|
|
600
635
|
(function (NodeStatusType) {
|
|
@@ -667,6 +702,8 @@ const KEY_TO_ACTION_DICT = {
|
|
|
667
702
|
Home: "firstCol",
|
|
668
703
|
"Control+End": "last",
|
|
669
704
|
"Control+Home": "first",
|
|
705
|
+
"Meta+ArrowDown": "last",
|
|
706
|
+
"Meta+ArrowUp": "first",
|
|
670
707
|
"*": "expandAll",
|
|
671
708
|
Multiply: "expandAll",
|
|
672
709
|
PageDown: "pageDown",
|
|
@@ -693,7 +730,7 @@ function makeNodeTitleStartMatcher(s) {
|
|
|
693
730
|
/*!
|
|
694
731
|
* Wunderbaum - wb_extension_base
|
|
695
732
|
* Copyright (c) 2021-2022, Martin Wendt. Released under the MIT license.
|
|
696
|
-
* v0.0.
|
|
733
|
+
* v0.0.3, Mon, 18 Apr 2022 11:52:44 GMT (https://github.com/mar10/wunderbaum)
|
|
697
734
|
*/
|
|
698
735
|
class WunderbaumExtension {
|
|
699
736
|
constructor(tree, id, defaults) {
|
|
@@ -1048,7 +1085,7 @@ function throttle(func, wait = 0, options = {}) {
|
|
|
1048
1085
|
/*!
|
|
1049
1086
|
* Wunderbaum - ext-filter
|
|
1050
1087
|
* Copyright (c) 2021-2022, Martin Wendt. Released under the MIT license.
|
|
1051
|
-
* v0.0.
|
|
1088
|
+
* v0.0.3, Mon, 18 Apr 2022 11:52:44 GMT (https://github.com/mar10/wunderbaum)
|
|
1052
1089
|
*/
|
|
1053
1090
|
const START_MARKER = "\uFFF7";
|
|
1054
1091
|
const END_MARKER = "\uFFF8";
|
|
@@ -1087,7 +1124,9 @@ class FilterExtension extends WunderbaumExtension {
|
|
|
1087
1124
|
});
|
|
1088
1125
|
}
|
|
1089
1126
|
_applyFilterImpl(filter, branchMode, _opts) {
|
|
1090
|
-
let match, temp, start = Date.now(), count = 0, tree = this.tree, treeOpts = tree.options,
|
|
1127
|
+
let match, temp, start = Date.now(), count = 0, tree = this.tree, treeOpts = tree.options,
|
|
1128
|
+
// escapeTitles = treeOpts.escapeTitles,
|
|
1129
|
+
prevAutoCollapse = treeOpts.autoCollapse, opts = extend({}, treeOpts.filter, _opts), hideMode = opts.mode === "hide", leavesOnly = !!opts.leavesOnly && !branchMode;
|
|
1091
1130
|
// Default to 'match title substring (case insensitive)'
|
|
1092
1131
|
if (typeof filter === "string") {
|
|
1093
1132
|
if (filter === "") {
|
|
@@ -1120,37 +1159,36 @@ class FilterExtension extends WunderbaumExtension {
|
|
|
1120
1159
|
if (!node.title) {
|
|
1121
1160
|
return false;
|
|
1122
1161
|
}
|
|
1123
|
-
let text = escapeTitles ? node.title : extractHtmlText(node.title);
|
|
1162
|
+
// let text = escapeTitles ? node.title : extractHtmlText(node.title);
|
|
1163
|
+
let text = node.title;
|
|
1124
1164
|
// `.match` instead of `.test` to get the capture groups
|
|
1125
1165
|
let res = text.match(re);
|
|
1126
1166
|
if (res && opts.highlight) {
|
|
1127
|
-
if (escapeTitles) {
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
}
|
|
1131
|
-
else {
|
|
1132
|
-
// #740: we must not apply the marks to escaped entity names, e.g. `"`
|
|
1133
|
-
// Use some exotic characters to mark matches:
|
|
1134
|
-
temp = text.replace(reHighlight, function (s) {
|
|
1135
|
-
return START_MARKER + s + END_MARKER;
|
|
1136
|
-
});
|
|
1137
|
-
}
|
|
1138
|
-
// now we can escape the title...
|
|
1139
|
-
node.titleWithHighlight = escapeHtml(temp)
|
|
1140
|
-
// ... and finally insert the desired `<mark>` tags
|
|
1141
|
-
.replace(RE_START_MARKER, "<mark>")
|
|
1142
|
-
.replace(RE_END_MARTKER, "</mark>");
|
|
1167
|
+
// if (escapeTitles) {
|
|
1168
|
+
if (opts.fuzzy) {
|
|
1169
|
+
temp = _markFuzzyMatchedChars(text, res, true);
|
|
1143
1170
|
}
|
|
1144
1171
|
else {
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
return "<mark>" + s + "</mark>";
|
|
1151
|
-
});
|
|
1152
|
-
}
|
|
1172
|
+
// #740: we must not apply the marks to escaped entity names, e.g. `"`
|
|
1173
|
+
// Use some exotic characters to mark matches:
|
|
1174
|
+
temp = text.replace(reHighlight, function (s) {
|
|
1175
|
+
return START_MARKER + s + END_MARKER;
|
|
1176
|
+
});
|
|
1153
1177
|
}
|
|
1178
|
+
// now we can escape the title...
|
|
1179
|
+
node.titleWithHighlight = escapeHtml(temp)
|
|
1180
|
+
// ... and finally insert the desired `<mark>` tags
|
|
1181
|
+
.replace(RE_START_MARKER, "<mark>")
|
|
1182
|
+
.replace(RE_END_MARTKER, "</mark>");
|
|
1183
|
+
// } else {
|
|
1184
|
+
// if (opts.fuzzy) {
|
|
1185
|
+
// node.titleWithHighlight = _markFuzzyMatchedChars(text, res);
|
|
1186
|
+
// } else {
|
|
1187
|
+
// node.titleWithHighlight = text.replace(reHighlight, function (s) {
|
|
1188
|
+
// return "<mark>" + s + "</mark>";
|
|
1189
|
+
// });
|
|
1190
|
+
// }
|
|
1191
|
+
// }
|
|
1154
1192
|
// node.debug("filter", escapeTitles, text, node.titleWithHighlight);
|
|
1155
1193
|
}
|
|
1156
1194
|
return !!res;
|
|
@@ -1236,8 +1274,6 @@ class FilterExtension extends WunderbaumExtension {
|
|
|
1236
1274
|
}
|
|
1237
1275
|
/**
|
|
1238
1276
|
* [ext-filter] Re-apply current filter.
|
|
1239
|
-
*
|
|
1240
|
-
* @requires jquery.fancytree.filter.js
|
|
1241
1277
|
*/
|
|
1242
1278
|
updateFilter() {
|
|
1243
1279
|
let tree = this.tree;
|
|
@@ -1252,14 +1288,11 @@ class FilterExtension extends WunderbaumExtension {
|
|
|
1252
1288
|
}
|
|
1253
1289
|
/**
|
|
1254
1290
|
* [ext-filter] Reset the filter.
|
|
1255
|
-
*
|
|
1256
|
-
* @alias Fancytree#clearFilter
|
|
1257
|
-
* @requires jquery.fancytree.filter.js
|
|
1258
1291
|
*/
|
|
1259
1292
|
clearFilter() {
|
|
1260
|
-
let tree = this.tree
|
|
1293
|
+
let tree = this.tree;
|
|
1261
1294
|
// statusNode = tree.root.findDirectChild(KEY_NODATA),
|
|
1262
|
-
escapeTitles = tree.options.escapeTitles;
|
|
1295
|
+
// escapeTitles = tree.options.escapeTitles;
|
|
1263
1296
|
// enhanceTitle = tree.options.enhanceTitle,
|
|
1264
1297
|
tree.enableUpdate(false);
|
|
1265
1298
|
// if (statusNode) {
|
|
@@ -1273,12 +1306,11 @@ class FilterExtension extends WunderbaumExtension {
|
|
|
1273
1306
|
if (node.match && node._rowElem) {
|
|
1274
1307
|
// #491, #601
|
|
1275
1308
|
let titleElem = node._rowElem.querySelector("span.wb-title");
|
|
1276
|
-
if (escapeTitles) {
|
|
1277
|
-
|
|
1278
|
-
}
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
}
|
|
1309
|
+
// if (escapeTitles) {
|
|
1310
|
+
titleElem.textContent = node.title;
|
|
1311
|
+
// } else {
|
|
1312
|
+
// titleElem.innerHTML = node.title;
|
|
1313
|
+
// }
|
|
1282
1314
|
node._callEvent("enhanceTitle", { titleElem: titleElem });
|
|
1283
1315
|
}
|
|
1284
1316
|
delete node.match;
|
|
@@ -1314,7 +1346,7 @@ class FilterExtension extends WunderbaumExtension {
|
|
|
1314
1346
|
* @param {string} text
|
|
1315
1347
|
* @param {RegExpMatchArray} matches
|
|
1316
1348
|
*/
|
|
1317
|
-
function _markFuzzyMatchedChars(text, matches, escapeTitles =
|
|
1349
|
+
function _markFuzzyMatchedChars(text, matches, escapeTitles = true) {
|
|
1318
1350
|
let matchingIndices = [];
|
|
1319
1351
|
// get the indices of matched characters (Iterate through `RegExpMatchArray`)
|
|
1320
1352
|
for (let _matchingArrIdx = 1; _matchingArrIdx < matches.length; _matchingArrIdx++) {
|
|
@@ -1329,7 +1361,7 @@ function _markFuzzyMatchedChars(text, matches, escapeTitles = false) {
|
|
|
1329
1361
|
// Map each `text` char to its position and store in `textPoses`.
|
|
1330
1362
|
let textPoses = text.split("");
|
|
1331
1363
|
if (escapeTitles) {
|
|
1332
|
-
// If escaping the title, then wrap the
|
|
1364
|
+
// If escaping the title, then wrap the matching char within exotic chars
|
|
1333
1365
|
matchingIndices.forEach(function (v) {
|
|
1334
1366
|
textPoses[v] = START_MARKER + textPoses[v] + END_MARKER;
|
|
1335
1367
|
});
|
|
@@ -1347,7 +1379,7 @@ function _markFuzzyMatchedChars(text, matches, escapeTitles = false) {
|
|
|
1347
1379
|
/*!
|
|
1348
1380
|
* Wunderbaum - ext-keynav
|
|
1349
1381
|
* Copyright (c) 2021-2022, Martin Wendt. Released under the MIT license.
|
|
1350
|
-
* v0.0.
|
|
1382
|
+
* v0.0.3, Mon, 18 Apr 2022 11:52:44 GMT (https://github.com/mar10/wunderbaum)
|
|
1351
1383
|
*/
|
|
1352
1384
|
class KeynavExtension extends WunderbaumExtension {
|
|
1353
1385
|
constructor(tree) {
|
|
@@ -1414,7 +1446,7 @@ class KeynavExtension extends WunderbaumExtension {
|
|
|
1414
1446
|
eventName = "Add"; // expand
|
|
1415
1447
|
}
|
|
1416
1448
|
else if (navModeOption === NavigationModeOption.startRow) {
|
|
1417
|
-
tree.
|
|
1449
|
+
tree.setNavigationMode(NavigationMode.cellNav);
|
|
1418
1450
|
return;
|
|
1419
1451
|
}
|
|
1420
1452
|
break;
|
|
@@ -1453,6 +1485,8 @@ class KeynavExtension extends WunderbaumExtension {
|
|
|
1453
1485
|
case "Home":
|
|
1454
1486
|
case "Control+End":
|
|
1455
1487
|
case "Control+Home":
|
|
1488
|
+
case "Meta+ArrowDown":
|
|
1489
|
+
case "Meta+ArrowUp":
|
|
1456
1490
|
case "PageDown":
|
|
1457
1491
|
case "PageUp":
|
|
1458
1492
|
node.navigate(eventName, { activate: activate, event: event });
|
|
@@ -1484,11 +1518,11 @@ class KeynavExtension extends WunderbaumExtension {
|
|
|
1484
1518
|
break;
|
|
1485
1519
|
case "Escape":
|
|
1486
1520
|
if (tree.navMode === NavigationMode.cellEdit) {
|
|
1487
|
-
tree.
|
|
1521
|
+
tree.setNavigationMode(NavigationMode.cellNav);
|
|
1488
1522
|
handled = true;
|
|
1489
1523
|
}
|
|
1490
1524
|
else if (tree.navMode === NavigationMode.cellNav) {
|
|
1491
|
-
tree.
|
|
1525
|
+
tree.setNavigationMode(NavigationMode.row);
|
|
1492
1526
|
handled = true;
|
|
1493
1527
|
}
|
|
1494
1528
|
break;
|
|
@@ -1498,7 +1532,7 @@ class KeynavExtension extends WunderbaumExtension {
|
|
|
1498
1532
|
handled = true;
|
|
1499
1533
|
}
|
|
1500
1534
|
else if (navModeOption !== NavigationModeOption.cell) {
|
|
1501
|
-
tree.
|
|
1535
|
+
tree.setNavigationMode(NavigationMode.row);
|
|
1502
1536
|
handled = true;
|
|
1503
1537
|
}
|
|
1504
1538
|
break;
|
|
@@ -1515,6 +1549,8 @@ class KeynavExtension extends WunderbaumExtension {
|
|
|
1515
1549
|
case "Home":
|
|
1516
1550
|
case "Control+End":
|
|
1517
1551
|
case "Control+Home":
|
|
1552
|
+
case "Meta+ArrowDown":
|
|
1553
|
+
case "Meta+ArrowUp":
|
|
1518
1554
|
case "PageDown":
|
|
1519
1555
|
case "PageUp":
|
|
1520
1556
|
node.navigate(eventName, { activate: activate, event: event });
|
|
@@ -1533,7 +1569,7 @@ class KeynavExtension extends WunderbaumExtension {
|
|
|
1533
1569
|
/*!
|
|
1534
1570
|
* Wunderbaum - ext-logger
|
|
1535
1571
|
* Copyright (c) 2021-2022, Martin Wendt. Released under the MIT license.
|
|
1536
|
-
* v0.0.
|
|
1572
|
+
* v0.0.3, Mon, 18 Apr 2022 11:52:44 GMT (https://github.com/mar10/wunderbaum)
|
|
1537
1573
|
*/
|
|
1538
1574
|
class LoggerExtension extends WunderbaumExtension {
|
|
1539
1575
|
constructor(tree) {
|
|
@@ -1573,19 +1609,9 @@ class LoggerExtension extends WunderbaumExtension {
|
|
|
1573
1609
|
/*!
|
|
1574
1610
|
* Wunderbaum - ext-dnd
|
|
1575
1611
|
* Copyright (c) 2021-2022, Martin Wendt. Released under the MIT license.
|
|
1576
|
-
* v0.0.
|
|
1612
|
+
* v0.0.3, Mon, 18 Apr 2022 11:52:44 GMT (https://github.com/mar10/wunderbaum)
|
|
1577
1613
|
*/
|
|
1578
|
-
const nodeMimeType = "application/x-
|
|
1579
|
-
// type AllowedDropRegionType =
|
|
1580
|
-
// | "after"
|
|
1581
|
-
// | "afterBefore"
|
|
1582
|
-
// // | "afterBeforeOver" // == all == true
|
|
1583
|
-
// | "afterOver"
|
|
1584
|
-
// | "all" // == true
|
|
1585
|
-
// | "before"
|
|
1586
|
-
// | "beforeOver"
|
|
1587
|
-
// | "none" // == false == "" == null
|
|
1588
|
-
// | "over"; // == "child"
|
|
1614
|
+
const nodeMimeType = "application/x-wunderbaum-node";
|
|
1589
1615
|
class DndExtension extends WunderbaumExtension {
|
|
1590
1616
|
constructor(tree) {
|
|
1591
1617
|
super(tree, "dnd", {
|
|
@@ -1848,10 +1874,181 @@ class DndExtension extends WunderbaumExtension {
|
|
|
1848
1874
|
}
|
|
1849
1875
|
}
|
|
1850
1876
|
|
|
1877
|
+
/*!
|
|
1878
|
+
* Wunderbaum - drag_observer
|
|
1879
|
+
* Copyright (c) 2021-2022, Martin Wendt. Released under the MIT license.
|
|
1880
|
+
* v0.0.3, Mon, 18 Apr 2022 11:52:44 GMT (https://github.com/mar10/wunderbaum)
|
|
1881
|
+
*/
|
|
1882
|
+
/**
|
|
1883
|
+
* Convert mouse- and touch events to 'dragstart', 'drag', and 'dragstop'.
|
|
1884
|
+
*/
|
|
1885
|
+
class DragObserver {
|
|
1886
|
+
constructor(opts) {
|
|
1887
|
+
this.start = {
|
|
1888
|
+
x: 0,
|
|
1889
|
+
y: 0,
|
|
1890
|
+
altKey: false,
|
|
1891
|
+
ctrlKey: false,
|
|
1892
|
+
metaKey: false,
|
|
1893
|
+
shiftKey: false,
|
|
1894
|
+
};
|
|
1895
|
+
this.dragElem = null;
|
|
1896
|
+
this.dragging = false;
|
|
1897
|
+
// TODO: touch events
|
|
1898
|
+
this.events = ["mousedown", "mouseup", "mousemove", "keydown"];
|
|
1899
|
+
assert(opts.root);
|
|
1900
|
+
this.opts = extend({ thresh: 5 }, opts);
|
|
1901
|
+
this.root = opts.root;
|
|
1902
|
+
this._handler = this.handleEvent.bind(this);
|
|
1903
|
+
this.events.forEach((type) => {
|
|
1904
|
+
this.root.addEventListener(type, this._handler);
|
|
1905
|
+
});
|
|
1906
|
+
}
|
|
1907
|
+
/** Unregister all event listeners. */
|
|
1908
|
+
disconnect() {
|
|
1909
|
+
this.events.forEach((type) => {
|
|
1910
|
+
this.root.removeEventListener(type, this._handler);
|
|
1911
|
+
});
|
|
1912
|
+
}
|
|
1913
|
+
getDragElem() {
|
|
1914
|
+
return this.dragElem;
|
|
1915
|
+
}
|
|
1916
|
+
isDragging() {
|
|
1917
|
+
return this.dragging;
|
|
1918
|
+
}
|
|
1919
|
+
stopDrag(cb_event) {
|
|
1920
|
+
if (this.dragging && this.opts.dragstop && cb_event) {
|
|
1921
|
+
cb_event.type = "dragstop";
|
|
1922
|
+
this.opts.dragstop(cb_event);
|
|
1923
|
+
}
|
|
1924
|
+
this.dragElem = null;
|
|
1925
|
+
this.dragging = false;
|
|
1926
|
+
}
|
|
1927
|
+
handleEvent(e) {
|
|
1928
|
+
const type = e.type;
|
|
1929
|
+
const opts = this.opts;
|
|
1930
|
+
const cb_event = {
|
|
1931
|
+
type: e.type,
|
|
1932
|
+
event: e,
|
|
1933
|
+
dragElem: this.dragElem,
|
|
1934
|
+
dx: e.pageX - this.start.x,
|
|
1935
|
+
dy: e.pageY - this.start.y,
|
|
1936
|
+
apply: undefined,
|
|
1937
|
+
};
|
|
1938
|
+
switch (type) {
|
|
1939
|
+
case "keydown":
|
|
1940
|
+
this.stopDrag(cb_event);
|
|
1941
|
+
break;
|
|
1942
|
+
case "mousedown":
|
|
1943
|
+
if (this.dragElem) {
|
|
1944
|
+
this.stopDrag(cb_event);
|
|
1945
|
+
break;
|
|
1946
|
+
}
|
|
1947
|
+
if (opts.selector) {
|
|
1948
|
+
let elem = e.target;
|
|
1949
|
+
if (elem.matches(opts.selector)) {
|
|
1950
|
+
this.dragElem = elem;
|
|
1951
|
+
}
|
|
1952
|
+
else {
|
|
1953
|
+
elem = elem.closest(opts.selector);
|
|
1954
|
+
if (elem) {
|
|
1955
|
+
this.dragElem = elem;
|
|
1956
|
+
}
|
|
1957
|
+
else {
|
|
1958
|
+
break; // no event delegation selector matched
|
|
1959
|
+
}
|
|
1960
|
+
}
|
|
1961
|
+
}
|
|
1962
|
+
this.start.x = e.pageX;
|
|
1963
|
+
this.start.y = e.pageY;
|
|
1964
|
+
this.start.altKey = e.altKey;
|
|
1965
|
+
this.start.ctrlKey = e.ctrlKey;
|
|
1966
|
+
this.start.metaKey = e.metaKey;
|
|
1967
|
+
this.start.shiftKey = e.shiftKey;
|
|
1968
|
+
break;
|
|
1969
|
+
case "mousemove":
|
|
1970
|
+
// TODO: debounce/throttle?
|
|
1971
|
+
// TODO: horizontal mode: ignore if dx unchanged
|
|
1972
|
+
if (!this.dragElem) {
|
|
1973
|
+
break;
|
|
1974
|
+
}
|
|
1975
|
+
if (!this.dragging) {
|
|
1976
|
+
if (opts.thresh) {
|
|
1977
|
+
const dist2 = cb_event.dx * cb_event.dx + cb_event.dy * cb_event.dy;
|
|
1978
|
+
if (dist2 < opts.thresh * opts.thresh) {
|
|
1979
|
+
break;
|
|
1980
|
+
}
|
|
1981
|
+
}
|
|
1982
|
+
cb_event.type = "dragstart";
|
|
1983
|
+
if (opts.dragstart(cb_event) === false) {
|
|
1984
|
+
this.stopDrag(cb_event);
|
|
1985
|
+
break;
|
|
1986
|
+
}
|
|
1987
|
+
this.dragging = true;
|
|
1988
|
+
}
|
|
1989
|
+
if (this.dragging && this.opts.drag) {
|
|
1990
|
+
cb_event.type = "drag";
|
|
1991
|
+
this.opts.drag(cb_event);
|
|
1992
|
+
}
|
|
1993
|
+
break;
|
|
1994
|
+
case "mouseup":
|
|
1995
|
+
if (!this.dragging) {
|
|
1996
|
+
this.stopDrag(cb_event);
|
|
1997
|
+
break;
|
|
1998
|
+
}
|
|
1999
|
+
if (e.button === 0) {
|
|
2000
|
+
cb_event.apply = true;
|
|
2001
|
+
}
|
|
2002
|
+
else {
|
|
2003
|
+
cb_event.apply = false;
|
|
2004
|
+
}
|
|
2005
|
+
this.stopDrag(cb_event);
|
|
2006
|
+
break;
|
|
2007
|
+
}
|
|
2008
|
+
}
|
|
2009
|
+
}
|
|
2010
|
+
|
|
2011
|
+
/*!
|
|
2012
|
+
* Wunderbaum - ext-grid
|
|
2013
|
+
* Copyright (c) 2021-2022, Martin Wendt. Released under the MIT license.
|
|
2014
|
+
* v0.0.3, Mon, 18 Apr 2022 11:52:44 GMT (https://github.com/mar10/wunderbaum)
|
|
2015
|
+
*/
|
|
2016
|
+
class GridExtension extends WunderbaumExtension {
|
|
2017
|
+
constructor(tree) {
|
|
2018
|
+
super(tree, "grid", {
|
|
2019
|
+
// throttle: 200,
|
|
2020
|
+
});
|
|
2021
|
+
this.observer = new DragObserver({
|
|
2022
|
+
root: window.document,
|
|
2023
|
+
selector: "span.wb-col-resizer",
|
|
2024
|
+
thresh: 4,
|
|
2025
|
+
// throttle: 400,
|
|
2026
|
+
dragstart: (e) => {
|
|
2027
|
+
return this.tree.element.contains(e.dragElem);
|
|
2028
|
+
},
|
|
2029
|
+
drag: (e) => {
|
|
2030
|
+
// TODO: throttle
|
|
2031
|
+
return this.handleDrag(e);
|
|
2032
|
+
},
|
|
2033
|
+
dragstop: (e) => {
|
|
2034
|
+
return this.handleDrag(e);
|
|
2035
|
+
},
|
|
2036
|
+
});
|
|
2037
|
+
}
|
|
2038
|
+
init() {
|
|
2039
|
+
super.init();
|
|
2040
|
+
}
|
|
2041
|
+
handleDrag(e) {
|
|
2042
|
+
const info = Wunderbaum.getEventInfo(e.event);
|
|
2043
|
+
// this.tree.options.
|
|
2044
|
+
this.tree.log(`${e.type}(${e.dx})`, e, info);
|
|
2045
|
+
}
|
|
2046
|
+
}
|
|
2047
|
+
|
|
1851
2048
|
/*!
|
|
1852
2049
|
* Wunderbaum - deferred
|
|
1853
2050
|
* Copyright (c) 2021-2022, Martin Wendt. Released under the MIT license.
|
|
1854
|
-
* v0.0.
|
|
2051
|
+
* v0.0.3, Mon, 18 Apr 2022 11:52:44 GMT (https://github.com/mar10/wunderbaum)
|
|
1855
2052
|
*/
|
|
1856
2053
|
/**
|
|
1857
2054
|
* Deferred is a ES6 Promise, that exposes the resolve() and reject()` method.
|
|
@@ -1894,7 +2091,7 @@ class Deferred {
|
|
|
1894
2091
|
/*!
|
|
1895
2092
|
* Wunderbaum - wunderbaum_node
|
|
1896
2093
|
* Copyright (c) 2021-2022, Martin Wendt. Released under the MIT license.
|
|
1897
|
-
* v0.0.
|
|
2094
|
+
* v0.0.3, Mon, 18 Apr 2022 11:52:44 GMT (https://github.com/mar10/wunderbaum)
|
|
1898
2095
|
*/
|
|
1899
2096
|
/** Top-level properties that can be passed with `data`. */
|
|
1900
2097
|
const NODE_PROPS = new Set([
|
|
@@ -1931,15 +2128,31 @@ const NODE_ATTRS = new Set([
|
|
|
1931
2128
|
"unselectableIgnore",
|
|
1932
2129
|
"unselectableStatus",
|
|
1933
2130
|
]);
|
|
2131
|
+
/**
|
|
2132
|
+
* A single tree node.
|
|
2133
|
+
*
|
|
2134
|
+
* **NOTE:** <br>
|
|
2135
|
+
* Generally you should not modify properties directly, since this may break
|
|
2136
|
+
* the internal bookkeeping.
|
|
2137
|
+
*/
|
|
1934
2138
|
class WunderbaumNode {
|
|
1935
2139
|
constructor(tree, parent, data) {
|
|
1936
2140
|
var _a, _b;
|
|
2141
|
+
/** Reference key. Unlike {@link key}, a `refKey` may occur multiple
|
|
2142
|
+
* times within a tree (in this case we have 'clone nodes').
|
|
2143
|
+
* @see Use {@link setKey} to modify.
|
|
2144
|
+
*/
|
|
1937
2145
|
this.refKey = undefined;
|
|
1938
2146
|
this.children = null;
|
|
1939
2147
|
this.lazy = false;
|
|
2148
|
+
/** Expansion state.
|
|
2149
|
+
* @see {@link isExpandable}, {@link isExpanded}, {@link setExpanded}. */
|
|
1940
2150
|
this.expanded = false;
|
|
2151
|
+
/** Selection state.
|
|
2152
|
+
* @see {@link isSelected}, {@link setSelected}. */
|
|
1941
2153
|
this.selected = false;
|
|
1942
|
-
/** Additional classes added to `div.wb-row`.
|
|
2154
|
+
/** Additional classes added to `div.wb-row`.
|
|
2155
|
+
* @see {@link addClass}, {@link removeClass}, {@link toggleClass}. */
|
|
1943
2156
|
this.extraClasses = new Set();
|
|
1944
2157
|
/** Custom data that was passed to the constructor */
|
|
1945
2158
|
this.data = {};
|
|
@@ -2066,7 +2279,7 @@ class WunderbaumNode {
|
|
|
2066
2279
|
// this.fixSelection3FromEndNodes();
|
|
2067
2280
|
// }
|
|
2068
2281
|
// this.triggerModifyChild("add", nodeList.length === 1 ? nodeList[0] : null);
|
|
2069
|
-
this.tree.setModified(ChangeType.structure
|
|
2282
|
+
this.tree.setModified(ChangeType.structure);
|
|
2070
2283
|
return nodeList[0];
|
|
2071
2284
|
}
|
|
2072
2285
|
finally {
|
|
@@ -2105,7 +2318,8 @@ class WunderbaumNode {
|
|
|
2105
2318
|
}
|
|
2106
2319
|
/**
|
|
2107
2320
|
* Apply a modification (or navigation) operation.
|
|
2108
|
-
*
|
|
2321
|
+
*
|
|
2322
|
+
* @see {@link Wunderbaum.applyCommand}
|
|
2109
2323
|
*/
|
|
2110
2324
|
applyCommand(cmd, opts) {
|
|
2111
2325
|
return this.tree.applyCommand(cmd, this, opts);
|
|
@@ -2198,8 +2412,7 @@ class WunderbaumNode {
|
|
|
2198
2412
|
}
|
|
2199
2413
|
/** Find a node relative to self.
|
|
2200
2414
|
*
|
|
2201
|
-
* @
|
|
2202
|
-
* or a keyword ('down', 'first', 'last', 'left', 'parent', 'right', 'up').
|
|
2415
|
+
* @see {@link Wunderbaum.findRelatedNode|tree.findRelatedNode()}
|
|
2203
2416
|
*/
|
|
2204
2417
|
findRelatedNode(where, includeHidden = false) {
|
|
2205
2418
|
return this.tree.findRelatedNode(this, where, includeHidden);
|
|
@@ -2500,7 +2713,7 @@ class WunderbaumNode {
|
|
|
2500
2713
|
assert(!this.parent);
|
|
2501
2714
|
tree.columns = data.columns;
|
|
2502
2715
|
delete data.columns;
|
|
2503
|
-
tree.
|
|
2716
|
+
tree.updateColumns({ calculateCols: false });
|
|
2504
2717
|
}
|
|
2505
2718
|
this._loadSourceObject(data);
|
|
2506
2719
|
}
|
|
@@ -2536,19 +2749,20 @@ class WunderbaumNode {
|
|
|
2536
2749
|
this.setStatus(NodeStatusType.ok);
|
|
2537
2750
|
return;
|
|
2538
2751
|
}
|
|
2539
|
-
assert(isArray(source) || (source && source.url), "The lazyLoad event must return a node list, `{url: ...}
|
|
2752
|
+
assert(isArray(source) || (source && source.url), "The lazyLoad event must return a node list, `{url: ...}`, or false.");
|
|
2540
2753
|
await this.load(source); // also calls setStatus('ok')
|
|
2541
2754
|
if (wasExpanded) {
|
|
2542
2755
|
this.expanded = true;
|
|
2543
|
-
this.tree.
|
|
2756
|
+
this.tree.setModified(ChangeType.structure);
|
|
2544
2757
|
}
|
|
2545
2758
|
else {
|
|
2546
|
-
this.
|
|
2759
|
+
this.setModified(); // Fix expander icon to 'loaded'
|
|
2547
2760
|
}
|
|
2548
2761
|
}
|
|
2549
2762
|
catch (e) {
|
|
2763
|
+
this.logError("Error during loadLazy()", e);
|
|
2764
|
+
this._callEvent("error", { error: e });
|
|
2550
2765
|
this.setStatus(NodeStatusType.error, "" + e);
|
|
2551
|
-
// } finally {
|
|
2552
2766
|
}
|
|
2553
2767
|
return;
|
|
2554
2768
|
}
|
|
@@ -2696,7 +2910,7 @@ class WunderbaumNode {
|
|
|
2696
2910
|
n.tree = targetNode.tree;
|
|
2697
2911
|
}, true);
|
|
2698
2912
|
}
|
|
2699
|
-
tree.
|
|
2913
|
+
tree.setModified(ChangeType.structure);
|
|
2700
2914
|
// TODO: fix selection state
|
|
2701
2915
|
// TODO: fix active state
|
|
2702
2916
|
}
|
|
@@ -2716,31 +2930,33 @@ class WunderbaumNode {
|
|
|
2716
2930
|
// Allow to pass 'ArrowLeft' instead of 'left'
|
|
2717
2931
|
where = KEY_TO_ACTION_DICT[where] || where;
|
|
2718
2932
|
// Otherwise activate or focus the related node
|
|
2719
|
-
|
|
2720
|
-
if (node) {
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
node.
|
|
2727
|
-
if ((options === null || options === void 0 ? void 0 : options.activate) === false) {
|
|
2728
|
-
return Promise.resolve(this);
|
|
2729
|
-
}
|
|
2730
|
-
return node.setActive(true, { event: options === null || options === void 0 ? void 0 : options.event });
|
|
2933
|
+
const node = this.findRelatedNode(where);
|
|
2934
|
+
if (!node) {
|
|
2935
|
+
this.logWarn(`Could not find related node '${where}'.`);
|
|
2936
|
+
return Promise.resolve(this);
|
|
2937
|
+
}
|
|
2938
|
+
// setFocus/setActive will scroll later (if autoScroll is specified)
|
|
2939
|
+
try {
|
|
2940
|
+
node.makeVisible({ scrollIntoView: false });
|
|
2731
2941
|
}
|
|
2732
|
-
|
|
2733
|
-
|
|
2942
|
+
catch (e) { } // #272
|
|
2943
|
+
node.setFocus();
|
|
2944
|
+
if ((options === null || options === void 0 ? void 0 : options.activate) === false) {
|
|
2945
|
+
return Promise.resolve(this);
|
|
2946
|
+
}
|
|
2947
|
+
return node.setActive(true, { event: options === null || options === void 0 ? void 0 : options.event });
|
|
2734
2948
|
}
|
|
2735
2949
|
/** Delete this node and all descendants. */
|
|
2736
2950
|
remove() {
|
|
2737
2951
|
const tree = this.tree;
|
|
2738
2952
|
const pos = this.parent.children.indexOf(this);
|
|
2953
|
+
this.triggerModify("remove");
|
|
2739
2954
|
this.parent.children.splice(pos, 1);
|
|
2740
2955
|
this.visit((n) => {
|
|
2741
2956
|
n.removeMarkup();
|
|
2742
2957
|
tree._unregisterNode(n);
|
|
2743
2958
|
}, true);
|
|
2959
|
+
tree.setModified(ChangeType.structure);
|
|
2744
2960
|
}
|
|
2745
2961
|
/** Remove all descendants of this node. */
|
|
2746
2962
|
removeChildren() {
|
|
@@ -2772,7 +2988,7 @@ class WunderbaumNode {
|
|
|
2772
2988
|
if (!this.isRootNode()) {
|
|
2773
2989
|
this.expanded = false;
|
|
2774
2990
|
}
|
|
2775
|
-
this.tree.
|
|
2991
|
+
this.tree.setModified(ChangeType.structure);
|
|
2776
2992
|
}
|
|
2777
2993
|
/** Remove all HTML markup from the DOM. */
|
|
2778
2994
|
removeMarkup() {
|
|
@@ -2865,6 +3081,7 @@ class WunderbaumNode {
|
|
|
2865
3081
|
const activeColIdx = tree.navMode === NavigationMode.row ? null : tree.activeColIdx;
|
|
2866
3082
|
// let colElems: HTMLElement[];
|
|
2867
3083
|
const isNew = !rowDiv;
|
|
3084
|
+
assert(!isNew || (opts && opts.after), "opts.after expected, unless updating");
|
|
2868
3085
|
assert(!this.isRootNode());
|
|
2869
3086
|
//
|
|
2870
3087
|
let rowClasses = ["wb-row"];
|
|
@@ -2916,7 +3133,7 @@ class WunderbaumNode {
|
|
|
2916
3133
|
nodeElem.appendChild(elem);
|
|
2917
3134
|
ofsTitlePx += ICON_WIDTH;
|
|
2918
3135
|
}
|
|
2919
|
-
if (level > treeOptions.minExpandLevel) {
|
|
3136
|
+
if (!treeOptions.minExpandLevel || level > treeOptions.minExpandLevel) {
|
|
2920
3137
|
expanderSpan = document.createElement("i");
|
|
2921
3138
|
nodeElem.appendChild(expanderSpan);
|
|
2922
3139
|
ofsTitlePx += ICON_WIDTH;
|
|
@@ -3007,11 +3224,11 @@ class WunderbaumNode {
|
|
|
3007
3224
|
if (this.titleWithHighlight) {
|
|
3008
3225
|
titleSpan.innerHTML = this.titleWithHighlight;
|
|
3009
3226
|
}
|
|
3010
|
-
else if (tree.options.escapeTitles) {
|
|
3011
|
-
titleSpan.textContent = this.title;
|
|
3012
|
-
}
|
|
3013
3227
|
else {
|
|
3014
|
-
|
|
3228
|
+
// } else if (tree.options.escapeTitles) {
|
|
3229
|
+
titleSpan.textContent = this.title;
|
|
3230
|
+
// } else {
|
|
3231
|
+
// titleSpan.innerHTML = this.title;
|
|
3015
3232
|
}
|
|
3016
3233
|
// Set the width of the title span, so overflow ellipsis work
|
|
3017
3234
|
if (!treeOptions.skeleton) {
|
|
@@ -3045,9 +3262,19 @@ class WunderbaumNode {
|
|
|
3045
3262
|
});
|
|
3046
3263
|
}
|
|
3047
3264
|
// Attach to DOM as late as possible
|
|
3048
|
-
|
|
3049
|
-
|
|
3050
|
-
|
|
3265
|
+
if (isNew) {
|
|
3266
|
+
const after = opts ? opts.after : "last";
|
|
3267
|
+
switch (after) {
|
|
3268
|
+
case "first":
|
|
3269
|
+
tree.nodeListElement.prepend(rowDiv);
|
|
3270
|
+
break;
|
|
3271
|
+
case "last":
|
|
3272
|
+
tree.nodeListElement.appendChild(rowDiv);
|
|
3273
|
+
break;
|
|
3274
|
+
default:
|
|
3275
|
+
opts.after.after(rowDiv);
|
|
3276
|
+
}
|
|
3277
|
+
}
|
|
3051
3278
|
}
|
|
3052
3279
|
/**
|
|
3053
3280
|
* Remove all children, collapse, and set the lazy-flag, so that the lazyLoad
|
|
@@ -3058,7 +3285,7 @@ class WunderbaumNode {
|
|
|
3058
3285
|
this.expanded = false;
|
|
3059
3286
|
this.lazy = true;
|
|
3060
3287
|
this.children = null;
|
|
3061
|
-
this.tree.
|
|
3288
|
+
this.tree.setModified(ChangeType.structure);
|
|
3062
3289
|
}
|
|
3063
3290
|
/** Convert node (or whole branch) into a plain object.
|
|
3064
3291
|
*
|
|
@@ -3121,14 +3348,15 @@ class WunderbaumNode {
|
|
|
3121
3348
|
*
|
|
3122
3349
|
* Evaluation sequence:
|
|
3123
3350
|
*
|
|
3124
|
-
* If `tree.options.<name>` is a callback that returns something, use that.
|
|
3125
|
-
* Else if `node.<name>` is defined, use that.
|
|
3126
|
-
* Else if `tree.types[<node.type>]` is a value, use that.
|
|
3127
|
-
* Else if `tree.options.<name>` is a value, use that.
|
|
3128
|
-
* Else use `defaultValue`.
|
|
3351
|
+
* - If `tree.options.<name>` is a callback that returns something, use that.
|
|
3352
|
+
* - Else if `node.<name>` is defined, use that.
|
|
3353
|
+
* - Else if `tree.types[<node.type>]` is a value, use that.
|
|
3354
|
+
* - Else if `tree.options.<name>` is a value, use that.
|
|
3355
|
+
* - Else use `defaultValue`.
|
|
3129
3356
|
*
|
|
3130
3357
|
* @param name name of the option property (on node and tree)
|
|
3131
3358
|
* @param defaultValue return this if nothing else matched
|
|
3359
|
+
* {@link Wunderbaum.getOption|Wunderbaum.getOption()}
|
|
3132
3360
|
*/
|
|
3133
3361
|
getOption(name, defaultValue) {
|
|
3134
3362
|
let tree = this.tree;
|
|
@@ -3163,15 +3391,21 @@ class WunderbaumNode {
|
|
|
3163
3391
|
// Use value from value options dict, fallback do default
|
|
3164
3392
|
return value !== null && value !== void 0 ? value : defaultValue;
|
|
3165
3393
|
}
|
|
3394
|
+
/** Make sure that this node is visible in the viewport.
|
|
3395
|
+
* @see {@link Wunderbaum.scrollTo|Wunderbaum.scrollTo()}
|
|
3396
|
+
*/
|
|
3166
3397
|
async scrollIntoView(options) {
|
|
3167
3398
|
return this.tree.scrollTo(this);
|
|
3168
3399
|
}
|
|
3400
|
+
/**
|
|
3401
|
+
* Activate this node, deactivate previous, send events, activate column and scroll int viewport.
|
|
3402
|
+
*/
|
|
3169
3403
|
async setActive(flag = true, options) {
|
|
3170
3404
|
const tree = this.tree;
|
|
3171
3405
|
const prev = tree.activeNode;
|
|
3172
3406
|
const retrigger = options === null || options === void 0 ? void 0 : options.retrigger;
|
|
3173
|
-
const
|
|
3174
|
-
if (!
|
|
3407
|
+
const noEvents = options === null || options === void 0 ? void 0 : options.noEvents;
|
|
3408
|
+
if (!noEvents) {
|
|
3175
3409
|
let orgEvent = options === null || options === void 0 ? void 0 : options.event;
|
|
3176
3410
|
if (flag) {
|
|
3177
3411
|
if (prev !== this || retrigger) {
|
|
@@ -3186,7 +3420,7 @@ class WunderbaumNode {
|
|
|
3186
3420
|
orgEvent: orgEvent,
|
|
3187
3421
|
}) === false) {
|
|
3188
3422
|
tree.activeNode = null;
|
|
3189
|
-
prev === null || prev === void 0 ? void 0 : prev.
|
|
3423
|
+
prev === null || prev === void 0 ? void 0 : prev.setModified();
|
|
3190
3424
|
return;
|
|
3191
3425
|
}
|
|
3192
3426
|
}
|
|
@@ -3197,8 +3431,8 @@ class WunderbaumNode {
|
|
|
3197
3431
|
}
|
|
3198
3432
|
if (prev !== this) {
|
|
3199
3433
|
tree.activeNode = this;
|
|
3200
|
-
prev === null || prev === void 0 ? void 0 : prev.
|
|
3201
|
-
this.
|
|
3434
|
+
prev === null || prev === void 0 ? void 0 : prev.setModified();
|
|
3435
|
+
this.setModified();
|
|
3202
3436
|
}
|
|
3203
3437
|
if (options &&
|
|
3204
3438
|
options.colIdx != null &&
|
|
@@ -3209,57 +3443,62 @@ class WunderbaumNode {
|
|
|
3209
3443
|
// requestAnimationFrame(() => {
|
|
3210
3444
|
// this.scrollIntoView();
|
|
3211
3445
|
// })
|
|
3212
|
-
this.scrollIntoView();
|
|
3213
|
-
}
|
|
3214
|
-
setDirty(type) {
|
|
3215
|
-
if (this.tree._disableUpdate) {
|
|
3216
|
-
return;
|
|
3217
|
-
}
|
|
3218
|
-
if (type === ChangeType.structure) {
|
|
3219
|
-
this.tree.updateViewport();
|
|
3220
|
-
}
|
|
3221
|
-
else if (this._rowElem) {
|
|
3222
|
-
// otherwise not in viewport, so no need to render
|
|
3223
|
-
this.render();
|
|
3224
|
-
}
|
|
3446
|
+
return this.scrollIntoView();
|
|
3225
3447
|
}
|
|
3448
|
+
/**
|
|
3449
|
+
* Expand or collapse this node.
|
|
3450
|
+
*/
|
|
3226
3451
|
async setExpanded(flag = true, options) {
|
|
3227
3452
|
// alert("" + this.getLevel() + ", "+ this.getOption("minExpandLevel");
|
|
3228
3453
|
if (!flag &&
|
|
3229
3454
|
this.isExpanded() &&
|
|
3230
3455
|
this.getLevel() < this.getOption("minExpandLevel") &&
|
|
3231
3456
|
!getOption(options, "force")) {
|
|
3232
|
-
this.logDebug("Ignored collapse request.");
|
|
3457
|
+
this.logDebug("Ignored collapse request below expandLevel.");
|
|
3233
3458
|
return;
|
|
3234
3459
|
}
|
|
3235
3460
|
if (flag && this.lazy && this.children == null) {
|
|
3236
3461
|
await this.loadLazy();
|
|
3237
3462
|
}
|
|
3238
3463
|
this.expanded = flag;
|
|
3239
|
-
this.
|
|
3240
|
-
}
|
|
3241
|
-
setIcon() {
|
|
3242
|
-
throw new Error("Not yet implemented");
|
|
3243
|
-
// this.setDirty(ChangeType.status);
|
|
3464
|
+
this.tree.setModified(ChangeType.structure);
|
|
3244
3465
|
}
|
|
3466
|
+
/**
|
|
3467
|
+
* Set keyboard focus here.
|
|
3468
|
+
* @see {@link setActive}
|
|
3469
|
+
*/
|
|
3245
3470
|
setFocus(flag = true, options) {
|
|
3246
3471
|
const prev = this.tree.focusNode;
|
|
3247
3472
|
this.tree.focusNode = this;
|
|
3248
|
-
prev === null || prev === void 0 ? void 0 : prev.
|
|
3249
|
-
this.
|
|
3473
|
+
prev === null || prev === void 0 ? void 0 : prev.setModified();
|
|
3474
|
+
this.setModified();
|
|
3250
3475
|
}
|
|
3476
|
+
/** Set a new icon path or class. */
|
|
3477
|
+
setIcon() {
|
|
3478
|
+
throw new Error("Not yet implemented");
|
|
3479
|
+
// this.setModified();
|
|
3480
|
+
}
|
|
3481
|
+
/** Change node's {@link key} and/or {@link refKey}. */
|
|
3482
|
+
setKey(key, refKey) {
|
|
3483
|
+
throw new Error("Not yet implemented");
|
|
3484
|
+
}
|
|
3485
|
+
/** Schedule a render, typically called to update after a status or data change. */
|
|
3486
|
+
setModified(change = ChangeType.status) {
|
|
3487
|
+
assert(change === ChangeType.status);
|
|
3488
|
+
this.tree.setModified(ChangeType.row, this);
|
|
3489
|
+
}
|
|
3490
|
+
/** Modify the check/uncheck state. */
|
|
3251
3491
|
setSelected(flag = true, options) {
|
|
3252
3492
|
const prev = this.selected;
|
|
3253
3493
|
if (!!flag !== prev) {
|
|
3254
3494
|
this._callEvent("select", { flag: flag });
|
|
3255
3495
|
}
|
|
3256
3496
|
this.selected = !!flag;
|
|
3257
|
-
this.
|
|
3497
|
+
this.setModified();
|
|
3258
3498
|
}
|
|
3259
|
-
/**
|
|
3260
|
-
*/
|
|
3499
|
+
/** Display node status (ok, loading, error, noData) using styles and a dummy child node. */
|
|
3261
3500
|
setStatus(status, message, details) {
|
|
3262
|
-
|
|
3501
|
+
const tree = this.tree;
|
|
3263
3502
|
let statusNode = null;
|
|
3264
3503
|
const _clearStatusNode = () => {
|
|
3265
3504
|
// Remove dedicated dummy node, if any
|
|
@@ -3330,12 +3569,13 @@ class WunderbaumNode {
|
|
|
3330
3569
|
default:
|
|
3331
3570
|
error("invalid node status " + status);
|
|
3332
3571
|
}
|
|
3333
|
-
tree.
|
|
3572
|
+
tree.setModified(ChangeType.structure);
|
|
3334
3573
|
return statusNode;
|
|
3335
3574
|
}
|
|
3575
|
+
/** Rename this node. */
|
|
3336
3576
|
setTitle(title) {
|
|
3337
3577
|
this.title = title;
|
|
3338
|
-
this.
|
|
3578
|
+
this.setModified();
|
|
3339
3579
|
// this.triggerModify("rename"); // TODO
|
|
3340
3580
|
}
|
|
3341
3581
|
/**
|
|
@@ -3356,10 +3596,16 @@ class WunderbaumNode {
|
|
|
3356
3596
|
* @param {object} [extra]
|
|
3357
3597
|
*/
|
|
3358
3598
|
triggerModify(operation, extra) {
|
|
3599
|
+
if (!this.parent) {
|
|
3600
|
+
return;
|
|
3601
|
+
}
|
|
3359
3602
|
this.parent.triggerModifyChild(operation, this, extra);
|
|
3360
3603
|
}
|
|
3361
|
-
/**
|
|
3362
|
-
*
|
|
3604
|
+
/**
|
|
3605
|
+
* Call fn(node) for all child nodes in hierarchical order (depth-first).
|
|
3606
|
+
*
|
|
3607
|
+
* Stop iteration, if fn() returns false. Skip current branch, if fn()
|
|
3608
|
+
* returns "skip".<br>
|
|
3363
3609
|
* Return false if iteration was stopped.
|
|
3364
3610
|
*
|
|
3365
3611
|
* @param {function} callback the callback function.
|
|
@@ -3403,7 +3649,8 @@ class WunderbaumNode {
|
|
|
3403
3649
|
}
|
|
3404
3650
|
return true;
|
|
3405
3651
|
}
|
|
3406
|
-
/**
|
|
3652
|
+
/**
|
|
3653
|
+
* Call fn(node) for all sibling nodes.<br>
|
|
3407
3654
|
* Stop iteration, if fn() returns false.<br>
|
|
3408
3655
|
* Return false if iteration was stopped.
|
|
3409
3656
|
*
|
|
@@ -3434,7 +3681,7 @@ WunderbaumNode.sequence = 0;
|
|
|
3434
3681
|
/*!
|
|
3435
3682
|
* Wunderbaum - ext-edit
|
|
3436
3683
|
* Copyright (c) 2021-2022, Martin Wendt. Released under the MIT license.
|
|
3437
|
-
* v0.0.
|
|
3684
|
+
* v0.0.3, Mon, 18 Apr 2022 11:52:44 GMT (https://github.com/mar10/wunderbaum)
|
|
3438
3685
|
*/
|
|
3439
3686
|
// const START_MARKER = "\uFFF7";
|
|
3440
3687
|
class EditExtension extends WunderbaumExtension {
|
|
@@ -3552,7 +3799,7 @@ class EditExtension extends WunderbaumExtension {
|
|
|
3552
3799
|
break;
|
|
3553
3800
|
case "F2":
|
|
3554
3801
|
if (trigger.indexOf("F2") >= 0) {
|
|
3555
|
-
// tree.
|
|
3802
|
+
// tree.setNavigationMode(NavigationMode.cellEdit);
|
|
3556
3803
|
this.startEditTitle();
|
|
3557
3804
|
return false;
|
|
3558
3805
|
}
|
|
@@ -3593,6 +3840,7 @@ class EditExtension extends WunderbaumExtension {
|
|
|
3593
3840
|
if (validity) {
|
|
3594
3841
|
// Permanently apply input validations (CSS and tooltip)
|
|
3595
3842
|
inputElem.addEventListener("keydown", (e) => {
|
|
3843
|
+
inputElem.setCustomValidity("");
|
|
3596
3844
|
if (!inputElem.reportValidity()) ;
|
|
3597
3845
|
});
|
|
3598
3846
|
}
|
|
@@ -3633,6 +3881,11 @@ class EditExtension extends WunderbaumExtension {
|
|
|
3633
3881
|
}
|
|
3634
3882
|
node.logDebug(`stopEditTitle(${apply})`, opts, focusElem, newValue);
|
|
3635
3883
|
if (apply && newValue !== null && newValue !== node.title) {
|
|
3884
|
+
const errMsg = focusElem.validationMessage;
|
|
3885
|
+
if (errMsg) {
|
|
3886
|
+
// input element's native validation failed
|
|
3887
|
+
throw new Error(`Input validation failed for "${newValue}": ${errMsg}.`);
|
|
3888
|
+
}
|
|
3636
3889
|
const colElem = node.getColElem(0);
|
|
3637
3890
|
this._applyChange("edit.apply", node, colElem, {
|
|
3638
3891
|
oldValue: node.title,
|
|
@@ -3719,12 +3972,12 @@ class EditExtension extends WunderbaumExtension {
|
|
|
3719
3972
|
* Copyright (c) 2021-2022, Martin Wendt (https://wwWendt.de).
|
|
3720
3973
|
* Released under the MIT license.
|
|
3721
3974
|
*
|
|
3722
|
-
* @version v0.0.
|
|
3723
|
-
* @date
|
|
3975
|
+
* @version v0.0.3
|
|
3976
|
+
* @date Mon, 18 Apr 2022 11:52:44 GMT
|
|
3724
3977
|
*/
|
|
3725
3978
|
// const class_prefix = "wb-";
|
|
3726
3979
|
// const node_props: string[] = ["title", "key", "refKey"];
|
|
3727
|
-
const MAX_CHANGED_NODES = 10;
|
|
3980
|
+
// const MAX_CHANGED_NODES = 10;
|
|
3728
3981
|
/**
|
|
3729
3982
|
* A persistent plain object or array.
|
|
3730
3983
|
*
|
|
@@ -3736,36 +3989,43 @@ class Wunderbaum {
|
|
|
3736
3989
|
this.extensions = {};
|
|
3737
3990
|
this.keyMap = new Map();
|
|
3738
3991
|
this.refKeyMap = new Map();
|
|
3739
|
-
|
|
3740
|
-
|
|
3741
|
-
|
|
3992
|
+
// protected viewNodes = new Set<WunderbaumNode>();
|
|
3993
|
+
this.treeRowCount = 0;
|
|
3994
|
+
this._disableUpdateCount = 0;
|
|
3742
3995
|
// protected eventHandlers : Array<function> = [];
|
|
3996
|
+
/** Currently active node if any. */
|
|
3743
3997
|
this.activeNode = null;
|
|
3998
|
+
/** Current node hat has keyboard focus if any. */
|
|
3744
3999
|
this.focusNode = null;
|
|
3745
|
-
this._disableUpdate = 0;
|
|
3746
|
-
this._disableUpdateCount = 0;
|
|
3747
4000
|
/** Shared properties, referenced by `node.type`. */
|
|
3748
4001
|
this.types = {};
|
|
3749
4002
|
/** List of column definitions. */
|
|
3750
4003
|
this.columns = [];
|
|
3751
4004
|
this._columnsById = {};
|
|
3752
4005
|
// Modification Status
|
|
3753
|
-
|
|
3754
|
-
|
|
3755
|
-
|
|
4006
|
+
// protected changedSince = 0;
|
|
4007
|
+
// protected changes = new Set<ChangeType>();
|
|
4008
|
+
// protected changedNodes = new Set<WunderbaumNode>();
|
|
4009
|
+
this.changeRedrawRequestPending = false;
|
|
4010
|
+
/** Expose some useful methods of the util.ts module as `tree._util`. */
|
|
4011
|
+
this._util = util;
|
|
3756
4012
|
// --- FILTER ---
|
|
3757
4013
|
this.filterMode = null;
|
|
3758
4014
|
// --- KEYNAV ---
|
|
4015
|
+
/** @internal Use `setColumn()`/`getActiveColElem()`*/
|
|
3759
4016
|
this.activeColIdx = 0;
|
|
4017
|
+
/** @internal */
|
|
3760
4018
|
this.navMode = NavigationMode.row;
|
|
4019
|
+
/** @internal */
|
|
3761
4020
|
this.lastQuicksearchTime = 0;
|
|
4021
|
+
/** @internal */
|
|
3762
4022
|
this.lastQuicksearchTerm = "";
|
|
3763
4023
|
// --- EDIT ---
|
|
3764
4024
|
this.lastClickTime = 0;
|
|
3765
|
-
|
|
3766
|
-
|
|
3767
|
-
|
|
3768
|
-
this.log = this.logDebug;
|
|
4025
|
+
/** Alias for {@link Wunderbaum.logDebug}.
|
|
4026
|
+
* @alias Wunderbaum.logDebug
|
|
4027
|
+
*/
|
|
4028
|
+
this.log = this.logDebug;
|
|
3769
4029
|
let opts = (this.options = extend({
|
|
3770
4030
|
id: null,
|
|
3771
4031
|
source: null,
|
|
@@ -3776,7 +4036,7 @@ class Wunderbaum {
|
|
|
3776
4036
|
rowHeightPx: ROW_HEIGHT,
|
|
3777
4037
|
columns: null,
|
|
3778
4038
|
types: null,
|
|
3779
|
-
escapeTitles: true,
|
|
4039
|
+
// escapeTitles: true,
|
|
3780
4040
|
showSpinner: false,
|
|
3781
4041
|
checkbox: true,
|
|
3782
4042
|
minExpandLevel: 0,
|
|
@@ -3833,6 +4093,7 @@ class Wunderbaum {
|
|
|
3833
4093
|
this._registerExtension(new EditExtension(this));
|
|
3834
4094
|
this._registerExtension(new FilterExtension(this));
|
|
3835
4095
|
this._registerExtension(new DndExtension(this));
|
|
4096
|
+
this._registerExtension(new GridExtension(this));
|
|
3836
4097
|
this._registerExtension(new LoggerExtension(this));
|
|
3837
4098
|
// --- Evaluate options
|
|
3838
4099
|
this.columns = opts.columns;
|
|
@@ -3926,11 +4187,9 @@ class Wunderbaum {
|
|
|
3926
4187
|
var _a;
|
|
3927
4188
|
(_a = this.element.querySelector("progress.spinner")) === null || _a === void 0 ? void 0 : _a.remove();
|
|
3928
4189
|
this.element.classList.remove("wb-initializing");
|
|
3929
|
-
// this.updateViewport();
|
|
3930
4190
|
});
|
|
3931
4191
|
}
|
|
3932
4192
|
else {
|
|
3933
|
-
// this.updateViewport();
|
|
3934
4193
|
readyDeferred.resolve();
|
|
3935
4194
|
}
|
|
3936
4195
|
// TODO: This is sometimes required, because this.element.clientWidth
|
|
@@ -3940,13 +4199,10 @@ class Wunderbaum {
|
|
|
3940
4199
|
}, 50);
|
|
3941
4200
|
// --- Bind listeners
|
|
3942
4201
|
this.scrollContainer.addEventListener("scroll", (e) => {
|
|
3943
|
-
this.
|
|
4202
|
+
this.setModified(ChangeType.vscroll);
|
|
3944
4203
|
});
|
|
3945
|
-
// window.addEventListener("resize", (e: Event) => {
|
|
3946
|
-
// this.updateViewport();
|
|
3947
|
-
// });
|
|
3948
4204
|
this.resizeObserver = new ResizeObserver((entries) => {
|
|
3949
|
-
this.
|
|
4205
|
+
this.setModified(ChangeType.vscroll);
|
|
3950
4206
|
console.log("ResizeObserver: Size changed", entries);
|
|
3951
4207
|
});
|
|
3952
4208
|
this.resizeObserver.observe(this.element);
|
|
@@ -4003,37 +4259,18 @@ class Wunderbaum {
|
|
|
4003
4259
|
forceClose: true,
|
|
4004
4260
|
});
|
|
4005
4261
|
}
|
|
4006
|
-
// if (flag && !this.activeNode ) {
|
|
4007
|
-
// setTimeout(() => {
|
|
4008
|
-
// if (!this.activeNode) {
|
|
4009
|
-
// const firstNode = this.getFirstChild();
|
|
4010
|
-
// if (firstNode && !firstNode?.isStatusNode()) {
|
|
4011
|
-
// firstNode.logInfo("Activate on focus", e);
|
|
4012
|
-
// firstNode.setActive(true, { event: e });
|
|
4013
|
-
// }
|
|
4014
|
-
// }
|
|
4015
|
-
// }, 10);
|
|
4016
|
-
// }
|
|
4017
4262
|
});
|
|
4018
4263
|
}
|
|
4019
|
-
/**
|
|
4020
|
-
|
|
4021
|
-
// const coldivs = "<span class='wb-col'></span>".repeat(this.columns.length);
|
|
4022
|
-
// this.element.innerHTML = `
|
|
4023
|
-
// <div class='wb-header'>
|
|
4024
|
-
// <div class='wb-row'>
|
|
4025
|
-
// ${coldivs}
|
|
4026
|
-
// </div>
|
|
4027
|
-
// </div>`;
|
|
4028
|
-
// }
|
|
4029
|
-
/** Return a Wunderbaum instance, from element, index, or event.
|
|
4264
|
+
/**
|
|
4265
|
+
* Return a Wunderbaum instance, from element, id, index, or event.
|
|
4030
4266
|
*
|
|
4031
|
-
*
|
|
4032
|
-
* getTree();
|
|
4033
|
-
* getTree(1);
|
|
4034
|
-
* getTree(event);
|
|
4035
|
-
* getTree("foo");
|
|
4267
|
+
* ```js
|
|
4268
|
+
* getTree(); // Get first Wunderbaum instance on page
|
|
4269
|
+
* getTree(1); // Get second Wunderbaum instance on page
|
|
4270
|
+
* getTree(event); // Get tree for this mouse- or keyboard event
|
|
4271
|
+
* getTree("foo"); // Get tree for this `tree.options.id`
|
|
4036
4272
|
* getTree("#tree"); // Get tree for this matching element
|
|
4273
|
+
* ```
|
|
4037
4274
|
*/
|
|
4038
4275
|
static getTree(el) {
|
|
4039
4276
|
if (el instanceof Wunderbaum) {
|
|
@@ -4074,9 +4311,8 @@ class Wunderbaum {
|
|
|
4074
4311
|
}
|
|
4075
4312
|
return null;
|
|
4076
4313
|
}
|
|
4077
|
-
/**
|
|
4078
|
-
*
|
|
4079
|
-
* @param el
|
|
4314
|
+
/**
|
|
4315
|
+
* Return a WunderbaumNode instance from element or event.
|
|
4080
4316
|
*/
|
|
4081
4317
|
static getNode(el) {
|
|
4082
4318
|
if (!el) {
|
|
@@ -4098,7 +4334,7 @@ class Wunderbaum {
|
|
|
4098
4334
|
}
|
|
4099
4335
|
return null;
|
|
4100
4336
|
}
|
|
4101
|
-
/** */
|
|
4337
|
+
/** @internal */
|
|
4102
4338
|
_registerExtension(extension) {
|
|
4103
4339
|
this.extensionList.push(extension);
|
|
4104
4340
|
this.extensions[extension.id] = extension;
|
|
@@ -4140,7 +4376,7 @@ class Wunderbaum {
|
|
|
4140
4376
|
node.tree = null;
|
|
4141
4377
|
node.parent = null;
|
|
4142
4378
|
// node.title = "DISPOSED: " + node.title
|
|
4143
|
-
this.viewNodes.delete(node);
|
|
4379
|
+
// this.viewNodes.delete(node);
|
|
4144
4380
|
node.removeMarkup();
|
|
4145
4381
|
}
|
|
4146
4382
|
/** Call all hook methods of all registered extensions.*/
|
|
@@ -4158,7 +4394,9 @@ class Wunderbaum {
|
|
|
4158
4394
|
}
|
|
4159
4395
|
return res;
|
|
4160
4396
|
}
|
|
4161
|
-
/**
|
|
4397
|
+
/**
|
|
4398
|
+
* Call tree method or extension method if defined.
|
|
4399
|
+
*
|
|
4162
4400
|
* Example:
|
|
4163
4401
|
* ```js
|
|
4164
4402
|
* tree._callMethod("edit.startEdit", "arg1", "arg2")
|
|
@@ -4175,7 +4413,9 @@ class Wunderbaum {
|
|
|
4175
4413
|
this.logError(`Calling undefined method '${name}()'.`);
|
|
4176
4414
|
}
|
|
4177
4415
|
}
|
|
4178
|
-
/**
|
|
4416
|
+
/**
|
|
4417
|
+
* Call event handler if defined in tree or tree.EXTENSION options.
|
|
4418
|
+
*
|
|
4179
4419
|
* Example:
|
|
4180
4420
|
* ```js
|
|
4181
4421
|
* tree._callEvent("edit.beforeEdit", {foo: 42})
|
|
@@ -4191,27 +4431,33 @@ class Wunderbaum {
|
|
|
4191
4431
|
// this.logError(`Triggering undefined event '${name}'.`)
|
|
4192
4432
|
}
|
|
4193
4433
|
}
|
|
4194
|
-
/** Return the
|
|
4195
|
-
|
|
4196
|
-
let topIdx, node;
|
|
4197
|
-
if (complete) {
|
|
4198
|
-
topIdx = Math.ceil(this.scrollContainer.scrollTop / ROW_HEIGHT);
|
|
4199
|
-
}
|
|
4200
|
-
else {
|
|
4201
|
-
topIdx = Math.floor(this.scrollContainer.scrollTop / ROW_HEIGHT);
|
|
4202
|
-
}
|
|
4434
|
+
/** Return the node for given row index. */
|
|
4435
|
+
_getNodeByRowIdx(idx) {
|
|
4203
4436
|
// TODO: start searching from active node (reverse)
|
|
4437
|
+
let node = null;
|
|
4204
4438
|
this.visitRows((n) => {
|
|
4205
|
-
if (n._rowIdx ===
|
|
4439
|
+
if (n._rowIdx === idx) {
|
|
4206
4440
|
node = n;
|
|
4207
4441
|
return false;
|
|
4208
4442
|
}
|
|
4209
4443
|
});
|
|
4210
4444
|
return node;
|
|
4211
4445
|
}
|
|
4212
|
-
/** Return the
|
|
4446
|
+
/** Return the topmost visible node in the viewport. */
|
|
4447
|
+
_firstNodeInView(complete = true) {
|
|
4448
|
+
let topIdx;
|
|
4449
|
+
const gracePy = 1; // ignore subpixel scrolling
|
|
4450
|
+
if (complete) {
|
|
4451
|
+
topIdx = Math.ceil((this.scrollContainer.scrollTop - gracePy) / ROW_HEIGHT);
|
|
4452
|
+
}
|
|
4453
|
+
else {
|
|
4454
|
+
topIdx = Math.floor(this.scrollContainer.scrollTop / ROW_HEIGHT);
|
|
4455
|
+
}
|
|
4456
|
+
return this._getNodeByRowIdx(topIdx);
|
|
4457
|
+
}
|
|
4458
|
+
/** Return the lowest visible node in the viewport. */
|
|
4213
4459
|
_lastNodeInView(complete = true) {
|
|
4214
|
-
let bottomIdx
|
|
4460
|
+
let bottomIdx;
|
|
4215
4461
|
if (complete) {
|
|
4216
4462
|
bottomIdx =
|
|
4217
4463
|
Math.floor((this.scrollContainer.scrollTop + this.scrollContainer.clientHeight) /
|
|
@@ -4222,16 +4468,10 @@ class Wunderbaum {
|
|
|
4222
4468
|
Math.ceil((this.scrollContainer.scrollTop + this.scrollContainer.clientHeight) /
|
|
4223
4469
|
ROW_HEIGHT) - 1;
|
|
4224
4470
|
}
|
|
4225
|
-
|
|
4226
|
-
this.
|
|
4227
|
-
if (n._rowIdx === bottomIdx) {
|
|
4228
|
-
node = n;
|
|
4229
|
-
return false;
|
|
4230
|
-
}
|
|
4231
|
-
});
|
|
4232
|
-
return node;
|
|
4471
|
+
bottomIdx = Math.min(bottomIdx, this.count(true) - 1);
|
|
4472
|
+
return this._getNodeByRowIdx(bottomIdx);
|
|
4233
4473
|
}
|
|
4234
|
-
/** Return preceeding visible node in the viewport */
|
|
4474
|
+
/** Return preceeding visible node in the viewport. */
|
|
4235
4475
|
_getPrevNodeInView(node, ofs = 1) {
|
|
4236
4476
|
this.visitRows((n) => {
|
|
4237
4477
|
node = n;
|
|
@@ -4241,7 +4481,7 @@ class Wunderbaum {
|
|
|
4241
4481
|
}, { reverse: true, start: node || this.getActiveNode() });
|
|
4242
4482
|
return node;
|
|
4243
4483
|
}
|
|
4244
|
-
/** Return following visible node in the viewport */
|
|
4484
|
+
/** Return following visible node in the viewport. */
|
|
4245
4485
|
_getNextNodeInView(node, ofs = 1) {
|
|
4246
4486
|
this.visitRows((n) => {
|
|
4247
4487
|
node = n;
|
|
@@ -4251,10 +4491,15 @@ class Wunderbaum {
|
|
|
4251
4491
|
}, { reverse: false, start: node || this.getActiveNode() });
|
|
4252
4492
|
return node;
|
|
4253
4493
|
}
|
|
4494
|
+
/**
|
|
4495
|
+
* Append (or insert) a list of toplevel nodes.
|
|
4496
|
+
*
|
|
4497
|
+
* @see {@link WunderbaumNode.addChildren}
|
|
4498
|
+
*/
|
|
4254
4499
|
addChildren(nodeData, options) {
|
|
4255
4500
|
return this.root.addChildren(nodeData, options);
|
|
4256
4501
|
}
|
|
4257
|
-
|
|
4502
|
+
/**
|
|
4258
4503
|
* Apply a modification or navigation operation.
|
|
4259
4504
|
*
|
|
4260
4505
|
* Most of these commands simply map to a node or tree method.
|
|
@@ -4379,16 +4624,17 @@ class Wunderbaum {
|
|
|
4379
4624
|
this.root.children = null;
|
|
4380
4625
|
this.keyMap.clear();
|
|
4381
4626
|
this.refKeyMap.clear();
|
|
4382
|
-
this.viewNodes.clear();
|
|
4627
|
+
// this.viewNodes.clear();
|
|
4628
|
+
this.treeRowCount = 0;
|
|
4383
4629
|
this.activeNode = null;
|
|
4384
4630
|
this.focusNode = null;
|
|
4385
4631
|
// this.types = {};
|
|
4386
4632
|
// this. columns =[];
|
|
4387
4633
|
// this._columnsById = {};
|
|
4388
4634
|
// Modification Status
|
|
4389
|
-
this.changedSince = 0;
|
|
4390
|
-
this.changes.clear();
|
|
4391
|
-
this.changedNodes.clear();
|
|
4635
|
+
// this.changedSince = 0;
|
|
4636
|
+
// this.changes.clear();
|
|
4637
|
+
// this.changedNodes.clear();
|
|
4392
4638
|
// // --- FILTER ---
|
|
4393
4639
|
// public filterMode: FilterModeType = null;
|
|
4394
4640
|
// // --- KEYNAV ---
|
|
@@ -4396,7 +4642,7 @@ class Wunderbaum {
|
|
|
4396
4642
|
// public cellNavMode = false;
|
|
4397
4643
|
// public lastQuicksearchTime = 0;
|
|
4398
4644
|
// public lastQuicksearchTerm = "";
|
|
4399
|
-
this.
|
|
4645
|
+
this.setModified(ChangeType.structure);
|
|
4400
4646
|
}
|
|
4401
4647
|
/**
|
|
4402
4648
|
* Clear nodes and markup and detach events and observers.
|
|
@@ -4416,10 +4662,11 @@ class Wunderbaum {
|
|
|
4416
4662
|
/**
|
|
4417
4663
|
* Return `tree.option.NAME` (also resolving if this is a callback).
|
|
4418
4664
|
*
|
|
4419
|
-
* See also
|
|
4420
|
-
* `tree.types[node.type].NAME`.
|
|
4665
|
+
* See also {@link WunderbaumNode.getOption|WunderbaumNode.getOption()}
|
|
4666
|
+
* to consider `node.NAME` setting and `tree.types[node.type].NAME`.
|
|
4421
4667
|
*
|
|
4422
|
-
* @param name option name (use dot notation to access extension option, e.g.
|
|
4668
|
+
* @param name option name (use dot notation to access extension option, e.g.
|
|
4669
|
+
* `filter.mode`)
|
|
4423
4670
|
*/
|
|
4424
4671
|
getOption(name, defaultValue) {
|
|
4425
4672
|
let ext;
|
|
@@ -4463,18 +4710,14 @@ class Wunderbaum {
|
|
|
4463
4710
|
}
|
|
4464
4711
|
/** Run code, but defer `updateViewport()` until done. */
|
|
4465
4712
|
runWithoutUpdate(func, hint = null) {
|
|
4466
|
-
// const prev = this._disableUpdate;
|
|
4467
|
-
// const start = Date.now();
|
|
4468
|
-
// this._disableUpdate = Date.now();
|
|
4469
4713
|
try {
|
|
4470
4714
|
this.enableUpdate(false);
|
|
4471
|
-
|
|
4715
|
+
const res = func();
|
|
4716
|
+
assert(!(res instanceof Promise));
|
|
4717
|
+
return res;
|
|
4472
4718
|
}
|
|
4473
4719
|
finally {
|
|
4474
4720
|
this.enableUpdate(true);
|
|
4475
|
-
// if (!prev && this._disableUpdate === start) {
|
|
4476
|
-
// this._disableUpdate = 0;
|
|
4477
|
-
// }
|
|
4478
4721
|
}
|
|
4479
4722
|
}
|
|
4480
4723
|
/** Recursively expand all expandable nodes (triggers lazy load id needed). */
|
|
@@ -4492,11 +4735,12 @@ class Wunderbaum {
|
|
|
4492
4735
|
/** Return the number of nodes in the data model.*/
|
|
4493
4736
|
count(visible = false) {
|
|
4494
4737
|
if (visible) {
|
|
4495
|
-
return this.
|
|
4738
|
+
return this.treeRowCount;
|
|
4739
|
+
// return this.viewNodes.size;
|
|
4496
4740
|
}
|
|
4497
4741
|
return this.keyMap.size;
|
|
4498
4742
|
}
|
|
4499
|
-
|
|
4743
|
+
/** @internal sanity check. */
|
|
4500
4744
|
_check() {
|
|
4501
4745
|
let i = 0;
|
|
4502
4746
|
this.visit((n) => {
|
|
@@ -4507,25 +4751,30 @@ class Wunderbaum {
|
|
|
4507
4751
|
}
|
|
4508
4752
|
// util.assert(this.keyMap.size === i);
|
|
4509
4753
|
}
|
|
4510
|
-
/**
|
|
4754
|
+
/**
|
|
4755
|
+
* Find all nodes that matches condition.
|
|
4511
4756
|
*
|
|
4512
4757
|
* @param match title string to search for, or a
|
|
4513
4758
|
* callback function that returns `true` if a node is matched.
|
|
4514
|
-
*
|
|
4759
|
+
*
|
|
4760
|
+
* @see {@link WunderbaumNode.findAll}
|
|
4515
4761
|
*/
|
|
4516
4762
|
findAll(match) {
|
|
4517
4763
|
return this.root.findAll(match);
|
|
4518
4764
|
}
|
|
4519
|
-
/**
|
|
4765
|
+
/**
|
|
4766
|
+
* Find first node that matches condition.
|
|
4520
4767
|
*
|
|
4521
4768
|
* @param match title string to search for, or a
|
|
4522
4769
|
* callback function that returns `true` if a node is matched.
|
|
4523
|
-
* @see
|
|
4770
|
+
* @see {@link WunderbaumNode.findFirst}
|
|
4771
|
+
*
|
|
4524
4772
|
*/
|
|
4525
4773
|
findFirst(match) {
|
|
4526
4774
|
return this.root.findFirst(match);
|
|
4527
4775
|
}
|
|
4528
|
-
/**
|
|
4776
|
+
/**
|
|
4777
|
+
* Find the next visible node that starts with `match`, starting at `startNode`
|
|
4529
4778
|
* and wrap-around at the end.
|
|
4530
4779
|
*/
|
|
4531
4780
|
findNextNode(match, startNode) {
|
|
@@ -4555,7 +4804,8 @@ class Wunderbaum {
|
|
|
4555
4804
|
}
|
|
4556
4805
|
return res;
|
|
4557
4806
|
}
|
|
4558
|
-
/**
|
|
4807
|
+
/**
|
|
4808
|
+
* Find a node relative to another node.
|
|
4559
4809
|
*
|
|
4560
4810
|
* @param node
|
|
4561
4811
|
* @param where 'down', 'first', 'last', 'left', 'parent', 'right', or 'up'.
|
|
@@ -4565,7 +4815,7 @@ class Wunderbaum {
|
|
|
4565
4815
|
*/
|
|
4566
4816
|
findRelatedNode(node, where, includeHidden = false) {
|
|
4567
4817
|
let res = null;
|
|
4568
|
-
|
|
4818
|
+
const pageSize = Math.floor(this.scrollContainer.clientHeight / ROW_HEIGHT);
|
|
4569
4819
|
switch (where) {
|
|
4570
4820
|
case "parent":
|
|
4571
4821
|
if (node.parent && node.parent.parent) {
|
|
@@ -4621,9 +4871,9 @@ class Wunderbaum {
|
|
|
4621
4871
|
res = this._getNextNodeInView(node);
|
|
4622
4872
|
break;
|
|
4623
4873
|
case "pageDown":
|
|
4624
|
-
|
|
4625
|
-
// this.logDebug(where
|
|
4626
|
-
if (
|
|
4874
|
+
const bottomNode = this._lastNodeInView();
|
|
4875
|
+
// this.logDebug(`${where}(${node}) -> ${bottomNode}`);
|
|
4876
|
+
if (node._rowIdx < bottomNode._rowIdx) {
|
|
4627
4877
|
res = bottomNode;
|
|
4628
4878
|
}
|
|
4629
4879
|
else {
|
|
@@ -4631,12 +4881,13 @@ class Wunderbaum {
|
|
|
4631
4881
|
}
|
|
4632
4882
|
break;
|
|
4633
4883
|
case "pageUp":
|
|
4634
|
-
if (
|
|
4635
|
-
res =
|
|
4884
|
+
if (node._rowIdx === 0) {
|
|
4885
|
+
res = node;
|
|
4636
4886
|
}
|
|
4637
4887
|
else {
|
|
4638
|
-
|
|
4639
|
-
|
|
4888
|
+
const topNode = this._firstNodeInView();
|
|
4889
|
+
// this.logDebug(`${where}(${node}) -> ${topNode}`);
|
|
4890
|
+
if (node._rowIdx > topNode._rowIdx) {
|
|
4640
4891
|
res = topNode;
|
|
4641
4892
|
}
|
|
4642
4893
|
else {
|
|
@@ -4650,7 +4901,7 @@ class Wunderbaum {
|
|
|
4650
4901
|
return res;
|
|
4651
4902
|
}
|
|
4652
4903
|
/**
|
|
4653
|
-
* Return the active cell of the currently active node or null.
|
|
4904
|
+
* Return the active cell (`span.wb-col`) of the currently active node or null.
|
|
4654
4905
|
*/
|
|
4655
4906
|
getActiveColElem() {
|
|
4656
4907
|
if (this.activeNode && this.activeColIdx >= 0) {
|
|
@@ -4664,8 +4915,8 @@ class Wunderbaum {
|
|
|
4664
4915
|
getActiveNode() {
|
|
4665
4916
|
return this.activeNode;
|
|
4666
4917
|
}
|
|
4667
|
-
/**
|
|
4668
|
-
*
|
|
4918
|
+
/**
|
|
4919
|
+
* Return the first top level node if any (not the invisible root node).
|
|
4669
4920
|
*/
|
|
4670
4921
|
getFirstChild() {
|
|
4671
4922
|
return this.root.getFirstChild();
|
|
@@ -4676,14 +4927,15 @@ class Wunderbaum {
|
|
|
4676
4927
|
getFocusNode() {
|
|
4677
4928
|
return this.focusNode;
|
|
4678
4929
|
}
|
|
4679
|
-
/** Return a {node:
|
|
4930
|
+
/** Return a {node: WunderbaumNode, region: TYPE} object for a mouse event.
|
|
4680
4931
|
*
|
|
4681
4932
|
* @param {Event} event Mouse event, e.g. click, ...
|
|
4682
|
-
* @returns {object} Return a {node:
|
|
4933
|
+
* @returns {object} Return a {node: WunderbaumNode, region: TYPE} object
|
|
4683
4934
|
* TYPE: 'title' | 'prefix' | 'expander' | 'checkbox' | 'icon' | undefined
|
|
4684
4935
|
*/
|
|
4685
4936
|
static getEventInfo(event) {
|
|
4686
|
-
let target = event.target, cl = target.classList, parentCol = target.closest(".wb-col"), node = Wunderbaum.getNode(target), res = {
|
|
4937
|
+
let target = event.target, cl = target.classList, parentCol = target.closest("span.wb-col"), node = Wunderbaum.getNode(target), tree = node ? node.tree : Wunderbaum.getTree(event), res = {
|
|
4938
|
+
tree: tree,
|
|
4687
4939
|
node: node,
|
|
4688
4940
|
region: TargetType.unknown,
|
|
4689
4941
|
colDef: undefined,
|
|
@@ -4715,13 +4967,15 @@ class Wunderbaum {
|
|
|
4715
4967
|
}
|
|
4716
4968
|
else {
|
|
4717
4969
|
// Somewhere near the title
|
|
4718
|
-
|
|
4970
|
+
if (event.type !== "mousemove" && !(event instanceof KeyboardEvent)) {
|
|
4971
|
+
console.warn("getEventInfo(): not found", event, res);
|
|
4972
|
+
}
|
|
4719
4973
|
return res;
|
|
4720
4974
|
}
|
|
4721
4975
|
if (res.colIdx === -1) {
|
|
4722
4976
|
res.colIdx = 0;
|
|
4723
4977
|
}
|
|
4724
|
-
res.colDef =
|
|
4978
|
+
res.colDef = tree === null || tree === void 0 ? void 0 : tree.columns[res.colIdx];
|
|
4725
4979
|
res.colDef != null ? (res.colId = res.colDef.id) : 0;
|
|
4726
4980
|
// this.log("Event", event, res);
|
|
4727
4981
|
return res;
|
|
@@ -4745,7 +4999,8 @@ class Wunderbaum {
|
|
|
4745
4999
|
isEditing() {
|
|
4746
5000
|
return this._callMethod("edit.isEditingTitle");
|
|
4747
5001
|
}
|
|
4748
|
-
/**
|
|
5002
|
+
/**
|
|
5003
|
+
* Return true if any node is currently beeing loaded, i.e. a Ajax request is pending.
|
|
4749
5004
|
*/
|
|
4750
5005
|
isLoading() {
|
|
4751
5006
|
var res = false;
|
|
@@ -4772,7 +5027,7 @@ class Wunderbaum {
|
|
|
4772
5027
|
console.error.apply(console, args);
|
|
4773
5028
|
}
|
|
4774
5029
|
}
|
|
4775
|
-
|
|
5030
|
+
/** Log to console if opts.debugLevel >= 3 */
|
|
4776
5031
|
logInfo(...args) {
|
|
4777
5032
|
if (this.options.debugLevel >= 3) {
|
|
4778
5033
|
Array.prototype.unshift.call(args, this.toString());
|
|
@@ -4799,77 +5054,13 @@ class Wunderbaum {
|
|
|
4799
5054
|
console.warn.apply(console, args);
|
|
4800
5055
|
}
|
|
4801
5056
|
}
|
|
4802
|
-
/** */
|
|
4803
|
-
render(opts) {
|
|
4804
|
-
const label = this.logTime("render");
|
|
4805
|
-
let idx = 0;
|
|
4806
|
-
let top = 0;
|
|
4807
|
-
const height = ROW_HEIGHT;
|
|
4808
|
-
let modified = false;
|
|
4809
|
-
let start = opts === null || opts === void 0 ? void 0 : opts.startIdx;
|
|
4810
|
-
let end = opts === null || opts === void 0 ? void 0 : opts.endIdx;
|
|
4811
|
-
const obsoleteViewNodes = this.viewNodes;
|
|
4812
|
-
this.viewNodes = new Set();
|
|
4813
|
-
let viewNodes = this.viewNodes;
|
|
4814
|
-
// this.debug("render", opts);
|
|
4815
|
-
assert(start != null && end != null);
|
|
4816
|
-
// Make sure start is always even, so the alternating row colors don't
|
|
4817
|
-
// change when scrolling:
|
|
4818
|
-
if (start % 2) {
|
|
4819
|
-
start--;
|
|
4820
|
-
}
|
|
4821
|
-
this.visitRows(function (node) {
|
|
4822
|
-
const prevIdx = node._rowIdx;
|
|
4823
|
-
viewNodes.add(node);
|
|
4824
|
-
obsoleteViewNodes.delete(node);
|
|
4825
|
-
if (prevIdx !== idx) {
|
|
4826
|
-
node._rowIdx = idx;
|
|
4827
|
-
modified = true;
|
|
4828
|
-
}
|
|
4829
|
-
if (idx < start || idx > end) {
|
|
4830
|
-
node._callEvent("discard");
|
|
4831
|
-
node.removeMarkup();
|
|
4832
|
-
}
|
|
4833
|
-
else {
|
|
4834
|
-
// if (!node._rowElem || prevIdx != idx) {
|
|
4835
|
-
node.render({ top: top });
|
|
4836
|
-
}
|
|
4837
|
-
idx++;
|
|
4838
|
-
top += height;
|
|
4839
|
-
});
|
|
4840
|
-
for (const prevNode of obsoleteViewNodes) {
|
|
4841
|
-
prevNode._callEvent("discard");
|
|
4842
|
-
prevNode.removeMarkup();
|
|
4843
|
-
}
|
|
4844
|
-
// Resize tree container
|
|
4845
|
-
this.nodeListElement.style.height = "" + top + "px";
|
|
4846
|
-
// this.log("render()", this.nodeListElement.style.height);
|
|
4847
|
-
this.logTimeEnd(label);
|
|
4848
|
-
return modified;
|
|
4849
|
-
}
|
|
4850
|
-
/**Recalc and apply header columns from `this.columns`. */
|
|
4851
|
-
renderHeader() {
|
|
4852
|
-
if (!this.headerElement) {
|
|
4853
|
-
return;
|
|
4854
|
-
}
|
|
4855
|
-
const headerRow = this.headerElement.querySelector(".wb-row");
|
|
4856
|
-
assert(headerRow);
|
|
4857
|
-
headerRow.innerHTML = "<span class='wb-col'></span>".repeat(this.columns.length);
|
|
4858
|
-
for (let i = 0; i < this.columns.length; i++) {
|
|
4859
|
-
let col = this.columns[i];
|
|
4860
|
-
let colElem = headerRow.children[i];
|
|
4861
|
-
colElem.style.left = col._ofsPx + "px";
|
|
4862
|
-
colElem.style.width = col._widthPx + "px";
|
|
4863
|
-
colElem.textContent = col.title || col.id;
|
|
4864
|
-
}
|
|
4865
|
-
}
|
|
4866
5057
|
/**
|
|
5058
|
+
* Make sure that this node is scrolled into the viewport.
|
|
4867
5059
|
*
|
|
4868
5060
|
* @param {boolean | PlainObject} [effects=false] animation options.
|
|
4869
5061
|
* @param {object} [options=null] {topNode: null, effects: ..., parent: ...}
|
|
4870
5062
|
* this node will remain visible in
|
|
4871
5063
|
* any case, even if `this` is outside the scroll pane.
|
|
4872
|
-
* Make sure that a node is scrolled into the viewport.
|
|
4873
5064
|
*/
|
|
4874
5065
|
scrollTo(opts) {
|
|
4875
5066
|
const MARGIN = 1;
|
|
@@ -4890,30 +5081,18 @@ class Wunderbaum {
|
|
|
4890
5081
|
// Node is above viewport
|
|
4891
5082
|
newTop = nodeOfs + MARGIN;
|
|
4892
5083
|
}
|
|
4893
|
-
this.log("scrollTo(" + nodeOfs + "): " + curTop + " => " + newTop, height);
|
|
4894
5084
|
if (newTop != null) {
|
|
5085
|
+
this.log("scrollTo(" + nodeOfs + "): " + curTop + " => " + newTop, height);
|
|
4895
5086
|
this.scrollContainer.scrollTop = newTop;
|
|
4896
|
-
this.
|
|
4897
|
-
}
|
|
4898
|
-
}
|
|
4899
|
-
/** */
|
|
4900
|
-
setCellMode(mode) {
|
|
4901
|
-
// util.assert(this.cellNavMode);
|
|
4902
|
-
// util.assert(0 <= colIdx && colIdx < this.columns.length);
|
|
4903
|
-
if (mode === this.navMode) {
|
|
4904
|
-
return;
|
|
4905
|
-
}
|
|
4906
|
-
const prevMode = this.navMode;
|
|
4907
|
-
const cellMode = mode !== NavigationMode.row;
|
|
4908
|
-
this.navMode = mode;
|
|
4909
|
-
if (cellMode && prevMode === NavigationMode.row) {
|
|
4910
|
-
this.setColumn(0);
|
|
5087
|
+
this.setModified(ChangeType.vscroll);
|
|
4911
5088
|
}
|
|
4912
|
-
this.element.classList.toggle("wb-cell-mode", cellMode);
|
|
4913
|
-
this.element.classList.toggle("wb-cell-edit-mode", mode === NavigationMode.cellEdit);
|
|
4914
|
-
this.setModified(ChangeType.row, this.activeNode);
|
|
4915
5089
|
}
|
|
4916
|
-
/**
|
|
5090
|
+
/**
|
|
5091
|
+
* Set column #colIdx to 'active'.
|
|
5092
|
+
*
|
|
5093
|
+
* This higlights the column header and -cells by adding the `wb-active` class.
|
|
5094
|
+
* Available in cell-nav and cell-edit mode, not in row-mode.
|
|
5095
|
+
*/
|
|
4917
5096
|
setColumn(colIdx) {
|
|
4918
5097
|
assert(this.navMode !== NavigationMode.row);
|
|
4919
5098
|
assert(0 <= colIdx && colIdx < this.columns.length);
|
|
@@ -4938,7 +5117,7 @@ class Wunderbaum {
|
|
|
4938
5117
|
}
|
|
4939
5118
|
}
|
|
4940
5119
|
}
|
|
4941
|
-
/** */
|
|
5120
|
+
/** Set or remove keybaord focus to the tree container. */
|
|
4942
5121
|
setFocus(flag = true) {
|
|
4943
5122
|
if (flag) {
|
|
4944
5123
|
this.element.focus();
|
|
@@ -4947,88 +5126,145 @@ class Wunderbaum {
|
|
|
4947
5126
|
this.element.blur();
|
|
4948
5127
|
}
|
|
4949
5128
|
}
|
|
4950
|
-
/** */
|
|
4951
5129
|
setModified(change, node, options) {
|
|
4952
|
-
if (
|
|
4953
|
-
|
|
5130
|
+
if (this._disableUpdateCount) {
|
|
5131
|
+
// Assuming that we redraw all when enableUpdate() is re-enabled.
|
|
5132
|
+
// this.log(
|
|
5133
|
+
// `IGNORED setModified(${change}) node=${node} (disable level ${this._disableUpdateCount})`
|
|
5134
|
+
// );
|
|
5135
|
+
return;
|
|
4954
5136
|
}
|
|
4955
|
-
this.
|
|
4956
|
-
if (
|
|
4957
|
-
|
|
5137
|
+
// this.log(`setModified(${change}) node=${node}`);
|
|
5138
|
+
if (!(node instanceof WunderbaumNode)) {
|
|
5139
|
+
options = node;
|
|
5140
|
+
}
|
|
5141
|
+
const immediate = !!getOption(options, "immediate");
|
|
5142
|
+
switch (change) {
|
|
5143
|
+
case ChangeType.any:
|
|
5144
|
+
case ChangeType.structure:
|
|
5145
|
+
case ChangeType.header:
|
|
5146
|
+
this.changeRedrawRequestPending = true;
|
|
5147
|
+
this.updateViewport(immediate);
|
|
5148
|
+
break;
|
|
5149
|
+
case ChangeType.vscroll:
|
|
5150
|
+
this.updateViewport(immediate);
|
|
5151
|
+
break;
|
|
5152
|
+
case ChangeType.row:
|
|
5153
|
+
case ChangeType.status:
|
|
5154
|
+
// Single nodes are immedialtely updated if already inside the viewport
|
|
5155
|
+
// (otherwise we can ignore)
|
|
5156
|
+
if (node._rowElem) {
|
|
5157
|
+
node.render();
|
|
5158
|
+
}
|
|
5159
|
+
break;
|
|
5160
|
+
default:
|
|
5161
|
+
error(`Invalid change type ${change}`);
|
|
4958
5162
|
}
|
|
4959
|
-
|
|
4960
|
-
|
|
4961
|
-
|
|
4962
|
-
|
|
4963
|
-
|
|
4964
|
-
|
|
4965
|
-
|
|
4966
|
-
}
|
|
5163
|
+
}
|
|
5164
|
+
/** Set the tree's navigation mode. */
|
|
5165
|
+
setNavigationMode(mode) {
|
|
5166
|
+
// util.assert(this.cellNavMode);
|
|
5167
|
+
// util.assert(0 <= colIdx && colIdx < this.columns.length);
|
|
5168
|
+
if (mode === this.navMode) {
|
|
5169
|
+
return;
|
|
4967
5170
|
}
|
|
4968
|
-
|
|
5171
|
+
const prevMode = this.navMode;
|
|
5172
|
+
const cellMode = mode !== NavigationMode.row;
|
|
5173
|
+
this.navMode = mode;
|
|
5174
|
+
if (cellMode && prevMode === NavigationMode.row) {
|
|
5175
|
+
this.setColumn(0);
|
|
5176
|
+
}
|
|
5177
|
+
this.element.classList.toggle("wb-cell-mode", cellMode);
|
|
5178
|
+
this.element.classList.toggle("wb-cell-edit-mode", mode === NavigationMode.cellEdit);
|
|
5179
|
+
this.setModified(ChangeType.row, this.activeNode);
|
|
4969
5180
|
}
|
|
5181
|
+
/** Display tree status (ok, loading, error, noData) using styles and a dummy root node. */
|
|
4970
5182
|
setStatus(status, message, details) {
|
|
4971
5183
|
return this.root.setStatus(status, message, details);
|
|
4972
5184
|
}
|
|
4973
5185
|
/** Update column headers and width. */
|
|
4974
5186
|
updateColumns(opts) {
|
|
4975
|
-
|
|
4976
|
-
|
|
4977
|
-
|
|
5187
|
+
opts = Object.assign({ calculateCols: true, updateRows: true }, opts);
|
|
5188
|
+
const minWidth = 4;
|
|
5189
|
+
const vpWidth = this.element.clientWidth;
|
|
4978
5190
|
let totalWeight = 0;
|
|
4979
5191
|
let fixedWidth = 0;
|
|
4980
|
-
|
|
4981
|
-
|
|
4982
|
-
|
|
4983
|
-
this._columnsById
|
|
4984
|
-
let
|
|
4985
|
-
|
|
4986
|
-
|
|
4987
|
-
|
|
4988
|
-
|
|
4989
|
-
|
|
4990
|
-
|
|
4991
|
-
|
|
4992
|
-
|
|
4993
|
-
|
|
4994
|
-
|
|
4995
|
-
|
|
4996
|
-
|
|
4997
|
-
|
|
4998
|
-
col._widthPx
|
|
5192
|
+
let modified = false;
|
|
5193
|
+
if (opts.calculateCols) {
|
|
5194
|
+
// Gather width requests
|
|
5195
|
+
this._columnsById = {};
|
|
5196
|
+
for (let col of this.columns) {
|
|
5197
|
+
this._columnsById[col.id] = col;
|
|
5198
|
+
let cw = col.width;
|
|
5199
|
+
if (!cw || cw === "*") {
|
|
5200
|
+
col._weight = 1.0;
|
|
5201
|
+
totalWeight += 1.0;
|
|
5202
|
+
}
|
|
5203
|
+
else if (typeof cw === "number") {
|
|
5204
|
+
col._weight = cw;
|
|
5205
|
+
totalWeight += cw;
|
|
5206
|
+
}
|
|
5207
|
+
else if (typeof cw === "string" && cw.endsWith("px")) {
|
|
5208
|
+
col._weight = 0;
|
|
5209
|
+
let px = parseFloat(cw.slice(0, -2));
|
|
5210
|
+
if (col._widthPx != px) {
|
|
5211
|
+
modified = true;
|
|
5212
|
+
col._widthPx = px;
|
|
5213
|
+
}
|
|
5214
|
+
fixedWidth += px;
|
|
5215
|
+
}
|
|
5216
|
+
else {
|
|
5217
|
+
error("Invalid column width: " + cw);
|
|
4999
5218
|
}
|
|
5000
|
-
fixedWidth += px;
|
|
5001
5219
|
}
|
|
5002
|
-
|
|
5003
|
-
|
|
5004
|
-
|
|
5005
|
-
|
|
5006
|
-
|
|
5007
|
-
|
|
5008
|
-
|
|
5009
|
-
|
|
5010
|
-
|
|
5011
|
-
|
|
5012
|
-
if (col._widthPx != px) {
|
|
5013
|
-
modified = true;
|
|
5014
|
-
col._widthPx = px;
|
|
5220
|
+
// Share remaining space between non-fixed columns
|
|
5221
|
+
const restPx = Math.max(0, vpWidth - fixedWidth);
|
|
5222
|
+
let ofsPx = 0;
|
|
5223
|
+
for (let col of this.columns) {
|
|
5224
|
+
if (col._weight) {
|
|
5225
|
+
const px = Math.max(minWidth, (restPx * col._weight) / totalWeight);
|
|
5226
|
+
if (col._widthPx != px) {
|
|
5227
|
+
modified = true;
|
|
5228
|
+
col._widthPx = px;
|
|
5229
|
+
}
|
|
5015
5230
|
}
|
|
5231
|
+
col._ofsPx = ofsPx;
|
|
5232
|
+
ofsPx += col._widthPx;
|
|
5016
5233
|
}
|
|
5017
|
-
col._ofsPx = ofsPx;
|
|
5018
|
-
ofsPx += col._widthPx;
|
|
5019
5234
|
}
|
|
5020
5235
|
// Every column has now a calculated `_ofsPx` and `_widthPx`
|
|
5021
5236
|
// this.logInfo("UC", this.columns, vpWidth, this.element.clientWidth, this.element);
|
|
5022
5237
|
// console.trace();
|
|
5023
5238
|
// util.error("BREAK");
|
|
5024
5239
|
if (modified) {
|
|
5025
|
-
this.
|
|
5026
|
-
if (opts.
|
|
5027
|
-
this.
|
|
5240
|
+
this._renderHeaderMarkup();
|
|
5241
|
+
if (opts.updateRows) {
|
|
5242
|
+
this._updateRows();
|
|
5028
5243
|
}
|
|
5029
5244
|
}
|
|
5030
5245
|
}
|
|
5031
|
-
/**
|
|
5246
|
+
/** Create/update header markup from `this.columns` definition.
|
|
5247
|
+
* @internal
|
|
5248
|
+
*/
|
|
5249
|
+
_renderHeaderMarkup() {
|
|
5250
|
+
if (!this.headerElement) {
|
|
5251
|
+
return;
|
|
5252
|
+
}
|
|
5253
|
+
const headerRow = this.headerElement.querySelector(".wb-row");
|
|
5254
|
+
assert(headerRow);
|
|
5255
|
+
headerRow.innerHTML = "<span class='wb-col'></span>".repeat(this.columns.length);
|
|
5256
|
+
for (let i = 0; i < this.columns.length; i++) {
|
|
5257
|
+
const col = this.columns[i];
|
|
5258
|
+
const colElem = headerRow.children[i];
|
|
5259
|
+
colElem.style.left = col._ofsPx + "px";
|
|
5260
|
+
colElem.style.width = col._widthPx + "px";
|
|
5261
|
+
// colElem.textContent = col.title || col.id;
|
|
5262
|
+
const title = escapeHtml(col.title || col.id);
|
|
5263
|
+
colElem.innerHTML = `<span class="wb-col-title">${title}</span> <span class="wb-col-resizer"></span>`;
|
|
5264
|
+
// colElem.innerHTML = `${title} <span class="wb-col-resizer"></span>`;
|
|
5265
|
+
}
|
|
5266
|
+
}
|
|
5267
|
+
/** Render header and all rows that are visible in the viewport (async, throttled). */
|
|
5032
5268
|
updateViewport(immediate = false) {
|
|
5033
5269
|
// Call the `throttle` wrapper for `this._updateViewport()` which will
|
|
5034
5270
|
// execute immediately on the leading edge of a sequence:
|
|
@@ -5037,39 +5273,163 @@ class Wunderbaum {
|
|
|
5037
5273
|
this._updateViewportThrottled.flush();
|
|
5038
5274
|
}
|
|
5039
5275
|
}
|
|
5276
|
+
/**
|
|
5277
|
+
* This is the actual update method, which is wrapped inside a throttle method.
|
|
5278
|
+
* This protected method should not be called directly but via
|
|
5279
|
+
* `tree.updateViewport()` or `tree.setModified()`.
|
|
5280
|
+
* It calls `updateColumns()` and `_updateRows()`.
|
|
5281
|
+
* @internal
|
|
5282
|
+
*/
|
|
5040
5283
|
_updateViewport() {
|
|
5041
|
-
if (this.
|
|
5284
|
+
if (this._disableUpdateCount) {
|
|
5285
|
+
this.log(`IGNORED _updateViewport() disable level: ${this._disableUpdateCount}`);
|
|
5042
5286
|
return;
|
|
5043
5287
|
}
|
|
5288
|
+
const newNodesOnly = !this.changeRedrawRequestPending;
|
|
5289
|
+
this.changeRedrawRequestPending = false;
|
|
5044
5290
|
let height = this.scrollContainer.clientHeight;
|
|
5045
|
-
// We cannot get the height for
|
|
5291
|
+
// We cannot get the height for absolute positioned parent, so look at first col
|
|
5046
5292
|
// let headerHeight = this.headerElement.clientHeight
|
|
5047
5293
|
// let headerHeight = this.headerElement.children[0].children[0].clientHeight;
|
|
5048
5294
|
const headerHeight = this.options.headerHeightPx;
|
|
5049
|
-
|
|
5050
|
-
let ofs = this.scrollContainer.scrollTop;
|
|
5295
|
+
const wantHeight = this.element.clientHeight - headerHeight;
|
|
5051
5296
|
if (Math.abs(height - wantHeight) > 1.0) {
|
|
5052
5297
|
// this.log("resize", height, wantHeight);
|
|
5053
5298
|
this.scrollContainer.style.height = wantHeight + "px";
|
|
5054
5299
|
height = wantHeight;
|
|
5055
5300
|
}
|
|
5056
|
-
this.updateColumns({
|
|
5057
|
-
this.
|
|
5058
|
-
startIdx: Math.max(0, ofs / ROW_HEIGHT - RENDER_MAX_PREFETCH),
|
|
5059
|
-
endIdx: Math.max(0, (ofs + height) / ROW_HEIGHT + RENDER_MAX_PREFETCH),
|
|
5060
|
-
});
|
|
5301
|
+
this.updateColumns({ updateRows: false });
|
|
5302
|
+
this._updateRows({ newNodesOnly: newNodesOnly });
|
|
5061
5303
|
this._callEvent("update");
|
|
5062
5304
|
}
|
|
5063
|
-
/**
|
|
5305
|
+
/**
|
|
5306
|
+
* Assert that TR order matches the natural node order
|
|
5307
|
+
* @internal
|
|
5308
|
+
*/
|
|
5309
|
+
_validateRows() {
|
|
5310
|
+
let trs = this.nodeListElement.childNodes;
|
|
5311
|
+
let i = 0;
|
|
5312
|
+
let prev = -1;
|
|
5313
|
+
let ok = true;
|
|
5314
|
+
trs.forEach((element) => {
|
|
5315
|
+
const tr = element;
|
|
5316
|
+
const top = Number.parseInt(tr.style.top);
|
|
5317
|
+
const n = tr._wb_node;
|
|
5318
|
+
// if (i < 4) {
|
|
5319
|
+
// console.info(
|
|
5320
|
+
// `TR#${i}, rowIdx=${n._rowIdx} , top=${top}px: '${n.title}'`
|
|
5321
|
+
// );
|
|
5322
|
+
// }
|
|
5323
|
+
if (top <= prev) {
|
|
5324
|
+
console.warn(`TR order mismatch at index ${i}: top=${top}px, node=${n}`);
|
|
5325
|
+
// throw new Error("fault");
|
|
5326
|
+
ok = false;
|
|
5327
|
+
}
|
|
5328
|
+
prev = top;
|
|
5329
|
+
i++;
|
|
5330
|
+
});
|
|
5331
|
+
return ok;
|
|
5332
|
+
}
|
|
5333
|
+
/*
|
|
5334
|
+
* - Traverse all *visible* of the whole tree, i.e. skip collapsed nodes.
|
|
5335
|
+
* - Store count of rows to `tree.treeRowCount`.
|
|
5336
|
+
* - Renumber `node._rowIdx` for all visible nodes.
|
|
5337
|
+
* - Calculate the index range that must be rendered to fill the viewport
|
|
5338
|
+
* (including upper and lower prefetch)
|
|
5339
|
+
* -
|
|
5340
|
+
*/
|
|
5341
|
+
_updateRows(opts) {
|
|
5342
|
+
const label = this.logTime("_updateRows");
|
|
5343
|
+
opts = Object.assign({ newNodesOnly: false }, opts);
|
|
5344
|
+
const newNodesOnly = !!opts.newNodesOnly;
|
|
5345
|
+
const row_height = ROW_HEIGHT;
|
|
5346
|
+
const vp_height = this.scrollContainer.clientHeight;
|
|
5347
|
+
const prefetch = RENDER_MAX_PREFETCH;
|
|
5348
|
+
const ofs = this.scrollContainer.scrollTop;
|
|
5349
|
+
let startIdx = Math.max(0, ofs / row_height - prefetch);
|
|
5350
|
+
startIdx = Math.floor(startIdx);
|
|
5351
|
+
// Make sure start is always even, so the alternating row colors don't
|
|
5352
|
+
// change when scrolling:
|
|
5353
|
+
if (startIdx % 2) {
|
|
5354
|
+
startIdx--;
|
|
5355
|
+
}
|
|
5356
|
+
let endIdx = Math.max(0, (ofs + vp_height) / row_height + prefetch);
|
|
5357
|
+
endIdx = Math.ceil(endIdx);
|
|
5358
|
+
// const obsoleteViewNodes = this.viewNodes;
|
|
5359
|
+
// this.viewNodes = new Set();
|
|
5360
|
+
// const viewNodes = this.viewNodes;
|
|
5361
|
+
// this.debug("render", opts);
|
|
5362
|
+
const obsoleteNodes = new Set();
|
|
5363
|
+
this.nodeListElement.childNodes.forEach((elem) => {
|
|
5364
|
+
const tr = elem;
|
|
5365
|
+
obsoleteNodes.add(tr._wb_node);
|
|
5366
|
+
});
|
|
5367
|
+
let idx = 0;
|
|
5368
|
+
let top = 0;
|
|
5369
|
+
let modified = false;
|
|
5370
|
+
let prevElem = "first";
|
|
5371
|
+
this.visitRows(function (node) {
|
|
5372
|
+
// console.log("visit", node)
|
|
5373
|
+
const rowDiv = node._rowElem;
|
|
5374
|
+
// Renumber all expanded nodes
|
|
5375
|
+
if (node._rowIdx !== idx) {
|
|
5376
|
+
node._rowIdx = idx;
|
|
5377
|
+
modified = true;
|
|
5378
|
+
}
|
|
5379
|
+
if (idx < startIdx || idx > endIdx) {
|
|
5380
|
+
// row is outside viewport bounds
|
|
5381
|
+
if (rowDiv) {
|
|
5382
|
+
prevElem = rowDiv;
|
|
5383
|
+
}
|
|
5384
|
+
}
|
|
5385
|
+
else if (rowDiv && newNodesOnly) {
|
|
5386
|
+
obsoleteNodes.delete(node);
|
|
5387
|
+
// no need to update existing node markup
|
|
5388
|
+
rowDiv.style.top = idx * ROW_HEIGHT + "px";
|
|
5389
|
+
prevElem = rowDiv;
|
|
5390
|
+
}
|
|
5391
|
+
else {
|
|
5392
|
+
obsoleteNodes.delete(node);
|
|
5393
|
+
// Create new markup
|
|
5394
|
+
node.render({ top: top, after: prevElem });
|
|
5395
|
+
// console.log("render", top, prevElem, "=>", node._rowElem);
|
|
5396
|
+
prevElem = node._rowElem;
|
|
5397
|
+
}
|
|
5398
|
+
idx++;
|
|
5399
|
+
top += row_height;
|
|
5400
|
+
});
|
|
5401
|
+
this.treeRowCount = idx;
|
|
5402
|
+
for (const n of obsoleteNodes) {
|
|
5403
|
+
n._callEvent("discard");
|
|
5404
|
+
n.removeMarkup();
|
|
5405
|
+
}
|
|
5406
|
+
// Resize tree container
|
|
5407
|
+
this.nodeListElement.style.height = `${top}px`;
|
|
5408
|
+
// this.log(
|
|
5409
|
+
// `render(scrollOfs:${ofs}, ${startIdx}..${endIdx})`,
|
|
5410
|
+
// this.nodeListElement.style.height
|
|
5411
|
+
// );
|
|
5412
|
+
this.logTimeEnd(label);
|
|
5413
|
+
this._validateRows();
|
|
5414
|
+
return modified;
|
|
5415
|
+
}
|
|
5416
|
+
/**
|
|
5417
|
+
* Call callback(node) for all nodes in hierarchical order (depth-first).
|
|
5064
5418
|
*
|
|
5065
5419
|
* @param {function} callback the callback function.
|
|
5066
|
-
* Return false to stop iteration, return "skip" to skip this node and
|
|
5420
|
+
* Return false to stop iteration, return "skip" to skip this node and
|
|
5421
|
+
* children only.
|
|
5067
5422
|
* @returns {boolean} false, if the iterator was stopped.
|
|
5068
5423
|
*/
|
|
5069
5424
|
visit(callback) {
|
|
5070
5425
|
return this.root.visit(callback, false);
|
|
5071
5426
|
}
|
|
5072
|
-
/**
|
|
5427
|
+
/**
|
|
5428
|
+
* Call fn(node) for all nodes in vertical order, top down (or bottom up).
|
|
5429
|
+
*
|
|
5430
|
+
* Note that this considers expansion state, i.e. children of collapsed nodes
|
|
5431
|
+
* are skipped.
|
|
5432
|
+
*
|
|
5073
5433
|
* Stop iteration, if fn() returns false.<br>
|
|
5074
5434
|
* Return false if iteration was stopped.
|
|
5075
5435
|
*
|
|
@@ -5149,7 +5509,8 @@ class Wunderbaum {
|
|
|
5149
5509
|
}
|
|
5150
5510
|
return true;
|
|
5151
5511
|
}
|
|
5152
|
-
/**
|
|
5512
|
+
/**
|
|
5513
|
+
* Call fn(node) for all nodes in vertical order, bottom up.
|
|
5153
5514
|
* @internal
|
|
5154
5515
|
*/
|
|
5155
5516
|
_visitRowsUp(callback, opts) {
|
|
@@ -5193,19 +5554,36 @@ class Wunderbaum {
|
|
|
5193
5554
|
}
|
|
5194
5555
|
return true;
|
|
5195
5556
|
}
|
|
5196
|
-
/**
|
|
5557
|
+
/**
|
|
5558
|
+
* Reload the tree with a new source.
|
|
5559
|
+
*
|
|
5560
|
+
* Previous data is cleared.
|
|
5561
|
+
* Pass `options.columns` to define a header (may also be part of `source.columns`).
|
|
5562
|
+
*/
|
|
5197
5563
|
load(source, options = {}) {
|
|
5198
5564
|
this.clear();
|
|
5199
5565
|
const columns = options.columns || source.columns;
|
|
5200
5566
|
if (columns) {
|
|
5201
5567
|
this.columns = options.columns;
|
|
5202
|
-
this.
|
|
5203
|
-
|
|
5568
|
+
// this._renderHeaderMarkup();
|
|
5569
|
+
this.updateColumns({ calculateCols: false });
|
|
5204
5570
|
}
|
|
5205
5571
|
return this.root.load(source);
|
|
5206
5572
|
}
|
|
5207
5573
|
/**
|
|
5574
|
+
* Disable render requests during operations that would trigger many updates.
|
|
5208
5575
|
*
|
|
5576
|
+
* ```js
|
|
5577
|
+
* try {
|
|
5578
|
+
* tree.enableUpdate(false);
|
|
5579
|
+
* // ... (long running operation that would trigger many updates)
|
|
5580
|
+
* foo();
|
|
5581
|
+
* // ... NOTE: make sure that async operations have finished
|
|
5582
|
+
* await foo();
|
|
5583
|
+
* } finally {
|
|
5584
|
+
* tree.enableUpdate(true);
|
|
5585
|
+
* }
|
|
5586
|
+
* ```
|
|
5209
5587
|
*/
|
|
5210
5588
|
enableUpdate(flag) {
|
|
5211
5589
|
/*
|
|
@@ -5213,20 +5591,22 @@ class Wunderbaum {
|
|
|
5213
5591
|
1 >-------------------------------------<
|
|
5214
5592
|
2 >--------------------<
|
|
5215
5593
|
3 >--------------------------<
|
|
5216
|
-
|
|
5217
|
-
5
|
|
5218
|
-
|
|
5219
5594
|
*/
|
|
5220
|
-
// this.logDebug( `enableUpdate(${flag}): count=${this._disableUpdateCount}...` );
|
|
5221
5595
|
if (flag) {
|
|
5222
|
-
assert(this._disableUpdateCount > 0);
|
|
5596
|
+
assert(this._disableUpdateCount > 0, "enableUpdate(true) was called too often");
|
|
5223
5597
|
this._disableUpdateCount--;
|
|
5598
|
+
// this.logDebug(
|
|
5599
|
+
// `enableUpdate(${flag}): count -> ${this._disableUpdateCount}...`
|
|
5600
|
+
// );
|
|
5224
5601
|
if (this._disableUpdateCount === 0) {
|
|
5225
5602
|
this.updateViewport();
|
|
5226
5603
|
}
|
|
5227
5604
|
}
|
|
5228
5605
|
else {
|
|
5229
5606
|
this._disableUpdateCount++;
|
|
5607
|
+
// this.logDebug(
|
|
5608
|
+
// `enableUpdate(${flag}): count -> ${this._disableUpdateCount}...`
|
|
5609
|
+
// );
|
|
5230
5610
|
// this._disableUpdate = Date.now();
|
|
5231
5611
|
}
|
|
5232
5612
|
// return !flag; // return previous value
|
|
@@ -5259,8 +5639,10 @@ class Wunderbaum {
|
|
|
5259
5639
|
return this.extensions.filter.updateFilter();
|
|
5260
5640
|
}
|
|
5261
5641
|
}
|
|
5262
|
-
Wunderbaum.version = "v0.0.1-0"; // Set to semver by 'grunt release'
|
|
5263
5642
|
Wunderbaum.sequence = 0;
|
|
5643
|
+
/** Wunderbaum release version number "MAJOR.MINOR.PATCH". */
|
|
5644
|
+
Wunderbaum.version = "v0.0.3"; // Set to semver by 'grunt release'
|
|
5645
|
+
/** Expose some useful methods of the util.ts module as `Wunderbaum.util`. */
|
|
5264
5646
|
Wunderbaum.util = util;
|
|
5265
5647
|
|
|
5266
5648
|
export { Wunderbaum };
|