wunderbaum 0.0.7 → 0.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -1
- package/dist/wunderbaum.css +2 -2
- package/dist/wunderbaum.d.ts +186 -58
- package/dist/wunderbaum.esm.js +517 -150
- package/dist/wunderbaum.esm.min.js +23 -23
- package/dist/wunderbaum.esm.min.js.map +1 -1
- package/dist/wunderbaum.umd.js +517 -150
- package/dist/wunderbaum.umd.min.js +32 -32
- package/dist/wunderbaum.umd.min.js.map +1 -1
- package/package.json +1 -1
- package/src/common.ts +156 -14
- package/src/drag_observer.ts +4 -3
- package/src/types.ts +44 -19
- package/src/util.ts +9 -4
- package/src/wb_ext_dnd.ts +5 -0
- package/src/wb_ext_keynav.ts +24 -7
- package/src/wb_node.ts +273 -67
- package/src/wunderbaum.scss +23 -1
- package/src/wunderbaum.ts +111 -59
package/dist/wunderbaum.umd.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
/*!
|
|
8
8
|
* Wunderbaum - util
|
|
9
9
|
* Copyright (c) 2021-2022, Martin Wendt. Released under the MIT license.
|
|
10
|
-
* v0.0.
|
|
10
|
+
* v0.0.9, Mon, 31 Oct 2022 17:06:10 GMT (https://github.com/mar10/wunderbaum)
|
|
11
11
|
*/
|
|
12
12
|
/** @module util */
|
|
13
13
|
/** Readable names for `MouseEvent.button` */
|
|
@@ -287,11 +287,17 @@
|
|
|
287
287
|
case "week":
|
|
288
288
|
case "datetime":
|
|
289
289
|
case "datetime-local":
|
|
290
|
-
input.valueAsDate = value;
|
|
290
|
+
input.valueAsDate = new Date(value);
|
|
291
|
+
// input.valueAsDate = value; // breaks in Edge?
|
|
291
292
|
break;
|
|
292
293
|
case "number":
|
|
293
294
|
case "range":
|
|
294
|
-
|
|
295
|
+
if (value == null) {
|
|
296
|
+
input.value = value;
|
|
297
|
+
}
|
|
298
|
+
else {
|
|
299
|
+
input.valueAsNumber = value;
|
|
300
|
+
}
|
|
295
301
|
break;
|
|
296
302
|
case "radio":
|
|
297
303
|
error("Not implemented");
|
|
@@ -308,7 +314,7 @@
|
|
|
308
314
|
break;
|
|
309
315
|
case "text":
|
|
310
316
|
default:
|
|
311
|
-
input.value = value
|
|
317
|
+
input.value = value !== null && value !== void 0 ? value : "";
|
|
312
318
|
}
|
|
313
319
|
}
|
|
314
320
|
else if (tag === "SELECT") {
|
|
@@ -707,7 +713,7 @@
|
|
|
707
713
|
/*!
|
|
708
714
|
* Wunderbaum - types
|
|
709
715
|
* Copyright (c) 2021-2022, Martin Wendt. Released under the MIT license.
|
|
710
|
-
* v0.0.
|
|
716
|
+
* v0.0.9, Mon, 31 Oct 2022 17:06:10 GMT (https://github.com/mar10/wunderbaum)
|
|
711
717
|
*/
|
|
712
718
|
/** Possible values for `setModified()`. */
|
|
713
719
|
var ChangeType;
|
|
@@ -759,7 +765,7 @@
|
|
|
759
765
|
/*!
|
|
760
766
|
* Wunderbaum - wb_extension_base
|
|
761
767
|
* Copyright (c) 2021-2022, Martin Wendt. Released under the MIT license.
|
|
762
|
-
* v0.0.
|
|
768
|
+
* v0.0.9, Mon, 31 Oct 2022 17:06:10 GMT (https://github.com/mar10/wunderbaum)
|
|
763
769
|
*/
|
|
764
770
|
class WunderbaumExtension {
|
|
765
771
|
constructor(tree, id, defaults) {
|
|
@@ -1050,7 +1056,7 @@
|
|
|
1050
1056
|
/*!
|
|
1051
1057
|
* Wunderbaum - ext-filter
|
|
1052
1058
|
* Copyright (c) 2021-2022, Martin Wendt. Released under the MIT license.
|
|
1053
|
-
* v0.0.
|
|
1059
|
+
* v0.0.9, Mon, 31 Oct 2022 17:06:10 GMT (https://github.com/mar10/wunderbaum)
|
|
1054
1060
|
*/
|
|
1055
1061
|
const START_MARKER = "\uFFF7";
|
|
1056
1062
|
const END_MARKER = "\uFFF8";
|
|
@@ -1355,7 +1361,7 @@
|
|
|
1355
1361
|
/*!
|
|
1356
1362
|
* Wunderbaum - ext-keynav
|
|
1357
1363
|
* Copyright (c) 2021-2022, Martin Wendt. Released under the MIT license.
|
|
1358
|
-
* v0.0.
|
|
1364
|
+
* v0.0.9, Mon, 31 Oct 2022 17:06:10 GMT (https://github.com/mar10/wunderbaum)
|
|
1359
1365
|
*/
|
|
1360
1366
|
const QUICKSEARCH_DELAY = 500;
|
|
1361
1367
|
class KeynavExtension extends WunderbaumExtension {
|
|
@@ -1453,7 +1459,8 @@
|
|
|
1453
1459
|
if (!node.expanded && (node.children || node.lazy)) {
|
|
1454
1460
|
eventName = "Add"; // expand
|
|
1455
1461
|
}
|
|
1456
|
-
else if (navModeOption === NavigationOptions.
|
|
1462
|
+
else if (navModeOption === NavigationOptions.startCell ||
|
|
1463
|
+
navModeOption === NavigationOptions.startRow) {
|
|
1457
1464
|
tree.setCellNav();
|
|
1458
1465
|
return;
|
|
1459
1466
|
}
|
|
@@ -1606,17 +1613,31 @@
|
|
|
1606
1613
|
}
|
|
1607
1614
|
handled = true;
|
|
1608
1615
|
break;
|
|
1616
|
+
case "Home": // Generated by FN + ArrowLeft on Mac
|
|
1617
|
+
case "Meta+ArrowLeft":
|
|
1618
|
+
tree.setFocus(); // Blur prev. input if any
|
|
1619
|
+
if (!isColspan && tree.activeColIdx > 0) {
|
|
1620
|
+
tree.setColumn(0);
|
|
1621
|
+
}
|
|
1622
|
+
handled = true;
|
|
1623
|
+
break;
|
|
1624
|
+
case "End": // Generated by FN + ArrowRight on Mac
|
|
1625
|
+
case "Meta+ArrowRight":
|
|
1626
|
+
tree.setFocus(); // Blur prev. input if any
|
|
1627
|
+
if (!isColspan && tree.activeColIdx < tree.columns.length - 1) {
|
|
1628
|
+
tree.setColumn(tree.columns.length - 1);
|
|
1629
|
+
}
|
|
1630
|
+
handled = true;
|
|
1631
|
+
break;
|
|
1609
1632
|
case "ArrowDown":
|
|
1610
1633
|
case "ArrowUp":
|
|
1611
1634
|
case "Backspace":
|
|
1612
|
-
case "End":
|
|
1613
|
-
case "Home":
|
|
1614
|
-
case "Control+End":
|
|
1615
|
-
case "Control+Home":
|
|
1635
|
+
case "Control+End": // Generated by FN + Control + ArrowRight on Mac
|
|
1636
|
+
case "Control+Home": // Generated by FN + Control + Arrowleft on Mac
|
|
1616
1637
|
case "Meta+ArrowDown":
|
|
1617
1638
|
case "Meta+ArrowUp":
|
|
1618
|
-
case "PageDown":
|
|
1619
|
-
case "PageUp":
|
|
1639
|
+
case "PageDown": // Generated by FN + ArrowDown on Mac
|
|
1640
|
+
case "PageUp": // Generated by FN + ArrowUp on Mac
|
|
1620
1641
|
node.navigate(eventName, { activate: activate, event: event });
|
|
1621
1642
|
// if (isCellEditMode) {
|
|
1622
1643
|
// this._getEmbeddedInputElem(null, true); // set focus to input
|
|
@@ -1637,7 +1658,7 @@
|
|
|
1637
1658
|
/*!
|
|
1638
1659
|
* Wunderbaum - ext-logger
|
|
1639
1660
|
* Copyright (c) 2021-2022, Martin Wendt. Released under the MIT license.
|
|
1640
|
-
* v0.0.
|
|
1661
|
+
* v0.0.9, Mon, 31 Oct 2022 17:06:10 GMT (https://github.com/mar10/wunderbaum)
|
|
1641
1662
|
*/
|
|
1642
1663
|
class LoggerExtension extends WunderbaumExtension {
|
|
1643
1664
|
constructor(tree) {
|
|
@@ -1677,7 +1698,7 @@
|
|
|
1677
1698
|
/*!
|
|
1678
1699
|
* Wunderbaum - common
|
|
1679
1700
|
* Copyright (c) 2021-2022, Martin Wendt. Released under the MIT license.
|
|
1680
|
-
* v0.0.
|
|
1701
|
+
* v0.0.9, Mon, 31 Oct 2022 17:06:10 GMT (https://github.com/mar10/wunderbaum)
|
|
1681
1702
|
*/
|
|
1682
1703
|
const DEFAULT_DEBUGLEVEL = 4; // Replaced by rollup script
|
|
1683
1704
|
/**
|
|
@@ -1732,14 +1753,14 @@
|
|
|
1732
1753
|
};
|
|
1733
1754
|
/** Dict keys that are evaluated by source loader (others are added to `tree.data` instead). */
|
|
1734
1755
|
const RESERVED_TREE_SOURCE_KEYS = new Set([
|
|
1756
|
+
"_format",
|
|
1757
|
+
"_keyMap",
|
|
1758
|
+
"_positional",
|
|
1759
|
+
"_typeList",
|
|
1760
|
+
"_version",
|
|
1735
1761
|
"children",
|
|
1736
1762
|
"columns",
|
|
1737
|
-
"format",
|
|
1738
|
-
"keyMap",
|
|
1739
|
-
"positional",
|
|
1740
|
-
"typeList",
|
|
1741
1763
|
"types",
|
|
1742
|
-
"version", // reserved for future use
|
|
1743
1764
|
]);
|
|
1744
1765
|
// /** Key codes that trigger grid navigation, even when inside an input element. */
|
|
1745
1766
|
// export const INPUT_BREAKOUT_KEYS: Set<string> = new Set([
|
|
@@ -1773,11 +1794,22 @@
|
|
|
1773
1794
|
"-": "collapse",
|
|
1774
1795
|
Subtract: "collapse",
|
|
1775
1796
|
};
|
|
1776
|
-
/** Return a callback that returns true if the node title
|
|
1777
|
-
|
|
1778
|
-
|
|
1797
|
+
/** Return a callback that returns true if the node title matches the string
|
|
1798
|
+
* or regular expression.
|
|
1799
|
+
* @see {@link WunderbaumNode.findAll}
|
|
1800
|
+
*/
|
|
1801
|
+
function makeNodeTitleMatcher(match) {
|
|
1802
|
+
if (match instanceof RegExp) {
|
|
1803
|
+
return function (node) {
|
|
1804
|
+
return match.test(node.title);
|
|
1805
|
+
};
|
|
1806
|
+
}
|
|
1807
|
+
assert(typeof match === "string");
|
|
1808
|
+
// s = escapeRegex(s.toLowerCase());
|
|
1779
1809
|
return function (node) {
|
|
1780
|
-
return node.title
|
|
1810
|
+
return node.title === match;
|
|
1811
|
+
// console.log("match " + node, node.title.toLowerCase().indexOf(match))
|
|
1812
|
+
// return node.title.toLowerCase().indexOf(match) >= 0;
|
|
1781
1813
|
};
|
|
1782
1814
|
}
|
|
1783
1815
|
/** Return a callback that returns true if the node title starts with a string (case-insensitive). */
|
|
@@ -1788,11 +1820,125 @@
|
|
|
1788
1820
|
return reMatch.test(node.title);
|
|
1789
1821
|
};
|
|
1790
1822
|
}
|
|
1823
|
+
function unflattenSource(source) {
|
|
1824
|
+
var _a, _b, _c;
|
|
1825
|
+
const { _format, _keyMap, _positional, children } = source;
|
|
1826
|
+
if (_format !== "flat") {
|
|
1827
|
+
throw new Error(`Expected source._format: "flat", but got ${_format}`);
|
|
1828
|
+
}
|
|
1829
|
+
if (_positional && _positional.includes("children")) {
|
|
1830
|
+
throw new Error(`source._positional must not include "children": ${_positional}`);
|
|
1831
|
+
}
|
|
1832
|
+
// Inverse keyMap:
|
|
1833
|
+
let longToShort = {};
|
|
1834
|
+
if (_keyMap) {
|
|
1835
|
+
for (const [key, value] of Object.entries(_keyMap)) {
|
|
1836
|
+
longToShort[value] = key;
|
|
1837
|
+
}
|
|
1838
|
+
}
|
|
1839
|
+
const positionalShort = _positional.map((e) => longToShort[e]);
|
|
1840
|
+
const newChildren = [];
|
|
1841
|
+
const keyToNodeMap = {};
|
|
1842
|
+
const indexToNodeMap = {};
|
|
1843
|
+
const keyAttrName = (_a = longToShort["key"]) !== null && _a !== void 0 ? _a : "key";
|
|
1844
|
+
const childrenAttrName = (_b = longToShort["children"]) !== null && _b !== void 0 ? _b : "children";
|
|
1845
|
+
for (const [index, node] of children.entries()) {
|
|
1846
|
+
// Node entry format:
|
|
1847
|
+
// [PARENT_ID, [POSITIONAL_ARGS]]
|
|
1848
|
+
// or
|
|
1849
|
+
// [PARENT_ID, [POSITIONAL_ARGS], {KEY_VALUE_ARGS}]
|
|
1850
|
+
const [parentId, args, kwargs = {}] = node;
|
|
1851
|
+
// Free up some memory as we go
|
|
1852
|
+
node[1] = null;
|
|
1853
|
+
if (node[2] != null) {
|
|
1854
|
+
node[2] = null;
|
|
1855
|
+
}
|
|
1856
|
+
// console.log("flatten", parentId, args, kwargs)
|
|
1857
|
+
// We keep `kwargs` as our new node definition. Then we add all positional
|
|
1858
|
+
// values to this object:
|
|
1859
|
+
args.forEach((val, positionalIdx) => {
|
|
1860
|
+
kwargs[positionalShort[positionalIdx]] = val;
|
|
1861
|
+
});
|
|
1862
|
+
// Find the parent node. `null` means 'toplevel'. PARENT_ID may be the numeric
|
|
1863
|
+
// index of the source.children list. If PARENT_ID is a string, we search
|
|
1864
|
+
// a parent with node.key of this value.
|
|
1865
|
+
indexToNodeMap[index] = kwargs;
|
|
1866
|
+
const key = kwargs[keyAttrName];
|
|
1867
|
+
if (key != null) {
|
|
1868
|
+
keyToNodeMap[key] = kwargs;
|
|
1869
|
+
}
|
|
1870
|
+
let parentNode = null;
|
|
1871
|
+
if (parentId === null) ;
|
|
1872
|
+
else if (typeof parentId === "number") {
|
|
1873
|
+
parentNode = indexToNodeMap[parentId];
|
|
1874
|
+
if (parentNode === undefined) {
|
|
1875
|
+
throw new Error(`unflattenSource: Could not find parent node by index: ${parentId}.`);
|
|
1876
|
+
}
|
|
1877
|
+
}
|
|
1878
|
+
else {
|
|
1879
|
+
parentNode = keyToNodeMap[parentId];
|
|
1880
|
+
if (parentNode === undefined) {
|
|
1881
|
+
throw new Error(`unflattenSource: Could not find parent node by key: ${parentId}`);
|
|
1882
|
+
}
|
|
1883
|
+
}
|
|
1884
|
+
if (parentNode) {
|
|
1885
|
+
(_c = parentNode[childrenAttrName]) !== null && _c !== void 0 ? _c : (parentNode[childrenAttrName] = []);
|
|
1886
|
+
parentNode[childrenAttrName].push(kwargs);
|
|
1887
|
+
}
|
|
1888
|
+
else {
|
|
1889
|
+
newChildren.push(kwargs);
|
|
1890
|
+
}
|
|
1891
|
+
}
|
|
1892
|
+
delete source.children;
|
|
1893
|
+
source.children = newChildren;
|
|
1894
|
+
}
|
|
1895
|
+
function inflateSourceData(source) {
|
|
1896
|
+
const { _format, _keyMap, _typeList } = source;
|
|
1897
|
+
if (_format === "flat") {
|
|
1898
|
+
unflattenSource(source);
|
|
1899
|
+
}
|
|
1900
|
+
delete source._format;
|
|
1901
|
+
delete source._version;
|
|
1902
|
+
delete source._keyMap;
|
|
1903
|
+
delete source._typeList;
|
|
1904
|
+
delete source._positional;
|
|
1905
|
+
function _iter(childList) {
|
|
1906
|
+
for (let node of childList) {
|
|
1907
|
+
// Expand short alias names
|
|
1908
|
+
if (_keyMap) {
|
|
1909
|
+
// Iterate over a list of names, because we modify inside the loop:
|
|
1910
|
+
Object.getOwnPropertyNames(node).forEach((propName) => {
|
|
1911
|
+
var _a;
|
|
1912
|
+
const long = (_a = _keyMap[propName]) !== null && _a !== void 0 ? _a : propName;
|
|
1913
|
+
if (long !== propName) {
|
|
1914
|
+
node[long] = node[propName];
|
|
1915
|
+
delete node[propName];
|
|
1916
|
+
}
|
|
1917
|
+
});
|
|
1918
|
+
}
|
|
1919
|
+
// `node` now has long attribute names
|
|
1920
|
+
// Resolve node type indexes
|
|
1921
|
+
const type = node.type;
|
|
1922
|
+
if (_typeList && type != null && typeof type === "number") {
|
|
1923
|
+
const newType = _typeList[type];
|
|
1924
|
+
if (newType == null) {
|
|
1925
|
+
throw new Error(`Expected typeList[${type}] entry in [${_typeList}]`);
|
|
1926
|
+
}
|
|
1927
|
+
node.type = newType;
|
|
1928
|
+
}
|
|
1929
|
+
// Recursion
|
|
1930
|
+
if (node.children) {
|
|
1931
|
+
_iter(node.children);
|
|
1932
|
+
}
|
|
1933
|
+
}
|
|
1934
|
+
}
|
|
1935
|
+
_iter(source.children);
|
|
1936
|
+
}
|
|
1791
1937
|
|
|
1792
1938
|
/*!
|
|
1793
1939
|
* Wunderbaum - ext-dnd
|
|
1794
1940
|
* Copyright (c) 2021-2022, Martin Wendt. Released under the MIT license.
|
|
1795
|
-
* v0.0.
|
|
1941
|
+
* v0.0.9, Mon, 31 Oct 2022 17:06:10 GMT (https://github.com/mar10/wunderbaum)
|
|
1796
1942
|
*/
|
|
1797
1943
|
const nodeMimeType = "application/x-wunderbaum-node";
|
|
1798
1944
|
class DndExtension extends WunderbaumExtension {
|
|
@@ -1936,6 +2082,11 @@
|
|
|
1936
2082
|
if (e.type === "dragstart") {
|
|
1937
2083
|
// Set a default definition of allowed effects
|
|
1938
2084
|
e.dataTransfer.effectAllowed = dndOpts.effectAllowed; //"copyMove"; // "all";
|
|
2085
|
+
if (srcNode.isEditing()) {
|
|
2086
|
+
srcNode.logDebug("Prevented dragging node in edit mode.");
|
|
2087
|
+
e.preventDefault();
|
|
2088
|
+
return false;
|
|
2089
|
+
}
|
|
1939
2090
|
// Let user cancel the drag operation, override effectAllowed, etc.:
|
|
1940
2091
|
const res = srcNode._callEvent("dnd.dragStart", { event: e });
|
|
1941
2092
|
if (!res) {
|
|
@@ -2060,7 +2211,7 @@
|
|
|
2060
2211
|
/*!
|
|
2061
2212
|
* Wunderbaum - drag_observer
|
|
2062
2213
|
* Copyright (c) 2021-2022, Martin Wendt. Released under the MIT license.
|
|
2063
|
-
* v0.0.
|
|
2214
|
+
* v0.0.9, Mon, 31 Oct 2022 17:06:10 GMT (https://github.com/mar10/wunderbaum)
|
|
2064
2215
|
*/
|
|
2065
2216
|
/**
|
|
2066
2217
|
* Convert mouse- and touch events to 'dragstart', 'drag', and 'dragstop'.
|
|
@@ -2079,8 +2230,10 @@
|
|
|
2079
2230
|
this.dragging = false;
|
|
2080
2231
|
// TODO: touch events
|
|
2081
2232
|
this.events = ["mousedown", "mouseup", "mousemove", "keydown"];
|
|
2082
|
-
|
|
2083
|
-
|
|
2233
|
+
if (!opts.root) {
|
|
2234
|
+
throw new Error("Missing `root` option.");
|
|
2235
|
+
}
|
|
2236
|
+
this.opts = Object.assign({ thresh: 5 }, opts);
|
|
2084
2237
|
this.root = opts.root;
|
|
2085
2238
|
this._handler = this.handleEvent.bind(this);
|
|
2086
2239
|
this.events.forEach((type) => {
|
|
@@ -2194,7 +2347,7 @@
|
|
|
2194
2347
|
/*!
|
|
2195
2348
|
* Wunderbaum - ext-grid
|
|
2196
2349
|
* Copyright (c) 2021-2022, Martin Wendt. Released under the MIT license.
|
|
2197
|
-
* v0.0.
|
|
2350
|
+
* v0.0.9, Mon, 31 Oct 2022 17:06:10 GMT (https://github.com/mar10/wunderbaum)
|
|
2198
2351
|
*/
|
|
2199
2352
|
class GridExtension extends WunderbaumExtension {
|
|
2200
2353
|
constructor(tree) {
|
|
@@ -2231,7 +2384,7 @@
|
|
|
2231
2384
|
/*!
|
|
2232
2385
|
* Wunderbaum - deferred
|
|
2233
2386
|
* Copyright (c) 2021-2022, Martin Wendt. Released under the MIT license.
|
|
2234
|
-
* v0.0.
|
|
2387
|
+
* v0.0.9, Mon, 31 Oct 2022 17:06:10 GMT (https://github.com/mar10/wunderbaum)
|
|
2235
2388
|
*/
|
|
2236
2389
|
/**
|
|
2237
2390
|
* Implement a ES6 Promise, that exposes a resolve() and reject() method.
|
|
@@ -2284,7 +2437,7 @@
|
|
|
2284
2437
|
/*!
|
|
2285
2438
|
* Wunderbaum - wunderbaum_node
|
|
2286
2439
|
* Copyright (c) 2021-2022, Martin Wendt. Released under the MIT license.
|
|
2287
|
-
* v0.0.
|
|
2440
|
+
* v0.0.9, Mon, 31 Oct 2022 17:06:10 GMT (https://github.com/mar10/wunderbaum)
|
|
2288
2441
|
*/
|
|
2289
2442
|
/** Top-level properties that can be passed with `data`. */
|
|
2290
2443
|
const NODE_PROPS = new Set([
|
|
@@ -2395,6 +2548,35 @@
|
|
|
2395
2548
|
toString() {
|
|
2396
2549
|
return `WunderbaumNode@${this.key}<'${this.title}'>`;
|
|
2397
2550
|
}
|
|
2551
|
+
/**
|
|
2552
|
+
* Iterate all descendant nodes depth-first, pre-order using `for ... of ...` syntax.
|
|
2553
|
+
* More concise, but slightly slower than {@link WunderbaumNode.visit}.
|
|
2554
|
+
*
|
|
2555
|
+
* Example:
|
|
2556
|
+
* ```js
|
|
2557
|
+
* for(const n of node) {
|
|
2558
|
+
* ...
|
|
2559
|
+
* }
|
|
2560
|
+
* ```
|
|
2561
|
+
*/
|
|
2562
|
+
*[Symbol.iterator]() {
|
|
2563
|
+
// let node: WunderbaumNode | null = this;
|
|
2564
|
+
const cl = this.children;
|
|
2565
|
+
if (cl) {
|
|
2566
|
+
for (let i = 0, l = cl.length; i < l; i++) {
|
|
2567
|
+
const n = cl[i];
|
|
2568
|
+
yield n;
|
|
2569
|
+
if (n.children) {
|
|
2570
|
+
yield* n;
|
|
2571
|
+
}
|
|
2572
|
+
}
|
|
2573
|
+
// Slower:
|
|
2574
|
+
// for (let node of this.children) {
|
|
2575
|
+
// yield node;
|
|
2576
|
+
// yield* node : 0;
|
|
2577
|
+
// }
|
|
2578
|
+
}
|
|
2579
|
+
}
|
|
2398
2580
|
// /** Return an option value. */
|
|
2399
2581
|
// protected _getOpt(
|
|
2400
2582
|
// name: string,
|
|
@@ -2425,48 +2607,46 @@
|
|
|
2425
2607
|
/**
|
|
2426
2608
|
* Append (or insert) a list of child nodes.
|
|
2427
2609
|
*
|
|
2428
|
-
* Tip: pass `{ before: 0 }` to prepend children
|
|
2429
|
-
*
|
|
2430
|
-
* @param child node (or key or index of such).
|
|
2431
|
-
* If omitted, the new children are appended.
|
|
2610
|
+
* Tip: pass `{ before: 0 }` to prepend new nodes as first children.
|
|
2611
|
+
*
|
|
2432
2612
|
* @returns first child added
|
|
2433
2613
|
*/
|
|
2434
2614
|
addChildren(nodeData, options) {
|
|
2435
2615
|
const tree = this.tree;
|
|
2436
|
-
|
|
2437
|
-
let
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
nodeList = [];
|
|
2616
|
+
let { before = null, applyMinExpanLevel = true, _level } = options !== null && options !== void 0 ? options : {};
|
|
2617
|
+
// let { before, loadLazy=true, _level } = options ?? {};
|
|
2618
|
+
// const isTopCall = _level == null;
|
|
2619
|
+
_level !== null && _level !== void 0 ? _level : (_level = this.getLevel());
|
|
2620
|
+
const nodeList = [];
|
|
2442
2621
|
try {
|
|
2443
2622
|
tree.enableUpdate(false);
|
|
2444
2623
|
if (isPlainObject(nodeData)) {
|
|
2445
2624
|
nodeData = [nodeData];
|
|
2446
2625
|
}
|
|
2447
|
-
const forceExpand =
|
|
2626
|
+
const forceExpand = applyMinExpanLevel && _level < tree.options.minExpandLevel;
|
|
2448
2627
|
for (let child of nodeData) {
|
|
2449
|
-
|
|
2628
|
+
const subChildren = child.children;
|
|
2450
2629
|
delete child.children;
|
|
2451
|
-
|
|
2452
|
-
if (forceExpand && !n.
|
|
2630
|
+
const n = new WunderbaumNode(tree, this, child);
|
|
2631
|
+
if (forceExpand && !n.isUnloaded()) {
|
|
2453
2632
|
n.expanded = true;
|
|
2633
|
+
}
|
|
2454
2634
|
nodeList.push(n);
|
|
2455
2635
|
if (subChildren) {
|
|
2456
|
-
n.addChildren(subChildren, {
|
|
2636
|
+
n.addChildren(subChildren, { _level: _level + 1 });
|
|
2457
2637
|
}
|
|
2458
2638
|
}
|
|
2459
2639
|
if (!this.children) {
|
|
2460
2640
|
this.children = nodeList;
|
|
2461
2641
|
}
|
|
2462
|
-
else if (
|
|
2642
|
+
else if (before == null || this.children.length === 0) {
|
|
2463
2643
|
this.children = this.children.concat(nodeList);
|
|
2464
2644
|
}
|
|
2465
2645
|
else {
|
|
2466
|
-
// Returns null if
|
|
2467
|
-
|
|
2468
|
-
let pos = this.children.indexOf(
|
|
2469
|
-
assert(pos >= 0,
|
|
2646
|
+
// Returns null if before is not a direct child:
|
|
2647
|
+
before = this.findDirectChild(before);
|
|
2648
|
+
let pos = this.children.indexOf(before);
|
|
2649
|
+
assert(pos >= 0, `options.before must be a direct child of ${this}`);
|
|
2470
2650
|
// insert nodeList after children[pos]
|
|
2471
2651
|
this.children.splice(pos, 0, ...nodeList);
|
|
2472
2652
|
}
|
|
@@ -2476,11 +2656,14 @@
|
|
|
2476
2656
|
// }
|
|
2477
2657
|
// this.triggerModifyChild("add", nodeList.length === 1 ? nodeList[0] : null);
|
|
2478
2658
|
tree.setModified(ChangeType.structure);
|
|
2479
|
-
return nodeList[0];
|
|
2480
2659
|
}
|
|
2481
2660
|
finally {
|
|
2482
2661
|
tree.enableUpdate(true);
|
|
2483
2662
|
}
|
|
2663
|
+
// if(isTopCall && loadLazy){
|
|
2664
|
+
// this.logWarn("addChildren(): loadLazy is not yet implemented.")
|
|
2665
|
+
// }
|
|
2666
|
+
return nodeList[0];
|
|
2484
2667
|
}
|
|
2485
2668
|
/**
|
|
2486
2669
|
* Append or prepend a node, or append a child node.
|
|
@@ -2554,21 +2737,98 @@
|
|
|
2554
2737
|
}
|
|
2555
2738
|
}
|
|
2556
2739
|
}
|
|
2557
|
-
/** Call `setExpanded()` on
|
|
2558
|
-
async expandAll(flag = true) {
|
|
2559
|
-
this.
|
|
2560
|
-
|
|
2561
|
-
}
|
|
2740
|
+
/** Call `setExpanded()` on all descendant nodes. */
|
|
2741
|
+
async expandAll(flag = true, options) {
|
|
2742
|
+
const tree = this.tree;
|
|
2743
|
+
const minExpandLevel = this.tree.options.minExpandLevel;
|
|
2744
|
+
let { depth = 99, loadLazy, force } = options !== null && options !== void 0 ? options : {};
|
|
2745
|
+
const expand_opts = {
|
|
2746
|
+
scrollIntoView: false,
|
|
2747
|
+
force: force,
|
|
2748
|
+
loadLazy: loadLazy,
|
|
2749
|
+
};
|
|
2750
|
+
// this.logInfo(`expandAll(${flag})`);
|
|
2751
|
+
// Expand all direct children in parallel:
|
|
2752
|
+
async function _iter(n, level) {
|
|
2753
|
+
var _a;
|
|
2754
|
+
// n.logInfo(` _iter(${level})`);
|
|
2755
|
+
if (level === 0) {
|
|
2756
|
+
return;
|
|
2757
|
+
}
|
|
2758
|
+
// if (!flag && minExpandLevel && !force && n.getLevel() <= minExpandLevel) {
|
|
2759
|
+
// return; // Do not collapse until minExpandLevel
|
|
2760
|
+
// }
|
|
2761
|
+
const level_1 = level == null ? null : level - 1;
|
|
2762
|
+
const promises = [];
|
|
2763
|
+
(_a = n.children) === null || _a === void 0 ? void 0 : _a.forEach((cn) => {
|
|
2764
|
+
if (flag) {
|
|
2765
|
+
if (!cn.expanded && (cn.children || (loadLazy && cn.lazy))) {
|
|
2766
|
+
// Node is collapsed and may be expanded (i.e. has children or is lazy)
|
|
2767
|
+
// Expanding may be async, so we store the promise.
|
|
2768
|
+
// Also the recursion is delayed until expansion finished.
|
|
2769
|
+
const p = cn.setExpanded(true, expand_opts);
|
|
2770
|
+
promises.push(p);
|
|
2771
|
+
p.then(async () => {
|
|
2772
|
+
await _iter(cn, level_1);
|
|
2773
|
+
});
|
|
2774
|
+
}
|
|
2775
|
+
else {
|
|
2776
|
+
// We don't expand the node, but still visit descendants.
|
|
2777
|
+
// There we may find lazy nodes, so we
|
|
2778
|
+
promises.push(_iter(cn, level_1));
|
|
2779
|
+
}
|
|
2780
|
+
}
|
|
2781
|
+
else {
|
|
2782
|
+
// Collapsing is always synchronous, so no promises required
|
|
2783
|
+
if (!minExpandLevel || force || cn.getLevel() > minExpandLevel) {
|
|
2784
|
+
// Do not collapse until minExpandLevel
|
|
2785
|
+
cn.setExpanded(false, expand_opts);
|
|
2786
|
+
}
|
|
2787
|
+
_iter(cn, level_1); // recursion, even if cn was already collapsed
|
|
2788
|
+
}
|
|
2789
|
+
});
|
|
2790
|
+
return new Promise((resolve) => {
|
|
2791
|
+
Promise.all(promises).then(() => {
|
|
2792
|
+
resolve(true);
|
|
2793
|
+
});
|
|
2794
|
+
});
|
|
2795
|
+
}
|
|
2796
|
+
const tag = tree.logTime(`${this}.expandAll(${flag})`);
|
|
2797
|
+
try {
|
|
2798
|
+
tree.enableUpdate(false);
|
|
2799
|
+
await _iter(this, depth);
|
|
2800
|
+
}
|
|
2801
|
+
finally {
|
|
2802
|
+
tree.enableUpdate(true);
|
|
2803
|
+
tree.logTimeEnd(tag);
|
|
2804
|
+
}
|
|
2562
2805
|
}
|
|
2563
|
-
/**
|
|
2806
|
+
/**
|
|
2807
|
+
* Find all descendant nodes that match condition (excluding self).
|
|
2564
2808
|
*
|
|
2565
|
-
*
|
|
2566
|
-
*
|
|
2809
|
+
* If `match` is a string, search for exact node title.
|
|
2810
|
+
* If `match` is a RegExp expression, apply it to node.title, using
|
|
2811
|
+
* [RegExp.test()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/test).
|
|
2812
|
+
* If `match` is a callback, match all nodes for that the callback(node) returns true.
|
|
2813
|
+
*
|
|
2814
|
+
* Returns an empty array if no nodes were found.
|
|
2815
|
+
*
|
|
2816
|
+
* Examples:
|
|
2817
|
+
* ```js
|
|
2818
|
+
* // Match all node titles that match exactly 'Joe':
|
|
2819
|
+
* nodeList = node.findAll("Joe")
|
|
2820
|
+
* // Match all node titles that start with 'Joe' case sensitive:
|
|
2821
|
+
* nodeList = node.findAll(/^Joe/)
|
|
2822
|
+
* // Match all node titles that contain 'oe', case insensitive:
|
|
2823
|
+
* nodeList = node.findAll(/oe/i)
|
|
2824
|
+
* // Match all nodes with `data.price` >= 99:
|
|
2825
|
+
* nodeList = node.findAll((n) => {
|
|
2826
|
+
* return n.data.price >= 99;
|
|
2827
|
+
* })
|
|
2828
|
+
* ```
|
|
2567
2829
|
*/
|
|
2568
2830
|
findAll(match) {
|
|
2569
|
-
const matcher =
|
|
2570
|
-
? match
|
|
2571
|
-
: makeNodeTitleMatcher(match);
|
|
2831
|
+
const matcher = typeof match === "function" ? match : makeNodeTitleMatcher(match);
|
|
2572
2832
|
const res = [];
|
|
2573
2833
|
this.visit((n) => {
|
|
2574
2834
|
if (matcher(n)) {
|
|
@@ -2598,15 +2858,13 @@
|
|
|
2598
2858
|
}
|
|
2599
2859
|
return null;
|
|
2600
2860
|
}
|
|
2601
|
-
/**
|
|
2861
|
+
/**
|
|
2862
|
+
* Find first descendant node that matches condition (excluding self) or null.
|
|
2602
2863
|
*
|
|
2603
|
-
* @
|
|
2604
|
-
* callback function that returns `true` if a node is matched.
|
|
2864
|
+
* @see {@link WunderbaumNode.findAll} for examples.
|
|
2605
2865
|
*/
|
|
2606
2866
|
findFirst(match) {
|
|
2607
|
-
const matcher =
|
|
2608
|
-
? match
|
|
2609
|
-
: makeNodeTitleMatcher(match);
|
|
2867
|
+
const matcher = typeof match === "function" ? match : makeNodeTitleMatcher(match);
|
|
2610
2868
|
let res = null;
|
|
2611
2869
|
this.visit((n) => {
|
|
2612
2870
|
if (matcher(n)) {
|
|
@@ -2623,6 +2881,57 @@
|
|
|
2623
2881
|
findRelatedNode(where, includeHidden = false) {
|
|
2624
2882
|
return this.tree.findRelatedNode(this, where, includeHidden);
|
|
2625
2883
|
}
|
|
2884
|
+
/**
|
|
2885
|
+
* Iterator version of {@link WunderbaumNode.format}.
|
|
2886
|
+
*/
|
|
2887
|
+
*format_iter(name_cb, connectors) {
|
|
2888
|
+
connectors !== null && connectors !== void 0 ? connectors : (connectors = [" ", " | ", " ╰─ ", " ├─ "]);
|
|
2889
|
+
name_cb !== null && name_cb !== void 0 ? name_cb : (name_cb = (node) => "" + node);
|
|
2890
|
+
function _is_last(node) {
|
|
2891
|
+
const ca = node.parent.children;
|
|
2892
|
+
return node === ca[ca.length - 1];
|
|
2893
|
+
}
|
|
2894
|
+
const _format_line = (node) => {
|
|
2895
|
+
// https://www.measurethat.net/Benchmarks/Show/12196/0/arr-unshift-vs-push-reverse-small-array
|
|
2896
|
+
const parts = [name_cb(node)];
|
|
2897
|
+
parts.unshift(connectors[_is_last(node) ? 2 : 3]);
|
|
2898
|
+
let p = node.parent;
|
|
2899
|
+
while (p && p !== this) {
|
|
2900
|
+
// `this` is the top node
|
|
2901
|
+
parts.unshift(connectors[_is_last(p) ? 0 : 1]);
|
|
2902
|
+
p = p.parent;
|
|
2903
|
+
}
|
|
2904
|
+
return parts.join("");
|
|
2905
|
+
};
|
|
2906
|
+
yield name_cb(this);
|
|
2907
|
+
for (let node of this) {
|
|
2908
|
+
yield _format_line(node);
|
|
2909
|
+
}
|
|
2910
|
+
}
|
|
2911
|
+
/**
|
|
2912
|
+
* Return multiline string representation of a node/subnode hierarchy.
|
|
2913
|
+
* Mostly useful for debugging.
|
|
2914
|
+
*
|
|
2915
|
+
* Example:
|
|
2916
|
+
* ```js
|
|
2917
|
+
* console.info(tree.getActiveNode().format((n)=>n.title));
|
|
2918
|
+
* ```
|
|
2919
|
+
* logs
|
|
2920
|
+
* ```
|
|
2921
|
+
* Books
|
|
2922
|
+
* ├─ Art of War
|
|
2923
|
+
* ╰─ Don Quixote
|
|
2924
|
+
* ...
|
|
2925
|
+
* ```
|
|
2926
|
+
* @see {@link WunderbaumNode.format_iter}
|
|
2927
|
+
*/
|
|
2928
|
+
format(name_cb, connectors) {
|
|
2929
|
+
const a = [];
|
|
2930
|
+
for (let line of this.format_iter(name_cb, connectors)) {
|
|
2931
|
+
a.push(line);
|
|
2932
|
+
}
|
|
2933
|
+
return a.join("\n");
|
|
2934
|
+
}
|
|
2626
2935
|
/** Return the `<span class='wb-col'>` element with a given index or id.
|
|
2627
2936
|
* @returns {WunderbaumNode | null}
|
|
2628
2937
|
*/
|
|
@@ -2770,7 +3079,8 @@
|
|
|
2770
3079
|
* an expand operation is currently possible.
|
|
2771
3080
|
*/
|
|
2772
3081
|
isExpandable(andCollapsed = false) {
|
|
2773
|
-
return !!this.children && (!this.expanded || !andCollapsed);
|
|
3082
|
+
// return !!this.children && (!this.expanded || !andCollapsed);
|
|
3083
|
+
return !!(this.children || this.lazy) && (!this.expanded || !andCollapsed);
|
|
2774
3084
|
}
|
|
2775
3085
|
/** Return true if this node is currently in edit-title mode. */
|
|
2776
3086
|
isEditing() {
|
|
@@ -2872,14 +3182,22 @@
|
|
|
2872
3182
|
return true;
|
|
2873
3183
|
}
|
|
2874
3184
|
_loadSourceObject(source, level) {
|
|
3185
|
+
var _a;
|
|
2875
3186
|
const tree = this.tree;
|
|
2876
3187
|
level !== null && level !== void 0 ? level : (level = this.getLevel());
|
|
2877
3188
|
// Let caller modify the parsed JSON response:
|
|
2878
|
-
this._callEvent("receive", { response: source });
|
|
3189
|
+
const res = this._callEvent("receive", { response: source });
|
|
3190
|
+
if (res != null) {
|
|
3191
|
+
source = res;
|
|
3192
|
+
}
|
|
2879
3193
|
if (isArray(source)) {
|
|
2880
3194
|
source = { children: source };
|
|
2881
3195
|
}
|
|
2882
3196
|
assert(isPlainObject(source));
|
|
3197
|
+
const format = (_a = source.format) !== null && _a !== void 0 ? _a : "nested";
|
|
3198
|
+
assert(format === "nested" || format === "flat");
|
|
3199
|
+
// Pre-rocess for 'nested' or 'flat' format
|
|
3200
|
+
inflateSourceData(source);
|
|
2883
3201
|
assert(source.children, "If `source` is an object, it must have a `children` property");
|
|
2884
3202
|
if (source.types) {
|
|
2885
3203
|
tree.logInfo("Redefine types", source.columns);
|
|
@@ -3039,17 +3357,16 @@
|
|
|
3039
3357
|
}
|
|
3040
3358
|
/** Expand all parents and optionally scroll into visible area as neccessary.
|
|
3041
3359
|
* Promise is resolved, when lazy loading and animations are done.
|
|
3042
|
-
* @param {object} [
|
|
3360
|
+
* @param {object} [options] passed to `setExpanded()`.
|
|
3043
3361
|
* Defaults to {noAnimation: false, noEvents: false, scrollIntoView: true}
|
|
3044
3362
|
*/
|
|
3045
|
-
async makeVisible(
|
|
3046
|
-
let i, dfd = new Deferred(), deferreds = [], parents = this.getParentList(false, false), len = parents.length,
|
|
3047
|
-
//
|
|
3048
|
-
scroll = !(opts && opts.scrollIntoView === false);
|
|
3363
|
+
async makeVisible(options) {
|
|
3364
|
+
let i, dfd = new Deferred(), deferreds = [], parents = this.getParentList(false, false), len = parents.length, noAnimation = getOption(options, "noAnimation", false), scroll = getOption(options, "scrollIntoView", true);
|
|
3365
|
+
// scroll = !(options && options.scrollIntoView === false);
|
|
3049
3366
|
// Expand bottom-up, so only the top node is animated
|
|
3050
3367
|
for (i = len - 1; i >= 0; i--) {
|
|
3051
3368
|
// self.debug("pushexpand" + parents[i]);
|
|
3052
|
-
const seOpts = { noAnimation:
|
|
3369
|
+
const seOpts = { noAnimation: noAnimation };
|
|
3053
3370
|
deferreds.push(parents[i].setExpanded(true, seOpts));
|
|
3054
3371
|
}
|
|
3055
3372
|
Promise.all(deferreds).then(() => {
|
|
@@ -3269,13 +3586,15 @@
|
|
|
3269
3586
|
renderColInfosById: renderColInfosById,
|
|
3270
3587
|
};
|
|
3271
3588
|
}
|
|
3272
|
-
_createIcon(parentElem, replaceChild) {
|
|
3589
|
+
_createIcon(parentElem, replaceChild, showLoading) {
|
|
3273
3590
|
let iconSpan;
|
|
3274
3591
|
let icon = this.getOption("icon");
|
|
3275
3592
|
if (this._errorInfo) {
|
|
3276
3593
|
icon = iconMap.error;
|
|
3277
3594
|
}
|
|
3278
|
-
else if (this._isLoading) {
|
|
3595
|
+
else if (this._isLoading && showLoading) {
|
|
3596
|
+
// Status nodes, or nodes without expander (< minExpandLevel) should
|
|
3597
|
+
// display the 'loading' status with the i.wb-icon span
|
|
3279
3598
|
icon = iconMap.loading;
|
|
3280
3599
|
}
|
|
3281
3600
|
if (icon === false) {
|
|
@@ -3370,7 +3689,9 @@
|
|
|
3370
3689
|
nodeElem.appendChild(expanderSpan);
|
|
3371
3690
|
ofsTitlePx += ICON_WIDTH;
|
|
3372
3691
|
}
|
|
3373
|
-
|
|
3692
|
+
// Render the icon (show a 'loading' icon if we do not have an expander that
|
|
3693
|
+
// we would prefer).
|
|
3694
|
+
iconSpan = this._createIcon(nodeElem, null, !expanderSpan);
|
|
3374
3695
|
if (iconSpan) {
|
|
3375
3696
|
ofsTitlePx += ICON_WIDTH;
|
|
3376
3697
|
}
|
|
@@ -3528,7 +3849,10 @@
|
|
|
3528
3849
|
rowDiv.classList.add(...typeInfo.classes);
|
|
3529
3850
|
}
|
|
3530
3851
|
if (expanderSpan) {
|
|
3531
|
-
if (this.
|
|
3852
|
+
if (this._isLoading) {
|
|
3853
|
+
expanderSpan.className = "wb-expander " + iconMap.loading;
|
|
3854
|
+
}
|
|
3855
|
+
else if (this.isExpandable(false)) {
|
|
3532
3856
|
if (this.expanded) {
|
|
3533
3857
|
expanderSpan.className = "wb-expander " + iconMap.expanderExpanded;
|
|
3534
3858
|
}
|
|
@@ -3536,9 +3860,6 @@
|
|
|
3536
3860
|
expanderSpan.className = "wb-expander " + iconMap.expanderCollapsed;
|
|
3537
3861
|
}
|
|
3538
3862
|
}
|
|
3539
|
-
else if (this._isLoading) {
|
|
3540
|
-
expanderSpan.className = "wb-expander " + iconMap.loading;
|
|
3541
|
-
}
|
|
3542
3863
|
else if (this.lazy && this.children == null) {
|
|
3543
3864
|
expanderSpan.className = "wb-expander " + iconMap.expanderLazy;
|
|
3544
3865
|
}
|
|
@@ -3563,7 +3884,7 @@
|
|
|
3563
3884
|
// Update icon (if not opts.isNew, which would rebuild markup anyway)
|
|
3564
3885
|
const iconSpan = nodeElem.querySelector("i.wb-icon");
|
|
3565
3886
|
if (iconSpan) {
|
|
3566
|
-
this._createIcon(nodeElem, iconSpan);
|
|
3887
|
+
this._createIcon(nodeElem, iconSpan, !expanderSpan);
|
|
3567
3888
|
}
|
|
3568
3889
|
}
|
|
3569
3890
|
}
|
|
@@ -3572,11 +3893,14 @@
|
|
|
3572
3893
|
*
|
|
3573
3894
|
* `options.change` defaults to ChangeType.data, which updates the title,
|
|
3574
3895
|
* icon, and status. It also triggers the `render` event, that lets the user
|
|
3575
|
-
* create or update the content of embeded cell elements
|
|
3896
|
+
* create or update the content of embeded cell elements.
|
|
3576
3897
|
*
|
|
3577
3898
|
* If only the status or other class-only modifications have changed,
|
|
3578
3899
|
* `options.change` should be set to ChangeType.status instead for best
|
|
3579
3900
|
* efficiency.
|
|
3901
|
+
*
|
|
3902
|
+
* Calling `setModified` instead may be a better alternative.
|
|
3903
|
+
* @see {@link WunderbaumNode.setModified}
|
|
3580
3904
|
*/
|
|
3581
3905
|
render(options) {
|
|
3582
3906
|
// this.log("render", options);
|
|
@@ -3776,25 +4100,29 @@
|
|
|
3776
4100
|
* Expand or collapse this node.
|
|
3777
4101
|
*/
|
|
3778
4102
|
async setExpanded(flag = true, options) {
|
|
4103
|
+
const { force, scrollIntoView, immediate } = options !== null && options !== void 0 ? options : {};
|
|
3779
4104
|
if (!flag &&
|
|
3780
4105
|
this.isExpanded() &&
|
|
3781
4106
|
this.getLevel() <= this.tree.getOption("minExpandLevel") &&
|
|
3782
|
-
!
|
|
4107
|
+
!force) {
|
|
3783
4108
|
this.logDebug("Ignored collapse request below expandLevel.");
|
|
3784
4109
|
return;
|
|
3785
4110
|
}
|
|
3786
4111
|
if (!flag === !this.expanded) {
|
|
3787
4112
|
return; // Nothing to do
|
|
3788
4113
|
}
|
|
4114
|
+
// this.log("setExpanded()");
|
|
3789
4115
|
if (flag && this.lazy && this.children == null) {
|
|
3790
4116
|
await this.loadLazy();
|
|
3791
4117
|
}
|
|
3792
4118
|
this.expanded = flag;
|
|
3793
|
-
const updateOpts = { immediate:
|
|
4119
|
+
const updateOpts = { immediate: immediate };
|
|
4120
|
+
// const updateOpts = { immediate: !!util.getOption(options, "immediate") };
|
|
3794
4121
|
this.tree.setModified(ChangeType.structure, updateOpts);
|
|
3795
|
-
if (
|
|
4122
|
+
if (flag && scrollIntoView !== false) {
|
|
3796
4123
|
const lastChild = this.getLastChild();
|
|
3797
4124
|
if (lastChild) {
|
|
4125
|
+
this.tree.updatePendingModifications();
|
|
3798
4126
|
lastChild.scrollIntoView({ topNode: this });
|
|
3799
4127
|
}
|
|
3800
4128
|
}
|
|
@@ -3872,8 +4200,13 @@
|
|
|
3872
4200
|
this._errorInfo = null;
|
|
3873
4201
|
break;
|
|
3874
4202
|
case "loading":
|
|
3875
|
-
|
|
3876
|
-
|
|
4203
|
+
this._isLoading = true;
|
|
4204
|
+
this._errorInfo = null;
|
|
4205
|
+
if (this.parent) {
|
|
4206
|
+
this.setModified(ChangeType.status);
|
|
4207
|
+
}
|
|
4208
|
+
else {
|
|
4209
|
+
// If this is the invisible root, add a visible top-level node
|
|
3877
4210
|
_setStatusNode({
|
|
3878
4211
|
statusNodeType: status,
|
|
3879
4212
|
title: tree.options.strings.loading +
|
|
@@ -3883,8 +4216,6 @@
|
|
|
3883
4216
|
tooltip: details,
|
|
3884
4217
|
});
|
|
3885
4218
|
}
|
|
3886
|
-
this._isLoading = true;
|
|
3887
|
-
this._errorInfo = null;
|
|
3888
4219
|
// this.render();
|
|
3889
4220
|
break;
|
|
3890
4221
|
case "error":
|
|
@@ -3947,7 +4278,7 @@
|
|
|
3947
4278
|
this.parent.triggerModifyChild(operation, this, extra);
|
|
3948
4279
|
}
|
|
3949
4280
|
/**
|
|
3950
|
-
* Call
|
|
4281
|
+
* Call `callback(node)` for all child nodes in hierarchical order (depth-first, pre-order).
|
|
3951
4282
|
*
|
|
3952
4283
|
* Stop iteration, if fn() returns false. Skip current branch, if fn()
|
|
3953
4284
|
* returns "skip".<br>
|
|
@@ -3956,6 +4287,7 @@
|
|
|
3956
4287
|
* @param {function} callback the callback function.
|
|
3957
4288
|
* Return false to stop iteration, return "skip" to skip this node and
|
|
3958
4289
|
* its children only.
|
|
4290
|
+
* @see {@link WunderbaumNode.*[Symbol.iterator]}, {@link Wunderbaum.visit}.
|
|
3959
4291
|
*/
|
|
3960
4292
|
visit(callback, includeSelf = false) {
|
|
3961
4293
|
let i, l, res = true, children = this.children;
|
|
@@ -4026,7 +4358,7 @@
|
|
|
4026
4358
|
/*!
|
|
4027
4359
|
* Wunderbaum - ext-edit
|
|
4028
4360
|
* Copyright (c) 2021-2022, Martin Wendt. Released under the MIT license.
|
|
4029
|
-
* v0.0.
|
|
4361
|
+
* v0.0.9, Mon, 31 Oct 2022 17:06:10 GMT (https://github.com/mar10/wunderbaum)
|
|
4030
4362
|
*/
|
|
4031
4363
|
// const START_MARKER = "\uFFF7";
|
|
4032
4364
|
class EditExtension extends WunderbaumExtension {
|
|
@@ -4319,8 +4651,8 @@
|
|
|
4319
4651
|
* https://github.com/mar10/wunderbaum
|
|
4320
4652
|
*
|
|
4321
4653
|
* Released under the MIT license.
|
|
4322
|
-
* @version v0.0.
|
|
4323
|
-
* @date
|
|
4654
|
+
* @version v0.0.9
|
|
4655
|
+
* @date Mon, 31 Oct 2022 17:06:10 GMT
|
|
4324
4656
|
*/
|
|
4325
4657
|
class WbSystemRoot extends WunderbaumNode {
|
|
4326
4658
|
constructor(tree) {
|
|
@@ -4707,6 +5039,20 @@
|
|
|
4707
5039
|
}
|
|
4708
5040
|
return null;
|
|
4709
5041
|
}
|
|
5042
|
+
/**
|
|
5043
|
+
* Iterate all descendant nodes depth-first, pre-order using `for ... of ...` syntax.
|
|
5044
|
+
* More concise, but slightly slower than {@link Wunderbaum.visit}.
|
|
5045
|
+
*
|
|
5046
|
+
* Example:
|
|
5047
|
+
* ```js
|
|
5048
|
+
* for(const node of tree) {
|
|
5049
|
+
* ...
|
|
5050
|
+
* }
|
|
5051
|
+
* ```
|
|
5052
|
+
*/
|
|
5053
|
+
*[Symbol.iterator]() {
|
|
5054
|
+
yield* this.root;
|
|
5055
|
+
}
|
|
4710
5056
|
/** @internal */
|
|
4711
5057
|
_registerExtension(extension) {
|
|
4712
5058
|
this.extensionList.push(extension);
|
|
@@ -4721,8 +5067,8 @@
|
|
|
4721
5067
|
}
|
|
4722
5068
|
/** Add node to tree's bookkeeping data structures. */
|
|
4723
5069
|
_registerNode(node) {
|
|
4724
|
-
|
|
4725
|
-
assert(key != null && !this.keyMap.has(key));
|
|
5070
|
+
const key = node.key;
|
|
5071
|
+
assert(key != null && !this.keyMap.has(key), `Missing or duplicate key: '${key}'.`);
|
|
4726
5072
|
this.keyMap.set(key, node);
|
|
4727
5073
|
let rk = node.refKey;
|
|
4728
5074
|
if (rk) {
|
|
@@ -4891,22 +5237,22 @@
|
|
|
4891
5237
|
* - 'down', 'first', 'last', 'left', 'parent', 'right', 'up': navigate
|
|
4892
5238
|
*
|
|
4893
5239
|
*/
|
|
4894
|
-
applyCommand(cmd, nodeOrOpts,
|
|
5240
|
+
applyCommand(cmd, nodeOrOpts, options) {
|
|
4895
5241
|
let // clipboard,
|
|
4896
5242
|
node, refNode;
|
|
4897
|
-
//
|
|
5243
|
+
// options = $.extend(
|
|
4898
5244
|
// { setActive: true, clipboard: CLIPBOARD },
|
|
4899
|
-
//
|
|
5245
|
+
// options_
|
|
4900
5246
|
// );
|
|
4901
5247
|
if (nodeOrOpts instanceof WunderbaumNode) {
|
|
4902
5248
|
node = nodeOrOpts;
|
|
4903
5249
|
}
|
|
4904
5250
|
else {
|
|
4905
5251
|
node = this.getActiveNode();
|
|
4906
|
-
assert(
|
|
4907
|
-
|
|
5252
|
+
assert(options === undefined);
|
|
5253
|
+
options = nodeOrOpts;
|
|
4908
5254
|
}
|
|
4909
|
-
// clipboard =
|
|
5255
|
+
// clipboard = options.clipboard;
|
|
4910
5256
|
switch (cmd) {
|
|
4911
5257
|
// Sorting and indentation:
|
|
4912
5258
|
case "moveUp":
|
|
@@ -5112,16 +5458,8 @@
|
|
|
5112
5458
|
}
|
|
5113
5459
|
}
|
|
5114
5460
|
/** Recursively expand all expandable nodes (triggers lazy load id needed). */
|
|
5115
|
-
async expandAll(flag = true) {
|
|
5116
|
-
|
|
5117
|
-
try {
|
|
5118
|
-
this.enableUpdate(false);
|
|
5119
|
-
await this.root.expandAll(flag);
|
|
5120
|
-
}
|
|
5121
|
-
finally {
|
|
5122
|
-
this.enableUpdate(true);
|
|
5123
|
-
this.logTimeEnd(tag);
|
|
5124
|
-
}
|
|
5461
|
+
async expandAll(flag = true, options) {
|
|
5462
|
+
await this.root.expandAll(flag, options);
|
|
5125
5463
|
}
|
|
5126
5464
|
/** Recursively select all nodes. */
|
|
5127
5465
|
selectAll(flag = true) {
|
|
@@ -5155,10 +5493,7 @@
|
|
|
5155
5493
|
// util.assert(this.keyMap.size === i);
|
|
5156
5494
|
}
|
|
5157
5495
|
/**
|
|
5158
|
-
* Find all nodes that
|
|
5159
|
-
*
|
|
5160
|
-
* @param match title string to search for, or a
|
|
5161
|
-
* callback function that returns `true` if a node is matched.
|
|
5496
|
+
* Find all nodes that match condition.
|
|
5162
5497
|
*
|
|
5163
5498
|
* @see {@link WunderbaumNode.findAll}
|
|
5164
5499
|
*/
|
|
@@ -5168,10 +5503,7 @@
|
|
|
5168
5503
|
/**
|
|
5169
5504
|
* Find first node that matches condition.
|
|
5170
5505
|
*
|
|
5171
|
-
* @param match title string to search for, or a
|
|
5172
|
-
* callback function that returns `true` if a node is matched.
|
|
5173
5506
|
* @see {@link WunderbaumNode.findFirst}
|
|
5174
|
-
*
|
|
5175
5507
|
*/
|
|
5176
5508
|
findFirst(match) {
|
|
5177
5509
|
return this.root.findFirst(match);
|
|
@@ -5185,7 +5517,7 @@
|
|
|
5185
5517
|
*
|
|
5186
5518
|
*/
|
|
5187
5519
|
findKey(key) {
|
|
5188
|
-
return this.keyMap.get(key);
|
|
5520
|
+
return this.keyMap.get(key) || null;
|
|
5189
5521
|
}
|
|
5190
5522
|
/**
|
|
5191
5523
|
* Find the next visible node that starts with `match`, starting at `startNode`
|
|
@@ -5314,6 +5646,35 @@
|
|
|
5314
5646
|
}
|
|
5315
5647
|
return res;
|
|
5316
5648
|
}
|
|
5649
|
+
/**
|
|
5650
|
+
* Iterator version of {@link Wunderbaum.format}.
|
|
5651
|
+
*/
|
|
5652
|
+
*format_iter(name_cb, connectors) {
|
|
5653
|
+
return this.root.format_iter(name_cb, connectors);
|
|
5654
|
+
}
|
|
5655
|
+
/**
|
|
5656
|
+
* Return multiline string representation of the node hierarchy.
|
|
5657
|
+
* Mostly useful for debugging.
|
|
5658
|
+
*
|
|
5659
|
+
* Example:
|
|
5660
|
+
* ```js
|
|
5661
|
+
* console.info(tree.format((n)=>n.title));
|
|
5662
|
+
* ```
|
|
5663
|
+
* logs
|
|
5664
|
+
* ```
|
|
5665
|
+
* Playground
|
|
5666
|
+
* ├─ Books
|
|
5667
|
+
* | ├─ Art of War
|
|
5668
|
+
* | ╰─ Don Quixote
|
|
5669
|
+
* ├─ Music
|
|
5670
|
+
* ...
|
|
5671
|
+
* ```
|
|
5672
|
+
*
|
|
5673
|
+
* @see {@link Wunderbaum.format_iter} and {@link WunderbaumNode.format}.
|
|
5674
|
+
*/
|
|
5675
|
+
format(name_cb, connectors) {
|
|
5676
|
+
return this.root.format(name_cb, connectors);
|
|
5677
|
+
}
|
|
5317
5678
|
/**
|
|
5318
5679
|
* Return the active cell (`span.wb-col`) of the currently active node or null.
|
|
5319
5680
|
*/
|
|
@@ -5481,13 +5842,13 @@
|
|
|
5481
5842
|
scrollTo(nodeOrOpts) {
|
|
5482
5843
|
const PADDING = 2; // leave some pixels between viewport bounds
|
|
5483
5844
|
let node;
|
|
5484
|
-
let
|
|
5845
|
+
let options;
|
|
5485
5846
|
if (nodeOrOpts instanceof WunderbaumNode) {
|
|
5486
5847
|
node = nodeOrOpts;
|
|
5487
5848
|
}
|
|
5488
5849
|
else {
|
|
5489
|
-
|
|
5490
|
-
node =
|
|
5850
|
+
options = nodeOrOpts;
|
|
5851
|
+
node = options.node;
|
|
5491
5852
|
}
|
|
5492
5853
|
assert(node && node._rowIdx != null);
|
|
5493
5854
|
const scrollParent = this.element;
|
|
@@ -5498,7 +5859,7 @@
|
|
|
5498
5859
|
const vpTop = headerHeight;
|
|
5499
5860
|
const vpRowTop = rowTop - scrollTop;
|
|
5500
5861
|
const vpRowBottom = vpRowTop + ROW_HEIGHT;
|
|
5501
|
-
const topNode =
|
|
5862
|
+
const topNode = options === null || options === void 0 ? void 0 : options.topNode;
|
|
5502
5863
|
// this.log( `scrollTo(${node.title}), vpTop:${vpTop}px, scrollTop:${scrollTop}, vpHeight:${vpHeight}, rowTop:${rowTop}, vpRowTop:${vpRowTop}`, nodeOrOpts );
|
|
5503
5864
|
let newScrollTop = null;
|
|
5504
5865
|
if (vpRowTop >= vpTop) {
|
|
@@ -5742,11 +6103,13 @@
|
|
|
5742
6103
|
}
|
|
5743
6104
|
}
|
|
5744
6105
|
/** Update column headers and width. */
|
|
5745
|
-
updateColumns(
|
|
5746
|
-
|
|
6106
|
+
updateColumns(options) {
|
|
6107
|
+
options = Object.assign({ calculateCols: true, updateRows: true }, options);
|
|
5747
6108
|
const defaultMinWidth = 4;
|
|
5748
6109
|
const vpWidth = this.element.clientWidth;
|
|
5749
6110
|
const isGrid = this.isGrid();
|
|
6111
|
+
// Shorten last column width to avoid h-scrollbar
|
|
6112
|
+
const FIX_ADJUST_LAST_COL = 2;
|
|
5750
6113
|
let totalWidth = 0;
|
|
5751
6114
|
let totalWeight = 0;
|
|
5752
6115
|
let fixedWidth = 0;
|
|
@@ -5755,7 +6118,7 @@
|
|
|
5755
6118
|
if (!isGrid && this.isCellNav()) {
|
|
5756
6119
|
this.setCellNav(false);
|
|
5757
6120
|
}
|
|
5758
|
-
if (
|
|
6121
|
+
if (options.calculateCols) {
|
|
5759
6122
|
// Gather width definitions
|
|
5760
6123
|
this._columnsById = {};
|
|
5761
6124
|
for (let col of this.columns) {
|
|
@@ -5807,7 +6170,8 @@
|
|
|
5807
6170
|
col._ofsPx = ofsPx;
|
|
5808
6171
|
ofsPx += col._widthPx;
|
|
5809
6172
|
}
|
|
5810
|
-
|
|
6173
|
+
this.columns[this.columns.length - 1]._widthPx -= FIX_ADJUST_LAST_COL;
|
|
6174
|
+
totalWidth = ofsPx - FIX_ADJUST_LAST_COL;
|
|
5811
6175
|
}
|
|
5812
6176
|
// if (this.options.fixedCol) {
|
|
5813
6177
|
// 'position: fixed' requires that the content has the correct size
|
|
@@ -5821,7 +6185,7 @@
|
|
|
5821
6185
|
// util.error("BREAK");
|
|
5822
6186
|
if (modified) {
|
|
5823
6187
|
this._renderHeaderMarkup();
|
|
5824
|
-
if (
|
|
6188
|
+
if (options.updateRows) {
|
|
5825
6189
|
this._updateRows();
|
|
5826
6190
|
}
|
|
5827
6191
|
}
|
|
@@ -5884,6 +6248,8 @@
|
|
|
5884
6248
|
*/
|
|
5885
6249
|
_updateViewportImmediately() {
|
|
5886
6250
|
var _a;
|
|
6251
|
+
// Shorten container height to avoid v-scrollbar
|
|
6252
|
+
const FIX_ADJUST_HEIGHT = 1;
|
|
5887
6253
|
if (this._disableUpdateCount) {
|
|
5888
6254
|
this.log(`IGNORED _updateViewportImmediately() disable level: ${this._disableUpdateCount}`);
|
|
5889
6255
|
return;
|
|
@@ -5897,7 +6263,7 @@
|
|
|
5897
6263
|
// let headerHeight = this.headerElement.children[0].children[0].clientHeight;
|
|
5898
6264
|
// const headerHeight = this.options.headerHeightPx;
|
|
5899
6265
|
const headerHeight = this.headerElement.clientHeight; // May be 0
|
|
5900
|
-
const wantHeight = this.element.clientHeight - headerHeight;
|
|
6266
|
+
const wantHeight = this.element.clientHeight - headerHeight - FIX_ADJUST_HEIGHT;
|
|
5901
6267
|
if (Math.abs(height - wantHeight) > 1.0) {
|
|
5902
6268
|
// this.log("resize", height, wantHeight);
|
|
5903
6269
|
this.scrollContainerElement.style.height = wantHeight + "px";
|
|
@@ -5954,11 +6320,11 @@
|
|
|
5954
6320
|
* (including upper and lower prefetch)
|
|
5955
6321
|
* -
|
|
5956
6322
|
*/
|
|
5957
|
-
_updateRows(
|
|
6323
|
+
_updateRows(options) {
|
|
5958
6324
|
// const label = this.logTime("_updateRows");
|
|
5959
6325
|
// this.log("_updateRows", opts)
|
|
5960
|
-
|
|
5961
|
-
const newNodesOnly = !!
|
|
6326
|
+
options = Object.assign({ newNodesOnly: false }, options);
|
|
6327
|
+
const newNodesOnly = !!options.newNodesOnly;
|
|
5962
6328
|
const row_height = ROW_HEIGHT;
|
|
5963
6329
|
const vp_height = this.element.clientHeight;
|
|
5964
6330
|
const prefetch = RENDER_MAX_PREFETCH;
|
|
@@ -6035,7 +6401,8 @@
|
|
|
6035
6401
|
return modified;
|
|
6036
6402
|
}
|
|
6037
6403
|
/**
|
|
6038
|
-
* Call callback(node) for all nodes in hierarchical order (depth-first).
|
|
6404
|
+
* Call `callback(node)` for all nodes in hierarchical order (depth-first, pre-order).
|
|
6405
|
+
* @see {@link Wunderbaum.*[Symbol.iterator]}, {@link WunderbaumNode.visit}.
|
|
6039
6406
|
*
|
|
6040
6407
|
* @param {function} callback the callback function.
|
|
6041
6408
|
* Return false to stop iteration, return "skip" to skip this node and
|
|
@@ -6061,16 +6428,16 @@
|
|
|
6061
6428
|
* {start: First tree node, reverse: false, includeSelf: true, includeHidden: false, wrap: false}
|
|
6062
6429
|
* @returns {boolean} false if iteration was canceled
|
|
6063
6430
|
*/
|
|
6064
|
-
visitRows(callback,
|
|
6431
|
+
visitRows(callback, options) {
|
|
6065
6432
|
if (!this.root.hasChildren()) {
|
|
6066
6433
|
return false;
|
|
6067
6434
|
}
|
|
6068
|
-
if (
|
|
6069
|
-
delete
|
|
6070
|
-
return this._visitRowsUp(callback,
|
|
6435
|
+
if (options && options.reverse) {
|
|
6436
|
+
delete options.reverse;
|
|
6437
|
+
return this._visitRowsUp(callback, options);
|
|
6071
6438
|
}
|
|
6072
|
-
|
|
6073
|
-
let i, nextIdx, parent, res, siblings, stopNode, siblingOfs = 0, skipFirstNode =
|
|
6439
|
+
options = options || {};
|
|
6440
|
+
let i, nextIdx, parent, res, siblings, stopNode, siblingOfs = 0, skipFirstNode = options.includeSelf === false, includeHidden = !!options.includeHidden, checkFilter = !includeHidden && this.filterMode === "hide", node = options.start || this.root.children[0];
|
|
6074
6441
|
parent = node.parent;
|
|
6075
6442
|
while (parent) {
|
|
6076
6443
|
// visit siblings
|
|
@@ -6119,11 +6486,11 @@
|
|
|
6119
6486
|
node = parent;
|
|
6120
6487
|
parent = parent.parent;
|
|
6121
6488
|
siblingOfs = 1; //
|
|
6122
|
-
if (!parent &&
|
|
6489
|
+
if (!parent && options.wrap) {
|
|
6123
6490
|
this.logDebug("visitRows(): wrap around");
|
|
6124
|
-
assert(
|
|
6125
|
-
stopNode =
|
|
6126
|
-
|
|
6491
|
+
assert(options.start, "`wrap` option requires `start`");
|
|
6492
|
+
stopNode = options.start;
|
|
6493
|
+
options.wrap = false;
|
|
6127
6494
|
parent = this.root;
|
|
6128
6495
|
siblingOfs = 0;
|
|
6129
6496
|
}
|
|
@@ -6258,7 +6625,7 @@
|
|
|
6258
6625
|
}
|
|
6259
6626
|
Wunderbaum.sequence = 0;
|
|
6260
6627
|
/** Wunderbaum release version number "MAJOR.MINOR.PATCH". */
|
|
6261
|
-
Wunderbaum.version = "v0.0.
|
|
6628
|
+
Wunderbaum.version = "v0.0.9"; // Set to semver by 'grunt release'
|
|
6262
6629
|
/** Expose some useful methods of the util.ts module as `Wunderbaum.util`. */
|
|
6263
6630
|
Wunderbaum.util = util;
|
|
6264
6631
|
|