wunderbaum 0.2.0 → 0.3.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/LICENSE +1 -1
- package/README.md +1 -7
- package/dist/wunderbaum.css +3 -3
- package/dist/wunderbaum.d.ts +190 -101
- package/dist/wunderbaum.esm.js +422 -206
- package/dist/wunderbaum.esm.min.js +35 -35
- package/dist/wunderbaum.esm.min.js.map +1 -1
- package/dist/wunderbaum.umd.js +422 -206
- package/dist/wunderbaum.umd.min.js +44 -44
- package/dist/wunderbaum.umd.min.js.map +1 -1
- package/package.json +1 -1
- package/src/common.ts +9 -1
- package/src/deferred.ts +1 -1
- package/src/drag_observer.ts +1 -1
- package/src/types.ts +99 -70
- package/src/util.ts +61 -10
- package/src/wb_ext_dnd.ts +27 -7
- package/src/wb_ext_edit.ts +3 -3
- package/src/wb_ext_filter.ts +1 -1
- 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 +1 -1
- package/src/wb_node.ts +130 -30
- package/src/wb_options.ts +9 -3
- package/src/wunderbaum.scss +6 -3
- package/src/wunderbaum.ts +199 -143
package/dist/wunderbaum.umd.js
CHANGED
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
|
|
7
7
|
/*!
|
|
8
8
|
* Wunderbaum - util
|
|
9
|
-
* Copyright (c) 2021-
|
|
10
|
-
* v0.
|
|
9
|
+
* Copyright (c) 2021-2023, Martin Wendt. Released under the MIT license.
|
|
10
|
+
* v0.3.0, Sat, 27 May 2023 04:56:52 GMT (https://github.com/mar10/wunderbaum)
|
|
11
11
|
*/
|
|
12
12
|
/** @module util */
|
|
13
13
|
/** Readable names for `MouseEvent.button` */
|
|
@@ -183,17 +183,19 @@
|
|
|
183
183
|
* the element is checked, unchecked, or indeterminate.
|
|
184
184
|
* For datetime input control a numerical value is assumed, etc.
|
|
185
185
|
*
|
|
186
|
-
* Common use case: store the new user input in
|
|
186
|
+
* Common use case: store the new user input in a `change` event handler:
|
|
187
187
|
*
|
|
188
188
|
* ```ts
|
|
189
189
|
* change: (e) => {
|
|
190
|
+
* const tree = e.tree;
|
|
191
|
+
* const node = e.node;
|
|
190
192
|
* // Read the value from the input control that triggered the change event:
|
|
191
|
-
* let value =
|
|
192
|
-
* //
|
|
193
|
-
*
|
|
193
|
+
* let value = tree.getValueFromElem(e.element);
|
|
194
|
+
* // and store it to the node model (assuming the column id matches the property name)
|
|
195
|
+
* node.data[e.info.colId] = value;
|
|
194
196
|
* },
|
|
195
197
|
* ```
|
|
196
|
-
* @param elem `<input>` or `<select>` element Also a parent `span.wb-col` is accepted.
|
|
198
|
+
* @param elem `<input>` or `<select>` element. Also a parent `span.wb-col` is accepted.
|
|
197
199
|
* @param coerce pass true to convert date/time inputs to `Date`.
|
|
198
200
|
* @returns the value
|
|
199
201
|
*/
|
|
@@ -257,6 +259,23 @@
|
|
|
257
259
|
* value is truethy, falsy, or `null`.
|
|
258
260
|
* For datetime input control a numerical value is assumed, etc.
|
|
259
261
|
*
|
|
262
|
+
* Common use case: update embedded input controls in a `render` event handler:
|
|
263
|
+
*
|
|
264
|
+
* ```ts
|
|
265
|
+
* render: (e) => {
|
|
266
|
+
* // e.node.log(e.type, e, e.node.data);
|
|
267
|
+
*
|
|
268
|
+
* for (const col of Object.values(e.renderColInfosById)) {
|
|
269
|
+
* switch (col.id) {
|
|
270
|
+
* default:
|
|
271
|
+
* // Assumption: we named column.id === node.data.NAME
|
|
272
|
+
* util.setValueToElem(col.elem, e.node.data[col.id]);
|
|
273
|
+
* break;
|
|
274
|
+
* }
|
|
275
|
+
* }
|
|
276
|
+
* },
|
|
277
|
+
* ```
|
|
278
|
+
*
|
|
260
279
|
* @param elem `<input>` or `<select>` element Also a parent `span.wb-col` is accepted.
|
|
261
280
|
* @param value a value that matches the target element.
|
|
262
281
|
*/
|
|
@@ -288,7 +307,6 @@
|
|
|
288
307
|
case "datetime":
|
|
289
308
|
case "datetime-local":
|
|
290
309
|
input.valueAsDate = new Date(value);
|
|
291
|
-
// input.valueAsDate = value; // breaks in Edge?
|
|
292
310
|
break;
|
|
293
311
|
case "number":
|
|
294
312
|
case "range":
|
|
@@ -300,7 +318,7 @@
|
|
|
300
318
|
}
|
|
301
319
|
break;
|
|
302
320
|
case "radio":
|
|
303
|
-
error(
|
|
321
|
+
error(`Not yet implemented: ${type}`);
|
|
304
322
|
// const name = input.name;
|
|
305
323
|
// const checked = input.parentElement!.querySelector(
|
|
306
324
|
// `input[name="${name}"]:checked`
|
|
@@ -327,7 +345,7 @@
|
|
|
327
345
|
}
|
|
328
346
|
}
|
|
329
347
|
}
|
|
330
|
-
/** Show/hide element by setting the `display`style to 'none'. */
|
|
348
|
+
/** Show/hide element by setting the `display` style to 'none'. */
|
|
331
349
|
function setElemDisplay(elem, flag) {
|
|
332
350
|
const style = elemFromSelector(elem).style;
|
|
333
351
|
if (flag) {
|
|
@@ -372,7 +390,23 @@
|
|
|
372
390
|
* The result also contains a prefix for modifiers if any, for example
|
|
373
391
|
* `"x"`, `"F2"`, `"Control+Home"`, or `"Shift+clickright"`.
|
|
374
392
|
* This is especially useful in `switch` statements, to make sure that modifier
|
|
375
|
-
* keys are considered and handled correctly
|
|
393
|
+
* keys are considered and handled correctly:
|
|
394
|
+
* ```ts
|
|
395
|
+
* const eventName = util.eventToString(e);
|
|
396
|
+
* switch (eventName) {
|
|
397
|
+
* case "+":
|
|
398
|
+
* case "Add":
|
|
399
|
+
* ...
|
|
400
|
+
* break;
|
|
401
|
+
* case "Enter":
|
|
402
|
+
* case "End":
|
|
403
|
+
* case "Control+End":
|
|
404
|
+
* case "Meta+ArrowDown":
|
|
405
|
+
* case "PageDown":
|
|
406
|
+
* ...
|
|
407
|
+
* break;
|
|
408
|
+
* }
|
|
409
|
+
* ```
|
|
376
410
|
*/
|
|
377
411
|
function eventToString(event) {
|
|
378
412
|
let key = event.key, et = event.type, s = [];
|
|
@@ -597,6 +631,21 @@
|
|
|
597
631
|
}
|
|
598
632
|
throw new Error("Cannot convert to Set<string>: " + val);
|
|
599
633
|
}
|
|
634
|
+
// /** Check if a string is contained in an Array or Set. */
|
|
635
|
+
// export function isAnyOf(s: string, items: Array<string>|Set<string>): boolean {
|
|
636
|
+
// return Array.prototype.includes.call(items, s)
|
|
637
|
+
// }
|
|
638
|
+
// /** Check if an Array or Set has at least one matching entry. */
|
|
639
|
+
// export function hasAnyOf(container: Array<string>|Set<string>, items: Array<string>): boolean {
|
|
640
|
+
// if (Array.isArray(container)) {
|
|
641
|
+
// return container.some(v => )
|
|
642
|
+
// }
|
|
643
|
+
// return container.some(v => {})
|
|
644
|
+
// // const container = toSet(items);
|
|
645
|
+
// // const itemSet = toSet(items);
|
|
646
|
+
// // Array.prototype.includes
|
|
647
|
+
// // throw new Error("Cannot convert to Set<string>: " + val);
|
|
648
|
+
// }
|
|
600
649
|
/** Return a canonical string representation for an object's type (e.g. 'array', 'number', ...). */
|
|
601
650
|
function type(obj) {
|
|
602
651
|
return Object.prototype.toString
|
|
@@ -712,28 +761,40 @@
|
|
|
712
761
|
|
|
713
762
|
/*!
|
|
714
763
|
* Wunderbaum - types
|
|
715
|
-
* Copyright (c) 2021-
|
|
716
|
-
* v0.
|
|
764
|
+
* Copyright (c) 2021-2023, Martin Wendt. Released under the MIT license.
|
|
765
|
+
* v0.3.0, Sat, 27 May 2023 04:56:52 GMT (https://github.com/mar10/wunderbaum)
|
|
766
|
+
*/
|
|
767
|
+
/**
|
|
768
|
+
* Possible values for {@link WunderbaumNode.setModified()} and {@link Wunderbaum.setModified()}.
|
|
717
769
|
*/
|
|
718
|
-
/** Possible values for `setModified()`. */
|
|
719
770
|
var ChangeType;
|
|
720
771
|
(function (ChangeType) {
|
|
721
772
|
/** Re-render the whole viewport, headers, and all rows. */
|
|
722
773
|
ChangeType["any"] = "any";
|
|
723
|
-
/**
|
|
774
|
+
/** A node's title, icon, columns, or status have changed. Update the existing row markup. */
|
|
724
775
|
ChangeType["data"] = "data";
|
|
725
|
-
/**
|
|
726
|
-
ChangeType["
|
|
727
|
-
/**
|
|
776
|
+
/** The `tree.columns` definition has changed beyond simple width adjustments. */
|
|
777
|
+
ChangeType["colStructure"] = "colStructure";
|
|
778
|
+
/** The viewport/window was resized. Adjust layout attributes for all elements. */
|
|
779
|
+
ChangeType["resize"] = "resize";
|
|
780
|
+
/** A node's definition has changed beyond status and data. Re-render the whole row's markup. */
|
|
728
781
|
ChangeType["row"] = "row";
|
|
729
|
-
/**
|
|
782
|
+
/** Nodes have been added, removed, etc. Update markup. */
|
|
730
783
|
ChangeType["structure"] = "structure";
|
|
731
|
-
/** Update current row's classes, to reflect active, selected, ... */
|
|
784
|
+
/** A node's status has changed. Update current row's classes, to reflect active, selected, ... */
|
|
732
785
|
ChangeType["status"] = "status";
|
|
733
|
-
/** Update the 'top' property of all rows. */
|
|
734
|
-
ChangeType["
|
|
786
|
+
/** Vertical scroll event. Update the 'top' property of all rows. */
|
|
787
|
+
ChangeType["scroll"] = "scroll";
|
|
735
788
|
})(ChangeType || (ChangeType = {}));
|
|
736
|
-
|
|
789
|
+
/* Internal use. */
|
|
790
|
+
var RenderFlag;
|
|
791
|
+
(function (RenderFlag) {
|
|
792
|
+
RenderFlag["clearMarkup"] = "clearMarkup";
|
|
793
|
+
RenderFlag["header"] = "header";
|
|
794
|
+
RenderFlag["redraw"] = "redraw";
|
|
795
|
+
RenderFlag["scroll"] = "scroll";
|
|
796
|
+
})(RenderFlag || (RenderFlag = {}));
|
|
797
|
+
/** Possible values for {@link WunderbaumNode.setStatus()}. */
|
|
737
798
|
var NodeStatusType;
|
|
738
799
|
(function (NodeStatusType) {
|
|
739
800
|
NodeStatusType["ok"] = "ok";
|
|
@@ -764,8 +825,8 @@
|
|
|
764
825
|
|
|
765
826
|
/*!
|
|
766
827
|
* Wunderbaum - wb_extension_base
|
|
767
|
-
* Copyright (c) 2021-
|
|
768
|
-
* v0.
|
|
828
|
+
* Copyright (c) 2021-2023, Martin Wendt. Released under the MIT license.
|
|
829
|
+
* v0.3.0, Sat, 27 May 2023 04:56:52 GMT (https://github.com/mar10/wunderbaum)
|
|
769
830
|
*/
|
|
770
831
|
class WunderbaumExtension {
|
|
771
832
|
constructor(tree, id, defaults) {
|
|
@@ -1055,8 +1116,8 @@
|
|
|
1055
1116
|
|
|
1056
1117
|
/*!
|
|
1057
1118
|
* Wunderbaum - ext-filter
|
|
1058
|
-
* Copyright (c) 2021-
|
|
1059
|
-
* v0.
|
|
1119
|
+
* Copyright (c) 2021-2023, Martin Wendt. Released under the MIT license.
|
|
1120
|
+
* v0.3.0, Sat, 27 May 2023 04:56:52 GMT (https://github.com/mar10/wunderbaum)
|
|
1060
1121
|
*/
|
|
1061
1122
|
const START_MARKER = "\uFFF7";
|
|
1062
1123
|
const END_MARKER = "\uFFF8";
|
|
@@ -1356,8 +1417,8 @@
|
|
|
1356
1417
|
|
|
1357
1418
|
/*!
|
|
1358
1419
|
* Wunderbaum - ext-keynav
|
|
1359
|
-
* Copyright (c) 2021-
|
|
1360
|
-
* v0.
|
|
1420
|
+
* Copyright (c) 2021-2023, Martin Wendt. Released under the MIT license.
|
|
1421
|
+
* v0.3.0, Sat, 27 May 2023 04:56:52 GMT (https://github.com/mar10/wunderbaum)
|
|
1361
1422
|
*/
|
|
1362
1423
|
const QUICKSEARCH_DELAY = 500;
|
|
1363
1424
|
class KeynavExtension extends WunderbaumExtension {
|
|
@@ -1696,8 +1757,8 @@
|
|
|
1696
1757
|
|
|
1697
1758
|
/*!
|
|
1698
1759
|
* Wunderbaum - ext-logger
|
|
1699
|
-
* Copyright (c) 2021-
|
|
1700
|
-
* v0.
|
|
1760
|
+
* Copyright (c) 2021-2023, Martin Wendt. Released under the MIT license.
|
|
1761
|
+
* v0.3.0, Sat, 27 May 2023 04:56:52 GMT (https://github.com/mar10/wunderbaum)
|
|
1701
1762
|
*/
|
|
1702
1763
|
class LoggerExtension extends WunderbaumExtension {
|
|
1703
1764
|
constructor(tree) {
|
|
@@ -1736,8 +1797,8 @@
|
|
|
1736
1797
|
|
|
1737
1798
|
/*!
|
|
1738
1799
|
* Wunderbaum - common
|
|
1739
|
-
* Copyright (c) 2021-
|
|
1740
|
-
* v0.
|
|
1800
|
+
* Copyright (c) 2021-2023, Martin Wendt. Released under the MIT license.
|
|
1801
|
+
* v0.3.0, Sat, 27 May 2023 04:56:52 GMT (https://github.com/mar10/wunderbaum)
|
|
1741
1802
|
*/
|
|
1742
1803
|
const DEFAULT_DEBUGLEVEL = 4; // Replaced by rollup script
|
|
1743
1804
|
/**
|
|
@@ -1859,6 +1920,12 @@
|
|
|
1859
1920
|
return reMatch.test(node.title);
|
|
1860
1921
|
};
|
|
1861
1922
|
}
|
|
1923
|
+
/** Compare two nodes by title (case-insensitive). */
|
|
1924
|
+
function nodeTitleSorter(a, b) {
|
|
1925
|
+
const x = a.title.toLowerCase();
|
|
1926
|
+
const y = b.title.toLowerCase();
|
|
1927
|
+
return x === y ? 0 : x > y ? 1 : -1;
|
|
1928
|
+
}
|
|
1862
1929
|
function unflattenSource(source) {
|
|
1863
1930
|
var _a, _b, _c;
|
|
1864
1931
|
const { _format, _keyMap, _positional, children } = source;
|
|
@@ -1976,8 +2043,8 @@
|
|
|
1976
2043
|
|
|
1977
2044
|
/*!
|
|
1978
2045
|
* Wunderbaum - ext-dnd
|
|
1979
|
-
* Copyright (c) 2021-
|
|
1980
|
-
* v0.
|
|
2046
|
+
* Copyright (c) 2021-2023, Martin Wendt. Released under the MIT license.
|
|
2047
|
+
* v0.3.0, Sat, 27 May 2023 04:56:52 GMT (https://github.com/mar10/wunderbaum)
|
|
1981
2048
|
*/
|
|
1982
2049
|
const nodeMimeType = "application/x-wunderbaum-node";
|
|
1983
2050
|
class DndExtension extends WunderbaumExtension {
|
|
@@ -2088,6 +2155,7 @@
|
|
|
2088
2155
|
// Only 'before' and 'after':
|
|
2089
2156
|
return dy > ROW_HEIGHT / 2 ? "after" : "before";
|
|
2090
2157
|
}
|
|
2158
|
+
// return "over";
|
|
2091
2159
|
}
|
|
2092
2160
|
/* Implement auto scrolling when drag cursor is in top/bottom area of scroll parent. */
|
|
2093
2161
|
autoScroll(event) {
|
|
@@ -2162,6 +2230,8 @@
|
|
|
2162
2230
|
}
|
|
2163
2231
|
onDropEvent(e) {
|
|
2164
2232
|
// const isLink = event.dataTransfer.types.includes("text/uri-list");
|
|
2233
|
+
const srcNode = this.srcNode;
|
|
2234
|
+
const srcTree = srcNode ? srcNode.tree : null;
|
|
2165
2235
|
const targetNode = Wunderbaum.getNode(e);
|
|
2166
2236
|
const dndOpts = this.treeOpts.dnd;
|
|
2167
2237
|
const dt = e.dataTransfer;
|
|
@@ -2169,7 +2239,7 @@
|
|
|
2169
2239
|
this._leaveNode();
|
|
2170
2240
|
return;
|
|
2171
2241
|
}
|
|
2172
|
-
if (
|
|
2242
|
+
if (!["dragenter", "dragover", "dragleave"].includes(e.type)) {
|
|
2173
2243
|
this.tree.logDebug("onDropEvent." +
|
|
2174
2244
|
e.type +
|
|
2175
2245
|
" targetNode: " +
|
|
@@ -2190,9 +2260,24 @@
|
|
|
2190
2260
|
this.lastTargetNode = targetNode;
|
|
2191
2261
|
this.lastEnterStamp = Date.now();
|
|
2192
2262
|
if (
|
|
2193
|
-
// Don't
|
|
2194
|
-
|
|
2195
|
-
|
|
2263
|
+
// Don't drop on status node:
|
|
2264
|
+
targetNode.isStatusNode() ||
|
|
2265
|
+
// Prevent dropping nodes from different Wunderbaum trees:
|
|
2266
|
+
(dndOpts.preventForeignNodes && targetNode.tree !== srcTree) ||
|
|
2267
|
+
// Prevent dropping items on unloaded lazy Wunderbaum tree nodes:
|
|
2268
|
+
(dndOpts.preventLazyParents && !targetNode.isLoaded()) ||
|
|
2269
|
+
// Prevent dropping items other than Wunderbaum tree nodes:
|
|
2270
|
+
(dndOpts.preventNonNodes && !srcNode) ||
|
|
2271
|
+
// Prevent dropping nodes on own descendants:
|
|
2272
|
+
(dndOpts.preventRecursion &&
|
|
2273
|
+
srcNode &&
|
|
2274
|
+
srcNode.isAncestorOf(targetNode)) ||
|
|
2275
|
+
// Prevent dropping nodes under same direct parent:
|
|
2276
|
+
(dndOpts.preventSameParent &&
|
|
2277
|
+
srcNode &&
|
|
2278
|
+
targetNode.parent === srcNode.parent) ||
|
|
2279
|
+
// Don't allow void operation ('drop on self'): TODO: should be checke onn move only
|
|
2280
|
+
(dndOpts.preventVoidMoves && targetNode === srcNode)) {
|
|
2196
2281
|
dt.dropEffect = "none";
|
|
2197
2282
|
return true; // Prevent drop operation
|
|
2198
2283
|
}
|
|
@@ -2238,9 +2323,11 @@
|
|
|
2238
2323
|
else if (e.type === "drop") {
|
|
2239
2324
|
e.stopPropagation(); // prevent browser from opening links?
|
|
2240
2325
|
this._leaveNode();
|
|
2326
|
+
const region = this.lastDropRegion;
|
|
2241
2327
|
targetNode._callEvent("dnd.drop", {
|
|
2242
2328
|
event: e,
|
|
2243
|
-
region:
|
|
2329
|
+
region: region,
|
|
2330
|
+
defaultDropMode: region === "over" ? "appendChild" : region,
|
|
2244
2331
|
sourceNode: this.srcNode,
|
|
2245
2332
|
});
|
|
2246
2333
|
}
|
|
@@ -2249,8 +2336,8 @@
|
|
|
2249
2336
|
|
|
2250
2337
|
/*!
|
|
2251
2338
|
* Wunderbaum - drag_observer
|
|
2252
|
-
* Copyright (c) 2021-
|
|
2253
|
-
* v0.
|
|
2339
|
+
* Copyright (c) 2021-2023, Martin Wendt. Released under the MIT license.
|
|
2340
|
+
* v0.3.0, Sat, 27 May 2023 04:56:52 GMT (https://github.com/mar10/wunderbaum)
|
|
2254
2341
|
*/
|
|
2255
2342
|
/**
|
|
2256
2343
|
* Convert mouse- and touch events to 'dragstart', 'drag', and 'dragstop'.
|
|
@@ -2385,8 +2472,8 @@
|
|
|
2385
2472
|
|
|
2386
2473
|
/*!
|
|
2387
2474
|
* Wunderbaum - ext-grid
|
|
2388
|
-
* Copyright (c) 2021-
|
|
2389
|
-
* v0.
|
|
2475
|
+
* Copyright (c) 2021-2023, Martin Wendt. Released under the MIT license.
|
|
2476
|
+
* v0.3.0, Sat, 27 May 2023 04:56:52 GMT (https://github.com/mar10/wunderbaum)
|
|
2390
2477
|
*/
|
|
2391
2478
|
class GridExtension extends WunderbaumExtension {
|
|
2392
2479
|
constructor(tree) {
|
|
@@ -2422,8 +2509,8 @@
|
|
|
2422
2509
|
|
|
2423
2510
|
/*!
|
|
2424
2511
|
* Wunderbaum - deferred
|
|
2425
|
-
* Copyright (c) 2021-
|
|
2426
|
-
* v0.
|
|
2512
|
+
* Copyright (c) 2021-2023, Martin Wendt. Released under the MIT license.
|
|
2513
|
+
* v0.3.0, Sat, 27 May 2023 04:56:52 GMT (https://github.com/mar10/wunderbaum)
|
|
2427
2514
|
*/
|
|
2428
2515
|
/**
|
|
2429
2516
|
* Implement a ES6 Promise, that exposes a resolve() and reject() method.
|
|
@@ -2475,8 +2562,8 @@
|
|
|
2475
2562
|
|
|
2476
2563
|
/*!
|
|
2477
2564
|
* Wunderbaum - wunderbaum_node
|
|
2478
|
-
* Copyright (c) 2021-
|
|
2479
|
-
* v0.
|
|
2565
|
+
* Copyright (c) 2021-2023, Martin Wendt. Released under the MIT license.
|
|
2566
|
+
* v0.3.0, Sat, 27 May 2023 04:56:52 GMT (https://github.com/mar10/wunderbaum)
|
|
2480
2567
|
*/
|
|
2481
2568
|
/** Top-level properties that can be passed with `data`. */
|
|
2482
2569
|
const NODE_PROPS = new Set([
|
|
@@ -2713,9 +2800,9 @@
|
|
|
2713
2800
|
* @param [mode=child] 'before', 'after', 'firstChild', or 'child' ('over' is a synonym for 'child')
|
|
2714
2801
|
* @returns new node
|
|
2715
2802
|
*/
|
|
2716
|
-
addNode(nodeData, mode = "
|
|
2803
|
+
addNode(nodeData, mode = "appendChild") {
|
|
2717
2804
|
if (mode === "over") {
|
|
2718
|
-
mode = "
|
|
2805
|
+
mode = "appendChild"; // compatible with drop region
|
|
2719
2806
|
}
|
|
2720
2807
|
switch (mode) {
|
|
2721
2808
|
case "after":
|
|
@@ -2724,11 +2811,11 @@
|
|
|
2724
2811
|
});
|
|
2725
2812
|
case "before":
|
|
2726
2813
|
return this.parent.addChildren(nodeData, { before: this });
|
|
2727
|
-
case "
|
|
2814
|
+
case "prependChild":
|
|
2728
2815
|
// Insert before the first child if any
|
|
2729
2816
|
// let insertBefore = this.children ? this.children[0] : undefined;
|
|
2730
2817
|
return this.addChildren(nodeData, { before: 0 });
|
|
2731
|
-
case "
|
|
2818
|
+
case "appendChild":
|
|
2732
2819
|
return this.addChildren(nodeData);
|
|
2733
2820
|
}
|
|
2734
2821
|
assert(false, "Invalid mode: " + mode);
|
|
@@ -3123,8 +3210,17 @@
|
|
|
3123
3210
|
* an expand operation is currently possible.
|
|
3124
3211
|
*/
|
|
3125
3212
|
isExpandable(andCollapsed = false) {
|
|
3126
|
-
//
|
|
3127
|
-
|
|
3213
|
+
// `false` is never expandable (unoffical)
|
|
3214
|
+
if ((andCollapsed && this.expanded) || this.children === false) {
|
|
3215
|
+
return false;
|
|
3216
|
+
}
|
|
3217
|
+
if (this.children == null) {
|
|
3218
|
+
return this.lazy; // null or undefined can trigger lazy load
|
|
3219
|
+
}
|
|
3220
|
+
if (this.children.length === 0) {
|
|
3221
|
+
return !!this.tree.options.emptyChildListExpandable;
|
|
3222
|
+
}
|
|
3223
|
+
return true;
|
|
3128
3224
|
}
|
|
3129
3225
|
/** Return true if this node is currently in edit-title mode. */
|
|
3130
3226
|
isEditing() {
|
|
@@ -3258,7 +3354,7 @@
|
|
|
3258
3354
|
tree.logInfo("Redefine columns", source.columns);
|
|
3259
3355
|
tree.columns = source.columns;
|
|
3260
3356
|
delete source.columns;
|
|
3261
|
-
tree.
|
|
3357
|
+
tree.setModified(ChangeType.colStructure);
|
|
3262
3358
|
}
|
|
3263
3359
|
this.addChildren(source.children);
|
|
3264
3360
|
// Add extra data to `tree.data`
|
|
@@ -3270,12 +3366,50 @@
|
|
|
3270
3366
|
}
|
|
3271
3367
|
this._callEvent("load");
|
|
3272
3368
|
}
|
|
3369
|
+
async _fetchWithOptions(source) {
|
|
3370
|
+
var _a, _b;
|
|
3371
|
+
// Either a URL string or an object with a `.url` property.
|
|
3372
|
+
let url, params, body, options, rest;
|
|
3373
|
+
let fetchOpts = {};
|
|
3374
|
+
if (typeof source === "string") {
|
|
3375
|
+
// source is a plain URL string: assume GET request
|
|
3376
|
+
url = source;
|
|
3377
|
+
fetchOpts.method = "GET";
|
|
3378
|
+
}
|
|
3379
|
+
else if (isPlainObject(source)) {
|
|
3380
|
+
// source is a plain object with `.url` property.
|
|
3381
|
+
({ url, params, body, options, ...rest } = source);
|
|
3382
|
+
assert(typeof url === "string", `expected source.url as string`);
|
|
3383
|
+
if (isPlainObject(options)) {
|
|
3384
|
+
fetchOpts = options;
|
|
3385
|
+
}
|
|
3386
|
+
if (isPlainObject(body)) {
|
|
3387
|
+
// we also accept 'body' as object...
|
|
3388
|
+
assert(!fetchOpts.body, "options.body should be passed as source.body");
|
|
3389
|
+
fetchOpts.body = JSON.stringify(fetchOpts.body);
|
|
3390
|
+
(_a = fetchOpts.method) !== null && _a !== void 0 ? _a : (fetchOpts.method = "POST"); // set default
|
|
3391
|
+
}
|
|
3392
|
+
if (isPlainObject(params)) {
|
|
3393
|
+
url += "?" + new URLSearchParams(params);
|
|
3394
|
+
(_b = fetchOpts.method) !== null && _b !== void 0 ? _b : (fetchOpts.method = "GET"); // set default
|
|
3395
|
+
}
|
|
3396
|
+
}
|
|
3397
|
+
else {
|
|
3398
|
+
url = ""; // keep linter happy
|
|
3399
|
+
error(`Unsupported source format: ${source}`);
|
|
3400
|
+
}
|
|
3401
|
+
this.setStatus(NodeStatusType.loading);
|
|
3402
|
+
const response = await fetch(url, fetchOpts);
|
|
3403
|
+
if (!response.ok) {
|
|
3404
|
+
error(`GET ${url} returned ${response.status}, ${response}`);
|
|
3405
|
+
}
|
|
3406
|
+
return await response.json();
|
|
3407
|
+
}
|
|
3273
3408
|
/** Download data from the cloud, then call `.update()`. */
|
|
3274
3409
|
async load(source) {
|
|
3275
3410
|
const tree = this.tree;
|
|
3276
3411
|
const requestId = Date.now();
|
|
3277
3412
|
const prevParent = this.parent;
|
|
3278
|
-
const url = typeof source === "string" ? source : source.url;
|
|
3279
3413
|
const start = Date.now();
|
|
3280
3414
|
let elap = 0, elapLoad = 0, elapProcess = 0;
|
|
3281
3415
|
// Check for overlapping requests
|
|
@@ -3286,17 +3420,16 @@
|
|
|
3286
3420
|
this._requestId = requestId;
|
|
3287
3421
|
// const timerLabel = tree.logTime(this + ".load()");
|
|
3288
3422
|
try {
|
|
3423
|
+
let url = typeof source === "string" ? source : source.url;
|
|
3289
3424
|
if (!url) {
|
|
3425
|
+
// An array or a plain object (that does NOT contain a `.url` property)
|
|
3426
|
+
// will be treated as native Wunderbaum data
|
|
3290
3427
|
this._loadSourceObject(source);
|
|
3291
3428
|
elapProcess = Date.now() - start;
|
|
3292
3429
|
}
|
|
3293
3430
|
else {
|
|
3294
|
-
|
|
3295
|
-
const
|
|
3296
|
-
if (!response.ok) {
|
|
3297
|
-
error(`GET ${url} returned ${response.status}, ${response}`);
|
|
3298
|
-
}
|
|
3299
|
-
const data = await response.json();
|
|
3431
|
+
// Either a URL string or an object with a `.url` property.
|
|
3432
|
+
const data = await this._fetchWithOptions(source);
|
|
3300
3433
|
elapLoad = Date.now() - start;
|
|
3301
3434
|
if (this._requestId && this._requestId > requestId) {
|
|
3302
3435
|
this.logWarn(`Ignored load response #${requestId} because #${this._requestId} is pending.`);
|
|
@@ -3439,6 +3572,9 @@
|
|
|
3439
3572
|
}
|
|
3440
3573
|
/** Move this node to targetNode. */
|
|
3441
3574
|
moveTo(targetNode, mode = "appendChild", map) {
|
|
3575
|
+
if (mode === "over") {
|
|
3576
|
+
mode = "appendChild"; // compatible with drop region
|
|
3577
|
+
}
|
|
3442
3578
|
if (mode === "prependChild") {
|
|
3443
3579
|
if (targetNode.children && targetNode.children.length) {
|
|
3444
3580
|
mode = "before";
|
|
@@ -3495,7 +3631,7 @@
|
|
|
3495
3631
|
targetParent.children.splice(pos + 1, 0, this);
|
|
3496
3632
|
break;
|
|
3497
3633
|
default:
|
|
3498
|
-
error(
|
|
3634
|
+
error(`Invalid mode '${mode}'.`);
|
|
3499
3635
|
}
|
|
3500
3636
|
}
|
|
3501
3637
|
else {
|
|
@@ -3522,7 +3658,12 @@
|
|
|
3522
3658
|
n.tree = targetNode.tree;
|
|
3523
3659
|
}, true);
|
|
3524
3660
|
}
|
|
3525
|
-
|
|
3661
|
+
// Make sure we update async, because discarding the markup would prevent
|
|
3662
|
+
// DragAndDrop to generate a dragend event on the source node
|
|
3663
|
+
setTimeout(() => {
|
|
3664
|
+
// Even indentation may have changed:
|
|
3665
|
+
tree.setModified(ChangeType.any);
|
|
3666
|
+
}, 0);
|
|
3526
3667
|
// TODO: fix selection state
|
|
3527
3668
|
// TODO: fix active state
|
|
3528
3669
|
}
|
|
@@ -3648,7 +3789,7 @@
|
|
|
3648
3789
|
icon = iconMap.loading;
|
|
3649
3790
|
}
|
|
3650
3791
|
if (icon === false) {
|
|
3651
|
-
return null;
|
|
3792
|
+
return null; // explicitly disabled: don't try default icons
|
|
3652
3793
|
}
|
|
3653
3794
|
if (typeof icon === "string") ;
|
|
3654
3795
|
else if (this.statusNodeType) {
|
|
@@ -3667,7 +3808,11 @@
|
|
|
3667
3808
|
icon = iconMap.doc;
|
|
3668
3809
|
}
|
|
3669
3810
|
// this.log("_createIcon: " + icon);
|
|
3670
|
-
if (icon
|
|
3811
|
+
if (!icon) {
|
|
3812
|
+
iconSpan = document.createElement("i");
|
|
3813
|
+
iconSpan.className = "wb-icon";
|
|
3814
|
+
}
|
|
3815
|
+
else if (icon.indexOf("<") >= 0) {
|
|
3671
3816
|
// HTML
|
|
3672
3817
|
iconSpan = elemFromHtml(icon);
|
|
3673
3818
|
}
|
|
@@ -3985,9 +4130,12 @@
|
|
|
3985
4130
|
case "data":
|
|
3986
4131
|
this._render_data(opts);
|
|
3987
4132
|
break;
|
|
3988
|
-
|
|
4133
|
+
case "row":
|
|
4134
|
+
// _rowElem is not yet created (asserted in _render_markup)
|
|
3989
4135
|
this._render_markup(opts);
|
|
3990
4136
|
break;
|
|
4137
|
+
default:
|
|
4138
|
+
error(`Invalid change type '${opts.change}'.`);
|
|
3991
4139
|
}
|
|
3992
4140
|
}
|
|
3993
4141
|
/**
|
|
@@ -4209,20 +4357,24 @@
|
|
|
4209
4357
|
this.setModified();
|
|
4210
4358
|
}
|
|
4211
4359
|
/** Set a new icon path or class. */
|
|
4212
|
-
setIcon() {
|
|
4213
|
-
|
|
4214
|
-
|
|
4360
|
+
setIcon(icon) {
|
|
4361
|
+
this.icon = icon;
|
|
4362
|
+
this.setModified();
|
|
4215
4363
|
}
|
|
4216
4364
|
/** Change node's {@link key} and/or {@link refKey}. */
|
|
4217
4365
|
setKey(key, refKey) {
|
|
4218
4366
|
throw new Error("Not yet implemented");
|
|
4219
4367
|
}
|
|
4220
4368
|
/**
|
|
4221
|
-
*
|
|
4369
|
+
* Trigger a repaint, typically after a status or data change.
|
|
4222
4370
|
*
|
|
4223
4371
|
* `change` defaults to 'data', which handles modifcations of title, icon,
|
|
4224
4372
|
* and column content. It can be reduced to 'ChangeType.status' if only
|
|
4225
4373
|
* active/focus/selected state has changed.
|
|
4374
|
+
*
|
|
4375
|
+
* This method will eventually call {@link WunderbaumNode.render()} with
|
|
4376
|
+
* default options, but may be more consistent with the tree's
|
|
4377
|
+
* {@link Wunderbaum.setModified()} API.
|
|
4226
4378
|
*/
|
|
4227
4379
|
setModified(change = ChangeType.data) {
|
|
4228
4380
|
assert(change === ChangeType.status || change === ChangeType.data);
|
|
@@ -4258,7 +4410,7 @@
|
|
|
4258
4410
|
let firstChild = children ? children[0] : null;
|
|
4259
4411
|
assert(data.statusNodeType);
|
|
4260
4412
|
assert(!firstChild || !firstChild.isStatusNode());
|
|
4261
|
-
statusNode = this.addNode(data, "
|
|
4413
|
+
statusNode = this.addNode(data, "prependChild");
|
|
4262
4414
|
statusNode.match = true;
|
|
4263
4415
|
tree.setModified(ChangeType.structure);
|
|
4264
4416
|
return statusNode;
|
|
@@ -4324,11 +4476,37 @@
|
|
|
4324
4476
|
this.setModified();
|
|
4325
4477
|
// this.triggerModify("rename"); // TODO
|
|
4326
4478
|
}
|
|
4479
|
+
_sortChildren(cmp, deep) {
|
|
4480
|
+
const cl = this.children;
|
|
4481
|
+
if (!cl) {
|
|
4482
|
+
return;
|
|
4483
|
+
}
|
|
4484
|
+
cl.sort(cmp);
|
|
4485
|
+
if (deep) {
|
|
4486
|
+
for (let i = 0, l = cl.length; i < l; i++) {
|
|
4487
|
+
if (cl[i].children) {
|
|
4488
|
+
cl[i]._sortChildren(cmp, deep);
|
|
4489
|
+
}
|
|
4490
|
+
}
|
|
4491
|
+
}
|
|
4492
|
+
}
|
|
4493
|
+
/**
|
|
4494
|
+
* Sort child list by title or custom criteria.
|
|
4495
|
+
* @param {function} cmp custom compare function(a, b) that returns -1, 0, or 1
|
|
4496
|
+
* (defaults to sorting by title).
|
|
4497
|
+
* @param {boolean} deep pass true to sort all descendant nodes recursively
|
|
4498
|
+
*/
|
|
4499
|
+
sortChildren(cmp = nodeTitleSorter, deep = false) {
|
|
4500
|
+
this._sortChildren(cmp || nodeTitleSorter, deep);
|
|
4501
|
+
this.tree.setModified(ChangeType.structure);
|
|
4502
|
+
// this.triggerModify("sort"); // TODO
|
|
4503
|
+
}
|
|
4327
4504
|
/**
|
|
4328
4505
|
* Trigger `modifyChild` event on a parent to signal that a child was modified.
|
|
4329
4506
|
* @param {string} operation Type of change: 'add', 'remove', 'rename', 'move', 'data', ...
|
|
4330
4507
|
*/
|
|
4331
4508
|
triggerModifyChild(operation, child, extra) {
|
|
4509
|
+
this.logDebug(`modifyChild(${operation})`, extra, child);
|
|
4332
4510
|
if (!this.tree.options.modifyChild)
|
|
4333
4511
|
return;
|
|
4334
4512
|
if (child && child.parent !== this) {
|
|
@@ -4427,8 +4605,8 @@
|
|
|
4427
4605
|
|
|
4428
4606
|
/*!
|
|
4429
4607
|
* Wunderbaum - ext-edit
|
|
4430
|
-
* Copyright (c) 2021-
|
|
4431
|
-
* v0.
|
|
4608
|
+
* Copyright (c) 2021-2023, Martin Wendt. Released under the MIT license.
|
|
4609
|
+
* v0.3.0, Sat, 27 May 2023 04:56:52 GMT (https://github.com/mar10/wunderbaum)
|
|
4432
4610
|
*/
|
|
4433
4611
|
// const START_MARKER = "\uFFF7";
|
|
4434
4612
|
class EditExtension extends WunderbaumExtension {
|
|
@@ -4719,12 +4897,12 @@
|
|
|
4719
4897
|
*
|
|
4720
4898
|
* A treegrid control.
|
|
4721
4899
|
*
|
|
4722
|
-
* Copyright (c) 2021-
|
|
4900
|
+
* Copyright (c) 2021-2023, Martin Wendt (https://wwWendt.de).
|
|
4723
4901
|
* https://github.com/mar10/wunderbaum
|
|
4724
4902
|
*
|
|
4725
4903
|
* Released under the MIT license.
|
|
4726
|
-
* @version v0.
|
|
4727
|
-
* @date
|
|
4904
|
+
* @version v0.3.0
|
|
4905
|
+
* @date Sat, 27 May 2023 04:56:52 GMT
|
|
4728
4906
|
*/
|
|
4729
4907
|
class WbSystemRoot extends WunderbaumNode {
|
|
4730
4908
|
constructor(tree) {
|
|
@@ -4753,6 +4931,7 @@
|
|
|
4753
4931
|
this.refKeyMap = new Map();
|
|
4754
4932
|
this.treeRowCount = 0;
|
|
4755
4933
|
this._disableUpdateCount = 0;
|
|
4934
|
+
this._disableUpdateIgnoreCount = 0;
|
|
4756
4935
|
/** Currently active node if any. */
|
|
4757
4936
|
this.activeNode = null;
|
|
4758
4937
|
/** Current node hat has keyboard focus if any. */
|
|
@@ -4763,8 +4942,7 @@
|
|
|
4763
4942
|
this.columns = []; // any[] = [];
|
|
4764
4943
|
this._columnsById = {};
|
|
4765
4944
|
// Modification Status
|
|
4766
|
-
this.
|
|
4767
|
-
this.changeScrollRequestPending = false;
|
|
4945
|
+
this.pendingChangeTypes = new Set();
|
|
4768
4946
|
/** Expose some useful methods of the util.ts module as `tree._util`. */
|
|
4769
4947
|
this._util = util;
|
|
4770
4948
|
// --- FILTER ---
|
|
@@ -4800,6 +4978,7 @@
|
|
|
4800
4978
|
showSpinner: false,
|
|
4801
4979
|
checkbox: false,
|
|
4802
4980
|
minExpandLevel: 0,
|
|
4981
|
+
emptyChildListExpandable: false,
|
|
4803
4982
|
updateThrottleWait: 200,
|
|
4804
4983
|
skeleton: false,
|
|
4805
4984
|
connectTopBreadcrumb: null,
|
|
@@ -4962,11 +5141,11 @@
|
|
|
4962
5141
|
// --- Bind listeners
|
|
4963
5142
|
this.element.addEventListener("scroll", (e) => {
|
|
4964
5143
|
// this.log(`scroll, scrollTop:${e.target.scrollTop}`, e);
|
|
4965
|
-
this.setModified(ChangeType.
|
|
5144
|
+
this.setModified(ChangeType.scroll);
|
|
4966
5145
|
});
|
|
4967
5146
|
this.resizeObserver = new ResizeObserver((entries) => {
|
|
4968
|
-
this.setModified(ChangeType.vscroll);
|
|
4969
5147
|
// this.log("ResizeObserver: Size changed", entries);
|
|
5148
|
+
this.setModified(ChangeType.resize);
|
|
4970
5149
|
});
|
|
4971
5150
|
this.resizeObserver.observe(this.element);
|
|
4972
5151
|
onEvent(this.nodeListElement, "click", "div.wb-row", (e) => {
|
|
@@ -5502,7 +5681,7 @@
|
|
|
5502
5681
|
this.options[name] = value;
|
|
5503
5682
|
switch (name) {
|
|
5504
5683
|
case "checkbox":
|
|
5505
|
-
this.setModified(ChangeType.any
|
|
5684
|
+
this.setModified(ChangeType.any);
|
|
5506
5685
|
break;
|
|
5507
5686
|
case "enabled":
|
|
5508
5687
|
this.setEnabled(!!value);
|
|
@@ -5802,8 +5981,9 @@
|
|
|
5802
5981
|
res.region = NodeRegion.title;
|
|
5803
5982
|
}
|
|
5804
5983
|
else if (cl.contains("wb-expander")) {
|
|
5805
|
-
res.region =
|
|
5806
|
-
|
|
5984
|
+
res.region = node.isExpandable()
|
|
5985
|
+
? NodeRegion.expander
|
|
5986
|
+
: NodeRegion.prefix;
|
|
5807
5987
|
}
|
|
5808
5988
|
else if (cl.contains("wb-checkbox")) {
|
|
5809
5989
|
res.region = NodeRegion.checkbox;
|
|
@@ -5954,7 +6134,7 @@
|
|
|
5954
6134
|
// Make sure the topNode is always visible
|
|
5955
6135
|
this.scrollTo(topNode);
|
|
5956
6136
|
}
|
|
5957
|
-
// this.setModified(ChangeType.
|
|
6137
|
+
// this.setModified(ChangeType.scroll);
|
|
5958
6138
|
}
|
|
5959
6139
|
}
|
|
5960
6140
|
/**
|
|
@@ -5982,7 +6162,7 @@
|
|
|
5982
6162
|
// util.assert(node._rowIdx != null);
|
|
5983
6163
|
this.log(`scrollToHorz(${this.activeColIdx}): ${colLeft}..${colRight}, fixedOfs=${fixedWidth}, vpWidth=${vpWidth}, curLeft=${scrollLeft} -> ${newLeft}`);
|
|
5984
6164
|
this.element.scrollLeft = newLeft;
|
|
5985
|
-
// this.setModified(ChangeType.
|
|
6165
|
+
// this.setModified(ChangeType.scroll);
|
|
5986
6166
|
}
|
|
5987
6167
|
/**
|
|
5988
6168
|
* Set column #colIdx to 'active'.
|
|
@@ -6037,34 +6217,40 @@
|
|
|
6037
6217
|
// this.log(
|
|
6038
6218
|
// `IGNORED setModified(${change}) node=${node} (disable level ${this._disableUpdateCount})`
|
|
6039
6219
|
// );
|
|
6220
|
+
this._disableUpdateIgnoreCount++;
|
|
6040
6221
|
return;
|
|
6041
6222
|
}
|
|
6042
6223
|
// this.log(`setModified(${change}) node=${node}`);
|
|
6043
6224
|
if (!(node instanceof WunderbaumNode)) {
|
|
6044
6225
|
options = node;
|
|
6226
|
+
node = null;
|
|
6045
6227
|
}
|
|
6046
6228
|
const immediate = !!getOption(options, "immediate");
|
|
6047
|
-
const
|
|
6048
|
-
|
|
6049
|
-
this.visit((n) => {
|
|
6050
|
-
n.removeMarkup();
|
|
6051
|
-
});
|
|
6052
|
-
}
|
|
6053
|
-
let callUpdate = false;
|
|
6229
|
+
const RF = RenderFlag;
|
|
6230
|
+
const pending = this.pendingChangeTypes;
|
|
6054
6231
|
switch (change) {
|
|
6055
6232
|
case ChangeType.any:
|
|
6233
|
+
case ChangeType.colStructure:
|
|
6234
|
+
pending.add(RF.header);
|
|
6235
|
+
pending.add(RF.clearMarkup);
|
|
6236
|
+
pending.add(RF.redraw);
|
|
6237
|
+
pending.add(RF.scroll);
|
|
6238
|
+
break;
|
|
6239
|
+
case ChangeType.resize:
|
|
6240
|
+
// case ChangeType.colWidth:
|
|
6241
|
+
pending.add(RF.header);
|
|
6242
|
+
pending.add(RF.redraw);
|
|
6243
|
+
break;
|
|
6056
6244
|
case ChangeType.structure:
|
|
6057
|
-
|
|
6058
|
-
this.changeRedrawRequestPending = true;
|
|
6059
|
-
callUpdate = true;
|
|
6245
|
+
pending.add(RF.redraw);
|
|
6060
6246
|
break;
|
|
6061
|
-
case ChangeType.
|
|
6062
|
-
|
|
6063
|
-
callUpdate = true;
|
|
6247
|
+
case ChangeType.scroll:
|
|
6248
|
+
pending.add(RF.scroll);
|
|
6064
6249
|
break;
|
|
6065
6250
|
case ChangeType.row:
|
|
6066
6251
|
case ChangeType.data:
|
|
6067
6252
|
case ChangeType.status:
|
|
6253
|
+
assert(node, `Option '${change}' requires a node.`);
|
|
6068
6254
|
// Single nodes are immediately updated if already inside the viewport
|
|
6069
6255
|
// (otherwise we can ignore)
|
|
6070
6256
|
if (node._rowElem) {
|
|
@@ -6072,9 +6258,16 @@
|
|
|
6072
6258
|
}
|
|
6073
6259
|
break;
|
|
6074
6260
|
default:
|
|
6075
|
-
error(`Invalid change type ${change}
|
|
6261
|
+
error(`Invalid change type '${change}'.`);
|
|
6262
|
+
}
|
|
6263
|
+
if (change === ChangeType.colStructure) {
|
|
6264
|
+
const isGrid = this.isGrid();
|
|
6265
|
+
this.element.classList.toggle("wb-grid", isGrid);
|
|
6266
|
+
if (!isGrid && this.isCellNav()) {
|
|
6267
|
+
this.setCellNav(false);
|
|
6268
|
+
}
|
|
6076
6269
|
}
|
|
6077
|
-
if (
|
|
6270
|
+
if (pending.size > 0) {
|
|
6078
6271
|
if (immediate) {
|
|
6079
6272
|
this._updateViewportImmediately();
|
|
6080
6273
|
}
|
|
@@ -6146,7 +6339,7 @@
|
|
|
6146
6339
|
}
|
|
6147
6340
|
break;
|
|
6148
6341
|
default:
|
|
6149
|
-
error(`Invalid mode '${mode}'
|
|
6342
|
+
error(`Invalid mode '${mode}'.`);
|
|
6150
6343
|
}
|
|
6151
6344
|
}
|
|
6152
6345
|
/** Display tree status (ok, loading, error, noData) using styles and a dummy root node. */
|
|
@@ -6169,14 +6362,24 @@
|
|
|
6169
6362
|
}
|
|
6170
6363
|
}
|
|
6171
6364
|
}
|
|
6172
|
-
/**
|
|
6365
|
+
/**
|
|
6366
|
+
* Sort nodes list by title or custom criteria.
|
|
6367
|
+
* @param {function} cmp custom compare function(a, b) that returns -1, 0, or 1
|
|
6368
|
+
* (defaults to sorting by title).
|
|
6369
|
+
* @param {boolean} deep pass true to sort all descendant nodes recursively
|
|
6370
|
+
*/
|
|
6371
|
+
sortChildren(cmp = nodeTitleSorter, deep = false) {
|
|
6372
|
+
this.root.sortChildren(cmp, deep);
|
|
6373
|
+
}
|
|
6374
|
+
/**
|
|
6375
|
+
* Update column headers and column width.
|
|
6173
6376
|
* Return true if at least one column width changed.
|
|
6174
6377
|
*/
|
|
6175
|
-
|
|
6176
|
-
|
|
6378
|
+
// _updateColumnWidths(options?: UpdateColumnsOptions): boolean {
|
|
6379
|
+
_updateColumnWidths() {
|
|
6380
|
+
// options = Object.assign({ updateRows: true, renderMarkup: false }, options);
|
|
6177
6381
|
const defaultMinWidth = 4;
|
|
6178
6382
|
const vpWidth = this.element.clientWidth;
|
|
6179
|
-
const isGrid = this.isGrid();
|
|
6180
6383
|
// Shorten last column width to avoid h-scrollbar
|
|
6181
6384
|
const FIX_ADJUST_LAST_COL = 2;
|
|
6182
6385
|
const columns = this.columns;
|
|
@@ -6185,73 +6388,70 @@
|
|
|
6185
6388
|
let totalWeight = 0;
|
|
6186
6389
|
let fixedWidth = 0;
|
|
6187
6390
|
let modified = false;
|
|
6188
|
-
this.element.classList.toggle("wb-grid", isGrid);
|
|
6189
|
-
if (!isGrid && this.isCellNav()) {
|
|
6190
|
-
|
|
6391
|
+
// this.element.classList.toggle("wb-grid", isGrid);
|
|
6392
|
+
// if (!isGrid && this.isCellNav()) {
|
|
6393
|
+
// this.setCellNav(false);
|
|
6394
|
+
// }
|
|
6395
|
+
// if (options.calculateCols) {
|
|
6396
|
+
if (col0.id !== "*") {
|
|
6397
|
+
throw new Error(`First column must have id '*': got '${col0.id}'.`);
|
|
6191
6398
|
}
|
|
6192
|
-
|
|
6193
|
-
|
|
6194
|
-
|
|
6195
|
-
|
|
6196
|
-
|
|
6197
|
-
|
|
6198
|
-
|
|
6199
|
-
|
|
6200
|
-
|
|
6201
|
-
|
|
6202
|
-
|
|
6203
|
-
|
|
6204
|
-
|
|
6205
|
-
|
|
6206
|
-
|
|
6399
|
+
// Gather width definitions
|
|
6400
|
+
this._columnsById = {};
|
|
6401
|
+
for (let col of columns) {
|
|
6402
|
+
this._columnsById[col.id] = col;
|
|
6403
|
+
let cw = col.width;
|
|
6404
|
+
if (col.id === "*" && col !== col0) {
|
|
6405
|
+
throw new Error(`Column id '*' must be defined only once: '${col.title}'.`);
|
|
6406
|
+
}
|
|
6407
|
+
if (!cw || cw === "*") {
|
|
6408
|
+
col._weight = 1.0;
|
|
6409
|
+
totalWeight += 1.0;
|
|
6410
|
+
}
|
|
6411
|
+
else if (typeof cw === "number") {
|
|
6412
|
+
col._weight = cw;
|
|
6413
|
+
totalWeight += cw;
|
|
6414
|
+
}
|
|
6415
|
+
else if (typeof cw === "string" && cw.endsWith("px")) {
|
|
6416
|
+
col._weight = 0;
|
|
6417
|
+
let px = parseFloat(cw.slice(0, -2));
|
|
6418
|
+
if (col._widthPx != px) {
|
|
6419
|
+
modified = true;
|
|
6420
|
+
col._widthPx = px;
|
|
6207
6421
|
}
|
|
6208
|
-
|
|
6209
|
-
|
|
6210
|
-
|
|
6422
|
+
fixedWidth += px;
|
|
6423
|
+
}
|
|
6424
|
+
else {
|
|
6425
|
+
error(`Invalid column width: ${cw} (expected string ending with 'px' or number, e.g. "<num>px" or <int>).`);
|
|
6426
|
+
}
|
|
6427
|
+
}
|
|
6428
|
+
// Share remaining space between non-fixed columns
|
|
6429
|
+
const restPx = Math.max(0, vpWidth - fixedWidth);
|
|
6430
|
+
let ofsPx = 0;
|
|
6431
|
+
for (let col of columns) {
|
|
6432
|
+
let minWidth;
|
|
6433
|
+
if (col._weight) {
|
|
6434
|
+
const cmw = col.minWidth;
|
|
6435
|
+
if (typeof cmw === "number") {
|
|
6436
|
+
minWidth = cmw;
|
|
6211
6437
|
}
|
|
6212
|
-
else if (typeof
|
|
6213
|
-
|
|
6214
|
-
let px = parseFloat(cw.slice(0, -2));
|
|
6215
|
-
if (col._widthPx != px) {
|
|
6216
|
-
modified = true;
|
|
6217
|
-
col._widthPx = px;
|
|
6218
|
-
}
|
|
6219
|
-
fixedWidth += px;
|
|
6438
|
+
else if (typeof cmw === "string" && cmw.endsWith("px")) {
|
|
6439
|
+
minWidth = parseFloat(cmw.slice(0, -2));
|
|
6220
6440
|
}
|
|
6221
6441
|
else {
|
|
6222
|
-
|
|
6442
|
+
minWidth = defaultMinWidth;
|
|
6223
6443
|
}
|
|
6224
|
-
|
|
6225
|
-
|
|
6226
|
-
|
|
6227
|
-
|
|
6228
|
-
for (let col of columns) {
|
|
6229
|
-
let minWidth;
|
|
6230
|
-
if (col._weight) {
|
|
6231
|
-
const cmw = col.minWidth;
|
|
6232
|
-
if (typeof cmw === "number") {
|
|
6233
|
-
minWidth = cmw;
|
|
6234
|
-
}
|
|
6235
|
-
else if (typeof cmw === "string" && cmw.endsWith("px")) {
|
|
6236
|
-
minWidth = parseFloat(cmw.slice(0, -2));
|
|
6237
|
-
}
|
|
6238
|
-
else {
|
|
6239
|
-
minWidth = defaultMinWidth;
|
|
6240
|
-
}
|
|
6241
|
-
const px = Math.max(minWidth, (restPx * col._weight) / totalWeight);
|
|
6242
|
-
if (col._widthPx != px) {
|
|
6243
|
-
modified = true;
|
|
6244
|
-
col._widthPx = px;
|
|
6245
|
-
}
|
|
6444
|
+
const px = Math.max(minWidth, (restPx * col._weight) / totalWeight);
|
|
6445
|
+
if (col._widthPx != px) {
|
|
6446
|
+
modified = true;
|
|
6447
|
+
col._widthPx = px;
|
|
6246
6448
|
}
|
|
6247
|
-
col._ofsPx = ofsPx;
|
|
6248
|
-
ofsPx += col._widthPx;
|
|
6249
6449
|
}
|
|
6250
|
-
|
|
6251
|
-
|
|
6450
|
+
col._ofsPx = ofsPx;
|
|
6451
|
+
ofsPx += col._widthPx;
|
|
6252
6452
|
}
|
|
6253
|
-
|
|
6254
|
-
|
|
6453
|
+
columns[columns.length - 1]._widthPx -= FIX_ADJUST_LAST_COL;
|
|
6454
|
+
totalWidth = ofsPx - FIX_ADJUST_LAST_COL;
|
|
6255
6455
|
const tw = `${totalWidth}px`;
|
|
6256
6456
|
this.headerElement.style.width = tw;
|
|
6257
6457
|
this.listContainerElement.style.width = tw;
|
|
@@ -6260,12 +6460,14 @@
|
|
|
6260
6460
|
// this.logInfo("UC", this.columns, vpWidth, this.element.clientWidth, this.element);
|
|
6261
6461
|
// console.trace();
|
|
6262
6462
|
// util.error("BREAK");
|
|
6263
|
-
if (modified) {
|
|
6264
|
-
|
|
6265
|
-
|
|
6266
|
-
|
|
6267
|
-
|
|
6268
|
-
|
|
6463
|
+
// if (modified) {
|
|
6464
|
+
// this._renderHeaderMarkup();
|
|
6465
|
+
// if (options.renderMarkup) {
|
|
6466
|
+
// this.setModified(ChangeType.header, { removeMarkup: true });
|
|
6467
|
+
// } else if (options.updateRows) {
|
|
6468
|
+
// this._updateRows();
|
|
6469
|
+
// }
|
|
6470
|
+
// }
|
|
6269
6471
|
return modified;
|
|
6270
6472
|
}
|
|
6271
6473
|
/** Create/update header markup from `this.columns` definition.
|
|
@@ -6314,7 +6516,7 @@
|
|
|
6314
6516
|
* pending async changes if any.
|
|
6315
6517
|
*/
|
|
6316
6518
|
updatePendingModifications() {
|
|
6317
|
-
if (this.
|
|
6519
|
+
if (this.pendingChangeTypes.size > 0) {
|
|
6318
6520
|
this._updateViewportImmediately();
|
|
6319
6521
|
}
|
|
6320
6522
|
}
|
|
@@ -6329,31 +6531,50 @@
|
|
|
6329
6531
|
*/
|
|
6330
6532
|
_updateViewportImmediately() {
|
|
6331
6533
|
var _a;
|
|
6332
|
-
// Shorten container height to avoid v-scrollbar
|
|
6333
|
-
const FIX_ADJUST_HEIGHT = 1;
|
|
6334
6534
|
if (this._disableUpdateCount) {
|
|
6335
|
-
this.log(`
|
|
6535
|
+
this.log(`_updateViewportImmediately() IGNORED (disable level: ${this._disableUpdateCount})`);
|
|
6536
|
+
this._disableUpdateIgnoreCount++;
|
|
6336
6537
|
return;
|
|
6337
6538
|
}
|
|
6338
|
-
|
|
6339
|
-
|
|
6340
|
-
|
|
6341
|
-
|
|
6342
|
-
|
|
6343
|
-
|
|
6344
|
-
|
|
6345
|
-
|
|
6346
|
-
|
|
6347
|
-
|
|
6348
|
-
|
|
6349
|
-
|
|
6350
|
-
this.listContainerElement.
|
|
6351
|
-
height
|
|
6352
|
-
|
|
6353
|
-
|
|
6354
|
-
|
|
6355
|
-
|
|
6356
|
-
|
|
6539
|
+
// Shorten container height to avoid v-scrollbar
|
|
6540
|
+
const FIX_ADJUST_HEIGHT = 1;
|
|
6541
|
+
const RF = RenderFlag;
|
|
6542
|
+
const pending = new Set(this.pendingChangeTypes);
|
|
6543
|
+
this.pendingChangeTypes.clear();
|
|
6544
|
+
const scrollOnly = pending.has(RF.scroll) && pending.size === 1;
|
|
6545
|
+
if (scrollOnly) {
|
|
6546
|
+
this._updateRows({ newNodesOnly: true });
|
|
6547
|
+
// this.log("_updateViewportImmediately(): scroll only.");
|
|
6548
|
+
}
|
|
6549
|
+
else {
|
|
6550
|
+
this.log("_updateViewportImmediately():", pending);
|
|
6551
|
+
let height = this.listContainerElement.clientHeight;
|
|
6552
|
+
// We cannot get the height for absolute positioned parent, so look at first col
|
|
6553
|
+
// let headerHeight = this.headerElement.clientHeight
|
|
6554
|
+
// let headerHeight = this.headerElement.children[0].children[0].clientHeight;
|
|
6555
|
+
// const headerHeight = this.options.headerHeightPx;
|
|
6556
|
+
const headerHeight = this.headerElement.clientHeight; // May be 0
|
|
6557
|
+
const wantHeight = this.element.clientHeight - headerHeight - FIX_ADJUST_HEIGHT;
|
|
6558
|
+
if (Math.abs(height - wantHeight) > 1.0) {
|
|
6559
|
+
// this.log("resize", height, wantHeight);
|
|
6560
|
+
this.listContainerElement.style.height = wantHeight + "px";
|
|
6561
|
+
height = wantHeight;
|
|
6562
|
+
}
|
|
6563
|
+
// console.profile(`_updateViewportImmediately()`)
|
|
6564
|
+
if (pending.has(RF.clearMarkup)) {
|
|
6565
|
+
this.visit((n) => {
|
|
6566
|
+
n.removeMarkup();
|
|
6567
|
+
});
|
|
6568
|
+
}
|
|
6569
|
+
// let widthModified = false;
|
|
6570
|
+
if (pending.has(RF.header)) {
|
|
6571
|
+
// widthModified = this._updateColumnWidths();
|
|
6572
|
+
this._updateColumnWidths();
|
|
6573
|
+
this._renderHeaderMarkup();
|
|
6574
|
+
}
|
|
6575
|
+
this._updateRows();
|
|
6576
|
+
// console.profileEnd(`_updateViewportImmediately()`)
|
|
6577
|
+
}
|
|
6357
6578
|
if (this.options.connectTopBreadcrumb) {
|
|
6358
6579
|
let path = (_a = this.getTopmostVpNode(true)) === null || _a === void 0 ? void 0 : _a.getPath(false, "title", " > ");
|
|
6359
6580
|
path = path ? path + " >" : "";
|
|
@@ -6494,19 +6715,14 @@
|
|
|
6494
6715
|
return this.root.visit(callback, false);
|
|
6495
6716
|
}
|
|
6496
6717
|
/**
|
|
6497
|
-
* Call
|
|
6718
|
+
* Call callback(node) for all nodes in vertical order, top down (or bottom up).
|
|
6498
6719
|
*
|
|
6499
|
-
* Note that this considers expansion state, i.e.
|
|
6500
|
-
* are skipped.
|
|
6720
|
+
* Note that this considers expansion state, i.e. filtered nodes and children
|
|
6721
|
+
* of collapsed nodes are skipped, unless `includeHidden` is set.
|
|
6501
6722
|
*
|
|
6502
|
-
* Stop iteration
|
|
6723
|
+
* Stop iteration if callback() returns false.<br>
|
|
6503
6724
|
* Return false if iteration was stopped.
|
|
6504
6725
|
*
|
|
6505
|
-
* @param callback the callback function.
|
|
6506
|
-
* Return false to stop iteration, return "skip" to skip this node and children only.
|
|
6507
|
-
* @param [options]
|
|
6508
|
-
* Defaults:
|
|
6509
|
-
* {start: First tree node, reverse: false, includeSelf: true, includeHidden: false, wrap: false}
|
|
6510
6726
|
* @returns {boolean} false if iteration was canceled
|
|
6511
6727
|
*/
|
|
6512
6728
|
visitRows(callback, options) {
|
|
@@ -6524,7 +6740,7 @@
|
|
|
6524
6740
|
// visit siblings
|
|
6525
6741
|
siblings = parent.children;
|
|
6526
6742
|
nextIdx = siblings.indexOf(node) + siblingOfs;
|
|
6527
|
-
assert(nextIdx >= 0,
|
|
6743
|
+
assert(nextIdx >= 0, `Could not find ${node} in parent's children: ${parent}`);
|
|
6528
6744
|
for (i = nextIdx; i < siblings.length; i++) {
|
|
6529
6745
|
node = siblings[i];
|
|
6530
6746
|
if (node === stopNode) {
|
|
@@ -6662,8 +6878,8 @@
|
|
|
6662
6878
|
// `enableUpdate(${flag}): count -> ${this._disableUpdateCount}...`
|
|
6663
6879
|
// );
|
|
6664
6880
|
if (this._disableUpdateCount === 0) {
|
|
6665
|
-
|
|
6666
|
-
|
|
6881
|
+
this.logDebug(`enableUpdate(): active again. Re-painting to catch up with ${this._disableUpdateIgnoreCount} ignored update requests...`);
|
|
6882
|
+
this._disableUpdateIgnoreCount = 0;
|
|
6667
6883
|
this.setModified(ChangeType.any, { immediate: true });
|
|
6668
6884
|
}
|
|
6669
6885
|
}
|
|
@@ -6718,7 +6934,7 @@
|
|
|
6718
6934
|
}
|
|
6719
6935
|
Wunderbaum.sequence = 0;
|
|
6720
6936
|
/** Wunderbaum release version number "MAJOR.MINOR.PATCH". */
|
|
6721
|
-
Wunderbaum.version = "v0.
|
|
6937
|
+
Wunderbaum.version = "v0.3.0"; // Set to semver by 'grunt release'
|
|
6722
6938
|
/** Expose some useful methods of the util.ts module as `Wunderbaum.util`. */
|
|
6723
6939
|
Wunderbaum.util = util;
|
|
6724
6940
|
|