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.esm.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/*!
|
|
2
2
|
* Wunderbaum - util
|
|
3
|
-
* Copyright (c) 2021-
|
|
4
|
-
* v0.
|
|
3
|
+
* Copyright (c) 2021-2023, Martin Wendt. Released under the MIT license.
|
|
4
|
+
* v0.3.0, Sat, 27 May 2023 04:56:52 GMT (https://github.com/mar10/wunderbaum)
|
|
5
5
|
*/
|
|
6
6
|
/** @module util */
|
|
7
7
|
/** Readable names for `MouseEvent.button` */
|
|
@@ -177,17 +177,19 @@ function extractHtmlText(s) {
|
|
|
177
177
|
* the element is checked, unchecked, or indeterminate.
|
|
178
178
|
* For datetime input control a numerical value is assumed, etc.
|
|
179
179
|
*
|
|
180
|
-
* Common use case: store the new user input in
|
|
180
|
+
* Common use case: store the new user input in a `change` event handler:
|
|
181
181
|
*
|
|
182
182
|
* ```ts
|
|
183
183
|
* change: (e) => {
|
|
184
|
+
* const tree = e.tree;
|
|
185
|
+
* const node = e.node;
|
|
184
186
|
* // Read the value from the input control that triggered the change event:
|
|
185
|
-
* let value =
|
|
186
|
-
* //
|
|
187
|
-
*
|
|
187
|
+
* let value = tree.getValueFromElem(e.element);
|
|
188
|
+
* // and store it to the node model (assuming the column id matches the property name)
|
|
189
|
+
* node.data[e.info.colId] = value;
|
|
188
190
|
* },
|
|
189
191
|
* ```
|
|
190
|
-
* @param elem `<input>` or `<select>` element Also a parent `span.wb-col` is accepted.
|
|
192
|
+
* @param elem `<input>` or `<select>` element. Also a parent `span.wb-col` is accepted.
|
|
191
193
|
* @param coerce pass true to convert date/time inputs to `Date`.
|
|
192
194
|
* @returns the value
|
|
193
195
|
*/
|
|
@@ -251,6 +253,23 @@ function getValueFromElem(elem, coerce = false) {
|
|
|
251
253
|
* value is truethy, falsy, or `null`.
|
|
252
254
|
* For datetime input control a numerical value is assumed, etc.
|
|
253
255
|
*
|
|
256
|
+
* Common use case: update embedded input controls in a `render` event handler:
|
|
257
|
+
*
|
|
258
|
+
* ```ts
|
|
259
|
+
* render: (e) => {
|
|
260
|
+
* // e.node.log(e.type, e, e.node.data);
|
|
261
|
+
*
|
|
262
|
+
* for (const col of Object.values(e.renderColInfosById)) {
|
|
263
|
+
* switch (col.id) {
|
|
264
|
+
* default:
|
|
265
|
+
* // Assumption: we named column.id === node.data.NAME
|
|
266
|
+
* util.setValueToElem(col.elem, e.node.data[col.id]);
|
|
267
|
+
* break;
|
|
268
|
+
* }
|
|
269
|
+
* }
|
|
270
|
+
* },
|
|
271
|
+
* ```
|
|
272
|
+
*
|
|
254
273
|
* @param elem `<input>` or `<select>` element Also a parent `span.wb-col` is accepted.
|
|
255
274
|
* @param value a value that matches the target element.
|
|
256
275
|
*/
|
|
@@ -282,7 +301,6 @@ function setValueToElem(elem, value) {
|
|
|
282
301
|
case "datetime":
|
|
283
302
|
case "datetime-local":
|
|
284
303
|
input.valueAsDate = new Date(value);
|
|
285
|
-
// input.valueAsDate = value; // breaks in Edge?
|
|
286
304
|
break;
|
|
287
305
|
case "number":
|
|
288
306
|
case "range":
|
|
@@ -294,7 +312,7 @@ function setValueToElem(elem, value) {
|
|
|
294
312
|
}
|
|
295
313
|
break;
|
|
296
314
|
case "radio":
|
|
297
|
-
error(
|
|
315
|
+
error(`Not yet implemented: ${type}`);
|
|
298
316
|
// const name = input.name;
|
|
299
317
|
// const checked = input.parentElement!.querySelector(
|
|
300
318
|
// `input[name="${name}"]:checked`
|
|
@@ -321,7 +339,7 @@ function setValueToElem(elem, value) {
|
|
|
321
339
|
}
|
|
322
340
|
}
|
|
323
341
|
}
|
|
324
|
-
/** Show/hide element by setting the `display`style to 'none'. */
|
|
342
|
+
/** Show/hide element by setting the `display` style to 'none'. */
|
|
325
343
|
function setElemDisplay(elem, flag) {
|
|
326
344
|
const style = elemFromSelector(elem).style;
|
|
327
345
|
if (flag) {
|
|
@@ -366,7 +384,23 @@ function eventTargetFromSelector(obj) {
|
|
|
366
384
|
* The result also contains a prefix for modifiers if any, for example
|
|
367
385
|
* `"x"`, `"F2"`, `"Control+Home"`, or `"Shift+clickright"`.
|
|
368
386
|
* This is especially useful in `switch` statements, to make sure that modifier
|
|
369
|
-
* keys are considered and handled correctly
|
|
387
|
+
* keys are considered and handled correctly:
|
|
388
|
+
* ```ts
|
|
389
|
+
* const eventName = util.eventToString(e);
|
|
390
|
+
* switch (eventName) {
|
|
391
|
+
* case "+":
|
|
392
|
+
* case "Add":
|
|
393
|
+
* ...
|
|
394
|
+
* break;
|
|
395
|
+
* case "Enter":
|
|
396
|
+
* case "End":
|
|
397
|
+
* case "Control+End":
|
|
398
|
+
* case "Meta+ArrowDown":
|
|
399
|
+
* case "PageDown":
|
|
400
|
+
* ...
|
|
401
|
+
* break;
|
|
402
|
+
* }
|
|
403
|
+
* ```
|
|
370
404
|
*/
|
|
371
405
|
function eventToString(event) {
|
|
372
406
|
let key = event.key, et = event.type, s = [];
|
|
@@ -591,6 +625,21 @@ function toSet(val) {
|
|
|
591
625
|
}
|
|
592
626
|
throw new Error("Cannot convert to Set<string>: " + val);
|
|
593
627
|
}
|
|
628
|
+
// /** Check if a string is contained in an Array or Set. */
|
|
629
|
+
// export function isAnyOf(s: string, items: Array<string>|Set<string>): boolean {
|
|
630
|
+
// return Array.prototype.includes.call(items, s)
|
|
631
|
+
// }
|
|
632
|
+
// /** Check if an Array or Set has at least one matching entry. */
|
|
633
|
+
// export function hasAnyOf(container: Array<string>|Set<string>, items: Array<string>): boolean {
|
|
634
|
+
// if (Array.isArray(container)) {
|
|
635
|
+
// return container.some(v => )
|
|
636
|
+
// }
|
|
637
|
+
// return container.some(v => {})
|
|
638
|
+
// // const container = toSet(items);
|
|
639
|
+
// // const itemSet = toSet(items);
|
|
640
|
+
// // Array.prototype.includes
|
|
641
|
+
// // throw new Error("Cannot convert to Set<string>: " + val);
|
|
642
|
+
// }
|
|
594
643
|
/** Return a canonical string representation for an object's type (e.g. 'array', 'number', ...). */
|
|
595
644
|
function type(obj) {
|
|
596
645
|
return Object.prototype.toString
|
|
@@ -706,28 +755,40 @@ var util = /*#__PURE__*/Object.freeze({
|
|
|
706
755
|
|
|
707
756
|
/*!
|
|
708
757
|
* Wunderbaum - types
|
|
709
|
-
* Copyright (c) 2021-
|
|
710
|
-
* v0.
|
|
758
|
+
* Copyright (c) 2021-2023, Martin Wendt. Released under the MIT license.
|
|
759
|
+
* v0.3.0, Sat, 27 May 2023 04:56:52 GMT (https://github.com/mar10/wunderbaum)
|
|
760
|
+
*/
|
|
761
|
+
/**
|
|
762
|
+
* Possible values for {@link WunderbaumNode.setModified()} and {@link Wunderbaum.setModified()}.
|
|
711
763
|
*/
|
|
712
|
-
/** Possible values for `setModified()`. */
|
|
713
764
|
var ChangeType;
|
|
714
765
|
(function (ChangeType) {
|
|
715
766
|
/** Re-render the whole viewport, headers, and all rows. */
|
|
716
767
|
ChangeType["any"] = "any";
|
|
717
|
-
/**
|
|
768
|
+
/** A node's title, icon, columns, or status have changed. Update the existing row markup. */
|
|
718
769
|
ChangeType["data"] = "data";
|
|
719
|
-
/**
|
|
720
|
-
ChangeType["
|
|
721
|
-
/**
|
|
770
|
+
/** The `tree.columns` definition has changed beyond simple width adjustments. */
|
|
771
|
+
ChangeType["colStructure"] = "colStructure";
|
|
772
|
+
/** The viewport/window was resized. Adjust layout attributes for all elements. */
|
|
773
|
+
ChangeType["resize"] = "resize";
|
|
774
|
+
/** A node's definition has changed beyond status and data. Re-render the whole row's markup. */
|
|
722
775
|
ChangeType["row"] = "row";
|
|
723
|
-
/**
|
|
776
|
+
/** Nodes have been added, removed, etc. Update markup. */
|
|
724
777
|
ChangeType["structure"] = "structure";
|
|
725
|
-
/** Update current row's classes, to reflect active, selected, ... */
|
|
778
|
+
/** A node's status has changed. Update current row's classes, to reflect active, selected, ... */
|
|
726
779
|
ChangeType["status"] = "status";
|
|
727
|
-
/** Update the 'top' property of all rows. */
|
|
728
|
-
ChangeType["
|
|
780
|
+
/** Vertical scroll event. Update the 'top' property of all rows. */
|
|
781
|
+
ChangeType["scroll"] = "scroll";
|
|
729
782
|
})(ChangeType || (ChangeType = {}));
|
|
730
|
-
|
|
783
|
+
/* Internal use. */
|
|
784
|
+
var RenderFlag;
|
|
785
|
+
(function (RenderFlag) {
|
|
786
|
+
RenderFlag["clearMarkup"] = "clearMarkup";
|
|
787
|
+
RenderFlag["header"] = "header";
|
|
788
|
+
RenderFlag["redraw"] = "redraw";
|
|
789
|
+
RenderFlag["scroll"] = "scroll";
|
|
790
|
+
})(RenderFlag || (RenderFlag = {}));
|
|
791
|
+
/** Possible values for {@link WunderbaumNode.setStatus()}. */
|
|
731
792
|
var NodeStatusType;
|
|
732
793
|
(function (NodeStatusType) {
|
|
733
794
|
NodeStatusType["ok"] = "ok";
|
|
@@ -758,8 +819,8 @@ var NavModeEnum;
|
|
|
758
819
|
|
|
759
820
|
/*!
|
|
760
821
|
* Wunderbaum - wb_extension_base
|
|
761
|
-
* Copyright (c) 2021-
|
|
762
|
-
* v0.
|
|
822
|
+
* Copyright (c) 2021-2023, Martin Wendt. Released under the MIT license.
|
|
823
|
+
* v0.3.0, Sat, 27 May 2023 04:56:52 GMT (https://github.com/mar10/wunderbaum)
|
|
763
824
|
*/
|
|
764
825
|
class WunderbaumExtension {
|
|
765
826
|
constructor(tree, id, defaults) {
|
|
@@ -1049,8 +1110,8 @@ function debounce(func, wait = 0, options = {}) {
|
|
|
1049
1110
|
|
|
1050
1111
|
/*!
|
|
1051
1112
|
* Wunderbaum - ext-filter
|
|
1052
|
-
* Copyright (c) 2021-
|
|
1053
|
-
* v0.
|
|
1113
|
+
* Copyright (c) 2021-2023, Martin Wendt. Released under the MIT license.
|
|
1114
|
+
* v0.3.0, Sat, 27 May 2023 04:56:52 GMT (https://github.com/mar10/wunderbaum)
|
|
1054
1115
|
*/
|
|
1055
1116
|
const START_MARKER = "\uFFF7";
|
|
1056
1117
|
const END_MARKER = "\uFFF8";
|
|
@@ -1350,8 +1411,8 @@ function _markFuzzyMatchedChars(text, matches, escapeTitles = true) {
|
|
|
1350
1411
|
|
|
1351
1412
|
/*!
|
|
1352
1413
|
* Wunderbaum - ext-keynav
|
|
1353
|
-
* Copyright (c) 2021-
|
|
1354
|
-
* v0.
|
|
1414
|
+
* Copyright (c) 2021-2023, Martin Wendt. Released under the MIT license.
|
|
1415
|
+
* v0.3.0, Sat, 27 May 2023 04:56:52 GMT (https://github.com/mar10/wunderbaum)
|
|
1355
1416
|
*/
|
|
1356
1417
|
const QUICKSEARCH_DELAY = 500;
|
|
1357
1418
|
class KeynavExtension extends WunderbaumExtension {
|
|
@@ -1690,8 +1751,8 @@ class KeynavExtension extends WunderbaumExtension {
|
|
|
1690
1751
|
|
|
1691
1752
|
/*!
|
|
1692
1753
|
* Wunderbaum - ext-logger
|
|
1693
|
-
* Copyright (c) 2021-
|
|
1694
|
-
* v0.
|
|
1754
|
+
* Copyright (c) 2021-2023, Martin Wendt. Released under the MIT license.
|
|
1755
|
+
* v0.3.0, Sat, 27 May 2023 04:56:52 GMT (https://github.com/mar10/wunderbaum)
|
|
1695
1756
|
*/
|
|
1696
1757
|
class LoggerExtension extends WunderbaumExtension {
|
|
1697
1758
|
constructor(tree) {
|
|
@@ -1730,8 +1791,8 @@ class LoggerExtension extends WunderbaumExtension {
|
|
|
1730
1791
|
|
|
1731
1792
|
/*!
|
|
1732
1793
|
* Wunderbaum - common
|
|
1733
|
-
* Copyright (c) 2021-
|
|
1734
|
-
* v0.
|
|
1794
|
+
* Copyright (c) 2021-2023, Martin Wendt. Released under the MIT license.
|
|
1795
|
+
* v0.3.0, Sat, 27 May 2023 04:56:52 GMT (https://github.com/mar10/wunderbaum)
|
|
1735
1796
|
*/
|
|
1736
1797
|
const DEFAULT_DEBUGLEVEL = 4; // Replaced by rollup script
|
|
1737
1798
|
/**
|
|
@@ -1853,6 +1914,12 @@ function makeNodeTitleStartMatcher(s) {
|
|
|
1853
1914
|
return reMatch.test(node.title);
|
|
1854
1915
|
};
|
|
1855
1916
|
}
|
|
1917
|
+
/** Compare two nodes by title (case-insensitive). */
|
|
1918
|
+
function nodeTitleSorter(a, b) {
|
|
1919
|
+
const x = a.title.toLowerCase();
|
|
1920
|
+
const y = b.title.toLowerCase();
|
|
1921
|
+
return x === y ? 0 : x > y ? 1 : -1;
|
|
1922
|
+
}
|
|
1856
1923
|
function unflattenSource(source) {
|
|
1857
1924
|
var _a, _b, _c;
|
|
1858
1925
|
const { _format, _keyMap, _positional, children } = source;
|
|
@@ -1970,8 +2037,8 @@ function inflateSourceData(source) {
|
|
|
1970
2037
|
|
|
1971
2038
|
/*!
|
|
1972
2039
|
* Wunderbaum - ext-dnd
|
|
1973
|
-
* Copyright (c) 2021-
|
|
1974
|
-
* v0.
|
|
2040
|
+
* Copyright (c) 2021-2023, Martin Wendt. Released under the MIT license.
|
|
2041
|
+
* v0.3.0, Sat, 27 May 2023 04:56:52 GMT (https://github.com/mar10/wunderbaum)
|
|
1975
2042
|
*/
|
|
1976
2043
|
const nodeMimeType = "application/x-wunderbaum-node";
|
|
1977
2044
|
class DndExtension extends WunderbaumExtension {
|
|
@@ -2082,6 +2149,7 @@ class DndExtension extends WunderbaumExtension {
|
|
|
2082
2149
|
// Only 'before' and 'after':
|
|
2083
2150
|
return dy > ROW_HEIGHT / 2 ? "after" : "before";
|
|
2084
2151
|
}
|
|
2152
|
+
// return "over";
|
|
2085
2153
|
}
|
|
2086
2154
|
/* Implement auto scrolling when drag cursor is in top/bottom area of scroll parent. */
|
|
2087
2155
|
autoScroll(event) {
|
|
@@ -2156,6 +2224,8 @@ class DndExtension extends WunderbaumExtension {
|
|
|
2156
2224
|
}
|
|
2157
2225
|
onDropEvent(e) {
|
|
2158
2226
|
// const isLink = event.dataTransfer.types.includes("text/uri-list");
|
|
2227
|
+
const srcNode = this.srcNode;
|
|
2228
|
+
const srcTree = srcNode ? srcNode.tree : null;
|
|
2159
2229
|
const targetNode = Wunderbaum.getNode(e);
|
|
2160
2230
|
const dndOpts = this.treeOpts.dnd;
|
|
2161
2231
|
const dt = e.dataTransfer;
|
|
@@ -2163,7 +2233,7 @@ class DndExtension extends WunderbaumExtension {
|
|
|
2163
2233
|
this._leaveNode();
|
|
2164
2234
|
return;
|
|
2165
2235
|
}
|
|
2166
|
-
if (
|
|
2236
|
+
if (!["dragenter", "dragover", "dragleave"].includes(e.type)) {
|
|
2167
2237
|
this.tree.logDebug("onDropEvent." +
|
|
2168
2238
|
e.type +
|
|
2169
2239
|
" targetNode: " +
|
|
@@ -2184,9 +2254,24 @@ class DndExtension extends WunderbaumExtension {
|
|
|
2184
2254
|
this.lastTargetNode = targetNode;
|
|
2185
2255
|
this.lastEnterStamp = Date.now();
|
|
2186
2256
|
if (
|
|
2187
|
-
// Don't
|
|
2188
|
-
|
|
2189
|
-
|
|
2257
|
+
// Don't drop on status node:
|
|
2258
|
+
targetNode.isStatusNode() ||
|
|
2259
|
+
// Prevent dropping nodes from different Wunderbaum trees:
|
|
2260
|
+
(dndOpts.preventForeignNodes && targetNode.tree !== srcTree) ||
|
|
2261
|
+
// Prevent dropping items on unloaded lazy Wunderbaum tree nodes:
|
|
2262
|
+
(dndOpts.preventLazyParents && !targetNode.isLoaded()) ||
|
|
2263
|
+
// Prevent dropping items other than Wunderbaum tree nodes:
|
|
2264
|
+
(dndOpts.preventNonNodes && !srcNode) ||
|
|
2265
|
+
// Prevent dropping nodes on own descendants:
|
|
2266
|
+
(dndOpts.preventRecursion &&
|
|
2267
|
+
srcNode &&
|
|
2268
|
+
srcNode.isAncestorOf(targetNode)) ||
|
|
2269
|
+
// Prevent dropping nodes under same direct parent:
|
|
2270
|
+
(dndOpts.preventSameParent &&
|
|
2271
|
+
srcNode &&
|
|
2272
|
+
targetNode.parent === srcNode.parent) ||
|
|
2273
|
+
// Don't allow void operation ('drop on self'): TODO: should be checke onn move only
|
|
2274
|
+
(dndOpts.preventVoidMoves && targetNode === srcNode)) {
|
|
2190
2275
|
dt.dropEffect = "none";
|
|
2191
2276
|
return true; // Prevent drop operation
|
|
2192
2277
|
}
|
|
@@ -2232,9 +2317,11 @@ class DndExtension extends WunderbaumExtension {
|
|
|
2232
2317
|
else if (e.type === "drop") {
|
|
2233
2318
|
e.stopPropagation(); // prevent browser from opening links?
|
|
2234
2319
|
this._leaveNode();
|
|
2320
|
+
const region = this.lastDropRegion;
|
|
2235
2321
|
targetNode._callEvent("dnd.drop", {
|
|
2236
2322
|
event: e,
|
|
2237
|
-
region:
|
|
2323
|
+
region: region,
|
|
2324
|
+
defaultDropMode: region === "over" ? "appendChild" : region,
|
|
2238
2325
|
sourceNode: this.srcNode,
|
|
2239
2326
|
});
|
|
2240
2327
|
}
|
|
@@ -2243,8 +2330,8 @@ class DndExtension extends WunderbaumExtension {
|
|
|
2243
2330
|
|
|
2244
2331
|
/*!
|
|
2245
2332
|
* Wunderbaum - drag_observer
|
|
2246
|
-
* Copyright (c) 2021-
|
|
2247
|
-
* v0.
|
|
2333
|
+
* Copyright (c) 2021-2023, Martin Wendt. Released under the MIT license.
|
|
2334
|
+
* v0.3.0, Sat, 27 May 2023 04:56:52 GMT (https://github.com/mar10/wunderbaum)
|
|
2248
2335
|
*/
|
|
2249
2336
|
/**
|
|
2250
2337
|
* Convert mouse- and touch events to 'dragstart', 'drag', and 'dragstop'.
|
|
@@ -2379,8 +2466,8 @@ class DragObserver {
|
|
|
2379
2466
|
|
|
2380
2467
|
/*!
|
|
2381
2468
|
* Wunderbaum - ext-grid
|
|
2382
|
-
* Copyright (c) 2021-
|
|
2383
|
-
* v0.
|
|
2469
|
+
* Copyright (c) 2021-2023, Martin Wendt. Released under the MIT license.
|
|
2470
|
+
* v0.3.0, Sat, 27 May 2023 04:56:52 GMT (https://github.com/mar10/wunderbaum)
|
|
2384
2471
|
*/
|
|
2385
2472
|
class GridExtension extends WunderbaumExtension {
|
|
2386
2473
|
constructor(tree) {
|
|
@@ -2416,8 +2503,8 @@ class GridExtension extends WunderbaumExtension {
|
|
|
2416
2503
|
|
|
2417
2504
|
/*!
|
|
2418
2505
|
* Wunderbaum - deferred
|
|
2419
|
-
* Copyright (c) 2021-
|
|
2420
|
-
* v0.
|
|
2506
|
+
* Copyright (c) 2021-2023, Martin Wendt. Released under the MIT license.
|
|
2507
|
+
* v0.3.0, Sat, 27 May 2023 04:56:52 GMT (https://github.com/mar10/wunderbaum)
|
|
2421
2508
|
*/
|
|
2422
2509
|
/**
|
|
2423
2510
|
* Implement a ES6 Promise, that exposes a resolve() and reject() method.
|
|
@@ -2469,8 +2556,8 @@ class Deferred {
|
|
|
2469
2556
|
|
|
2470
2557
|
/*!
|
|
2471
2558
|
* Wunderbaum - wunderbaum_node
|
|
2472
|
-
* Copyright (c) 2021-
|
|
2473
|
-
* v0.
|
|
2559
|
+
* Copyright (c) 2021-2023, Martin Wendt. Released under the MIT license.
|
|
2560
|
+
* v0.3.0, Sat, 27 May 2023 04:56:52 GMT (https://github.com/mar10/wunderbaum)
|
|
2474
2561
|
*/
|
|
2475
2562
|
/** Top-level properties that can be passed with `data`. */
|
|
2476
2563
|
const NODE_PROPS = new Set([
|
|
@@ -2707,9 +2794,9 @@ class WunderbaumNode {
|
|
|
2707
2794
|
* @param [mode=child] 'before', 'after', 'firstChild', or 'child' ('over' is a synonym for 'child')
|
|
2708
2795
|
* @returns new node
|
|
2709
2796
|
*/
|
|
2710
|
-
addNode(nodeData, mode = "
|
|
2797
|
+
addNode(nodeData, mode = "appendChild") {
|
|
2711
2798
|
if (mode === "over") {
|
|
2712
|
-
mode = "
|
|
2799
|
+
mode = "appendChild"; // compatible with drop region
|
|
2713
2800
|
}
|
|
2714
2801
|
switch (mode) {
|
|
2715
2802
|
case "after":
|
|
@@ -2718,11 +2805,11 @@ class WunderbaumNode {
|
|
|
2718
2805
|
});
|
|
2719
2806
|
case "before":
|
|
2720
2807
|
return this.parent.addChildren(nodeData, { before: this });
|
|
2721
|
-
case "
|
|
2808
|
+
case "prependChild":
|
|
2722
2809
|
// Insert before the first child if any
|
|
2723
2810
|
// let insertBefore = this.children ? this.children[0] : undefined;
|
|
2724
2811
|
return this.addChildren(nodeData, { before: 0 });
|
|
2725
|
-
case "
|
|
2812
|
+
case "appendChild":
|
|
2726
2813
|
return this.addChildren(nodeData);
|
|
2727
2814
|
}
|
|
2728
2815
|
assert(false, "Invalid mode: " + mode);
|
|
@@ -3117,8 +3204,17 @@ class WunderbaumNode {
|
|
|
3117
3204
|
* an expand operation is currently possible.
|
|
3118
3205
|
*/
|
|
3119
3206
|
isExpandable(andCollapsed = false) {
|
|
3120
|
-
//
|
|
3121
|
-
|
|
3207
|
+
// `false` is never expandable (unoffical)
|
|
3208
|
+
if ((andCollapsed && this.expanded) || this.children === false) {
|
|
3209
|
+
return false;
|
|
3210
|
+
}
|
|
3211
|
+
if (this.children == null) {
|
|
3212
|
+
return this.lazy; // null or undefined can trigger lazy load
|
|
3213
|
+
}
|
|
3214
|
+
if (this.children.length === 0) {
|
|
3215
|
+
return !!this.tree.options.emptyChildListExpandable;
|
|
3216
|
+
}
|
|
3217
|
+
return true;
|
|
3122
3218
|
}
|
|
3123
3219
|
/** Return true if this node is currently in edit-title mode. */
|
|
3124
3220
|
isEditing() {
|
|
@@ -3252,7 +3348,7 @@ class WunderbaumNode {
|
|
|
3252
3348
|
tree.logInfo("Redefine columns", source.columns);
|
|
3253
3349
|
tree.columns = source.columns;
|
|
3254
3350
|
delete source.columns;
|
|
3255
|
-
tree.
|
|
3351
|
+
tree.setModified(ChangeType.colStructure);
|
|
3256
3352
|
}
|
|
3257
3353
|
this.addChildren(source.children);
|
|
3258
3354
|
// Add extra data to `tree.data`
|
|
@@ -3264,12 +3360,50 @@ class WunderbaumNode {
|
|
|
3264
3360
|
}
|
|
3265
3361
|
this._callEvent("load");
|
|
3266
3362
|
}
|
|
3363
|
+
async _fetchWithOptions(source) {
|
|
3364
|
+
var _a, _b;
|
|
3365
|
+
// Either a URL string or an object with a `.url` property.
|
|
3366
|
+
let url, params, body, options, rest;
|
|
3367
|
+
let fetchOpts = {};
|
|
3368
|
+
if (typeof source === "string") {
|
|
3369
|
+
// source is a plain URL string: assume GET request
|
|
3370
|
+
url = source;
|
|
3371
|
+
fetchOpts.method = "GET";
|
|
3372
|
+
}
|
|
3373
|
+
else if (isPlainObject(source)) {
|
|
3374
|
+
// source is a plain object with `.url` property.
|
|
3375
|
+
({ url, params, body, options, ...rest } = source);
|
|
3376
|
+
assert(typeof url === "string", `expected source.url as string`);
|
|
3377
|
+
if (isPlainObject(options)) {
|
|
3378
|
+
fetchOpts = options;
|
|
3379
|
+
}
|
|
3380
|
+
if (isPlainObject(body)) {
|
|
3381
|
+
// we also accept 'body' as object...
|
|
3382
|
+
assert(!fetchOpts.body, "options.body should be passed as source.body");
|
|
3383
|
+
fetchOpts.body = JSON.stringify(fetchOpts.body);
|
|
3384
|
+
(_a = fetchOpts.method) !== null && _a !== void 0 ? _a : (fetchOpts.method = "POST"); // set default
|
|
3385
|
+
}
|
|
3386
|
+
if (isPlainObject(params)) {
|
|
3387
|
+
url += "?" + new URLSearchParams(params);
|
|
3388
|
+
(_b = fetchOpts.method) !== null && _b !== void 0 ? _b : (fetchOpts.method = "GET"); // set default
|
|
3389
|
+
}
|
|
3390
|
+
}
|
|
3391
|
+
else {
|
|
3392
|
+
url = ""; // keep linter happy
|
|
3393
|
+
error(`Unsupported source format: ${source}`);
|
|
3394
|
+
}
|
|
3395
|
+
this.setStatus(NodeStatusType.loading);
|
|
3396
|
+
const response = await fetch(url, fetchOpts);
|
|
3397
|
+
if (!response.ok) {
|
|
3398
|
+
error(`GET ${url} returned ${response.status}, ${response}`);
|
|
3399
|
+
}
|
|
3400
|
+
return await response.json();
|
|
3401
|
+
}
|
|
3267
3402
|
/** Download data from the cloud, then call `.update()`. */
|
|
3268
3403
|
async load(source) {
|
|
3269
3404
|
const tree = this.tree;
|
|
3270
3405
|
const requestId = Date.now();
|
|
3271
3406
|
const prevParent = this.parent;
|
|
3272
|
-
const url = typeof source === "string" ? source : source.url;
|
|
3273
3407
|
const start = Date.now();
|
|
3274
3408
|
let elap = 0, elapLoad = 0, elapProcess = 0;
|
|
3275
3409
|
// Check for overlapping requests
|
|
@@ -3280,17 +3414,16 @@ class WunderbaumNode {
|
|
|
3280
3414
|
this._requestId = requestId;
|
|
3281
3415
|
// const timerLabel = tree.logTime(this + ".load()");
|
|
3282
3416
|
try {
|
|
3417
|
+
let url = typeof source === "string" ? source : source.url;
|
|
3283
3418
|
if (!url) {
|
|
3419
|
+
// An array or a plain object (that does NOT contain a `.url` property)
|
|
3420
|
+
// will be treated as native Wunderbaum data
|
|
3284
3421
|
this._loadSourceObject(source);
|
|
3285
3422
|
elapProcess = Date.now() - start;
|
|
3286
3423
|
}
|
|
3287
3424
|
else {
|
|
3288
|
-
|
|
3289
|
-
const
|
|
3290
|
-
if (!response.ok) {
|
|
3291
|
-
error(`GET ${url} returned ${response.status}, ${response}`);
|
|
3292
|
-
}
|
|
3293
|
-
const data = await response.json();
|
|
3425
|
+
// Either a URL string or an object with a `.url` property.
|
|
3426
|
+
const data = await this._fetchWithOptions(source);
|
|
3294
3427
|
elapLoad = Date.now() - start;
|
|
3295
3428
|
if (this._requestId && this._requestId > requestId) {
|
|
3296
3429
|
this.logWarn(`Ignored load response #${requestId} because #${this._requestId} is pending.`);
|
|
@@ -3433,6 +3566,9 @@ class WunderbaumNode {
|
|
|
3433
3566
|
}
|
|
3434
3567
|
/** Move this node to targetNode. */
|
|
3435
3568
|
moveTo(targetNode, mode = "appendChild", map) {
|
|
3569
|
+
if (mode === "over") {
|
|
3570
|
+
mode = "appendChild"; // compatible with drop region
|
|
3571
|
+
}
|
|
3436
3572
|
if (mode === "prependChild") {
|
|
3437
3573
|
if (targetNode.children && targetNode.children.length) {
|
|
3438
3574
|
mode = "before";
|
|
@@ -3489,7 +3625,7 @@ class WunderbaumNode {
|
|
|
3489
3625
|
targetParent.children.splice(pos + 1, 0, this);
|
|
3490
3626
|
break;
|
|
3491
3627
|
default:
|
|
3492
|
-
error(
|
|
3628
|
+
error(`Invalid mode '${mode}'.`);
|
|
3493
3629
|
}
|
|
3494
3630
|
}
|
|
3495
3631
|
else {
|
|
@@ -3516,7 +3652,12 @@ class WunderbaumNode {
|
|
|
3516
3652
|
n.tree = targetNode.tree;
|
|
3517
3653
|
}, true);
|
|
3518
3654
|
}
|
|
3519
|
-
|
|
3655
|
+
// Make sure we update async, because discarding the markup would prevent
|
|
3656
|
+
// DragAndDrop to generate a dragend event on the source node
|
|
3657
|
+
setTimeout(() => {
|
|
3658
|
+
// Even indentation may have changed:
|
|
3659
|
+
tree.setModified(ChangeType.any);
|
|
3660
|
+
}, 0);
|
|
3520
3661
|
// TODO: fix selection state
|
|
3521
3662
|
// TODO: fix active state
|
|
3522
3663
|
}
|
|
@@ -3642,7 +3783,7 @@ class WunderbaumNode {
|
|
|
3642
3783
|
icon = iconMap.loading;
|
|
3643
3784
|
}
|
|
3644
3785
|
if (icon === false) {
|
|
3645
|
-
return null;
|
|
3786
|
+
return null; // explicitly disabled: don't try default icons
|
|
3646
3787
|
}
|
|
3647
3788
|
if (typeof icon === "string") ;
|
|
3648
3789
|
else if (this.statusNodeType) {
|
|
@@ -3661,7 +3802,11 @@ class WunderbaumNode {
|
|
|
3661
3802
|
icon = iconMap.doc;
|
|
3662
3803
|
}
|
|
3663
3804
|
// this.log("_createIcon: " + icon);
|
|
3664
|
-
if (icon
|
|
3805
|
+
if (!icon) {
|
|
3806
|
+
iconSpan = document.createElement("i");
|
|
3807
|
+
iconSpan.className = "wb-icon";
|
|
3808
|
+
}
|
|
3809
|
+
else if (icon.indexOf("<") >= 0) {
|
|
3665
3810
|
// HTML
|
|
3666
3811
|
iconSpan = elemFromHtml(icon);
|
|
3667
3812
|
}
|
|
@@ -3979,9 +4124,12 @@ class WunderbaumNode {
|
|
|
3979
4124
|
case "data":
|
|
3980
4125
|
this._render_data(opts);
|
|
3981
4126
|
break;
|
|
3982
|
-
|
|
4127
|
+
case "row":
|
|
4128
|
+
// _rowElem is not yet created (asserted in _render_markup)
|
|
3983
4129
|
this._render_markup(opts);
|
|
3984
4130
|
break;
|
|
4131
|
+
default:
|
|
4132
|
+
error(`Invalid change type '${opts.change}'.`);
|
|
3985
4133
|
}
|
|
3986
4134
|
}
|
|
3987
4135
|
/**
|
|
@@ -4203,20 +4351,24 @@ class WunderbaumNode {
|
|
|
4203
4351
|
this.setModified();
|
|
4204
4352
|
}
|
|
4205
4353
|
/** Set a new icon path or class. */
|
|
4206
|
-
setIcon() {
|
|
4207
|
-
|
|
4208
|
-
|
|
4354
|
+
setIcon(icon) {
|
|
4355
|
+
this.icon = icon;
|
|
4356
|
+
this.setModified();
|
|
4209
4357
|
}
|
|
4210
4358
|
/** Change node's {@link key} and/or {@link refKey}. */
|
|
4211
4359
|
setKey(key, refKey) {
|
|
4212
4360
|
throw new Error("Not yet implemented");
|
|
4213
4361
|
}
|
|
4214
4362
|
/**
|
|
4215
|
-
*
|
|
4363
|
+
* Trigger a repaint, typically after a status or data change.
|
|
4216
4364
|
*
|
|
4217
4365
|
* `change` defaults to 'data', which handles modifcations of title, icon,
|
|
4218
4366
|
* and column content. It can be reduced to 'ChangeType.status' if only
|
|
4219
4367
|
* active/focus/selected state has changed.
|
|
4368
|
+
*
|
|
4369
|
+
* This method will eventually call {@link WunderbaumNode.render()} with
|
|
4370
|
+
* default options, but may be more consistent with the tree's
|
|
4371
|
+
* {@link Wunderbaum.setModified()} API.
|
|
4220
4372
|
*/
|
|
4221
4373
|
setModified(change = ChangeType.data) {
|
|
4222
4374
|
assert(change === ChangeType.status || change === ChangeType.data);
|
|
@@ -4252,7 +4404,7 @@ class WunderbaumNode {
|
|
|
4252
4404
|
let firstChild = children ? children[0] : null;
|
|
4253
4405
|
assert(data.statusNodeType);
|
|
4254
4406
|
assert(!firstChild || !firstChild.isStatusNode());
|
|
4255
|
-
statusNode = this.addNode(data, "
|
|
4407
|
+
statusNode = this.addNode(data, "prependChild");
|
|
4256
4408
|
statusNode.match = true;
|
|
4257
4409
|
tree.setModified(ChangeType.structure);
|
|
4258
4410
|
return statusNode;
|
|
@@ -4318,11 +4470,37 @@ class WunderbaumNode {
|
|
|
4318
4470
|
this.setModified();
|
|
4319
4471
|
// this.triggerModify("rename"); // TODO
|
|
4320
4472
|
}
|
|
4473
|
+
_sortChildren(cmp, deep) {
|
|
4474
|
+
const cl = this.children;
|
|
4475
|
+
if (!cl) {
|
|
4476
|
+
return;
|
|
4477
|
+
}
|
|
4478
|
+
cl.sort(cmp);
|
|
4479
|
+
if (deep) {
|
|
4480
|
+
for (let i = 0, l = cl.length; i < l; i++) {
|
|
4481
|
+
if (cl[i].children) {
|
|
4482
|
+
cl[i]._sortChildren(cmp, deep);
|
|
4483
|
+
}
|
|
4484
|
+
}
|
|
4485
|
+
}
|
|
4486
|
+
}
|
|
4487
|
+
/**
|
|
4488
|
+
* Sort child list by title or custom criteria.
|
|
4489
|
+
* @param {function} cmp custom compare function(a, b) that returns -1, 0, or 1
|
|
4490
|
+
* (defaults to sorting by title).
|
|
4491
|
+
* @param {boolean} deep pass true to sort all descendant nodes recursively
|
|
4492
|
+
*/
|
|
4493
|
+
sortChildren(cmp = nodeTitleSorter, deep = false) {
|
|
4494
|
+
this._sortChildren(cmp || nodeTitleSorter, deep);
|
|
4495
|
+
this.tree.setModified(ChangeType.structure);
|
|
4496
|
+
// this.triggerModify("sort"); // TODO
|
|
4497
|
+
}
|
|
4321
4498
|
/**
|
|
4322
4499
|
* Trigger `modifyChild` event on a parent to signal that a child was modified.
|
|
4323
4500
|
* @param {string} operation Type of change: 'add', 'remove', 'rename', 'move', 'data', ...
|
|
4324
4501
|
*/
|
|
4325
4502
|
triggerModifyChild(operation, child, extra) {
|
|
4503
|
+
this.logDebug(`modifyChild(${operation})`, extra, child);
|
|
4326
4504
|
if (!this.tree.options.modifyChild)
|
|
4327
4505
|
return;
|
|
4328
4506
|
if (child && child.parent !== this) {
|
|
@@ -4421,8 +4599,8 @@ WunderbaumNode.sequence = 0;
|
|
|
4421
4599
|
|
|
4422
4600
|
/*!
|
|
4423
4601
|
* Wunderbaum - ext-edit
|
|
4424
|
-
* Copyright (c) 2021-
|
|
4425
|
-
* v0.
|
|
4602
|
+
* Copyright (c) 2021-2023, Martin Wendt. Released under the MIT license.
|
|
4603
|
+
* v0.3.0, Sat, 27 May 2023 04:56:52 GMT (https://github.com/mar10/wunderbaum)
|
|
4426
4604
|
*/
|
|
4427
4605
|
// const START_MARKER = "\uFFF7";
|
|
4428
4606
|
class EditExtension extends WunderbaumExtension {
|
|
@@ -4713,12 +4891,12 @@ class EditExtension extends WunderbaumExtension {
|
|
|
4713
4891
|
*
|
|
4714
4892
|
* A treegrid control.
|
|
4715
4893
|
*
|
|
4716
|
-
* Copyright (c) 2021-
|
|
4894
|
+
* Copyright (c) 2021-2023, Martin Wendt (https://wwWendt.de).
|
|
4717
4895
|
* https://github.com/mar10/wunderbaum
|
|
4718
4896
|
*
|
|
4719
4897
|
* Released under the MIT license.
|
|
4720
|
-
* @version v0.
|
|
4721
|
-
* @date
|
|
4898
|
+
* @version v0.3.0
|
|
4899
|
+
* @date Sat, 27 May 2023 04:56:52 GMT
|
|
4722
4900
|
*/
|
|
4723
4901
|
class WbSystemRoot extends WunderbaumNode {
|
|
4724
4902
|
constructor(tree) {
|
|
@@ -4747,6 +4925,7 @@ class Wunderbaum {
|
|
|
4747
4925
|
this.refKeyMap = new Map();
|
|
4748
4926
|
this.treeRowCount = 0;
|
|
4749
4927
|
this._disableUpdateCount = 0;
|
|
4928
|
+
this._disableUpdateIgnoreCount = 0;
|
|
4750
4929
|
/** Currently active node if any. */
|
|
4751
4930
|
this.activeNode = null;
|
|
4752
4931
|
/** Current node hat has keyboard focus if any. */
|
|
@@ -4757,8 +4936,7 @@ class Wunderbaum {
|
|
|
4757
4936
|
this.columns = []; // any[] = [];
|
|
4758
4937
|
this._columnsById = {};
|
|
4759
4938
|
// Modification Status
|
|
4760
|
-
this.
|
|
4761
|
-
this.changeScrollRequestPending = false;
|
|
4939
|
+
this.pendingChangeTypes = new Set();
|
|
4762
4940
|
/** Expose some useful methods of the util.ts module as `tree._util`. */
|
|
4763
4941
|
this._util = util;
|
|
4764
4942
|
// --- FILTER ---
|
|
@@ -4794,6 +4972,7 @@ class Wunderbaum {
|
|
|
4794
4972
|
showSpinner: false,
|
|
4795
4973
|
checkbox: false,
|
|
4796
4974
|
minExpandLevel: 0,
|
|
4975
|
+
emptyChildListExpandable: false,
|
|
4797
4976
|
updateThrottleWait: 200,
|
|
4798
4977
|
skeleton: false,
|
|
4799
4978
|
connectTopBreadcrumb: null,
|
|
@@ -4956,11 +5135,11 @@ class Wunderbaum {
|
|
|
4956
5135
|
// --- Bind listeners
|
|
4957
5136
|
this.element.addEventListener("scroll", (e) => {
|
|
4958
5137
|
// this.log(`scroll, scrollTop:${e.target.scrollTop}`, e);
|
|
4959
|
-
this.setModified(ChangeType.
|
|
5138
|
+
this.setModified(ChangeType.scroll);
|
|
4960
5139
|
});
|
|
4961
5140
|
this.resizeObserver = new ResizeObserver((entries) => {
|
|
4962
|
-
this.setModified(ChangeType.vscroll);
|
|
4963
5141
|
// this.log("ResizeObserver: Size changed", entries);
|
|
5142
|
+
this.setModified(ChangeType.resize);
|
|
4964
5143
|
});
|
|
4965
5144
|
this.resizeObserver.observe(this.element);
|
|
4966
5145
|
onEvent(this.nodeListElement, "click", "div.wb-row", (e) => {
|
|
@@ -5496,7 +5675,7 @@ class Wunderbaum {
|
|
|
5496
5675
|
this.options[name] = value;
|
|
5497
5676
|
switch (name) {
|
|
5498
5677
|
case "checkbox":
|
|
5499
|
-
this.setModified(ChangeType.any
|
|
5678
|
+
this.setModified(ChangeType.any);
|
|
5500
5679
|
break;
|
|
5501
5680
|
case "enabled":
|
|
5502
5681
|
this.setEnabled(!!value);
|
|
@@ -5796,8 +5975,9 @@ class Wunderbaum {
|
|
|
5796
5975
|
res.region = NodeRegion.title;
|
|
5797
5976
|
}
|
|
5798
5977
|
else if (cl.contains("wb-expander")) {
|
|
5799
|
-
res.region =
|
|
5800
|
-
|
|
5978
|
+
res.region = node.isExpandable()
|
|
5979
|
+
? NodeRegion.expander
|
|
5980
|
+
: NodeRegion.prefix;
|
|
5801
5981
|
}
|
|
5802
5982
|
else if (cl.contains("wb-checkbox")) {
|
|
5803
5983
|
res.region = NodeRegion.checkbox;
|
|
@@ -5948,7 +6128,7 @@ class Wunderbaum {
|
|
|
5948
6128
|
// Make sure the topNode is always visible
|
|
5949
6129
|
this.scrollTo(topNode);
|
|
5950
6130
|
}
|
|
5951
|
-
// this.setModified(ChangeType.
|
|
6131
|
+
// this.setModified(ChangeType.scroll);
|
|
5952
6132
|
}
|
|
5953
6133
|
}
|
|
5954
6134
|
/**
|
|
@@ -5976,7 +6156,7 @@ class Wunderbaum {
|
|
|
5976
6156
|
// util.assert(node._rowIdx != null);
|
|
5977
6157
|
this.log(`scrollToHorz(${this.activeColIdx}): ${colLeft}..${colRight}, fixedOfs=${fixedWidth}, vpWidth=${vpWidth}, curLeft=${scrollLeft} -> ${newLeft}`);
|
|
5978
6158
|
this.element.scrollLeft = newLeft;
|
|
5979
|
-
// this.setModified(ChangeType.
|
|
6159
|
+
// this.setModified(ChangeType.scroll);
|
|
5980
6160
|
}
|
|
5981
6161
|
/**
|
|
5982
6162
|
* Set column #colIdx to 'active'.
|
|
@@ -6031,34 +6211,40 @@ class Wunderbaum {
|
|
|
6031
6211
|
// this.log(
|
|
6032
6212
|
// `IGNORED setModified(${change}) node=${node} (disable level ${this._disableUpdateCount})`
|
|
6033
6213
|
// );
|
|
6214
|
+
this._disableUpdateIgnoreCount++;
|
|
6034
6215
|
return;
|
|
6035
6216
|
}
|
|
6036
6217
|
// this.log(`setModified(${change}) node=${node}`);
|
|
6037
6218
|
if (!(node instanceof WunderbaumNode)) {
|
|
6038
6219
|
options = node;
|
|
6220
|
+
node = null;
|
|
6039
6221
|
}
|
|
6040
6222
|
const immediate = !!getOption(options, "immediate");
|
|
6041
|
-
const
|
|
6042
|
-
|
|
6043
|
-
this.visit((n) => {
|
|
6044
|
-
n.removeMarkup();
|
|
6045
|
-
});
|
|
6046
|
-
}
|
|
6047
|
-
let callUpdate = false;
|
|
6223
|
+
const RF = RenderFlag;
|
|
6224
|
+
const pending = this.pendingChangeTypes;
|
|
6048
6225
|
switch (change) {
|
|
6049
6226
|
case ChangeType.any:
|
|
6227
|
+
case ChangeType.colStructure:
|
|
6228
|
+
pending.add(RF.header);
|
|
6229
|
+
pending.add(RF.clearMarkup);
|
|
6230
|
+
pending.add(RF.redraw);
|
|
6231
|
+
pending.add(RF.scroll);
|
|
6232
|
+
break;
|
|
6233
|
+
case ChangeType.resize:
|
|
6234
|
+
// case ChangeType.colWidth:
|
|
6235
|
+
pending.add(RF.header);
|
|
6236
|
+
pending.add(RF.redraw);
|
|
6237
|
+
break;
|
|
6050
6238
|
case ChangeType.structure:
|
|
6051
|
-
|
|
6052
|
-
this.changeRedrawRequestPending = true;
|
|
6053
|
-
callUpdate = true;
|
|
6239
|
+
pending.add(RF.redraw);
|
|
6054
6240
|
break;
|
|
6055
|
-
case ChangeType.
|
|
6056
|
-
|
|
6057
|
-
callUpdate = true;
|
|
6241
|
+
case ChangeType.scroll:
|
|
6242
|
+
pending.add(RF.scroll);
|
|
6058
6243
|
break;
|
|
6059
6244
|
case ChangeType.row:
|
|
6060
6245
|
case ChangeType.data:
|
|
6061
6246
|
case ChangeType.status:
|
|
6247
|
+
assert(node, `Option '${change}' requires a node.`);
|
|
6062
6248
|
// Single nodes are immediately updated if already inside the viewport
|
|
6063
6249
|
// (otherwise we can ignore)
|
|
6064
6250
|
if (node._rowElem) {
|
|
@@ -6066,9 +6252,16 @@ class Wunderbaum {
|
|
|
6066
6252
|
}
|
|
6067
6253
|
break;
|
|
6068
6254
|
default:
|
|
6069
|
-
error(`Invalid change type ${change}
|
|
6255
|
+
error(`Invalid change type '${change}'.`);
|
|
6256
|
+
}
|
|
6257
|
+
if (change === ChangeType.colStructure) {
|
|
6258
|
+
const isGrid = this.isGrid();
|
|
6259
|
+
this.element.classList.toggle("wb-grid", isGrid);
|
|
6260
|
+
if (!isGrid && this.isCellNav()) {
|
|
6261
|
+
this.setCellNav(false);
|
|
6262
|
+
}
|
|
6070
6263
|
}
|
|
6071
|
-
if (
|
|
6264
|
+
if (pending.size > 0) {
|
|
6072
6265
|
if (immediate) {
|
|
6073
6266
|
this._updateViewportImmediately();
|
|
6074
6267
|
}
|
|
@@ -6140,7 +6333,7 @@ class Wunderbaum {
|
|
|
6140
6333
|
}
|
|
6141
6334
|
break;
|
|
6142
6335
|
default:
|
|
6143
|
-
error(`Invalid mode '${mode}'
|
|
6336
|
+
error(`Invalid mode '${mode}'.`);
|
|
6144
6337
|
}
|
|
6145
6338
|
}
|
|
6146
6339
|
/** Display tree status (ok, loading, error, noData) using styles and a dummy root node. */
|
|
@@ -6163,14 +6356,24 @@ class Wunderbaum {
|
|
|
6163
6356
|
}
|
|
6164
6357
|
}
|
|
6165
6358
|
}
|
|
6166
|
-
/**
|
|
6359
|
+
/**
|
|
6360
|
+
* Sort nodes list by title or custom criteria.
|
|
6361
|
+
* @param {function} cmp custom compare function(a, b) that returns -1, 0, or 1
|
|
6362
|
+
* (defaults to sorting by title).
|
|
6363
|
+
* @param {boolean} deep pass true to sort all descendant nodes recursively
|
|
6364
|
+
*/
|
|
6365
|
+
sortChildren(cmp = nodeTitleSorter, deep = false) {
|
|
6366
|
+
this.root.sortChildren(cmp, deep);
|
|
6367
|
+
}
|
|
6368
|
+
/**
|
|
6369
|
+
* Update column headers and column width.
|
|
6167
6370
|
* Return true if at least one column width changed.
|
|
6168
6371
|
*/
|
|
6169
|
-
|
|
6170
|
-
|
|
6372
|
+
// _updateColumnWidths(options?: UpdateColumnsOptions): boolean {
|
|
6373
|
+
_updateColumnWidths() {
|
|
6374
|
+
// options = Object.assign({ updateRows: true, renderMarkup: false }, options);
|
|
6171
6375
|
const defaultMinWidth = 4;
|
|
6172
6376
|
const vpWidth = this.element.clientWidth;
|
|
6173
|
-
const isGrid = this.isGrid();
|
|
6174
6377
|
// Shorten last column width to avoid h-scrollbar
|
|
6175
6378
|
const FIX_ADJUST_LAST_COL = 2;
|
|
6176
6379
|
const columns = this.columns;
|
|
@@ -6179,73 +6382,70 @@ class Wunderbaum {
|
|
|
6179
6382
|
let totalWeight = 0;
|
|
6180
6383
|
let fixedWidth = 0;
|
|
6181
6384
|
let modified = false;
|
|
6182
|
-
this.element.classList.toggle("wb-grid", isGrid);
|
|
6183
|
-
if (!isGrid && this.isCellNav()) {
|
|
6184
|
-
|
|
6385
|
+
// this.element.classList.toggle("wb-grid", isGrid);
|
|
6386
|
+
// if (!isGrid && this.isCellNav()) {
|
|
6387
|
+
// this.setCellNav(false);
|
|
6388
|
+
// }
|
|
6389
|
+
// if (options.calculateCols) {
|
|
6390
|
+
if (col0.id !== "*") {
|
|
6391
|
+
throw new Error(`First column must have id '*': got '${col0.id}'.`);
|
|
6185
6392
|
}
|
|
6186
|
-
|
|
6187
|
-
|
|
6188
|
-
|
|
6189
|
-
|
|
6190
|
-
|
|
6191
|
-
|
|
6192
|
-
|
|
6193
|
-
|
|
6194
|
-
|
|
6195
|
-
|
|
6196
|
-
|
|
6197
|
-
|
|
6198
|
-
|
|
6199
|
-
|
|
6200
|
-
|
|
6393
|
+
// Gather width definitions
|
|
6394
|
+
this._columnsById = {};
|
|
6395
|
+
for (let col of columns) {
|
|
6396
|
+
this._columnsById[col.id] = col;
|
|
6397
|
+
let cw = col.width;
|
|
6398
|
+
if (col.id === "*" && col !== col0) {
|
|
6399
|
+
throw new Error(`Column id '*' must be defined only once: '${col.title}'.`);
|
|
6400
|
+
}
|
|
6401
|
+
if (!cw || cw === "*") {
|
|
6402
|
+
col._weight = 1.0;
|
|
6403
|
+
totalWeight += 1.0;
|
|
6404
|
+
}
|
|
6405
|
+
else if (typeof cw === "number") {
|
|
6406
|
+
col._weight = cw;
|
|
6407
|
+
totalWeight += cw;
|
|
6408
|
+
}
|
|
6409
|
+
else if (typeof cw === "string" && cw.endsWith("px")) {
|
|
6410
|
+
col._weight = 0;
|
|
6411
|
+
let px = parseFloat(cw.slice(0, -2));
|
|
6412
|
+
if (col._widthPx != px) {
|
|
6413
|
+
modified = true;
|
|
6414
|
+
col._widthPx = px;
|
|
6201
6415
|
}
|
|
6202
|
-
|
|
6203
|
-
|
|
6204
|
-
|
|
6416
|
+
fixedWidth += px;
|
|
6417
|
+
}
|
|
6418
|
+
else {
|
|
6419
|
+
error(`Invalid column width: ${cw} (expected string ending with 'px' or number, e.g. "<num>px" or <int>).`);
|
|
6420
|
+
}
|
|
6421
|
+
}
|
|
6422
|
+
// Share remaining space between non-fixed columns
|
|
6423
|
+
const restPx = Math.max(0, vpWidth - fixedWidth);
|
|
6424
|
+
let ofsPx = 0;
|
|
6425
|
+
for (let col of columns) {
|
|
6426
|
+
let minWidth;
|
|
6427
|
+
if (col._weight) {
|
|
6428
|
+
const cmw = col.minWidth;
|
|
6429
|
+
if (typeof cmw === "number") {
|
|
6430
|
+
minWidth = cmw;
|
|
6205
6431
|
}
|
|
6206
|
-
else if (typeof
|
|
6207
|
-
|
|
6208
|
-
let px = parseFloat(cw.slice(0, -2));
|
|
6209
|
-
if (col._widthPx != px) {
|
|
6210
|
-
modified = true;
|
|
6211
|
-
col._widthPx = px;
|
|
6212
|
-
}
|
|
6213
|
-
fixedWidth += px;
|
|
6432
|
+
else if (typeof cmw === "string" && cmw.endsWith("px")) {
|
|
6433
|
+
minWidth = parseFloat(cmw.slice(0, -2));
|
|
6214
6434
|
}
|
|
6215
6435
|
else {
|
|
6216
|
-
|
|
6436
|
+
minWidth = defaultMinWidth;
|
|
6217
6437
|
}
|
|
6218
|
-
|
|
6219
|
-
|
|
6220
|
-
|
|
6221
|
-
|
|
6222
|
-
for (let col of columns) {
|
|
6223
|
-
let minWidth;
|
|
6224
|
-
if (col._weight) {
|
|
6225
|
-
const cmw = col.minWidth;
|
|
6226
|
-
if (typeof cmw === "number") {
|
|
6227
|
-
minWidth = cmw;
|
|
6228
|
-
}
|
|
6229
|
-
else if (typeof cmw === "string" && cmw.endsWith("px")) {
|
|
6230
|
-
minWidth = parseFloat(cmw.slice(0, -2));
|
|
6231
|
-
}
|
|
6232
|
-
else {
|
|
6233
|
-
minWidth = defaultMinWidth;
|
|
6234
|
-
}
|
|
6235
|
-
const px = Math.max(minWidth, (restPx * col._weight) / totalWeight);
|
|
6236
|
-
if (col._widthPx != px) {
|
|
6237
|
-
modified = true;
|
|
6238
|
-
col._widthPx = px;
|
|
6239
|
-
}
|
|
6438
|
+
const px = Math.max(minWidth, (restPx * col._weight) / totalWeight);
|
|
6439
|
+
if (col._widthPx != px) {
|
|
6440
|
+
modified = true;
|
|
6441
|
+
col._widthPx = px;
|
|
6240
6442
|
}
|
|
6241
|
-
col._ofsPx = ofsPx;
|
|
6242
|
-
ofsPx += col._widthPx;
|
|
6243
6443
|
}
|
|
6244
|
-
|
|
6245
|
-
|
|
6444
|
+
col._ofsPx = ofsPx;
|
|
6445
|
+
ofsPx += col._widthPx;
|
|
6246
6446
|
}
|
|
6247
|
-
|
|
6248
|
-
|
|
6447
|
+
columns[columns.length - 1]._widthPx -= FIX_ADJUST_LAST_COL;
|
|
6448
|
+
totalWidth = ofsPx - FIX_ADJUST_LAST_COL;
|
|
6249
6449
|
const tw = `${totalWidth}px`;
|
|
6250
6450
|
this.headerElement.style.width = tw;
|
|
6251
6451
|
this.listContainerElement.style.width = tw;
|
|
@@ -6254,12 +6454,14 @@ class Wunderbaum {
|
|
|
6254
6454
|
// this.logInfo("UC", this.columns, vpWidth, this.element.clientWidth, this.element);
|
|
6255
6455
|
// console.trace();
|
|
6256
6456
|
// util.error("BREAK");
|
|
6257
|
-
if (modified) {
|
|
6258
|
-
|
|
6259
|
-
|
|
6260
|
-
|
|
6261
|
-
|
|
6262
|
-
|
|
6457
|
+
// if (modified) {
|
|
6458
|
+
// this._renderHeaderMarkup();
|
|
6459
|
+
// if (options.renderMarkup) {
|
|
6460
|
+
// this.setModified(ChangeType.header, { removeMarkup: true });
|
|
6461
|
+
// } else if (options.updateRows) {
|
|
6462
|
+
// this._updateRows();
|
|
6463
|
+
// }
|
|
6464
|
+
// }
|
|
6263
6465
|
return modified;
|
|
6264
6466
|
}
|
|
6265
6467
|
/** Create/update header markup from `this.columns` definition.
|
|
@@ -6308,7 +6510,7 @@ class Wunderbaum {
|
|
|
6308
6510
|
* pending async changes if any.
|
|
6309
6511
|
*/
|
|
6310
6512
|
updatePendingModifications() {
|
|
6311
|
-
if (this.
|
|
6513
|
+
if (this.pendingChangeTypes.size > 0) {
|
|
6312
6514
|
this._updateViewportImmediately();
|
|
6313
6515
|
}
|
|
6314
6516
|
}
|
|
@@ -6323,31 +6525,50 @@ class Wunderbaum {
|
|
|
6323
6525
|
*/
|
|
6324
6526
|
_updateViewportImmediately() {
|
|
6325
6527
|
var _a;
|
|
6326
|
-
// Shorten container height to avoid v-scrollbar
|
|
6327
|
-
const FIX_ADJUST_HEIGHT = 1;
|
|
6328
6528
|
if (this._disableUpdateCount) {
|
|
6329
|
-
this.log(`
|
|
6529
|
+
this.log(`_updateViewportImmediately() IGNORED (disable level: ${this._disableUpdateCount})`);
|
|
6530
|
+
this._disableUpdateIgnoreCount++;
|
|
6330
6531
|
return;
|
|
6331
6532
|
}
|
|
6332
|
-
|
|
6333
|
-
|
|
6334
|
-
|
|
6335
|
-
|
|
6336
|
-
|
|
6337
|
-
|
|
6338
|
-
|
|
6339
|
-
|
|
6340
|
-
|
|
6341
|
-
|
|
6342
|
-
|
|
6343
|
-
|
|
6344
|
-
this.listContainerElement.
|
|
6345
|
-
height
|
|
6346
|
-
|
|
6347
|
-
|
|
6348
|
-
|
|
6349
|
-
|
|
6350
|
-
|
|
6533
|
+
// Shorten container height to avoid v-scrollbar
|
|
6534
|
+
const FIX_ADJUST_HEIGHT = 1;
|
|
6535
|
+
const RF = RenderFlag;
|
|
6536
|
+
const pending = new Set(this.pendingChangeTypes);
|
|
6537
|
+
this.pendingChangeTypes.clear();
|
|
6538
|
+
const scrollOnly = pending.has(RF.scroll) && pending.size === 1;
|
|
6539
|
+
if (scrollOnly) {
|
|
6540
|
+
this._updateRows({ newNodesOnly: true });
|
|
6541
|
+
// this.log("_updateViewportImmediately(): scroll only.");
|
|
6542
|
+
}
|
|
6543
|
+
else {
|
|
6544
|
+
this.log("_updateViewportImmediately():", pending);
|
|
6545
|
+
let height = this.listContainerElement.clientHeight;
|
|
6546
|
+
// We cannot get the height for absolute positioned parent, so look at first col
|
|
6547
|
+
// let headerHeight = this.headerElement.clientHeight
|
|
6548
|
+
// let headerHeight = this.headerElement.children[0].children[0].clientHeight;
|
|
6549
|
+
// const headerHeight = this.options.headerHeightPx;
|
|
6550
|
+
const headerHeight = this.headerElement.clientHeight; // May be 0
|
|
6551
|
+
const wantHeight = this.element.clientHeight - headerHeight - FIX_ADJUST_HEIGHT;
|
|
6552
|
+
if (Math.abs(height - wantHeight) > 1.0) {
|
|
6553
|
+
// this.log("resize", height, wantHeight);
|
|
6554
|
+
this.listContainerElement.style.height = wantHeight + "px";
|
|
6555
|
+
height = wantHeight;
|
|
6556
|
+
}
|
|
6557
|
+
// console.profile(`_updateViewportImmediately()`)
|
|
6558
|
+
if (pending.has(RF.clearMarkup)) {
|
|
6559
|
+
this.visit((n) => {
|
|
6560
|
+
n.removeMarkup();
|
|
6561
|
+
});
|
|
6562
|
+
}
|
|
6563
|
+
// let widthModified = false;
|
|
6564
|
+
if (pending.has(RF.header)) {
|
|
6565
|
+
// widthModified = this._updateColumnWidths();
|
|
6566
|
+
this._updateColumnWidths();
|
|
6567
|
+
this._renderHeaderMarkup();
|
|
6568
|
+
}
|
|
6569
|
+
this._updateRows();
|
|
6570
|
+
// console.profileEnd(`_updateViewportImmediately()`)
|
|
6571
|
+
}
|
|
6351
6572
|
if (this.options.connectTopBreadcrumb) {
|
|
6352
6573
|
let path = (_a = this.getTopmostVpNode(true)) === null || _a === void 0 ? void 0 : _a.getPath(false, "title", " > ");
|
|
6353
6574
|
path = path ? path + " >" : "";
|
|
@@ -6488,19 +6709,14 @@ class Wunderbaum {
|
|
|
6488
6709
|
return this.root.visit(callback, false);
|
|
6489
6710
|
}
|
|
6490
6711
|
/**
|
|
6491
|
-
* Call
|
|
6712
|
+
* Call callback(node) for all nodes in vertical order, top down (or bottom up).
|
|
6492
6713
|
*
|
|
6493
|
-
* Note that this considers expansion state, i.e.
|
|
6494
|
-
* are skipped.
|
|
6714
|
+
* Note that this considers expansion state, i.e. filtered nodes and children
|
|
6715
|
+
* of collapsed nodes are skipped, unless `includeHidden` is set.
|
|
6495
6716
|
*
|
|
6496
|
-
* Stop iteration
|
|
6717
|
+
* Stop iteration if callback() returns false.<br>
|
|
6497
6718
|
* Return false if iteration was stopped.
|
|
6498
6719
|
*
|
|
6499
|
-
* @param callback the callback function.
|
|
6500
|
-
* Return false to stop iteration, return "skip" to skip this node and children only.
|
|
6501
|
-
* @param [options]
|
|
6502
|
-
* Defaults:
|
|
6503
|
-
* {start: First tree node, reverse: false, includeSelf: true, includeHidden: false, wrap: false}
|
|
6504
6720
|
* @returns {boolean} false if iteration was canceled
|
|
6505
6721
|
*/
|
|
6506
6722
|
visitRows(callback, options) {
|
|
@@ -6518,7 +6734,7 @@ class Wunderbaum {
|
|
|
6518
6734
|
// visit siblings
|
|
6519
6735
|
siblings = parent.children;
|
|
6520
6736
|
nextIdx = siblings.indexOf(node) + siblingOfs;
|
|
6521
|
-
assert(nextIdx >= 0,
|
|
6737
|
+
assert(nextIdx >= 0, `Could not find ${node} in parent's children: ${parent}`);
|
|
6522
6738
|
for (i = nextIdx; i < siblings.length; i++) {
|
|
6523
6739
|
node = siblings[i];
|
|
6524
6740
|
if (node === stopNode) {
|
|
@@ -6656,8 +6872,8 @@ class Wunderbaum {
|
|
|
6656
6872
|
// `enableUpdate(${flag}): count -> ${this._disableUpdateCount}...`
|
|
6657
6873
|
// );
|
|
6658
6874
|
if (this._disableUpdateCount === 0) {
|
|
6659
|
-
|
|
6660
|
-
|
|
6875
|
+
this.logDebug(`enableUpdate(): active again. Re-painting to catch up with ${this._disableUpdateIgnoreCount} ignored update requests...`);
|
|
6876
|
+
this._disableUpdateIgnoreCount = 0;
|
|
6661
6877
|
this.setModified(ChangeType.any, { immediate: true });
|
|
6662
6878
|
}
|
|
6663
6879
|
}
|
|
@@ -6712,7 +6928,7 @@ class Wunderbaum {
|
|
|
6712
6928
|
}
|
|
6713
6929
|
Wunderbaum.sequence = 0;
|
|
6714
6930
|
/** Wunderbaum release version number "MAJOR.MINOR.PATCH". */
|
|
6715
|
-
Wunderbaum.version = "v0.
|
|
6931
|
+
Wunderbaum.version = "v0.3.0"; // Set to semver by 'grunt release'
|
|
6716
6932
|
/** Expose some useful methods of the util.ts module as `Wunderbaum.util`. */
|
|
6717
6933
|
Wunderbaum.util = util;
|
|
6718
6934
|
|