wunderbaum 0.12.0 → 0.13.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 +10 -1
- package/dist/wunderbaum.css.map +1 -1
- package/dist/wunderbaum.d.ts +214 -59
- package/dist/wunderbaum.esm.js +417 -222
- package/dist/wunderbaum.esm.min.js +42 -42
- package/dist/wunderbaum.esm.min.js.map +1 -1
- package/dist/wunderbaum.umd.js +417 -222
- package/dist/wunderbaum.umd.min.js +45 -45
- package/dist/wunderbaum.umd.min.js.map +1 -1
- package/package.json +30 -28
- package/src/common.ts +58 -16
- package/src/debounce.ts +5 -0
- package/src/deferred.ts +1 -1
- package/src/drag_observer.ts +1 -1
- package/src/types.ts +163 -21
- package/src/util.ts +1 -14
- package/src/wb_ext_dnd.ts +3 -3
- package/src/wb_ext_edit.ts +2 -2
- package/src/wb_ext_filter.ts +119 -44
- package/src/wb_ext_grid.ts +1 -1
- package/src/wb_ext_keynav.ts +1 -1
- package/src/wb_ext_logger.ts +1 -1
- package/src/wb_extension_base.ts +4 -3
- package/src/wb_node.ts +27 -98
- package/src/wb_options.ts +7 -5
- package/src/wunderbaum.scss +12 -4
- package/src/wunderbaum.ts +272 -48
package/dist/wunderbaum.umd.js
CHANGED
|
@@ -5,6 +5,11 @@
|
|
|
5
5
|
})(this, (function (exports) { 'use strict';
|
|
6
6
|
|
|
7
7
|
/*!
|
|
8
|
+
* Wunderbaum - debounce.ts
|
|
9
|
+
* Copyright (c) 2021-2025, Martin Wendt. Released under the MIT license.
|
|
10
|
+
* v0.13.0, Sat, 08 Mar 2025 14:16:31 GMT (https://github.com/mar10/wunderbaum)
|
|
11
|
+
*/
|
|
12
|
+
/*
|
|
8
13
|
* debounce & throttle, taken from https://github.com/lodash/lodash v4.17.21
|
|
9
14
|
* MIT License: https://raw.githubusercontent.com/lodash/lodash/4.17.21-npm/LICENSE
|
|
10
15
|
* Modified for TypeScript type annotations.
|
|
@@ -293,8 +298,8 @@
|
|
|
293
298
|
|
|
294
299
|
/*!
|
|
295
300
|
* Wunderbaum - util
|
|
296
|
-
* Copyright (c) 2021-
|
|
297
|
-
* v0.
|
|
301
|
+
* Copyright (c) 2021-2025, Martin Wendt. Released under the MIT license.
|
|
302
|
+
* v0.13.0, Sat, 08 Mar 2025 14:16:31 GMT (https://github.com/mar10/wunderbaum)
|
|
298
303
|
*/
|
|
299
304
|
/** @module util */
|
|
300
305
|
/** Readable names for `MouseEvent.button` */
|
|
@@ -677,18 +682,6 @@
|
|
|
677
682
|
}
|
|
678
683
|
return obj;
|
|
679
684
|
}
|
|
680
|
-
// /** Return a EventTarget from selector or cast an existing element. */
|
|
681
|
-
// export function eventTargetFromSelector(
|
|
682
|
-
// obj: string | EventTarget
|
|
683
|
-
// ): EventTarget | null {
|
|
684
|
-
// if (!obj) {
|
|
685
|
-
// return null;
|
|
686
|
-
// }
|
|
687
|
-
// if (typeof obj === "string") {
|
|
688
|
-
// return document.querySelector(obj) as EventTarget;
|
|
689
|
-
// }
|
|
690
|
-
// return obj as EventTarget;
|
|
691
|
-
// }
|
|
692
685
|
/**
|
|
693
686
|
* Return a canonical descriptive string for a keyboard or mouse event.
|
|
694
687
|
*
|
|
@@ -1147,8 +1140,8 @@
|
|
|
1147
1140
|
|
|
1148
1141
|
/*!
|
|
1149
1142
|
* Wunderbaum - types
|
|
1150
|
-
* Copyright (c) 2021-
|
|
1151
|
-
* v0.
|
|
1143
|
+
* Copyright (c) 2021-2025, Martin Wendt. Released under the MIT license.
|
|
1144
|
+
* v0.13.0, Sat, 08 Mar 2025 14:16:31 GMT (https://github.com/mar10/wunderbaum)
|
|
1152
1145
|
*/
|
|
1153
1146
|
/**
|
|
1154
1147
|
* Possible values for {@link WunderbaumNode.update} and {@link Wunderbaum.update}.
|
|
@@ -1172,7 +1165,7 @@
|
|
|
1172
1165
|
/** Vertical scroll event. Update the 'top' property of all rows. */
|
|
1173
1166
|
ChangeType["scroll"] = "scroll";
|
|
1174
1167
|
})(ChangeType || (ChangeType = {}));
|
|
1175
|
-
|
|
1168
|
+
/** @internal */
|
|
1176
1169
|
var RenderFlag;
|
|
1177
1170
|
(function (RenderFlag) {
|
|
1178
1171
|
RenderFlag["clearMarkup"] = "clearMarkup";
|
|
@@ -1203,16 +1196,20 @@
|
|
|
1203
1196
|
/** Initial navigation mode and possible transition. */
|
|
1204
1197
|
var NavModeEnum;
|
|
1205
1198
|
(function (NavModeEnum) {
|
|
1199
|
+
/** Start with row mode, but allow cell-nav mode */
|
|
1206
1200
|
NavModeEnum["startRow"] = "startRow";
|
|
1201
|
+
/** Cell-nav mode only */
|
|
1207
1202
|
NavModeEnum["cell"] = "cell";
|
|
1203
|
+
/** Start in cell-nav mode, but allow row mode */
|
|
1208
1204
|
NavModeEnum["startCell"] = "startCell";
|
|
1205
|
+
/** Row mode only */
|
|
1209
1206
|
NavModeEnum["row"] = "row";
|
|
1210
1207
|
})(NavModeEnum || (NavModeEnum = {}));
|
|
1211
1208
|
|
|
1212
1209
|
/*!
|
|
1213
1210
|
* Wunderbaum - wb_extension_base
|
|
1214
|
-
* Copyright (c) 2021-
|
|
1215
|
-
* v0.
|
|
1211
|
+
* Copyright (c) 2021-2025, Martin Wendt. Released under the MIT license.
|
|
1212
|
+
* v0.13.0, Sat, 08 Mar 2025 14:16:31 GMT (https://github.com/mar10/wunderbaum)
|
|
1216
1213
|
*/
|
|
1217
1214
|
class WunderbaumExtension {
|
|
1218
1215
|
constructor(tree, id, defaults) {
|
|
@@ -1270,8 +1267,8 @@
|
|
|
1270
1267
|
|
|
1271
1268
|
/*!
|
|
1272
1269
|
* Wunderbaum - ext-filter
|
|
1273
|
-
* Copyright (c) 2021-
|
|
1274
|
-
* v0.
|
|
1270
|
+
* Copyright (c) 2021-2025, Martin Wendt. Released under the MIT license.
|
|
1271
|
+
* v0.13.0, Sat, 08 Mar 2025 14:16:31 GMT (https://github.com/mar10/wunderbaum)
|
|
1275
1272
|
*/
|
|
1276
1273
|
const START_MARKER = "\uFFF7";
|
|
1277
1274
|
const END_MARKER = "\uFFF8";
|
|
@@ -1283,7 +1280,7 @@
|
|
|
1283
1280
|
autoApply: true, // Re-apply last filter if lazy data is loaded
|
|
1284
1281
|
autoExpand: false, // Expand all branches that contain matches while filtered
|
|
1285
1282
|
matchBranch: false, // Whether to implicitly match all children of matched nodes
|
|
1286
|
-
|
|
1283
|
+
connect: null, // Element or selector of an input control for filter query strings
|
|
1287
1284
|
fuzzy: false, // Match single characters in order, e.g. 'fb' will match 'FooBar'
|
|
1288
1285
|
hideExpanders: false, // Hide expanders if all child nodes are hidden by filter
|
|
1289
1286
|
highlight: true, // Highlight matches by wrapping inside <mark> tags
|
|
@@ -1291,36 +1288,117 @@
|
|
|
1291
1288
|
mode: "dim", // Grayout unmatched nodes (pass "hide" to remove unmatched node instead)
|
|
1292
1289
|
noData: true, // Display a 'no data' status node if result is empty
|
|
1293
1290
|
});
|
|
1291
|
+
this.queryInput = null;
|
|
1292
|
+
this.prevButton = null;
|
|
1293
|
+
this.nextButton = null;
|
|
1294
|
+
this.modeButton = null;
|
|
1295
|
+
this.matchInfoElem = null;
|
|
1294
1296
|
this.lastFilterArgs = null;
|
|
1295
1297
|
}
|
|
1296
1298
|
init() {
|
|
1297
1299
|
super.init();
|
|
1298
|
-
const
|
|
1299
|
-
if (
|
|
1300
|
-
this.
|
|
1301
|
-
assert(this.queryInput, `Invalid 'filter.connectInput' option: ${connectInput}.`);
|
|
1302
|
-
onEvent(this.queryInput, "input", debounce((e) => {
|
|
1303
|
-
// this.tree.log("query", e);
|
|
1304
|
-
this.filterNodes(this.queryInput.value.trim(), {});
|
|
1305
|
-
}, 700));
|
|
1300
|
+
const connect = this.getPluginOption("connect");
|
|
1301
|
+
if (connect) {
|
|
1302
|
+
this._connectControls();
|
|
1306
1303
|
}
|
|
1307
1304
|
}
|
|
1308
1305
|
setPluginOption(name, value) {
|
|
1309
|
-
// alert("filter opt=" + name + ", " + value)
|
|
1310
1306
|
super.setPluginOption(name, value);
|
|
1311
1307
|
switch (name) {
|
|
1312
1308
|
case "mode":
|
|
1313
|
-
this.tree.filterMode =
|
|
1309
|
+
this.tree.filterMode =
|
|
1310
|
+
value === "hide" ? "hide" : value === "mark" ? "mark" : "dim";
|
|
1314
1311
|
this.tree.updateFilter();
|
|
1315
1312
|
break;
|
|
1316
1313
|
}
|
|
1317
1314
|
}
|
|
1315
|
+
_updatedConnectedControls() {
|
|
1316
|
+
var _a;
|
|
1317
|
+
const filterActive = this.tree.filterMode !== null;
|
|
1318
|
+
const activeNode = this.tree.getActiveNode();
|
|
1319
|
+
const matchCount = filterActive ? this.countMatches() : 0;
|
|
1320
|
+
const strings = this.treeOpts.strings;
|
|
1321
|
+
let matchIdx = "?";
|
|
1322
|
+
if (this.matchInfoElem) {
|
|
1323
|
+
if (filterActive) {
|
|
1324
|
+
let info;
|
|
1325
|
+
if (matchCount === 0) {
|
|
1326
|
+
info = strings.noMatch;
|
|
1327
|
+
}
|
|
1328
|
+
else if (activeNode && activeNode.match >= 1) {
|
|
1329
|
+
matchIdx = (_a = activeNode.match) !== null && _a !== void 0 ? _a : "?";
|
|
1330
|
+
info = strings.matchIndex;
|
|
1331
|
+
}
|
|
1332
|
+
else {
|
|
1333
|
+
info = strings.queryResult;
|
|
1334
|
+
}
|
|
1335
|
+
info = info
|
|
1336
|
+
.replace("${count}", this.tree.count().toLocaleString())
|
|
1337
|
+
.replace("${match}", "" + matchIdx)
|
|
1338
|
+
.replace("${matches}", matchCount.toLocaleString());
|
|
1339
|
+
this.matchInfoElem.textContent = info;
|
|
1340
|
+
}
|
|
1341
|
+
else {
|
|
1342
|
+
this.matchInfoElem.textContent = "";
|
|
1343
|
+
}
|
|
1344
|
+
}
|
|
1345
|
+
if (this.nextButton instanceof HTMLButtonElement) {
|
|
1346
|
+
this.nextButton.disabled = !matchCount;
|
|
1347
|
+
}
|
|
1348
|
+
if (this.prevButton instanceof HTMLButtonElement) {
|
|
1349
|
+
this.prevButton.disabled = !matchCount;
|
|
1350
|
+
}
|
|
1351
|
+
if (this.modeButton) {
|
|
1352
|
+
this.modeButton.disabled = !filterActive;
|
|
1353
|
+
this.modeButton.classList.toggle("wb-filter-hide", this.tree.filterMode === "hide");
|
|
1354
|
+
}
|
|
1355
|
+
}
|
|
1356
|
+
_connectControls() {
|
|
1357
|
+
const tree = this.tree;
|
|
1358
|
+
const connect = this.getPluginOption("connect");
|
|
1359
|
+
if (!connect) {
|
|
1360
|
+
return;
|
|
1361
|
+
}
|
|
1362
|
+
this.queryInput = elemFromSelector(connect.inputElem);
|
|
1363
|
+
if (!this.queryInput) {
|
|
1364
|
+
throw new Error(`Invalid 'filter.connect' option: ${connect.inputElem}.`);
|
|
1365
|
+
}
|
|
1366
|
+
this.prevButton = elemFromSelector(connect.prevButton);
|
|
1367
|
+
this.nextButton = elemFromSelector(connect.nextButton);
|
|
1368
|
+
this.modeButton = elemFromSelector(connect.modeButton);
|
|
1369
|
+
this.matchInfoElem = elemFromSelector(connect.matchInfoElem);
|
|
1370
|
+
if (this.prevButton) {
|
|
1371
|
+
onEvent(this.prevButton, "click", () => {
|
|
1372
|
+
tree.findRelatedNode(tree.getActiveNode() || tree.getFirstChild(), "prevMatch");
|
|
1373
|
+
this._updatedConnectedControls();
|
|
1374
|
+
});
|
|
1375
|
+
}
|
|
1376
|
+
if (this.nextButton) {
|
|
1377
|
+
onEvent(this.nextButton, "click", () => {
|
|
1378
|
+
tree.findRelatedNode(tree.getActiveNode() || tree.getFirstChild(), "nextMatch");
|
|
1379
|
+
this._updatedConnectedControls();
|
|
1380
|
+
});
|
|
1381
|
+
}
|
|
1382
|
+
if (this.modeButton) {
|
|
1383
|
+
onEvent(this.modeButton, "click", (e) => {
|
|
1384
|
+
if (!this.tree.filterMode) {
|
|
1385
|
+
return;
|
|
1386
|
+
}
|
|
1387
|
+
this.setPluginOption("mode", tree.filterMode === "dim" ? "hide" : "dim");
|
|
1388
|
+
});
|
|
1389
|
+
}
|
|
1390
|
+
onEvent(this.queryInput, "input", debounce((e) => {
|
|
1391
|
+
this.filterNodes(this.queryInput.value.trim(), {});
|
|
1392
|
+
}, 700));
|
|
1393
|
+
this._updatedConnectedControls();
|
|
1394
|
+
}
|
|
1318
1395
|
_applyFilterNoUpdate(filter, _opts) {
|
|
1319
1396
|
return this.tree.runWithDeferredUpdate(() => {
|
|
1320
1397
|
return this._applyFilterImpl(filter, _opts);
|
|
1321
1398
|
});
|
|
1322
1399
|
}
|
|
1323
1400
|
_applyFilterImpl(filter, _opts) {
|
|
1401
|
+
var _a;
|
|
1324
1402
|
let //temp,
|
|
1325
1403
|
count = 0;
|
|
1326
1404
|
const start = Date.now();
|
|
@@ -1402,11 +1480,11 @@
|
|
|
1402
1480
|
return !!res;
|
|
1403
1481
|
};
|
|
1404
1482
|
}
|
|
1405
|
-
tree.filterMode = opts.mode;
|
|
1406
|
-
// eslint-disable-next-line prefer-rest-params
|
|
1483
|
+
tree.filterMode = (_a = opts.mode) !== null && _a !== void 0 ? _a : "dim";
|
|
1484
|
+
// eslint-disable-next-line prefer-rest-params
|
|
1407
1485
|
this.lastFilterArgs = arguments;
|
|
1408
1486
|
tree.element.classList.toggle("wb-ext-filter-hide", !!hideMode);
|
|
1409
|
-
tree.element.classList.toggle("wb-ext-filter-dim",
|
|
1487
|
+
tree.element.classList.toggle("wb-ext-filter-dim", opts.mode === "dim");
|
|
1410
1488
|
tree.element.classList.toggle("wb-ext-filter-hide-expanders", !!opts.hideExpanders);
|
|
1411
1489
|
// Reset current filter
|
|
1412
1490
|
tree.root.subMatchCount = 0;
|
|
@@ -1415,10 +1493,6 @@
|
|
|
1415
1493
|
delete node.titleWithHighlight;
|
|
1416
1494
|
node.subMatchCount = 0;
|
|
1417
1495
|
});
|
|
1418
|
-
// statusNode = tree.root.findDirectChild(KEY_NODATA);
|
|
1419
|
-
// if (statusNode) {
|
|
1420
|
-
// statusNode.remove();
|
|
1421
|
-
// }
|
|
1422
1496
|
tree.setStatus(NodeStatusType.ok);
|
|
1423
1497
|
// Adjust node.hide, .match, and .subMatchCount properties
|
|
1424
1498
|
treeOpts.autoCollapse = false; // #528
|
|
@@ -1429,7 +1503,7 @@
|
|
|
1429
1503
|
let res = filter(node);
|
|
1430
1504
|
if (res === "skip") {
|
|
1431
1505
|
node.visit(function (c) {
|
|
1432
|
-
c.match =
|
|
1506
|
+
c.match = undefined;
|
|
1433
1507
|
}, true);
|
|
1434
1508
|
return "skip";
|
|
1435
1509
|
}
|
|
@@ -1440,7 +1514,7 @@
|
|
|
1440
1514
|
}
|
|
1441
1515
|
if (res) {
|
|
1442
1516
|
count++;
|
|
1443
|
-
node.match =
|
|
1517
|
+
node.match = count;
|
|
1444
1518
|
node.visitParents((p) => {
|
|
1445
1519
|
if (p !== node) {
|
|
1446
1520
|
p.subMatchCount += 1;
|
|
@@ -1467,6 +1541,7 @@
|
|
|
1467
1541
|
}
|
|
1468
1542
|
// Redraw whole tree
|
|
1469
1543
|
tree.logDebug(`Filter '${filter}' found ${count} nodes in ${Date.now() - start} ms.`);
|
|
1544
|
+
this._updatedConnectedControls();
|
|
1470
1545
|
return count;
|
|
1471
1546
|
}
|
|
1472
1547
|
/**
|
|
@@ -1511,34 +1586,22 @@
|
|
|
1511
1586
|
else {
|
|
1512
1587
|
tree.logWarn("updateFilter(): no filter active.");
|
|
1513
1588
|
}
|
|
1589
|
+
this._updatedConnectedControls();
|
|
1514
1590
|
}
|
|
1515
1591
|
/**
|
|
1516
1592
|
* [ext-filter] Reset the filter.
|
|
1517
1593
|
*/
|
|
1518
1594
|
clearFilter() {
|
|
1519
1595
|
const tree = this.tree;
|
|
1520
|
-
// statusNode = tree.root.findDirectChild(KEY_NODATA),
|
|
1521
|
-
// escapeTitles = tree.options.escapeTitles;
|
|
1522
1596
|
tree.enableUpdate(false);
|
|
1523
|
-
// if (statusNode) {
|
|
1524
|
-
// statusNode.remove();
|
|
1525
|
-
// }
|
|
1526
1597
|
tree.setStatus(NodeStatusType.ok);
|
|
1527
1598
|
// we also counted root node's subMatchCount
|
|
1528
1599
|
delete tree.root.match;
|
|
1529
1600
|
delete tree.root.subMatchCount;
|
|
1530
1601
|
tree.visit((node) => {
|
|
1531
|
-
// if (node.match && node._rowElem) {
|
|
1532
|
-
// let titleElem = node._rowElem.querySelector("span.wb-title")!;
|
|
1533
|
-
// node._callEvent("enhanceTitle", { titleElem: titleElem });
|
|
1534
|
-
// }
|
|
1535
1602
|
delete node.match;
|
|
1536
1603
|
delete node.subMatchCount;
|
|
1537
1604
|
delete node.titleWithHighlight;
|
|
1538
|
-
// if (node.subMatchBadge) {
|
|
1539
|
-
// node.subMatchBadge.remove();
|
|
1540
|
-
// delete node.subMatchBadge;
|
|
1541
|
-
// }
|
|
1542
1605
|
if (node._filterAutoExpanded && node.expanded) {
|
|
1543
1606
|
node.setExpanded(false, {
|
|
1544
1607
|
noAnimation: true,
|
|
@@ -1552,7 +1615,7 @@
|
|
|
1552
1615
|
tree.element.classList.remove(
|
|
1553
1616
|
// "wb-ext-filter",
|
|
1554
1617
|
"wb-ext-filter-dim", "wb-ext-filter-hide");
|
|
1555
|
-
|
|
1618
|
+
this._updatedConnectedControls();
|
|
1556
1619
|
tree.enableUpdate(true);
|
|
1557
1620
|
}
|
|
1558
1621
|
}
|
|
@@ -1595,8 +1658,8 @@
|
|
|
1595
1658
|
|
|
1596
1659
|
/*!
|
|
1597
1660
|
* Wunderbaum - ext-keynav
|
|
1598
|
-
* Copyright (c) 2021-
|
|
1599
|
-
* v0.
|
|
1661
|
+
* Copyright (c) 2021-2025, Martin Wendt. Released under the MIT license.
|
|
1662
|
+
* v0.13.0, Sat, 08 Mar 2025 14:16:31 GMT (https://github.com/mar10/wunderbaum)
|
|
1600
1663
|
*/
|
|
1601
1664
|
const QUICKSEARCH_DELAY = 500;
|
|
1602
1665
|
class KeynavExtension extends WunderbaumExtension {
|
|
@@ -1959,8 +2022,8 @@
|
|
|
1959
2022
|
|
|
1960
2023
|
/*!
|
|
1961
2024
|
* Wunderbaum - ext-logger
|
|
1962
|
-
* Copyright (c) 2021-
|
|
1963
|
-
* v0.
|
|
2025
|
+
* Copyright (c) 2021-2025, Martin Wendt. Released under the MIT license.
|
|
2026
|
+
* v0.13.0, Sat, 08 Mar 2025 14:16:31 GMT (https://github.com/mar10/wunderbaum)
|
|
1964
2027
|
*/
|
|
1965
2028
|
class LoggerExtension extends WunderbaumExtension {
|
|
1966
2029
|
constructor(tree) {
|
|
@@ -2001,8 +2064,8 @@
|
|
|
2001
2064
|
|
|
2002
2065
|
/*!
|
|
2003
2066
|
* Wunderbaum - ext-dnd
|
|
2004
|
-
* Copyright (c) 2021-
|
|
2005
|
-
* v0.
|
|
2067
|
+
* Copyright (c) 2021-2025, Martin Wendt. Released under the MIT license.
|
|
2068
|
+
* v0.13.0, Sat, 08 Mar 2025 14:16:31 GMT (https://github.com/mar10/wunderbaum)
|
|
2006
2069
|
*/
|
|
2007
2070
|
const nodeMimeType = "application/x-wunderbaum-node";
|
|
2008
2071
|
class DndExtension extends WunderbaumExtension {
|
|
@@ -2222,7 +2285,7 @@
|
|
|
2222
2285
|
viewportY >= height - sensitivity) {
|
|
2223
2286
|
// Mouse in bottom 20px area: scroll down
|
|
2224
2287
|
// sp.scrollTop = scrollTop + dndOpts.scrollSpeed;
|
|
2225
|
-
this.currentScrollDir =
|
|
2288
|
+
this.currentScrollDir = 1;
|
|
2226
2289
|
}
|
|
2227
2290
|
if (this.currentScrollDir) {
|
|
2228
2291
|
this.applyScrollDirThrottled();
|
|
@@ -2451,8 +2514,8 @@
|
|
|
2451
2514
|
|
|
2452
2515
|
/*!
|
|
2453
2516
|
* Wunderbaum - drag_observer
|
|
2454
|
-
* Copyright (c) 2021-
|
|
2455
|
-
* v0.
|
|
2517
|
+
* Copyright (c) 2021-2025, Martin Wendt. Released under the MIT license.
|
|
2518
|
+
* v0.13.0, Sat, 08 Mar 2025 14:16:31 GMT (https://github.com/mar10/wunderbaum)
|
|
2456
2519
|
*/
|
|
2457
2520
|
/**
|
|
2458
2521
|
* Convert mouse- and touch events to 'dragstart', 'drag', and 'dragstop'.
|
|
@@ -2600,8 +2663,8 @@
|
|
|
2600
2663
|
|
|
2601
2664
|
/*!
|
|
2602
2665
|
* Wunderbaum - common
|
|
2603
|
-
* Copyright (c) 2021-
|
|
2604
|
-
* v0.
|
|
2666
|
+
* Copyright (c) 2021-2025, Martin Wendt. Released under the MIT license.
|
|
2667
|
+
* v0.13.0, Sat, 08 Mar 2025 14:16:31 GMT (https://github.com/mar10/wunderbaum)
|
|
2605
2668
|
*/
|
|
2606
2669
|
const DEFAULT_DEBUGLEVEL = 3; // Replaced by rollup script
|
|
2607
2670
|
/**
|
|
@@ -2628,8 +2691,11 @@
|
|
|
2628
2691
|
// export const RECURSIVE_REQUEST_ERROR = "$recursive_request";
|
|
2629
2692
|
// export const INVALID_REQUEST_TARGET_ERROR = "$request_target_invalid";
|
|
2630
2693
|
/**
|
|
2631
|
-
* Default node icons
|
|
2632
|
-
*
|
|
2694
|
+
* Default node icons for icon libraries
|
|
2695
|
+
*
|
|
2696
|
+
* - 'bootstrap': {@link https://icons.getbootstrap.com}
|
|
2697
|
+
* - 'fontawesome6' {@link https://fontawesome.com/icons}
|
|
2698
|
+
*
|
|
2633
2699
|
*/
|
|
2634
2700
|
const iconMaps = {
|
|
2635
2701
|
bootstrap: {
|
|
@@ -2711,29 +2777,20 @@
|
|
|
2711
2777
|
// "Escape",
|
|
2712
2778
|
// ]);
|
|
2713
2779
|
/** Map `KeyEvent.key` to navigation action. */
|
|
2714
|
-
const
|
|
2715
|
-
" ": "toggleSelect",
|
|
2716
|
-
"+": "expand",
|
|
2717
|
-
Add: "expand",
|
|
2780
|
+
const KEY_TO_NAVIGATION_MAP = {
|
|
2718
2781
|
ArrowDown: "down",
|
|
2719
2782
|
ArrowLeft: "left",
|
|
2720
2783
|
ArrowRight: "right",
|
|
2721
2784
|
ArrowUp: "up",
|
|
2722
2785
|
Backspace: "parent",
|
|
2723
|
-
"/": "collapseAll",
|
|
2724
|
-
Divide: "collapseAll",
|
|
2725
2786
|
End: "lastCol",
|
|
2726
2787
|
Home: "firstCol",
|
|
2727
2788
|
"Control+End": "last",
|
|
2728
2789
|
"Control+Home": "first",
|
|
2729
2790
|
"Meta+ArrowDown": "last", // macOs
|
|
2730
2791
|
"Meta+ArrowUp": "first", // macOs
|
|
2731
|
-
"*": "expandAll",
|
|
2732
|
-
Multiply: "expandAll",
|
|
2733
2792
|
PageDown: "pageDown",
|
|
2734
2793
|
PageUp: "pageUp",
|
|
2735
|
-
"-": "collapse",
|
|
2736
|
-
Subtract: "collapse",
|
|
2737
2794
|
};
|
|
2738
2795
|
/** Return a callback that returns true if the node title matches the string
|
|
2739
2796
|
* or regular expression.
|
|
@@ -2770,10 +2827,12 @@
|
|
|
2770
2827
|
/**
|
|
2771
2828
|
* Convert 'flat' to 'nested' format.
|
|
2772
2829
|
*
|
|
2773
|
-
*
|
|
2774
|
-
* [
|
|
2775
|
-
*
|
|
2776
|
-
* [
|
|
2830
|
+
* Flat node entry format:
|
|
2831
|
+
* [PARENT_IDX, {KEY_VALUE_ARGS}]
|
|
2832
|
+
* or, if N _positional re defined:
|
|
2833
|
+
* [PARENT_IDX, POSITIONAL_ARG_1, POSITIONAL_ARG_2, ..., POSITIONAL_ARG_N]
|
|
2834
|
+
* Even if _positional additional are defined, KEY_VALUE_ARGS can be appended:
|
|
2835
|
+
* [PARENT_IDX, POSITIONAL_ARG_1, ..., {KEY_VALUE_ARGS}]
|
|
2777
2836
|
*
|
|
2778
2837
|
* 1. Parent-referencing list is converted to a list of nested dicts with
|
|
2779
2838
|
* optional `children` properties.
|
|
@@ -2782,10 +2841,11 @@
|
|
|
2782
2841
|
function unflattenSource(source) {
|
|
2783
2842
|
var _a, _b, _c;
|
|
2784
2843
|
const { _format, _keyMap = {}, _positional = [], children } = source;
|
|
2844
|
+
const _positionalCount = _positional.length;
|
|
2785
2845
|
if (_format !== "flat") {
|
|
2786
2846
|
throw new Error(`Expected source._format: "flat", but got ${_format}`);
|
|
2787
2847
|
}
|
|
2788
|
-
if (
|
|
2848
|
+
if (_positionalCount && _positional.includes("children")) {
|
|
2789
2849
|
throw new Error(`source._positional must not include "children": ${_positional}`);
|
|
2790
2850
|
}
|
|
2791
2851
|
let longToShort = _keyMap;
|
|
@@ -2799,7 +2859,7 @@
|
|
|
2799
2859
|
longToShort[value] = key;
|
|
2800
2860
|
}
|
|
2801
2861
|
}
|
|
2802
|
-
const positionalShort = _positional.map((e) => longToShort[e]);
|
|
2862
|
+
const positionalShort = _positional.map((e) => { var _a; return (_a = longToShort[e]) !== null && _a !== void 0 ? _a : e; });
|
|
2803
2863
|
const newChildren = [];
|
|
2804
2864
|
const keyToNodeMap = {};
|
|
2805
2865
|
const indexToNodeMap = {};
|
|
@@ -2809,19 +2869,32 @@
|
|
|
2809
2869
|
// Node entry format:
|
|
2810
2870
|
// [PARENT_ID, [POSITIONAL_ARGS]]
|
|
2811
2871
|
// or
|
|
2812
|
-
// [PARENT_ID,
|
|
2813
|
-
|
|
2872
|
+
// [PARENT_ID, POSITIONAL_ARG_1, POSITIONAL_ARG_2, ..., {KEY_VALUE_ARGS}]
|
|
2873
|
+
let kwargs;
|
|
2874
|
+
const [parentId, ...args] = nodeTuple;
|
|
2875
|
+
if (args.length === _positionalCount) {
|
|
2876
|
+
kwargs = {};
|
|
2877
|
+
}
|
|
2878
|
+
else if (args.length === _positionalCount + 1) {
|
|
2879
|
+
kwargs = args.pop();
|
|
2880
|
+
if (typeof kwargs !== "object") {
|
|
2881
|
+
throw new Error(`unflattenSource: Expected dict as last tuple element: ${nodeTuple}`);
|
|
2882
|
+
}
|
|
2883
|
+
}
|
|
2884
|
+
else {
|
|
2885
|
+
throw new Error(`unflattenSource: unexpected tuple length: ${nodeTuple}`);
|
|
2886
|
+
}
|
|
2814
2887
|
// Free up some memory as we go
|
|
2815
2888
|
nodeTuple[1] = null;
|
|
2816
2889
|
if (nodeTuple[2] != null) {
|
|
2817
2890
|
nodeTuple[2] = null;
|
|
2818
2891
|
}
|
|
2819
|
-
// console.log("flatten", parentId, args, kwargs)
|
|
2820
2892
|
// We keep `kwargs` as our new node definition. Then we add all positional
|
|
2821
2893
|
// values to this object:
|
|
2822
2894
|
args.forEach((val, positionalIdx) => {
|
|
2823
2895
|
kwargs[positionalShort[positionalIdx]] = val;
|
|
2824
2896
|
});
|
|
2897
|
+
args.length = 0;
|
|
2825
2898
|
// Find the parent node. `null` means 'toplevel'. PARENT_ID may be the numeric
|
|
2826
2899
|
// index of the source.children list. If PARENT_ID is a string, we search
|
|
2827
2900
|
// a parent with node.key of this value.
|
|
@@ -2940,8 +3013,8 @@
|
|
|
2940
3013
|
|
|
2941
3014
|
/*!
|
|
2942
3015
|
* Wunderbaum - ext-grid
|
|
2943
|
-
* Copyright (c) 2021-
|
|
2944
|
-
* v0.
|
|
3016
|
+
* Copyright (c) 2021-2025, Martin Wendt. Released under the MIT license.
|
|
3017
|
+
* v0.13.0, Sat, 08 Mar 2025 14:16:31 GMT (https://github.com/mar10/wunderbaum)
|
|
2945
3018
|
*/
|
|
2946
3019
|
class GridExtension extends WunderbaumExtension {
|
|
2947
3020
|
constructor(tree) {
|
|
@@ -3031,8 +3104,8 @@
|
|
|
3031
3104
|
|
|
3032
3105
|
/*!
|
|
3033
3106
|
* Wunderbaum - deferred
|
|
3034
|
-
* Copyright (c) 2021-
|
|
3035
|
-
* v0.
|
|
3107
|
+
* Copyright (c) 2021-2025, Martin Wendt. Released under the MIT license.
|
|
3108
|
+
* v0.13.0, Sat, 08 Mar 2025 14:16:31 GMT (https://github.com/mar10/wunderbaum)
|
|
3036
3109
|
*/
|
|
3037
3110
|
/**
|
|
3038
3111
|
* Implement a ES6 Promise, that exposes a resolve() and reject() method.
|
|
@@ -3084,8 +3157,8 @@
|
|
|
3084
3157
|
|
|
3085
3158
|
/*!
|
|
3086
3159
|
* Wunderbaum - wunderbaum_node
|
|
3087
|
-
* Copyright (c) 2021-
|
|
3088
|
-
* v0.
|
|
3160
|
+
* Copyright (c) 2021-2025, Martin Wendt. Released under the MIT license.
|
|
3161
|
+
* v0.13.0, Sat, 08 Mar 2025 14:16:31 GMT (https://github.com/mar10/wunderbaum)
|
|
3089
3162
|
*/
|
|
3090
3163
|
/** WunderbaumNode properties that can be passed with source data.
|
|
3091
3164
|
* (Any other source properties will be stored as `node.data.PROP`.)
|
|
@@ -3867,7 +3940,7 @@
|
|
|
3867
3940
|
isParentOf(other) {
|
|
3868
3941
|
return other && other.parent === this;
|
|
3869
3942
|
}
|
|
3870
|
-
/**
|
|
3943
|
+
/** Return true if this node is partially loaded. @experimental */
|
|
3871
3944
|
isPartload() {
|
|
3872
3945
|
return !!this._partload;
|
|
3873
3946
|
}
|
|
@@ -4325,10 +4398,11 @@
|
|
|
4325
4398
|
* @param options
|
|
4326
4399
|
*/
|
|
4327
4400
|
async navigate(where, options) {
|
|
4401
|
+
var _a;
|
|
4328
4402
|
// Allow to pass 'ArrowLeft' instead of 'left'
|
|
4329
|
-
|
|
4403
|
+
const navType = ((_a = KEY_TO_NAVIGATION_MAP[where]) !== null && _a !== void 0 ? _a : where);
|
|
4330
4404
|
// Otherwise activate or focus the related node
|
|
4331
|
-
const node = this.findRelatedNode(
|
|
4405
|
+
const node = this.findRelatedNode(navType);
|
|
4332
4406
|
if (!node) {
|
|
4333
4407
|
this.logWarn(`Could not find related node '${where}'.`);
|
|
4334
4408
|
return Promise.resolve(this);
|
|
@@ -4425,86 +4499,17 @@
|
|
|
4425
4499
|
renderColInfosById: renderColInfosById,
|
|
4426
4500
|
};
|
|
4427
4501
|
}
|
|
4428
|
-
_createIcon(
|
|
4429
|
-
|
|
4430
|
-
|
|
4431
|
-
|
|
4432
|
-
|
|
4433
|
-
}
|
|
4434
|
-
else if (this._isLoading && showLoading) {
|
|
4435
|
-
// Status nodes, or nodes without expander (< minExpandLevel) should
|
|
4436
|
-
// display the 'loading' status with the i.wb-icon span
|
|
4437
|
-
icon = iconMap.loading;
|
|
4438
|
-
}
|
|
4439
|
-
if (icon === false) {
|
|
4440
|
-
return null; // explicitly disabled: don't try default icons
|
|
4441
|
-
}
|
|
4442
|
-
if (typeof icon === "string") ;
|
|
4443
|
-
else if (this.statusNodeType) {
|
|
4444
|
-
icon = iconMap[this.statusNodeType];
|
|
4445
|
-
}
|
|
4446
|
-
else if (this.expanded) {
|
|
4447
|
-
icon = iconMap.folderOpen;
|
|
4448
|
-
}
|
|
4449
|
-
else if (this.children) {
|
|
4450
|
-
icon = iconMap.folder;
|
|
4451
|
-
}
|
|
4452
|
-
else if (this.lazy) {
|
|
4453
|
-
icon = iconMap.folderLazy;
|
|
4454
|
-
}
|
|
4455
|
-
else {
|
|
4456
|
-
icon = iconMap.doc;
|
|
4457
|
-
}
|
|
4458
|
-
// this.log("_createIcon: " + icon);
|
|
4459
|
-
if (!icon) {
|
|
4460
|
-
iconSpan = document.createElement("i");
|
|
4461
|
-
iconSpan.className = "wb-icon";
|
|
4462
|
-
}
|
|
4463
|
-
else if (icon.indexOf("<") >= 0) {
|
|
4464
|
-
// HTML
|
|
4465
|
-
iconSpan = elemFromHtml(icon);
|
|
4466
|
-
}
|
|
4467
|
-
else if (TEST_IMG.test(icon)) {
|
|
4468
|
-
// Image URL
|
|
4469
|
-
iconSpan = elemFromHtml(`<i class="wb-icon" style="background-image: url('${icon}');">`);
|
|
4470
|
-
}
|
|
4471
|
-
else {
|
|
4472
|
-
// Class name
|
|
4473
|
-
iconSpan = document.createElement("i");
|
|
4474
|
-
iconSpan.className = "wb-icon " + icon;
|
|
4475
|
-
}
|
|
4476
|
-
if (replaceChild) {
|
|
4477
|
-
parentElem.replaceChild(iconSpan, replaceChild);
|
|
4478
|
-
}
|
|
4479
|
-
else {
|
|
4480
|
-
parentElem.appendChild(iconSpan);
|
|
4481
|
-
}
|
|
4482
|
-
// Event handler `tree.iconBadge` can return a badge text or HTMLSpanElement
|
|
4483
|
-
const cbRes = this._callEvent("iconBadge", { iconSpan: iconSpan });
|
|
4484
|
-
let badge = null;
|
|
4485
|
-
if (cbRes != null && cbRes !== false) {
|
|
4486
|
-
let classes = "";
|
|
4487
|
-
let tooltip = "";
|
|
4488
|
-
if (isPlainObject(cbRes)) {
|
|
4489
|
-
badge = "" + cbRes.badge;
|
|
4490
|
-
classes = cbRes.badgeClass ? " " + cbRes.badgeClass : "";
|
|
4491
|
-
tooltip = cbRes.badgeTooltip ? ` title="${cbRes.badgeTooltip}"` : "";
|
|
4492
|
-
}
|
|
4493
|
-
else if (typeof cbRes === "number") {
|
|
4494
|
-
badge = "" + cbRes;
|
|
4502
|
+
_createIcon(parentElem, replaceChild, showLoading) {
|
|
4503
|
+
const iconElem = this.tree._createNodeIcon(this, showLoading, true);
|
|
4504
|
+
if (iconElem) {
|
|
4505
|
+
if (replaceChild) {
|
|
4506
|
+
parentElem.replaceChild(iconElem, replaceChild);
|
|
4495
4507
|
}
|
|
4496
4508
|
else {
|
|
4497
|
-
|
|
4498
|
-
}
|
|
4499
|
-
if (typeof badge === "string") {
|
|
4500
|
-
badge = elemFromHtml(`<span class="wb-badge${classes}"${tooltip}>${escapeHtml(badge)}</span>`);
|
|
4501
|
-
}
|
|
4502
|
-
if (badge) {
|
|
4503
|
-
iconSpan.append(badge);
|
|
4509
|
+
parentElem.appendChild(iconElem);
|
|
4504
4510
|
}
|
|
4505
4511
|
}
|
|
4506
|
-
|
|
4507
|
-
return iconSpan;
|
|
4512
|
+
return iconElem;
|
|
4508
4513
|
}
|
|
4509
4514
|
/**
|
|
4510
4515
|
* Create a whole new `<div class="wb-row">` element.
|
|
@@ -4559,7 +4564,7 @@
|
|
|
4559
4564
|
}
|
|
4560
4565
|
// Render the icon (show a 'loading' icon if we do not have an expander that
|
|
4561
4566
|
// we would prefer).
|
|
4562
|
-
const iconSpan = this._createIcon(
|
|
4567
|
+
const iconSpan = this._createIcon(nodeElem, null, !expanderSpan);
|
|
4563
4568
|
if (iconSpan) {
|
|
4564
4569
|
ofsTitlePx += ICON_WIDTH;
|
|
4565
4570
|
}
|
|
@@ -4791,7 +4796,7 @@
|
|
|
4791
4796
|
// Update icon (if not opts.isNew, which would rebuild markup anyway)
|
|
4792
4797
|
const iconSpan = nodeElem.querySelector("i.wb-icon");
|
|
4793
4798
|
if (iconSpan) {
|
|
4794
|
-
this._createIcon(
|
|
4799
|
+
this._createIcon(nodeElem, iconSpan, !expanderSpan);
|
|
4795
4800
|
}
|
|
4796
4801
|
}
|
|
4797
4802
|
// Adjust column width
|
|
@@ -5169,7 +5174,8 @@
|
|
|
5169
5174
|
case undefined:
|
|
5170
5175
|
changed = this.selected || !this._partsel;
|
|
5171
5176
|
this.selected = false;
|
|
5172
|
-
|
|
5177
|
+
// #110: end nodess cannot have a `_partsel` flag
|
|
5178
|
+
this._partsel = this.hasChildren() ? true : false;
|
|
5173
5179
|
break;
|
|
5174
5180
|
default:
|
|
5175
5181
|
error(`Invalid state: ${state}`);
|
|
@@ -5339,7 +5345,7 @@
|
|
|
5339
5345
|
assert(data.statusNodeType, "Not a status node");
|
|
5340
5346
|
assert(!firstChild || !firstChild.isStatusNode(), "Child must not be a status node");
|
|
5341
5347
|
statusNode = this.addNode(data, "prependChild");
|
|
5342
|
-
statusNode.match =
|
|
5348
|
+
statusNode.match = -1; // Mark as 'match' to avoid hiding
|
|
5343
5349
|
tree.update(ChangeType.structure);
|
|
5344
5350
|
return statusNode;
|
|
5345
5351
|
};
|
|
@@ -5630,8 +5636,8 @@
|
|
|
5630
5636
|
|
|
5631
5637
|
/*!
|
|
5632
5638
|
* Wunderbaum - ext-edit
|
|
5633
|
-
* Copyright (c) 2021-
|
|
5634
|
-
* v0.
|
|
5639
|
+
* Copyright (c) 2021-2025, Martin Wendt. Released under the MIT license.
|
|
5640
|
+
* v0.13.0, Sat, 08 Mar 2025 14:16:31 GMT (https://github.com/mar10/wunderbaum)
|
|
5635
5641
|
*/
|
|
5636
5642
|
// const START_MARKER = "\uFFF7";
|
|
5637
5643
|
class EditExtension extends WunderbaumExtension {
|
|
@@ -5950,7 +5956,7 @@
|
|
|
5950
5956
|
newNode.setClass("wb-edit-new");
|
|
5951
5957
|
this.relatedNode = node;
|
|
5952
5958
|
// Don't filter new nodes:
|
|
5953
|
-
newNode.match =
|
|
5959
|
+
newNode.match = -1;
|
|
5954
5960
|
newNode.makeVisible({ noAnimation: true }).then(() => {
|
|
5955
5961
|
this.startEditTitle(newNode);
|
|
5956
5962
|
});
|
|
@@ -5962,12 +5968,12 @@
|
|
|
5962
5968
|
*
|
|
5963
5969
|
* A treegrid control.
|
|
5964
5970
|
*
|
|
5965
|
-
* Copyright (c) 2021-
|
|
5971
|
+
* Copyright (c) 2021-2025, Martin Wendt (https://wwWendt.de).
|
|
5966
5972
|
* https://github.com/mar10/wunderbaum
|
|
5967
5973
|
*
|
|
5968
5974
|
* Released under the MIT license.
|
|
5969
|
-
* @version v0.
|
|
5970
|
-
* @date
|
|
5975
|
+
* @version v0.13.0
|
|
5976
|
+
* @date Sat, 08 Mar 2025 14:16:31 GMT
|
|
5971
5977
|
*/
|
|
5972
5978
|
// import "./wunderbaum.scss";
|
|
5973
5979
|
class WbSystemRoot extends WunderbaumNode {
|
|
@@ -5988,7 +5994,7 @@
|
|
|
5988
5994
|
*/
|
|
5989
5995
|
class Wunderbaum {
|
|
5990
5996
|
/** Currently active node if any.
|
|
5991
|
-
* Use @link
|
|
5997
|
+
* Use {@link WunderbaumNode.setActive|setActive} to modify.
|
|
5992
5998
|
*/
|
|
5993
5999
|
get activeNode() {
|
|
5994
6000
|
var _a;
|
|
@@ -5996,7 +6002,7 @@
|
|
|
5996
6002
|
return ((_a = this._activeNode) === null || _a === void 0 ? void 0 : _a.tree) ? this._activeNode : null;
|
|
5997
6003
|
}
|
|
5998
6004
|
/** Current node hat has keyboard focus if any.
|
|
5999
|
-
* Use @link
|
|
6005
|
+
* Use {@link WunderbaumNode.setFocus|setFocus()} to modify.
|
|
6000
6006
|
*/
|
|
6001
6007
|
get focusNode() {
|
|
6002
6008
|
var _a;
|
|
@@ -6028,6 +6034,9 @@
|
|
|
6028
6034
|
// --- SELECT ---
|
|
6029
6035
|
// /** @internal */
|
|
6030
6036
|
// public selectRangeAnchor: WunderbaumNode | null = null;
|
|
6037
|
+
// --- BREADCRUMB ---
|
|
6038
|
+
/** Filter options (used as defaults for calls to {@link Wunderbaum.filterNodes} ) */
|
|
6039
|
+
this.breadcrumb = null;
|
|
6031
6040
|
// --- FILTER ---
|
|
6032
6041
|
/** Filter options (used as defaults for calls to {@link Wunderbaum.filterNodes} ) */
|
|
6033
6042
|
this.filterMode = null;
|
|
@@ -6062,10 +6071,10 @@
|
|
|
6062
6071
|
emptyChildListExpandable: false,
|
|
6063
6072
|
// updateThrottleWait: 200,
|
|
6064
6073
|
skeleton: false,
|
|
6065
|
-
connectTopBreadcrumb: null,
|
|
6074
|
+
connectTopBreadcrumb: null,
|
|
6066
6075
|
selectMode: "multi", // SelectModeType
|
|
6067
6076
|
// --- KeyNav ---
|
|
6068
|
-
navigationModeOption: null, // NavModeEnum
|
|
6077
|
+
navigationModeOption: null, // NavModeEnum,
|
|
6069
6078
|
quicksearch: true,
|
|
6070
6079
|
// --- Events ---
|
|
6071
6080
|
iconBadge: null,
|
|
@@ -6077,8 +6086,11 @@
|
|
|
6077
6086
|
strings: {
|
|
6078
6087
|
loadError: "Error",
|
|
6079
6088
|
loading: "Loading...",
|
|
6080
|
-
// loading: "Loading…",
|
|
6081
6089
|
noData: "No data",
|
|
6090
|
+
breadcrumbDelimiter: " » ",
|
|
6091
|
+
queryResult: "Found ${matches} of ${count}",
|
|
6092
|
+
noMatch: "No results",
|
|
6093
|
+
matchIndex: "${match} of ${matches}",
|
|
6082
6094
|
},
|
|
6083
6095
|
}, options));
|
|
6084
6096
|
const readyDeferred = new Deferred();
|
|
@@ -6146,7 +6158,7 @@
|
|
|
6146
6158
|
const wantHeader = opts.header == null ? this.columns.length > 1 : !!opts.header;
|
|
6147
6159
|
if (this.headerElement) {
|
|
6148
6160
|
// User existing header markup to define `this.columns`
|
|
6149
|
-
assert(!this.columns, "`opts.columns` must not be set if markup already contains a header");
|
|
6161
|
+
assert(!this.columns, "`opts.columns` must not be set if table markup already contains a header");
|
|
6150
6162
|
this.columns = [];
|
|
6151
6163
|
const rowElement = this.headerElement.querySelector("div.wb-row");
|
|
6152
6164
|
for (const colDiv of rowElement.querySelectorAll("div")) {
|
|
@@ -6184,6 +6196,19 @@
|
|
|
6184
6196
|
this.headerElement =
|
|
6185
6197
|
this.element.querySelector("div.wb-header");
|
|
6186
6198
|
this.element.classList.toggle("wb-grid", this.columns.length > 1);
|
|
6199
|
+
if (this.options.connectTopBreadcrumb) {
|
|
6200
|
+
this.breadcrumb = elemFromSelector(this.options.connectTopBreadcrumb);
|
|
6201
|
+
assert(!this.breadcrumb || this.breadcrumb.innerHTML != null, `Invalid 'connectTopBreadcrumb' option: ${this.breadcrumb}.`);
|
|
6202
|
+
this.breadcrumb.addEventListener("click", (e) => {
|
|
6203
|
+
// const node = Wunderbaum.getNode(e)!;
|
|
6204
|
+
const elem = e.target;
|
|
6205
|
+
if (elem && elem.matches("a.wb-breadcrumb")) {
|
|
6206
|
+
const node = this.keyMap.get(elem.dataset.key);
|
|
6207
|
+
node === null || node === void 0 ? void 0 : node.setActive();
|
|
6208
|
+
e.preventDefault();
|
|
6209
|
+
}
|
|
6210
|
+
});
|
|
6211
|
+
}
|
|
6187
6212
|
this._initExtensions();
|
|
6188
6213
|
// --- apply initial options
|
|
6189
6214
|
["enabled", "fixedCol"].forEach((optName) => {
|
|
@@ -6194,8 +6219,7 @@
|
|
|
6194
6219
|
// --- Load initial data
|
|
6195
6220
|
if (opts.source) {
|
|
6196
6221
|
if (opts.showSpinner) {
|
|
6197
|
-
this.nodeListElement.innerHTML =
|
|
6198
|
-
"<progress class='spinner'>loading...</progress>";
|
|
6222
|
+
this.nodeListElement.innerHTML = `<progress class='spinner'>${opts.strings.loading}</progress>`;
|
|
6199
6223
|
}
|
|
6200
6224
|
this.load(opts.source)
|
|
6201
6225
|
.then(() => {
|
|
@@ -6548,7 +6572,10 @@
|
|
|
6548
6572
|
});
|
|
6549
6573
|
return node;
|
|
6550
6574
|
}
|
|
6551
|
-
/** Return the topmost visible node in the viewport.
|
|
6575
|
+
/** Return the topmost visible node in the viewport.
|
|
6576
|
+
* @param complete If `false`, the node is considered visible if at least one
|
|
6577
|
+
* pixel is visible.
|
|
6578
|
+
*/
|
|
6552
6579
|
getTopmostVpNode(complete = true) {
|
|
6553
6580
|
const rowHeight = this.options.rowHeightPx;
|
|
6554
6581
|
const gracePx = 1; // ignore subpixel scrolling
|
|
@@ -6581,24 +6608,19 @@
|
|
|
6581
6608
|
bottomIdx = Math.min(bottomIdx, this.count(true) - 1);
|
|
6582
6609
|
return this._getNodeByRowIdx(bottomIdx);
|
|
6583
6610
|
}
|
|
6584
|
-
/** Return
|
|
6585
|
-
|
|
6611
|
+
/** Return following visible node in the viewport. */
|
|
6612
|
+
_getNextNodeInView(node, options) {
|
|
6613
|
+
let ofs = (options === null || options === void 0 ? void 0 : options.ofs) || 1;
|
|
6614
|
+
const reverse = !!(options === null || options === void 0 ? void 0 : options.reverse);
|
|
6586
6615
|
this.visitRows((n) => {
|
|
6587
6616
|
node = n;
|
|
6588
|
-
if (
|
|
6617
|
+
if ((options === null || options === void 0 ? void 0 : options.cb) && options.cb(n)) {
|
|
6589
6618
|
return false;
|
|
6590
6619
|
}
|
|
6591
|
-
}, { reverse: true, start: node || this.getActiveNode() });
|
|
6592
|
-
return node;
|
|
6593
|
-
}
|
|
6594
|
-
/** Return following visible node in the viewport. */
|
|
6595
|
-
_getNextNodeInView(node, ofs = 1) {
|
|
6596
|
-
this.visitRows((n) => {
|
|
6597
|
-
node = n;
|
|
6598
6620
|
if (ofs-- <= 0) {
|
|
6599
6621
|
return false;
|
|
6600
6622
|
}
|
|
6601
|
-
}, { reverse:
|
|
6623
|
+
}, { reverse: reverse, start: node || this.getActiveNode() });
|
|
6602
6624
|
return node;
|
|
6603
6625
|
}
|
|
6604
6626
|
/**
|
|
@@ -6718,9 +6740,11 @@
|
|
|
6718
6740
|
case "first":
|
|
6719
6741
|
case "last":
|
|
6720
6742
|
case "left":
|
|
6743
|
+
case "nextMatch":
|
|
6721
6744
|
case "pageDown":
|
|
6722
6745
|
case "pageUp":
|
|
6723
6746
|
case "parent":
|
|
6747
|
+
case "prevMatch":
|
|
6724
6748
|
case "right":
|
|
6725
6749
|
case "up":
|
|
6726
6750
|
return node.navigate(cmd);
|
|
@@ -6912,6 +6936,11 @@
|
|
|
6912
6936
|
count(visible = false) {
|
|
6913
6937
|
return visible ? this.treeRowCount : this.keyMap.size;
|
|
6914
6938
|
}
|
|
6939
|
+
/** Return the number of *unique* nodes in the data model, i.e. unique `node.refKey`.
|
|
6940
|
+
*/
|
|
6941
|
+
countUnique() {
|
|
6942
|
+
return this.refKeyMap.size;
|
|
6943
|
+
}
|
|
6915
6944
|
/** @internal sanity check. */
|
|
6916
6945
|
_check() {
|
|
6917
6946
|
let i = 0;
|
|
@@ -6970,12 +6999,14 @@
|
|
|
6970
6999
|
* and wrap-around at the end.
|
|
6971
7000
|
* Used by quicksearch and keyboard navigation.
|
|
6972
7001
|
*/
|
|
6973
|
-
findNextNode(match, startNode) {
|
|
7002
|
+
findNextNode(match, startNode, reverse = false) {
|
|
6974
7003
|
//, visibleOnly) {
|
|
6975
7004
|
let res = null;
|
|
6976
7005
|
const firstNode = this.getFirstChild();
|
|
7006
|
+
// Last visible node (calculation is expensive, so do only if we need it):
|
|
7007
|
+
const lastNode = reverse ? this.findRelatedNode(firstNode, "last") : null;
|
|
6977
7008
|
const matcher = typeof match === "string" ? makeNodeTitleStartMatcher(match) : match;
|
|
6978
|
-
startNode = startNode || firstNode;
|
|
7009
|
+
startNode = startNode || (reverse ? lastNode : firstNode);
|
|
6979
7010
|
function _checkNode(n) {
|
|
6980
7011
|
// console.log("_check " + n)
|
|
6981
7012
|
if (matcher(n)) {
|
|
@@ -6988,12 +7019,14 @@
|
|
|
6988
7019
|
this.visitRows(_checkNode, {
|
|
6989
7020
|
start: startNode,
|
|
6990
7021
|
includeSelf: false,
|
|
7022
|
+
reverse: reverse,
|
|
6991
7023
|
});
|
|
6992
7024
|
// Wrap around search
|
|
6993
7025
|
if (!res && startNode !== firstNode) {
|
|
6994
7026
|
this.visitRows(_checkNode, {
|
|
6995
|
-
start: firstNode,
|
|
7027
|
+
start: reverse ? lastNode : firstNode,
|
|
6996
7028
|
includeSelf: true,
|
|
7029
|
+
reverse: reverse,
|
|
6997
7030
|
});
|
|
6998
7031
|
}
|
|
6999
7032
|
return res;
|
|
@@ -7060,7 +7093,7 @@
|
|
|
7060
7093
|
// }
|
|
7061
7094
|
break;
|
|
7062
7095
|
case "up":
|
|
7063
|
-
res = this.
|
|
7096
|
+
res = this._getNextNodeInView(node, { reverse: true });
|
|
7064
7097
|
break;
|
|
7065
7098
|
case "down":
|
|
7066
7099
|
res = this._getNextNodeInView(node);
|
|
@@ -7073,7 +7106,10 @@
|
|
|
7073
7106
|
res = bottomNode;
|
|
7074
7107
|
}
|
|
7075
7108
|
else {
|
|
7076
|
-
res = this._getNextNodeInView(node,
|
|
7109
|
+
res = this._getNextNodeInView(node, {
|
|
7110
|
+
reverse: false,
|
|
7111
|
+
ofs: pageSize,
|
|
7112
|
+
});
|
|
7077
7113
|
}
|
|
7078
7114
|
}
|
|
7079
7115
|
break;
|
|
@@ -7088,10 +7124,23 @@
|
|
|
7088
7124
|
res = topNode;
|
|
7089
7125
|
}
|
|
7090
7126
|
else {
|
|
7091
|
-
res = this.
|
|
7127
|
+
res = this._getNextNodeInView(node, {
|
|
7128
|
+
reverse: true,
|
|
7129
|
+
ofs: pageSize,
|
|
7130
|
+
});
|
|
7092
7131
|
}
|
|
7093
7132
|
}
|
|
7094
7133
|
break;
|
|
7134
|
+
case "prevMatch":
|
|
7135
|
+
// fallthrough
|
|
7136
|
+
case "nextMatch":
|
|
7137
|
+
if (!this.isFilterActive) {
|
|
7138
|
+
this.logWarn(`${where}: Filter is not active.`);
|
|
7139
|
+
break;
|
|
7140
|
+
}
|
|
7141
|
+
res = this.findNextNode((n) => n.isMatched(), node, where === "prevMatch");
|
|
7142
|
+
res === null || res === void 0 ? void 0 : res.setActive();
|
|
7143
|
+
break;
|
|
7095
7144
|
default:
|
|
7096
7145
|
this.logWarn("Unknown relation '" + where + "'.");
|
|
7097
7146
|
}
|
|
@@ -7153,6 +7202,12 @@
|
|
|
7153
7202
|
getFirstChild() {
|
|
7154
7203
|
return this.root.getFirstChild();
|
|
7155
7204
|
}
|
|
7205
|
+
/**
|
|
7206
|
+
* Return the last top level node if any (not the invisible root node).
|
|
7207
|
+
*/
|
|
7208
|
+
getLastChild() {
|
|
7209
|
+
return this.root.getLastChild();
|
|
7210
|
+
}
|
|
7156
7211
|
/**
|
|
7157
7212
|
* Return the node that currently has keyboard focus or null.
|
|
7158
7213
|
* Alias for {@link Wunderbaum.focusNode}.
|
|
@@ -7480,6 +7535,51 @@
|
|
|
7480
7535
|
_setFocusNode(node) {
|
|
7481
7536
|
this._focusNode = node;
|
|
7482
7537
|
}
|
|
7538
|
+
/** Return the current selection/expansion/activation status. @experimental */
|
|
7539
|
+
getState(options) {
|
|
7540
|
+
var _a, _b;
|
|
7541
|
+
let expandedKeys = undefined;
|
|
7542
|
+
if (options.expandedKeys !== false) {
|
|
7543
|
+
expandedKeys = [];
|
|
7544
|
+
for (const node of this) {
|
|
7545
|
+
if (node.expanded) {
|
|
7546
|
+
expandedKeys.push(node.key);
|
|
7547
|
+
}
|
|
7548
|
+
}
|
|
7549
|
+
}
|
|
7550
|
+
const state = {
|
|
7551
|
+
activeKey: (_b = (_a = this.activeNode) === null || _a === void 0 ? void 0 : _a.key) !== null && _b !== void 0 ? _b : null,
|
|
7552
|
+
activeColIdx: this.activeColIdx,
|
|
7553
|
+
selectedKeys: options.selectedKeys === false
|
|
7554
|
+
? undefined
|
|
7555
|
+
: this.getSelectedNodes().flatMap((n) => n.key),
|
|
7556
|
+
expandedKeys: expandedKeys,
|
|
7557
|
+
};
|
|
7558
|
+
return state;
|
|
7559
|
+
}
|
|
7560
|
+
/** Apply selection/expansion/activation status. @experimental */
|
|
7561
|
+
setState(state, options) {
|
|
7562
|
+
this.runWithDeferredUpdate(() => {
|
|
7563
|
+
var _a, _b;
|
|
7564
|
+
if (state.selectedKeys) {
|
|
7565
|
+
this.selectAll(false);
|
|
7566
|
+
for (const key of state.selectedKeys) {
|
|
7567
|
+
(_a = this.findKey(key)) === null || _a === void 0 ? void 0 : _a.setSelected(true);
|
|
7568
|
+
}
|
|
7569
|
+
}
|
|
7570
|
+
if (state.expandedKeys) {
|
|
7571
|
+
for (const key of state.expandedKeys) {
|
|
7572
|
+
(_b = this.findKey(key)) === null || _b === void 0 ? void 0 : _b.setExpanded(true);
|
|
7573
|
+
}
|
|
7574
|
+
}
|
|
7575
|
+
if (state.activeKey) {
|
|
7576
|
+
this.setActiveNode(state.activeKey);
|
|
7577
|
+
}
|
|
7578
|
+
if (state.activeColIdx != null) {
|
|
7579
|
+
this.setColumn(state.activeColIdx);
|
|
7580
|
+
}
|
|
7581
|
+
});
|
|
7582
|
+
}
|
|
7483
7583
|
update(change, node, options) {
|
|
7484
7584
|
// this.log(`update(${change}) node=${node}`);
|
|
7485
7585
|
if (!(node instanceof WunderbaumNode)) {
|
|
@@ -7761,11 +7861,11 @@
|
|
|
7761
7861
|
// }
|
|
7762
7862
|
return modified;
|
|
7763
7863
|
}
|
|
7764
|
-
_insertIcon(icon, elem) {
|
|
7765
|
-
|
|
7766
|
-
|
|
7767
|
-
|
|
7768
|
-
}
|
|
7864
|
+
// protected _insertIcon(icon: string, elem: HTMLElement) {
|
|
7865
|
+
// const iconElem = document.createElement("i");
|
|
7866
|
+
// iconElem.className = icon;
|
|
7867
|
+
// elem.appendChild(iconElem);
|
|
7868
|
+
// }
|
|
7769
7869
|
/** Create/update header markup from `this.columns` definition.
|
|
7770
7870
|
* @internal
|
|
7771
7871
|
*/
|
|
@@ -7863,6 +7963,104 @@
|
|
|
7863
7963
|
this._updateViewportImmediately();
|
|
7864
7964
|
}
|
|
7865
7965
|
}
|
|
7966
|
+
/** @internal */
|
|
7967
|
+
_createNodeIcon(node, showLoading, showBadge) {
|
|
7968
|
+
const iconMap = this.iconMap;
|
|
7969
|
+
let iconElem;
|
|
7970
|
+
let icon = node.getOption("icon");
|
|
7971
|
+
if (node._errorInfo) {
|
|
7972
|
+
icon = iconMap.error;
|
|
7973
|
+
}
|
|
7974
|
+
else if (node._isLoading && showLoading) {
|
|
7975
|
+
// Status nodes, or nodes without expander (< minExpandLevel) should
|
|
7976
|
+
// display the 'loading' status with the i.wb-icon span
|
|
7977
|
+
icon = iconMap.loading;
|
|
7978
|
+
}
|
|
7979
|
+
if (icon === false) {
|
|
7980
|
+
return null; // explicitly disabled: don't try default icons
|
|
7981
|
+
}
|
|
7982
|
+
if (typeof icon === "string") ;
|
|
7983
|
+
else if (node.statusNodeType) {
|
|
7984
|
+
icon = iconMap[node.statusNodeType];
|
|
7985
|
+
}
|
|
7986
|
+
else if (node.expanded) {
|
|
7987
|
+
icon = iconMap.folderOpen;
|
|
7988
|
+
}
|
|
7989
|
+
else if (node.children) {
|
|
7990
|
+
icon = iconMap.folder;
|
|
7991
|
+
}
|
|
7992
|
+
else if (node.lazy) {
|
|
7993
|
+
icon = iconMap.folderLazy;
|
|
7994
|
+
}
|
|
7995
|
+
else {
|
|
7996
|
+
icon = iconMap.doc;
|
|
7997
|
+
}
|
|
7998
|
+
if (!icon) {
|
|
7999
|
+
iconElem = document.createElement("i");
|
|
8000
|
+
iconElem.className = "wb-icon";
|
|
8001
|
+
}
|
|
8002
|
+
else if (icon.indexOf("<") >= 0) {
|
|
8003
|
+
// HTML
|
|
8004
|
+
iconElem = elemFromHtml(icon);
|
|
8005
|
+
}
|
|
8006
|
+
else if (TEST_IMG.test(icon)) {
|
|
8007
|
+
// Image URL
|
|
8008
|
+
iconElem = elemFromHtml(`<i class="wb-icon" style="background-image: url('${icon}');">`);
|
|
8009
|
+
}
|
|
8010
|
+
else {
|
|
8011
|
+
// Class name
|
|
8012
|
+
iconElem = document.createElement("i");
|
|
8013
|
+
iconElem.className = "wb-icon " + icon;
|
|
8014
|
+
}
|
|
8015
|
+
// Event handler `tree.iconBadge` can return a badge text or HTMLSpanElement
|
|
8016
|
+
const cbRes = showBadge && node._callEvent("iconBadge", { iconSpan: iconElem });
|
|
8017
|
+
let badge = null;
|
|
8018
|
+
if (cbRes != null && cbRes !== false) {
|
|
8019
|
+
let classes = "";
|
|
8020
|
+
let tooltip = "";
|
|
8021
|
+
if (isPlainObject(cbRes)) {
|
|
8022
|
+
badge = "" + cbRes.badge;
|
|
8023
|
+
classes = cbRes.badgeClass ? " " + cbRes.badgeClass : "";
|
|
8024
|
+
tooltip = cbRes.badgeTooltip ? ` title="${cbRes.badgeTooltip}"` : "";
|
|
8025
|
+
}
|
|
8026
|
+
else if (typeof cbRes === "number") {
|
|
8027
|
+
badge = "" + cbRes;
|
|
8028
|
+
}
|
|
8029
|
+
else {
|
|
8030
|
+
badge = cbRes; // string or HTMLSpanElement
|
|
8031
|
+
}
|
|
8032
|
+
if (typeof badge === "string") {
|
|
8033
|
+
badge = elemFromHtml(`<span class="wb-badge${classes}"${tooltip}>${escapeHtml(badge)}</span>`);
|
|
8034
|
+
}
|
|
8035
|
+
if (badge) {
|
|
8036
|
+
iconElem.append(badge);
|
|
8037
|
+
}
|
|
8038
|
+
}
|
|
8039
|
+
return iconElem;
|
|
8040
|
+
}
|
|
8041
|
+
_updateTopBreadcrumb() {
|
|
8042
|
+
const breadcrumb = this.breadcrumb;
|
|
8043
|
+
const topmost = this.getTopmostVpNode(true);
|
|
8044
|
+
const parentList = topmost === null || topmost === void 0 ? void 0 : topmost.getParentList(false, false);
|
|
8045
|
+
if (parentList === null || parentList === void 0 ? void 0 : parentList.length) {
|
|
8046
|
+
breadcrumb.innerHTML = "";
|
|
8047
|
+
for (const n of topmost.getParentList(false, false)) {
|
|
8048
|
+
const icon = this._createNodeIcon(n, false, false);
|
|
8049
|
+
if (icon) {
|
|
8050
|
+
breadcrumb.append(icon, " ");
|
|
8051
|
+
}
|
|
8052
|
+
const part = document.createElement("a");
|
|
8053
|
+
part.textContent = n.title;
|
|
8054
|
+
part.href = "#";
|
|
8055
|
+
part.classList.add("wb-breadcrumb");
|
|
8056
|
+
part.dataset.key = n.key;
|
|
8057
|
+
breadcrumb.append(part, this.options.strings.breadcrumbDelimiter);
|
|
8058
|
+
}
|
|
8059
|
+
}
|
|
8060
|
+
else {
|
|
8061
|
+
breadcrumb.innerHTML = " ";
|
|
8062
|
+
}
|
|
8063
|
+
}
|
|
7866
8064
|
/**
|
|
7867
8065
|
* This is the actual update method, which is wrapped inside a throttle method.
|
|
7868
8066
|
* It calls `updateColumns()` and `_updateRows()`.
|
|
@@ -7873,7 +8071,6 @@
|
|
|
7873
8071
|
* @internal
|
|
7874
8072
|
*/
|
|
7875
8073
|
_updateViewportImmediately() {
|
|
7876
|
-
var _a;
|
|
7877
8074
|
if (this._disableUpdateCount) {
|
|
7878
8075
|
this.log(`_updateViewportImmediately() IGNORED (disable level: ${this._disableUpdateCount}).`);
|
|
7879
8076
|
this._disableUpdateIgnoreCount++;
|
|
@@ -7920,11 +8117,8 @@
|
|
|
7920
8117
|
this._updateRows();
|
|
7921
8118
|
// console.profileEnd(`_updateViewportImmediately()`)
|
|
7922
8119
|
}
|
|
7923
|
-
if (this.
|
|
7924
|
-
|
|
7925
|
-
let path = (_a = this.getTopmostVpNode(true)) === null || _a === void 0 ? void 0 : _a.getPath(false, "title", " > ");
|
|
7926
|
-
path = path ? path + " >" : "";
|
|
7927
|
-
this.options.connectTopBreadcrumb.textContent = path;
|
|
8120
|
+
if (this.breadcrumb) {
|
|
8121
|
+
this._updateTopBreadcrumb();
|
|
7928
8122
|
}
|
|
7929
8123
|
this._callEvent("update");
|
|
7930
8124
|
}
|
|
@@ -7990,8 +8184,9 @@
|
|
|
7990
8184
|
// this.debug("render", opts);
|
|
7991
8185
|
const obsoleteNodes = new Set();
|
|
7992
8186
|
this.nodeListElement.childNodes.forEach((elem) => {
|
|
7993
|
-
|
|
7994
|
-
|
|
8187
|
+
if (elem._wb_node) {
|
|
8188
|
+
obsoleteNodes.add(elem._wb_node);
|
|
8189
|
+
}
|
|
7995
8190
|
});
|
|
7996
8191
|
let idx = 0;
|
|
7997
8192
|
let top = 0;
|
|
@@ -8292,7 +8487,7 @@
|
|
|
8292
8487
|
}
|
|
8293
8488
|
Wunderbaum.sequence = 0;
|
|
8294
8489
|
/** Wunderbaum release version number "MAJOR.MINOR.PATCH". */
|
|
8295
|
-
Wunderbaum.version = "v0.
|
|
8490
|
+
Wunderbaum.version = "v0.13.0"; // Set to semver by 'grunt release'
|
|
8296
8491
|
/** Expose some useful methods of the util.ts module as `Wunderbaum.util`. */
|
|
8297
8492
|
Wunderbaum.util = util;
|
|
8298
8493
|
|