wunderbaum 0.0.3 → 0.0.6
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 +3 -1
- package/dist/wunderbaum.css +1 -1
- package/dist/wunderbaum.d.ts +663 -332
- package/dist/wunderbaum.esm.js +1263 -694
- package/dist/wunderbaum.esm.min.js +34 -27
- package/dist/wunderbaum.esm.min.js.map +1 -1
- package/dist/wunderbaum.umd.js +1263 -694
- package/dist/wunderbaum.umd.min.js +38 -33
- package/dist/wunderbaum.umd.min.js.map +1 -1
- package/package.json +4 -4
- package/src/common.ts +24 -132
- package/src/deferred.ts +12 -2
- package/src/types.ts +470 -0
- package/src/util.ts +104 -7
- package/src/wb_ext_dnd.ts +12 -163
- package/src/wb_ext_edit.ts +12 -13
- package/src/wb_ext_filter.ts +17 -5
- package/src/wb_ext_keynav.ts +138 -35
- package/src/wb_extension_base.ts +3 -3
- package/src/wb_node.ts +425 -257
- package/src/wb_options.ts +104 -34
- package/src/wunderbaum.scss +253 -102
- package/src/wunderbaum.ts +526 -264
package/dist/wunderbaum.esm.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
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.6, Sat, 10 Sep 2022 19:29:21 GMT (https://github.com/mar10/wunderbaum)
|
|
5
5
|
*/
|
|
6
6
|
/** @module util */
|
|
7
7
|
/** Readable names for `MouseEvent.button` */
|
|
@@ -173,7 +173,7 @@ function extractHtmlText(s) {
|
|
|
173
173
|
*
|
|
174
174
|
* If a `<span class="wb-col">` is passed, the first child input is used.
|
|
175
175
|
* Depending on the target element type, `value` is interpreted accordingly.
|
|
176
|
-
* For example for a checkbox, a value of true, false, or null is returned if
|
|
176
|
+
* For example for a checkbox, a value of true, false, or null is returned if
|
|
177
177
|
* the element is checked, unchecked, or indeterminate.
|
|
178
178
|
* For datetime input control a numerical value is assumed, etc.
|
|
179
179
|
*
|
|
@@ -262,6 +262,7 @@ function setValueToElem(elem, value) {
|
|
|
262
262
|
if (embeddedInput) {
|
|
263
263
|
return setValueToElem(embeddedInput, value);
|
|
264
264
|
}
|
|
265
|
+
// No embedded input: simply write as escaped html
|
|
265
266
|
span.innerText = "" + value;
|
|
266
267
|
}
|
|
267
268
|
else if (tag === "INPUT") {
|
|
@@ -269,7 +270,9 @@ function setValueToElem(elem, value) {
|
|
|
269
270
|
const type = input.type;
|
|
270
271
|
switch (type) {
|
|
271
272
|
case "checkbox":
|
|
272
|
-
|
|
273
|
+
// An explicit `null` value is interpreted as 'indeterminate'.
|
|
274
|
+
// `undefined` is interpreted as 'unchecked'
|
|
275
|
+
input.indeterminate = value === null;
|
|
273
276
|
input.checked = !!value;
|
|
274
277
|
break;
|
|
275
278
|
case "date":
|
|
@@ -299,14 +302,30 @@ function setValueToElem(elem, value) {
|
|
|
299
302
|
break;
|
|
300
303
|
case "text":
|
|
301
304
|
default:
|
|
302
|
-
input.
|
|
305
|
+
input.value = value || "";
|
|
303
306
|
}
|
|
304
307
|
}
|
|
305
308
|
else if (tag === "SELECT") {
|
|
306
309
|
const select = elem;
|
|
307
|
-
|
|
310
|
+
if (value == null) {
|
|
311
|
+
select.selectedIndex = -1;
|
|
312
|
+
}
|
|
313
|
+
else {
|
|
314
|
+
select.value = value;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
/** Show/hide element by setting the `display`style to 'none'. */
|
|
319
|
+
function setElemDisplay(elem, flag) {
|
|
320
|
+
const style = elemFromSelector(elem).style;
|
|
321
|
+
if (flag) {
|
|
322
|
+
if (style.display === "none") {
|
|
323
|
+
style.display = "";
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
else if (style.display === "") {
|
|
327
|
+
style.display = "none";
|
|
308
328
|
}
|
|
309
|
-
// return value;
|
|
310
329
|
}
|
|
311
330
|
/** Create and return an unconnected `HTMLElement` from a HTML string. */
|
|
312
331
|
function elemFromHtml(html) {
|
|
@@ -479,7 +498,7 @@ function setTimeoutPromise(callback, ms) {
|
|
|
479
498
|
return new Promise((resolve, reject) => {
|
|
480
499
|
setTimeout(() => {
|
|
481
500
|
try {
|
|
482
|
-
resolve(callback.apply(
|
|
501
|
+
resolve(callback.apply(this));
|
|
483
502
|
}
|
|
484
503
|
catch (err) {
|
|
485
504
|
reject(err);
|
|
@@ -566,13 +585,79 @@ function toSet(val) {
|
|
|
566
585
|
}
|
|
567
586
|
throw new Error("Cannot convert to Set<string>: " + val);
|
|
568
587
|
}
|
|
569
|
-
/**Return a canonical string representation for an object's type (e.g. 'array', 'number', ...) */
|
|
588
|
+
/** Return a canonical string representation for an object's type (e.g. 'array', 'number', ...). */
|
|
570
589
|
function type(obj) {
|
|
571
590
|
return Object.prototype.toString
|
|
572
591
|
.call(obj)
|
|
573
592
|
.replace(/^\[object (.+)\]$/, "$1")
|
|
574
593
|
.toLowerCase();
|
|
575
594
|
}
|
|
595
|
+
/**
|
|
596
|
+
* Return a function that can be called instead of `callback`, but guarantees
|
|
597
|
+
* a limited execution rate.
|
|
598
|
+
* The execution rate is calculated based on the runtime duration of the
|
|
599
|
+
* previous call.
|
|
600
|
+
* Example:
|
|
601
|
+
* ```js
|
|
602
|
+
* throttledFoo = util.adaptiveThrottle(foo.bind(this), {});
|
|
603
|
+
* throttledFoo();
|
|
604
|
+
* throttledFoo();
|
|
605
|
+
* ```
|
|
606
|
+
*/
|
|
607
|
+
function adaptiveThrottle(callback, options) {
|
|
608
|
+
let waiting = 0; // Initially, we're not waiting
|
|
609
|
+
let pendingArgs = null;
|
|
610
|
+
const opts = Object.assign({
|
|
611
|
+
minDelay: 16,
|
|
612
|
+
defaultDelay: 200,
|
|
613
|
+
maxDelay: 5000,
|
|
614
|
+
delayFactor: 2.0,
|
|
615
|
+
}, options);
|
|
616
|
+
const minDelay = Math.max(16, +opts.minDelay);
|
|
617
|
+
const maxDelay = +opts.maxDelay;
|
|
618
|
+
const throttledFn = (...args) => {
|
|
619
|
+
if (waiting) {
|
|
620
|
+
pendingArgs = args;
|
|
621
|
+
// console.log(`adaptiveThrottle() queing request #${waiting}...`, args);
|
|
622
|
+
waiting += 1;
|
|
623
|
+
}
|
|
624
|
+
else {
|
|
625
|
+
// Prevent invocations while running or blocking
|
|
626
|
+
waiting = 1;
|
|
627
|
+
const useArgs = args; // pendingArgs || args;
|
|
628
|
+
pendingArgs = null;
|
|
629
|
+
// console.log(`adaptiveThrottle() execute...`, useArgs);
|
|
630
|
+
const start = Date.now();
|
|
631
|
+
try {
|
|
632
|
+
callback.apply(this, useArgs);
|
|
633
|
+
}
|
|
634
|
+
catch (error) {
|
|
635
|
+
console.error(error);
|
|
636
|
+
}
|
|
637
|
+
const elap = Date.now() - start;
|
|
638
|
+
const curDelay = Math.min(Math.max(minDelay, elap * opts.delayFactor), maxDelay);
|
|
639
|
+
const useDelay = Math.max(minDelay, curDelay - elap);
|
|
640
|
+
// console.log(
|
|
641
|
+
// `adaptiveThrottle() calling worker took ${elap}ms. delay = ${curDelay}ms, using ${useDelay}ms`,
|
|
642
|
+
// pendingArgs
|
|
643
|
+
// );
|
|
644
|
+
setTimeout(() => {
|
|
645
|
+
// Unblock, and trigger pending requests if any
|
|
646
|
+
// const skipped = waiting - 1;
|
|
647
|
+
waiting = 0; // And allow future invocations
|
|
648
|
+
if (pendingArgs != null) {
|
|
649
|
+
// There was another request while running or waiting
|
|
650
|
+
// console.log(
|
|
651
|
+
// `adaptiveThrottle() re-trigger (missed ${skipped})...`,
|
|
652
|
+
// pendingArgs
|
|
653
|
+
// );
|
|
654
|
+
throttledFn.apply(this, pendingArgs);
|
|
655
|
+
}
|
|
656
|
+
}, useDelay);
|
|
657
|
+
}
|
|
658
|
+
};
|
|
659
|
+
return throttledFn;
|
|
660
|
+
}
|
|
576
661
|
|
|
577
662
|
var util = /*#__PURE__*/Object.freeze({
|
|
578
663
|
__proto__: null,
|
|
@@ -591,6 +676,7 @@ var util = /*#__PURE__*/Object.freeze({
|
|
|
591
676
|
extractHtmlText: extractHtmlText,
|
|
592
677
|
getValueFromElem: getValueFromElem,
|
|
593
678
|
setValueToElem: setValueToElem,
|
|
679
|
+
setElemDisplay: setElemDisplay,
|
|
594
680
|
elemFromHtml: elemFromHtml,
|
|
595
681
|
elemFromSelector: elemFromSelector,
|
|
596
682
|
eventTargetFromSelector: eventTargetFromSelector,
|
|
@@ -608,29 +694,34 @@ var util = /*#__PURE__*/Object.freeze({
|
|
|
608
694
|
toggleCheckbox: toggleCheckbox,
|
|
609
695
|
getOption: getOption,
|
|
610
696
|
toSet: toSet,
|
|
611
|
-
type: type
|
|
697
|
+
type: type,
|
|
698
|
+
adaptiveThrottle: adaptiveThrottle
|
|
612
699
|
});
|
|
613
700
|
|
|
614
701
|
/*!
|
|
615
|
-
* Wunderbaum -
|
|
702
|
+
* Wunderbaum - types
|
|
616
703
|
* Copyright (c) 2021-2022, Martin Wendt. Released under the MIT license.
|
|
617
|
-
* v0.0.
|
|
704
|
+
* v0.0.6, Sat, 10 Sep 2022 19:29:21 GMT (https://github.com/mar10/wunderbaum)
|
|
618
705
|
*/
|
|
619
|
-
|
|
620
|
-
const ROW_HEIGHT = 22;
|
|
621
|
-
const ICON_WIDTH = 20;
|
|
622
|
-
const ROW_EXTRA_PAD = 7; // 2x $col-padding-x + 3px rounding errors
|
|
623
|
-
const RENDER_MAX_PREFETCH = 5;
|
|
624
|
-
const TEST_IMG = new RegExp(/\.|\//); // strings are considered image urls if they contain '.' or '/'
|
|
706
|
+
/** Possible values for `setModified()`. */
|
|
625
707
|
var ChangeType;
|
|
626
708
|
(function (ChangeType) {
|
|
709
|
+
/** Re-render the whole viewport, headers, and all rows. */
|
|
627
710
|
ChangeType["any"] = "any";
|
|
711
|
+
/** Update current row title, icon, columns, and status. */
|
|
712
|
+
ChangeType["data"] = "data";
|
|
713
|
+
/** Redraw the header and update the width of all row columns. */
|
|
714
|
+
ChangeType["header"] = "header";
|
|
715
|
+
/** Re-render the whole current row. */
|
|
628
716
|
ChangeType["row"] = "row";
|
|
717
|
+
/** Alias for 'any'. */
|
|
629
718
|
ChangeType["structure"] = "structure";
|
|
719
|
+
/** Update current row's classes, to reflect active, selected, ... */
|
|
630
720
|
ChangeType["status"] = "status";
|
|
721
|
+
/** Update the 'top' property of all rows. */
|
|
631
722
|
ChangeType["vscroll"] = "vscroll";
|
|
632
|
-
ChangeType["header"] = "header";
|
|
633
723
|
})(ChangeType || (ChangeType = {}));
|
|
724
|
+
/** Possible values for `setStatus()`. */
|
|
634
725
|
var NodeStatusType;
|
|
635
726
|
(function (NodeStatusType) {
|
|
636
727
|
NodeStatusType["ok"] = "ok";
|
|
@@ -639,7 +730,7 @@ var NodeStatusType;
|
|
|
639
730
|
NodeStatusType["noData"] = "noData";
|
|
640
731
|
// paging = "paging",
|
|
641
732
|
})(NodeStatusType || (NodeStatusType = {}));
|
|
642
|
-
/**Define the subregion of a node, where an event occurred. */
|
|
733
|
+
/** Define the subregion of a node, where an event occurred. */
|
|
643
734
|
var TargetType;
|
|
644
735
|
(function (TargetType) {
|
|
645
736
|
TargetType["unknown"] = "";
|
|
@@ -650,87 +741,19 @@ var TargetType;
|
|
|
650
741
|
TargetType["prefix"] = "prefix";
|
|
651
742
|
TargetType["title"] = "title";
|
|
652
743
|
})(TargetType || (TargetType = {}));
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
// expanderExpanded: "bi bi-dash-square",
|
|
662
|
-
expanderCollapsed: "bi bi-chevron-right",
|
|
663
|
-
// expanderCollapsed: "bi bi-plus-square",
|
|
664
|
-
expanderLazy: "bi bi-chevron-right wb-helper-lazy-expander",
|
|
665
|
-
// expanderLazy: "bi bi-chevron-bar-right",
|
|
666
|
-
checkChecked: "bi bi-check-square",
|
|
667
|
-
checkUnchecked: "bi bi-square",
|
|
668
|
-
checkUnknown: "bi dash-square-dotted",
|
|
669
|
-
radioChecked: "bi bi-circle-fill",
|
|
670
|
-
radioUnchecked: "bi bi-circle",
|
|
671
|
-
radioUnknown: "bi bi-circle-dotted",
|
|
672
|
-
folder: "bi bi-folder2",
|
|
673
|
-
folderOpen: "bi bi-folder2-open",
|
|
674
|
-
doc: "bi bi-file-earmark",
|
|
675
|
-
};
|
|
676
|
-
var NavigationModeOption;
|
|
677
|
-
(function (NavigationModeOption) {
|
|
678
|
-
NavigationModeOption["startRow"] = "startRow";
|
|
679
|
-
NavigationModeOption["cell"] = "cell";
|
|
680
|
-
NavigationModeOption["startCell"] = "startCell";
|
|
681
|
-
NavigationModeOption["row"] = "row";
|
|
682
|
-
})(NavigationModeOption || (NavigationModeOption = {}));
|
|
683
|
-
var NavigationMode;
|
|
684
|
-
(function (NavigationMode) {
|
|
685
|
-
NavigationMode["row"] = "row";
|
|
686
|
-
NavigationMode["cellNav"] = "cellNav";
|
|
687
|
-
NavigationMode["cellEdit"] = "cellEdit";
|
|
688
|
-
})(NavigationMode || (NavigationMode = {}));
|
|
689
|
-
/** Map `KeyEvent.key` to navigation action. */
|
|
690
|
-
const KEY_TO_ACTION_DICT = {
|
|
691
|
-
" ": "toggleSelect",
|
|
692
|
-
"+": "expand",
|
|
693
|
-
Add: "expand",
|
|
694
|
-
ArrowDown: "down",
|
|
695
|
-
ArrowLeft: "left",
|
|
696
|
-
ArrowRight: "right",
|
|
697
|
-
ArrowUp: "up",
|
|
698
|
-
Backspace: "parent",
|
|
699
|
-
"/": "collapseAll",
|
|
700
|
-
Divide: "collapseAll",
|
|
701
|
-
End: "lastCol",
|
|
702
|
-
Home: "firstCol",
|
|
703
|
-
"Control+End": "last",
|
|
704
|
-
"Control+Home": "first",
|
|
705
|
-
"Meta+ArrowDown": "last",
|
|
706
|
-
"Meta+ArrowUp": "first",
|
|
707
|
-
"*": "expandAll",
|
|
708
|
-
Multiply: "expandAll",
|
|
709
|
-
PageDown: "pageDown",
|
|
710
|
-
PageUp: "pageUp",
|
|
711
|
-
"-": "collapse",
|
|
712
|
-
Subtract: "collapse",
|
|
713
|
-
};
|
|
714
|
-
/** */
|
|
715
|
-
function makeNodeTitleMatcher(s) {
|
|
716
|
-
s = escapeRegex(s.toLowerCase());
|
|
717
|
-
return function (node) {
|
|
718
|
-
return node.title.toLowerCase().indexOf(s) >= 0;
|
|
719
|
-
};
|
|
720
|
-
}
|
|
721
|
-
/** */
|
|
722
|
-
function makeNodeTitleStartMatcher(s) {
|
|
723
|
-
s = escapeRegex(s);
|
|
724
|
-
const reMatch = new RegExp("^" + s, "i");
|
|
725
|
-
return function (node) {
|
|
726
|
-
return reMatch.test(node.title);
|
|
727
|
-
};
|
|
728
|
-
}
|
|
744
|
+
/** Initial navigation mode and possible transition. */
|
|
745
|
+
var NavigationOptions;
|
|
746
|
+
(function (NavigationOptions) {
|
|
747
|
+
NavigationOptions["startRow"] = "startRow";
|
|
748
|
+
NavigationOptions["cell"] = "cell";
|
|
749
|
+
NavigationOptions["startCell"] = "startCell";
|
|
750
|
+
NavigationOptions["row"] = "row";
|
|
751
|
+
})(NavigationOptions || (NavigationOptions = {}));
|
|
729
752
|
|
|
730
753
|
/*!
|
|
731
754
|
* Wunderbaum - wb_extension_base
|
|
732
755
|
* Copyright (c) 2021-2022, Martin Wendt. Released under the MIT license.
|
|
733
|
-
* v0.0.
|
|
756
|
+
* v0.0.6, Sat, 10 Sep 2022 19:29:21 GMT (https://github.com/mar10/wunderbaum)
|
|
734
757
|
*/
|
|
735
758
|
class WunderbaumExtension {
|
|
736
759
|
constructor(tree, id, defaults) {
|
|
@@ -753,14 +776,14 @@ class WunderbaumExtension {
|
|
|
753
776
|
init() {
|
|
754
777
|
this.tree.element.classList.add("wb-ext-" + this.id);
|
|
755
778
|
}
|
|
756
|
-
// protected callEvent(
|
|
757
|
-
// let func = this.extensionOpts[
|
|
779
|
+
// protected callEvent(type: string, extra?: any): any {
|
|
780
|
+
// let func = this.extensionOpts[type];
|
|
758
781
|
// if (func) {
|
|
759
782
|
// return func.call(
|
|
760
783
|
// this.tree,
|
|
761
784
|
// util.extend(
|
|
762
785
|
// {
|
|
763
|
-
// event: this.id + "." +
|
|
786
|
+
// event: this.id + "." + type,
|
|
764
787
|
// },
|
|
765
788
|
// extra
|
|
766
789
|
// )
|
|
@@ -1017,75 +1040,11 @@ function debounce(func, wait = 0, options = {}) {
|
|
|
1017
1040
|
debounced.pending = pending;
|
|
1018
1041
|
return debounced;
|
|
1019
1042
|
}
|
|
1020
|
-
/**
|
|
1021
|
-
* Creates a throttled function that only invokes `func` at most once per
|
|
1022
|
-
* every `wait` milliseconds (or once per browser frame). The throttled function
|
|
1023
|
-
* comes with a `cancel` method to cancel delayed `func` invocations and a
|
|
1024
|
-
* `flush` method to immediately invoke them. Provide `options` to indicate
|
|
1025
|
-
* whether `func` should be invoked on the leading and/or trailing edge of the
|
|
1026
|
-
* `wait` timeout. The `func` is invoked with the last arguments provided to the
|
|
1027
|
-
* throttled function. Subsequent calls to the throttled function return the
|
|
1028
|
-
* result of the last `func` invocation.
|
|
1029
|
-
*
|
|
1030
|
-
* **Note:** If `leading` and `trailing` options are `true`, `func` is
|
|
1031
|
-
* invoked on the trailing edge of the timeout only if the throttled function
|
|
1032
|
-
* is invoked more than once during the `wait` timeout.
|
|
1033
|
-
*
|
|
1034
|
-
* If `wait` is `0` and `leading` is `false`, `func` invocation is deferred
|
|
1035
|
-
* until the next tick, similar to `setTimeout` with a timeout of `0`.
|
|
1036
|
-
*
|
|
1037
|
-
* If `wait` is omitted in an environment with `requestAnimationFrame`, `func`
|
|
1038
|
-
* invocation will be deferred until the next frame is drawn (typically about
|
|
1039
|
-
* 16ms).
|
|
1040
|
-
*
|
|
1041
|
-
* See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)
|
|
1042
|
-
* for details over the differences between `throttle` and `debounce`.
|
|
1043
|
-
*
|
|
1044
|
-
* @since 0.1.0
|
|
1045
|
-
* @category Function
|
|
1046
|
-
* @param {Function} func The function to throttle.
|
|
1047
|
-
* @param {number} [wait=0]
|
|
1048
|
-
* The number of milliseconds to throttle invocations to; if omitted,
|
|
1049
|
-
* `requestAnimationFrame` is used (if available).
|
|
1050
|
-
* @param {Object} [options={}] The options object.
|
|
1051
|
-
* @param {boolean} [options.leading=true]
|
|
1052
|
-
* Specify invoking on the leading edge of the timeout.
|
|
1053
|
-
* @param {boolean} [options.trailing=true]
|
|
1054
|
-
* Specify invoking on the trailing edge of the timeout.
|
|
1055
|
-
* @returns {Function} Returns the new throttled function.
|
|
1056
|
-
* @example
|
|
1057
|
-
*
|
|
1058
|
-
* // Avoid excessively updating the position while scrolling.
|
|
1059
|
-
* jQuery(window).on('scroll', throttle(updatePosition, 100))
|
|
1060
|
-
*
|
|
1061
|
-
* // Invoke `renewToken` when the click event is fired, but not more than once every 5 minutes.
|
|
1062
|
-
* const throttled = throttle(renewToken, 300000, { 'trailing': false })
|
|
1063
|
-
* jQuery(element).on('click', throttled)
|
|
1064
|
-
*
|
|
1065
|
-
* // Cancel the trailing throttled invocation.
|
|
1066
|
-
* jQuery(window).on('popstate', throttled.cancel)
|
|
1067
|
-
*/
|
|
1068
|
-
function throttle(func, wait = 0, options = {}) {
|
|
1069
|
-
let leading = true;
|
|
1070
|
-
let trailing = true;
|
|
1071
|
-
if (typeof func !== "function") {
|
|
1072
|
-
throw new TypeError("Expected a function");
|
|
1073
|
-
}
|
|
1074
|
-
if (isObject(options)) {
|
|
1075
|
-
leading = "leading" in options ? !!options.leading : leading;
|
|
1076
|
-
trailing = "trailing" in options ? !!options.trailing : trailing;
|
|
1077
|
-
}
|
|
1078
|
-
return debounce(func, wait, {
|
|
1079
|
-
leading,
|
|
1080
|
-
trailing,
|
|
1081
|
-
maxWait: wait,
|
|
1082
|
-
});
|
|
1083
|
-
}
|
|
1084
1043
|
|
|
1085
1044
|
/*!
|
|
1086
1045
|
* Wunderbaum - ext-filter
|
|
1087
1046
|
* Copyright (c) 2021-2022, Martin Wendt. Released under the MIT license.
|
|
1088
|
-
* v0.0.
|
|
1047
|
+
* v0.0.6, Sat, 10 Sep 2022 19:29:21 GMT (https://github.com/mar10/wunderbaum)
|
|
1089
1048
|
*/
|
|
1090
1049
|
const START_MARKER = "\uFFF7";
|
|
1091
1050
|
const END_MARKER = "\uFFF8";
|
|
@@ -1094,6 +1053,7 @@ const RE_END_MARTKER = new RegExp(escapeRegex(END_MARKER), "g");
|
|
|
1094
1053
|
class FilterExtension extends WunderbaumExtension {
|
|
1095
1054
|
constructor(tree) {
|
|
1096
1055
|
super(tree, "filter", {
|
|
1056
|
+
connectInput: null,
|
|
1097
1057
|
autoApply: true,
|
|
1098
1058
|
autoExpand: false,
|
|
1099
1059
|
counter: true,
|
|
@@ -1102,22 +1062,32 @@ class FilterExtension extends WunderbaumExtension {
|
|
|
1102
1062
|
hideExpanders: false,
|
|
1103
1063
|
highlight: true,
|
|
1104
1064
|
leavesOnly: false,
|
|
1105
|
-
mode: "
|
|
1065
|
+
mode: "dim",
|
|
1106
1066
|
noData: true, // Display a 'no data' status node if result is empty
|
|
1107
1067
|
});
|
|
1108
1068
|
this.lastFilterArgs = null;
|
|
1109
1069
|
}
|
|
1110
1070
|
init() {
|
|
1111
1071
|
super.init();
|
|
1112
|
-
|
|
1113
|
-
if (
|
|
1114
|
-
this.queryInput = elemFromSelector(
|
|
1072
|
+
const connectInput = this.getPluginOption("connectInput");
|
|
1073
|
+
if (connectInput) {
|
|
1074
|
+
this.queryInput = elemFromSelector(connectInput);
|
|
1115
1075
|
onEvent(this.queryInput, "input", debounce((e) => {
|
|
1116
1076
|
// this.tree.log("query", e);
|
|
1117
1077
|
this.filterNodes(this.queryInput.value.trim(), {});
|
|
1118
1078
|
}, 700));
|
|
1119
1079
|
}
|
|
1120
1080
|
}
|
|
1081
|
+
setPluginOption(name, value) {
|
|
1082
|
+
// alert("filter opt=" + name + ", " + value)
|
|
1083
|
+
super.setPluginOption(name, value);
|
|
1084
|
+
switch (name) {
|
|
1085
|
+
case "mode":
|
|
1086
|
+
this.tree.filterMode = value === "hide" ? "hide" : "dim";
|
|
1087
|
+
this.tree.updateFilter();
|
|
1088
|
+
break;
|
|
1089
|
+
}
|
|
1090
|
+
}
|
|
1121
1091
|
_applyFilterNoUpdate(filter, branchMode, _opts) {
|
|
1122
1092
|
return this.tree.runWithoutUpdate(() => {
|
|
1123
1093
|
return this._applyFilterImpl(filter, branchMode, _opts);
|
|
@@ -1376,19 +1346,145 @@ function _markFuzzyMatchedChars(text, matches, escapeTitles = true) {
|
|
|
1376
1346
|
return textPoses.join("");
|
|
1377
1347
|
}
|
|
1378
1348
|
|
|
1349
|
+
/*!
|
|
1350
|
+
* Wunderbaum - common
|
|
1351
|
+
* Copyright (c) 2021-2022, Martin Wendt. Released under the MIT license.
|
|
1352
|
+
* v0.0.6, Sat, 10 Sep 2022 19:29:21 GMT (https://github.com/mar10/wunderbaum)
|
|
1353
|
+
*/
|
|
1354
|
+
const DEFAULT_DEBUGLEVEL = 4; // Replaced by rollup script
|
|
1355
|
+
const ROW_HEIGHT = 22;
|
|
1356
|
+
// export const HEADER_HEIGHT = ROW_HEIGHT;
|
|
1357
|
+
const ICON_WIDTH = 20;
|
|
1358
|
+
const ROW_EXTRA_PAD = 7; // 2x $col-padding-x + 3px rounding errors
|
|
1359
|
+
const RENDER_MAX_PREFETCH = 5;
|
|
1360
|
+
const TEST_IMG = new RegExp(/\.|\//); // strings are considered image urls if they contain '.' or '/'
|
|
1361
|
+
// export const RECURSIVE_REQUEST_ERROR = "$recursive_request";
|
|
1362
|
+
// export const INVALID_REQUEST_TARGET_ERROR = "$request_target_invalid";
|
|
1363
|
+
let iconMap = {
|
|
1364
|
+
error: "bi bi-exclamation-triangle",
|
|
1365
|
+
// loading: "bi bi-hourglass-split wb-busy",
|
|
1366
|
+
loading: "bi bi-chevron-right wb-busy",
|
|
1367
|
+
// loading: "bi bi-arrow-repeat wb-spin",
|
|
1368
|
+
// loading: '<div class="spinner-border spinner-border-sm" role="status"> <span class="visually-hidden">Loading...</span> </div>',
|
|
1369
|
+
// noData: "bi bi-search",
|
|
1370
|
+
noData: "bi bi-question-circle",
|
|
1371
|
+
expanderExpanded: "bi bi-chevron-down",
|
|
1372
|
+
// expanderExpanded: "bi bi-dash-square",
|
|
1373
|
+
expanderCollapsed: "bi bi-chevron-right",
|
|
1374
|
+
// expanderCollapsed: "bi bi-plus-square",
|
|
1375
|
+
expanderLazy: "bi bi-chevron-right wb-helper-lazy-expander",
|
|
1376
|
+
// expanderLazy: "bi bi-chevron-bar-right",
|
|
1377
|
+
checkChecked: "bi bi-check-square",
|
|
1378
|
+
checkUnchecked: "bi bi-square",
|
|
1379
|
+
checkUnknown: "bi dash-square-dotted",
|
|
1380
|
+
radioChecked: "bi bi-circle-fill",
|
|
1381
|
+
radioUnchecked: "bi bi-circle",
|
|
1382
|
+
radioUnknown: "bi bi-circle-dotted",
|
|
1383
|
+
folder: "bi bi-folder2",
|
|
1384
|
+
folderOpen: "bi bi-folder2-open",
|
|
1385
|
+
doc: "bi bi-file-earmark",
|
|
1386
|
+
};
|
|
1387
|
+
/** Dict keys that are evaluated by source loader (others are added to `tree.data` instead). */
|
|
1388
|
+
const RESERVED_TREE_SOURCE_KEYS = new Set([
|
|
1389
|
+
"children",
|
|
1390
|
+
"columns",
|
|
1391
|
+
"format",
|
|
1392
|
+
"keyMap",
|
|
1393
|
+
"positional",
|
|
1394
|
+
"typeList",
|
|
1395
|
+
"types",
|
|
1396
|
+
"version", // reserved for future use
|
|
1397
|
+
]);
|
|
1398
|
+
/** Key codes that trigger grid navigation, even when inside an input element. */
|
|
1399
|
+
const INPUT_BREAKOUT_KEYS = new Set([
|
|
1400
|
+
// "ArrowDown",
|
|
1401
|
+
// "ArrowUp",
|
|
1402
|
+
"Enter",
|
|
1403
|
+
"Escape",
|
|
1404
|
+
]);
|
|
1405
|
+
/** Map `KeyEvent.key` to navigation action. */
|
|
1406
|
+
const KEY_TO_ACTION_DICT = {
|
|
1407
|
+
" ": "toggleSelect",
|
|
1408
|
+
"+": "expand",
|
|
1409
|
+
Add: "expand",
|
|
1410
|
+
ArrowDown: "down",
|
|
1411
|
+
ArrowLeft: "left",
|
|
1412
|
+
ArrowRight: "right",
|
|
1413
|
+
ArrowUp: "up",
|
|
1414
|
+
Backspace: "parent",
|
|
1415
|
+
"/": "collapseAll",
|
|
1416
|
+
Divide: "collapseAll",
|
|
1417
|
+
End: "lastCol",
|
|
1418
|
+
Home: "firstCol",
|
|
1419
|
+
"Control+End": "last",
|
|
1420
|
+
"Control+Home": "first",
|
|
1421
|
+
"Meta+ArrowDown": "last",
|
|
1422
|
+
"Meta+ArrowUp": "first",
|
|
1423
|
+
"*": "expandAll",
|
|
1424
|
+
Multiply: "expandAll",
|
|
1425
|
+
PageDown: "pageDown",
|
|
1426
|
+
PageUp: "pageUp",
|
|
1427
|
+
"-": "collapse",
|
|
1428
|
+
Subtract: "collapse",
|
|
1429
|
+
};
|
|
1430
|
+
/** Return a callback that returns true if the node title contains a substring (case-insensitive). */
|
|
1431
|
+
function makeNodeTitleMatcher(s) {
|
|
1432
|
+
s = escapeRegex(s.toLowerCase());
|
|
1433
|
+
return function (node) {
|
|
1434
|
+
return node.title.toLowerCase().indexOf(s) >= 0;
|
|
1435
|
+
};
|
|
1436
|
+
}
|
|
1437
|
+
/** Return a callback that returns true if the node title starts with a string (case-insensitive). */
|
|
1438
|
+
function makeNodeTitleStartMatcher(s) {
|
|
1439
|
+
s = escapeRegex(s);
|
|
1440
|
+
const reMatch = new RegExp("^" + s, "i");
|
|
1441
|
+
return function (node) {
|
|
1442
|
+
return reMatch.test(node.title);
|
|
1443
|
+
};
|
|
1444
|
+
}
|
|
1445
|
+
|
|
1379
1446
|
/*!
|
|
1380
1447
|
* Wunderbaum - ext-keynav
|
|
1381
1448
|
* Copyright (c) 2021-2022, Martin Wendt. Released under the MIT license.
|
|
1382
|
-
* v0.0.
|
|
1449
|
+
* v0.0.6, Sat, 10 Sep 2022 19:29:21 GMT (https://github.com/mar10/wunderbaum)
|
|
1383
1450
|
*/
|
|
1451
|
+
const QUICKSEARCH_DELAY = 500;
|
|
1384
1452
|
class KeynavExtension extends WunderbaumExtension {
|
|
1385
1453
|
constructor(tree) {
|
|
1386
1454
|
super(tree, "keynav", {});
|
|
1387
1455
|
}
|
|
1456
|
+
_getEmbeddedInputElem(elem) {
|
|
1457
|
+
var _a;
|
|
1458
|
+
let input = null;
|
|
1459
|
+
if (elem && elem.type != null) {
|
|
1460
|
+
input = elem;
|
|
1461
|
+
}
|
|
1462
|
+
else {
|
|
1463
|
+
// ,[contenteditable]
|
|
1464
|
+
const ace = (_a = this.tree.getActiveColElem()) === null || _a === void 0 ? void 0 : _a.querySelector("input,select");
|
|
1465
|
+
if (ace) {
|
|
1466
|
+
input = ace;
|
|
1467
|
+
}
|
|
1468
|
+
}
|
|
1469
|
+
return input;
|
|
1470
|
+
}
|
|
1471
|
+
/* Return true if the current cell's embedded input has keyboard focus. */
|
|
1472
|
+
_isCurInputFocused() {
|
|
1473
|
+
var _a;
|
|
1474
|
+
const ace = (_a = this.tree
|
|
1475
|
+
.getActiveColElem()) === null || _a === void 0 ? void 0 : _a.querySelector("input:focus,select:focus");
|
|
1476
|
+
console.log(`_isCurInputFocused`, ace);
|
|
1477
|
+
return !!ace;
|
|
1478
|
+
}
|
|
1388
1479
|
onKeyEvent(data) {
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1480
|
+
const event = data.event, tree = this.tree, opts = data.options, activate = !event.ctrlKey || opts.autoActivate, curInput = this._getEmbeddedInputElem(event.target), navModeOption = opts.navigationModeOption;
|
|
1481
|
+
// isCellEditMode = tree.navMode === NavigationMode.cellEdit;
|
|
1482
|
+
let focusNode, eventName = eventToString(event), node = data.node, handled = true;
|
|
1483
|
+
tree.log(`onKeyEvent: ${eventName}, curInput`, curInput);
|
|
1484
|
+
if (!tree.isEnabled()) {
|
|
1485
|
+
// tree.logDebug(`onKeyEvent ignored for disabled tree: ${eventName}`);
|
|
1486
|
+
return false;
|
|
1487
|
+
}
|
|
1392
1488
|
// Let callback prevent default processing
|
|
1393
1489
|
if (tree._callEvent("keydown", data) === false) {
|
|
1394
1490
|
return false;
|
|
@@ -1399,30 +1495,33 @@ class KeynavExtension extends WunderbaumExtension {
|
|
|
1399
1495
|
}
|
|
1400
1496
|
// Set focus to active (or first node) if no other node has the focus yet
|
|
1401
1497
|
if (!node) {
|
|
1402
|
-
const
|
|
1498
|
+
const currentNode = tree.getFocusNode() || tree.getActiveNode();
|
|
1403
1499
|
const firstNode = tree.getFirstChild();
|
|
1404
|
-
if (!
|
|
1500
|
+
if (!currentNode && firstNode && eventName === "ArrowDown") {
|
|
1405
1501
|
firstNode.logInfo("Keydown: activate first node.");
|
|
1406
1502
|
firstNode.setActive();
|
|
1407
1503
|
return;
|
|
1408
1504
|
}
|
|
1409
|
-
focusNode =
|
|
1505
|
+
focusNode = currentNode || firstNode;
|
|
1410
1506
|
if (focusNode) {
|
|
1411
1507
|
focusNode.setFocus();
|
|
1412
1508
|
node = tree.getFocusNode();
|
|
1413
1509
|
node.logInfo("Keydown: force focus on active node.");
|
|
1414
1510
|
}
|
|
1415
1511
|
}
|
|
1416
|
-
|
|
1512
|
+
const isColspan = node.isColspan();
|
|
1513
|
+
if (tree.isRowNav()) {
|
|
1514
|
+
// -----------------------------------------------------------------------
|
|
1515
|
+
// --- Row Mode ---
|
|
1516
|
+
// -----------------------------------------------------------------------
|
|
1417
1517
|
// --- Quick-Search
|
|
1418
1518
|
if (opts.quicksearch &&
|
|
1419
1519
|
eventName.length === 1 &&
|
|
1420
|
-
/^\w$/.test(eventName)
|
|
1421
|
-
|
|
1422
|
-
) {
|
|
1520
|
+
/^\w$/.test(eventName) &&
|
|
1521
|
+
!curInput) {
|
|
1423
1522
|
// Allow to search for longer streaks if typed in quickly
|
|
1424
1523
|
const stamp = Date.now();
|
|
1425
|
-
if (stamp - tree.lastQuicksearchTime >
|
|
1524
|
+
if (stamp - tree.lastQuicksearchTime > QUICKSEARCH_DELAY) {
|
|
1426
1525
|
tree.lastQuicksearchTerm = "";
|
|
1427
1526
|
}
|
|
1428
1527
|
tree.lastQuicksearchTime = stamp;
|
|
@@ -1445,8 +1544,8 @@ class KeynavExtension extends WunderbaumExtension {
|
|
|
1445
1544
|
if (!node.expanded && (node.children || node.lazy)) {
|
|
1446
1545
|
eventName = "Add"; // expand
|
|
1447
1546
|
}
|
|
1448
|
-
else if (navModeOption ===
|
|
1449
|
-
tree.
|
|
1547
|
+
else if (navModeOption === NavigationOptions.startRow) {
|
|
1548
|
+
tree.setCellNav();
|
|
1450
1549
|
return;
|
|
1451
1550
|
}
|
|
1452
1551
|
break;
|
|
@@ -1496,48 +1595,103 @@ class KeynavExtension extends WunderbaumExtension {
|
|
|
1496
1595
|
}
|
|
1497
1596
|
}
|
|
1498
1597
|
else {
|
|
1499
|
-
|
|
1598
|
+
const curInput = this._getEmbeddedInputElem(null);
|
|
1599
|
+
const curInputType = curInput ? curInput.type || curInput.tagName : "";
|
|
1600
|
+
const inputHasFocus = curInput && this._isCurInputFocused();
|
|
1601
|
+
const inputCanFocus = curInput && curInputType !== "checkbox";
|
|
1602
|
+
if (inputHasFocus) {
|
|
1603
|
+
if (eventName === "Escape") {
|
|
1604
|
+
// Discard changes
|
|
1605
|
+
node.render();
|
|
1606
|
+
}
|
|
1607
|
+
else if (!INPUT_BREAKOUT_KEYS.has(eventName)) {
|
|
1608
|
+
// Let current `<input>` handle it
|
|
1609
|
+
node.logDebug(`Ignored ${eventName} inside input`);
|
|
1610
|
+
return;
|
|
1611
|
+
}
|
|
1612
|
+
// const curInputType = curInput.type || curInput.tagName;
|
|
1613
|
+
// const breakoutKeys = INPUT_KEYS[curInputType];
|
|
1614
|
+
// if (!breakoutKeys.includes(eventName)) {
|
|
1615
|
+
// node.logDebug(`Ignored ${eventName} inside ${curInputType} input`);
|
|
1616
|
+
// return;
|
|
1617
|
+
// }
|
|
1618
|
+
}
|
|
1619
|
+
else if (curInput) {
|
|
1620
|
+
// On a cell that has an embedded, unfocused <input>
|
|
1621
|
+
if (eventName.length === 1 && inputCanFocus) {
|
|
1622
|
+
curInput.focus();
|
|
1623
|
+
curInput.value = "";
|
|
1624
|
+
node.logDebug(`Focus imput: ${eventName}`);
|
|
1625
|
+
return false;
|
|
1626
|
+
}
|
|
1627
|
+
}
|
|
1628
|
+
if (eventName === "Tab") {
|
|
1629
|
+
eventName = "ArrowRight";
|
|
1630
|
+
handled = true;
|
|
1631
|
+
}
|
|
1632
|
+
else if (eventName === "Shift+Tab") {
|
|
1633
|
+
eventName = tree.activeColIdx > 0 ? "ArrowLeft" : "";
|
|
1634
|
+
handled = true;
|
|
1635
|
+
}
|
|
1636
|
+
else ;
|
|
1500
1637
|
switch (eventName) {
|
|
1501
1638
|
case " ":
|
|
1502
1639
|
if (tree.activeColIdx === 0 && node.getOption("checkbox")) {
|
|
1503
1640
|
node.setSelected(!node.isSelected());
|
|
1504
1641
|
handled = true;
|
|
1505
1642
|
}
|
|
1506
|
-
else {
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1643
|
+
else if (curInput && curInputType === "checkbox") {
|
|
1644
|
+
curInput.click();
|
|
1645
|
+
// toggleCheckbox(curInput)
|
|
1646
|
+
// new Event("change")
|
|
1647
|
+
// curInput.change
|
|
1648
|
+
handled = true;
|
|
1649
|
+
}
|
|
1650
|
+
break;
|
|
1651
|
+
case "F2":
|
|
1652
|
+
if (curInput && !inputHasFocus && inputCanFocus) {
|
|
1653
|
+
curInput.focus();
|
|
1654
|
+
handled = true;
|
|
1511
1655
|
}
|
|
1512
1656
|
break;
|
|
1513
1657
|
case "Enter":
|
|
1658
|
+
tree.setFocus(); // Blur prev. input if any
|
|
1514
1659
|
if (tree.activeColIdx === 0 && node.isExpandable()) {
|
|
1515
1660
|
node.setExpanded(!node.isExpanded());
|
|
1516
1661
|
handled = true;
|
|
1517
1662
|
}
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
if (tree.navMode === NavigationMode.cellEdit) {
|
|
1521
|
-
tree.setNavigationMode(NavigationMode.cellNav);
|
|
1663
|
+
else if (curInput && !inputHasFocus && inputCanFocus) {
|
|
1664
|
+
curInput.focus();
|
|
1522
1665
|
handled = true;
|
|
1523
1666
|
}
|
|
1524
|
-
|
|
1525
|
-
|
|
1667
|
+
break;
|
|
1668
|
+
case "Escape":
|
|
1669
|
+
tree.setFocus(); // Blur prev. input if any
|
|
1670
|
+
if (tree.isCellNav() && navModeOption !== NavigationOptions.cell) {
|
|
1671
|
+
tree.setCellNav(false); // row-nav mode
|
|
1526
1672
|
handled = true;
|
|
1527
1673
|
}
|
|
1528
1674
|
break;
|
|
1529
1675
|
case "ArrowLeft":
|
|
1530
|
-
|
|
1676
|
+
tree.setFocus(); // Blur prev. input if any
|
|
1677
|
+
if (isColspan && node.isExpanded()) {
|
|
1678
|
+
node.setExpanded(false);
|
|
1679
|
+
}
|
|
1680
|
+
else if (tree.activeColIdx > 0) {
|
|
1531
1681
|
tree.setColumn(tree.activeColIdx - 1);
|
|
1532
1682
|
handled = true;
|
|
1533
1683
|
}
|
|
1534
|
-
else if (navModeOption !==
|
|
1535
|
-
tree.
|
|
1684
|
+
else if (navModeOption !== NavigationOptions.cell) {
|
|
1685
|
+
tree.setCellNav(false); // row-nav mode
|
|
1536
1686
|
handled = true;
|
|
1537
1687
|
}
|
|
1538
1688
|
break;
|
|
1539
1689
|
case "ArrowRight":
|
|
1540
|
-
|
|
1690
|
+
tree.setFocus(); // Blur prev. input if any
|
|
1691
|
+
if (isColspan && !node.isExpanded()) {
|
|
1692
|
+
node.setExpanded();
|
|
1693
|
+
}
|
|
1694
|
+
else if (tree.activeColIdx < tree.columns.length - 1) {
|
|
1541
1695
|
tree.setColumn(tree.activeColIdx + 1);
|
|
1542
1696
|
handled = true;
|
|
1543
1697
|
}
|
|
@@ -1554,6 +1708,10 @@ class KeynavExtension extends WunderbaumExtension {
|
|
|
1554
1708
|
case "PageDown":
|
|
1555
1709
|
case "PageUp":
|
|
1556
1710
|
node.navigate(eventName, { activate: activate, event: event });
|
|
1711
|
+
// if (isCellEditMode) {
|
|
1712
|
+
// this._getEmbeddedInputElem(null, true); // set focus to input
|
|
1713
|
+
// }
|
|
1714
|
+
handled = true;
|
|
1557
1715
|
break;
|
|
1558
1716
|
default:
|
|
1559
1717
|
handled = false;
|
|
@@ -1569,7 +1727,7 @@ class KeynavExtension extends WunderbaumExtension {
|
|
|
1569
1727
|
/*!
|
|
1570
1728
|
* Wunderbaum - ext-logger
|
|
1571
1729
|
* Copyright (c) 2021-2022, Martin Wendt. Released under the MIT license.
|
|
1572
|
-
* v0.0.
|
|
1730
|
+
* v0.0.6, Sat, 10 Sep 2022 19:29:21 GMT (https://github.com/mar10/wunderbaum)
|
|
1573
1731
|
*/
|
|
1574
1732
|
class LoggerExtension extends WunderbaumExtension {
|
|
1575
1733
|
constructor(tree) {
|
|
@@ -1609,7 +1767,7 @@ class LoggerExtension extends WunderbaumExtension {
|
|
|
1609
1767
|
/*!
|
|
1610
1768
|
* Wunderbaum - ext-dnd
|
|
1611
1769
|
* Copyright (c) 2021-2022, Martin Wendt. Released under the MIT license.
|
|
1612
|
-
* v0.0.
|
|
1770
|
+
* v0.0.6, Sat, 10 Sep 2022 19:29:21 GMT (https://github.com/mar10/wunderbaum)
|
|
1613
1771
|
*/
|
|
1614
1772
|
const nodeMimeType = "application/x-wunderbaum-node";
|
|
1615
1773
|
class DndExtension extends WunderbaumExtension {
|
|
@@ -1679,7 +1837,7 @@ class DndExtension extends WunderbaumExtension {
|
|
|
1679
1837
|
const ltn = this.lastTargetNode;
|
|
1680
1838
|
this.lastEnterStamp = 0;
|
|
1681
1839
|
if (ltn) {
|
|
1682
|
-
ltn.
|
|
1840
|
+
ltn.setClass("wb-drop-target wb-drop-over wb-drop-after wb-drop-before", false);
|
|
1683
1841
|
this.lastTargetNode = null;
|
|
1684
1842
|
}
|
|
1685
1843
|
}
|
|
@@ -1723,7 +1881,7 @@ class DndExtension extends WunderbaumExtension {
|
|
|
1723
1881
|
}
|
|
1724
1882
|
/* Implement auto scrolling when drag cursor is in top/bottom area of scroll parent. */
|
|
1725
1883
|
autoScroll(event) {
|
|
1726
|
-
let tree = this.tree, dndOpts = tree.options.dnd, sp = tree.
|
|
1884
|
+
let tree = this.tree, dndOpts = tree.options.dnd, sp = tree.scrollContainerElement, sensitivity = dndOpts.scrollSensitivity, speed = dndOpts.scrollSpeed, scrolled = 0;
|
|
1727
1885
|
const scrollTop = sp.offsetTop;
|
|
1728
1886
|
if (scrollTop + sp.offsetHeight - event.pageY < sensitivity) {
|
|
1729
1887
|
const delta = sp.scrollHeight - sp.clientHeight - scrollTop;
|
|
@@ -1773,13 +1931,13 @@ class DndExtension extends WunderbaumExtension {
|
|
|
1773
1931
|
setTimeout(() => {
|
|
1774
1932
|
// Decouple this call, so the CSS is applied to the node, but not to
|
|
1775
1933
|
// the system generated drag image
|
|
1776
|
-
srcNode.
|
|
1934
|
+
srcNode.setClass("wb-drag-source");
|
|
1777
1935
|
}, 0);
|
|
1778
1936
|
// --- drag ---
|
|
1779
1937
|
}
|
|
1780
1938
|
else if (e.type === "drag") ;
|
|
1781
1939
|
else if (e.type === "dragend") {
|
|
1782
|
-
srcNode.
|
|
1940
|
+
srcNode.setClass("wb-drag-source", false);
|
|
1783
1941
|
this.srcNode = null;
|
|
1784
1942
|
if (this.lastTargetNode) {
|
|
1785
1943
|
this._leaveNode();
|
|
@@ -1833,7 +1991,7 @@ class DndExtension extends WunderbaumExtension {
|
|
|
1833
1991
|
}
|
|
1834
1992
|
this.lastAllowedDropRegions = regionSet;
|
|
1835
1993
|
this.lastDropEffect = dt.dropEffect;
|
|
1836
|
-
targetNode.
|
|
1994
|
+
targetNode.setClass("wb-drop-target");
|
|
1837
1995
|
e.preventDefault(); // Allow drop (Drop operation is denied by default)
|
|
1838
1996
|
return false;
|
|
1839
1997
|
// --- dragover ---
|
|
@@ -1852,9 +2010,9 @@ class DndExtension extends WunderbaumExtension {
|
|
|
1852
2010
|
if (!region) {
|
|
1853
2011
|
return; // We already rejected in dragenter
|
|
1854
2012
|
}
|
|
1855
|
-
targetNode.
|
|
1856
|
-
targetNode.
|
|
1857
|
-
targetNode.
|
|
2013
|
+
targetNode.setClass("wb-drop-over", region === "over");
|
|
2014
|
+
targetNode.setClass("wb-drop-before", region === "before");
|
|
2015
|
+
targetNode.setClass("wb-drop-after", region === "after");
|
|
1858
2016
|
// console.log("dragover", e);
|
|
1859
2017
|
// dt.dropEffect = this.lastDropEffect!;
|
|
1860
2018
|
e.preventDefault(); // Allow drop (Drop operation is denied by default)
|
|
@@ -1877,7 +2035,7 @@ class DndExtension extends WunderbaumExtension {
|
|
|
1877
2035
|
/*!
|
|
1878
2036
|
* Wunderbaum - drag_observer
|
|
1879
2037
|
* Copyright (c) 2021-2022, Martin Wendt. Released under the MIT license.
|
|
1880
|
-
* v0.0.
|
|
2038
|
+
* v0.0.6, Sat, 10 Sep 2022 19:29:21 GMT (https://github.com/mar10/wunderbaum)
|
|
1881
2039
|
*/
|
|
1882
2040
|
/**
|
|
1883
2041
|
* Convert mouse- and touch events to 'dragstart', 'drag', and 'dragstop'.
|
|
@@ -2011,7 +2169,7 @@ class DragObserver {
|
|
|
2011
2169
|
/*!
|
|
2012
2170
|
* Wunderbaum - ext-grid
|
|
2013
2171
|
* Copyright (c) 2021-2022, Martin Wendt. Released under the MIT license.
|
|
2014
|
-
* v0.0.
|
|
2172
|
+
* v0.0.6, Sat, 10 Sep 2022 19:29:21 GMT (https://github.com/mar10/wunderbaum)
|
|
2015
2173
|
*/
|
|
2016
2174
|
class GridExtension extends WunderbaumExtension {
|
|
2017
2175
|
constructor(tree) {
|
|
@@ -2048,12 +2206,22 @@ class GridExtension extends WunderbaumExtension {
|
|
|
2048
2206
|
/*!
|
|
2049
2207
|
* Wunderbaum - deferred
|
|
2050
2208
|
* Copyright (c) 2021-2022, Martin Wendt. Released under the MIT license.
|
|
2051
|
-
* v0.0.
|
|
2209
|
+
* v0.0.6, Sat, 10 Sep 2022 19:29:21 GMT (https://github.com/mar10/wunderbaum)
|
|
2052
2210
|
*/
|
|
2053
2211
|
/**
|
|
2054
|
-
*
|
|
2212
|
+
* Implement a ES6 Promise, that exposes a resolve() and reject() method.
|
|
2055
2213
|
*
|
|
2056
|
-
* Loosely mimics
|
|
2214
|
+
* Loosely mimics {@link https://api.jquery.com/category/deferred-object/ | jQuery.Deferred}.
|
|
2215
|
+
* Example:
|
|
2216
|
+
* ```js
|
|
2217
|
+
* function foo() {
|
|
2218
|
+
* let dfd = new Deferred(),
|
|
2219
|
+
* ...
|
|
2220
|
+
* dfd.resolve('foo')
|
|
2221
|
+
* ...
|
|
2222
|
+
* return dfd.promise();
|
|
2223
|
+
* }
|
|
2224
|
+
* ```
|
|
2057
2225
|
*/
|
|
2058
2226
|
class Deferred {
|
|
2059
2227
|
constructor() {
|
|
@@ -2091,7 +2259,7 @@ class Deferred {
|
|
|
2091
2259
|
/*!
|
|
2092
2260
|
* Wunderbaum - wunderbaum_node
|
|
2093
2261
|
* Copyright (c) 2021-2022, Martin Wendt. Released under the MIT license.
|
|
2094
|
-
* v0.0.
|
|
2262
|
+
* v0.0.6, Sat, 10 Sep 2022 19:29:21 GMT (https://github.com/mar10/wunderbaum)
|
|
2095
2263
|
*/
|
|
2096
2264
|
/** Top-level properties that can be passed with `data`. */
|
|
2097
2265
|
const NODE_PROPS = new Set([
|
|
@@ -2110,7 +2278,7 @@ const NODE_PROPS = new Set([
|
|
|
2110
2278
|
const NODE_ATTRS = new Set([
|
|
2111
2279
|
"checkbox",
|
|
2112
2280
|
"expanded",
|
|
2113
|
-
"
|
|
2281
|
+
"classes",
|
|
2114
2282
|
"folder",
|
|
2115
2283
|
"icon",
|
|
2116
2284
|
"iconTooltip",
|
|
@@ -2152,8 +2320,8 @@ class WunderbaumNode {
|
|
|
2152
2320
|
* @see {@link isSelected}, {@link setSelected}. */
|
|
2153
2321
|
this.selected = false;
|
|
2154
2322
|
/** Additional classes added to `div.wb-row`.
|
|
2155
|
-
* @see {@link
|
|
2156
|
-
this.
|
|
2323
|
+
* @see {@link hasClass}, {@link setClass}. */
|
|
2324
|
+
this.classes = null; //new Set<string>();
|
|
2157
2325
|
/** Custom data that was passed to the constructor */
|
|
2158
2326
|
this.data = {};
|
|
2159
2327
|
this._isLoading = false;
|
|
@@ -2182,9 +2350,7 @@ class WunderbaumNode {
|
|
|
2182
2350
|
this.lazy = data.lazy === true;
|
|
2183
2351
|
this.selected = data.selected === true;
|
|
2184
2352
|
if (data.classes) {
|
|
2185
|
-
|
|
2186
|
-
this.extraClasses.add(c.trim());
|
|
2187
|
-
}
|
|
2353
|
+
this.setClass(data.classes);
|
|
2188
2354
|
}
|
|
2189
2355
|
// Store custom fields as `node.data`
|
|
2190
2356
|
for (const [key, value] of Object.entries(data)) {
|
|
@@ -2202,7 +2368,7 @@ class WunderbaumNode {
|
|
|
2202
2368
|
* @internal
|
|
2203
2369
|
*/
|
|
2204
2370
|
toString() {
|
|
2205
|
-
return
|
|
2371
|
+
return `WunderbaumNode@${this.key}<'${this.title}'>`;
|
|
2206
2372
|
}
|
|
2207
2373
|
// /** Return an option value. */
|
|
2208
2374
|
// protected _getOpt(
|
|
@@ -2225,8 +2391,8 @@ class WunderbaumNode {
|
|
|
2225
2391
|
* node._callEvent("edit.beforeEdit", {foo: 42})
|
|
2226
2392
|
* ```
|
|
2227
2393
|
*/
|
|
2228
|
-
_callEvent(
|
|
2229
|
-
return this.tree._callEvent(
|
|
2394
|
+
_callEvent(type, extra) {
|
|
2395
|
+
return this.tree._callEvent(type, extend({
|
|
2230
2396
|
node: this,
|
|
2231
2397
|
typeInfo: this.type ? this.tree.types[this.type] : {},
|
|
2232
2398
|
}, extra));
|
|
@@ -2324,31 +2490,41 @@ class WunderbaumNode {
|
|
|
2324
2490
|
applyCommand(cmd, opts) {
|
|
2325
2491
|
return this.tree.applyCommand(cmd, this, opts);
|
|
2326
2492
|
}
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
const cnSet = toSet(className);
|
|
2337
|
-
cnSet.forEach((cn) => {
|
|
2338
|
-
var _a;
|
|
2339
|
-
this.extraClasses.delete(cn);
|
|
2340
|
-
(_a = this._rowElem) === null || _a === void 0 ? void 0 : _a.classList.remove(cn);
|
|
2341
|
-
});
|
|
2342
|
-
}
|
|
2343
|
-
toggleClass(className, flag) {
|
|
2493
|
+
/**
|
|
2494
|
+
* Add/remove one or more classes to `<div class='wb-row'>`.
|
|
2495
|
+
*
|
|
2496
|
+
* This also maintains `node.classes`, so the class will survive a re-render.
|
|
2497
|
+
*
|
|
2498
|
+
* @param className one or more class names. Multiple classes can be passed
|
|
2499
|
+
* as space-separated string, array of strings, or set of strings.
|
|
2500
|
+
*/
|
|
2501
|
+
setClass(className, flag = true) {
|
|
2344
2502
|
const cnSet = toSet(className);
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2503
|
+
if (flag) {
|
|
2504
|
+
if (this.classes === null) {
|
|
2505
|
+
this.classes = new Set();
|
|
2506
|
+
}
|
|
2507
|
+
cnSet.forEach((cn) => {
|
|
2508
|
+
var _a;
|
|
2509
|
+
this.classes.add(cn);
|
|
2510
|
+
(_a = this._rowElem) === null || _a === void 0 ? void 0 : _a.classList.toggle(cn, flag);
|
|
2511
|
+
});
|
|
2512
|
+
}
|
|
2513
|
+
else {
|
|
2514
|
+
if (this.classes === null) {
|
|
2515
|
+
return;
|
|
2516
|
+
}
|
|
2517
|
+
cnSet.forEach((cn) => {
|
|
2518
|
+
var _a;
|
|
2519
|
+
this.classes.delete(cn);
|
|
2520
|
+
(_a = this._rowElem) === null || _a === void 0 ? void 0 : _a.classList.toggle(cn, flag);
|
|
2521
|
+
});
|
|
2522
|
+
if (this.classes.size === 0) {
|
|
2523
|
+
this.classes = null;
|
|
2524
|
+
}
|
|
2525
|
+
}
|
|
2350
2526
|
}
|
|
2351
|
-
/** */
|
|
2527
|
+
/** Call `setExpanded()` on al child nodes*/
|
|
2352
2528
|
async expandAll(flag = true) {
|
|
2353
2529
|
this.visit((node) => {
|
|
2354
2530
|
node.setExpanded(flag);
|
|
@@ -2520,6 +2696,10 @@ class WunderbaumNode {
|
|
|
2520
2696
|
}
|
|
2521
2697
|
return !!(this.children && this.children.length);
|
|
2522
2698
|
}
|
|
2699
|
+
/** Return true if node has className set. */
|
|
2700
|
+
hasClass(className) {
|
|
2701
|
+
return this.classes ? this.classes.has(className) : false;
|
|
2702
|
+
}
|
|
2523
2703
|
/** Return true if this node is the currently active tree node. */
|
|
2524
2704
|
isActive() {
|
|
2525
2705
|
return this.tree.activeNode === this;
|
|
@@ -2530,6 +2710,12 @@ class WunderbaumNode {
|
|
|
2530
2710
|
isChildOf(other) {
|
|
2531
2711
|
return this.parent && this.parent === other;
|
|
2532
2712
|
}
|
|
2713
|
+
/** Return true if this node's title spans all columns, i.e. the node has no
|
|
2714
|
+
* grid cells.
|
|
2715
|
+
*/
|
|
2716
|
+
isColspan() {
|
|
2717
|
+
return !!this.getOption("colspan");
|
|
2718
|
+
}
|
|
2533
2719
|
/** Return true if this node is a direct or indirect sub node of `other`.
|
|
2534
2720
|
* (See also [[isChildOf]].)
|
|
2535
2721
|
*/
|
|
@@ -2665,29 +2851,46 @@ class WunderbaumNode {
|
|
|
2665
2851
|
assert(isPlainObject(source));
|
|
2666
2852
|
assert(source.children, "If `source` is an object, it must have a `children` property");
|
|
2667
2853
|
if (source.types) {
|
|
2668
|
-
|
|
2669
|
-
|
|
2854
|
+
tree.logInfo("Redefine types", source.columns);
|
|
2855
|
+
tree.setTypes(source.types, false);
|
|
2856
|
+
delete source.types;
|
|
2857
|
+
}
|
|
2858
|
+
if (source.columns) {
|
|
2859
|
+
tree.logInfo("Redefine columns", source.columns);
|
|
2860
|
+
tree.columns = source.columns;
|
|
2861
|
+
delete source.columns;
|
|
2862
|
+
tree.updateColumns({ calculateCols: false });
|
|
2670
2863
|
}
|
|
2671
2864
|
this.addChildren(source.children);
|
|
2865
|
+
delete source.columns;
|
|
2866
|
+
// Add extra data to `tree.data`
|
|
2867
|
+
for (const [key, value] of Object.entries(source)) {
|
|
2868
|
+
if (!RESERVED_TREE_SOURCE_KEYS.has(key)) {
|
|
2869
|
+
tree.data[key] = value;
|
|
2870
|
+
tree.logDebug(`Add source.${key} to tree.data.${key}`);
|
|
2871
|
+
}
|
|
2872
|
+
}
|
|
2672
2873
|
this._callEvent("load");
|
|
2673
2874
|
}
|
|
2674
2875
|
/** Download data from the cloud, then call `.update()`. */
|
|
2675
2876
|
async load(source) {
|
|
2676
2877
|
const tree = this.tree;
|
|
2677
|
-
// const opts = tree.options;
|
|
2678
2878
|
const requestId = Date.now();
|
|
2679
2879
|
const prevParent = this.parent;
|
|
2680
2880
|
const url = typeof source === "string" ? source : source.url;
|
|
2881
|
+
const start = Date.now();
|
|
2882
|
+
let elap = 0, elapLoad = 0, elapProcess = 0;
|
|
2681
2883
|
// Check for overlapping requests
|
|
2682
2884
|
if (this._requestId) {
|
|
2683
2885
|
this.logWarn(`Recursive load request #${requestId} while #${this._requestId} is pending.`);
|
|
2684
2886
|
// node.debug("Send load request #" + requestId);
|
|
2685
2887
|
}
|
|
2686
2888
|
this._requestId = requestId;
|
|
2687
|
-
const timerLabel = tree.logTime(this + ".load()");
|
|
2889
|
+
// const timerLabel = tree.logTime(this + ".load()");
|
|
2688
2890
|
try {
|
|
2689
2891
|
if (!url) {
|
|
2690
2892
|
this._loadSourceObject(source);
|
|
2893
|
+
elapProcess = Date.now() - start;
|
|
2691
2894
|
}
|
|
2692
2895
|
else {
|
|
2693
2896
|
this.setStatus(NodeStatusType.loading);
|
|
@@ -2696,6 +2899,7 @@ class WunderbaumNode {
|
|
|
2696
2899
|
error(`GET ${url} returned ${response.status}, ${response}`);
|
|
2697
2900
|
}
|
|
2698
2901
|
const data = await response.json();
|
|
2902
|
+
elapLoad = Date.now() - start;
|
|
2699
2903
|
if (this._requestId && this._requestId > requestId) {
|
|
2700
2904
|
this.logWarn(`Ignored load response #${requestId} because #${this._requestId} is pending.`);
|
|
2701
2905
|
return;
|
|
@@ -2708,25 +2912,30 @@ class WunderbaumNode {
|
|
|
2708
2912
|
return;
|
|
2709
2913
|
}
|
|
2710
2914
|
this.setStatus(NodeStatusType.ok);
|
|
2711
|
-
if (data.columns) {
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
}
|
|
2915
|
+
// if (data.columns) {
|
|
2916
|
+
// tree.logInfo("Re-define columns", data.columns);
|
|
2917
|
+
// util.assert(!this.parent);
|
|
2918
|
+
// tree.columns = data.columns;
|
|
2919
|
+
// delete data.columns;
|
|
2920
|
+
// tree.updateColumns({ calculateCols: false });
|
|
2921
|
+
// }
|
|
2922
|
+
const startProcess = Date.now();
|
|
2718
2923
|
this._loadSourceObject(data);
|
|
2924
|
+
elapProcess = Date.now() - startProcess;
|
|
2719
2925
|
}
|
|
2720
2926
|
}
|
|
2721
2927
|
catch (error) {
|
|
2722
2928
|
this.logError("Error during load()", source, error);
|
|
2723
2929
|
this._callEvent("error", { error: error });
|
|
2724
|
-
this.setStatus(NodeStatusType.error, "" + error);
|
|
2930
|
+
this.setStatus(NodeStatusType.error, { message: "" + error });
|
|
2725
2931
|
throw error;
|
|
2726
2932
|
}
|
|
2727
2933
|
finally {
|
|
2728
2934
|
this._requestId = 0;
|
|
2729
|
-
|
|
2935
|
+
elap = Date.now() - start;
|
|
2936
|
+
if (tree.options.debugLevel >= 3) {
|
|
2937
|
+
tree.logInfo(`Load source took ${elap / 1000} seconds (transfer: ${elapLoad / 1000}s, processing: ${elapProcess / 1000}s)`);
|
|
2938
|
+
}
|
|
2730
2939
|
}
|
|
2731
2940
|
}
|
|
2732
2941
|
/**Load content of a lazy node. */
|
|
@@ -2762,7 +2971,7 @@ class WunderbaumNode {
|
|
|
2762
2971
|
catch (e) {
|
|
2763
2972
|
this.logError("Error during loadLazy()", e);
|
|
2764
2973
|
this._callEvent("error", { error: e });
|
|
2765
|
-
this.setStatus(NodeStatusType.error, "" + e);
|
|
2974
|
+
this.setStatus(NodeStatusType.error, { message: "" + e });
|
|
2766
2975
|
}
|
|
2767
2976
|
return;
|
|
2768
2977
|
}
|
|
@@ -2804,17 +3013,23 @@ class WunderbaumNode {
|
|
|
2804
3013
|
* Defaults to {noAnimation: false, noEvents: false, scrollIntoView: true}
|
|
2805
3014
|
*/
|
|
2806
3015
|
async makeVisible(opts) {
|
|
2807
|
-
let i, dfd = new Deferred(), deferreds = [], parents = this.getParentList(false, false), len = parents.length,
|
|
3016
|
+
let i, dfd = new Deferred(), deferreds = [], parents = this.getParentList(false, false), len = parents.length,
|
|
3017
|
+
// effects = !(opts && opts.noAnimation === true),
|
|
3018
|
+
scroll = !(opts && opts.scrollIntoView === false);
|
|
2808
3019
|
// Expand bottom-up, so only the top node is animated
|
|
2809
3020
|
for (i = len - 1; i >= 0; i--) {
|
|
2810
3021
|
// self.debug("pushexpand" + parents[i]);
|
|
2811
|
-
|
|
3022
|
+
const seOpts = { noAnimation: opts === null || opts === void 0 ? void 0 : opts.noAnimation };
|
|
3023
|
+
deferreds.push(parents[i].setExpanded(true, seOpts));
|
|
2812
3024
|
}
|
|
2813
3025
|
Promise.all(deferreds).then(() => {
|
|
2814
3026
|
// All expands have finished
|
|
2815
3027
|
// self.debug("expand DONE", scroll);
|
|
2816
|
-
|
|
2817
|
-
|
|
3028
|
+
// Note: this.tree may be none when switching demo trees
|
|
3029
|
+
if (scroll && this.tree) {
|
|
3030
|
+
// Make sure markup and _rowIdx is updated before we do the scroll calculations
|
|
3031
|
+
this.tree.updatePendingModifications();
|
|
3032
|
+
this.scrollIntoView().then(() => {
|
|
2818
3033
|
// self.debug("scroll DONE");
|
|
2819
3034
|
dfd.resolve();
|
|
2820
3035
|
});
|
|
@@ -2999,21 +3214,30 @@ class WunderbaumNode {
|
|
|
2999
3214
|
}
|
|
3000
3215
|
}
|
|
3001
3216
|
_getRenderInfo() {
|
|
3002
|
-
|
|
3003
|
-
|
|
3004
|
-
|
|
3217
|
+
const allColInfosById = {};
|
|
3218
|
+
const renderColInfosById = {};
|
|
3219
|
+
const isColspan = this.isColspan();
|
|
3220
|
+
const colElems = this._rowElem
|
|
3005
3221
|
? (this._rowElem.querySelectorAll("span.wb-col"))
|
|
3006
3222
|
: null;
|
|
3223
|
+
let idx = 0;
|
|
3007
3224
|
for (let col of this.tree.columns) {
|
|
3008
|
-
|
|
3225
|
+
allColInfosById[col.id] = {
|
|
3009
3226
|
id: col.id,
|
|
3010
3227
|
idx: idx,
|
|
3011
3228
|
elem: colElems ? colElems[idx] : null,
|
|
3012
3229
|
info: col,
|
|
3013
3230
|
};
|
|
3231
|
+
// renderColInfosById only contains columns that need rendering:
|
|
3232
|
+
if (!isColspan && col.id !== "*") {
|
|
3233
|
+
renderColInfosById[col.id] = allColInfosById[col.id];
|
|
3234
|
+
}
|
|
3014
3235
|
idx++;
|
|
3015
3236
|
}
|
|
3016
|
-
return
|
|
3237
|
+
return {
|
|
3238
|
+
allColInfosById: allColInfosById,
|
|
3239
|
+
renderColInfosById: renderColInfosById,
|
|
3240
|
+
};
|
|
3017
3241
|
}
|
|
3018
3242
|
_createIcon(parentElem, replaceChild) {
|
|
3019
3243
|
let iconSpan;
|
|
@@ -3060,30 +3284,193 @@ class WunderbaumNode {
|
|
|
3060
3284
|
else {
|
|
3061
3285
|
parentElem.appendChild(iconSpan);
|
|
3062
3286
|
}
|
|
3063
|
-
// this.log("_createIcon: ", iconSpan);
|
|
3064
|
-
return iconSpan;
|
|
3287
|
+
// this.log("_createIcon: ", iconSpan);
|
|
3288
|
+
return iconSpan;
|
|
3289
|
+
}
|
|
3290
|
+
/**
|
|
3291
|
+
* Create a whole new `<div class="wb-row">` element.
|
|
3292
|
+
* @see {@link WunderbaumNode.render}
|
|
3293
|
+
*/
|
|
3294
|
+
_render_markup(opts) {
|
|
3295
|
+
const tree = this.tree;
|
|
3296
|
+
const treeOptions = tree.options;
|
|
3297
|
+
const checkbox = this.getOption("checkbox") !== false;
|
|
3298
|
+
const columns = tree.columns;
|
|
3299
|
+
const level = this.getLevel();
|
|
3300
|
+
let elem;
|
|
3301
|
+
let nodeElem;
|
|
3302
|
+
let rowDiv = this._rowElem;
|
|
3303
|
+
let titleSpan;
|
|
3304
|
+
let checkboxSpan = null;
|
|
3305
|
+
let iconSpan;
|
|
3306
|
+
let expanderSpan = null;
|
|
3307
|
+
const activeColIdx = tree.isRowNav() ? null : tree.activeColIdx;
|
|
3308
|
+
const isNew = !rowDiv;
|
|
3309
|
+
assert(isNew);
|
|
3310
|
+
assert(!isNew || (opts && opts.after), "opts.after expected, unless updating");
|
|
3311
|
+
assert(!this.isRootNode());
|
|
3312
|
+
rowDiv = document.createElement("div");
|
|
3313
|
+
rowDiv.classList.add("wb-row");
|
|
3314
|
+
rowDiv.style.top = this._rowIdx * ROW_HEIGHT + "px";
|
|
3315
|
+
this._rowElem = rowDiv;
|
|
3316
|
+
// Attach a node reference to the DOM Element:
|
|
3317
|
+
rowDiv._wb_node = this;
|
|
3318
|
+
nodeElem = document.createElement("span");
|
|
3319
|
+
nodeElem.classList.add("wb-node", "wb-col");
|
|
3320
|
+
rowDiv.appendChild(nodeElem);
|
|
3321
|
+
let ofsTitlePx = 0;
|
|
3322
|
+
if (checkbox) {
|
|
3323
|
+
checkboxSpan = document.createElement("i");
|
|
3324
|
+
checkboxSpan.classList.add("wb-checkbox");
|
|
3325
|
+
nodeElem.appendChild(checkboxSpan);
|
|
3326
|
+
ofsTitlePx += ICON_WIDTH;
|
|
3327
|
+
}
|
|
3328
|
+
for (let i = level - 1; i > 0; i--) {
|
|
3329
|
+
elem = document.createElement("i");
|
|
3330
|
+
elem.classList.add("wb-indent");
|
|
3331
|
+
nodeElem.appendChild(elem);
|
|
3332
|
+
ofsTitlePx += ICON_WIDTH;
|
|
3333
|
+
}
|
|
3334
|
+
if (!treeOptions.minExpandLevel || level > treeOptions.minExpandLevel) {
|
|
3335
|
+
expanderSpan = document.createElement("i");
|
|
3336
|
+
expanderSpan.classList.add("wb-expander");
|
|
3337
|
+
nodeElem.appendChild(expanderSpan);
|
|
3338
|
+
ofsTitlePx += ICON_WIDTH;
|
|
3339
|
+
}
|
|
3340
|
+
iconSpan = this._createIcon(nodeElem);
|
|
3341
|
+
if (iconSpan) {
|
|
3342
|
+
ofsTitlePx += ICON_WIDTH;
|
|
3343
|
+
}
|
|
3344
|
+
titleSpan = document.createElement("span");
|
|
3345
|
+
titleSpan.classList.add("wb-title");
|
|
3346
|
+
nodeElem.appendChild(titleSpan);
|
|
3347
|
+
this._callEvent("enhanceTitle", { titleSpan: titleSpan });
|
|
3348
|
+
// Store the width of leading icons with the node, so we can calculate
|
|
3349
|
+
// the width of the embedded title span later
|
|
3350
|
+
nodeElem._ofsTitlePx = ofsTitlePx;
|
|
3351
|
+
// Support HTML5 drag-n-drop
|
|
3352
|
+
if (tree.options.dnd.dragStart) {
|
|
3353
|
+
nodeElem.draggable = true;
|
|
3354
|
+
}
|
|
3355
|
+
// Render columns
|
|
3356
|
+
const isColspan = this.isColspan();
|
|
3357
|
+
if (!isColspan && columns.length > 1) {
|
|
3358
|
+
let colIdx = 0;
|
|
3359
|
+
for (let col of columns) {
|
|
3360
|
+
colIdx++;
|
|
3361
|
+
let colElem;
|
|
3362
|
+
if (col.id === "*") {
|
|
3363
|
+
colElem = nodeElem;
|
|
3364
|
+
}
|
|
3365
|
+
else {
|
|
3366
|
+
colElem = document.createElement("span");
|
|
3367
|
+
colElem.classList.add("wb-col");
|
|
3368
|
+
rowDiv.appendChild(colElem);
|
|
3369
|
+
}
|
|
3370
|
+
if (colIdx === activeColIdx) {
|
|
3371
|
+
colElem.classList.add("wb-active");
|
|
3372
|
+
}
|
|
3373
|
+
// Add classes from `columns` definition to `<div.wb-col>` cells
|
|
3374
|
+
col.classes ? colElem.classList.add(...col.classes.split(" ")) : 0;
|
|
3375
|
+
colElem.style.left = col._ofsPx + "px";
|
|
3376
|
+
colElem.style.width = col._widthPx + "px";
|
|
3377
|
+
if (isNew && col.html) {
|
|
3378
|
+
if (typeof col.html === "string") {
|
|
3379
|
+
colElem.innerHTML = col.html;
|
|
3380
|
+
}
|
|
3381
|
+
}
|
|
3382
|
+
}
|
|
3383
|
+
}
|
|
3384
|
+
// Attach to DOM as late as possible
|
|
3385
|
+
const after = opts ? opts.after : "last";
|
|
3386
|
+
switch (after) {
|
|
3387
|
+
case "first":
|
|
3388
|
+
tree.nodeListElement.prepend(rowDiv);
|
|
3389
|
+
break;
|
|
3390
|
+
case "last":
|
|
3391
|
+
tree.nodeListElement.appendChild(rowDiv);
|
|
3392
|
+
break;
|
|
3393
|
+
default:
|
|
3394
|
+
opts.after.after(rowDiv);
|
|
3395
|
+
}
|
|
3396
|
+
// Now go on and fill in data and update classes
|
|
3397
|
+
opts.isNew = true;
|
|
3398
|
+
this._render_data(opts);
|
|
3399
|
+
}
|
|
3400
|
+
/**
|
|
3401
|
+
* Render `node.title`, `.icon` into an existing row.
|
|
3402
|
+
*
|
|
3403
|
+
* @see {@link WunderbaumNode.render}
|
|
3404
|
+
*/
|
|
3405
|
+
_render_data(opts) {
|
|
3406
|
+
assert(this._rowElem);
|
|
3407
|
+
const tree = this.tree;
|
|
3408
|
+
const treeOptions = tree.options;
|
|
3409
|
+
const rowDiv = this._rowElem;
|
|
3410
|
+
const isNew = !!opts.isNew; // Called by _render_markup()?
|
|
3411
|
+
const columns = tree.columns;
|
|
3412
|
+
const isColspan = this.isColspan();
|
|
3413
|
+
// Row markup already exists
|
|
3414
|
+
const nodeElem = rowDiv.querySelector("span.wb-node");
|
|
3415
|
+
const titleSpan = nodeElem.querySelector("span.wb-title");
|
|
3416
|
+
if (this.titleWithHighlight) {
|
|
3417
|
+
titleSpan.innerHTML = this.titleWithHighlight;
|
|
3418
|
+
}
|
|
3419
|
+
else {
|
|
3420
|
+
titleSpan.textContent = this.title;
|
|
3421
|
+
}
|
|
3422
|
+
// Set the width of the title span, so overflow ellipsis work
|
|
3423
|
+
if (!treeOptions.skeleton) {
|
|
3424
|
+
if (isColspan) {
|
|
3425
|
+
let vpWidth = tree.element.clientWidth;
|
|
3426
|
+
titleSpan.style.width =
|
|
3427
|
+
vpWidth - nodeElem._ofsTitlePx - ROW_EXTRA_PAD + "px";
|
|
3428
|
+
}
|
|
3429
|
+
else {
|
|
3430
|
+
titleSpan.style.width =
|
|
3431
|
+
columns[0]._widthPx -
|
|
3432
|
+
nodeElem._ofsTitlePx -
|
|
3433
|
+
ROW_EXTRA_PAD +
|
|
3434
|
+
"px";
|
|
3435
|
+
}
|
|
3436
|
+
}
|
|
3437
|
+
// Update row classes
|
|
3438
|
+
opts.isDataChange = true;
|
|
3439
|
+
this._render_status(opts);
|
|
3440
|
+
// Let user modify the result
|
|
3441
|
+
if (this.statusNodeType) {
|
|
3442
|
+
this._callEvent("renderStatusNode", {
|
|
3443
|
+
isNew: isNew,
|
|
3444
|
+
nodeElem: nodeElem,
|
|
3445
|
+
});
|
|
3446
|
+
}
|
|
3447
|
+
else if (this.parent) {
|
|
3448
|
+
// Skip root node
|
|
3449
|
+
const renderInfo = this._getRenderInfo();
|
|
3450
|
+
this._callEvent("render", {
|
|
3451
|
+
isNew: isNew,
|
|
3452
|
+
isColspan: isColspan,
|
|
3453
|
+
// isDataChange: true,
|
|
3454
|
+
nodeElem: nodeElem,
|
|
3455
|
+
allColInfosById: renderInfo.allColInfosById,
|
|
3456
|
+
renderColInfosById: renderInfo.renderColInfosById,
|
|
3457
|
+
});
|
|
3458
|
+
}
|
|
3065
3459
|
}
|
|
3066
|
-
/**
|
|
3067
|
-
|
|
3460
|
+
/**
|
|
3461
|
+
* Update row classes to reflect active, focuses, etc.
|
|
3462
|
+
* @see {@link WunderbaumNode.render}
|
|
3463
|
+
*/
|
|
3464
|
+
_render_status(opts) {
|
|
3465
|
+
// this.log("_render_status", opts);
|
|
3068
3466
|
const tree = this.tree;
|
|
3069
3467
|
const treeOptions = tree.options;
|
|
3070
|
-
const checkbox = this.getOption("checkbox") !== false;
|
|
3071
|
-
const columns = tree.columns;
|
|
3072
3468
|
const typeInfo = this.type ? tree.types[this.type] : null;
|
|
3073
|
-
const
|
|
3074
|
-
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
|
|
3078
|
-
let checkboxSpan = null;
|
|
3079
|
-
let iconSpan;
|
|
3080
|
-
let expanderSpan = null;
|
|
3081
|
-
const activeColIdx = tree.navMode === NavigationMode.row ? null : tree.activeColIdx;
|
|
3082
|
-
// let colElems: HTMLElement[];
|
|
3083
|
-
const isNew = !rowDiv;
|
|
3084
|
-
assert(!isNew || (opts && opts.after), "opts.after expected, unless updating");
|
|
3085
|
-
assert(!this.isRootNode());
|
|
3086
|
-
//
|
|
3469
|
+
const rowDiv = this._rowElem;
|
|
3470
|
+
// Row markup already exists
|
|
3471
|
+
const nodeElem = rowDiv.querySelector("span.wb-node");
|
|
3472
|
+
const expanderSpan = nodeElem.querySelector("i.wb-expander");
|
|
3473
|
+
const checkboxSpan = nodeElem.querySelector("i.wb-checkbox");
|
|
3087
3474
|
let rowClasses = ["wb-row"];
|
|
3088
3475
|
this.expanded ? rowClasses.push("wb-expanded") : 0;
|
|
3089
3476
|
this.lazy ? rowClasses.push("wb-lazy") : 0;
|
|
@@ -3092,108 +3479,21 @@ class WunderbaumNode {
|
|
|
3092
3479
|
this === tree.focusNode ? rowClasses.push("wb-focus") : 0;
|
|
3093
3480
|
this._errorInfo ? rowClasses.push("wb-error") : 0;
|
|
3094
3481
|
this._isLoading ? rowClasses.push("wb-loading") : 0;
|
|
3482
|
+
this.isColspan() ? rowClasses.push("wb-colspan") : 0;
|
|
3095
3483
|
this.statusNodeType
|
|
3096
3484
|
? rowClasses.push("wb-status-" + this.statusNodeType)
|
|
3097
3485
|
: 0;
|
|
3098
3486
|
this.match ? rowClasses.push("wb-match") : 0;
|
|
3099
3487
|
this.subMatchCount ? rowClasses.push("wb-submatch") : 0;
|
|
3100
3488
|
treeOptions.skeleton ? rowClasses.push("wb-skeleton") : 0;
|
|
3101
|
-
//
|
|
3102
|
-
|
|
3103
|
-
|
|
3104
|
-
|
|
3105
|
-
nodeElem = rowDiv.querySelector("span.wb-node");
|
|
3106
|
-
titleSpan = nodeElem.querySelector("span.wb-title");
|
|
3107
|
-
expanderSpan = nodeElem.querySelector("i.wb-expander");
|
|
3108
|
-
checkboxSpan = nodeElem.querySelector("i.wb-checkbox");
|
|
3109
|
-
iconSpan = nodeElem.querySelector("i.wb-icon");
|
|
3110
|
-
// TODO: we need this, when icons should be replacable
|
|
3111
|
-
// iconSpan = this._createIcon(nodeElem, iconSpan);
|
|
3112
|
-
// colElems = (<unknown>(
|
|
3113
|
-
// rowDiv.querySelectorAll("span.wb-col")
|
|
3114
|
-
// )) as HTMLElement[];
|
|
3115
|
-
}
|
|
3116
|
-
else {
|
|
3117
|
-
rowDiv = document.createElement("div");
|
|
3118
|
-
// rowDiv.classList.add("wb-row");
|
|
3119
|
-
// Attach a node reference to the DOM Element:
|
|
3120
|
-
rowDiv._wb_node = this;
|
|
3121
|
-
nodeElem = document.createElement("span");
|
|
3122
|
-
nodeElem.classList.add("wb-node", "wb-col");
|
|
3123
|
-
rowDiv.appendChild(nodeElem);
|
|
3124
|
-
let ofsTitlePx = 0;
|
|
3125
|
-
if (checkbox) {
|
|
3126
|
-
checkboxSpan = document.createElement("i");
|
|
3127
|
-
nodeElem.appendChild(checkboxSpan);
|
|
3128
|
-
ofsTitlePx += ICON_WIDTH;
|
|
3129
|
-
}
|
|
3130
|
-
for (let i = level - 1; i > 0; i--) {
|
|
3131
|
-
elem = document.createElement("i");
|
|
3132
|
-
elem.classList.add("wb-indent");
|
|
3133
|
-
nodeElem.appendChild(elem);
|
|
3134
|
-
ofsTitlePx += ICON_WIDTH;
|
|
3135
|
-
}
|
|
3136
|
-
if (!treeOptions.minExpandLevel || level > treeOptions.minExpandLevel) {
|
|
3137
|
-
expanderSpan = document.createElement("i");
|
|
3138
|
-
nodeElem.appendChild(expanderSpan);
|
|
3139
|
-
ofsTitlePx += ICON_WIDTH;
|
|
3140
|
-
}
|
|
3141
|
-
iconSpan = this._createIcon(nodeElem);
|
|
3142
|
-
if (iconSpan) {
|
|
3143
|
-
ofsTitlePx += ICON_WIDTH;
|
|
3144
|
-
}
|
|
3145
|
-
titleSpan = document.createElement("span");
|
|
3146
|
-
titleSpan.classList.add("wb-title");
|
|
3147
|
-
nodeElem.appendChild(titleSpan);
|
|
3148
|
-
this._callEvent("enhanceTitle", { titleSpan: titleSpan });
|
|
3149
|
-
// Store the width of leading icons with the node, so we can calculate
|
|
3150
|
-
// the width of the embedded title span later
|
|
3151
|
-
nodeElem._ofsTitlePx = ofsTitlePx;
|
|
3152
|
-
if (tree.options.dnd.dragStart) {
|
|
3153
|
-
nodeElem.draggable = true;
|
|
3154
|
-
}
|
|
3155
|
-
// Render columns
|
|
3156
|
-
// colElems = [];
|
|
3157
|
-
if (!this.colspan && columns.length > 1) {
|
|
3158
|
-
let colIdx = 0;
|
|
3159
|
-
for (let col of columns) {
|
|
3160
|
-
colIdx++;
|
|
3161
|
-
let colElem;
|
|
3162
|
-
if (col.id === "*") {
|
|
3163
|
-
colElem = nodeElem;
|
|
3164
|
-
}
|
|
3165
|
-
else {
|
|
3166
|
-
colElem = document.createElement("span");
|
|
3167
|
-
colElem.classList.add("wb-col");
|
|
3168
|
-
// colElem.textContent = "" + col.id;
|
|
3169
|
-
rowDiv.appendChild(colElem);
|
|
3170
|
-
}
|
|
3171
|
-
if (colIdx === activeColIdx) {
|
|
3172
|
-
colElem.classList.add("wb-active");
|
|
3173
|
-
}
|
|
3174
|
-
// Add classes from `columns` definition to `<div.wb-col>` cells
|
|
3175
|
-
col.classes ? colElem.classList.add(...col.classes.split(" ")) : 0;
|
|
3176
|
-
colElem.style.left = col._ofsPx + "px";
|
|
3177
|
-
colElem.style.width = col._widthPx + "px";
|
|
3178
|
-
// colElems.push(colElem);
|
|
3179
|
-
if (isNew && col.html) {
|
|
3180
|
-
if (typeof col.html === "string") {
|
|
3181
|
-
colElem.innerHTML = col.html;
|
|
3182
|
-
}
|
|
3183
|
-
}
|
|
3184
|
-
}
|
|
3185
|
-
}
|
|
3186
|
-
}
|
|
3187
|
-
// --- From here common code starts (either new or existing markup):
|
|
3188
|
-
rowDiv.className = rowClasses.join(" "); // Reset prev. classes
|
|
3189
|
-
// Add classes from `node.extraClasses`
|
|
3190
|
-
rowDiv.classList.add(...this.extraClasses);
|
|
3489
|
+
// Replace previous classes:
|
|
3490
|
+
rowDiv.className = rowClasses.join(" ");
|
|
3491
|
+
// Add classes from `node.classes`
|
|
3492
|
+
this.classes ? rowDiv.classList.add(...this.classes) : 0;
|
|
3191
3493
|
// Add classes from `tree.types[node.type]`
|
|
3192
3494
|
if (typeInfo && typeInfo.classes) {
|
|
3193
3495
|
rowDiv.classList.add(...typeInfo.classes);
|
|
3194
3496
|
}
|
|
3195
|
-
// rowDiv.style.top = (this._rowIdx! * 1.1) + "em";
|
|
3196
|
-
rowDiv.style.top = this._rowIdx * ROW_HEIGHT + "px";
|
|
3197
3497
|
if (expanderSpan) {
|
|
3198
3498
|
if (this.isExpandable(false)) {
|
|
3199
3499
|
if (this.expanded) {
|
|
@@ -3221,59 +3521,46 @@ class WunderbaumNode {
|
|
|
3221
3521
|
checkboxSpan.className = "wb-checkbox " + iconMap.checkUnchecked;
|
|
3222
3522
|
}
|
|
3223
3523
|
}
|
|
3224
|
-
|
|
3225
|
-
|
|
3226
|
-
|
|
3227
|
-
|
|
3228
|
-
|
|
3229
|
-
titleSpan.textContent = this.title;
|
|
3230
|
-
// } else {
|
|
3231
|
-
// titleSpan.innerHTML = this.title;
|
|
3232
|
-
}
|
|
3233
|
-
// Set the width of the title span, so overflow ellipsis work
|
|
3234
|
-
if (!treeOptions.skeleton) {
|
|
3235
|
-
if (this.colspan) {
|
|
3236
|
-
let vpWidth = tree.element.clientWidth;
|
|
3237
|
-
titleSpan.style.width =
|
|
3238
|
-
vpWidth - nodeElem._ofsTitlePx - ROW_EXTRA_PAD + "px";
|
|
3524
|
+
// Fix active cell in cell-nav mode
|
|
3525
|
+
if (!opts.isNew) {
|
|
3526
|
+
let i = 0;
|
|
3527
|
+
for (let colSpan of rowDiv.children) {
|
|
3528
|
+
colSpan.classList.toggle("wb-active", i++ === tree.activeColIdx);
|
|
3239
3529
|
}
|
|
3240
|
-
|
|
3241
|
-
|
|
3242
|
-
|
|
3243
|
-
|
|
3244
|
-
ROW_EXTRA_PAD +
|
|
3245
|
-
"px";
|
|
3530
|
+
// Update icon (if not opts.isNew, which would rebuild markup anyway)
|
|
3531
|
+
const iconSpan = nodeElem.querySelector("i.wb-icon");
|
|
3532
|
+
if (iconSpan) {
|
|
3533
|
+
this._createIcon(nodeElem, iconSpan);
|
|
3246
3534
|
}
|
|
3247
3535
|
}
|
|
3248
|
-
|
|
3249
|
-
|
|
3250
|
-
|
|
3251
|
-
|
|
3252
|
-
|
|
3253
|
-
|
|
3254
|
-
|
|
3255
|
-
|
|
3256
|
-
|
|
3257
|
-
|
|
3258
|
-
|
|
3259
|
-
|
|
3260
|
-
|
|
3261
|
-
|
|
3262
|
-
|
|
3263
|
-
|
|
3264
|
-
|
|
3265
|
-
|
|
3266
|
-
|
|
3267
|
-
|
|
3268
|
-
|
|
3269
|
-
|
|
3270
|
-
|
|
3271
|
-
|
|
3272
|
-
|
|
3273
|
-
|
|
3274
|
-
|
|
3275
|
-
|
|
3276
|
-
}
|
|
3536
|
+
}
|
|
3537
|
+
/**
|
|
3538
|
+
* Create or update node's markup.
|
|
3539
|
+
*
|
|
3540
|
+
* `options.change` defaults to ChangeType.data, which updates the title,
|
|
3541
|
+
* icon, and status. It also triggers the `render` event, that lets the user
|
|
3542
|
+
* create or update the content of embeded cell elements.<br>
|
|
3543
|
+
*
|
|
3544
|
+
* If only the status or other class-only modifications have changed,
|
|
3545
|
+
* `options.change` should be set to ChangeType.status instead for best
|
|
3546
|
+
* efficiency.
|
|
3547
|
+
*/
|
|
3548
|
+
render(options) {
|
|
3549
|
+
// this.log("render", options);
|
|
3550
|
+
const opts = Object.assign({ change: ChangeType.data }, options);
|
|
3551
|
+
if (!this._rowElem) {
|
|
3552
|
+
opts.change = "row";
|
|
3553
|
+
}
|
|
3554
|
+
switch (opts.change) {
|
|
3555
|
+
case "status":
|
|
3556
|
+
this._render_status(opts);
|
|
3557
|
+
break;
|
|
3558
|
+
case "data":
|
|
3559
|
+
this._render_data(opts);
|
|
3560
|
+
break;
|
|
3561
|
+
default:
|
|
3562
|
+
this._render_markup(opts);
|
|
3563
|
+
break;
|
|
3277
3564
|
}
|
|
3278
3565
|
}
|
|
3279
3566
|
/**
|
|
@@ -3395,7 +3682,8 @@ class WunderbaumNode {
|
|
|
3395
3682
|
* @see {@link Wunderbaum.scrollTo|Wunderbaum.scrollTo()}
|
|
3396
3683
|
*/
|
|
3397
3684
|
async scrollIntoView(options) {
|
|
3398
|
-
|
|
3685
|
+
const opts = Object.assign({ node: this }, options);
|
|
3686
|
+
return this.tree.scrollTo(opts);
|
|
3399
3687
|
}
|
|
3400
3688
|
/**
|
|
3401
3689
|
* Activate this node, deactivate previous, send events, activate column and scroll int viewport.
|
|
@@ -3403,26 +3691,26 @@ class WunderbaumNode {
|
|
|
3403
3691
|
async setActive(flag = true, options) {
|
|
3404
3692
|
const tree = this.tree;
|
|
3405
3693
|
const prev = tree.activeNode;
|
|
3406
|
-
const retrigger = options === null || options === void 0 ? void 0 : options.retrigger;
|
|
3407
|
-
const
|
|
3694
|
+
const retrigger = options === null || options === void 0 ? void 0 : options.retrigger; // Default: false
|
|
3695
|
+
const focusTree = options === null || options === void 0 ? void 0 : options.focusTree; // Default: false
|
|
3696
|
+
const focusNode = (options === null || options === void 0 ? void 0 : options.focusNode) !== false; // Default: true
|
|
3697
|
+
const noEvents = options === null || options === void 0 ? void 0 : options.noEvents; // Default: false
|
|
3698
|
+
const orgEvent = options === null || options === void 0 ? void 0 : options.event; // Default: false
|
|
3408
3699
|
if (!noEvents) {
|
|
3409
|
-
let orgEvent = options === null || options === void 0 ? void 0 : options.event;
|
|
3410
3700
|
if (flag) {
|
|
3411
3701
|
if (prev !== this || retrigger) {
|
|
3412
3702
|
if ((prev === null || prev === void 0 ? void 0 : prev._callEvent("deactivate", {
|
|
3413
3703
|
nextNode: this,
|
|
3414
3704
|
orgEvent: orgEvent,
|
|
3415
|
-
})) === false
|
|
3416
|
-
|
|
3417
|
-
|
|
3418
|
-
|
|
3419
|
-
|
|
3420
|
-
orgEvent: orgEvent,
|
|
3421
|
-
}) === false) {
|
|
3422
|
-
tree.activeNode = null;
|
|
3423
|
-
prev === null || prev === void 0 ? void 0 : prev.setModified();
|
|
3705
|
+
})) === false ||
|
|
3706
|
+
this._callEvent("beforeActivate", {
|
|
3707
|
+
prevNode: prev,
|
|
3708
|
+
orgEvent: orgEvent,
|
|
3709
|
+
}) === false) {
|
|
3424
3710
|
return;
|
|
3425
3711
|
}
|
|
3712
|
+
tree.activeNode = null;
|
|
3713
|
+
prev === null || prev === void 0 ? void 0 : prev.setModified(ChangeType.status);
|
|
3426
3714
|
}
|
|
3427
3715
|
}
|
|
3428
3716
|
else if (prev === this || retrigger) {
|
|
@@ -3430,44 +3718,54 @@ class WunderbaumNode {
|
|
|
3430
3718
|
}
|
|
3431
3719
|
}
|
|
3432
3720
|
if (prev !== this) {
|
|
3433
|
-
|
|
3434
|
-
|
|
3435
|
-
|
|
3721
|
+
if (flag) {
|
|
3722
|
+
tree.activeNode = this;
|
|
3723
|
+
if (focusNode || focusTree)
|
|
3724
|
+
tree.focusNode = this;
|
|
3725
|
+
if (focusTree)
|
|
3726
|
+
tree.setFocus();
|
|
3727
|
+
}
|
|
3728
|
+
prev === null || prev === void 0 ? void 0 : prev.setModified(ChangeType.status);
|
|
3729
|
+
this.setModified(ChangeType.status);
|
|
3436
3730
|
}
|
|
3437
3731
|
if (options &&
|
|
3438
3732
|
options.colIdx != null &&
|
|
3439
3733
|
options.colIdx !== tree.activeColIdx &&
|
|
3440
|
-
tree.
|
|
3734
|
+
tree.isCellNav()) {
|
|
3441
3735
|
tree.setColumn(options.colIdx);
|
|
3442
3736
|
}
|
|
3443
|
-
|
|
3444
|
-
|
|
3445
|
-
|
|
3446
|
-
return this.
|
|
3737
|
+
if (flag && !noEvents) {
|
|
3738
|
+
this._callEvent("activate", { prevNode: prev, orgEvent: orgEvent });
|
|
3739
|
+
}
|
|
3740
|
+
return this.makeVisible();
|
|
3447
3741
|
}
|
|
3448
3742
|
/**
|
|
3449
3743
|
* Expand or collapse this node.
|
|
3450
3744
|
*/
|
|
3451
3745
|
async setExpanded(flag = true, options) {
|
|
3452
|
-
// alert("" + this.getLevel() + ", "+ this.getOption("minExpandLevel");
|
|
3453
3746
|
if (!flag &&
|
|
3454
3747
|
this.isExpanded() &&
|
|
3455
|
-
this.getLevel()
|
|
3748
|
+
this.getLevel() <= this.tree.getOption("minExpandLevel") &&
|
|
3456
3749
|
!getOption(options, "force")) {
|
|
3457
3750
|
this.logDebug("Ignored collapse request below expandLevel.");
|
|
3458
3751
|
return;
|
|
3459
3752
|
}
|
|
3753
|
+
if (!flag === !this.expanded) {
|
|
3754
|
+
return; // Nothing to do
|
|
3755
|
+
}
|
|
3460
3756
|
if (flag && this.lazy && this.children == null) {
|
|
3461
3757
|
await this.loadLazy();
|
|
3462
3758
|
}
|
|
3463
3759
|
this.expanded = flag;
|
|
3464
|
-
|
|
3760
|
+
const updateOpts = { immediate: !!getOption(options, "immediate") };
|
|
3761
|
+
this.tree.setModified(ChangeType.structure, updateOpts);
|
|
3465
3762
|
}
|
|
3466
3763
|
/**
|
|
3467
3764
|
* Set keyboard focus here.
|
|
3468
3765
|
* @see {@link setActive}
|
|
3469
3766
|
*/
|
|
3470
|
-
setFocus(flag = true
|
|
3767
|
+
setFocus(flag = true) {
|
|
3768
|
+
assert(!!flag, "blur is not yet implemented");
|
|
3471
3769
|
const prev = this.tree.focusNode;
|
|
3472
3770
|
this.tree.focusNode = this;
|
|
3473
3771
|
prev === null || prev === void 0 ? void 0 : prev.setModified();
|
|
@@ -3482,10 +3780,16 @@ class WunderbaumNode {
|
|
|
3482
3780
|
setKey(key, refKey) {
|
|
3483
3781
|
throw new Error("Not yet implemented");
|
|
3484
3782
|
}
|
|
3485
|
-
/**
|
|
3486
|
-
|
|
3487
|
-
|
|
3488
|
-
|
|
3783
|
+
/**
|
|
3784
|
+
* Schedule a render, typically called to update after a status or data change.
|
|
3785
|
+
*
|
|
3786
|
+
* `change` defaults to 'data', which handles modifcations of title, icon,
|
|
3787
|
+
* and column content. It can be reduced to 'ChangeType.status' if only
|
|
3788
|
+
* active/focus/selected state has changed.
|
|
3789
|
+
*/
|
|
3790
|
+
setModified(change = ChangeType.data) {
|
|
3791
|
+
assert(change === ChangeType.status || change === ChangeType.data);
|
|
3792
|
+
this.tree.setModified(change, this);
|
|
3489
3793
|
}
|
|
3490
3794
|
/** Modify the check/uncheck state. */
|
|
3491
3795
|
setSelected(flag = true, options) {
|
|
@@ -3497,8 +3801,10 @@ class WunderbaumNode {
|
|
|
3497
3801
|
this.setModified();
|
|
3498
3802
|
}
|
|
3499
3803
|
/** Display node status (ok, loading, error, noData) using styles and a dummy child node. */
|
|
3500
|
-
setStatus(status,
|
|
3804
|
+
setStatus(status, options) {
|
|
3501
3805
|
const tree = this.tree;
|
|
3806
|
+
const message = options === null || options === void 0 ? void 0 : options.message;
|
|
3807
|
+
const details = options === null || options === void 0 ? void 0 : options.details;
|
|
3502
3808
|
let statusNode = null;
|
|
3503
3809
|
const _clearStatusNode = () => {
|
|
3504
3810
|
// Remove dedicated dummy node, if any
|
|
@@ -3596,9 +3902,9 @@ class WunderbaumNode {
|
|
|
3596
3902
|
* @param {object} [extra]
|
|
3597
3903
|
*/
|
|
3598
3904
|
triggerModify(operation, extra) {
|
|
3599
|
-
if (!this.parent) {
|
|
3600
|
-
|
|
3601
|
-
}
|
|
3905
|
+
// if (!this.parent) {
|
|
3906
|
+
// return;
|
|
3907
|
+
// }
|
|
3602
3908
|
this.parent.triggerModifyChild(operation, this, extra);
|
|
3603
3909
|
}
|
|
3604
3910
|
/**
|
|
@@ -3681,7 +3987,7 @@ WunderbaumNode.sequence = 0;
|
|
|
3681
3987
|
/*!
|
|
3682
3988
|
* Wunderbaum - ext-edit
|
|
3683
3989
|
* Copyright (c) 2021-2022, Martin Wendt. Released under the MIT license.
|
|
3684
|
-
* v0.0.
|
|
3990
|
+
* v0.0.6, Sat, 10 Sep 2022 19:29:21 GMT (https://github.com/mar10/wunderbaum)
|
|
3685
3991
|
*/
|
|
3686
3992
|
// const START_MARKER = "\uFFF7";
|
|
3687
3993
|
class EditExtension extends WunderbaumExtension {
|
|
@@ -3711,7 +4017,7 @@ class EditExtension extends WunderbaumExtension {
|
|
|
3711
4017
|
_applyChange(eventName, node, colElem, extra) {
|
|
3712
4018
|
let res;
|
|
3713
4019
|
node.log(`_applyChange(${eventName})`, extra);
|
|
3714
|
-
colElem.classList.add("wb-
|
|
4020
|
+
colElem.classList.add("wb-busy");
|
|
3715
4021
|
colElem.classList.remove("wb-error");
|
|
3716
4022
|
try {
|
|
3717
4023
|
res = node._callEvent(eventName, extra);
|
|
@@ -3719,7 +4025,7 @@ class EditExtension extends WunderbaumExtension {
|
|
|
3719
4025
|
catch (err) {
|
|
3720
4026
|
node.logError(`Error in ${eventName} event handler`, err);
|
|
3721
4027
|
colElem.classList.add("wb-error");
|
|
3722
|
-
colElem.classList.remove("wb-
|
|
4028
|
+
colElem.classList.remove("wb-busy");
|
|
3723
4029
|
}
|
|
3724
4030
|
// Convert scalar return value to a resolved promise
|
|
3725
4031
|
if (!(res instanceof Promise)) {
|
|
@@ -3731,7 +4037,7 @@ class EditExtension extends WunderbaumExtension {
|
|
|
3731
4037
|
colElem.classList.add("wb-error");
|
|
3732
4038
|
})
|
|
3733
4039
|
.finally(() => {
|
|
3734
|
-
colElem.classList.remove("wb-
|
|
4040
|
+
colElem.classList.remove("wb-busy");
|
|
3735
4041
|
});
|
|
3736
4042
|
return res;
|
|
3737
4043
|
}
|
|
@@ -3770,12 +4076,12 @@ class EditExtension extends WunderbaumExtension {
|
|
|
3770
4076
|
const eventName = eventToString(event);
|
|
3771
4077
|
const tree = this.tree;
|
|
3772
4078
|
const trigger = this.getPluginOption("trigger");
|
|
3773
|
-
const inputElem =
|
|
3774
|
-
//
|
|
3775
|
-
tree.logDebug(`_preprocessKeyEvent: ${eventName}`);
|
|
4079
|
+
// const inputElem =
|
|
4080
|
+
// event.target && event.target.closest("input,[contenteditable]");
|
|
4081
|
+
// tree.logDebug(`_preprocessKeyEvent: ${eventName}`);
|
|
3776
4082
|
// --- Title editing: apply/discard ---
|
|
3777
|
-
if (inputElem) {
|
|
3778
|
-
|
|
4083
|
+
// if (inputElem) {
|
|
4084
|
+
if (this.isEditingTitle()) {
|
|
3779
4085
|
switch (eventName) {
|
|
3780
4086
|
case "Enter":
|
|
3781
4087
|
this._stopEditTitle(true, { event: event });
|
|
@@ -3789,7 +4095,7 @@ class EditExtension extends WunderbaumExtension {
|
|
|
3789
4095
|
return false;
|
|
3790
4096
|
}
|
|
3791
4097
|
// --- Trigger title editing
|
|
3792
|
-
if (tree.
|
|
4098
|
+
if (tree.isRowNav() || tree.activeColIdx === 0) {
|
|
3793
4099
|
switch (eventName) {
|
|
3794
4100
|
case "Enter":
|
|
3795
4101
|
if (trigger.indexOf("macEnter") >= 0 && isMac) {
|
|
@@ -3838,7 +4144,7 @@ class EditExtension extends WunderbaumExtension {
|
|
|
3838
4144
|
titleSpan.innerHTML = inputHtml;
|
|
3839
4145
|
const inputElem = titleSpan.firstElementChild;
|
|
3840
4146
|
if (validity) {
|
|
3841
|
-
// Permanently apply
|
|
4147
|
+
// Permanently apply input validations (CSS and tooltip)
|
|
3842
4148
|
inputElem.addEventListener("keydown", (e) => {
|
|
3843
4149
|
inputElem.setCustomValidity("");
|
|
3844
4150
|
if (!inputElem.reportValidity()) ;
|
|
@@ -3954,7 +4260,7 @@ class EditExtension extends WunderbaumExtension {
|
|
|
3954
4260
|
return;
|
|
3955
4261
|
}
|
|
3956
4262
|
const newNode = node.addNode(init, mode);
|
|
3957
|
-
newNode.
|
|
4263
|
+
newNode.setClass("wb-edit-new");
|
|
3958
4264
|
this.relatedNode = node;
|
|
3959
4265
|
// Don't filter new nodes:
|
|
3960
4266
|
newNode.match = true;
|
|
@@ -3967,17 +4273,26 @@ class EditExtension extends WunderbaumExtension {
|
|
|
3967
4273
|
/*!
|
|
3968
4274
|
* wunderbaum.ts
|
|
3969
4275
|
*
|
|
3970
|
-
* A
|
|
4276
|
+
* A treegrid control.
|
|
3971
4277
|
*
|
|
3972
4278
|
* Copyright (c) 2021-2022, Martin Wendt (https://wwWendt.de).
|
|
3973
|
-
*
|
|
4279
|
+
* https://github.com/mar10/wunderbaum
|
|
3974
4280
|
*
|
|
3975
|
-
*
|
|
3976
|
-
* @
|
|
4281
|
+
* Released under the MIT license.
|
|
4282
|
+
* @version v0.0.6
|
|
4283
|
+
* @date Sat, 10 Sep 2022 19:29:21 GMT
|
|
3977
4284
|
*/
|
|
3978
|
-
|
|
3979
|
-
|
|
3980
|
-
|
|
4285
|
+
class WbSystemRoot extends WunderbaumNode {
|
|
4286
|
+
constructor(tree) {
|
|
4287
|
+
super(tree, null, {
|
|
4288
|
+
key: "__root__",
|
|
4289
|
+
title: tree.id,
|
|
4290
|
+
});
|
|
4291
|
+
}
|
|
4292
|
+
toString() {
|
|
4293
|
+
return `WbSystemRoot@${this.key}<'${this.tree.id}'>`;
|
|
4294
|
+
}
|
|
4295
|
+
}
|
|
3981
4296
|
/**
|
|
3982
4297
|
* A persistent plain object or array.
|
|
3983
4298
|
*
|
|
@@ -3985,14 +4300,15 @@ class EditExtension extends WunderbaumExtension {
|
|
|
3985
4300
|
*/
|
|
3986
4301
|
class Wunderbaum {
|
|
3987
4302
|
constructor(options) {
|
|
4303
|
+
this.enabled = true;
|
|
4304
|
+
/** Contains additional data that was sent as response to an Ajax source load request. */
|
|
4305
|
+
this.data = {};
|
|
3988
4306
|
this.extensionList = [];
|
|
3989
4307
|
this.extensions = {};
|
|
3990
4308
|
this.keyMap = new Map();
|
|
3991
4309
|
this.refKeyMap = new Map();
|
|
3992
|
-
// protected viewNodes = new Set<WunderbaumNode>();
|
|
3993
4310
|
this.treeRowCount = 0;
|
|
3994
4311
|
this._disableUpdateCount = 0;
|
|
3995
|
-
// protected eventHandlers : Array<function> = [];
|
|
3996
4312
|
/** Currently active node if any. */
|
|
3997
4313
|
this.activeNode = null;
|
|
3998
4314
|
/** Current node hat has keyboard focus if any. */
|
|
@@ -4000,13 +4316,11 @@ class Wunderbaum {
|
|
|
4000
4316
|
/** Shared properties, referenced by `node.type`. */
|
|
4001
4317
|
this.types = {};
|
|
4002
4318
|
/** List of column definitions. */
|
|
4003
|
-
this.columns = [];
|
|
4319
|
+
this.columns = []; // any[] = [];
|
|
4004
4320
|
this._columnsById = {};
|
|
4005
4321
|
// Modification Status
|
|
4006
|
-
// protected changedSince = 0;
|
|
4007
|
-
// protected changes = new Set<ChangeType>();
|
|
4008
|
-
// protected changedNodes = new Set<WunderbaumNode>();
|
|
4009
4322
|
this.changeRedrawRequestPending = false;
|
|
4323
|
+
this.changeScrollRequestPending = false;
|
|
4010
4324
|
/** Expose some useful methods of the util.ts module as `tree._util`. */
|
|
4011
4325
|
this._util = util;
|
|
4012
4326
|
// --- FILTER ---
|
|
@@ -4015,7 +4329,7 @@ class Wunderbaum {
|
|
|
4015
4329
|
/** @internal Use `setColumn()`/`getActiveColElem()`*/
|
|
4016
4330
|
this.activeColIdx = 0;
|
|
4017
4331
|
/** @internal */
|
|
4018
|
-
this.
|
|
4332
|
+
this._cellNavMode = false;
|
|
4019
4333
|
/** @internal */
|
|
4020
4334
|
this.lastQuicksearchTime = 0;
|
|
4021
4335
|
/** @internal */
|
|
@@ -4032,18 +4346,21 @@ class Wunderbaum {
|
|
|
4032
4346
|
element: null,
|
|
4033
4347
|
debugLevel: DEFAULT_DEBUGLEVEL,
|
|
4034
4348
|
header: null,
|
|
4035
|
-
headerHeightPx: ROW_HEIGHT,
|
|
4349
|
+
// headerHeightPx: ROW_HEIGHT,
|
|
4036
4350
|
rowHeightPx: ROW_HEIGHT,
|
|
4037
4351
|
columns: null,
|
|
4038
4352
|
types: null,
|
|
4039
4353
|
// escapeTitles: true,
|
|
4354
|
+
enabled: true,
|
|
4355
|
+
fixedCol: false,
|
|
4040
4356
|
showSpinner: false,
|
|
4041
|
-
checkbox:
|
|
4357
|
+
checkbox: false,
|
|
4042
4358
|
minExpandLevel: 0,
|
|
4043
4359
|
updateThrottleWait: 200,
|
|
4044
4360
|
skeleton: false,
|
|
4361
|
+
connectTopBreadcrumb: null,
|
|
4045
4362
|
// --- KeyNav ---
|
|
4046
|
-
|
|
4363
|
+
navigationModeOption: null,
|
|
4047
4364
|
quicksearch: true,
|
|
4048
4365
|
// --- Events ---
|
|
4049
4366
|
change: noop,
|
|
@@ -4085,41 +4402,25 @@ class Wunderbaum {
|
|
|
4085
4402
|
}
|
|
4086
4403
|
});
|
|
4087
4404
|
this.id = opts.id || "wb_" + ++Wunderbaum.sequence;
|
|
4088
|
-
this.root = new
|
|
4089
|
-
key: "__root__",
|
|
4090
|
-
// title: "__root__",
|
|
4091
|
-
});
|
|
4405
|
+
this.root = new WbSystemRoot(this);
|
|
4092
4406
|
this._registerExtension(new KeynavExtension(this));
|
|
4093
4407
|
this._registerExtension(new EditExtension(this));
|
|
4094
4408
|
this._registerExtension(new FilterExtension(this));
|
|
4095
4409
|
this._registerExtension(new DndExtension(this));
|
|
4096
4410
|
this._registerExtension(new GridExtension(this));
|
|
4097
4411
|
this._registerExtension(new LoggerExtension(this));
|
|
4412
|
+
this._updateViewportThrottled = adaptiveThrottle(this._updateViewportImmediately.bind(this), {});
|
|
4098
4413
|
// --- Evaluate options
|
|
4099
4414
|
this.columns = opts.columns;
|
|
4100
4415
|
delete opts.columns;
|
|
4101
|
-
if (!this.columns) {
|
|
4102
|
-
|
|
4103
|
-
this.columns = [{ id: "*", title:
|
|
4104
|
-
}
|
|
4105
|
-
this.types = opts.types || {};
|
|
4106
|
-
delete opts.types;
|
|
4107
|
-
// Convert `TYPE.classes` to a Set
|
|
4108
|
-
for (let t of Object.values(this.types)) {
|
|
4109
|
-
if (t.classes) {
|
|
4110
|
-
t.classes = toSet(t.classes);
|
|
4111
|
-
}
|
|
4416
|
+
if (!this.columns || !this.columns.length) {
|
|
4417
|
+
const title = typeof opts.header === "string" ? opts.header : this.id;
|
|
4418
|
+
this.columns = [{ id: "*", title: title, width: "*" }];
|
|
4112
4419
|
}
|
|
4113
|
-
if (
|
|
4114
|
-
opts.
|
|
4420
|
+
if (opts.types) {
|
|
4421
|
+
this.setTypes(opts.types, true);
|
|
4115
4422
|
}
|
|
4116
|
-
|
|
4117
|
-
opts.navigationMode === NavigationModeOption.startCell) {
|
|
4118
|
-
this.navMode = NavigationMode.cellNav;
|
|
4119
|
-
}
|
|
4120
|
-
this._updateViewportThrottled = throttle(() => {
|
|
4121
|
-
this._updateViewport();
|
|
4122
|
-
}, opts.updateThrottleWait, { leading: true, trailing: true });
|
|
4423
|
+
delete opts.types;
|
|
4123
4424
|
// --- Create Markup
|
|
4124
4425
|
this.element = elemFromSelector(opts.element);
|
|
4125
4426
|
assert(!!this.element, `Invalid 'element' option: ${opts.element}`);
|
|
@@ -4139,12 +4440,15 @@ class Wunderbaum {
|
|
|
4139
4440
|
const rowElement = this.headerElement.querySelector("div.wb-row");
|
|
4140
4441
|
for (const colDiv of rowElement.querySelectorAll("div")) {
|
|
4141
4442
|
this.columns.push({
|
|
4142
|
-
id: colDiv.dataset.id ||
|
|
4143
|
-
|
|
4443
|
+
id: colDiv.dataset.id || `col_${this.columns.length}`,
|
|
4444
|
+
// id: colDiv.dataset.id || null,
|
|
4445
|
+
title: "" + colDiv.textContent,
|
|
4446
|
+
// text: "" + colDiv.textContent,
|
|
4447
|
+
width: "*", // TODO: read from header span
|
|
4144
4448
|
});
|
|
4145
4449
|
}
|
|
4146
4450
|
}
|
|
4147
|
-
else
|
|
4451
|
+
else {
|
|
4148
4452
|
// We need a row div, the rest will be computed from `this.columns`
|
|
4149
4453
|
const coldivs = "<span class='wb-col'></span>".repeat(this.columns.length);
|
|
4150
4454
|
this.element.innerHTML = `
|
|
@@ -4153,23 +4457,27 @@ class Wunderbaum {
|
|
|
4153
4457
|
${coldivs}
|
|
4154
4458
|
</div>
|
|
4155
4459
|
</div>`;
|
|
4156
|
-
|
|
4157
|
-
|
|
4158
|
-
|
|
4159
|
-
|
|
4460
|
+
if (!wantHeader) {
|
|
4461
|
+
const he = this.element.querySelector("div.wb-header");
|
|
4462
|
+
he.style.display = "none";
|
|
4463
|
+
}
|
|
4160
4464
|
}
|
|
4161
4465
|
//
|
|
4162
4466
|
this.element.innerHTML += `
|
|
4163
4467
|
<div class="wb-scroll-container">
|
|
4164
4468
|
<div class="wb-node-list"></div>
|
|
4165
4469
|
</div>`;
|
|
4166
|
-
this.
|
|
4167
|
-
this.nodeListElement = this.
|
|
4470
|
+
this.scrollContainerElement = this.element.querySelector("div.wb-scroll-container");
|
|
4471
|
+
this.nodeListElement = this.scrollContainerElement.querySelector("div.wb-node-list");
|
|
4168
4472
|
this.headerElement = this.element.querySelector("div.wb-header");
|
|
4169
|
-
|
|
4170
|
-
this.element.classList.add("wb-grid");
|
|
4171
|
-
}
|
|
4473
|
+
this.element.classList.toggle("wb-grid", this.columns.length > 1);
|
|
4172
4474
|
this._initExtensions();
|
|
4475
|
+
// --- apply initial options
|
|
4476
|
+
["enabled", "fixedCol"].forEach((optName) => {
|
|
4477
|
+
if (opts[optName] != null) {
|
|
4478
|
+
this.setOption(optName, opts[optName]);
|
|
4479
|
+
}
|
|
4480
|
+
});
|
|
4173
4481
|
// --- Load initial data
|
|
4174
4482
|
if (opts.source) {
|
|
4175
4483
|
if (opts.showSpinner) {
|
|
@@ -4178,6 +4486,18 @@ class Wunderbaum {
|
|
|
4178
4486
|
}
|
|
4179
4487
|
this.load(opts.source)
|
|
4180
4488
|
.then(() => {
|
|
4489
|
+
// The source may have defined columns, so we may adjust the nav mode
|
|
4490
|
+
if (opts.navigationModeOption == null) {
|
|
4491
|
+
if (this.isGrid()) {
|
|
4492
|
+
this.setNavigationOption(NavigationOptions.cell);
|
|
4493
|
+
}
|
|
4494
|
+
else {
|
|
4495
|
+
this.setNavigationOption(NavigationOptions.row);
|
|
4496
|
+
}
|
|
4497
|
+
}
|
|
4498
|
+
else {
|
|
4499
|
+
this.setNavigationOption(opts.navigationModeOption);
|
|
4500
|
+
}
|
|
4181
4501
|
readyDeferred.resolve();
|
|
4182
4502
|
})
|
|
4183
4503
|
.catch((error) => {
|
|
@@ -4192,23 +4512,37 @@ class Wunderbaum {
|
|
|
4192
4512
|
else {
|
|
4193
4513
|
readyDeferred.resolve();
|
|
4194
4514
|
}
|
|
4195
|
-
//
|
|
4196
|
-
//
|
|
4197
|
-
|
|
4198
|
-
this.updateViewport();
|
|
4199
|
-
}, 50);
|
|
4515
|
+
// Async mode is sometimes required, because this.element.clientWidth
|
|
4516
|
+
// has a wrong value at start???
|
|
4517
|
+
this.setModified(ChangeType.any);
|
|
4200
4518
|
// --- Bind listeners
|
|
4201
|
-
this.
|
|
4519
|
+
this.element.addEventListener("scroll", (e) => {
|
|
4520
|
+
// this.log("scroll", e);
|
|
4202
4521
|
this.setModified(ChangeType.vscroll);
|
|
4203
4522
|
});
|
|
4523
|
+
// this.scrollContainerElement.addEventListener("scroll", (e: Event) => {
|
|
4524
|
+
// this.log("scroll", e)
|
|
4525
|
+
// this.setModified(ChangeType.vscroll);
|
|
4526
|
+
// });
|
|
4204
4527
|
this.resizeObserver = new ResizeObserver((entries) => {
|
|
4205
4528
|
this.setModified(ChangeType.vscroll);
|
|
4206
|
-
|
|
4529
|
+
// this.log("ResizeObserver: Size changed", entries);
|
|
4207
4530
|
});
|
|
4208
4531
|
this.resizeObserver.observe(this.element);
|
|
4209
4532
|
onEvent(this.nodeListElement, "click", "div.wb-row", (e) => {
|
|
4210
4533
|
const info = Wunderbaum.getEventInfo(e);
|
|
4211
4534
|
const node = info.node;
|
|
4535
|
+
// this.log("click", info, e);
|
|
4536
|
+
// if( (e.target as HTMLElement).matches("input[type=checkbox]")){
|
|
4537
|
+
// // Click on an embedded checkbox triggers a change event.
|
|
4538
|
+
// // We return here, before the `setActive()` performs a render
|
|
4539
|
+
// this.log("click - cb", info, e);
|
|
4540
|
+
// // e.preventDefault()
|
|
4541
|
+
// setTimeout(()=>{
|
|
4542
|
+
// // (e.target as HTMLElement).click()
|
|
4543
|
+
// }, 50)
|
|
4544
|
+
// // return
|
|
4545
|
+
// }
|
|
4212
4546
|
if (this._callEvent("click", { event: e, node: node, info: info }) === false) {
|
|
4213
4547
|
this.lastClickTime = Date.now();
|
|
4214
4548
|
return false;
|
|
@@ -4236,16 +4570,15 @@ class Wunderbaum {
|
|
|
4236
4570
|
node.setSelected(!node.isSelected());
|
|
4237
4571
|
}
|
|
4238
4572
|
}
|
|
4239
|
-
// if(e.target.classList.)
|
|
4240
|
-
// this.log("click", info);
|
|
4241
4573
|
this.lastClickTime = Date.now();
|
|
4242
4574
|
});
|
|
4243
4575
|
onEvent(this.element, "keydown", (e) => {
|
|
4244
4576
|
const info = Wunderbaum.getEventInfo(e);
|
|
4245
4577
|
const eventName = eventToString(e);
|
|
4578
|
+
const node = info.node || this.getFocusNode();
|
|
4246
4579
|
this._callHook("onKeyEvent", {
|
|
4247
4580
|
event: e,
|
|
4248
|
-
node:
|
|
4581
|
+
node: node,
|
|
4249
4582
|
info: info,
|
|
4250
4583
|
eventName: eventName,
|
|
4251
4584
|
});
|
|
@@ -4421,14 +4754,14 @@ class Wunderbaum {
|
|
|
4421
4754
|
* tree._callEvent("edit.beforeEdit", {foo: 42})
|
|
4422
4755
|
* ```
|
|
4423
4756
|
*/
|
|
4424
|
-
_callEvent(
|
|
4425
|
-
const [p, n] =
|
|
4757
|
+
_callEvent(type, extra) {
|
|
4758
|
+
const [p, n] = type.split(".");
|
|
4426
4759
|
const opts = this.options;
|
|
4427
4760
|
const func = n ? opts[p][n] : opts[p];
|
|
4428
4761
|
if (func) {
|
|
4429
|
-
return func.call(this, extend({
|
|
4762
|
+
return func.call(this, extend({ type: type, tree: this, util: this._util }, extra));
|
|
4430
4763
|
// } else {
|
|
4431
|
-
// this.logError(`Triggering undefined event '${
|
|
4764
|
+
// this.logError(`Triggering undefined event '${type}'.`)
|
|
4432
4765
|
}
|
|
4433
4766
|
}
|
|
4434
4767
|
/** Return the node for given row index. */
|
|
@@ -4444,29 +4777,32 @@ class Wunderbaum {
|
|
|
4444
4777
|
return node;
|
|
4445
4778
|
}
|
|
4446
4779
|
/** Return the topmost visible node in the viewport. */
|
|
4447
|
-
|
|
4780
|
+
getTopmostVpNode(complete = true) {
|
|
4781
|
+
const gracePx = 1; // ignore subpixel scrolling
|
|
4782
|
+
const scrollParent = this.element;
|
|
4783
|
+
// const headerHeight = this.headerElement.clientHeight; // May be 0
|
|
4784
|
+
const scrollTop = scrollParent.scrollTop; // + headerHeight;
|
|
4448
4785
|
let topIdx;
|
|
4449
|
-
const gracePy = 1; // ignore subpixel scrolling
|
|
4450
4786
|
if (complete) {
|
|
4451
|
-
topIdx = Math.ceil((
|
|
4787
|
+
topIdx = Math.ceil((scrollTop - gracePx) / ROW_HEIGHT);
|
|
4452
4788
|
}
|
|
4453
4789
|
else {
|
|
4454
|
-
topIdx = Math.floor(
|
|
4790
|
+
topIdx = Math.floor(scrollTop / ROW_HEIGHT);
|
|
4455
4791
|
}
|
|
4456
4792
|
return this._getNodeByRowIdx(topIdx);
|
|
4457
4793
|
}
|
|
4458
4794
|
/** Return the lowest visible node in the viewport. */
|
|
4459
|
-
|
|
4795
|
+
getLowestVpNode(complete = true) {
|
|
4796
|
+
const scrollParent = this.element;
|
|
4797
|
+
const headerHeight = this.headerElement.clientHeight; // May be 0
|
|
4798
|
+
const scrollTop = scrollParent.scrollTop;
|
|
4799
|
+
const clientHeight = scrollParent.clientHeight - headerHeight;
|
|
4460
4800
|
let bottomIdx;
|
|
4461
4801
|
if (complete) {
|
|
4462
|
-
bottomIdx =
|
|
4463
|
-
Math.floor((this.scrollContainer.scrollTop + this.scrollContainer.clientHeight) /
|
|
4464
|
-
ROW_HEIGHT) - 1;
|
|
4802
|
+
bottomIdx = Math.floor((scrollTop + clientHeight) / ROW_HEIGHT) - 1;
|
|
4465
4803
|
}
|
|
4466
4804
|
else {
|
|
4467
|
-
bottomIdx =
|
|
4468
|
-
Math.ceil((this.scrollContainer.scrollTop + this.scrollContainer.clientHeight) /
|
|
4469
|
-
ROW_HEIGHT) - 1;
|
|
4805
|
+
bottomIdx = Math.ceil((scrollTop + clientHeight) / ROW_HEIGHT) - 1;
|
|
4470
4806
|
}
|
|
4471
4807
|
bottomIdx = Math.min(bottomIdx, this.count(true) - 1);
|
|
4472
4808
|
return this._getNodeByRowIdx(bottomIdx);
|
|
@@ -4663,7 +4999,7 @@ class Wunderbaum {
|
|
|
4663
4999
|
* Return `tree.option.NAME` (also resolving if this is a callback).
|
|
4664
5000
|
*
|
|
4665
5001
|
* See also {@link WunderbaumNode.getOption|WunderbaumNode.getOption()}
|
|
4666
|
-
* to
|
|
5002
|
+
* to evaluate `node.NAME` setting and `tree.types[node.type].NAME`.
|
|
4667
5003
|
*
|
|
4668
5004
|
* @param name option name (use dot notation to access extension option, e.g.
|
|
4669
5005
|
* `filter.mode`)
|
|
@@ -4682,33 +5018,48 @@ class Wunderbaum {
|
|
|
4682
5018
|
value = value({ type: "resolve", tree: this });
|
|
4683
5019
|
}
|
|
4684
5020
|
// Use value from value options dict, fallback do default
|
|
5021
|
+
// console.info(name, value, opts)
|
|
4685
5022
|
return value !== null && value !== void 0 ? value : defaultValue;
|
|
4686
5023
|
}
|
|
4687
5024
|
/**
|
|
4688
|
-
*
|
|
4689
|
-
*
|
|
4690
|
-
* @param value
|
|
5025
|
+
* Set tree option.
|
|
5026
|
+
* Use dot notation to set plugin option, e.g. "filter.mode".
|
|
4691
5027
|
*/
|
|
4692
5028
|
setOption(name, value) {
|
|
4693
|
-
|
|
4694
|
-
|
|
4695
|
-
|
|
4696
|
-
|
|
4697
|
-
|
|
4698
|
-
// default:
|
|
4699
|
-
// break;
|
|
4700
|
-
// }
|
|
5029
|
+
// this.log(`setOption(${name}, ${value})`);
|
|
5030
|
+
if (name.indexOf(".") >= 0) {
|
|
5031
|
+
const parts = name.split(".");
|
|
5032
|
+
const ext = this.extensions[parts[0]];
|
|
5033
|
+
ext.setPluginOption(parts[1], value);
|
|
4701
5034
|
return;
|
|
4702
5035
|
}
|
|
4703
|
-
|
|
4704
|
-
|
|
4705
|
-
|
|
5036
|
+
this.options[name] = value;
|
|
5037
|
+
switch (name) {
|
|
5038
|
+
case "checkbox":
|
|
5039
|
+
this.setModified(ChangeType.any, { removeMarkup: true });
|
|
5040
|
+
break;
|
|
5041
|
+
case "enabled":
|
|
5042
|
+
this.setEnabled(!!value);
|
|
5043
|
+
break;
|
|
5044
|
+
case "fixedCol":
|
|
5045
|
+
this.element.classList.toggle("wb-fixed-col", !!value);
|
|
5046
|
+
break;
|
|
5047
|
+
}
|
|
4706
5048
|
}
|
|
4707
|
-
/**Return true if the tree (or one of its nodes) has the input focus. */
|
|
5049
|
+
/** Return true if the tree (or one of its nodes) has the input focus. */
|
|
4708
5050
|
hasFocus() {
|
|
4709
5051
|
return this.element.contains(document.activeElement);
|
|
4710
5052
|
}
|
|
4711
|
-
/**
|
|
5053
|
+
/**
|
|
5054
|
+
* Return true if the tree displays a header. Grids have a header unless the
|
|
5055
|
+
* `header` option is set to `false`. Plain trees have a header if the `header`
|
|
5056
|
+
* option is a string or `true`.
|
|
5057
|
+
*/
|
|
5058
|
+
hasHeader() {
|
|
5059
|
+
const header = this.options.header;
|
|
5060
|
+
return this.isGrid() ? header !== false : !!header;
|
|
5061
|
+
}
|
|
5062
|
+
/** Run code, but defer rendering of viewport until done. */
|
|
4712
5063
|
runWithoutUpdate(func, hint = null) {
|
|
4713
5064
|
try {
|
|
4714
5065
|
this.enableUpdate(false);
|
|
@@ -4732,6 +5083,18 @@ class Wunderbaum {
|
|
|
4732
5083
|
this.logTimeEnd(tag);
|
|
4733
5084
|
}
|
|
4734
5085
|
}
|
|
5086
|
+
/** Recursively select all nodes. */
|
|
5087
|
+
selectAll(flag = true) {
|
|
5088
|
+
try {
|
|
5089
|
+
this.enableUpdate(false);
|
|
5090
|
+
this.visit((node) => {
|
|
5091
|
+
node.setSelected(flag);
|
|
5092
|
+
});
|
|
5093
|
+
}
|
|
5094
|
+
finally {
|
|
5095
|
+
this.enableUpdate(true);
|
|
5096
|
+
}
|
|
5097
|
+
}
|
|
4735
5098
|
/** Return the number of nodes in the data model.*/
|
|
4736
5099
|
count(visible = false) {
|
|
4737
5100
|
if (visible) {
|
|
@@ -4773,6 +5136,17 @@ class Wunderbaum {
|
|
|
4773
5136
|
findFirst(match) {
|
|
4774
5137
|
return this.root.findFirst(match);
|
|
4775
5138
|
}
|
|
5139
|
+
/**
|
|
5140
|
+
* Find first node that matches condition.
|
|
5141
|
+
*
|
|
5142
|
+
* @param match title string to search for, or a
|
|
5143
|
+
* callback function that returns `true` if a node is matched.
|
|
5144
|
+
* @see {@link WunderbaumNode.findFirst}
|
|
5145
|
+
*
|
|
5146
|
+
*/
|
|
5147
|
+
findKey(key) {
|
|
5148
|
+
return this.keyMap.get(key);
|
|
5149
|
+
}
|
|
4776
5150
|
/**
|
|
4777
5151
|
* Find the next visible node that starts with `match`, starting at `startNode`
|
|
4778
5152
|
* and wrap-around at the end.
|
|
@@ -4815,7 +5189,7 @@ class Wunderbaum {
|
|
|
4815
5189
|
*/
|
|
4816
5190
|
findRelatedNode(node, where, includeHidden = false) {
|
|
4817
5191
|
let res = null;
|
|
4818
|
-
const pageSize = Math.floor(this.
|
|
5192
|
+
const pageSize = Math.floor(this.scrollContainerElement.clientHeight / ROW_HEIGHT);
|
|
4819
5193
|
switch (where) {
|
|
4820
5194
|
case "parent":
|
|
4821
5195
|
if (node.parent && node.parent.parent) {
|
|
@@ -4871,7 +5245,7 @@ class Wunderbaum {
|
|
|
4871
5245
|
res = this._getNextNodeInView(node);
|
|
4872
5246
|
break;
|
|
4873
5247
|
case "pageDown":
|
|
4874
|
-
const bottomNode = this.
|
|
5248
|
+
const bottomNode = this.getLowestVpNode();
|
|
4875
5249
|
// this.logDebug(`${where}(${node}) -> ${bottomNode}`);
|
|
4876
5250
|
if (node._rowIdx < bottomNode._rowIdx) {
|
|
4877
5251
|
res = bottomNode;
|
|
@@ -4885,7 +5259,7 @@ class Wunderbaum {
|
|
|
4885
5259
|
res = node;
|
|
4886
5260
|
}
|
|
4887
5261
|
else {
|
|
4888
|
-
const topNode = this.
|
|
5262
|
+
const topNode = this.getTopmostVpNode();
|
|
4889
5263
|
// this.logDebug(`${where}(${node}) -> ${topNode}`);
|
|
4890
5264
|
if (node._rowIdx > topNode._rowIdx) {
|
|
4891
5265
|
res = topNode;
|
|
@@ -4965,6 +5339,10 @@ class Wunderbaum {
|
|
|
4965
5339
|
const idx = Array.prototype.indexOf.call(parentCol.parentNode.children, parentCol);
|
|
4966
5340
|
res.colIdx = idx;
|
|
4967
5341
|
}
|
|
5342
|
+
else if (cl.contains("wb-row")) {
|
|
5343
|
+
// Plain tree
|
|
5344
|
+
res.region = TargetType.title;
|
|
5345
|
+
}
|
|
4968
5346
|
else {
|
|
4969
5347
|
// Somewhere near the title
|
|
4970
5348
|
if (event.type !== "mousemove" && !(event instanceof KeyboardEvent)) {
|
|
@@ -4993,7 +5371,7 @@ class Wunderbaum {
|
|
|
4993
5371
|
* @internal
|
|
4994
5372
|
*/
|
|
4995
5373
|
toString() {
|
|
4996
|
-
return
|
|
5374
|
+
return `Wunderbaum<'${this.id}'>`;
|
|
4997
5375
|
}
|
|
4998
5376
|
/** Return true if any node is currently in edit-title mode. */
|
|
4999
5377
|
isEditing() {
|
|
@@ -5055,67 +5433,119 @@ class Wunderbaum {
|
|
|
5055
5433
|
}
|
|
5056
5434
|
}
|
|
5057
5435
|
/**
|
|
5058
|
-
* Make sure that this node is scrolled into the viewport.
|
|
5059
|
-
*
|
|
5060
|
-
* @param {boolean | PlainObject} [effects=false] animation options.
|
|
5061
|
-
* @param {object} [options=null] {topNode: null, effects: ..., parent: ...}
|
|
5062
|
-
* this node will remain visible in
|
|
5063
|
-
* any case, even if `this` is outside the scroll pane.
|
|
5436
|
+
* Make sure that this node is vertically scrolled into the viewport.
|
|
5064
5437
|
*/
|
|
5065
|
-
scrollTo(
|
|
5066
|
-
const
|
|
5067
|
-
|
|
5068
|
-
|
|
5069
|
-
|
|
5070
|
-
|
|
5071
|
-
|
|
5072
|
-
|
|
5073
|
-
|
|
5074
|
-
|
|
5438
|
+
scrollTo(nodeOrOpts) {
|
|
5439
|
+
const PADDING = 2; // leave some pixels between viewport bounds
|
|
5440
|
+
let node;
|
|
5441
|
+
let opts;
|
|
5442
|
+
if (nodeOrOpts instanceof WunderbaumNode) {
|
|
5443
|
+
node = nodeOrOpts;
|
|
5444
|
+
}
|
|
5445
|
+
else {
|
|
5446
|
+
opts = nodeOrOpts;
|
|
5447
|
+
node = opts.node;
|
|
5448
|
+
}
|
|
5449
|
+
assert(node && node._rowIdx != null);
|
|
5450
|
+
const scrollParent = this.element;
|
|
5451
|
+
const headerHeight = this.headerElement.clientHeight; // May be 0
|
|
5452
|
+
const scrollTop = scrollParent.scrollTop;
|
|
5453
|
+
const vpHeight = scrollParent.clientHeight;
|
|
5454
|
+
const rowTop = node._rowIdx * ROW_HEIGHT + headerHeight;
|
|
5455
|
+
const vpTop = headerHeight;
|
|
5456
|
+
const vpRowTop = rowTop - scrollTop;
|
|
5457
|
+
const vpRowBottom = vpRowTop + ROW_HEIGHT;
|
|
5458
|
+
// this.log( `scrollTo(${node.title}), vpTop:${vpTop}px, scrollTop:${scrollTop}, vpHeight:${vpHeight}, rowTop:${rowTop}, vpRowTop:${vpRowTop}`, nodeOrOpts );
|
|
5459
|
+
let newScrollTop = null;
|
|
5460
|
+
if (vpRowTop >= vpTop) {
|
|
5461
|
+
if (vpRowBottom <= vpHeight) ;
|
|
5075
5462
|
else {
|
|
5076
5463
|
// Node is below viewport
|
|
5077
|
-
|
|
5464
|
+
// this.log("Below viewport");
|
|
5465
|
+
newScrollTop = rowTop + ROW_HEIGHT - vpHeight + PADDING; // leave some pixels between vieeport bounds
|
|
5078
5466
|
}
|
|
5079
5467
|
}
|
|
5080
|
-
else
|
|
5468
|
+
else {
|
|
5081
5469
|
// Node is above viewport
|
|
5082
|
-
|
|
5470
|
+
// this.log("Above viewport");
|
|
5471
|
+
newScrollTop = rowTop - vpTop - PADDING; // leave some pixels between vieeport bounds
|
|
5083
5472
|
}
|
|
5084
|
-
if (
|
|
5085
|
-
this.log(
|
|
5086
|
-
|
|
5087
|
-
this.setModified(ChangeType.vscroll);
|
|
5473
|
+
if (newScrollTop != null) {
|
|
5474
|
+
this.log(`scrollTo(${rowTop}): ${scrollTop} => ${newScrollTop}`);
|
|
5475
|
+
scrollParent.scrollTop = newScrollTop;
|
|
5476
|
+
// this.setModified(ChangeType.vscroll);
|
|
5088
5477
|
}
|
|
5089
5478
|
}
|
|
5479
|
+
/**
|
|
5480
|
+
* Make sure that this node is horizontally scrolled into the viewport.
|
|
5481
|
+
* Called by {@link setColumn}.
|
|
5482
|
+
*/
|
|
5483
|
+
scrollToHorz() {
|
|
5484
|
+
// const PADDING = 1;
|
|
5485
|
+
const fixedWidth = this.columns[0]._widthPx;
|
|
5486
|
+
const vpWidth = this.element.clientWidth;
|
|
5487
|
+
const scrollLeft = this.element.scrollLeft;
|
|
5488
|
+
// if (scrollLeft <= 0) {
|
|
5489
|
+
// return; // Not scrolled horizontally: Nothing to do
|
|
5490
|
+
// }
|
|
5491
|
+
const colElem = this.getActiveColElem();
|
|
5492
|
+
const colLeft = Number.parseInt(colElem === null || colElem === void 0 ? void 0 : colElem.style.left, 10);
|
|
5493
|
+
const colRight = colLeft + Number.parseInt(colElem === null || colElem === void 0 ? void 0 : colElem.style.width, 10);
|
|
5494
|
+
let newLeft = scrollLeft;
|
|
5495
|
+
if (colLeft - scrollLeft < fixedWidth) {
|
|
5496
|
+
// The current column is scrolled behind the left fixed column
|
|
5497
|
+
newLeft = colLeft - fixedWidth;
|
|
5498
|
+
}
|
|
5499
|
+
else if (colRight - scrollLeft > vpWidth) {
|
|
5500
|
+
// The current column is scrolled outside the right side
|
|
5501
|
+
newLeft = colRight - vpWidth;
|
|
5502
|
+
}
|
|
5503
|
+
// util.assert(node._rowIdx != null);
|
|
5504
|
+
// const curLeft = this.scrollContainer.scrollLeft;
|
|
5505
|
+
this.log(`scrollToHorz(${this.activeColIdx}): ${colLeft}..${colRight}, fixedOfs=${fixedWidth}, vpWidth=${vpWidth}, curLeft=${scrollLeft} -> ${newLeft}`);
|
|
5506
|
+
// const nodeOfs = node._rowIdx * ROW_HEIGHT;
|
|
5507
|
+
// let newLeft;
|
|
5508
|
+
this.element.scrollLeft = newLeft;
|
|
5509
|
+
// this.setModified(ChangeType.vscroll);
|
|
5510
|
+
// }
|
|
5511
|
+
}
|
|
5090
5512
|
/**
|
|
5091
5513
|
* Set column #colIdx to 'active'.
|
|
5092
5514
|
*
|
|
5093
5515
|
* This higlights the column header and -cells by adding the `wb-active` class.
|
|
5094
|
-
* Available in cell-nav
|
|
5516
|
+
* Available in cell-nav mode only.
|
|
5095
5517
|
*/
|
|
5096
5518
|
setColumn(colIdx) {
|
|
5097
|
-
|
|
5519
|
+
var _a;
|
|
5520
|
+
assert(this.isCellNav());
|
|
5098
5521
|
assert(0 <= colIdx && colIdx < this.columns.length);
|
|
5099
5522
|
this.activeColIdx = colIdx;
|
|
5100
|
-
// node.setActive(true, { column: tree.activeColIdx + 1 });
|
|
5101
|
-
this.setModified(ChangeType.row, this.activeNode);
|
|
5102
5523
|
// Update `wb-active` class for all headers
|
|
5103
|
-
if (this.
|
|
5524
|
+
if (this.hasHeader()) {
|
|
5104
5525
|
for (let rowDiv of this.headerElement.children) {
|
|
5105
|
-
// for (let rowDiv of document.querySelector("div.wb-header").children) {
|
|
5106
5526
|
let i = 0;
|
|
5107
5527
|
for (let colDiv of rowDiv.children) {
|
|
5108
5528
|
colDiv.classList.toggle("wb-active", i++ === colIdx);
|
|
5109
5529
|
}
|
|
5110
5530
|
}
|
|
5111
5531
|
}
|
|
5112
|
-
|
|
5532
|
+
(_a = this.activeNode) === null || _a === void 0 ? void 0 : _a.setModified(ChangeType.status);
|
|
5533
|
+
// Update `wb-active` class for all cell spans
|
|
5113
5534
|
for (let rowDiv of this.nodeListElement.children) {
|
|
5114
5535
|
let i = 0;
|
|
5115
5536
|
for (let colDiv of rowDiv.children) {
|
|
5116
5537
|
colDiv.classList.toggle("wb-active", i++ === colIdx);
|
|
5117
5538
|
}
|
|
5118
5539
|
}
|
|
5540
|
+
// Vertical scroll into view
|
|
5541
|
+
// if (this.options.fixedCol) {
|
|
5542
|
+
this.scrollToHorz();
|
|
5543
|
+
// }
|
|
5544
|
+
}
|
|
5545
|
+
/** Set or remove keybaord focus to the tree container. */
|
|
5546
|
+
setActiveNode(key, flag = true, options) {
|
|
5547
|
+
var _a;
|
|
5548
|
+
(_a = this.findKey(key)) === null || _a === void 0 ? void 0 : _a.setActive(flag, options);
|
|
5119
5549
|
}
|
|
5120
5550
|
/** Set or remove keybaord focus to the tree container. */
|
|
5121
5551
|
setFocus(flag = true) {
|
|
@@ -5139,59 +5569,147 @@ class Wunderbaum {
|
|
|
5139
5569
|
options = node;
|
|
5140
5570
|
}
|
|
5141
5571
|
const immediate = !!getOption(options, "immediate");
|
|
5572
|
+
const removeMarkup = !!getOption(options, "removeMarkup");
|
|
5573
|
+
if (removeMarkup) {
|
|
5574
|
+
this.visit((n) => {
|
|
5575
|
+
n.removeMarkup();
|
|
5576
|
+
});
|
|
5577
|
+
}
|
|
5578
|
+
let callUpdate = false;
|
|
5142
5579
|
switch (change) {
|
|
5143
5580
|
case ChangeType.any:
|
|
5144
5581
|
case ChangeType.structure:
|
|
5145
5582
|
case ChangeType.header:
|
|
5146
5583
|
this.changeRedrawRequestPending = true;
|
|
5147
|
-
|
|
5584
|
+
callUpdate = true;
|
|
5148
5585
|
break;
|
|
5149
5586
|
case ChangeType.vscroll:
|
|
5150
|
-
this.
|
|
5587
|
+
this.changeScrollRequestPending = true;
|
|
5588
|
+
callUpdate = true;
|
|
5151
5589
|
break;
|
|
5152
5590
|
case ChangeType.row:
|
|
5591
|
+
case ChangeType.data:
|
|
5153
5592
|
case ChangeType.status:
|
|
5154
|
-
// Single nodes are
|
|
5593
|
+
// Single nodes are immediately updated if already inside the viewport
|
|
5155
5594
|
// (otherwise we can ignore)
|
|
5156
5595
|
if (node._rowElem) {
|
|
5157
|
-
node.render();
|
|
5596
|
+
node.render({ change: change });
|
|
5158
5597
|
}
|
|
5159
5598
|
break;
|
|
5160
5599
|
default:
|
|
5161
5600
|
error(`Invalid change type ${change}`);
|
|
5162
5601
|
}
|
|
5602
|
+
if (callUpdate) {
|
|
5603
|
+
if (immediate) {
|
|
5604
|
+
this._updateViewportImmediately();
|
|
5605
|
+
}
|
|
5606
|
+
else {
|
|
5607
|
+
this._updateViewportThrottled();
|
|
5608
|
+
}
|
|
5609
|
+
}
|
|
5610
|
+
}
|
|
5611
|
+
/** Disable mouse and keyboard interaction (return prev. state). */
|
|
5612
|
+
setEnabled(flag = true) {
|
|
5613
|
+
const prev = this.enabled;
|
|
5614
|
+
this.enabled = !!flag;
|
|
5615
|
+
this.element.classList.toggle("wb-disabled", !flag);
|
|
5616
|
+
return prev;
|
|
5617
|
+
}
|
|
5618
|
+
/** Return false if tree is disabled. */
|
|
5619
|
+
isEnabled() {
|
|
5620
|
+
return this.enabled;
|
|
5621
|
+
}
|
|
5622
|
+
/** Return true if tree has more than one column, i.e. has additional data columns. */
|
|
5623
|
+
isGrid() {
|
|
5624
|
+
return this.columns && this.columns.length > 1;
|
|
5625
|
+
}
|
|
5626
|
+
/** Return true if cell-navigation mode is acive. */
|
|
5627
|
+
isCellNav() {
|
|
5628
|
+
return !!this._cellNavMode;
|
|
5629
|
+
}
|
|
5630
|
+
/** Return true if row-navigation mode is acive. */
|
|
5631
|
+
isRowNav() {
|
|
5632
|
+
return !this._cellNavMode;
|
|
5163
5633
|
}
|
|
5164
5634
|
/** Set the tree's navigation mode. */
|
|
5165
|
-
|
|
5166
|
-
|
|
5167
|
-
|
|
5168
|
-
if (
|
|
5635
|
+
setCellNav(flag = true) {
|
|
5636
|
+
var _a;
|
|
5637
|
+
const prev = this._cellNavMode;
|
|
5638
|
+
// if (flag === prev) {
|
|
5639
|
+
// return;
|
|
5640
|
+
// }
|
|
5641
|
+
this._cellNavMode = !!flag;
|
|
5642
|
+
if (flag && !prev) {
|
|
5643
|
+
// switch from row to cell mode
|
|
5644
|
+
this.setColumn(0);
|
|
5645
|
+
}
|
|
5646
|
+
this.element.classList.toggle("wb-cell-mode", flag);
|
|
5647
|
+
(_a = this.activeNode) === null || _a === void 0 ? void 0 : _a.setModified(ChangeType.status);
|
|
5648
|
+
}
|
|
5649
|
+
/** Set the tree's navigation mode option. */
|
|
5650
|
+
setNavigationOption(mode, reset = false) {
|
|
5651
|
+
if (!this.isGrid() && mode !== NavigationOptions.row) {
|
|
5652
|
+
this.logWarn("Plain trees only support row navigation mode.");
|
|
5169
5653
|
return;
|
|
5170
5654
|
}
|
|
5171
|
-
|
|
5172
|
-
|
|
5173
|
-
|
|
5174
|
-
|
|
5175
|
-
|
|
5655
|
+
this.options.navigationModeOption = mode;
|
|
5656
|
+
switch (mode) {
|
|
5657
|
+
case NavigationOptions.cell:
|
|
5658
|
+
this.setCellNav(true);
|
|
5659
|
+
break;
|
|
5660
|
+
case NavigationOptions.row:
|
|
5661
|
+
this.setCellNav(false);
|
|
5662
|
+
break;
|
|
5663
|
+
case NavigationOptions.startCell:
|
|
5664
|
+
if (reset) {
|
|
5665
|
+
this.setCellNav(true);
|
|
5666
|
+
}
|
|
5667
|
+
break;
|
|
5668
|
+
case NavigationOptions.startRow:
|
|
5669
|
+
if (reset) {
|
|
5670
|
+
this.setCellNav(false);
|
|
5671
|
+
}
|
|
5672
|
+
break;
|
|
5673
|
+
default:
|
|
5674
|
+
error(`Invalid mode '${mode}'`);
|
|
5176
5675
|
}
|
|
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);
|
|
5180
5676
|
}
|
|
5181
5677
|
/** Display tree status (ok, loading, error, noData) using styles and a dummy root node. */
|
|
5182
|
-
setStatus(status,
|
|
5183
|
-
return this.root.setStatus(status,
|
|
5678
|
+
setStatus(status, options) {
|
|
5679
|
+
return this.root.setStatus(status, options);
|
|
5680
|
+
}
|
|
5681
|
+
/** Add or redefine node type definitions. */
|
|
5682
|
+
setTypes(types, replace = true) {
|
|
5683
|
+
assert(isPlainObject(types));
|
|
5684
|
+
if (replace) {
|
|
5685
|
+
this.types = types;
|
|
5686
|
+
}
|
|
5687
|
+
else {
|
|
5688
|
+
extend(this.types, types);
|
|
5689
|
+
}
|
|
5690
|
+
// Convert `TYPE.classes` to a Set
|
|
5691
|
+
for (let t of Object.values(this.types)) {
|
|
5692
|
+
if (t.classes) {
|
|
5693
|
+
t.classes = toSet(t.classes);
|
|
5694
|
+
}
|
|
5695
|
+
}
|
|
5184
5696
|
}
|
|
5185
5697
|
/** Update column headers and width. */
|
|
5186
5698
|
updateColumns(opts) {
|
|
5187
5699
|
opts = Object.assign({ calculateCols: true, updateRows: true }, opts);
|
|
5188
|
-
const
|
|
5700
|
+
const defaultMinWidth = 4;
|
|
5189
5701
|
const vpWidth = this.element.clientWidth;
|
|
5702
|
+
const isGrid = this.isGrid();
|
|
5703
|
+
let totalWidth = 0;
|
|
5190
5704
|
let totalWeight = 0;
|
|
5191
5705
|
let fixedWidth = 0;
|
|
5192
5706
|
let modified = false;
|
|
5707
|
+
this.element.classList.toggle("wb-grid", isGrid);
|
|
5708
|
+
if (!isGrid && this.isCellNav()) {
|
|
5709
|
+
this.setCellNav(false);
|
|
5710
|
+
}
|
|
5193
5711
|
if (opts.calculateCols) {
|
|
5194
|
-
// Gather width
|
|
5712
|
+
// Gather width definitions
|
|
5195
5713
|
this._columnsById = {};
|
|
5196
5714
|
for (let col of this.columns) {
|
|
5197
5715
|
this._columnsById[col.id] = col;
|
|
@@ -5214,14 +5732,25 @@ class Wunderbaum {
|
|
|
5214
5732
|
fixedWidth += px;
|
|
5215
5733
|
}
|
|
5216
5734
|
else {
|
|
5217
|
-
error(
|
|
5735
|
+
error(`Invalid column width: ${cw}`);
|
|
5218
5736
|
}
|
|
5219
5737
|
}
|
|
5220
5738
|
// Share remaining space between non-fixed columns
|
|
5221
5739
|
const restPx = Math.max(0, vpWidth - fixedWidth);
|
|
5222
5740
|
let ofsPx = 0;
|
|
5223
5741
|
for (let col of this.columns) {
|
|
5742
|
+
let minWidth;
|
|
5224
5743
|
if (col._weight) {
|
|
5744
|
+
const cmw = col.minWidth;
|
|
5745
|
+
if (typeof cmw === "number") {
|
|
5746
|
+
minWidth = cmw;
|
|
5747
|
+
}
|
|
5748
|
+
else if (typeof cmw === "string" && cmw.endsWith("px")) {
|
|
5749
|
+
minWidth = parseFloat(cmw.slice(0, -2));
|
|
5750
|
+
}
|
|
5751
|
+
else {
|
|
5752
|
+
minWidth = defaultMinWidth;
|
|
5753
|
+
}
|
|
5225
5754
|
const px = Math.max(minWidth, (restPx * col._weight) / totalWeight);
|
|
5226
5755
|
if (col._widthPx != px) {
|
|
5227
5756
|
modified = true;
|
|
@@ -5231,7 +5760,14 @@ class Wunderbaum {
|
|
|
5231
5760
|
col._ofsPx = ofsPx;
|
|
5232
5761
|
ofsPx += col._widthPx;
|
|
5233
5762
|
}
|
|
5763
|
+
totalWidth = ofsPx;
|
|
5234
5764
|
}
|
|
5765
|
+
// if (this.options.fixedCol) {
|
|
5766
|
+
// 'position: fixed' requires that the content has the correct size
|
|
5767
|
+
const tw = `${totalWidth}px`;
|
|
5768
|
+
this.headerElement.style.width = tw;
|
|
5769
|
+
this.scrollContainerElement.style.width = tw;
|
|
5770
|
+
// }
|
|
5235
5771
|
// Every column has now a calculated `_ofsPx` and `_widthPx`
|
|
5236
5772
|
// this.logInfo("UC", this.columns, vpWidth, this.element.clientWidth, this.element);
|
|
5237
5773
|
// console.trace();
|
|
@@ -5247,91 +5783,124 @@ class Wunderbaum {
|
|
|
5247
5783
|
* @internal
|
|
5248
5784
|
*/
|
|
5249
5785
|
_renderHeaderMarkup() {
|
|
5250
|
-
|
|
5786
|
+
assert(this.headerElement);
|
|
5787
|
+
const wantHeader = this.hasHeader();
|
|
5788
|
+
setElemDisplay(this.headerElement, wantHeader);
|
|
5789
|
+
if (!wantHeader) {
|
|
5251
5790
|
return;
|
|
5252
5791
|
}
|
|
5792
|
+
const colCount = this.columns.length;
|
|
5253
5793
|
const headerRow = this.headerElement.querySelector(".wb-row");
|
|
5254
5794
|
assert(headerRow);
|
|
5255
|
-
headerRow.innerHTML = "<span class='wb-col'></span>".repeat(
|
|
5256
|
-
for (let i = 0; i <
|
|
5795
|
+
headerRow.innerHTML = "<span class='wb-col'></span>".repeat(colCount);
|
|
5796
|
+
for (let i = 0; i < colCount; i++) {
|
|
5257
5797
|
const col = this.columns[i];
|
|
5258
5798
|
const colElem = headerRow.children[i];
|
|
5259
5799
|
colElem.style.left = col._ofsPx + "px";
|
|
5260
5800
|
colElem.style.width = col._widthPx + "px";
|
|
5261
|
-
// colElem.textContent = col.title || col.id;
|
|
5262
5801
|
const title = escapeHtml(col.title || col.id);
|
|
5263
|
-
|
|
5264
|
-
|
|
5802
|
+
let tooltip = "";
|
|
5803
|
+
if (col.tooltip) {
|
|
5804
|
+
tooltip = escapeTooltip(col.tooltip);
|
|
5805
|
+
tooltip = ` title="${tooltip}"`;
|
|
5806
|
+
}
|
|
5807
|
+
let resizer = "";
|
|
5808
|
+
if (i < colCount - 1) {
|
|
5809
|
+
resizer = '<span class="wb-col-resizer"></span>';
|
|
5810
|
+
}
|
|
5811
|
+
colElem.innerHTML = `<span class="wb-col-title"${tooltip}>${title}</span>${resizer}`;
|
|
5265
5812
|
}
|
|
5266
5813
|
}
|
|
5267
|
-
/**
|
|
5268
|
-
|
|
5269
|
-
|
|
5270
|
-
|
|
5271
|
-
|
|
5272
|
-
|
|
5273
|
-
|
|
5814
|
+
/**
|
|
5815
|
+
* Render pending changes that were scheduled using {@link WunderbaumNode.setModified} if any.
|
|
5816
|
+
*
|
|
5817
|
+
* This is hardly ever neccessary, since we normally either
|
|
5818
|
+
* - call `setModified(ChangeType.TYPE)` (async, throttled), or
|
|
5819
|
+
* - call `setModified(ChangeType.TYPE, {immediate: true})` (synchronous)
|
|
5820
|
+
*
|
|
5821
|
+
* `updatePendingModifications()` will only force immediate execution of
|
|
5822
|
+
* pending async changes if any.
|
|
5823
|
+
*/
|
|
5824
|
+
updatePendingModifications() {
|
|
5825
|
+
if (this.changeRedrawRequestPending || this.changeScrollRequestPending) {
|
|
5826
|
+
this._updateViewportImmediately();
|
|
5274
5827
|
}
|
|
5275
5828
|
}
|
|
5276
5829
|
/**
|
|
5277
5830
|
* 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
5831
|
* It calls `updateColumns()` and `_updateRows()`.
|
|
5832
|
+
*
|
|
5833
|
+
* This protected method should not be called directly but via
|
|
5834
|
+
* {@link WunderbaumNode.setModified}`, {@link Wunderbaum.setModified},
|
|
5835
|
+
* or {@link Wunderbaum.updatePendingModifications}.
|
|
5281
5836
|
* @internal
|
|
5282
5837
|
*/
|
|
5283
|
-
|
|
5838
|
+
_updateViewportImmediately() {
|
|
5839
|
+
var _a;
|
|
5284
5840
|
if (this._disableUpdateCount) {
|
|
5285
|
-
this.log(`IGNORED
|
|
5841
|
+
this.log(`IGNORED _updateViewportImmediately() disable level: ${this._disableUpdateCount}`);
|
|
5286
5842
|
return;
|
|
5287
5843
|
}
|
|
5288
5844
|
const newNodesOnly = !this.changeRedrawRequestPending;
|
|
5289
5845
|
this.changeRedrawRequestPending = false;
|
|
5290
|
-
|
|
5846
|
+
this.changeScrollRequestPending = false;
|
|
5847
|
+
let height = this.scrollContainerElement.clientHeight;
|
|
5291
5848
|
// We cannot get the height for absolute positioned parent, so look at first col
|
|
5292
5849
|
// let headerHeight = this.headerElement.clientHeight
|
|
5293
5850
|
// let headerHeight = this.headerElement.children[0].children[0].clientHeight;
|
|
5294
|
-
const headerHeight = this.options.headerHeightPx;
|
|
5851
|
+
// const headerHeight = this.options.headerHeightPx;
|
|
5852
|
+
const headerHeight = this.headerElement.clientHeight; // May be 0
|
|
5295
5853
|
const wantHeight = this.element.clientHeight - headerHeight;
|
|
5296
5854
|
if (Math.abs(height - wantHeight) > 1.0) {
|
|
5297
5855
|
// this.log("resize", height, wantHeight);
|
|
5298
|
-
this.
|
|
5856
|
+
this.scrollContainerElement.style.height = wantHeight + "px";
|
|
5299
5857
|
height = wantHeight;
|
|
5300
5858
|
}
|
|
5859
|
+
// console.profile(`_updateViewportImmediately()`)
|
|
5301
5860
|
this.updateColumns({ updateRows: false });
|
|
5302
5861
|
this._updateRows({ newNodesOnly: newNodesOnly });
|
|
5862
|
+
// console.profileEnd(`_updateViewportImmediately()`)
|
|
5863
|
+
if (this.options.connectTopBreadcrumb) {
|
|
5864
|
+
let path = (_a = this.getTopmostVpNode(true)) === null || _a === void 0 ? void 0 : _a.getPath(false, "title", " > ");
|
|
5865
|
+
path = path ? path + " >" : "";
|
|
5866
|
+
this.options.connectTopBreadcrumb.textContent = path;
|
|
5867
|
+
}
|
|
5303
5868
|
this._callEvent("update");
|
|
5304
5869
|
}
|
|
5305
|
-
/**
|
|
5306
|
-
|
|
5307
|
-
|
|
5308
|
-
|
|
5309
|
-
_validateRows() {
|
|
5310
|
-
|
|
5311
|
-
|
|
5312
|
-
|
|
5313
|
-
|
|
5314
|
-
|
|
5315
|
-
|
|
5316
|
-
|
|
5317
|
-
|
|
5318
|
-
|
|
5319
|
-
|
|
5320
|
-
|
|
5321
|
-
|
|
5322
|
-
|
|
5323
|
-
|
|
5324
|
-
|
|
5325
|
-
|
|
5326
|
-
|
|
5327
|
-
|
|
5328
|
-
|
|
5329
|
-
|
|
5330
|
-
|
|
5331
|
-
|
|
5332
|
-
|
|
5870
|
+
// /**
|
|
5871
|
+
// * Assert that TR order matches the natural node order
|
|
5872
|
+
// * @internal
|
|
5873
|
+
// */
|
|
5874
|
+
// protected _validateRows(): boolean {
|
|
5875
|
+
// let trs = this.nodeListElement.childNodes;
|
|
5876
|
+
// let i = 0;
|
|
5877
|
+
// let prev = -1;
|
|
5878
|
+
// let ok = true;
|
|
5879
|
+
// trs.forEach((element) => {
|
|
5880
|
+
// const tr = element as HTMLTableRowElement;
|
|
5881
|
+
// const top = Number.parseInt(tr.style.top);
|
|
5882
|
+
// const n = (<any>tr)._wb_node;
|
|
5883
|
+
// // if (i < 4) {
|
|
5884
|
+
// // console.info(
|
|
5885
|
+
// // `TR#${i}, rowIdx=${n._rowIdx} , top=${top}px: '${n.title}'`
|
|
5886
|
+
// // );
|
|
5887
|
+
// // }
|
|
5888
|
+
// if (prev >= 0 && top !== prev + ROW_HEIGHT) {
|
|
5889
|
+
// n.logWarn(
|
|
5890
|
+
// `TR order mismatch at index ${i}: top=${top}px != ${
|
|
5891
|
+
// prev + ROW_HEIGHT
|
|
5892
|
+
// }`
|
|
5893
|
+
// );
|
|
5894
|
+
// // throw new Error("fault");
|
|
5895
|
+
// ok = false;
|
|
5896
|
+
// }
|
|
5897
|
+
// prev = top;
|
|
5898
|
+
// i++;
|
|
5899
|
+
// });
|
|
5900
|
+
// return ok;
|
|
5901
|
+
// }
|
|
5333
5902
|
/*
|
|
5334
|
-
* - Traverse all *visible* of the whole tree, i.e. skip collapsed nodes.
|
|
5903
|
+
* - Traverse all *visible* nodes of the whole tree, i.e. skip collapsed nodes.
|
|
5335
5904
|
* - Store count of rows to `tree.treeRowCount`.
|
|
5336
5905
|
* - Renumber `node._rowIdx` for all visible nodes.
|
|
5337
5906
|
* - Calculate the index range that must be rendered to fill the viewport
|
|
@@ -5339,13 +5908,14 @@ class Wunderbaum {
|
|
|
5339
5908
|
* -
|
|
5340
5909
|
*/
|
|
5341
5910
|
_updateRows(opts) {
|
|
5342
|
-
const label = this.logTime("_updateRows");
|
|
5911
|
+
// const label = this.logTime("_updateRows");
|
|
5912
|
+
// this.log("_updateRows", opts)
|
|
5343
5913
|
opts = Object.assign({ newNodesOnly: false }, opts);
|
|
5344
5914
|
const newNodesOnly = !!opts.newNodesOnly;
|
|
5345
5915
|
const row_height = ROW_HEIGHT;
|
|
5346
|
-
const vp_height = this.
|
|
5916
|
+
const vp_height = this.element.clientHeight;
|
|
5347
5917
|
const prefetch = RENDER_MAX_PREFETCH;
|
|
5348
|
-
const ofs = this.
|
|
5918
|
+
const ofs = this.element.scrollTop;
|
|
5349
5919
|
let startIdx = Math.max(0, ofs / row_height - prefetch);
|
|
5350
5920
|
startIdx = Math.floor(startIdx);
|
|
5351
5921
|
// Make sure start is always even, so the alternating row colors don't
|
|
@@ -5369,7 +5939,7 @@ class Wunderbaum {
|
|
|
5369
5939
|
let modified = false;
|
|
5370
5940
|
let prevElem = "first";
|
|
5371
5941
|
this.visitRows(function (node) {
|
|
5372
|
-
//
|
|
5942
|
+
// node.log("visit")
|
|
5373
5943
|
const rowDiv = node._rowElem;
|
|
5374
5944
|
// Renumber all expanded nodes
|
|
5375
5945
|
if (node._rowIdx !== idx) {
|
|
@@ -5391,8 +5961,11 @@ class Wunderbaum {
|
|
|
5391
5961
|
else {
|
|
5392
5962
|
obsoleteNodes.delete(node);
|
|
5393
5963
|
// Create new markup
|
|
5964
|
+
if (rowDiv) {
|
|
5965
|
+
rowDiv.style.top = idx * ROW_HEIGHT + "px";
|
|
5966
|
+
}
|
|
5394
5967
|
node.render({ top: top, after: prevElem });
|
|
5395
|
-
//
|
|
5968
|
+
// node.log("render", top, prevElem, "=>", node._rowElem);
|
|
5396
5969
|
prevElem = node._rowElem;
|
|
5397
5970
|
}
|
|
5398
5971
|
idx++;
|
|
@@ -5409,8 +5982,8 @@ class Wunderbaum {
|
|
|
5409
5982
|
// `render(scrollOfs:${ofs}, ${startIdx}..${endIdx})`,
|
|
5410
5983
|
// this.nodeListElement.style.height
|
|
5411
5984
|
// );
|
|
5412
|
-
this.logTimeEnd(label);
|
|
5413
|
-
this._validateRows();
|
|
5985
|
+
// this.logTimeEnd(label);
|
|
5986
|
+
// this._validateRows();
|
|
5414
5987
|
return modified;
|
|
5415
5988
|
}
|
|
5416
5989
|
/**
|
|
@@ -5557,17 +6130,11 @@ class Wunderbaum {
|
|
|
5557
6130
|
/**
|
|
5558
6131
|
* Reload the tree with a new source.
|
|
5559
6132
|
*
|
|
5560
|
-
* Previous data is cleared.
|
|
5561
|
-
*
|
|
6133
|
+
* Previous data is cleared. Note that also column- and type defintions may
|
|
6134
|
+
* be passed with the `source` object.
|
|
5562
6135
|
*/
|
|
5563
|
-
load(source
|
|
6136
|
+
load(source) {
|
|
5564
6137
|
this.clear();
|
|
5565
|
-
const columns = options.columns || source.columns;
|
|
5566
|
-
if (columns) {
|
|
5567
|
-
this.columns = options.columns;
|
|
5568
|
-
// this._renderHeaderMarkup();
|
|
5569
|
-
this.updateColumns({ calculateCols: false });
|
|
5570
|
-
}
|
|
5571
6138
|
return this.root.load(source);
|
|
5572
6139
|
}
|
|
5573
6140
|
/**
|
|
@@ -5578,7 +6145,7 @@ class Wunderbaum {
|
|
|
5578
6145
|
* tree.enableUpdate(false);
|
|
5579
6146
|
* // ... (long running operation that would trigger many updates)
|
|
5580
6147
|
* foo();
|
|
5581
|
-
* // ... NOTE: make sure that async operations have finished
|
|
6148
|
+
* // ... NOTE: make sure that async operations have finished, e.g.
|
|
5582
6149
|
* await foo();
|
|
5583
6150
|
* } finally {
|
|
5584
6151
|
* tree.enableUpdate(true);
|
|
@@ -5599,7 +6166,9 @@ class Wunderbaum {
|
|
|
5599
6166
|
// `enableUpdate(${flag}): count -> ${this._disableUpdateCount}...`
|
|
5600
6167
|
// );
|
|
5601
6168
|
if (this._disableUpdateCount === 0) {
|
|
5602
|
-
this.
|
|
6169
|
+
// this.changeRedrawRequestPending = true; // make sure, we re-render all markup
|
|
6170
|
+
// this.updateViewport();
|
|
6171
|
+
this.setModified(ChangeType.any, { immediate: true });
|
|
5603
6172
|
}
|
|
5604
6173
|
}
|
|
5605
6174
|
else {
|
|
@@ -5641,7 +6210,7 @@ class Wunderbaum {
|
|
|
5641
6210
|
}
|
|
5642
6211
|
Wunderbaum.sequence = 0;
|
|
5643
6212
|
/** Wunderbaum release version number "MAJOR.MINOR.PATCH". */
|
|
5644
|
-
Wunderbaum.version = "v0.0.
|
|
6213
|
+
Wunderbaum.version = "v0.0.6"; // Set to semver by 'grunt release'
|
|
5645
6214
|
/** Expose some useful methods of the util.ts module as `Wunderbaum.util`. */
|
|
5646
6215
|
Wunderbaum.util = util;
|
|
5647
6216
|
|