wunderbaum 0.3.5 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/wunderbaum.css +382 -291
- package/dist/wunderbaum.css.map +1 -0
- package/dist/wunderbaum.d.ts +232 -107
- package/dist/wunderbaum.esm.js +635 -198
- package/dist/wunderbaum.esm.min.js +24 -24
- package/dist/wunderbaum.esm.min.js.map +1 -1
- package/dist/wunderbaum.umd.js +635 -198
- package/dist/wunderbaum.umd.min.js +31 -30
- package/dist/wunderbaum.umd.min.js.map +1 -1
- package/package.json +10 -9
- package/src/common.ts +44 -24
- package/src/types.ts +110 -28
- package/src/wb_ext_dnd.ts +63 -22
- package/src/wb_ext_edit.ts +2 -2
- package/src/wb_ext_filter.ts +1 -2
- package/src/wb_ext_keynav.ts +4 -4
- package/src/wb_node.ts +376 -96
- package/src/wb_options.ts +35 -8
- package/src/wunderbaum.scss +32 -15
- package/src/wunderbaum.ts +150 -53
package/dist/wunderbaum.umd.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
/*!
|
|
8
8
|
* Wunderbaum - util
|
|
9
9
|
* Copyright (c) 2021-2023, Martin Wendt. Released under the MIT license.
|
|
10
|
-
* v0.
|
|
10
|
+
* v0.5.0, Fri, 15 Sep 2023 14:34:23 GMT (https://github.com/mar10/wunderbaum)
|
|
11
11
|
*/
|
|
12
12
|
/** @module util */
|
|
13
13
|
/** Readable names for `MouseEvent.button` */
|
|
@@ -762,10 +762,10 @@
|
|
|
762
762
|
/*!
|
|
763
763
|
* Wunderbaum - types
|
|
764
764
|
* Copyright (c) 2021-2023, Martin Wendt. Released under the MIT license.
|
|
765
|
-
* v0.
|
|
765
|
+
* v0.5.0, Fri, 15 Sep 2023 14:34:23 GMT (https://github.com/mar10/wunderbaum)
|
|
766
766
|
*/
|
|
767
767
|
/**
|
|
768
|
-
* Possible values for {@link WunderbaumNode.
|
|
768
|
+
* Possible values for {@link WunderbaumNode.update()} and {@link Wunderbaum.update()}.
|
|
769
769
|
*/
|
|
770
770
|
var ChangeType;
|
|
771
771
|
(function (ChangeType) {
|
|
@@ -801,7 +801,7 @@
|
|
|
801
801
|
NodeStatusType["loading"] = "loading";
|
|
802
802
|
NodeStatusType["error"] = "error";
|
|
803
803
|
NodeStatusType["noData"] = "noData";
|
|
804
|
-
|
|
804
|
+
NodeStatusType["paging"] = "paging";
|
|
805
805
|
})(NodeStatusType || (NodeStatusType = {}));
|
|
806
806
|
/** Define the subregion of a node, where an event occurred. */
|
|
807
807
|
var NodeRegion;
|
|
@@ -826,7 +826,7 @@
|
|
|
826
826
|
/*!
|
|
827
827
|
* Wunderbaum - wb_extension_base
|
|
828
828
|
* Copyright (c) 2021-2023, Martin Wendt. Released under the MIT license.
|
|
829
|
-
* v0.
|
|
829
|
+
* v0.5.0, Fri, 15 Sep 2023 14:34:23 GMT (https://github.com/mar10/wunderbaum)
|
|
830
830
|
*/
|
|
831
831
|
class WunderbaumExtension {
|
|
832
832
|
constructor(tree, id, defaults) {
|
|
@@ -1113,11 +1113,75 @@
|
|
|
1113
1113
|
debounced.pending = pending;
|
|
1114
1114
|
return debounced;
|
|
1115
1115
|
}
|
|
1116
|
+
/**
|
|
1117
|
+
* Creates a throttled function that only invokes `func` at most once per
|
|
1118
|
+
* every `wait` milliseconds (or once per browser frame). The throttled function
|
|
1119
|
+
* comes with a `cancel` method to cancel delayed `func` invocations and a
|
|
1120
|
+
* `flush` method to immediately invoke them. Provide `options` to indicate
|
|
1121
|
+
* whether `func` should be invoked on the leading and/or trailing edge of the
|
|
1122
|
+
* `wait` timeout. The `func` is invoked with the last arguments provided to the
|
|
1123
|
+
* throttled function. Subsequent calls to the throttled function return the
|
|
1124
|
+
* result of the last `func` invocation.
|
|
1125
|
+
*
|
|
1126
|
+
* **Note:** If `leading` and `trailing` options are `true`, `func` is
|
|
1127
|
+
* invoked on the trailing edge of the timeout only if the throttled function
|
|
1128
|
+
* is invoked more than once during the `wait` timeout.
|
|
1129
|
+
*
|
|
1130
|
+
* If `wait` is `0` and `leading` is `false`, `func` invocation is deferred
|
|
1131
|
+
* until the next tick, similar to `setTimeout` with a timeout of `0`.
|
|
1132
|
+
*
|
|
1133
|
+
* If `wait` is omitted in an environment with `requestAnimationFrame`, `func`
|
|
1134
|
+
* invocation will be deferred until the next frame is drawn (typically about
|
|
1135
|
+
* 16ms).
|
|
1136
|
+
*
|
|
1137
|
+
* See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)
|
|
1138
|
+
* for details over the differences between `throttle` and `debounce`.
|
|
1139
|
+
*
|
|
1140
|
+
* @since 0.1.0
|
|
1141
|
+
* @category Function
|
|
1142
|
+
* @param {Function} func The function to throttle.
|
|
1143
|
+
* @param {number} [wait=0]
|
|
1144
|
+
* The number of milliseconds to throttle invocations to; if omitted,
|
|
1145
|
+
* `requestAnimationFrame` is used (if available).
|
|
1146
|
+
* @param {Object} [options={}] The options object.
|
|
1147
|
+
* @param {boolean} [options.leading=true]
|
|
1148
|
+
* Specify invoking on the leading edge of the timeout.
|
|
1149
|
+
* @param {boolean} [options.trailing=true]
|
|
1150
|
+
* Specify invoking on the trailing edge of the timeout.
|
|
1151
|
+
* @returns {Function} Returns the new throttled function.
|
|
1152
|
+
* @example
|
|
1153
|
+
*
|
|
1154
|
+
* // Avoid excessively updating the position while scrolling.
|
|
1155
|
+
* jQuery(window).on('scroll', throttle(updatePosition, 100))
|
|
1156
|
+
*
|
|
1157
|
+
* // Invoke `renewToken` when the click event is fired, but not more than once every 5 minutes.
|
|
1158
|
+
* const throttled = throttle(renewToken, 300000, { 'trailing': false })
|
|
1159
|
+
* jQuery(element).on('click', throttled)
|
|
1160
|
+
*
|
|
1161
|
+
* // Cancel the trailing throttled invocation.
|
|
1162
|
+
* jQuery(window).on('popstate', throttled.cancel)
|
|
1163
|
+
*/
|
|
1164
|
+
function throttle(func, wait = 0, options = {}) {
|
|
1165
|
+
let leading = true;
|
|
1166
|
+
let trailing = true;
|
|
1167
|
+
if (typeof func !== "function") {
|
|
1168
|
+
throw new TypeError("Expected a function");
|
|
1169
|
+
}
|
|
1170
|
+
if (isObject(options)) {
|
|
1171
|
+
leading = "leading" in options ? !!options.leading : leading;
|
|
1172
|
+
trailing = "trailing" in options ? !!options.trailing : trailing;
|
|
1173
|
+
}
|
|
1174
|
+
return debounce(func, wait, {
|
|
1175
|
+
leading,
|
|
1176
|
+
trailing,
|
|
1177
|
+
maxWait: wait,
|
|
1178
|
+
});
|
|
1179
|
+
}
|
|
1116
1180
|
|
|
1117
1181
|
/*!
|
|
1118
1182
|
* Wunderbaum - ext-filter
|
|
1119
1183
|
* Copyright (c) 2021-2023, Martin Wendt. Released under the MIT license.
|
|
1120
|
-
* v0.
|
|
1184
|
+
* v0.5.0, Fri, 15 Sep 2023 14:34:23 GMT (https://github.com/mar10/wunderbaum)
|
|
1121
1185
|
*/
|
|
1122
1186
|
const START_MARKER = "\uFFF7";
|
|
1123
1187
|
const END_MARKER = "\uFFF8";
|
|
@@ -1162,7 +1226,7 @@
|
|
|
1162
1226
|
}
|
|
1163
1227
|
}
|
|
1164
1228
|
_applyFilterNoUpdate(filter, branchMode, _opts) {
|
|
1165
|
-
return this.tree.
|
|
1229
|
+
return this.tree.runWithDeferredUpdate(() => {
|
|
1166
1230
|
return this._applyFilterImpl(filter, branchMode, _opts);
|
|
1167
1231
|
});
|
|
1168
1232
|
}
|
|
@@ -1374,7 +1438,6 @@
|
|
|
1374
1438
|
// "wb-ext-filter",
|
|
1375
1439
|
"wb-ext-filter-dim", "wb-ext-filter-hide");
|
|
1376
1440
|
// tree._callHook("treeStructureChanged", this, "clearFilter");
|
|
1377
|
-
// tree.render();
|
|
1378
1441
|
tree.enableUpdate(true);
|
|
1379
1442
|
}
|
|
1380
1443
|
}
|
|
@@ -1418,7 +1481,7 @@
|
|
|
1418
1481
|
/*!
|
|
1419
1482
|
* Wunderbaum - ext-keynav
|
|
1420
1483
|
* Copyright (c) 2021-2023, Martin Wendt. Released under the MIT license.
|
|
1421
|
-
* v0.
|
|
1484
|
+
* v0.5.0, Fri, 15 Sep 2023 14:34:23 GMT (https://github.com/mar10/wunderbaum)
|
|
1422
1485
|
*/
|
|
1423
1486
|
const QUICKSEARCH_DELAY = 500;
|
|
1424
1487
|
class KeynavExtension extends WunderbaumExtension {
|
|
@@ -1495,7 +1558,7 @@
|
|
|
1495
1558
|
tree.setFocus();
|
|
1496
1559
|
break;
|
|
1497
1560
|
case "Escape":
|
|
1498
|
-
node.
|
|
1561
|
+
node._render();
|
|
1499
1562
|
tree.setFocus();
|
|
1500
1563
|
break;
|
|
1501
1564
|
}
|
|
@@ -1565,7 +1628,7 @@
|
|
|
1565
1628
|
// tree._triggerNodeEvent("clickPaging", ctx, event);
|
|
1566
1629
|
// } else
|
|
1567
1630
|
if (node.getOption("checkbox")) {
|
|
1568
|
-
node.
|
|
1631
|
+
node.toggleSelected();
|
|
1569
1632
|
}
|
|
1570
1633
|
else {
|
|
1571
1634
|
node.setActive(true, { event: event });
|
|
@@ -1601,7 +1664,7 @@
|
|
|
1601
1664
|
if (inputHasFocus) {
|
|
1602
1665
|
if (eventName === "Escape") {
|
|
1603
1666
|
// Discard changes
|
|
1604
|
-
node.
|
|
1667
|
+
node._render();
|
|
1605
1668
|
// Keep cell-nav mode
|
|
1606
1669
|
node.logDebug(`Reset focused input`);
|
|
1607
1670
|
tree.setFocus();
|
|
@@ -1651,7 +1714,7 @@
|
|
|
1651
1714
|
break;
|
|
1652
1715
|
case " ": // Space
|
|
1653
1716
|
if (tree.activeColIdx === 0 && node.getOption("checkbox")) {
|
|
1654
|
-
node.
|
|
1717
|
+
node.toggleSelected();
|
|
1655
1718
|
handled = true;
|
|
1656
1719
|
}
|
|
1657
1720
|
else if (curInput && curInputType === "checkbox") {
|
|
@@ -1758,7 +1821,7 @@
|
|
|
1758
1821
|
/*!
|
|
1759
1822
|
* Wunderbaum - ext-logger
|
|
1760
1823
|
* Copyright (c) 2021-2023, Martin Wendt. Released under the MIT license.
|
|
1761
|
-
* v0.
|
|
1824
|
+
* v0.5.0, Fri, 15 Sep 2023 14:34:23 GMT (https://github.com/mar10/wunderbaum)
|
|
1762
1825
|
*/
|
|
1763
1826
|
class LoggerExtension extends WunderbaumExtension {
|
|
1764
1827
|
constructor(tree) {
|
|
@@ -1798,9 +1861,9 @@
|
|
|
1798
1861
|
/*!
|
|
1799
1862
|
* Wunderbaum - common
|
|
1800
1863
|
* Copyright (c) 2021-2023, Martin Wendt. Released under the MIT license.
|
|
1801
|
-
* v0.
|
|
1864
|
+
* v0.5.0, Fri, 15 Sep 2023 14:34:23 GMT (https://github.com/mar10/wunderbaum)
|
|
1802
1865
|
*/
|
|
1803
|
-
const DEFAULT_DEBUGLEVEL =
|
|
1866
|
+
const DEFAULT_DEBUGLEVEL = 3; // Replaced by rollup script
|
|
1804
1867
|
/**
|
|
1805
1868
|
* Fixed height of a row in pixel. Must match the SCSS variable `$row-outer-height`.
|
|
1806
1869
|
*/
|
|
@@ -1826,30 +1889,50 @@
|
|
|
1826
1889
|
* Default node icons.
|
|
1827
1890
|
* Requires bootstrap icons https://icons.getbootstrap.com
|
|
1828
1891
|
*/
|
|
1829
|
-
const
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1892
|
+
const iconMaps = {
|
|
1893
|
+
bootstrap: {
|
|
1894
|
+
error: "bi bi-exclamation-triangle",
|
|
1895
|
+
// loading: "bi bi-hourglass-split wb-busy",
|
|
1896
|
+
loading: "bi bi-chevron-right wb-busy",
|
|
1897
|
+
// loading: "bi bi-arrow-repeat wb-spin",
|
|
1898
|
+
// loading: '<div class="spinner-border spinner-border-sm" role="status"> <span class="visually-hidden">Loading...</span> </div>',
|
|
1899
|
+
// noData: "bi bi-search",
|
|
1900
|
+
noData: "bi bi-question-circle",
|
|
1901
|
+
expanderExpanded: "bi bi-chevron-down",
|
|
1902
|
+
// expanderExpanded: "bi bi-dash-square",
|
|
1903
|
+
expanderCollapsed: "bi bi-chevron-right",
|
|
1904
|
+
// expanderCollapsed: "bi bi-plus-square",
|
|
1905
|
+
expanderLazy: "bi bi-chevron-right wb-helper-lazy-expander",
|
|
1906
|
+
// expanderLazy: "bi bi-chevron-bar-right",
|
|
1907
|
+
checkChecked: "bi bi-check-square",
|
|
1908
|
+
checkUnchecked: "bi bi-square",
|
|
1909
|
+
checkUnknown: "bi bi-dash-square-dotted",
|
|
1910
|
+
radioChecked: "bi bi-circle-fill",
|
|
1911
|
+
radioUnchecked: "bi bi-circle",
|
|
1912
|
+
radioUnknown: "bi bi-record-circle",
|
|
1913
|
+
folder: "bi bi-folder2",
|
|
1914
|
+
folderOpen: "bi bi-folder2-open",
|
|
1915
|
+
folderLazy: "bi bi-folder-symlink",
|
|
1916
|
+
doc: "bi bi-file-earmark",
|
|
1917
|
+
},
|
|
1918
|
+
fontawesome6: {
|
|
1919
|
+
error: "fa-solid fa-triangle-exclamation",
|
|
1920
|
+
loading: "fa-regular fa-chevron-right fa-beat",
|
|
1921
|
+
noData: "fa-solid fa-circle-question",
|
|
1922
|
+
expanderExpanded: "fa-regular fa-chevron-down",
|
|
1923
|
+
expanderCollapsed: "fa-regular fa-chevron-right",
|
|
1924
|
+
expanderLazy: "fa-regular fa-chevron-right wb-helper-lazy-expander",
|
|
1925
|
+
checkChecked: "fa-regular fa-square-check",
|
|
1926
|
+
checkUnchecked: "fa-regular fa-square",
|
|
1927
|
+
checkUnknown: "fa-regular fa-square-minus",
|
|
1928
|
+
radioChecked: "fa-solid fa-circle",
|
|
1929
|
+
radioUnchecked: "fa-regular fa-circle",
|
|
1930
|
+
radioUnknown: "fa-regular fa-circle-question",
|
|
1931
|
+
folder: "fa-solid fa-folder-closed",
|
|
1932
|
+
folderOpen: "fa-regular fa-folder-open",
|
|
1933
|
+
folderLazy: "fa-solid fa-folder-plus",
|
|
1934
|
+
doc: "fa-regular fa-file",
|
|
1935
|
+
},
|
|
1853
1936
|
};
|
|
1854
1937
|
/** Dict keys that are evaluated by source loader (others are added to `tree.data` instead). */
|
|
1855
1938
|
const RESERVED_TREE_SOURCE_KEYS = new Set([
|
|
@@ -2044,7 +2127,7 @@
|
|
|
2044
2127
|
/*!
|
|
2045
2128
|
* Wunderbaum - ext-dnd
|
|
2046
2129
|
* Copyright (c) 2021-2023, Martin Wendt. Released under the MIT license.
|
|
2047
|
-
* v0.
|
|
2130
|
+
* v0.5.0, Fri, 15 Sep 2023 14:34:23 GMT (https://github.com/mar10/wunderbaum)
|
|
2048
2131
|
*/
|
|
2049
2132
|
const nodeMimeType = "application/x-wunderbaum-node";
|
|
2050
2133
|
class DndExtension extends WunderbaumExtension {
|
|
@@ -2067,6 +2150,7 @@
|
|
|
2067
2150
|
preventVoidMoves: true,
|
|
2068
2151
|
scroll: true,
|
|
2069
2152
|
scrollSensitivity: 20,
|
|
2153
|
+
// scrollnterval: 50, // Generste event every 50 ms
|
|
2070
2154
|
scrollSpeed: 5,
|
|
2071
2155
|
// setTextTypeJson: false, // Allow dragging of nodes to different IE windows
|
|
2072
2156
|
sourceCopyHook: null,
|
|
@@ -2088,6 +2172,8 @@
|
|
|
2088
2172
|
this.lastAllowedDropRegions = null;
|
|
2089
2173
|
this.lastDropEffect = null;
|
|
2090
2174
|
this.lastDropRegion = false;
|
|
2175
|
+
this.currentScrollDir = 0;
|
|
2176
|
+
this.applyScrollDirThrottled = throttle(this.applyScrollDir, 50);
|
|
2091
2177
|
}
|
|
2092
2178
|
init() {
|
|
2093
2179
|
super.init();
|
|
@@ -2158,22 +2244,55 @@
|
|
|
2158
2244
|
// return "over";
|
|
2159
2245
|
}
|
|
2160
2246
|
/* Implement auto scrolling when drag cursor is in top/bottom area of scroll parent. */
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
const
|
|
2166
|
-
if (
|
|
2167
|
-
sp.scrollTop =
|
|
2247
|
+
applyScrollDir() {
|
|
2248
|
+
if (this.isDragging() && this.currentScrollDir) {
|
|
2249
|
+
const dndOpts = this.tree.options.dnd;
|
|
2250
|
+
const sp = this.tree.element; // scroll parent
|
|
2251
|
+
const scrollTop = sp.scrollTop;
|
|
2252
|
+
if (this.currentScrollDir < 0) {
|
|
2253
|
+
sp.scrollTop = Math.max(0, scrollTop - dndOpts.scrollSpeed);
|
|
2254
|
+
}
|
|
2255
|
+
else if (this.currentScrollDir > 0) {
|
|
2256
|
+
sp.scrollTop = scrollTop + dndOpts.scrollSpeed;
|
|
2168
2257
|
}
|
|
2169
2258
|
}
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2259
|
+
}
|
|
2260
|
+
/* Implement auto scrolling when drag cursor is in top/bottom area of scroll parent. */
|
|
2261
|
+
autoScroll(viewportY) {
|
|
2262
|
+
const tree = this.tree;
|
|
2263
|
+
const dndOpts = tree.options.dnd;
|
|
2264
|
+
const sensitivity = dndOpts.scrollSensitivity;
|
|
2265
|
+
const sp = tree.element; // scroll parent
|
|
2266
|
+
const headerHeight = tree.headerElement.clientHeight; // May be 0
|
|
2267
|
+
// const height = sp.clientHeight - headerHeight;
|
|
2268
|
+
// const height = sp.offsetHeight + headerHeight;
|
|
2269
|
+
const height = sp.offsetHeight;
|
|
2270
|
+
const scrollTop = sp.scrollTop;
|
|
2271
|
+
// tree.logDebug(
|
|
2272
|
+
// `autoScroll: height=${height}, scrollTop=${scrollTop}, viewportY=${viewportY}`
|
|
2273
|
+
// );
|
|
2274
|
+
this.currentScrollDir = 0;
|
|
2275
|
+
if (scrollTop > 0 &&
|
|
2276
|
+
viewportY > 0 &&
|
|
2277
|
+
viewportY <= sensitivity + headerHeight) {
|
|
2278
|
+
// Mouse in top 20px area: scroll up
|
|
2279
|
+
// sp.scrollTop = Math.max(0, scrollTop - dndOpts.scrollSpeed);
|
|
2280
|
+
this.currentScrollDir = -1;
|
|
2281
|
+
}
|
|
2282
|
+
else if (scrollTop < sp.scrollHeight - height &&
|
|
2283
|
+
viewportY >= height - sensitivity) {
|
|
2284
|
+
// Mouse in bottom 20px area: scroll down
|
|
2285
|
+
// sp.scrollTop = scrollTop + dndOpts.scrollSpeed;
|
|
2286
|
+
this.currentScrollDir = +1;
|
|
2287
|
+
}
|
|
2288
|
+
if (this.currentScrollDir) {
|
|
2289
|
+
this.applyScrollDirThrottled();
|
|
2290
|
+
}
|
|
2291
|
+
return sp.scrollTop - scrollTop;
|
|
2292
|
+
}
|
|
2293
|
+
/** Return true if a drag operation currently in progress. */
|
|
2294
|
+
isDragging() {
|
|
2295
|
+
return !!this.srcNode;
|
|
2177
2296
|
}
|
|
2178
2297
|
onDragEvent(e) {
|
|
2179
2298
|
// const tree = this.tree;
|
|
@@ -2205,7 +2324,7 @@
|
|
|
2205
2324
|
n._org_key = n.key;
|
|
2206
2325
|
delete n.key;
|
|
2207
2326
|
});
|
|
2208
|
-
nodeData.
|
|
2327
|
+
nodeData._treeId = srcNode.tree.id;
|
|
2209
2328
|
const json = JSON.stringify(nodeData);
|
|
2210
2329
|
e.dataTransfer.setData(nodeMimeType, json);
|
|
2211
2330
|
// e.dataTransfer!.setData("text/html", $(node.span).html());
|
|
@@ -2297,7 +2416,8 @@
|
|
|
2297
2416
|
// --- dragover ---
|
|
2298
2417
|
}
|
|
2299
2418
|
else if (e.type === "dragover") {
|
|
2300
|
-
this.
|
|
2419
|
+
const viewportY = e.clientY - this.tree.element.offsetTop;
|
|
2420
|
+
this.autoScroll(viewportY);
|
|
2301
2421
|
const region = this._calcDropRegion(e, this.lastAllowedDropRegions);
|
|
2302
2422
|
this.lastDropRegion = region;
|
|
2303
2423
|
if (dndOpts.autoExpandMS > 0 &&
|
|
@@ -2337,7 +2457,7 @@
|
|
|
2337
2457
|
/*!
|
|
2338
2458
|
* Wunderbaum - drag_observer
|
|
2339
2459
|
* Copyright (c) 2021-2023, Martin Wendt. Released under the MIT license.
|
|
2340
|
-
* v0.
|
|
2460
|
+
* v0.5.0, Fri, 15 Sep 2023 14:34:23 GMT (https://github.com/mar10/wunderbaum)
|
|
2341
2461
|
*/
|
|
2342
2462
|
/**
|
|
2343
2463
|
* Convert mouse- and touch events to 'dragstart', 'drag', and 'dragstop'.
|
|
@@ -2473,7 +2593,7 @@
|
|
|
2473
2593
|
/*!
|
|
2474
2594
|
* Wunderbaum - ext-grid
|
|
2475
2595
|
* Copyright (c) 2021-2023, Martin Wendt. Released under the MIT license.
|
|
2476
|
-
* v0.
|
|
2596
|
+
* v0.5.0, Fri, 15 Sep 2023 14:34:23 GMT (https://github.com/mar10/wunderbaum)
|
|
2477
2597
|
*/
|
|
2478
2598
|
class GridExtension extends WunderbaumExtension {
|
|
2479
2599
|
constructor(tree) {
|
|
@@ -2510,7 +2630,7 @@
|
|
|
2510
2630
|
/*!
|
|
2511
2631
|
* Wunderbaum - deferred
|
|
2512
2632
|
* Copyright (c) 2021-2023, Martin Wendt. Released under the MIT license.
|
|
2513
|
-
* v0.
|
|
2633
|
+
* v0.5.0, Fri, 15 Sep 2023 14:34:23 GMT (https://github.com/mar10/wunderbaum)
|
|
2514
2634
|
*/
|
|
2515
2635
|
/**
|
|
2516
2636
|
* Implement a ES6 Promise, that exposes a resolve() and reject() method.
|
|
@@ -2563,32 +2683,20 @@
|
|
|
2563
2683
|
/*!
|
|
2564
2684
|
* Wunderbaum - wunderbaum_node
|
|
2565
2685
|
* Copyright (c) 2021-2023, Martin Wendt. Released under the MIT license.
|
|
2566
|
-
* v0.
|
|
2686
|
+
* v0.5.0, Fri, 15 Sep 2023 14:34:23 GMT (https://github.com/mar10/wunderbaum)
|
|
2687
|
+
*/
|
|
2688
|
+
/** WunderbaumNode properties that can be passed with source data.
|
|
2689
|
+
* (Any other source properties will be stored as `node.data.PROP`.)
|
|
2567
2690
|
*/
|
|
2568
|
-
/** Top-level properties that can be passed with `data`. */
|
|
2569
2691
|
const NODE_PROPS = new Set([
|
|
2570
|
-
// TODO: use NODE_ATTRS instead?
|
|
2571
|
-
"classes",
|
|
2572
|
-
"expanded",
|
|
2573
|
-
"icon",
|
|
2574
|
-
"key",
|
|
2575
|
-
"lazy",
|
|
2576
|
-
"refKey",
|
|
2577
|
-
"selected",
|
|
2578
|
-
"title",
|
|
2579
|
-
"tooltip",
|
|
2580
|
-
"type",
|
|
2581
|
-
]);
|
|
2582
|
-
const NODE_ATTRS = new Set([
|
|
2583
2692
|
"checkbox",
|
|
2584
|
-
"expanded",
|
|
2585
2693
|
"classes",
|
|
2586
|
-
"
|
|
2694
|
+
"expanded",
|
|
2587
2695
|
"icon",
|
|
2588
2696
|
"iconTooltip",
|
|
2589
2697
|
"key",
|
|
2590
2698
|
"lazy",
|
|
2591
|
-
"
|
|
2699
|
+
"_partsel",
|
|
2592
2700
|
"radiogroup",
|
|
2593
2701
|
"refKey",
|
|
2594
2702
|
"selected",
|
|
@@ -2597,9 +2705,12 @@
|
|
|
2597
2705
|
"tooltip",
|
|
2598
2706
|
"type",
|
|
2599
2707
|
"unselectable",
|
|
2600
|
-
"unselectableIgnore",
|
|
2601
|
-
"unselectableStatus",
|
|
2602
2708
|
]);
|
|
2709
|
+
/** WunderbaumNode properties that will be returned by `node.toDict()`.)
|
|
2710
|
+
*/
|
|
2711
|
+
const NODE_DICT_PROPS = new Set(NODE_PROPS);
|
|
2712
|
+
NODE_DICT_PROPS.delete("_partsel");
|
|
2713
|
+
NODE_DICT_PROPS.delete("unselectable");
|
|
2603
2714
|
/**
|
|
2604
2715
|
* A single tree node.
|
|
2605
2716
|
*
|
|
@@ -2616,13 +2727,6 @@
|
|
|
2616
2727
|
*/
|
|
2617
2728
|
this.refKey = undefined;
|
|
2618
2729
|
this.children = null;
|
|
2619
|
-
this.lazy = false;
|
|
2620
|
-
/** Expansion state.
|
|
2621
|
-
* @see {@link isExpandable}, {@link isExpanded}, {@link setExpanded}. */
|
|
2622
|
-
this.expanded = false;
|
|
2623
|
-
/** Selection state.
|
|
2624
|
-
* @see {@link isSelected}, {@link setSelected}. */
|
|
2625
|
-
this.selected = false;
|
|
2626
2730
|
/** Additional classes added to `div.wb-row`.
|
|
2627
2731
|
* @see {@link hasClass}, {@link setClass}. */
|
|
2628
2732
|
this.classes = null; //new Set<string>();
|
|
@@ -2643,16 +2747,19 @@
|
|
|
2643
2747
|
this.key = "" + ((_a = data.key) !== null && _a !== void 0 ? _a : ++WunderbaumNode.sequence);
|
|
2644
2748
|
this.title = "" + ((_b = data.title) !== null && _b !== void 0 ? _b : "<" + this.key + ">");
|
|
2645
2749
|
data.refKey != null ? (this.refKey = "" + data.refKey) : 0;
|
|
2646
|
-
data.statusNodeType != null
|
|
2647
|
-
? (this.statusNodeType = "" + data.statusNodeType)
|
|
2648
|
-
: 0;
|
|
2649
2750
|
data.type != null ? (this.type = "" + data.type) : 0;
|
|
2650
|
-
data.checkbox != null ? (this.checkbox = !!data.checkbox) : 0;
|
|
2651
|
-
data.colspan != null ? (this.colspan = !!data.colspan) : 0;
|
|
2652
2751
|
this.expanded = data.expanded === true;
|
|
2653
2752
|
data.icon != null ? (this.icon = data.icon) : 0;
|
|
2654
2753
|
this.lazy = data.lazy === true;
|
|
2754
|
+
data.statusNodeType != null
|
|
2755
|
+
? (this.statusNodeType = ("" + data.statusNodeType))
|
|
2756
|
+
: 0;
|
|
2757
|
+
data.colspan != null ? (this.colspan = !!data.colspan) : 0;
|
|
2758
|
+
// Selection
|
|
2759
|
+
data.checkbox != null ? (this.checkbox = !!data.checkbox) : 0;
|
|
2760
|
+
data.radiogroup != null ? (this.radiogroup = !!data.radiogroup) : 0;
|
|
2655
2761
|
this.selected = data.selected === true;
|
|
2762
|
+
data.unselectable === true ? (this.unselectable = true) : 0;
|
|
2656
2763
|
if (data.classes) {
|
|
2657
2764
|
this.setClass(data.classes);
|
|
2658
2765
|
}
|
|
@@ -2776,14 +2883,17 @@
|
|
|
2776
2883
|
// insert nodeList after children[pos]
|
|
2777
2884
|
this.children.splice(pos, 0, ...nodeList);
|
|
2778
2885
|
}
|
|
2779
|
-
// TODO:
|
|
2780
|
-
// if (tree.options.selectMode === 3) {
|
|
2781
|
-
// this.fixSelection3FromEndNodes();
|
|
2782
|
-
// }
|
|
2783
2886
|
// this.triggerModifyChild("add", nodeList.length === 1 ? nodeList[0] : null);
|
|
2784
|
-
tree.
|
|
2887
|
+
tree.update(ChangeType.structure);
|
|
2785
2888
|
}
|
|
2786
2889
|
finally {
|
|
2890
|
+
// if (tree.options.selectMode === "hier") {
|
|
2891
|
+
// if (this.parent && this.parent.children) {
|
|
2892
|
+
// this.fixSelection3FromEndNodes();
|
|
2893
|
+
// } else {
|
|
2894
|
+
// // may happen when loading __root__;
|
|
2895
|
+
// }
|
|
2896
|
+
// }
|
|
2787
2897
|
tree.enableUpdate(true);
|
|
2788
2898
|
}
|
|
2789
2899
|
// if(isTopCall && loadLazy){
|
|
@@ -2829,6 +2939,17 @@
|
|
|
2829
2939
|
applyCommand(cmd, options) {
|
|
2830
2940
|
return this.tree.applyCommand(cmd, this, options);
|
|
2831
2941
|
}
|
|
2942
|
+
/**
|
|
2943
|
+
* Collapse all expanded sibling nodes if any.
|
|
2944
|
+
* (Automatically called when `autoCollapse` is true.)
|
|
2945
|
+
*/
|
|
2946
|
+
collapseSiblings(options) {
|
|
2947
|
+
for (let node of this.parent.children) {
|
|
2948
|
+
if (node !== this && node.expanded) {
|
|
2949
|
+
node.setExpanded(false, options);
|
|
2950
|
+
}
|
|
2951
|
+
}
|
|
2952
|
+
}
|
|
2832
2953
|
/**
|
|
2833
2954
|
* Add/remove one or more classes to `<div class='wb-row'>`.
|
|
2834
2955
|
*
|
|
@@ -2868,7 +2989,7 @@
|
|
|
2868
2989
|
const tree = this.tree;
|
|
2869
2990
|
const minExpandLevel = this.tree.options.minExpandLevel;
|
|
2870
2991
|
let { depth = 99, loadLazy, force } = options !== null && options !== void 0 ? options : {};
|
|
2871
|
-
const
|
|
2992
|
+
const expandOpts = {
|
|
2872
2993
|
scrollIntoView: false,
|
|
2873
2994
|
force: force,
|
|
2874
2995
|
loadLazy: loadLazy,
|
|
@@ -2892,7 +3013,7 @@
|
|
|
2892
3013
|
// Node is collapsed and may be expanded (i.e. has children or is lazy)
|
|
2893
3014
|
// Expanding may be async, so we store the promise.
|
|
2894
3015
|
// Also the recursion is delayed until expansion finished.
|
|
2895
|
-
const p = cn.setExpanded(true,
|
|
3016
|
+
const p = cn.setExpanded(true, expandOpts);
|
|
2896
3017
|
promises.push(p);
|
|
2897
3018
|
p.then(async () => {
|
|
2898
3019
|
await _iter(cn, level_1);
|
|
@@ -2908,7 +3029,7 @@
|
|
|
2908
3029
|
// Collapsing is always synchronous, so no promises required
|
|
2909
3030
|
if (!minExpandLevel || force || cn.getLevel() > minExpandLevel) {
|
|
2910
3031
|
// Do not collapse until minExpandLevel
|
|
2911
|
-
cn.setExpanded(false,
|
|
3032
|
+
cn.setExpanded(false, expandOpts);
|
|
2912
3033
|
}
|
|
2913
3034
|
_iter(cn, level_1); // recursion, even if cn was already collapsed
|
|
2914
3035
|
}
|
|
@@ -3215,7 +3336,7 @@
|
|
|
3215
3336
|
return false;
|
|
3216
3337
|
}
|
|
3217
3338
|
if (this.children == null) {
|
|
3218
|
-
return this.lazy; // null or undefined can trigger lazy load
|
|
3339
|
+
return !!this.lazy; // null or undefined can trigger lazy load
|
|
3219
3340
|
}
|
|
3220
3341
|
if (this.children.length === 0) {
|
|
3221
3342
|
return !!this.tree.options.emptyChildListExpandable;
|
|
@@ -3280,9 +3401,11 @@
|
|
|
3280
3401
|
isRootNode() {
|
|
3281
3402
|
return this.tree.root === this;
|
|
3282
3403
|
}
|
|
3283
|
-
/** Return true if this node is selected, i.e. the checkbox is set.
|
|
3404
|
+
/** Return true if this node is selected, i.e. the checkbox is set.
|
|
3405
|
+
* `undefined` if partly selected (tri-state), false otherwise.
|
|
3406
|
+
*/
|
|
3284
3407
|
isSelected() {
|
|
3285
|
-
return
|
|
3408
|
+
return this.selected ? true : this._partsel ? undefined : false;
|
|
3286
3409
|
}
|
|
3287
3410
|
/** Return true if this node is a temporarily generated system node like
|
|
3288
3411
|
* 'loading', 'paging', or 'error' (node.statusNodeType contains the type).
|
|
@@ -3354,7 +3477,7 @@
|
|
|
3354
3477
|
tree.logInfo("Redefine columns", source.columns);
|
|
3355
3478
|
tree.columns = source.columns;
|
|
3356
3479
|
delete source.columns;
|
|
3357
|
-
tree.
|
|
3480
|
+
tree.update(ChangeType.colStructure);
|
|
3358
3481
|
}
|
|
3359
3482
|
this.addChildren(source.children);
|
|
3360
3483
|
// Add extra data to `tree.data`
|
|
@@ -3364,6 +3487,9 @@
|
|
|
3364
3487
|
tree.logDebug(`Add source.${key} to tree.data.${key}`);
|
|
3365
3488
|
}
|
|
3366
3489
|
}
|
|
3490
|
+
if (tree.options.selectMode === "hier") {
|
|
3491
|
+
this.fixSelection3FromEndNodes();
|
|
3492
|
+
}
|
|
3367
3493
|
this._callEvent("load");
|
|
3368
3494
|
}
|
|
3369
3495
|
async _fetchWithOptions(source) {
|
|
@@ -3379,6 +3505,7 @@
|
|
|
3379
3505
|
else if (isPlainObject(source)) {
|
|
3380
3506
|
// source is a plain object with `.url` property.
|
|
3381
3507
|
({ url, params, body, options, ...rest } = source);
|
|
3508
|
+
assert(!rest || Object.keys(rest).length === 0, `Unexpected source properties: ${Object.keys(rest)}. Use 'options' instead.`);
|
|
3382
3509
|
assert(typeof url === "string", `expected source.url as string`);
|
|
3383
3510
|
if (isPlainObject(options)) {
|
|
3384
3511
|
fetchOpts = options;
|
|
@@ -3493,10 +3620,10 @@
|
|
|
3493
3620
|
await this.load(source); // also calls setStatus('ok')
|
|
3494
3621
|
if (wasExpanded) {
|
|
3495
3622
|
this.expanded = true;
|
|
3496
|
-
this.tree.
|
|
3623
|
+
this.tree.update(ChangeType.structure);
|
|
3497
3624
|
}
|
|
3498
3625
|
else {
|
|
3499
|
-
this.
|
|
3626
|
+
this.update(); // Fix expander icon to 'loaded'
|
|
3500
3627
|
}
|
|
3501
3628
|
}
|
|
3502
3629
|
catch (e) {
|
|
@@ -3653,7 +3780,7 @@
|
|
|
3653
3780
|
// Fix node.tree for all source nodes
|
|
3654
3781
|
// util.assert(false, "Cross-tree move is not yet implemented.");
|
|
3655
3782
|
this.logWarn("Cross-tree moveTo is experimental!");
|
|
3656
|
-
this.visit(
|
|
3783
|
+
this.visit((n) => {
|
|
3657
3784
|
// TODO: fix selection state and activation, ...
|
|
3658
3785
|
n.tree = targetNode.tree;
|
|
3659
3786
|
}, true);
|
|
@@ -3662,7 +3789,7 @@
|
|
|
3662
3789
|
// DragAndDrop to generate a dragend event on the source node
|
|
3663
3790
|
setTimeout(() => {
|
|
3664
3791
|
// Even indentation may have changed:
|
|
3665
|
-
tree.
|
|
3792
|
+
tree.update(ChangeType.any);
|
|
3666
3793
|
}, 0);
|
|
3667
3794
|
// TODO: fix selection state
|
|
3668
3795
|
// TODO: fix active state
|
|
@@ -3709,7 +3836,7 @@
|
|
|
3709
3836
|
n.removeMarkup();
|
|
3710
3837
|
tree._unregisterNode(n);
|
|
3711
3838
|
}, true);
|
|
3712
|
-
tree.
|
|
3839
|
+
tree.update(ChangeType.structure);
|
|
3713
3840
|
}
|
|
3714
3841
|
/** Remove all descendants of this node. */
|
|
3715
3842
|
removeChildren() {
|
|
@@ -3741,7 +3868,7 @@
|
|
|
3741
3868
|
if (!this.isRootNode()) {
|
|
3742
3869
|
this.expanded = false;
|
|
3743
3870
|
}
|
|
3744
|
-
this.tree.
|
|
3871
|
+
this.tree.update(ChangeType.structure);
|
|
3745
3872
|
}
|
|
3746
3873
|
/** Remove all HTML markup from the DOM. */
|
|
3747
3874
|
removeMarkup() {
|
|
@@ -3777,7 +3904,7 @@
|
|
|
3777
3904
|
renderColInfosById: renderColInfosById,
|
|
3778
3905
|
};
|
|
3779
3906
|
}
|
|
3780
|
-
_createIcon(parentElem, replaceChild, showLoading) {
|
|
3907
|
+
_createIcon(iconMap, parentElem, replaceChild, showLoading) {
|
|
3781
3908
|
let iconSpan;
|
|
3782
3909
|
let icon = this.getOption("icon");
|
|
3783
3910
|
if (this._errorInfo) {
|
|
@@ -3836,12 +3963,13 @@
|
|
|
3836
3963
|
}
|
|
3837
3964
|
/**
|
|
3838
3965
|
* Create a whole new `<div class="wb-row">` element.
|
|
3839
|
-
* @see {@link WunderbaumNode.
|
|
3966
|
+
* @see {@link WunderbaumNode._render}
|
|
3840
3967
|
*/
|
|
3841
3968
|
_render_markup(opts) {
|
|
3842
3969
|
const tree = this.tree;
|
|
3843
3970
|
const treeOptions = tree.options;
|
|
3844
|
-
const checkbox = this.getOption("checkbox")
|
|
3971
|
+
const checkbox = this.getOption("checkbox");
|
|
3972
|
+
// const checkbox = this.getOption("checkbox") !== false;
|
|
3845
3973
|
const columns = tree.columns;
|
|
3846
3974
|
const level = this.getLevel();
|
|
3847
3975
|
let elem;
|
|
@@ -3869,6 +3997,9 @@
|
|
|
3869
3997
|
if (checkbox) {
|
|
3870
3998
|
checkboxSpan = document.createElement("i");
|
|
3871
3999
|
checkboxSpan.classList.add("wb-checkbox");
|
|
4000
|
+
if (checkbox === "radio" || this.parent.radiogroup) {
|
|
4001
|
+
checkboxSpan.classList.add("wb-radio");
|
|
4002
|
+
}
|
|
3872
4003
|
nodeElem.appendChild(checkboxSpan);
|
|
3873
4004
|
ofsTitlePx += ICON_WIDTH;
|
|
3874
4005
|
}
|
|
@@ -3886,7 +4017,7 @@
|
|
|
3886
4017
|
}
|
|
3887
4018
|
// Render the icon (show a 'loading' icon if we do not have an expander that
|
|
3888
4019
|
// we would prefer).
|
|
3889
|
-
iconSpan = this._createIcon(nodeElem, null, !expanderSpan);
|
|
4020
|
+
iconSpan = this._createIcon(tree.iconMap, nodeElem, null, !expanderSpan);
|
|
3890
4021
|
if (iconSpan) {
|
|
3891
4022
|
ofsTitlePx += ICON_WIDTH;
|
|
3892
4023
|
}
|
|
@@ -3949,7 +4080,7 @@
|
|
|
3949
4080
|
/**
|
|
3950
4081
|
* Render `node.title`, `.icon` into an existing row.
|
|
3951
4082
|
*
|
|
3952
|
-
* @see {@link WunderbaumNode.
|
|
4083
|
+
* @see {@link WunderbaumNode._render}
|
|
3953
4084
|
*/
|
|
3954
4085
|
_render_data(opts) {
|
|
3955
4086
|
assert(this._rowElem);
|
|
@@ -4015,11 +4146,12 @@
|
|
|
4015
4146
|
}
|
|
4016
4147
|
/**
|
|
4017
4148
|
* Update row classes to reflect active, focuses, etc.
|
|
4018
|
-
* @see {@link WunderbaumNode.
|
|
4149
|
+
* @see {@link WunderbaumNode._render}
|
|
4019
4150
|
*/
|
|
4020
4151
|
_render_status(opts) {
|
|
4021
4152
|
// this.log("_render_status", opts);
|
|
4022
4153
|
const tree = this.tree;
|
|
4154
|
+
const iconMap = tree.iconMap;
|
|
4023
4155
|
const treeOptions = tree.options;
|
|
4024
4156
|
const typeInfo = this.type ? tree.types[this.type] : null;
|
|
4025
4157
|
const rowDiv = this._rowElem;
|
|
@@ -4031,6 +4163,7 @@
|
|
|
4031
4163
|
this.expanded ? rowClasses.push("wb-expanded") : 0;
|
|
4032
4164
|
this.lazy ? rowClasses.push("wb-lazy") : 0;
|
|
4033
4165
|
this.selected ? rowClasses.push("wb-selected") : 0;
|
|
4166
|
+
this._partsel ? rowClasses.push("wb-partsel") : 0;
|
|
4034
4167
|
this === tree.activeNode ? rowClasses.push("wb-active") : 0;
|
|
4035
4168
|
this === tree.focusNode ? rowClasses.push("wb-focus") : 0;
|
|
4036
4169
|
this._errorInfo ? rowClasses.push("wb-error") : 0;
|
|
@@ -4070,12 +4203,30 @@
|
|
|
4070
4203
|
}
|
|
4071
4204
|
}
|
|
4072
4205
|
if (checkboxSpan) {
|
|
4073
|
-
|
|
4074
|
-
|
|
4206
|
+
let cbclass = "wb-checkbox ";
|
|
4207
|
+
if (this.parent.radiogroup) {
|
|
4208
|
+
cbclass += "wb-radio ";
|
|
4209
|
+
if (this.selected) {
|
|
4210
|
+
cbclass += iconMap.radioChecked;
|
|
4211
|
+
// } else if (this._partsel) {
|
|
4212
|
+
// cbclass += iconMap.radioUnknown;
|
|
4213
|
+
}
|
|
4214
|
+
else {
|
|
4215
|
+
cbclass += iconMap.radioUnchecked;
|
|
4216
|
+
}
|
|
4075
4217
|
}
|
|
4076
4218
|
else {
|
|
4077
|
-
|
|
4219
|
+
if (this.selected) {
|
|
4220
|
+
cbclass += iconMap.checkChecked;
|
|
4221
|
+
}
|
|
4222
|
+
else if (this._partsel) {
|
|
4223
|
+
cbclass += iconMap.checkUnknown;
|
|
4224
|
+
}
|
|
4225
|
+
else {
|
|
4226
|
+
cbclass += iconMap.checkUnchecked;
|
|
4227
|
+
}
|
|
4078
4228
|
}
|
|
4229
|
+
checkboxSpan.className = cbclass;
|
|
4079
4230
|
}
|
|
4080
4231
|
// Fix active cell in cell-nav mode
|
|
4081
4232
|
if (!opts.isNew) {
|
|
@@ -4086,7 +4237,7 @@
|
|
|
4086
4237
|
// Update icon (if not opts.isNew, which would rebuild markup anyway)
|
|
4087
4238
|
const iconSpan = nodeElem.querySelector("i.wb-icon");
|
|
4088
4239
|
if (iconSpan) {
|
|
4089
|
-
this._createIcon(nodeElem, iconSpan, !expanderSpan);
|
|
4240
|
+
this._createIcon(tree.iconMap, nodeElem, iconSpan, !expanderSpan);
|
|
4090
4241
|
}
|
|
4091
4242
|
}
|
|
4092
4243
|
// Adjust column width
|
|
@@ -4103,7 +4254,7 @@
|
|
|
4103
4254
|
}
|
|
4104
4255
|
}
|
|
4105
4256
|
}
|
|
4106
|
-
|
|
4257
|
+
/*
|
|
4107
4258
|
* Create or update node's markup.
|
|
4108
4259
|
*
|
|
4109
4260
|
* `options.change` defaults to ChangeType.data, which updates the title,
|
|
@@ -4114,10 +4265,10 @@
|
|
|
4114
4265
|
* `options.change` should be set to ChangeType.status instead for best
|
|
4115
4266
|
* efficiency.
|
|
4116
4267
|
*
|
|
4117
|
-
* Calling `
|
|
4118
|
-
* @see {@link WunderbaumNode.
|
|
4268
|
+
* Calling `update()` is almost always a better alternative.
|
|
4269
|
+
* @see {@link WunderbaumNode.update}
|
|
4119
4270
|
*/
|
|
4120
|
-
|
|
4271
|
+
_render(options) {
|
|
4121
4272
|
// this.log("render", options);
|
|
4122
4273
|
const opts = Object.assign({ change: ChangeType.data }, options);
|
|
4123
4274
|
if (!this._rowElem) {
|
|
@@ -4147,7 +4298,7 @@
|
|
|
4147
4298
|
this.expanded = false;
|
|
4148
4299
|
this.lazy = true;
|
|
4149
4300
|
this.children = null;
|
|
4150
|
-
this.tree.
|
|
4301
|
+
this.tree.update(ChangeType.structure);
|
|
4151
4302
|
}
|
|
4152
4303
|
/** Convert node (or whole branch) into a plain object.
|
|
4153
4304
|
*
|
|
@@ -4162,7 +4313,7 @@
|
|
|
4162
4313
|
*/
|
|
4163
4314
|
toDict(recursive = false, callback) {
|
|
4164
4315
|
const dict = {};
|
|
4165
|
-
|
|
4316
|
+
NODE_DICT_PROPS.forEach((propName) => {
|
|
4166
4317
|
const val = this[propName];
|
|
4167
4318
|
if (val instanceof Set) {
|
|
4168
4319
|
// Convert Set to string (or skip if set is empty)
|
|
@@ -4287,7 +4438,7 @@
|
|
|
4287
4438
|
return;
|
|
4288
4439
|
}
|
|
4289
4440
|
tree.activeNode = null;
|
|
4290
|
-
prev === null || prev === void 0 ? void 0 : prev.
|
|
4441
|
+
prev === null || prev === void 0 ? void 0 : prev.update(ChangeType.status);
|
|
4291
4442
|
}
|
|
4292
4443
|
}
|
|
4293
4444
|
else if (prev === this || retrigger) {
|
|
@@ -4302,8 +4453,8 @@
|
|
|
4302
4453
|
if (focusTree)
|
|
4303
4454
|
tree.setFocus();
|
|
4304
4455
|
}
|
|
4305
|
-
prev === null || prev === void 0 ? void 0 : prev.
|
|
4306
|
-
this.
|
|
4456
|
+
prev === null || prev === void 0 ? void 0 : prev.update(ChangeType.status);
|
|
4457
|
+
this.update(ChangeType.status);
|
|
4307
4458
|
}
|
|
4308
4459
|
if (options &&
|
|
4309
4460
|
options.colIdx != null &&
|
|
@@ -4332,13 +4483,16 @@
|
|
|
4332
4483
|
return; // Nothing to do
|
|
4333
4484
|
}
|
|
4334
4485
|
// this.log("setExpanded()");
|
|
4486
|
+
if (flag && this.getOption("autoCollapse")) {
|
|
4487
|
+
this.collapseSiblings(options);
|
|
4488
|
+
}
|
|
4335
4489
|
if (flag && this.lazy && this.children == null) {
|
|
4336
4490
|
await this.loadLazy();
|
|
4337
4491
|
}
|
|
4338
4492
|
this.expanded = flag;
|
|
4339
4493
|
const updateOpts = { immediate: immediate };
|
|
4340
4494
|
// const updateOpts = { immediate: !!util.getOption(options, "immediate") };
|
|
4341
|
-
this.tree.
|
|
4495
|
+
this.tree.update(ChangeType.structure, updateOpts);
|
|
4342
4496
|
if (flag && scrollIntoView !== false) {
|
|
4343
4497
|
const lastChild = this.getLastChild();
|
|
4344
4498
|
if (lastChild) {
|
|
@@ -4355,18 +4509,25 @@
|
|
|
4355
4509
|
assert(!!flag, "blur is not yet implemented");
|
|
4356
4510
|
const prev = this.tree.focusNode;
|
|
4357
4511
|
this.tree.focusNode = this;
|
|
4358
|
-
prev === null || prev === void 0 ? void 0 : prev.
|
|
4359
|
-
this.
|
|
4512
|
+
prev === null || prev === void 0 ? void 0 : prev.update();
|
|
4513
|
+
this.update();
|
|
4360
4514
|
}
|
|
4361
4515
|
/** Set a new icon path or class. */
|
|
4362
4516
|
setIcon(icon) {
|
|
4363
4517
|
this.icon = icon;
|
|
4364
|
-
this.
|
|
4518
|
+
this.update();
|
|
4365
4519
|
}
|
|
4366
4520
|
/** Change node's {@link key} and/or {@link refKey}. */
|
|
4367
4521
|
setKey(key, refKey) {
|
|
4368
4522
|
throw new Error("Not yet implemented");
|
|
4369
4523
|
}
|
|
4524
|
+
/**
|
|
4525
|
+
* @deprecated since v0.3.6: use `update()` instead.
|
|
4526
|
+
*/
|
|
4527
|
+
setModified(change = ChangeType.data) {
|
|
4528
|
+
this.logWarn("setModified() is deprecated: use update() instead.");
|
|
4529
|
+
return this.update(change);
|
|
4530
|
+
}
|
|
4370
4531
|
/**
|
|
4371
4532
|
* Trigger a repaint, typically after a status or data change.
|
|
4372
4533
|
*
|
|
@@ -4374,22 +4535,219 @@
|
|
|
4374
4535
|
* and column content. It can be reduced to 'ChangeType.status' if only
|
|
4375
4536
|
* active/focus/selected state has changed.
|
|
4376
4537
|
*
|
|
4377
|
-
* This method will eventually call {@link WunderbaumNode.
|
|
4538
|
+
* This method will eventually call {@link WunderbaumNode._render()} with
|
|
4378
4539
|
* default options, but may be more consistent with the tree's
|
|
4379
|
-
* {@link Wunderbaum.
|
|
4540
|
+
* {@link Wunderbaum.update()} API.
|
|
4380
4541
|
*/
|
|
4381
|
-
|
|
4542
|
+
update(change = ChangeType.data) {
|
|
4382
4543
|
assert(change === ChangeType.status || change === ChangeType.data);
|
|
4383
|
-
this.tree.
|
|
4544
|
+
this.tree.update(change, this);
|
|
4545
|
+
}
|
|
4546
|
+
/**
|
|
4547
|
+
* Return an array of selected nodes.
|
|
4548
|
+
* @param stopOnParents only return the topmost selected node (useful with selectMode 'hier')
|
|
4549
|
+
*/
|
|
4550
|
+
getSelectedNodes(stopOnParents = false) {
|
|
4551
|
+
let nodeList = [];
|
|
4552
|
+
this.visit((node) => {
|
|
4553
|
+
if (node.selected) {
|
|
4554
|
+
nodeList.push(node);
|
|
4555
|
+
if (stopOnParents === true) {
|
|
4556
|
+
return "skip"; // stop processing this branch
|
|
4557
|
+
}
|
|
4558
|
+
}
|
|
4559
|
+
});
|
|
4560
|
+
return nodeList;
|
|
4561
|
+
}
|
|
4562
|
+
/** Toggle the check/uncheck state. */
|
|
4563
|
+
toggleSelected(options) {
|
|
4564
|
+
let flag = this.isSelected();
|
|
4565
|
+
if (flag === undefined) {
|
|
4566
|
+
flag = this._anySelectable();
|
|
4567
|
+
}
|
|
4568
|
+
else {
|
|
4569
|
+
flag = !flag;
|
|
4570
|
+
}
|
|
4571
|
+
return this.setSelected(flag, options);
|
|
4572
|
+
}
|
|
4573
|
+
/** Return true if at least on selectable descendant end-node is unselected. @internal */
|
|
4574
|
+
_anySelectable() {
|
|
4575
|
+
let found = false;
|
|
4576
|
+
this.visit((node) => {
|
|
4577
|
+
if (node.selected === false &&
|
|
4578
|
+
!node.unselectable &&
|
|
4579
|
+
!node.hasChildren() &&
|
|
4580
|
+
!node.parent.radiogroup) {
|
|
4581
|
+
found = true;
|
|
4582
|
+
return false; // Stop iteration
|
|
4583
|
+
}
|
|
4584
|
+
});
|
|
4585
|
+
return found;
|
|
4586
|
+
}
|
|
4587
|
+
/* Apply selection state to a single node. */
|
|
4588
|
+
_changeSelectStatusProps(state) {
|
|
4589
|
+
let changed = false;
|
|
4590
|
+
switch (state) {
|
|
4591
|
+
case false:
|
|
4592
|
+
changed = this.selected || this._partsel;
|
|
4593
|
+
this.selected = false;
|
|
4594
|
+
this._partsel = false;
|
|
4595
|
+
break;
|
|
4596
|
+
case true:
|
|
4597
|
+
changed = !this.selected || !this._partsel;
|
|
4598
|
+
this.selected = true;
|
|
4599
|
+
this._partsel = true;
|
|
4600
|
+
break;
|
|
4601
|
+
case undefined:
|
|
4602
|
+
changed = this.selected || !this._partsel;
|
|
4603
|
+
this.selected = false;
|
|
4604
|
+
this._partsel = true;
|
|
4605
|
+
break;
|
|
4606
|
+
default:
|
|
4607
|
+
error(`Invalid state: ${state}`);
|
|
4608
|
+
}
|
|
4609
|
+
if (changed) {
|
|
4610
|
+
this.update();
|
|
4611
|
+
}
|
|
4612
|
+
return changed;
|
|
4613
|
+
}
|
|
4614
|
+
/**
|
|
4615
|
+
* Fix selection status, after this node was (de)selected in `selectMode: 'hier'`.
|
|
4616
|
+
* This includes (de)selecting all descendants.
|
|
4617
|
+
*/
|
|
4618
|
+
fixSelection3AfterClick(opts) {
|
|
4619
|
+
const force = !!(opts === null || opts === void 0 ? void 0 : opts.force);
|
|
4620
|
+
let flag = this.isSelected();
|
|
4621
|
+
this.visit((node) => {
|
|
4622
|
+
if (node.radiogroup) {
|
|
4623
|
+
return "skip"; // Don't (de)select this branch
|
|
4624
|
+
}
|
|
4625
|
+
if (force || !node.getOption("unselectable")) {
|
|
4626
|
+
node._changeSelectStatusProps(flag);
|
|
4627
|
+
}
|
|
4628
|
+
});
|
|
4629
|
+
this.fixSelection3FromEndNodes();
|
|
4630
|
+
}
|
|
4631
|
+
/**
|
|
4632
|
+
* Fix selection status for multi-hier mode.
|
|
4633
|
+
* Only end-nodes are considered to update the descendants branch and parents.
|
|
4634
|
+
* Should be called after this node has loaded new children or after
|
|
4635
|
+
* children have been modified using the API.
|
|
4636
|
+
*/
|
|
4637
|
+
fixSelection3FromEndNodes(opts) {
|
|
4638
|
+
const force = !!(opts === null || opts === void 0 ? void 0 : opts.force);
|
|
4639
|
+
assert(this.tree.options.selectMode === "hier", "expected selectMode 'hier'");
|
|
4640
|
+
// Visit all end nodes and adjust their parent's `selected` and `_partsel`
|
|
4641
|
+
// attributes. Return selection state true, false, or undefined.
|
|
4642
|
+
const _walk = (node) => {
|
|
4643
|
+
let state;
|
|
4644
|
+
const children = node.children;
|
|
4645
|
+
if (children && children.length) {
|
|
4646
|
+
// check all children recursively
|
|
4647
|
+
let allSelected = true;
|
|
4648
|
+
let someSelected = false;
|
|
4649
|
+
for (let i = 0, l = children.length; i < l; i++) {
|
|
4650
|
+
const child = children[i];
|
|
4651
|
+
// the selection state of a node is not relevant; we need the end-nodes
|
|
4652
|
+
const s = _walk(child);
|
|
4653
|
+
if (s !== false) {
|
|
4654
|
+
someSelected = true;
|
|
4655
|
+
}
|
|
4656
|
+
if (s !== true) {
|
|
4657
|
+
allSelected = false;
|
|
4658
|
+
}
|
|
4659
|
+
}
|
|
4660
|
+
state = allSelected ? true : someSelected ? undefined : false;
|
|
4661
|
+
}
|
|
4662
|
+
else {
|
|
4663
|
+
// This is an end-node: simply report the status
|
|
4664
|
+
state = !!node.selected;
|
|
4665
|
+
}
|
|
4666
|
+
// #939: Keep a `_partsel` flag that was explicitly set on a lazy node
|
|
4667
|
+
if (node._partsel &&
|
|
4668
|
+
!node.selected &&
|
|
4669
|
+
node.lazy &&
|
|
4670
|
+
node.children == null) {
|
|
4671
|
+
state = undefined;
|
|
4672
|
+
}
|
|
4673
|
+
if (force || !node.getOption("unselectable")) {
|
|
4674
|
+
node._changeSelectStatusProps(state);
|
|
4675
|
+
}
|
|
4676
|
+
return state;
|
|
4677
|
+
};
|
|
4678
|
+
_walk(this);
|
|
4679
|
+
// Update parent's state
|
|
4680
|
+
this.visitParents((node) => {
|
|
4681
|
+
let state;
|
|
4682
|
+
const children = node.children;
|
|
4683
|
+
let allSelected = true;
|
|
4684
|
+
let someSelected = false;
|
|
4685
|
+
for (let i = 0, l = children.length; i < l; i++) {
|
|
4686
|
+
const child = children[i];
|
|
4687
|
+
state = !!child.selected;
|
|
4688
|
+
// When fixing the parents, we trust the sibling status (i.e. we don't recurse)
|
|
4689
|
+
if (state || child._partsel) {
|
|
4690
|
+
someSelected = true;
|
|
4691
|
+
}
|
|
4692
|
+
if (!state) {
|
|
4693
|
+
allSelected = false;
|
|
4694
|
+
}
|
|
4695
|
+
}
|
|
4696
|
+
state = allSelected ? true : someSelected ? undefined : false;
|
|
4697
|
+
node._changeSelectStatusProps(state);
|
|
4698
|
+
});
|
|
4384
4699
|
}
|
|
4385
4700
|
/** Modify the check/uncheck state. */
|
|
4386
4701
|
setSelected(flag = true, options) {
|
|
4387
|
-
const
|
|
4388
|
-
|
|
4702
|
+
const tree = this.tree;
|
|
4703
|
+
const sendEvents = !(options === null || options === void 0 ? void 0 : options.noEvents); // Default: send events
|
|
4704
|
+
const prev = this.isSelected();
|
|
4705
|
+
const isRadio = this.parent && this.parent.radiogroup;
|
|
4706
|
+
const selectMode = tree.options.selectMode;
|
|
4707
|
+
const canSelect = (options === null || options === void 0 ? void 0 : options.force) || !this.getOption("unselectable");
|
|
4708
|
+
flag = !!flag;
|
|
4709
|
+
// this.logDebug(`setSelected(${flag})`, this);
|
|
4710
|
+
if (!canSelect) {
|
|
4711
|
+
return prev;
|
|
4712
|
+
}
|
|
4713
|
+
if ((options === null || options === void 0 ? void 0 : options.propagateDown) && selectMode === "multi") {
|
|
4714
|
+
tree.runWithDeferredUpdate(() => {
|
|
4715
|
+
this.visit((node) => {
|
|
4716
|
+
node.setSelected(flag);
|
|
4717
|
+
});
|
|
4718
|
+
});
|
|
4719
|
+
return prev;
|
|
4720
|
+
}
|
|
4721
|
+
if (flag === prev ||
|
|
4722
|
+
(sendEvents && this._callEvent("beforeSelect", { flag: flag }) === false)) {
|
|
4723
|
+
return prev;
|
|
4724
|
+
}
|
|
4725
|
+
tree.runWithDeferredUpdate(() => {
|
|
4726
|
+
if (isRadio) {
|
|
4727
|
+
// Radiobutton Group
|
|
4728
|
+
if (!flag && !(options === null || options === void 0 ? void 0 : options.force)) {
|
|
4729
|
+
return prev; // don't uncheck radio buttons
|
|
4730
|
+
}
|
|
4731
|
+
for (let sibling of this.parent.children) {
|
|
4732
|
+
sibling.selected = sibling === this;
|
|
4733
|
+
}
|
|
4734
|
+
}
|
|
4735
|
+
else {
|
|
4736
|
+
this.selected = flag;
|
|
4737
|
+
if (selectMode === "hier") {
|
|
4738
|
+
this.fixSelection3AfterClick();
|
|
4739
|
+
}
|
|
4740
|
+
else if (selectMode === "single") {
|
|
4741
|
+
tree.visit((n) => {
|
|
4742
|
+
n.selected = false;
|
|
4743
|
+
});
|
|
4744
|
+
}
|
|
4745
|
+
}
|
|
4746
|
+
});
|
|
4747
|
+
if (sendEvents) {
|
|
4389
4748
|
this._callEvent("select", { flag: flag });
|
|
4390
4749
|
}
|
|
4391
|
-
|
|
4392
|
-
this.setModified();
|
|
4750
|
+
return prev;
|
|
4393
4751
|
}
|
|
4394
4752
|
/** Display node status (ok, loading, error, noData) using styles and a dummy child node. */
|
|
4395
4753
|
setStatus(status, options) {
|
|
@@ -4414,7 +4772,7 @@
|
|
|
4414
4772
|
assert(!firstChild || !firstChild.isStatusNode());
|
|
4415
4773
|
statusNode = this.addNode(data, "prependChild");
|
|
4416
4774
|
statusNode.match = true;
|
|
4417
|
-
tree.
|
|
4775
|
+
tree.update(ChangeType.structure);
|
|
4418
4776
|
return statusNode;
|
|
4419
4777
|
};
|
|
4420
4778
|
_clearStatusNode();
|
|
@@ -4427,7 +4785,7 @@
|
|
|
4427
4785
|
this._isLoading = true;
|
|
4428
4786
|
this._errorInfo = null;
|
|
4429
4787
|
if (this.parent) {
|
|
4430
|
-
this.
|
|
4788
|
+
this.update(ChangeType.status);
|
|
4431
4789
|
}
|
|
4432
4790
|
else {
|
|
4433
4791
|
// If this is the invisible root, add a visible top-level node
|
|
@@ -4440,7 +4798,7 @@
|
|
|
4440
4798
|
tooltip: details,
|
|
4441
4799
|
});
|
|
4442
4800
|
}
|
|
4443
|
-
// this.
|
|
4801
|
+
// this.update();
|
|
4444
4802
|
break;
|
|
4445
4803
|
case "error":
|
|
4446
4804
|
_setStatusNode({
|
|
@@ -4469,13 +4827,13 @@
|
|
|
4469
4827
|
default:
|
|
4470
4828
|
error("invalid node status " + status);
|
|
4471
4829
|
}
|
|
4472
|
-
tree.
|
|
4830
|
+
tree.update(ChangeType.structure);
|
|
4473
4831
|
return statusNode;
|
|
4474
4832
|
}
|
|
4475
4833
|
/** Rename this node. */
|
|
4476
4834
|
setTitle(title) {
|
|
4477
4835
|
this.title = title;
|
|
4478
|
-
this.
|
|
4836
|
+
this.update();
|
|
4479
4837
|
// this.triggerModify("rename"); // TODO
|
|
4480
4838
|
}
|
|
4481
4839
|
_sortChildren(cmp, deep) {
|
|
@@ -4500,7 +4858,7 @@
|
|
|
4500
4858
|
*/
|
|
4501
4859
|
sortChildren(cmp = nodeTitleSorter, deep = false) {
|
|
4502
4860
|
this._sortChildren(cmp || nodeTitleSorter, deep);
|
|
4503
|
-
this.tree.
|
|
4861
|
+
this.tree.update(ChangeType.structure);
|
|
4504
4862
|
// this.triggerModify("sort"); // TODO
|
|
4505
4863
|
}
|
|
4506
4864
|
/**
|
|
@@ -4528,7 +4886,7 @@
|
|
|
4528
4886
|
this.parent.triggerModifyChild(operation, this, extra);
|
|
4529
4887
|
}
|
|
4530
4888
|
/**
|
|
4531
|
-
* Call `callback(node)` for all
|
|
4889
|
+
* Call `callback(node)` for all descendant nodes in hierarchical order (depth-first, pre-order).
|
|
4532
4890
|
*
|
|
4533
4891
|
* Stop iteration, if fn() returns false. Skip current branch, if fn()
|
|
4534
4892
|
* returns "skip".<br>
|
|
@@ -4608,7 +4966,7 @@
|
|
|
4608
4966
|
/*!
|
|
4609
4967
|
* Wunderbaum - ext-edit
|
|
4610
4968
|
* Copyright (c) 2021-2023, Martin Wendt. Released under the MIT license.
|
|
4611
|
-
* v0.
|
|
4969
|
+
* v0.5.0, Fri, 15 Sep 2023 14:34:23 GMT (https://github.com/mar10/wunderbaum)
|
|
4612
4970
|
*/
|
|
4613
4971
|
// const START_MARKER = "\uFFF7";
|
|
4614
4972
|
class EditExtension extends WunderbaumExtension {
|
|
@@ -4836,7 +5194,7 @@
|
|
|
4836
5194
|
node === null || node === void 0 ? void 0 : node.setTitle(newValue);
|
|
4837
5195
|
// NOTE: At least on Safari, this render call triggers a scroll event
|
|
4838
5196
|
// probably because the focused input is replaced.
|
|
4839
|
-
this.curEditNode.
|
|
5197
|
+
this.curEditNode._render({ preventScroll: true });
|
|
4840
5198
|
this.curEditNode = null;
|
|
4841
5199
|
this.relatedNode = null;
|
|
4842
5200
|
this.tree.setFocus(); // restore focus that was in the input element
|
|
@@ -4851,7 +5209,7 @@
|
|
|
4851
5209
|
// Discard the embedded `<input>`
|
|
4852
5210
|
// NOTE: At least on Safari, this render call triggers a scroll event
|
|
4853
5211
|
// probably because the focused input is replaced.
|
|
4854
|
-
this.curEditNode.
|
|
5212
|
+
this.curEditNode._render({ preventScroll: true });
|
|
4855
5213
|
this.curEditNode = null;
|
|
4856
5214
|
this.relatedNode = null;
|
|
4857
5215
|
// We discarded the <input>, so we have to acquire keyboard focus again
|
|
@@ -4904,9 +5262,10 @@
|
|
|
4904
5262
|
* https://github.com/mar10/wunderbaum
|
|
4905
5263
|
*
|
|
4906
5264
|
* Released under the MIT license.
|
|
4907
|
-
* @version v0.
|
|
4908
|
-
* @date
|
|
5265
|
+
* @version v0.5.0
|
|
5266
|
+
* @date Fri, 15 Sep 2023 14:34:23 GMT
|
|
4909
5267
|
*/
|
|
5268
|
+
// import "./wunderbaum.scss";
|
|
4910
5269
|
class WbSystemRoot extends WunderbaumNode {
|
|
4911
5270
|
constructor(tree) {
|
|
4912
5271
|
super(tree, null, {
|
|
@@ -4948,6 +5307,9 @@
|
|
|
4948
5307
|
this.pendingChangeTypes = new Set();
|
|
4949
5308
|
/** Expose some useful methods of the util.ts module as `tree._util`. */
|
|
4950
5309
|
this._util = util;
|
|
5310
|
+
// --- SELECT ---
|
|
5311
|
+
// /** @internal */
|
|
5312
|
+
// public selectRangeAnchor: WunderbaumNode | null = null;
|
|
4951
5313
|
// --- FILTER ---
|
|
4952
5314
|
this.filterMode = null;
|
|
4953
5315
|
// --- KEYNAV ---
|
|
@@ -4973,6 +5335,7 @@
|
|
|
4973
5335
|
header: null,
|
|
4974
5336
|
// headerHeightPx: ROW_HEIGHT,
|
|
4975
5337
|
rowHeightPx: ROW_HEIGHT,
|
|
5338
|
+
iconMap: "bootstrap",
|
|
4976
5339
|
columns: null,
|
|
4977
5340
|
types: null,
|
|
4978
5341
|
// escapeTitles: true,
|
|
@@ -4982,9 +5345,10 @@
|
|
|
4982
5345
|
checkbox: false,
|
|
4983
5346
|
minExpandLevel: 0,
|
|
4984
5347
|
emptyChildListExpandable: false,
|
|
4985
|
-
updateThrottleWait: 200,
|
|
5348
|
+
// updateThrottleWait: 200,
|
|
4986
5349
|
skeleton: false,
|
|
4987
5350
|
connectTopBreadcrumb: null,
|
|
5351
|
+
selectMode: "multi",
|
|
4988
5352
|
// --- KeyNav ---
|
|
4989
5353
|
navigationModeOption: null,
|
|
4990
5354
|
quicksearch: true,
|
|
@@ -5140,26 +5504,34 @@
|
|
|
5140
5504
|
}
|
|
5141
5505
|
// Async mode is sometimes required, because this.element.clientWidth
|
|
5142
5506
|
// has a wrong value at start???
|
|
5143
|
-
this.
|
|
5507
|
+
this.update(ChangeType.any);
|
|
5144
5508
|
// --- Bind listeners
|
|
5145
5509
|
this.element.addEventListener("scroll", (e) => {
|
|
5146
5510
|
// this.log(`scroll, scrollTop:${e.target.scrollTop}`, e);
|
|
5147
|
-
this.
|
|
5511
|
+
this.update(ChangeType.scroll);
|
|
5148
5512
|
});
|
|
5149
5513
|
this.resizeObserver = new ResizeObserver((entries) => {
|
|
5150
5514
|
// this.log("ResizeObserver: Size changed", entries);
|
|
5151
|
-
this.
|
|
5515
|
+
this.update(ChangeType.resize);
|
|
5152
5516
|
});
|
|
5153
5517
|
this.resizeObserver.observe(this.element);
|
|
5154
5518
|
onEvent(this.nodeListElement, "click", "div.wb-row", (e) => {
|
|
5155
5519
|
const info = Wunderbaum.getEventInfo(e);
|
|
5156
5520
|
const node = info.node;
|
|
5157
|
-
|
|
5521
|
+
const mouseEvent = e;
|
|
5522
|
+
// this.log("click", info);
|
|
5523
|
+
// if (this._selectRange(info) === false) {
|
|
5524
|
+
// return;
|
|
5525
|
+
// }
|
|
5158
5526
|
if (this._callEvent("click", { event: e, node: node, info: info }) === false) {
|
|
5159
5527
|
this.lastClickTime = Date.now();
|
|
5160
5528
|
return false;
|
|
5161
5529
|
}
|
|
5162
5530
|
if (node) {
|
|
5531
|
+
if (mouseEvent.ctrlKey) {
|
|
5532
|
+
node.toggleSelected();
|
|
5533
|
+
return;
|
|
5534
|
+
}
|
|
5163
5535
|
// Edit title if 'clickActive' is triggered:
|
|
5164
5536
|
const trigger = this.getOption("edit.trigger");
|
|
5165
5537
|
const slowClickDelay = this.getOption("edit.slowClickDelay");
|
|
@@ -5179,7 +5551,7 @@
|
|
|
5179
5551
|
node.setExpanded(!node.isExpanded());
|
|
5180
5552
|
}
|
|
5181
5553
|
else if (info.region === NodeRegion.checkbox) {
|
|
5182
|
-
node.
|
|
5554
|
+
node.toggleSelected();
|
|
5183
5555
|
}
|
|
5184
5556
|
}
|
|
5185
5557
|
this.lastClickTime = Date.now();
|
|
@@ -5278,6 +5650,16 @@
|
|
|
5278
5650
|
}
|
|
5279
5651
|
return null;
|
|
5280
5652
|
}
|
|
5653
|
+
/**
|
|
5654
|
+
* Return the icon-function -> icon-definition mapping.
|
|
5655
|
+
*/
|
|
5656
|
+
get iconMap() {
|
|
5657
|
+
const map = this.options.iconMap;
|
|
5658
|
+
if (typeof map === "string") {
|
|
5659
|
+
return iconMaps[map];
|
|
5660
|
+
}
|
|
5661
|
+
return map;
|
|
5662
|
+
}
|
|
5281
5663
|
/**
|
|
5282
5664
|
* Return a WunderbaumNode instance from element or event.
|
|
5283
5665
|
*/
|
|
@@ -5626,7 +6008,7 @@
|
|
|
5626
6008
|
// public cellNavMode = false;
|
|
5627
6009
|
// public lastQuicksearchTime = 0;
|
|
5628
6010
|
// public lastQuicksearchTerm = "";
|
|
5629
|
-
this.
|
|
6011
|
+
this.update(ChangeType.structure);
|
|
5630
6012
|
}
|
|
5631
6013
|
/**
|
|
5632
6014
|
* Clear nodes and markup and detach events and observers.
|
|
@@ -5684,7 +6066,7 @@
|
|
|
5684
6066
|
this.options[name] = value;
|
|
5685
6067
|
switch (name) {
|
|
5686
6068
|
case "checkbox":
|
|
5687
|
-
this.
|
|
6069
|
+
this.update(ChangeType.any);
|
|
5688
6070
|
break;
|
|
5689
6071
|
case "enabled":
|
|
5690
6072
|
this.setEnabled(!!value);
|
|
@@ -5707,8 +6089,15 @@
|
|
|
5707
6089
|
const header = this.options.header;
|
|
5708
6090
|
return this.isGrid() ? header !== false : !!header;
|
|
5709
6091
|
}
|
|
5710
|
-
/** Run code, but defer rendering of viewport until done.
|
|
5711
|
-
|
|
6092
|
+
/** Run code, but defer rendering of viewport until done.
|
|
6093
|
+
*
|
|
6094
|
+
* ```
|
|
6095
|
+
* tree.runWithDeferredUpdate(() => {
|
|
6096
|
+
* return someFuncThatWouldUpdateManyNodes();
|
|
6097
|
+
* });
|
|
6098
|
+
* ```
|
|
6099
|
+
*/
|
|
6100
|
+
runWithDeferredUpdate(func, hint = null) {
|
|
5712
6101
|
try {
|
|
5713
6102
|
this.enableUpdate(false);
|
|
5714
6103
|
const res = func();
|
|
@@ -5719,29 +6108,66 @@
|
|
|
5719
6108
|
this.enableUpdate(true);
|
|
5720
6109
|
}
|
|
5721
6110
|
}
|
|
5722
|
-
/** Recursively expand all expandable nodes (triggers lazy load
|
|
6111
|
+
/** Recursively expand all expandable nodes (triggers lazy load if needed). */
|
|
5723
6112
|
async expandAll(flag = true, options) {
|
|
5724
6113
|
await this.root.expandAll(flag, options);
|
|
5725
6114
|
}
|
|
5726
6115
|
/** Recursively select all nodes. */
|
|
5727
6116
|
selectAll(flag = true) {
|
|
5728
|
-
|
|
5729
|
-
|
|
5730
|
-
|
|
5731
|
-
|
|
5732
|
-
|
|
5733
|
-
|
|
5734
|
-
|
|
5735
|
-
|
|
5736
|
-
|
|
6117
|
+
return this.root.setSelected(flag, { propagateDown: true });
|
|
6118
|
+
}
|
|
6119
|
+
/** Toggle select all nodes. */
|
|
6120
|
+
toggleSelect() {
|
|
6121
|
+
this.selectAll(this.root._anySelectable());
|
|
6122
|
+
}
|
|
6123
|
+
/**
|
|
6124
|
+
* Return an array of selected nodes.
|
|
6125
|
+
* @param stopOnParents only return the topmost selected node (useful with selectMode 'hier')
|
|
6126
|
+
*/
|
|
6127
|
+
getSelectedNodes(stopOnParents = false) {
|
|
6128
|
+
return this.root.getSelectedNodes(stopOnParents);
|
|
6129
|
+
}
|
|
6130
|
+
/*
|
|
6131
|
+
* Return an array of selected nodes.
|
|
6132
|
+
*/
|
|
6133
|
+
_selectRange(eventInfo) {
|
|
6134
|
+
this.logDebug("_selectRange", eventInfo);
|
|
6135
|
+
error("Not yet implemented.");
|
|
6136
|
+
// const mode = this.options.selectMode!;
|
|
6137
|
+
// if (mode !== "multi") {
|
|
6138
|
+
// this.logDebug(`Range selection only available for selectMode 'multi'`);
|
|
6139
|
+
// return;
|
|
6140
|
+
// }
|
|
6141
|
+
// if (eventInfo.canonicalName === "Meta+click") {
|
|
6142
|
+
// eventInfo.node?.toggleSelected();
|
|
6143
|
+
// return false; // don't
|
|
6144
|
+
// } else if (eventInfo.canonicalName === "Shift+click") {
|
|
6145
|
+
// let from = this.activeNode;
|
|
6146
|
+
// let to = eventInfo.node;
|
|
6147
|
+
// if (!from || !to || from === to) {
|
|
6148
|
+
// return;
|
|
6149
|
+
// }
|
|
6150
|
+
// this.runWithDeferredUpdate(() => {
|
|
6151
|
+
// this.visitRows(
|
|
6152
|
+
// (node) => {
|
|
6153
|
+
// node.setSelected();
|
|
6154
|
+
// },
|
|
6155
|
+
// {
|
|
6156
|
+
// includeHidden: true,
|
|
6157
|
+
// includeSelf: false,
|
|
6158
|
+
// start: from,
|
|
6159
|
+
// reverse: from!._rowIdx! > to!._rowIdx!,
|
|
6160
|
+
// }
|
|
6161
|
+
// );
|
|
6162
|
+
// });
|
|
6163
|
+
// return false;
|
|
6164
|
+
// }
|
|
5737
6165
|
}
|
|
5738
|
-
/** Return the number of nodes in the data model
|
|
6166
|
+
/** Return the number of nodes in the data model.
|
|
6167
|
+
* @param visible if true, nodes that are hidden due to collapsed parents are ignored.
|
|
6168
|
+
*/
|
|
5739
6169
|
count(visible = false) {
|
|
5740
|
-
|
|
5741
|
-
return this.treeRowCount;
|
|
5742
|
-
// return this.viewNodes.size;
|
|
5743
|
-
}
|
|
5744
|
-
return this.keyMap.size;
|
|
6170
|
+
return visible ? this.treeRowCount : this.keyMap.size;
|
|
5745
6171
|
}
|
|
5746
6172
|
/** @internal sanity check. */
|
|
5747
6173
|
_check() {
|
|
@@ -5832,7 +6258,7 @@
|
|
|
5832
6258
|
break;
|
|
5833
6259
|
case "first":
|
|
5834
6260
|
// First visible node
|
|
5835
|
-
this.visit(
|
|
6261
|
+
this.visit((n) => {
|
|
5836
6262
|
if (n.isVisible()) {
|
|
5837
6263
|
res = n;
|
|
5838
6264
|
return false;
|
|
@@ -5840,7 +6266,7 @@
|
|
|
5840
6266
|
});
|
|
5841
6267
|
break;
|
|
5842
6268
|
case "last":
|
|
5843
|
-
this.visit(
|
|
6269
|
+
this.visit((n) => {
|
|
5844
6270
|
// last visible node
|
|
5845
6271
|
if (n.isVisible()) {
|
|
5846
6272
|
res = n;
|
|
@@ -5972,6 +6398,8 @@
|
|
|
5972
6398
|
*/
|
|
5973
6399
|
static getEventInfo(event) {
|
|
5974
6400
|
let target = event.target, cl = target.classList, parentCol = target.closest("span.wb-col"), node = Wunderbaum.getNode(target), tree = node ? node.tree : Wunderbaum.getTree(event), res = {
|
|
6401
|
+
event: event,
|
|
6402
|
+
canonicalName: eventToString(event),
|
|
5975
6403
|
tree: tree,
|
|
5976
6404
|
node: node,
|
|
5977
6405
|
region: NodeRegion.unknown,
|
|
@@ -6137,7 +6565,7 @@
|
|
|
6137
6565
|
// Make sure the topNode is always visible
|
|
6138
6566
|
this.scrollTo(topNode);
|
|
6139
6567
|
}
|
|
6140
|
-
// this.
|
|
6568
|
+
// this.update(ChangeType.scroll);
|
|
6141
6569
|
}
|
|
6142
6570
|
}
|
|
6143
6571
|
/**
|
|
@@ -6165,7 +6593,7 @@
|
|
|
6165
6593
|
// util.assert(node._rowIdx != null);
|
|
6166
6594
|
this.log(`scrollToHorz(${this.activeColIdx}): ${colLeft}..${colRight}, fixedOfs=${fixedWidth}, vpWidth=${vpWidth}, curLeft=${scrollLeft} -> ${newLeft}`);
|
|
6167
6595
|
this.element.scrollLeft = newLeft;
|
|
6168
|
-
// this.
|
|
6596
|
+
// this.update(ChangeType.scroll);
|
|
6169
6597
|
}
|
|
6170
6598
|
/**
|
|
6171
6599
|
* Set column #colIdx to 'active'.
|
|
@@ -6187,7 +6615,7 @@
|
|
|
6187
6615
|
}
|
|
6188
6616
|
}
|
|
6189
6617
|
}
|
|
6190
|
-
(_a = this.activeNode) === null || _a === void 0 ? void 0 : _a.
|
|
6618
|
+
(_a = this.activeNode) === null || _a === void 0 ? void 0 : _a.update(ChangeType.status);
|
|
6191
6619
|
// Update `wb-active` class for all cell spans
|
|
6192
6620
|
for (let rowDiv of this.nodeListElement.children) {
|
|
6193
6621
|
let i = 0;
|
|
@@ -6214,16 +6642,25 @@
|
|
|
6214
6642
|
this.element.blur();
|
|
6215
6643
|
}
|
|
6216
6644
|
}
|
|
6217
|
-
|
|
6645
|
+
/**
|
|
6646
|
+
* @deprecated since v0.3.6: use `update()` instead.
|
|
6647
|
+
*/
|
|
6648
|
+
setModified(change, ...args) {
|
|
6649
|
+
this.logWarn("setModified() is deprecated: use update() instead.");
|
|
6650
|
+
// @ts-ignore
|
|
6651
|
+
// (!) TS2556: A spread argument must either have a tuple type or be passed to a rest parameter.
|
|
6652
|
+
return this.update.call(this, change, ...args);
|
|
6653
|
+
}
|
|
6654
|
+
update(change, node, options) {
|
|
6218
6655
|
if (this._disableUpdateCount) {
|
|
6219
6656
|
// Assuming that we redraw all when enableUpdate() is re-enabled.
|
|
6220
6657
|
// this.log(
|
|
6221
|
-
// `IGNORED
|
|
6658
|
+
// `IGNORED update(${change}) node=${node} (disable level ${this._disableUpdateCount})`
|
|
6222
6659
|
// );
|
|
6223
6660
|
this._disableUpdateIgnoreCount++;
|
|
6224
6661
|
return;
|
|
6225
6662
|
}
|
|
6226
|
-
// this.log(`
|
|
6663
|
+
// this.log(`update(${change}) node=${node}`);
|
|
6227
6664
|
if (!(node instanceof WunderbaumNode)) {
|
|
6228
6665
|
options = node;
|
|
6229
6666
|
node = null;
|
|
@@ -6257,7 +6694,7 @@
|
|
|
6257
6694
|
// Single nodes are immediately updated if already inside the viewport
|
|
6258
6695
|
// (otherwise we can ignore)
|
|
6259
6696
|
if (node._rowElem) {
|
|
6260
|
-
node.
|
|
6697
|
+
node._render({ change: change });
|
|
6261
6698
|
}
|
|
6262
6699
|
break;
|
|
6263
6700
|
default:
|
|
@@ -6315,7 +6752,7 @@
|
|
|
6315
6752
|
this.setColumn(0);
|
|
6316
6753
|
}
|
|
6317
6754
|
this.element.classList.toggle("wb-cell-mode", flag);
|
|
6318
|
-
(_a = this.activeNode) === null || _a === void 0 ? void 0 : _a.
|
|
6755
|
+
(_a = this.activeNode) === null || _a === void 0 ? void 0 : _a.update(ChangeType.status);
|
|
6319
6756
|
}
|
|
6320
6757
|
/** Set the tree's navigation mode option. */
|
|
6321
6758
|
setNavigationOption(mode, reset = false) {
|
|
@@ -6479,7 +6916,7 @@
|
|
|
6479
6916
|
// if (modified) {
|
|
6480
6917
|
// this._renderHeaderMarkup();
|
|
6481
6918
|
// if (options.renderMarkup) {
|
|
6482
|
-
// this.
|
|
6919
|
+
// this.update(ChangeType.header, { removeMarkup: true });
|
|
6483
6920
|
// } else if (options.updateRows) {
|
|
6484
6921
|
// this._updateRows();
|
|
6485
6922
|
// }
|
|
@@ -6531,11 +6968,11 @@
|
|
|
6531
6968
|
}
|
|
6532
6969
|
}
|
|
6533
6970
|
/**
|
|
6534
|
-
* Render pending changes that were scheduled using {@link WunderbaumNode.
|
|
6971
|
+
* Render pending changes that were scheduled using {@link WunderbaumNode.update} if any.
|
|
6535
6972
|
*
|
|
6536
6973
|
* This is hardly ever neccessary, since we normally either
|
|
6537
|
-
* - call `
|
|
6538
|
-
* - call `
|
|
6974
|
+
* - call `update(ChangeType.TYPE)` (async, throttled), or
|
|
6975
|
+
* - call `update(ChangeType.TYPE, {immediate: true})` (synchronous)
|
|
6539
6976
|
*
|
|
6540
6977
|
* `updatePendingModifications()` will only force immediate execution of
|
|
6541
6978
|
* pending async changes if any.
|
|
@@ -6550,7 +6987,7 @@
|
|
|
6550
6987
|
* It calls `updateColumns()` and `_updateRows()`.
|
|
6551
6988
|
*
|
|
6552
6989
|
* This protected method should not be called directly but via
|
|
6553
|
-
* {@link WunderbaumNode.
|
|
6990
|
+
* {@link WunderbaumNode.update}`, {@link Wunderbaum.update},
|
|
6554
6991
|
* or {@link Wunderbaum.updatePendingModifications}.
|
|
6555
6992
|
* @internal
|
|
6556
6993
|
*/
|
|
@@ -6705,7 +7142,7 @@
|
|
|
6705
7142
|
if (rowDiv) {
|
|
6706
7143
|
rowDiv.style.top = idx * ROW_HEIGHT + "px";
|
|
6707
7144
|
}
|
|
6708
|
-
node.
|
|
7145
|
+
node._render({ top: top, after: prevElem });
|
|
6709
7146
|
// node.log("render", top, prevElem, "=>", node._rowElem);
|
|
6710
7147
|
prevElem = node._rowElem;
|
|
6711
7148
|
}
|
|
@@ -6785,7 +7222,7 @@
|
|
|
6785
7222
|
if (node.children &&
|
|
6786
7223
|
node.children.length &&
|
|
6787
7224
|
(includeHidden || node.expanded)) {
|
|
6788
|
-
res = node.visit(
|
|
7225
|
+
res = node.visit((n) => {
|
|
6789
7226
|
if (n === stopNode) {
|
|
6790
7227
|
return false;
|
|
6791
7228
|
}
|
|
@@ -6905,7 +7342,7 @@
|
|
|
6905
7342
|
if (this._disableUpdateCount === 0) {
|
|
6906
7343
|
this.logDebug(`enableUpdate(): active again. Re-painting to catch up with ${this._disableUpdateIgnoreCount} ignored update requests...`);
|
|
6907
7344
|
this._disableUpdateIgnoreCount = 0;
|
|
6908
|
-
this.
|
|
7345
|
+
this.update(ChangeType.any, { immediate: true });
|
|
6909
7346
|
}
|
|
6910
7347
|
}
|
|
6911
7348
|
else {
|
|
@@ -6959,7 +7396,7 @@
|
|
|
6959
7396
|
}
|
|
6960
7397
|
Wunderbaum.sequence = 0;
|
|
6961
7398
|
/** Wunderbaum release version number "MAJOR.MINOR.PATCH". */
|
|
6962
|
-
Wunderbaum.version = "v0.
|
|
7399
|
+
Wunderbaum.version = "v0.5.0"; // Set to semver by 'grunt release'
|
|
6963
7400
|
/** Expose some useful methods of the util.ts module as `Wunderbaum.util`. */
|
|
6964
7401
|
Wunderbaum.util = util;
|
|
6965
7402
|
|