wunderbaum 0.10.1 → 0.11.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -7
- package/dist/wunderbaum.css +18 -10
- package/dist/wunderbaum.css.map +1 -1
- package/dist/wunderbaum.d.ts +204 -49
- package/dist/wunderbaum.esm.js +826 -606
- package/dist/wunderbaum.esm.min.js +26 -26
- package/dist/wunderbaum.esm.min.js.map +1 -1
- package/dist/wunderbaum.umd.js +826 -606
- package/dist/wunderbaum.umd.min.js +32 -32
- package/dist/wunderbaum.umd.min.js.map +1 -1
- package/package.json +11 -10
- package/src/common.ts +17 -2
- package/src/types.ts +126 -30
- package/src/util.ts +18 -8
- package/src/wb_ext_dnd.ts +9 -4
- package/src/wb_ext_edit.ts +4 -0
- package/src/wb_ext_grid.ts +1 -1
- package/src/wb_node.ts +164 -17
- package/src/wb_options.ts +26 -2
- package/src/wunderbaum.scss +10 -0
- package/src/wunderbaum.ts +124 -47
package/dist/wunderbaum.umd.js
CHANGED
|
@@ -294,7 +294,7 @@
|
|
|
294
294
|
/*!
|
|
295
295
|
* Wunderbaum - util
|
|
296
296
|
* Copyright (c) 2021-2024, Martin Wendt. Released under the MIT license.
|
|
297
|
-
* v0.
|
|
297
|
+
* v0.11.1, Fri, 27 Dec 2024 22:58:06 GMT (https://github.com/mar10/wunderbaum)
|
|
298
298
|
*/
|
|
299
299
|
/** @module util */
|
|
300
300
|
/** Readable names for `MouseEvent.button` */
|
|
@@ -926,6 +926,11 @@
|
|
|
926
926
|
// Use value from value options dict, fallback do default
|
|
927
927
|
return value !== null && value !== void 0 ? value : defaultValue;
|
|
928
928
|
}
|
|
929
|
+
/** Return the next value from a list of values (rotating). @since 0.11 */
|
|
930
|
+
function rotate(value, values) {
|
|
931
|
+
const idx = values.indexOf(value);
|
|
932
|
+
return values[(idx + 1) % values.length];
|
|
933
|
+
}
|
|
929
934
|
/** Convert an Array or space-separated string to a Set. */
|
|
930
935
|
function toSet(val) {
|
|
931
936
|
if (val instanceof Set) {
|
|
@@ -954,12 +959,7 @@
|
|
|
954
959
|
* const width = util.toPixel(x, y, 100); // returns 123
|
|
955
960
|
* ```
|
|
956
961
|
*/
|
|
957
|
-
function toPixel(
|
|
958
|
-
// val: string | number | undefined | null,
|
|
959
|
-
...defaults) {
|
|
960
|
-
// if (typeof val === "number") {
|
|
961
|
-
// return val;
|
|
962
|
-
// }
|
|
962
|
+
function toPixel(...defaults) {
|
|
963
963
|
for (const d of defaults) {
|
|
964
964
|
if (typeof d === "number") {
|
|
965
965
|
return d;
|
|
@@ -978,12 +978,7 @@
|
|
|
978
978
|
* const value = util.toBool(opts.foo, opts.flag, false); // returns true
|
|
979
979
|
* ```
|
|
980
980
|
*/
|
|
981
|
-
function toBool(
|
|
982
|
-
// val: boolean | undefined | null,
|
|
983
|
-
...boolDefaults) {
|
|
984
|
-
// if (val != null) {
|
|
985
|
-
// return !!val;
|
|
986
|
-
// }
|
|
981
|
+
function toBool(...boolDefaults) {
|
|
987
982
|
for (const d of boolDefaults) {
|
|
988
983
|
if (d != null) {
|
|
989
984
|
return !!d;
|
|
@@ -991,6 +986,15 @@
|
|
|
991
986
|
}
|
|
992
987
|
throw new Error("No default boolean value provided");
|
|
993
988
|
}
|
|
989
|
+
/**
|
|
990
|
+
* Return `val` unless `val` is a number in which case we convert to boolean.
|
|
991
|
+
* This is useful when a boolean value is stored as a 0/1 (e.g. in JSON) and
|
|
992
|
+
* we still want to maintain string values. null and undefined are returned as
|
|
993
|
+
* is. E.g. `checkbox` may be boolean or 'radio'.
|
|
994
|
+
*/
|
|
995
|
+
function intToBool(val) {
|
|
996
|
+
return typeof val === "number" ? !!val : val;
|
|
997
|
+
}
|
|
994
998
|
// /** Check if a string is contained in an Array or Set. */
|
|
995
999
|
// export function isAnyOf(s: string, items: Array<string>|Set<string>): boolean {
|
|
996
1000
|
// return Array.prototype.includes.call(items, s)
|
|
@@ -1119,6 +1123,7 @@
|
|
|
1119
1123
|
extractHtmlText: extractHtmlText,
|
|
1120
1124
|
getOption: getOption,
|
|
1121
1125
|
getValueFromElem: getValueFromElem,
|
|
1126
|
+
intToBool: intToBool,
|
|
1122
1127
|
isArray: isArray,
|
|
1123
1128
|
isEmptyObject: isEmptyObject,
|
|
1124
1129
|
isFunction: isFunction,
|
|
@@ -1127,6 +1132,7 @@
|
|
|
1127
1132
|
noop: noop,
|
|
1128
1133
|
onEvent: onEvent,
|
|
1129
1134
|
overrideMethod: overrideMethod,
|
|
1135
|
+
rotate: rotate,
|
|
1130
1136
|
setElemDisplay: setElemDisplay,
|
|
1131
1137
|
setTimeoutPromise: setTimeoutPromise,
|
|
1132
1138
|
setValueToElem: setValueToElem,
|
|
@@ -1142,10 +1148,10 @@
|
|
|
1142
1148
|
/*!
|
|
1143
1149
|
* Wunderbaum - types
|
|
1144
1150
|
* Copyright (c) 2021-2024, Martin Wendt. Released under the MIT license.
|
|
1145
|
-
* v0.
|
|
1151
|
+
* v0.11.1, Fri, 27 Dec 2024 22:58:06 GMT (https://github.com/mar10/wunderbaum)
|
|
1146
1152
|
*/
|
|
1147
1153
|
/**
|
|
1148
|
-
* Possible values for {@link WunderbaumNode.update
|
|
1154
|
+
* Possible values for {@link WunderbaumNode.update} and {@link Wunderbaum.update}.
|
|
1149
1155
|
*/
|
|
1150
1156
|
var ChangeType;
|
|
1151
1157
|
(function (ChangeType) {
|
|
@@ -1174,7 +1180,7 @@
|
|
|
1174
1180
|
RenderFlag["redraw"] = "redraw";
|
|
1175
1181
|
RenderFlag["scroll"] = "scroll";
|
|
1176
1182
|
})(RenderFlag || (RenderFlag = {}));
|
|
1177
|
-
/** Possible values for {@link WunderbaumNode.setStatus
|
|
1183
|
+
/** Possible values for {@link WunderbaumNode.setStatus}. */
|
|
1178
1184
|
var NodeStatusType;
|
|
1179
1185
|
(function (NodeStatusType) {
|
|
1180
1186
|
NodeStatusType["ok"] = "ok";
|
|
@@ -1206,7 +1212,7 @@
|
|
|
1206
1212
|
/*!
|
|
1207
1213
|
* Wunderbaum - wb_extension_base
|
|
1208
1214
|
* Copyright (c) 2021-2024, Martin Wendt. Released under the MIT license.
|
|
1209
|
-
* v0.
|
|
1215
|
+
* v0.11.1, Fri, 27 Dec 2024 22:58:06 GMT (https://github.com/mar10/wunderbaum)
|
|
1210
1216
|
*/
|
|
1211
1217
|
class WunderbaumExtension {
|
|
1212
1218
|
constructor(tree, id, defaults) {
|
|
@@ -1265,7 +1271,7 @@
|
|
|
1265
1271
|
/*!
|
|
1266
1272
|
* Wunderbaum - ext-filter
|
|
1267
1273
|
* Copyright (c) 2021-2024, Martin Wendt. Released under the MIT license.
|
|
1268
|
-
* v0.
|
|
1274
|
+
* v0.11.1, Fri, 27 Dec 2024 22:58:06 GMT (https://github.com/mar10/wunderbaum)
|
|
1269
1275
|
*/
|
|
1270
1276
|
const START_MARKER = "\uFFF7";
|
|
1271
1277
|
const END_MARKER = "\uFFF8";
|
|
@@ -1590,7 +1596,7 @@
|
|
|
1590
1596
|
/*!
|
|
1591
1597
|
* Wunderbaum - ext-keynav
|
|
1592
1598
|
* Copyright (c) 2021-2024, Martin Wendt. Released under the MIT license.
|
|
1593
|
-
* v0.
|
|
1599
|
+
* v0.11.1, Fri, 27 Dec 2024 22:58:06 GMT (https://github.com/mar10/wunderbaum)
|
|
1594
1600
|
*/
|
|
1595
1601
|
const QUICKSEARCH_DELAY = 500;
|
|
1596
1602
|
class KeynavExtension extends WunderbaumExtension {
|
|
@@ -1954,7 +1960,7 @@
|
|
|
1954
1960
|
/*!
|
|
1955
1961
|
* Wunderbaum - ext-logger
|
|
1956
1962
|
* Copyright (c) 2021-2024, Martin Wendt. Released under the MIT license.
|
|
1957
|
-
* v0.
|
|
1963
|
+
* v0.11.1, Fri, 27 Dec 2024 22:58:06 GMT (https://github.com/mar10/wunderbaum)
|
|
1958
1964
|
*/
|
|
1959
1965
|
class LoggerExtension extends WunderbaumExtension {
|
|
1960
1966
|
constructor(tree) {
|
|
@@ -1994,409 +2000,84 @@
|
|
|
1994
2000
|
}
|
|
1995
2001
|
|
|
1996
2002
|
/*!
|
|
1997
|
-
* Wunderbaum -
|
|
2003
|
+
* Wunderbaum - ext-dnd
|
|
1998
2004
|
* Copyright (c) 2021-2024, Martin Wendt. Released under the MIT license.
|
|
1999
|
-
* v0.
|
|
2000
|
-
*/
|
|
2001
|
-
const DEFAULT_DEBUGLEVEL = 3; // Replaced by rollup script
|
|
2002
|
-
/**
|
|
2003
|
-
* Fixed height of a row in pixel. Must match the SCSS variable `$row-outer-height`.
|
|
2004
|
-
*/
|
|
2005
|
-
const ROW_HEIGHT = 22;
|
|
2006
|
-
/**
|
|
2007
|
-
* Fixed width of node icons in pixel. Must match the SCSS variable `$icon-outer-width`.
|
|
2008
|
-
*/
|
|
2009
|
-
const ICON_WIDTH = 20;
|
|
2010
|
-
/**
|
|
2011
|
-
* Adjust the width of the title span, so overflow ellipsis work.
|
|
2012
|
-
* (2 x `$col-padding-x` + 3px rounding errors).
|
|
2013
|
-
*/
|
|
2014
|
-
const TITLE_SPAN_PAD_Y = 7;
|
|
2015
|
-
/** Render row markup for N nodes above and below the visible viewport. */
|
|
2016
|
-
const RENDER_MAX_PREFETCH = 5;
|
|
2017
|
-
/** Minimum column width if not set otherwise. */
|
|
2018
|
-
const DEFAULT_MIN_COL_WIDTH = 4;
|
|
2019
|
-
/** Regular expression to detect if a string describes an image URL (in contrast
|
|
2020
|
-
* to a class name). Strings are considered image urls if they contain '.' or '/'.
|
|
2021
|
-
*/
|
|
2022
|
-
const TEST_IMG = new RegExp(/\.|\//);
|
|
2023
|
-
// export const RECURSIVE_REQUEST_ERROR = "$recursive_request";
|
|
2024
|
-
// export const INVALID_REQUEST_TARGET_ERROR = "$request_target_invalid";
|
|
2025
|
-
/**
|
|
2026
|
-
* Default node icons.
|
|
2027
|
-
* Requires bootstrap icons https://icons.getbootstrap.com
|
|
2028
|
-
*/
|
|
2029
|
-
const iconMaps = {
|
|
2030
|
-
bootstrap: {
|
|
2031
|
-
error: "bi bi-exclamation-triangle",
|
|
2032
|
-
// loading: "bi bi-hourglass-split wb-busy",
|
|
2033
|
-
loading: "bi bi-chevron-right wb-busy",
|
|
2034
|
-
// loading: "bi bi-arrow-repeat wb-spin",
|
|
2035
|
-
// loading: '<div class="spinner-border spinner-border-sm" role="status"> <span class="visually-hidden">Loading...</span> </div>',
|
|
2036
|
-
// noData: "bi bi-search",
|
|
2037
|
-
noData: "bi bi-question-circle",
|
|
2038
|
-
expanderExpanded: "bi bi-chevron-down",
|
|
2039
|
-
// expanderExpanded: "bi bi-dash-square",
|
|
2040
|
-
expanderCollapsed: "bi bi-chevron-right",
|
|
2041
|
-
// expanderCollapsed: "bi bi-plus-square",
|
|
2042
|
-
expanderLazy: "bi bi-chevron-right wb-helper-lazy-expander",
|
|
2043
|
-
// expanderLazy: "bi bi-chevron-bar-right",
|
|
2044
|
-
checkChecked: "bi bi-check-square",
|
|
2045
|
-
checkUnchecked: "bi bi-square",
|
|
2046
|
-
checkUnknown: "bi bi-dash-square-dotted",
|
|
2047
|
-
radioChecked: "bi bi-circle-fill",
|
|
2048
|
-
radioUnchecked: "bi bi-circle",
|
|
2049
|
-
radioUnknown: "bi bi-record-circle",
|
|
2050
|
-
folder: "bi bi-folder2",
|
|
2051
|
-
folderOpen: "bi bi-folder2-open",
|
|
2052
|
-
folderLazy: "bi bi-folder-symlink",
|
|
2053
|
-
doc: "bi bi-file-earmark",
|
|
2054
|
-
},
|
|
2055
|
-
fontawesome6: {
|
|
2056
|
-
error: "fa-solid fa-triangle-exclamation",
|
|
2057
|
-
loading: "fa-solid fa-chevron-right fa-beat",
|
|
2058
|
-
noData: "fa-solid fa-circle-question",
|
|
2059
|
-
expanderExpanded: "fa-solid fa-chevron-down",
|
|
2060
|
-
expanderCollapsed: "fa-solid fa-chevron-right",
|
|
2061
|
-
expanderLazy: "fa-solid fa-chevron-right wb-helper-lazy-expander",
|
|
2062
|
-
checkChecked: "fa-regular fa-square-check",
|
|
2063
|
-
checkUnchecked: "fa-regular fa-square",
|
|
2064
|
-
checkUnknown: "fa-regular fa-square-minus",
|
|
2065
|
-
radioChecked: "fa-solid fa-circle",
|
|
2066
|
-
radioUnchecked: "fa-regular fa-circle",
|
|
2067
|
-
radioUnknown: "fa-regular fa-circle-question",
|
|
2068
|
-
folder: "fa-solid fa-folder-closed",
|
|
2069
|
-
folderOpen: "fa-regular fa-folder-open",
|
|
2070
|
-
folderLazy: "fa-solid fa-folder-plus",
|
|
2071
|
-
doc: "fa-regular fa-file",
|
|
2072
|
-
},
|
|
2073
|
-
};
|
|
2074
|
-
/** Dict keys that are evaluated by source loader (others are added to `tree.data` instead). */
|
|
2075
|
-
const RESERVED_TREE_SOURCE_KEYS = new Set([
|
|
2076
|
-
"_format", // reserved for future use
|
|
2077
|
-
"_keyMap", // Used for compressed data format
|
|
2078
|
-
"_positional", // Used for compressed data format
|
|
2079
|
-
"_typeList", // Used for compressed data format @deprecated
|
|
2080
|
-
"_valueMap", // Used for compressed data format
|
|
2081
|
-
"_version", // reserved for future use
|
|
2082
|
-
"children",
|
|
2083
|
-
"columns",
|
|
2084
|
-
"types",
|
|
2085
|
-
]);
|
|
2086
|
-
// /** Key codes that trigger grid navigation, even when inside an input element. */
|
|
2087
|
-
// export const INPUT_BREAKOUT_KEYS: Set<string> = new Set([
|
|
2088
|
-
// // "ArrowDown",
|
|
2089
|
-
// // "ArrowUp",
|
|
2090
|
-
// "Enter",
|
|
2091
|
-
// "Escape",
|
|
2092
|
-
// ]);
|
|
2093
|
-
/** Map `KeyEvent.key` to navigation action. */
|
|
2094
|
-
const KEY_TO_ACTION_DICT = {
|
|
2095
|
-
" ": "toggleSelect",
|
|
2096
|
-
"+": "expand",
|
|
2097
|
-
Add: "expand",
|
|
2098
|
-
ArrowDown: "down",
|
|
2099
|
-
ArrowLeft: "left",
|
|
2100
|
-
ArrowRight: "right",
|
|
2101
|
-
ArrowUp: "up",
|
|
2102
|
-
Backspace: "parent",
|
|
2103
|
-
"/": "collapseAll",
|
|
2104
|
-
Divide: "collapseAll",
|
|
2105
|
-
End: "lastCol",
|
|
2106
|
-
Home: "firstCol",
|
|
2107
|
-
"Control+End": "last",
|
|
2108
|
-
"Control+Home": "first",
|
|
2109
|
-
"Meta+ArrowDown": "last", // macOs
|
|
2110
|
-
"Meta+ArrowUp": "first", // macOs
|
|
2111
|
-
"*": "expandAll",
|
|
2112
|
-
Multiply: "expandAll",
|
|
2113
|
-
PageDown: "pageDown",
|
|
2114
|
-
PageUp: "pageUp",
|
|
2115
|
-
"-": "collapse",
|
|
2116
|
-
Subtract: "collapse",
|
|
2117
|
-
};
|
|
2118
|
-
/** Return a callback that returns true if the node title matches the string
|
|
2119
|
-
* or regular expression.
|
|
2120
|
-
* @see {@link WunderbaumNode.findAll()}
|
|
2121
|
-
*/
|
|
2122
|
-
function makeNodeTitleMatcher(match) {
|
|
2123
|
-
if (match instanceof RegExp) {
|
|
2124
|
-
return function (node) {
|
|
2125
|
-
return match.test(node.title);
|
|
2126
|
-
};
|
|
2127
|
-
}
|
|
2128
|
-
assert(typeof match === "string", `Expected a string or RegExp: ${match}`);
|
|
2129
|
-
// s = escapeRegex(s.toLowerCase());
|
|
2130
|
-
return function (node) {
|
|
2131
|
-
return node.title === match;
|
|
2132
|
-
// console.log("match " + node, node.title.toLowerCase().indexOf(match))
|
|
2133
|
-
// return node.title.toLowerCase().indexOf(match) >= 0;
|
|
2134
|
-
};
|
|
2135
|
-
}
|
|
2136
|
-
/** Return a callback that returns true if the node title starts with a string (case-insensitive). */
|
|
2137
|
-
function makeNodeTitleStartMatcher(s) {
|
|
2138
|
-
s = escapeRegex(s);
|
|
2139
|
-
const reMatch = new RegExp("^" + s, "i");
|
|
2140
|
-
return function (node) {
|
|
2141
|
-
return reMatch.test(node.title);
|
|
2142
|
-
};
|
|
2143
|
-
}
|
|
2144
|
-
/** Compare two nodes by title (case-insensitive). */
|
|
2145
|
-
function nodeTitleSorter(a, b) {
|
|
2146
|
-
const x = a.title.toLowerCase();
|
|
2147
|
-
const y = b.title.toLowerCase();
|
|
2148
|
-
return x === y ? 0 : x > y ? 1 : -1;
|
|
2149
|
-
}
|
|
2150
|
-
/**
|
|
2151
|
-
* Convert 'flat' to 'nested' format.
|
|
2152
|
-
*
|
|
2153
|
-
* Flat node entry format:
|
|
2154
|
-
* [PARENT_ID, [POSITIONAL_ARGS]]
|
|
2155
|
-
* or
|
|
2156
|
-
* [PARENT_ID, [POSITIONAL_ARGS], {KEY_VALUE_ARGS}]
|
|
2157
|
-
*
|
|
2158
|
-
* 1. Parent-referencing list is converted to a list of nested dicts with
|
|
2159
|
-
* optional `children` properties.
|
|
2160
|
-
* 2. `[POSITIONAL_ARGS]` are added as dict attributes.
|
|
2005
|
+
* v0.11.1, Fri, 27 Dec 2024 22:58:06 GMT (https://github.com/mar10/wunderbaum)
|
|
2161
2006
|
*/
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2007
|
+
const nodeMimeType = "application/x-wunderbaum-node";
|
|
2008
|
+
class DndExtension extends WunderbaumExtension {
|
|
2009
|
+
constructor(tree) {
|
|
2010
|
+
super(tree, "dnd", {
|
|
2011
|
+
autoExpandMS: 1500, // Expand nodes after n milliseconds of hovering
|
|
2012
|
+
// dropMarkerInsertOffsetX: -16, // Additional offset for drop-marker with hitMode = "before"/"after"
|
|
2013
|
+
// dropMarkerOffsetX: -24, // Absolute position offset for .fancytree-drop-marker relatively to ..fancytree-title (icon/img near a node accepting drop)
|
|
2014
|
+
// #1021 `document.body` is not available yet
|
|
2015
|
+
// dropMarkerParent: "body", // Root Container used for drop marker (could be a shadow root)
|
|
2016
|
+
multiSource: false, // true: Drag multiple (i.e. selected) nodes. Also a callback() is allowed
|
|
2017
|
+
effectAllowed: "all", // Restrict the possible cursor shapes and modifier operations (can also be set in the dragStart event)
|
|
2018
|
+
dropEffectDefault: "move", // Default dropEffect ('copy', 'link', or 'move') when no modifier is pressed (override in drag, dragOver).
|
|
2019
|
+
guessDropEffect: true, // Calculate from `effectAllowed` and modifier keys)
|
|
2020
|
+
preventForeignNodes: false, // Prevent dropping nodes from different Wunderbaum trees
|
|
2021
|
+
preventLazyParents: true, // Prevent dropping items on unloaded lazy Wunderbaum tree nodes
|
|
2022
|
+
preventNonNodes: false, // Prevent dropping items other than Wunderbaum tree nodes
|
|
2023
|
+
preventRecursion: true, // Prevent dropping nodes on own descendants
|
|
2024
|
+
preventSameParent: false, // Prevent dropping nodes under same direct parent
|
|
2025
|
+
preventVoidMoves: true, // Prevent dropping nodes 'before self', etc. (move only)
|
|
2026
|
+
serializeClipboardData: true, // Serialize node data to dataTransfer object
|
|
2027
|
+
scroll: true, // Enable auto-scrolling while dragging
|
|
2028
|
+
scrollSensitivity: 20, // Active top/bottom margin in pixel
|
|
2029
|
+
// scrollnterval: 50, // Generate event every 50 ms
|
|
2030
|
+
scrollSpeed: 5, // Scroll pixel per 50 ms
|
|
2031
|
+
// setTextTypeJson: false, // Allow dragging of nodes to different IE windows
|
|
2032
|
+
sourceCopyHook: null, // Optional callback passed to `toDict` on dragStart @since 2.38
|
|
2033
|
+
// Events (drag support)
|
|
2034
|
+
dragStart: null, // Callback(sourceNode, data), return true, to enable dnd drag
|
|
2035
|
+
drag: null, // Callback(sourceNode, data)
|
|
2036
|
+
dragEnd: null, // Callback(sourceNode, data)
|
|
2037
|
+
// Events (drop support)
|
|
2038
|
+
dragEnter: null, // Callback(targetNode, data), return true, to enable dnd drop
|
|
2039
|
+
dragOver: null, // Callback(targetNode, data)
|
|
2040
|
+
dragExpand: null, // Callback(targetNode, data), return false to prevent autoExpand
|
|
2041
|
+
drop: null, // Callback(targetNode, data)
|
|
2042
|
+
dragLeave: null, // Callback(targetNode, data)
|
|
2043
|
+
});
|
|
2044
|
+
// public dropMarkerElem?: HTMLElement;
|
|
2045
|
+
this.srcNode = null;
|
|
2046
|
+
this.lastTargetNode = null;
|
|
2047
|
+
this.lastEnterStamp = 0;
|
|
2048
|
+
this.lastAllowedDropRegions = null;
|
|
2049
|
+
this.lastDropEffect = null;
|
|
2050
|
+
this.lastDropRegion = false;
|
|
2051
|
+
this.currentScrollDir = 0;
|
|
2052
|
+
this.applyScrollDirThrottled = throttle(this._applyScrollDir, 50);
|
|
2167
2053
|
}
|
|
2168
|
-
|
|
2169
|
-
|
|
2054
|
+
init() {
|
|
2055
|
+
super.init();
|
|
2056
|
+
// Store the current scroll parent, which may be the tree
|
|
2057
|
+
// container, any enclosing div, or the document.
|
|
2058
|
+
// #761: scrollParent() always needs a container child
|
|
2059
|
+
// $temp = $("<span>").appendTo(this.$container);
|
|
2060
|
+
// this.$scrollParent = $temp.scrollParent();
|
|
2061
|
+
// $temp.remove();
|
|
2062
|
+
const tree = this.tree;
|
|
2063
|
+
const dndOpts = tree.options.dnd;
|
|
2064
|
+
// Enable drag support if dragStart() is specified:
|
|
2065
|
+
if (dndOpts.dragStart) {
|
|
2066
|
+
onEvent(tree.element, "dragstart drag dragend", this.onDragEvent.bind(this));
|
|
2067
|
+
}
|
|
2068
|
+
// Enable drop support if dragEnter() is specified:
|
|
2069
|
+
if (dndOpts.dragEnter) {
|
|
2070
|
+
onEvent(tree.element, "dragenter dragover dragleave drop", this.onDropEvent.bind(this));
|
|
2071
|
+
}
|
|
2170
2072
|
}
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
//
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
longToShort[value] = key;
|
|
2180
|
-
}
|
|
2181
|
-
}
|
|
2182
|
-
const positionalShort = _positional.map((e) => longToShort[e]);
|
|
2183
|
-
const newChildren = [];
|
|
2184
|
-
const keyToNodeMap = {};
|
|
2185
|
-
const indexToNodeMap = {};
|
|
2186
|
-
const keyAttrName = (_a = longToShort["key"]) !== null && _a !== void 0 ? _a : "key";
|
|
2187
|
-
const childrenAttrName = (_b = longToShort["children"]) !== null && _b !== void 0 ? _b : "children";
|
|
2188
|
-
for (const [index, nodeTuple] of children.entries()) {
|
|
2189
|
-
// Node entry format:
|
|
2190
|
-
// [PARENT_ID, [POSITIONAL_ARGS]]
|
|
2191
|
-
// or
|
|
2192
|
-
// [PARENT_ID, [POSITIONAL_ARGS], {KEY_VALUE_ARGS}]
|
|
2193
|
-
const [parentId, args, kwargs = {}] = nodeTuple;
|
|
2194
|
-
// Free up some memory as we go
|
|
2195
|
-
nodeTuple[1] = null;
|
|
2196
|
-
if (nodeTuple[2] != null) {
|
|
2197
|
-
nodeTuple[2] = null;
|
|
2198
|
-
}
|
|
2199
|
-
// console.log("flatten", parentId, args, kwargs)
|
|
2200
|
-
// We keep `kwargs` as our new node definition. Then we add all positional
|
|
2201
|
-
// values to this object:
|
|
2202
|
-
args.forEach((val, positionalIdx) => {
|
|
2203
|
-
kwargs[positionalShort[positionalIdx]] = val;
|
|
2204
|
-
});
|
|
2205
|
-
// Find the parent node. `null` means 'toplevel'. PARENT_ID may be the numeric
|
|
2206
|
-
// index of the source.children list. If PARENT_ID is a string, we search
|
|
2207
|
-
// a parent with node.key of this value.
|
|
2208
|
-
indexToNodeMap[index] = kwargs;
|
|
2209
|
-
const key = kwargs[keyAttrName];
|
|
2210
|
-
if (key != null) {
|
|
2211
|
-
keyToNodeMap[key] = kwargs;
|
|
2212
|
-
}
|
|
2213
|
-
let parentNode = null;
|
|
2214
|
-
if (parentId === null) ;
|
|
2215
|
-
else if (typeof parentId === "number") {
|
|
2216
|
-
parentNode = indexToNodeMap[parentId];
|
|
2217
|
-
if (parentNode === undefined) {
|
|
2218
|
-
throw new Error(`unflattenSource: Could not find parent node by index: ${parentId}.`);
|
|
2219
|
-
}
|
|
2220
|
-
}
|
|
2221
|
-
else {
|
|
2222
|
-
parentNode = keyToNodeMap[parentId];
|
|
2223
|
-
if (parentNode === undefined) {
|
|
2224
|
-
throw new Error(`unflattenSource: Could not find parent node by key: ${parentId}`);
|
|
2225
|
-
}
|
|
2226
|
-
}
|
|
2227
|
-
if (parentNode) {
|
|
2228
|
-
(_c = parentNode[childrenAttrName]) !== null && _c !== void 0 ? _c : (parentNode[childrenAttrName] = []);
|
|
2229
|
-
parentNode[childrenAttrName].push(kwargs);
|
|
2230
|
-
}
|
|
2231
|
-
else {
|
|
2232
|
-
newChildren.push(kwargs);
|
|
2233
|
-
}
|
|
2234
|
-
}
|
|
2235
|
-
source.children = newChildren;
|
|
2236
|
-
}
|
|
2237
|
-
/**
|
|
2238
|
-
* Decompresses the source data by
|
|
2239
|
-
* - converting from 'flat' to 'nested' format
|
|
2240
|
-
* - expanding short alias names to long names (if defined in _keyMap)
|
|
2241
|
-
* - resolving value indexes to value strings (if defined in _valueMap)
|
|
2242
|
-
*
|
|
2243
|
-
* @param source - The source object to be decompressed.
|
|
2244
|
-
* @returns void
|
|
2245
|
-
*/
|
|
2246
|
-
function decompressSourceData(source) {
|
|
2247
|
-
let { _format, _version = 1, _keyMap, _valueMap } = source;
|
|
2248
|
-
assert(_version === 1, `Expected file version 1 instead of ${_version}`);
|
|
2249
|
-
let longToShort = _keyMap;
|
|
2250
|
-
let shortToLong = {};
|
|
2251
|
-
if (longToShort) {
|
|
2252
|
-
for (const [key, value] of Object.entries(longToShort)) {
|
|
2253
|
-
shortToLong[value] = key;
|
|
2254
|
-
}
|
|
2255
|
-
}
|
|
2256
|
-
// Fallback for old format (pre 0.7.0, using _keyMap in reverse direction)
|
|
2257
|
-
// TODO: raise Error on final 1.x release
|
|
2258
|
-
if (longToShort && longToShort.t) {
|
|
2259
|
-
const msg = `source._keyMap maps from long to short since v0.7.0. Flip key/value!`;
|
|
2260
|
-
console.warn(msg); // eslint-disable-line no-console
|
|
2261
|
-
[longToShort, shortToLong] = [shortToLong, longToShort];
|
|
2262
|
-
}
|
|
2263
|
-
// Fallback for old format (pre 0.7.0, using _typeList instead of _valueMap)
|
|
2264
|
-
// TODO: raise Error on final 1.x release
|
|
2265
|
-
if (source._typeList != null) {
|
|
2266
|
-
const msg = `source._typeList is deprecated since v0.7.0: use source._valueMap: {"type": [...]} instead.`;
|
|
2267
|
-
if (_valueMap != null) {
|
|
2268
|
-
throw new Error(msg);
|
|
2269
|
-
}
|
|
2270
|
-
else {
|
|
2271
|
-
console.warn(msg); // eslint-disable-line no-console
|
|
2272
|
-
_valueMap = { type: source._typeList };
|
|
2273
|
-
delete source._typeList;
|
|
2274
|
-
}
|
|
2275
|
-
}
|
|
2276
|
-
if (_format === "flat") {
|
|
2277
|
-
unflattenSource(source);
|
|
2278
|
-
}
|
|
2279
|
-
delete source._format;
|
|
2280
|
-
delete source._version;
|
|
2281
|
-
delete source._keyMap;
|
|
2282
|
-
delete source._valueMap;
|
|
2283
|
-
delete source._positional;
|
|
2284
|
-
function _iter(childList) {
|
|
2285
|
-
for (const node of childList) {
|
|
2286
|
-
// Iterate over a list of names, because we modify inside the loop
|
|
2287
|
-
// (for ... of ... does not allow this)
|
|
2288
|
-
Object.getOwnPropertyNames(node).forEach((propName) => {
|
|
2289
|
-
const value = node[propName];
|
|
2290
|
-
// Replace short names with long names if defined in _keyMap
|
|
2291
|
-
let longName = propName;
|
|
2292
|
-
if (_keyMap && shortToLong[propName] != null) {
|
|
2293
|
-
longName = shortToLong[propName];
|
|
2294
|
-
if (longName !== propName) {
|
|
2295
|
-
node[longName] = value;
|
|
2296
|
-
delete node[propName];
|
|
2297
|
-
}
|
|
2298
|
-
}
|
|
2299
|
-
// Replace type index with type name if defined in _valueMap
|
|
2300
|
-
if (_valueMap &&
|
|
2301
|
-
typeof value === "number" &&
|
|
2302
|
-
_valueMap[longName] != null) {
|
|
2303
|
-
const newValue = _valueMap[longName][value];
|
|
2304
|
-
if (newValue == null) {
|
|
2305
|
-
throw new Error(`Expected valueMap[${longName}][${value}] entry in [${_valueMap[longName]}]`);
|
|
2306
|
-
}
|
|
2307
|
-
node[longName] = newValue;
|
|
2308
|
-
}
|
|
2309
|
-
});
|
|
2310
|
-
// Recursion
|
|
2311
|
-
if (node.children) {
|
|
2312
|
-
_iter(node.children);
|
|
2313
|
-
}
|
|
2314
|
-
}
|
|
2315
|
-
}
|
|
2316
|
-
if (_keyMap || _valueMap) {
|
|
2317
|
-
_iter(source.children);
|
|
2318
|
-
}
|
|
2319
|
-
}
|
|
2320
|
-
|
|
2321
|
-
/*!
|
|
2322
|
-
* Wunderbaum - ext-dnd
|
|
2323
|
-
* Copyright (c) 2021-2024, Martin Wendt. Released under the MIT license.
|
|
2324
|
-
* v0.10.1, Sat, 20 Jul 2024 13:53:46 GMT (https://github.com/mar10/wunderbaum)
|
|
2325
|
-
*/
|
|
2326
|
-
const nodeMimeType = "application/x-wunderbaum-node";
|
|
2327
|
-
class DndExtension extends WunderbaumExtension {
|
|
2328
|
-
constructor(tree) {
|
|
2329
|
-
super(tree, "dnd", {
|
|
2330
|
-
autoExpandMS: 1500, // Expand nodes after n milliseconds of hovering
|
|
2331
|
-
// dropMarkerInsertOffsetX: -16, // Additional offset for drop-marker with hitMode = "before"/"after"
|
|
2332
|
-
// dropMarkerOffsetX: -24, // Absolute position offset for .fancytree-drop-marker relatively to ..fancytree-title (icon/img near a node accepting drop)
|
|
2333
|
-
// #1021 `document.body` is not available yet
|
|
2334
|
-
// dropMarkerParent: "body", // Root Container used for drop marker (could be a shadow root)
|
|
2335
|
-
multiSource: false, // true: Drag multiple (i.e. selected) nodes. Also a callback() is allowed
|
|
2336
|
-
effectAllowed: "all", // Restrict the possible cursor shapes and modifier operations (can also be set in the dragStart event)
|
|
2337
|
-
dropEffectDefault: "move", // Default dropEffect ('copy', 'link', or 'move') when no modifier is pressed (override in drag, dragOver).
|
|
2338
|
-
guessDropEffect: true, // Calculate from `effectAllowed` and modifier keys)
|
|
2339
|
-
preventForeignNodes: false, // Prevent dropping nodes from different Wunderbaum trees
|
|
2340
|
-
preventLazyParents: true, // Prevent dropping items on unloaded lazy Wunderbaum tree nodes
|
|
2341
|
-
preventNonNodes: false, // Prevent dropping items other than Wunderbaum tree nodes
|
|
2342
|
-
preventRecursion: true, // Prevent dropping nodes on own descendants
|
|
2343
|
-
preventSameParent: false, // Prevent dropping nodes under same direct parent
|
|
2344
|
-
preventVoidMoves: true, // Prevent dropping nodes 'before self', etc. (move only)
|
|
2345
|
-
serializeClipboardData: true, // Serialize node data to dataTransfer object
|
|
2346
|
-
scroll: true, // Enable auto-scrolling while dragging
|
|
2347
|
-
scrollSensitivity: 20, // Active top/bottom margin in pixel
|
|
2348
|
-
// scrollnterval: 50, // Generate event every 50 ms
|
|
2349
|
-
scrollSpeed: 5, // Scroll pixel per 50 ms
|
|
2350
|
-
// setTextTypeJson: false, // Allow dragging of nodes to different IE windows
|
|
2351
|
-
sourceCopyHook: null, // Optional callback passed to `toDict` on dragStart @since 2.38
|
|
2352
|
-
// Events (drag support)
|
|
2353
|
-
dragStart: null, // Callback(sourceNode, data), return true, to enable dnd drag
|
|
2354
|
-
drag: null, // Callback(sourceNode, data)
|
|
2355
|
-
dragEnd: null, // Callback(sourceNode, data)
|
|
2356
|
-
// Events (drop support)
|
|
2357
|
-
dragEnter: null, // Callback(targetNode, data), return true, to enable dnd drop
|
|
2358
|
-
dragOver: null, // Callback(targetNode, data)
|
|
2359
|
-
dragExpand: null, // Callback(targetNode, data), return false to prevent autoExpand
|
|
2360
|
-
drop: null, // Callback(targetNode, data)
|
|
2361
|
-
dragLeave: null, // Callback(targetNode, data)
|
|
2362
|
-
});
|
|
2363
|
-
// public dropMarkerElem?: HTMLElement;
|
|
2364
|
-
this.srcNode = null;
|
|
2365
|
-
this.lastTargetNode = null;
|
|
2366
|
-
this.lastEnterStamp = 0;
|
|
2367
|
-
this.lastAllowedDropRegions = null;
|
|
2368
|
-
this.lastDropEffect = null;
|
|
2369
|
-
this.lastDropRegion = false;
|
|
2370
|
-
this.currentScrollDir = 0;
|
|
2371
|
-
this.applyScrollDirThrottled = throttle(this._applyScrollDir, 50);
|
|
2372
|
-
}
|
|
2373
|
-
init() {
|
|
2374
|
-
super.init();
|
|
2375
|
-
// Store the current scroll parent, which may be the tree
|
|
2376
|
-
// container, any enclosing div, or the document.
|
|
2377
|
-
// #761: scrollParent() always needs a container child
|
|
2378
|
-
// $temp = $("<span>").appendTo(this.$container);
|
|
2379
|
-
// this.$scrollParent = $temp.scrollParent();
|
|
2380
|
-
// $temp.remove();
|
|
2381
|
-
const tree = this.tree;
|
|
2382
|
-
const dndOpts = tree.options.dnd;
|
|
2383
|
-
// Enable drag support if dragStart() is specified:
|
|
2384
|
-
if (dndOpts.dragStart) {
|
|
2385
|
-
onEvent(tree.element, "dragstart drag dragend", this.onDragEvent.bind(this));
|
|
2386
|
-
}
|
|
2387
|
-
// Enable drop support if dragEnter() is specified:
|
|
2388
|
-
if (dndOpts.dragEnter) {
|
|
2389
|
-
onEvent(tree.element, "dragenter dragover dragleave drop", this.onDropEvent.bind(this));
|
|
2390
|
-
}
|
|
2391
|
-
}
|
|
2392
|
-
/** Cleanup classes after target node is no longer hovered. */
|
|
2393
|
-
_leaveNode() {
|
|
2394
|
-
// We remove the marker on dragenter from the previous target:
|
|
2395
|
-
const ltn = this.lastTargetNode;
|
|
2396
|
-
this.lastEnterStamp = 0;
|
|
2397
|
-
if (ltn) {
|
|
2398
|
-
ltn.setClass("wb-drop-target wb-drop-over wb-drop-after wb-drop-before", false);
|
|
2399
|
-
this.lastTargetNode = null;
|
|
2073
|
+
/** Cleanup classes after target node is no longer hovered. */
|
|
2074
|
+
_leaveNode() {
|
|
2075
|
+
// We remove the marker on dragenter from the previous target:
|
|
2076
|
+
const ltn = this.lastTargetNode;
|
|
2077
|
+
this.lastEnterStamp = 0;
|
|
2078
|
+
if (ltn) {
|
|
2079
|
+
ltn.setClass("wb-drop-target wb-drop-over wb-drop-after wb-drop-before", false);
|
|
2080
|
+
this.lastTargetNode = null;
|
|
2400
2081
|
}
|
|
2401
2082
|
}
|
|
2402
2083
|
/** */
|
|
@@ -2420,14 +2101,15 @@
|
|
|
2420
2101
|
* Calculates the drop region based on the drag event and the allowed drop regions.
|
|
2421
2102
|
*/
|
|
2422
2103
|
_calcDropRegion(e, allowed) {
|
|
2104
|
+
const rowHeight = this.tree.options.rowHeightPx;
|
|
2423
2105
|
const dy = e.offsetY;
|
|
2424
2106
|
if (!allowed) {
|
|
2425
2107
|
return false;
|
|
2426
2108
|
}
|
|
2427
2109
|
else if (allowed.size === 3) {
|
|
2428
|
-
return dy < 0.25 *
|
|
2110
|
+
return dy < 0.25 * rowHeight
|
|
2429
2111
|
? "before"
|
|
2430
|
-
: dy > 0.75 *
|
|
2112
|
+
: dy > 0.75 * rowHeight
|
|
2431
2113
|
? "after"
|
|
2432
2114
|
: "over";
|
|
2433
2115
|
}
|
|
@@ -2436,7 +2118,7 @@
|
|
|
2436
2118
|
}
|
|
2437
2119
|
else {
|
|
2438
2120
|
// Only 'before' and 'after':
|
|
2439
|
-
return dy >
|
|
2121
|
+
return dy > rowHeight / 2 ? "after" : "before";
|
|
2440
2122
|
}
|
|
2441
2123
|
// return "over";
|
|
2442
2124
|
}
|
|
@@ -2697,7 +2379,11 @@
|
|
|
2697
2379
|
}
|
|
2698
2380
|
this.lastAllowedDropRegions = regionSet;
|
|
2699
2381
|
this.lastDropEffect = dt.dropEffect;
|
|
2382
|
+
const region = this._calcDropRegion(e, this.lastAllowedDropRegions);
|
|
2700
2383
|
targetNode.setClass("wb-drop-target");
|
|
2384
|
+
targetNode.setClass("wb-drop-over", region === "over");
|
|
2385
|
+
targetNode.setClass("wb-drop-before", region === "before");
|
|
2386
|
+
targetNode.setClass("wb-drop-after", region === "after");
|
|
2701
2387
|
e.preventDefault(); // Allow drop (Drop operation is denied by default)
|
|
2702
2388
|
return false;
|
|
2703
2389
|
// --- dragover ---
|
|
@@ -2762,160 +2448,500 @@
|
|
|
2762
2448
|
return false;
|
|
2763
2449
|
}
|
|
2764
2450
|
}
|
|
2765
|
-
|
|
2766
|
-
/*!
|
|
2767
|
-
* Wunderbaum - drag_observer
|
|
2768
|
-
* Copyright (c) 2021-2024, Martin Wendt. Released under the MIT license.
|
|
2769
|
-
* v0.
|
|
2770
|
-
*/
|
|
2451
|
+
|
|
2452
|
+
/*!
|
|
2453
|
+
* Wunderbaum - drag_observer
|
|
2454
|
+
* Copyright (c) 2021-2024, Martin Wendt. Released under the MIT license.
|
|
2455
|
+
* v0.11.1, Fri, 27 Dec 2024 22:58:06 GMT (https://github.com/mar10/wunderbaum)
|
|
2456
|
+
*/
|
|
2457
|
+
/**
|
|
2458
|
+
* Convert mouse- and touch events to 'dragstart', 'drag', and 'dragstop'.
|
|
2459
|
+
*/
|
|
2460
|
+
class DragObserver {
|
|
2461
|
+
constructor(opts) {
|
|
2462
|
+
this.start = {
|
|
2463
|
+
event: null,
|
|
2464
|
+
x: 0,
|
|
2465
|
+
y: 0,
|
|
2466
|
+
altKey: false,
|
|
2467
|
+
ctrlKey: false,
|
|
2468
|
+
metaKey: false,
|
|
2469
|
+
shiftKey: false,
|
|
2470
|
+
};
|
|
2471
|
+
this.dragElem = null;
|
|
2472
|
+
this.dragging = false;
|
|
2473
|
+
this.customData = {};
|
|
2474
|
+
// TODO: touch events
|
|
2475
|
+
this.events = ["mousedown", "mouseup", "mousemove", "keydown"];
|
|
2476
|
+
if (!opts.root) {
|
|
2477
|
+
throw new Error("Missing `root` option.");
|
|
2478
|
+
}
|
|
2479
|
+
this.opts = Object.assign({ thresh: 5 }, opts);
|
|
2480
|
+
this.root = opts.root;
|
|
2481
|
+
this._handler = this.handleEvent.bind(this);
|
|
2482
|
+
this.events.forEach((type) => {
|
|
2483
|
+
this.root.addEventListener(type, this._handler);
|
|
2484
|
+
});
|
|
2485
|
+
}
|
|
2486
|
+
/** Unregister all event listeners. */
|
|
2487
|
+
disconnect() {
|
|
2488
|
+
this.events.forEach((type) => {
|
|
2489
|
+
this.root.removeEventListener(type, this._handler);
|
|
2490
|
+
});
|
|
2491
|
+
}
|
|
2492
|
+
getDragElem() {
|
|
2493
|
+
return this.dragElem;
|
|
2494
|
+
}
|
|
2495
|
+
isDragging() {
|
|
2496
|
+
return this.dragging;
|
|
2497
|
+
}
|
|
2498
|
+
stopDrag(cb_event) {
|
|
2499
|
+
if (this.dragging && this.opts.dragstop && cb_event) {
|
|
2500
|
+
cb_event.type = "dragstop";
|
|
2501
|
+
try {
|
|
2502
|
+
this.opts.dragstop(cb_event);
|
|
2503
|
+
}
|
|
2504
|
+
catch (err) {
|
|
2505
|
+
console.error("dragstop error", err); // eslint-disable-line no-console
|
|
2506
|
+
}
|
|
2507
|
+
}
|
|
2508
|
+
this.dragElem = null;
|
|
2509
|
+
this.dragging = false;
|
|
2510
|
+
this.start.event = null;
|
|
2511
|
+
this.customData = {};
|
|
2512
|
+
}
|
|
2513
|
+
handleEvent(e) {
|
|
2514
|
+
const type = e.type;
|
|
2515
|
+
const opts = this.opts;
|
|
2516
|
+
const cb_event = {
|
|
2517
|
+
type: e.type,
|
|
2518
|
+
startEvent: type === "mousedown" ? e : this.start.event,
|
|
2519
|
+
event: e,
|
|
2520
|
+
customData: this.customData,
|
|
2521
|
+
dragElem: this.dragElem,
|
|
2522
|
+
dx: e.pageX - this.start.x,
|
|
2523
|
+
dy: e.pageY - this.start.y,
|
|
2524
|
+
apply: undefined,
|
|
2525
|
+
};
|
|
2526
|
+
// console.log("handleEvent", type, cb_event);
|
|
2527
|
+
switch (type) {
|
|
2528
|
+
case "keydown":
|
|
2529
|
+
this.stopDrag(cb_event);
|
|
2530
|
+
break;
|
|
2531
|
+
case "mousedown":
|
|
2532
|
+
if (this.dragElem) {
|
|
2533
|
+
this.stopDrag(cb_event);
|
|
2534
|
+
break;
|
|
2535
|
+
}
|
|
2536
|
+
if (opts.selector) {
|
|
2537
|
+
let elem = e.target;
|
|
2538
|
+
if (elem.matches(opts.selector)) {
|
|
2539
|
+
this.dragElem = elem;
|
|
2540
|
+
}
|
|
2541
|
+
else {
|
|
2542
|
+
elem = elem.closest(opts.selector);
|
|
2543
|
+
if (elem) {
|
|
2544
|
+
this.dragElem = elem;
|
|
2545
|
+
}
|
|
2546
|
+
else {
|
|
2547
|
+
break; // no event delegation selector matched
|
|
2548
|
+
}
|
|
2549
|
+
}
|
|
2550
|
+
}
|
|
2551
|
+
this.start.event = e;
|
|
2552
|
+
this.start.x = e.pageX;
|
|
2553
|
+
this.start.y = e.pageY;
|
|
2554
|
+
this.start.altKey = e.altKey;
|
|
2555
|
+
this.start.ctrlKey = e.ctrlKey;
|
|
2556
|
+
this.start.metaKey = e.metaKey;
|
|
2557
|
+
this.start.shiftKey = e.shiftKey;
|
|
2558
|
+
break;
|
|
2559
|
+
case "mousemove":
|
|
2560
|
+
// TODO: debounce/throttle?
|
|
2561
|
+
// TODO: horizontal mode: ignore if dx unchanged
|
|
2562
|
+
if (!this.dragElem) {
|
|
2563
|
+
break;
|
|
2564
|
+
}
|
|
2565
|
+
if (!this.dragging) {
|
|
2566
|
+
if (opts.thresh) {
|
|
2567
|
+
const dist2 = cb_event.dx * cb_event.dx + cb_event.dy * cb_event.dy;
|
|
2568
|
+
if (dist2 < opts.thresh * opts.thresh) {
|
|
2569
|
+
break;
|
|
2570
|
+
}
|
|
2571
|
+
}
|
|
2572
|
+
cb_event.type = "dragstart";
|
|
2573
|
+
if (opts.dragstart(cb_event) === false) {
|
|
2574
|
+
this.stopDrag(cb_event);
|
|
2575
|
+
break;
|
|
2576
|
+
}
|
|
2577
|
+
this.dragging = true;
|
|
2578
|
+
}
|
|
2579
|
+
if (this.dragging && this.opts.drag) {
|
|
2580
|
+
cb_event.type = "drag";
|
|
2581
|
+
this.opts.drag(cb_event);
|
|
2582
|
+
}
|
|
2583
|
+
break;
|
|
2584
|
+
case "mouseup":
|
|
2585
|
+
if (!this.dragging) {
|
|
2586
|
+
this.stopDrag(cb_event);
|
|
2587
|
+
break;
|
|
2588
|
+
}
|
|
2589
|
+
if (e.button === 0) {
|
|
2590
|
+
cb_event.apply = true;
|
|
2591
|
+
}
|
|
2592
|
+
else {
|
|
2593
|
+
cb_event.apply = false;
|
|
2594
|
+
}
|
|
2595
|
+
this.stopDrag(cb_event);
|
|
2596
|
+
break;
|
|
2597
|
+
}
|
|
2598
|
+
}
|
|
2599
|
+
}
|
|
2600
|
+
|
|
2601
|
+
/*!
|
|
2602
|
+
* Wunderbaum - common
|
|
2603
|
+
* Copyright (c) 2021-2024, Martin Wendt. Released under the MIT license.
|
|
2604
|
+
* v0.11.1, Fri, 27 Dec 2024 22:58:06 GMT (https://github.com/mar10/wunderbaum)
|
|
2605
|
+
*/
|
|
2606
|
+
const DEFAULT_DEBUGLEVEL = 3; // Replaced by rollup script
|
|
2607
|
+
/**
|
|
2608
|
+
* Fixed height of a row in pixel. Must match the SCSS variable `$row-outer-height`.
|
|
2609
|
+
*/
|
|
2610
|
+
const DEFAULT_ROW_HEIGHT = 22;
|
|
2611
|
+
/**
|
|
2612
|
+
* Fixed width of node icons in pixel. Must match the SCSS variable `$icon-outer-width`.
|
|
2613
|
+
*/
|
|
2614
|
+
const ICON_WIDTH = 20;
|
|
2615
|
+
/**
|
|
2616
|
+
* Adjust the width of the title span, so overflow ellipsis work.
|
|
2617
|
+
* (2 x `$col-padding-x` + 3px rounding errors).
|
|
2618
|
+
*/
|
|
2619
|
+
const TITLE_SPAN_PAD_Y = 7;
|
|
2620
|
+
/** Render row markup for N nodes above and below the visible viewport. */
|
|
2621
|
+
const RENDER_MAX_PREFETCH = 5;
|
|
2622
|
+
/** Minimum column width if not set otherwise. */
|
|
2623
|
+
const DEFAULT_MIN_COL_WIDTH = 4;
|
|
2624
|
+
/** Regular expression to detect if a string describes an image URL (in contrast
|
|
2625
|
+
* to a class name). Strings are considered image urls if they contain '.' or '/'.
|
|
2626
|
+
*/
|
|
2627
|
+
const TEST_IMG = new RegExp(/\.|\//);
|
|
2628
|
+
// export const RECURSIVE_REQUEST_ERROR = "$recursive_request";
|
|
2629
|
+
// export const INVALID_REQUEST_TARGET_ERROR = "$request_target_invalid";
|
|
2630
|
+
/**
|
|
2631
|
+
* Default node icons.
|
|
2632
|
+
* Requires bootstrap icons https://icons.getbootstrap.com
|
|
2633
|
+
*/
|
|
2634
|
+
const iconMaps = {
|
|
2635
|
+
bootstrap: {
|
|
2636
|
+
error: "bi bi-exclamation-triangle",
|
|
2637
|
+
// loading: "bi bi-hourglass-split wb-busy",
|
|
2638
|
+
loading: "bi bi-chevron-right wb-busy",
|
|
2639
|
+
// loading: "bi bi-arrow-repeat wb-spin",
|
|
2640
|
+
// loading: '<div class="spinner-border spinner-border-sm" role="status"> <span class="visually-hidden">Loading...</span> </div>',
|
|
2641
|
+
// noData: "bi bi-search",
|
|
2642
|
+
noData: "bi bi-question-circle",
|
|
2643
|
+
expanderExpanded: "bi bi-chevron-down",
|
|
2644
|
+
// expanderExpanded: "bi bi-dash-square",
|
|
2645
|
+
expanderCollapsed: "bi bi-chevron-right",
|
|
2646
|
+
// expanderCollapsed: "bi bi-plus-square",
|
|
2647
|
+
expanderLazy: "bi bi-chevron-right wb-helper-lazy-expander",
|
|
2648
|
+
// expanderLazy: "bi bi-chevron-bar-right",
|
|
2649
|
+
checkChecked: "bi bi-check-square",
|
|
2650
|
+
checkUnchecked: "bi bi-square",
|
|
2651
|
+
checkUnknown: "bi bi-dash-square-dotted",
|
|
2652
|
+
radioChecked: "bi bi-circle-fill",
|
|
2653
|
+
radioUnchecked: "bi bi-circle",
|
|
2654
|
+
radioUnknown: "bi bi-record-circle",
|
|
2655
|
+
folder: "bi bi-folder2",
|
|
2656
|
+
folderOpen: "bi bi-folder2-open",
|
|
2657
|
+
folderLazy: "bi bi-folder-symlink",
|
|
2658
|
+
doc: "bi bi-file-earmark",
|
|
2659
|
+
colSortable: "bi bi-chevron-expand",
|
|
2660
|
+
// colSortable: "bi bi-arrow-down-up",
|
|
2661
|
+
// colSortAsc: "bi bi-chevron-down",
|
|
2662
|
+
// colSortDesc: "bi bi-chevron-up",
|
|
2663
|
+
colSortAsc: "bi bi-arrow-down",
|
|
2664
|
+
colSortDesc: "bi bi-arrow-up",
|
|
2665
|
+
colFilter: "bi bi-filter-circle",
|
|
2666
|
+
colFilterActive: "bi bi-filter-circle-fill wb-helper-invalid",
|
|
2667
|
+
colMenu: "bi bi-three-dots-vertical",
|
|
2668
|
+
},
|
|
2669
|
+
fontawesome6: {
|
|
2670
|
+
error: "fa-solid fa-triangle-exclamation",
|
|
2671
|
+
loading: "fa-solid fa-chevron-right fa-beat",
|
|
2672
|
+
noData: "fa-solid fa-circle-question",
|
|
2673
|
+
expanderExpanded: "fa-solid fa-chevron-down",
|
|
2674
|
+
expanderCollapsed: "fa-solid fa-chevron-right",
|
|
2675
|
+
expanderLazy: "fa-solid fa-chevron-right wb-helper-lazy-expander",
|
|
2676
|
+
checkChecked: "fa-regular fa-square-check",
|
|
2677
|
+
checkUnchecked: "fa-regular fa-square",
|
|
2678
|
+
checkUnknown: "fa-regular fa-square-minus",
|
|
2679
|
+
radioChecked: "fa-solid fa-circle",
|
|
2680
|
+
radioUnchecked: "fa-regular fa-circle",
|
|
2681
|
+
radioUnknown: "fa-regular fa-circle-question",
|
|
2682
|
+
folder: "fa-solid fa-folder-closed",
|
|
2683
|
+
folderOpen: "fa-regular fa-folder-open",
|
|
2684
|
+
folderLazy: "fa-solid fa-folder-plus",
|
|
2685
|
+
doc: "fa-regular fa-file",
|
|
2686
|
+
colSortable: "fa-solid fa-fw fa-sort",
|
|
2687
|
+
colSortAsc: "fa-solid fa-fw fa-sort-up",
|
|
2688
|
+
colSortDesc: "fa-solid fa-fw fa-sort-down",
|
|
2689
|
+
colFilter: "fa-solid fa-fw fa-filter",
|
|
2690
|
+
colFilterActive: "fa-solid fa-fw fa-filter wb-helper-invalid",
|
|
2691
|
+
colMenu: "fa-solid fa-fw fa-ellipsis-v",
|
|
2692
|
+
},
|
|
2693
|
+
};
|
|
2694
|
+
/** Dict keys that are evaluated by source loader (others are added to `tree.data` instead). */
|
|
2695
|
+
const RESERVED_TREE_SOURCE_KEYS = new Set([
|
|
2696
|
+
"_format", // reserved for future use
|
|
2697
|
+
"_keyMap", // Used for compressed data format
|
|
2698
|
+
"_positional", // Used for compressed data format
|
|
2699
|
+
"_typeList", // Used for compressed data format @deprecated
|
|
2700
|
+
"_valueMap", // Used for compressed data format
|
|
2701
|
+
"_version", // reserved for future use
|
|
2702
|
+
"children",
|
|
2703
|
+
"columns",
|
|
2704
|
+
"types",
|
|
2705
|
+
]);
|
|
2706
|
+
// /** Key codes that trigger grid navigation, even when inside an input element. */
|
|
2707
|
+
// export const INPUT_BREAKOUT_KEYS: Set<string> = new Set([
|
|
2708
|
+
// // "ArrowDown",
|
|
2709
|
+
// // "ArrowUp",
|
|
2710
|
+
// "Enter",
|
|
2711
|
+
// "Escape",
|
|
2712
|
+
// ]);
|
|
2713
|
+
/** Map `KeyEvent.key` to navigation action. */
|
|
2714
|
+
const KEY_TO_ACTION_DICT = {
|
|
2715
|
+
" ": "toggleSelect",
|
|
2716
|
+
"+": "expand",
|
|
2717
|
+
Add: "expand",
|
|
2718
|
+
ArrowDown: "down",
|
|
2719
|
+
ArrowLeft: "left",
|
|
2720
|
+
ArrowRight: "right",
|
|
2721
|
+
ArrowUp: "up",
|
|
2722
|
+
Backspace: "parent",
|
|
2723
|
+
"/": "collapseAll",
|
|
2724
|
+
Divide: "collapseAll",
|
|
2725
|
+
End: "lastCol",
|
|
2726
|
+
Home: "firstCol",
|
|
2727
|
+
"Control+End": "last",
|
|
2728
|
+
"Control+Home": "first",
|
|
2729
|
+
"Meta+ArrowDown": "last", // macOs
|
|
2730
|
+
"Meta+ArrowUp": "first", // macOs
|
|
2731
|
+
"*": "expandAll",
|
|
2732
|
+
Multiply: "expandAll",
|
|
2733
|
+
PageDown: "pageDown",
|
|
2734
|
+
PageUp: "pageUp",
|
|
2735
|
+
"-": "collapse",
|
|
2736
|
+
Subtract: "collapse",
|
|
2737
|
+
};
|
|
2738
|
+
/** Return a callback that returns true if the node title matches the string
|
|
2739
|
+
* or regular expression.
|
|
2740
|
+
* @see {@link WunderbaumNode.findAll}
|
|
2741
|
+
*/
|
|
2742
|
+
function makeNodeTitleMatcher(match) {
|
|
2743
|
+
if (match instanceof RegExp) {
|
|
2744
|
+
return function (node) {
|
|
2745
|
+
return match.test(node.title);
|
|
2746
|
+
};
|
|
2747
|
+
}
|
|
2748
|
+
assert(typeof match === "string", `Expected a string or RegExp: ${match}`);
|
|
2749
|
+
// s = escapeRegex(s.toLowerCase());
|
|
2750
|
+
return function (node) {
|
|
2751
|
+
return node.title === match;
|
|
2752
|
+
// console.log("match " + node, node.title.toLowerCase().indexOf(match))
|
|
2753
|
+
// return node.title.toLowerCase().indexOf(match) >= 0;
|
|
2754
|
+
};
|
|
2755
|
+
}
|
|
2756
|
+
/** Return a callback that returns true if the node title starts with a string (case-insensitive). */
|
|
2757
|
+
function makeNodeTitleStartMatcher(s) {
|
|
2758
|
+
s = escapeRegex(s);
|
|
2759
|
+
const reMatch = new RegExp("^" + s, "i");
|
|
2760
|
+
return function (node) {
|
|
2761
|
+
return reMatch.test(node.title);
|
|
2762
|
+
};
|
|
2763
|
+
}
|
|
2764
|
+
/** Compare two nodes by title (case-insensitive). */
|
|
2765
|
+
function nodeTitleSorter(a, b) {
|
|
2766
|
+
const x = a.title.toLowerCase();
|
|
2767
|
+
const y = b.title.toLowerCase();
|
|
2768
|
+
return x === y ? 0 : x > y ? 1 : -1;
|
|
2769
|
+
}
|
|
2770
|
+
/**
|
|
2771
|
+
* Convert 'flat' to 'nested' format.
|
|
2772
|
+
*
|
|
2773
|
+
* Flat node entry format:
|
|
2774
|
+
* [PARENT_ID, [POSITIONAL_ARGS]]
|
|
2775
|
+
* or
|
|
2776
|
+
* [PARENT_ID, [POSITIONAL_ARGS], {KEY_VALUE_ARGS}]
|
|
2777
|
+
*
|
|
2778
|
+
* 1. Parent-referencing list is converted to a list of nested dicts with
|
|
2779
|
+
* optional `children` properties.
|
|
2780
|
+
* 2. `[POSITIONAL_ARGS]` are added as dict attributes.
|
|
2781
|
+
*/
|
|
2782
|
+
function unflattenSource(source) {
|
|
2783
|
+
var _a, _b, _c;
|
|
2784
|
+
const { _format, _keyMap = {}, _positional = [], children } = source;
|
|
2785
|
+
if (_format !== "flat") {
|
|
2786
|
+
throw new Error(`Expected source._format: "flat", but got ${_format}`);
|
|
2787
|
+
}
|
|
2788
|
+
if (_positional && _positional.includes("children")) {
|
|
2789
|
+
throw new Error(`source._positional must not include "children": ${_positional}`);
|
|
2790
|
+
}
|
|
2791
|
+
let longToShort = _keyMap;
|
|
2792
|
+
if (_keyMap.t) {
|
|
2793
|
+
// Inverse keyMap was used (pre 0.7.0)
|
|
2794
|
+
// TODO: raise Error on final 1.x release
|
|
2795
|
+
const msg = `source._keyMap maps from long to short since v0.7.0. Flip key/value!`;
|
|
2796
|
+
console.warn(msg); // eslint-disable-line no-console
|
|
2797
|
+
longToShort = {};
|
|
2798
|
+
for (const [key, value] of Object.entries(_keyMap)) {
|
|
2799
|
+
longToShort[value] = key;
|
|
2800
|
+
}
|
|
2801
|
+
}
|
|
2802
|
+
const positionalShort = _positional.map((e) => longToShort[e]);
|
|
2803
|
+
const newChildren = [];
|
|
2804
|
+
const keyToNodeMap = {};
|
|
2805
|
+
const indexToNodeMap = {};
|
|
2806
|
+
const keyAttrName = (_a = longToShort["key"]) !== null && _a !== void 0 ? _a : "key";
|
|
2807
|
+
const childrenAttrName = (_b = longToShort["children"]) !== null && _b !== void 0 ? _b : "children";
|
|
2808
|
+
for (const [index, nodeTuple] of children.entries()) {
|
|
2809
|
+
// Node entry format:
|
|
2810
|
+
// [PARENT_ID, [POSITIONAL_ARGS]]
|
|
2811
|
+
// or
|
|
2812
|
+
// [PARENT_ID, [POSITIONAL_ARGS], {KEY_VALUE_ARGS}]
|
|
2813
|
+
const [parentId, args, kwargs = {}] = nodeTuple;
|
|
2814
|
+
// Free up some memory as we go
|
|
2815
|
+
nodeTuple[1] = null;
|
|
2816
|
+
if (nodeTuple[2] != null) {
|
|
2817
|
+
nodeTuple[2] = null;
|
|
2818
|
+
}
|
|
2819
|
+
// console.log("flatten", parentId, args, kwargs)
|
|
2820
|
+
// We keep `kwargs` as our new node definition. Then we add all positional
|
|
2821
|
+
// values to this object:
|
|
2822
|
+
args.forEach((val, positionalIdx) => {
|
|
2823
|
+
kwargs[positionalShort[positionalIdx]] = val;
|
|
2824
|
+
});
|
|
2825
|
+
// Find the parent node. `null` means 'toplevel'. PARENT_ID may be the numeric
|
|
2826
|
+
// index of the source.children list. If PARENT_ID is a string, we search
|
|
2827
|
+
// a parent with node.key of this value.
|
|
2828
|
+
indexToNodeMap[index] = kwargs;
|
|
2829
|
+
const key = kwargs[keyAttrName];
|
|
2830
|
+
if (key != null) {
|
|
2831
|
+
keyToNodeMap[key] = kwargs;
|
|
2832
|
+
}
|
|
2833
|
+
let parentNode = null;
|
|
2834
|
+
if (parentId === null) ;
|
|
2835
|
+
else if (typeof parentId === "number") {
|
|
2836
|
+
parentNode = indexToNodeMap[parentId];
|
|
2837
|
+
if (parentNode === undefined) {
|
|
2838
|
+
throw new Error(`unflattenSource: Could not find parent node by index: ${parentId}.`);
|
|
2839
|
+
}
|
|
2840
|
+
}
|
|
2841
|
+
else {
|
|
2842
|
+
parentNode = keyToNodeMap[parentId];
|
|
2843
|
+
if (parentNode === undefined) {
|
|
2844
|
+
throw new Error(`unflattenSource: Could not find parent node by key: ${parentId}`);
|
|
2845
|
+
}
|
|
2846
|
+
}
|
|
2847
|
+
if (parentNode) {
|
|
2848
|
+
(_c = parentNode[childrenAttrName]) !== null && _c !== void 0 ? _c : (parentNode[childrenAttrName] = []);
|
|
2849
|
+
parentNode[childrenAttrName].push(kwargs);
|
|
2850
|
+
}
|
|
2851
|
+
else {
|
|
2852
|
+
newChildren.push(kwargs);
|
|
2853
|
+
}
|
|
2854
|
+
}
|
|
2855
|
+
source.children = newChildren;
|
|
2856
|
+
}
|
|
2771
2857
|
/**
|
|
2772
|
-
*
|
|
2858
|
+
* Decompresses the source data by
|
|
2859
|
+
* - converting from 'flat' to 'nested' format
|
|
2860
|
+
* - expanding short alias names to long names (if defined in _keyMap)
|
|
2861
|
+
* - resolving value indexes to value strings (if defined in _valueMap)
|
|
2862
|
+
*
|
|
2863
|
+
* @param source - The source object to be decompressed.
|
|
2864
|
+
* @returns void
|
|
2773
2865
|
*/
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
metaKey: false,
|
|
2783
|
-
shiftKey: false,
|
|
2784
|
-
};
|
|
2785
|
-
this.dragElem = null;
|
|
2786
|
-
this.dragging = false;
|
|
2787
|
-
this.customData = {};
|
|
2788
|
-
// TODO: touch events
|
|
2789
|
-
this.events = ["mousedown", "mouseup", "mousemove", "keydown"];
|
|
2790
|
-
if (!opts.root) {
|
|
2791
|
-
throw new Error("Missing `root` option.");
|
|
2866
|
+
function decompressSourceData(source) {
|
|
2867
|
+
let { _format, _version = 1, _keyMap, _valueMap } = source;
|
|
2868
|
+
assert(_version === 1, `Expected file version 1 instead of ${_version}`);
|
|
2869
|
+
let longToShort = _keyMap;
|
|
2870
|
+
let shortToLong = {};
|
|
2871
|
+
if (longToShort) {
|
|
2872
|
+
for (const [key, value] of Object.entries(longToShort)) {
|
|
2873
|
+
shortToLong[value] = key;
|
|
2792
2874
|
}
|
|
2793
|
-
this.opts = Object.assign({ thresh: 5 }, opts);
|
|
2794
|
-
this.root = opts.root;
|
|
2795
|
-
this._handler = this.handleEvent.bind(this);
|
|
2796
|
-
this.events.forEach((type) => {
|
|
2797
|
-
this.root.addEventListener(type, this._handler);
|
|
2798
|
-
});
|
|
2799
|
-
}
|
|
2800
|
-
/** Unregister all event listeners. */
|
|
2801
|
-
disconnect() {
|
|
2802
|
-
this.events.forEach((type) => {
|
|
2803
|
-
this.root.removeEventListener(type, this._handler);
|
|
2804
|
-
});
|
|
2805
|
-
}
|
|
2806
|
-
getDragElem() {
|
|
2807
|
-
return this.dragElem;
|
|
2808
2875
|
}
|
|
2809
|
-
|
|
2810
|
-
|
|
2876
|
+
// Fallback for old format (pre 0.7.0, using _keyMap in reverse direction)
|
|
2877
|
+
// TODO: raise Error on final 1.x release
|
|
2878
|
+
if (longToShort && longToShort.t) {
|
|
2879
|
+
const msg = `source._keyMap maps from long to short since v0.7.0. Flip key/value!`;
|
|
2880
|
+
console.warn(msg); // eslint-disable-line no-console
|
|
2881
|
+
[longToShort, shortToLong] = [shortToLong, longToShort];
|
|
2811
2882
|
}
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
|
|
2883
|
+
// Fallback for old format (pre 0.7.0, using _typeList instead of _valueMap)
|
|
2884
|
+
// TODO: raise Error on final 1.x release
|
|
2885
|
+
if (source._typeList != null) {
|
|
2886
|
+
const msg = `source._typeList is deprecated since v0.7.0: use source._valueMap: {"type": [...]} instead.`;
|
|
2887
|
+
if (_valueMap != null) {
|
|
2888
|
+
throw new Error(msg);
|
|
2889
|
+
}
|
|
2890
|
+
else {
|
|
2891
|
+
console.warn(msg); // eslint-disable-line no-console
|
|
2892
|
+
_valueMap = { type: source._typeList };
|
|
2893
|
+
delete source._typeList;
|
|
2821
2894
|
}
|
|
2822
|
-
this.dragElem = null;
|
|
2823
|
-
this.dragging = false;
|
|
2824
|
-
this.start.event = null;
|
|
2825
|
-
this.customData = {};
|
|
2826
2895
|
}
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
|
|
2839
|
-
|
|
2840
|
-
|
|
2841
|
-
|
|
2842
|
-
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
|
|
2848
|
-
break;
|
|
2849
|
-
}
|
|
2850
|
-
if (opts.selector) {
|
|
2851
|
-
let elem = e.target;
|
|
2852
|
-
if (elem.matches(opts.selector)) {
|
|
2853
|
-
this.dragElem = elem;
|
|
2854
|
-
}
|
|
2855
|
-
else {
|
|
2856
|
-
elem = elem.closest(opts.selector);
|
|
2857
|
-
if (elem) {
|
|
2858
|
-
this.dragElem = elem;
|
|
2859
|
-
}
|
|
2860
|
-
else {
|
|
2861
|
-
break; // no event delegation selector matched
|
|
2862
|
-
}
|
|
2896
|
+
if (_format === "flat") {
|
|
2897
|
+
unflattenSource(source);
|
|
2898
|
+
}
|
|
2899
|
+
delete source._format;
|
|
2900
|
+
delete source._version;
|
|
2901
|
+
delete source._keyMap;
|
|
2902
|
+
delete source._valueMap;
|
|
2903
|
+
delete source._positional;
|
|
2904
|
+
function _iter(childList) {
|
|
2905
|
+
for (const node of childList) {
|
|
2906
|
+
// Iterate over a list of names, because we modify inside the loop
|
|
2907
|
+
// (for ... of ... does not allow this)
|
|
2908
|
+
Object.getOwnPropertyNames(node).forEach((propName) => {
|
|
2909
|
+
const value = node[propName];
|
|
2910
|
+
// Replace short names with long names if defined in _keyMap
|
|
2911
|
+
let longName = propName;
|
|
2912
|
+
if (_keyMap && shortToLong[propName] != null) {
|
|
2913
|
+
longName = shortToLong[propName];
|
|
2914
|
+
if (longName !== propName) {
|
|
2915
|
+
node[longName] = value;
|
|
2916
|
+
delete node[propName];
|
|
2863
2917
|
}
|
|
2864
2918
|
}
|
|
2865
|
-
|
|
2866
|
-
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
break;
|
|
2873
|
-
case "mousemove":
|
|
2874
|
-
// TODO: debounce/throttle?
|
|
2875
|
-
// TODO: horizontal mode: ignore if dx unchanged
|
|
2876
|
-
if (!this.dragElem) {
|
|
2877
|
-
break;
|
|
2878
|
-
}
|
|
2879
|
-
if (!this.dragging) {
|
|
2880
|
-
if (opts.thresh) {
|
|
2881
|
-
const dist2 = cb_event.dx * cb_event.dx + cb_event.dy * cb_event.dy;
|
|
2882
|
-
if (dist2 < opts.thresh * opts.thresh) {
|
|
2883
|
-
break;
|
|
2884
|
-
}
|
|
2885
|
-
}
|
|
2886
|
-
cb_event.type = "dragstart";
|
|
2887
|
-
if (opts.dragstart(cb_event) === false) {
|
|
2888
|
-
this.stopDrag(cb_event);
|
|
2889
|
-
break;
|
|
2919
|
+
// Replace type index with type name if defined in _valueMap
|
|
2920
|
+
if (_valueMap &&
|
|
2921
|
+
typeof value === "number" &&
|
|
2922
|
+
_valueMap[longName] != null) {
|
|
2923
|
+
const newValue = _valueMap[longName][value];
|
|
2924
|
+
if (newValue == null) {
|
|
2925
|
+
throw new Error(`Expected valueMap[${longName}][${value}] entry in [${_valueMap[longName]}]`);
|
|
2890
2926
|
}
|
|
2891
|
-
|
|
2892
|
-
}
|
|
2893
|
-
if (this.dragging && this.opts.drag) {
|
|
2894
|
-
cb_event.type = "drag";
|
|
2895
|
-
this.opts.drag(cb_event);
|
|
2896
|
-
}
|
|
2897
|
-
break;
|
|
2898
|
-
case "mouseup":
|
|
2899
|
-
if (!this.dragging) {
|
|
2900
|
-
this.stopDrag(cb_event);
|
|
2901
|
-
break;
|
|
2902
|
-
}
|
|
2903
|
-
if (e.button === 0) {
|
|
2904
|
-
cb_event.apply = true;
|
|
2905
|
-
}
|
|
2906
|
-
else {
|
|
2907
|
-
cb_event.apply = false;
|
|
2927
|
+
node[longName] = newValue;
|
|
2908
2928
|
}
|
|
2909
|
-
|
|
2910
|
-
|
|
2929
|
+
});
|
|
2930
|
+
// Recursion
|
|
2931
|
+
if (node.children) {
|
|
2932
|
+
_iter(node.children);
|
|
2933
|
+
}
|
|
2911
2934
|
}
|
|
2912
2935
|
}
|
|
2936
|
+
if (_keyMap || _valueMap) {
|
|
2937
|
+
_iter(source.children);
|
|
2938
|
+
}
|
|
2913
2939
|
}
|
|
2914
2940
|
|
|
2915
2941
|
/*!
|
|
2916
2942
|
* Wunderbaum - ext-grid
|
|
2917
2943
|
* Copyright (c) 2021-2024, Martin Wendt. Released under the MIT license.
|
|
2918
|
-
* v0.
|
|
2944
|
+
* v0.11.1, Fri, 27 Dec 2024 22:58:06 GMT (https://github.com/mar10/wunderbaum)
|
|
2919
2945
|
*/
|
|
2920
2946
|
class GridExtension extends WunderbaumExtension {
|
|
2921
2947
|
constructor(tree) {
|
|
@@ -2932,7 +2958,7 @@
|
|
|
2932
2958
|
const colDef = info.colDef;
|
|
2933
2959
|
const allow = colDef &&
|
|
2934
2960
|
this.tree.element.contains(e.dragElem) &&
|
|
2935
|
-
toBool(colDef.resizable, tree.options.
|
|
2961
|
+
toBool(colDef.resizable, tree.options.columnsResizable, false);
|
|
2936
2962
|
// this.tree.log("dragstart", colDef, e, info);
|
|
2937
2963
|
this.tree.element.classList.toggle("wb-col-resizing", !!allow);
|
|
2938
2964
|
info.colElem.classList.toggle("wb-col-resizing", !!allow);
|
|
@@ -3006,7 +3032,7 @@
|
|
|
3006
3032
|
/*!
|
|
3007
3033
|
* Wunderbaum - deferred
|
|
3008
3034
|
* Copyright (c) 2021-2024, Martin Wendt. Released under the MIT license.
|
|
3009
|
-
* v0.
|
|
3035
|
+
* v0.11.1, Fri, 27 Dec 2024 22:58:06 GMT (https://github.com/mar10/wunderbaum)
|
|
3010
3036
|
*/
|
|
3011
3037
|
/**
|
|
3012
3038
|
* Implement a ES6 Promise, that exposes a resolve() and reject() method.
|
|
@@ -3059,7 +3085,7 @@
|
|
|
3059
3085
|
/*!
|
|
3060
3086
|
* Wunderbaum - wunderbaum_node
|
|
3061
3087
|
* Copyright (c) 2021-2024, Martin Wendt. Released under the MIT license.
|
|
3062
|
-
* v0.
|
|
3088
|
+
* v0.11.1, Fri, 27 Dec 2024 22:58:06 GMT (https://github.com/mar10/wunderbaum)
|
|
3063
3089
|
*/
|
|
3064
3090
|
/** WunderbaumNode properties that can be passed with source data.
|
|
3065
3091
|
* (Any other source properties will be stored as `node.data.PROP`.)
|
|
@@ -3087,6 +3113,20 @@
|
|
|
3087
3113
|
const NODE_DICT_PROPS = new Set(NODE_PROPS);
|
|
3088
3114
|
NODE_DICT_PROPS.delete("_partsel");
|
|
3089
3115
|
NODE_DICT_PROPS.delete("unselectable");
|
|
3116
|
+
// /** Node properties that are of type bool (or boolean & string).
|
|
3117
|
+
// * When parsing, we accept 0 for false and 1 for true for better JSON compression.
|
|
3118
|
+
// */
|
|
3119
|
+
// export const NODE_BOOL_PROPS: Set<string> = new Set([
|
|
3120
|
+
// "checkbox",
|
|
3121
|
+
// "colspan",
|
|
3122
|
+
// "expanded",
|
|
3123
|
+
// "icon",
|
|
3124
|
+
// "iconTooltip",
|
|
3125
|
+
// "radiogroup",
|
|
3126
|
+
// "selected",
|
|
3127
|
+
// "tooltip",
|
|
3128
|
+
// "unselectable",
|
|
3129
|
+
// ]);
|
|
3090
3130
|
/**
|
|
3091
3131
|
* A single tree node.
|
|
3092
3132
|
*
|
|
@@ -3102,6 +3142,12 @@
|
|
|
3102
3142
|
* @see Use {@link setKey} to modify.
|
|
3103
3143
|
*/
|
|
3104
3144
|
this.refKey = undefined;
|
|
3145
|
+
/**
|
|
3146
|
+
* Array of child nodes (null for leaf nodes).
|
|
3147
|
+
* For lazy nodes, this is `null` or ùndefined` until the children are loaded
|
|
3148
|
+
* and leaf nodes may be `[]` (empty array).
|
|
3149
|
+
* @see {@link hasChildren}, {@link addChildren}, {@link lazy}.
|
|
3150
|
+
*/
|
|
3105
3151
|
this.children = null;
|
|
3106
3152
|
/** Additional classes added to `div.wb-row`.
|
|
3107
3153
|
* @see {@link hasClass}, {@link setClass}. */
|
|
@@ -3122,20 +3168,26 @@
|
|
|
3122
3168
|
this.parent = parent;
|
|
3123
3169
|
this.key = "" + ((_a = data.key) !== null && _a !== void 0 ? _a : ++WunderbaumNode.sequence);
|
|
3124
3170
|
this.title = "" + ((_b = data.title) !== null && _b !== void 0 ? _b : "<" + this.key + ">");
|
|
3171
|
+
this.expanded = !!data.expanded;
|
|
3172
|
+
this.lazy = !!data.lazy;
|
|
3173
|
+
// We set the following node properties only if a matching data value is
|
|
3174
|
+
// passed
|
|
3125
3175
|
data.refKey != null ? (this.refKey = "" + data.refKey) : 0;
|
|
3126
3176
|
data.type != null ? (this.type = "" + data.type) : 0;
|
|
3127
|
-
this.
|
|
3128
|
-
data.
|
|
3129
|
-
|
|
3177
|
+
data.icon != null ? (this.icon = intToBool(data.icon)) : 0;
|
|
3178
|
+
data.tooltip != null ? (this.tooltip = intToBool(data.tooltip)) : 0;
|
|
3179
|
+
data.iconTooltip != null
|
|
3180
|
+
? (this.iconTooltip = intToBool(data.iconTooltip))
|
|
3181
|
+
: 0;
|
|
3130
3182
|
data.statusNodeType != null
|
|
3131
3183
|
? (this.statusNodeType = ("" + data.statusNodeType))
|
|
3132
3184
|
: 0;
|
|
3133
3185
|
data.colspan != null ? (this.colspan = !!data.colspan) : 0;
|
|
3134
3186
|
// Selection
|
|
3135
|
-
data.checkbox != null ? (
|
|
3187
|
+
data.checkbox != null ? intToBool(data.checkbox) : 0;
|
|
3136
3188
|
data.radiogroup != null ? (this.radiogroup = !!data.radiogroup) : 0;
|
|
3137
|
-
this.selected = data.selected
|
|
3138
|
-
data.unselectable
|
|
3189
|
+
data.selected != null ? (this.selected = !!data.selected) : 0;
|
|
3190
|
+
data.unselectable != null ? (this.unselectable = !!data.unselectable) : 0;
|
|
3139
3191
|
if (data.classes) {
|
|
3140
3192
|
this.setClass(data.classes);
|
|
3141
3193
|
}
|
|
@@ -3689,7 +3741,7 @@
|
|
|
3689
3741
|
hasClass(className) {
|
|
3690
3742
|
return this.classes ? this.classes.has(className) : false;
|
|
3691
3743
|
}
|
|
3692
|
-
/** Return true if node ist the currently focused node. */
|
|
3744
|
+
/** Return true if node ist the currently focused node. @since 0.9.0 */
|
|
3693
3745
|
hasFocus() {
|
|
3694
3746
|
return this.tree.focusNode === this;
|
|
3695
3747
|
}
|
|
@@ -3911,6 +3963,8 @@
|
|
|
3911
3963
|
if (tree.options.selectMode === "hier") {
|
|
3912
3964
|
this.fixSelection3FromEndNodes();
|
|
3913
3965
|
}
|
|
3966
|
+
// Allow to un-sort nodes after sorting
|
|
3967
|
+
this.resetNativeChildOrder();
|
|
3914
3968
|
this._callEvent("load");
|
|
3915
3969
|
}
|
|
3916
3970
|
async _fetchWithOptions(source) {
|
|
@@ -3962,8 +4016,8 @@
|
|
|
3962
4016
|
let elap = 0, elapLoad = 0, elapProcess = 0;
|
|
3963
4017
|
// Check for overlapping requests
|
|
3964
4018
|
if (this._requestId) {
|
|
3965
|
-
this.logWarn(`Recursive load request #${requestId} while #${this._requestId} is pending
|
|
3966
|
-
|
|
4019
|
+
this.logWarn(`Recursive load request #${requestId} while #${this._requestId} is pending. ` +
|
|
4020
|
+
"The previous request will be ignored.");
|
|
3967
4021
|
}
|
|
3968
4022
|
this._requestId = requestId;
|
|
3969
4023
|
// const timerLabel = tree.logTime(this + ".load()");
|
|
@@ -4443,6 +4497,7 @@
|
|
|
4443
4497
|
_render_markup(opts) {
|
|
4444
4498
|
const tree = this.tree;
|
|
4445
4499
|
const treeOptions = tree.options;
|
|
4500
|
+
const rowHeight = treeOptions.rowHeightPx;
|
|
4446
4501
|
const checkbox = this.getOption("checkbox");
|
|
4447
4502
|
const columns = tree.columns;
|
|
4448
4503
|
const level = this.getLevel();
|
|
@@ -4457,7 +4512,7 @@
|
|
|
4457
4512
|
assert(!this.isRootNode(), "Root node not allowed");
|
|
4458
4513
|
rowDiv = document.createElement("div");
|
|
4459
4514
|
rowDiv.classList.add("wb-row");
|
|
4460
|
-
rowDiv.style.top = this._rowIdx *
|
|
4515
|
+
rowDiv.style.top = this._rowIdx * rowHeight + "px";
|
|
4461
4516
|
this._rowElem = rowDiv;
|
|
4462
4517
|
// Attach a node reference to the DOM Element:
|
|
4463
4518
|
rowDiv._wb_node = this;
|
|
@@ -4854,7 +4909,7 @@
|
|
|
4854
4909
|
*
|
|
4855
4910
|
* @param name name of the option property (on node and tree)
|
|
4856
4911
|
* @param defaultValue return this if nothing else matched
|
|
4857
|
-
* {@link Wunderbaum.getOption|Wunderbaum.getOption
|
|
4912
|
+
* {@link Wunderbaum.getOption|Wunderbaum.getOption}
|
|
4858
4913
|
*/
|
|
4859
4914
|
getOption(name, defaultValue) {
|
|
4860
4915
|
const tree = this.tree;
|
|
@@ -4890,7 +4945,7 @@
|
|
|
4890
4945
|
return value !== null && value !== void 0 ? value : defaultValue;
|
|
4891
4946
|
}
|
|
4892
4947
|
/** Make sure that this node is visible in the viewport.
|
|
4893
|
-
* @see {@link Wunderbaum.scrollTo|Wunderbaum.scrollTo
|
|
4948
|
+
* @see {@link Wunderbaum.scrollTo|Wunderbaum.scrollTo}
|
|
4894
4949
|
*/
|
|
4895
4950
|
async scrollIntoView(options) {
|
|
4896
4951
|
const opts = Object.assign({ node: this }, options);
|
|
@@ -5029,9 +5084,9 @@
|
|
|
5029
5084
|
* and column content. It can be reduced to 'ChangeType.status' if only
|
|
5030
5085
|
* active/focus/selected state has changed.
|
|
5031
5086
|
*
|
|
5032
|
-
* This method will eventually call {@link WunderbaumNode._render
|
|
5087
|
+
* This method will eventually call {@link WunderbaumNode._render} with
|
|
5033
5088
|
* default options, but may be more consistent with the tree's
|
|
5034
|
-
* {@link Wunderbaum.update
|
|
5089
|
+
* {@link Wunderbaum.update} API.
|
|
5035
5090
|
*/
|
|
5036
5091
|
update(change = ChangeType.data) {
|
|
5037
5092
|
assert(change === ChangeType.status || change === ChangeType.data, `Invalid change type ${change}`);
|
|
@@ -5360,6 +5415,95 @@
|
|
|
5360
5415
|
this.tree.update(ChangeType.structure);
|
|
5361
5416
|
// this.triggerModify("sort"); // TODO
|
|
5362
5417
|
}
|
|
5418
|
+
/**
|
|
5419
|
+
* Renumber nodes `_nativeIndex`. This is useful to allow to restore the
|
|
5420
|
+
* order after sorting a column.
|
|
5421
|
+
* This method is automatically called after loading new child nodes.
|
|
5422
|
+
* @since 0.11.0
|
|
5423
|
+
*/
|
|
5424
|
+
resetNativeChildOrder(options) {
|
|
5425
|
+
const { recursive = true, propName = "_nativeIndex" } = options !== null && options !== void 0 ? options : {};
|
|
5426
|
+
if (this.children) {
|
|
5427
|
+
this.children.forEach((child, i) => {
|
|
5428
|
+
child.data[propName] = i;
|
|
5429
|
+
if (recursive && child.children) {
|
|
5430
|
+
child.resetNativeChildOrder(options);
|
|
5431
|
+
}
|
|
5432
|
+
});
|
|
5433
|
+
}
|
|
5434
|
+
}
|
|
5435
|
+
/**
|
|
5436
|
+
* Convenience method to implement column sorting.
|
|
5437
|
+
* @since 0.11.0
|
|
5438
|
+
*/
|
|
5439
|
+
sortByProperty(options) {
|
|
5440
|
+
var _a, _b, _c;
|
|
5441
|
+
const { caseInsensitive = true, deep = true, nativeOrderPropName = "_nativeIndex", updateColInfo = false, } = options;
|
|
5442
|
+
let order;
|
|
5443
|
+
let colDef;
|
|
5444
|
+
if (updateColInfo) {
|
|
5445
|
+
colDef = this.tree["_columnsById"][options.colId];
|
|
5446
|
+
assert(colDef, `Invalid colId specified: ${options.colId}`);
|
|
5447
|
+
order =
|
|
5448
|
+
(_a = options.order) !== null && _a !== void 0 ? _a : rotate(colDef.sortOrder, ["asc", "desc", undefined]);
|
|
5449
|
+
for (const col of this.tree.columns) {
|
|
5450
|
+
col.sortOrder = col === colDef ? order : undefined;
|
|
5451
|
+
}
|
|
5452
|
+
this.tree.update(ChangeType.colStructure);
|
|
5453
|
+
}
|
|
5454
|
+
else {
|
|
5455
|
+
order = (_b = options.order) !== null && _b !== void 0 ? _b : "asc";
|
|
5456
|
+
}
|
|
5457
|
+
let propName = (_c = options.propName) !== null && _c !== void 0 ? _c : (options.colId || "");
|
|
5458
|
+
if (propName === "*") {
|
|
5459
|
+
propName = "title";
|
|
5460
|
+
}
|
|
5461
|
+
if (order == null) {
|
|
5462
|
+
propName = nativeOrderPropName;
|
|
5463
|
+
order = "asc";
|
|
5464
|
+
}
|
|
5465
|
+
this.logDebug(`sortByProperty(), propName=${propName}, ${order}`, options);
|
|
5466
|
+
assert(propName, "No property name specified");
|
|
5467
|
+
const cmp = (a, b) => {
|
|
5468
|
+
let av, bv;
|
|
5469
|
+
if (NODE_DICT_PROPS.has(propName)) {
|
|
5470
|
+
av = a[propName];
|
|
5471
|
+
bv = b[propName];
|
|
5472
|
+
}
|
|
5473
|
+
else {
|
|
5474
|
+
av = a.data[propName];
|
|
5475
|
+
bv = b.data[propName];
|
|
5476
|
+
}
|
|
5477
|
+
if (av == null && bv == null) {
|
|
5478
|
+
return 0;
|
|
5479
|
+
}
|
|
5480
|
+
if (av == null) {
|
|
5481
|
+
av = typeof bv === "string" ? "" : 0;
|
|
5482
|
+
}
|
|
5483
|
+
else if (typeof av === "boolean") {
|
|
5484
|
+
av = av ? 1 : 0;
|
|
5485
|
+
}
|
|
5486
|
+
if (bv == null) {
|
|
5487
|
+
bv = typeof av === "string" ? "" : 0;
|
|
5488
|
+
}
|
|
5489
|
+
else if (typeof bv === "boolean") {
|
|
5490
|
+
bv = bv ? 1 : 0;
|
|
5491
|
+
}
|
|
5492
|
+
if (caseInsensitive) {
|
|
5493
|
+
if (typeof av === "string") {
|
|
5494
|
+
av = av.toLowerCase();
|
|
5495
|
+
}
|
|
5496
|
+
if (typeof bv === "string") {
|
|
5497
|
+
bv = bv.toLowerCase();
|
|
5498
|
+
}
|
|
5499
|
+
}
|
|
5500
|
+
if (order === "desc") {
|
|
5501
|
+
return av === bv ? 0 : av > bv ? -1 : 1;
|
|
5502
|
+
}
|
|
5503
|
+
return av === bv ? 0 : av > bv ? 1 : -1;
|
|
5504
|
+
};
|
|
5505
|
+
return this.sortChildren(cmp, deep);
|
|
5506
|
+
}
|
|
5363
5507
|
/**
|
|
5364
5508
|
* Trigger `modifyChild` event on a parent to signal that a child was modified.
|
|
5365
5509
|
* @param {string} operation Type of change: 'add', 'remove', 'rename', 'move', 'data', ...
|
|
@@ -5468,7 +5612,7 @@
|
|
|
5468
5612
|
/*!
|
|
5469
5613
|
* Wunderbaum - ext-edit
|
|
5470
5614
|
* Copyright (c) 2021-2024, Martin Wendt. Released under the MIT license.
|
|
5471
|
-
* v0.
|
|
5615
|
+
* v0.11.1, Fri, 27 Dec 2024 22:58:06 GMT (https://github.com/mar10/wunderbaum)
|
|
5472
5616
|
*/
|
|
5473
5617
|
// const START_MARKER = "\uFFF7";
|
|
5474
5618
|
class EditExtension extends WunderbaumExtension {
|
|
@@ -5632,6 +5776,10 @@
|
|
|
5632
5776
|
if (!node) {
|
|
5633
5777
|
return;
|
|
5634
5778
|
}
|
|
5779
|
+
if (node.isStatusNode()) {
|
|
5780
|
+
node.logWarn("Cannot edit status node.");
|
|
5781
|
+
return;
|
|
5782
|
+
}
|
|
5635
5783
|
this.tree.logDebug(`startEditTitle(node=${node})`);
|
|
5636
5784
|
let inputHtml = node._callEvent("edit.beforeEdit");
|
|
5637
5785
|
if (inputHtml === false) {
|
|
@@ -5799,8 +5947,8 @@
|
|
|
5799
5947
|
* https://github.com/mar10/wunderbaum
|
|
5800
5948
|
*
|
|
5801
5949
|
* Released under the MIT license.
|
|
5802
|
-
* @version v0.
|
|
5803
|
-
* @date
|
|
5950
|
+
* @version v0.11.1
|
|
5951
|
+
* @date Fri, 27 Dec 2024 22:58:06 GMT
|
|
5804
5952
|
*/
|
|
5805
5953
|
// import "./wunderbaum.scss";
|
|
5806
5954
|
class WbSystemRoot extends WunderbaumNode {
|
|
@@ -5882,7 +6030,7 @@
|
|
|
5882
6030
|
debugLevel: DEFAULT_DEBUGLEVEL, // 0:quiet, 1:errors, 2:warnings, 3:info, 4:verbose
|
|
5883
6031
|
header: null, // Show/hide header (pass bool or string)
|
|
5884
6032
|
// headerHeightPx: ROW_HEIGHT,
|
|
5885
|
-
rowHeightPx:
|
|
6033
|
+
rowHeightPx: DEFAULT_ROW_HEIGHT,
|
|
5886
6034
|
iconMap: "bootstrap",
|
|
5887
6035
|
columns: null,
|
|
5888
6036
|
types: null,
|
|
@@ -5967,10 +6115,15 @@
|
|
|
5967
6115
|
if (!this.element.getAttribute("tabindex")) {
|
|
5968
6116
|
this.element.tabIndex = 0;
|
|
5969
6117
|
}
|
|
6118
|
+
if (opts.rowHeightPx !== DEFAULT_ROW_HEIGHT) {
|
|
6119
|
+
this.element.style.setProperty("--wb-row-outer-height", opts.rowHeightPx + "px");
|
|
6120
|
+
this.element.style.setProperty("--wb-row-inner-height", opts.rowHeightPx - 2 + "px");
|
|
6121
|
+
}
|
|
5970
6122
|
// Attach tree instance to <div>
|
|
5971
6123
|
this.element._wb_tree = this;
|
|
5972
6124
|
// Create header markup, or take it from the existing html
|
|
5973
|
-
this.headerElement =
|
|
6125
|
+
this.headerElement =
|
|
6126
|
+
this.element.querySelector("div.wb-header");
|
|
5974
6127
|
const wantHeader = opts.header == null ? this.columns.length > 1 : !!opts.header;
|
|
5975
6128
|
if (this.headerElement) {
|
|
5976
6129
|
// User existing header markup to define `this.columns`
|
|
@@ -6007,8 +6160,10 @@
|
|
|
6007
6160
|
<div class="wb-node-list"></div>
|
|
6008
6161
|
</div>`;
|
|
6009
6162
|
this.listContainerElement = this.element.querySelector("div.wb-list-container");
|
|
6010
|
-
this.nodeListElement =
|
|
6011
|
-
|
|
6163
|
+
this.nodeListElement =
|
|
6164
|
+
this.listContainerElement.querySelector("div.wb-node-list");
|
|
6165
|
+
this.headerElement =
|
|
6166
|
+
this.element.querySelector("div.wb-header");
|
|
6012
6167
|
this.element.classList.toggle("wb-grid", this.columns.length > 1);
|
|
6013
6168
|
this._initExtensions();
|
|
6014
6169
|
// --- apply initial options
|
|
@@ -6065,6 +6220,16 @@
|
|
|
6065
6220
|
this.update(ChangeType.resize);
|
|
6066
6221
|
});
|
|
6067
6222
|
this.resizeObserver.observe(this.element);
|
|
6223
|
+
onEvent(this.element, "click", ".wb-button,.wb-col-icon", (e) => {
|
|
6224
|
+
var _a, _b;
|
|
6225
|
+
const info = Wunderbaum.getEventInfo(e);
|
|
6226
|
+
const command = (_b = (_a = e.target) === null || _a === void 0 ? void 0 : _a.dataset) === null || _b === void 0 ? void 0 : _b.command;
|
|
6227
|
+
this._callEvent("buttonClick", {
|
|
6228
|
+
event: e,
|
|
6229
|
+
info: info,
|
|
6230
|
+
command: command,
|
|
6231
|
+
});
|
|
6232
|
+
});
|
|
6068
6233
|
onEvent(this.nodeListElement, "click", "div.wb-row", (e) => {
|
|
6069
6234
|
const info = Wunderbaum.getEventInfo(e);
|
|
6070
6235
|
const node = info.node;
|
|
@@ -6363,31 +6528,33 @@
|
|
|
6363
6528
|
}
|
|
6364
6529
|
/** Return the topmost visible node in the viewport. */
|
|
6365
6530
|
getTopmostVpNode(complete = true) {
|
|
6531
|
+
const rowHeight = this.options.rowHeightPx;
|
|
6366
6532
|
const gracePx = 1; // ignore subpixel scrolling
|
|
6367
6533
|
const scrollParent = this.element;
|
|
6368
6534
|
// const headerHeight = this.headerElement.clientHeight; // May be 0
|
|
6369
6535
|
const scrollTop = scrollParent.scrollTop; // + headerHeight;
|
|
6370
6536
|
let topIdx;
|
|
6371
6537
|
if (complete) {
|
|
6372
|
-
topIdx = Math.ceil((scrollTop - gracePx) /
|
|
6538
|
+
topIdx = Math.ceil((scrollTop - gracePx) / rowHeight);
|
|
6373
6539
|
}
|
|
6374
6540
|
else {
|
|
6375
|
-
topIdx = Math.floor(scrollTop /
|
|
6541
|
+
topIdx = Math.floor(scrollTop / rowHeight);
|
|
6376
6542
|
}
|
|
6377
6543
|
return this._getNodeByRowIdx(topIdx);
|
|
6378
6544
|
}
|
|
6379
6545
|
/** Return the lowest visible node in the viewport. */
|
|
6380
6546
|
getLowestVpNode(complete = true) {
|
|
6547
|
+
const rowHeight = this.options.rowHeightPx;
|
|
6381
6548
|
const scrollParent = this.element;
|
|
6382
6549
|
const headerHeight = this.headerElement.clientHeight; // May be 0
|
|
6383
6550
|
const scrollTop = scrollParent.scrollTop;
|
|
6384
6551
|
const clientHeight = scrollParent.clientHeight - headerHeight;
|
|
6385
6552
|
let bottomIdx;
|
|
6386
6553
|
if (complete) {
|
|
6387
|
-
bottomIdx = Math.floor((scrollTop + clientHeight) /
|
|
6554
|
+
bottomIdx = Math.floor((scrollTop + clientHeight) / rowHeight) - 1;
|
|
6388
6555
|
}
|
|
6389
6556
|
else {
|
|
6390
|
-
bottomIdx = Math.ceil((scrollTop + clientHeight) /
|
|
6557
|
+
bottomIdx = Math.ceil((scrollTop + clientHeight) / rowHeight) - 1;
|
|
6391
6558
|
}
|
|
6392
6559
|
bottomIdx = Math.min(bottomIdx, this.count(true) - 1);
|
|
6393
6560
|
return this._getNodeByRowIdx(bottomIdx);
|
|
@@ -6645,7 +6812,7 @@
|
|
|
6645
6812
|
}
|
|
6646
6813
|
/** Run code, but defer rendering of viewport until done.
|
|
6647
6814
|
*
|
|
6648
|
-
* ```
|
|
6815
|
+
* ```js
|
|
6649
6816
|
* tree.runWithDeferredUpdate(() => {
|
|
6650
6817
|
* return someFuncThatWouldUpdateManyNodes();
|
|
6651
6818
|
* });
|
|
@@ -6819,8 +6986,9 @@
|
|
|
6819
6986
|
* @param includeHidden Not yet implemented
|
|
6820
6987
|
*/
|
|
6821
6988
|
findRelatedNode(node, where, includeHidden = false) {
|
|
6989
|
+
const rowHeight = this.options.rowHeightPx;
|
|
6822
6990
|
let res = null;
|
|
6823
|
-
const pageSize = Math.floor(this.listContainerElement.clientHeight /
|
|
6991
|
+
const pageSize = Math.floor(this.listContainerElement.clientHeight / rowHeight);
|
|
6824
6992
|
switch (where) {
|
|
6825
6993
|
case "parent":
|
|
6826
6994
|
if (node.parent && node.parent.parent) {
|
|
@@ -7123,13 +7291,17 @@
|
|
|
7123
7291
|
console.warn(this.toString(), ...args); // eslint-disable-line no-console
|
|
7124
7292
|
}
|
|
7125
7293
|
}
|
|
7126
|
-
/** Reset column widths to default. */
|
|
7294
|
+
/** Reset column widths to default. @since 0.10.0 */
|
|
7127
7295
|
resetColumns() {
|
|
7128
7296
|
this.columns.forEach((col) => {
|
|
7129
7297
|
delete col.customWidthPx;
|
|
7130
7298
|
});
|
|
7131
7299
|
this.update(ChangeType.colStructure);
|
|
7132
7300
|
}
|
|
7301
|
+
// /** Renumber nodes `_nativeIndex`. @see {@link WunderbaumNode.resetNativeChildOrder} */
|
|
7302
|
+
// resetNativeChildOrder(options?: ResetOrderOptions) {
|
|
7303
|
+
// this.root.resetNativeChildOrder(options);
|
|
7304
|
+
// }
|
|
7133
7305
|
/**
|
|
7134
7306
|
* Make sure that this node is vertically scrolled into the viewport.
|
|
7135
7307
|
*
|
|
@@ -7139,6 +7311,7 @@
|
|
|
7139
7311
|
scrollTo(nodeOrOpts) {
|
|
7140
7312
|
const PADDING = 2; // leave some pixels between viewport bounds
|
|
7141
7313
|
let node;
|
|
7314
|
+
// WunderbaumNode;
|
|
7142
7315
|
let options;
|
|
7143
7316
|
if (nodeOrOpts instanceof WunderbaumNode) {
|
|
7144
7317
|
node = nodeOrOpts;
|
|
@@ -7148,14 +7321,15 @@
|
|
|
7148
7321
|
node = options.node;
|
|
7149
7322
|
}
|
|
7150
7323
|
assert(node && node._rowIdx != null, `Invalid node: ${node}`);
|
|
7324
|
+
const rowHeight = this.options.rowHeightPx;
|
|
7151
7325
|
const scrollParent = this.element;
|
|
7152
7326
|
const headerHeight = this.headerElement.clientHeight; // May be 0
|
|
7153
7327
|
const scrollTop = scrollParent.scrollTop;
|
|
7154
7328
|
const vpHeight = scrollParent.clientHeight;
|
|
7155
|
-
const rowTop = node._rowIdx *
|
|
7329
|
+
const rowTop = node._rowIdx * rowHeight + headerHeight;
|
|
7156
7330
|
const vpTop = headerHeight;
|
|
7157
7331
|
const vpRowTop = rowTop - scrollTop;
|
|
7158
|
-
const vpRowBottom = vpRowTop +
|
|
7332
|
+
const vpRowBottom = vpRowTop + rowHeight;
|
|
7159
7333
|
const topNode = options === null || options === void 0 ? void 0 : options.topNode;
|
|
7160
7334
|
// this.log( `scrollTo(${node.title}), vpTop:${vpTop}px, scrollTop:${scrollTop}, vpHeight:${vpHeight}, rowTop:${rowTop}, vpRowTop:${vpRowTop}`, nodeOrOpts , options);
|
|
7161
7335
|
let newScrollTop = null;
|
|
@@ -7164,7 +7338,7 @@
|
|
|
7164
7338
|
else {
|
|
7165
7339
|
// Node is below viewport
|
|
7166
7340
|
// this.log("Below viewport");
|
|
7167
|
-
newScrollTop = rowTop +
|
|
7341
|
+
newScrollTop = rowTop + rowHeight - vpHeight + PADDING; // leave some pixels between viewport bounds
|
|
7168
7342
|
}
|
|
7169
7343
|
}
|
|
7170
7344
|
else {
|
|
@@ -7444,6 +7618,14 @@
|
|
|
7444
7618
|
sortChildren(cmp = nodeTitleSorter, deep = false) {
|
|
7445
7619
|
this.root.sortChildren(cmp, deep);
|
|
7446
7620
|
}
|
|
7621
|
+
/**
|
|
7622
|
+
* Convenience method to implement column sorting.
|
|
7623
|
+
* @see {@link WunderbaumNode.sortByProperty}.
|
|
7624
|
+
* @since 0.11.0
|
|
7625
|
+
*/
|
|
7626
|
+
sortByProperty(options) {
|
|
7627
|
+
this.root.sortByProperty(options);
|
|
7628
|
+
}
|
|
7447
7629
|
/** Convert tree to an array of plain objects.
|
|
7448
7630
|
*
|
|
7449
7631
|
* @param callback is called for every node, in order to allow
|
|
@@ -7557,6 +7739,11 @@
|
|
|
7557
7739
|
// }
|
|
7558
7740
|
return modified;
|
|
7559
7741
|
}
|
|
7742
|
+
_insertIcon(icon, elem) {
|
|
7743
|
+
const iconElem = document.createElement("i");
|
|
7744
|
+
iconElem.className = icon;
|
|
7745
|
+
elem.appendChild(iconElem);
|
|
7746
|
+
}
|
|
7560
7747
|
/** Create/update header markup from `this.columns` definition.
|
|
7561
7748
|
* @internal
|
|
7562
7749
|
*/
|
|
@@ -7567,6 +7754,7 @@
|
|
|
7567
7754
|
if (!wantHeader) {
|
|
7568
7755
|
return;
|
|
7569
7756
|
}
|
|
7757
|
+
const iconMap = this.iconMap;
|
|
7570
7758
|
const colCount = this.columns.length;
|
|
7571
7759
|
const headerRow = this.headerElement.querySelector(".wb-row");
|
|
7572
7760
|
assert(headerRow, "Expected a row in header element");
|
|
@@ -7585,23 +7773,54 @@
|
|
|
7585
7773
|
else {
|
|
7586
7774
|
col.classes ? colElem.classList.add(...col.classes.split(" ")) : 0;
|
|
7587
7775
|
}
|
|
7588
|
-
|
|
7776
|
+
// Add tooltip to column title
|
|
7589
7777
|
let tooltip = "";
|
|
7590
7778
|
if (col.tooltip) {
|
|
7591
7779
|
tooltip = escapeTooltip(col.tooltip);
|
|
7592
7780
|
tooltip = ` title="${tooltip}"`;
|
|
7593
7781
|
}
|
|
7594
|
-
|
|
7782
|
+
// Add column header icons
|
|
7783
|
+
let addMarkup = "";
|
|
7784
|
+
// NOTE: we use CSS float: right to align icons, so they must be added in
|
|
7785
|
+
// reverse order
|
|
7786
|
+
if (toBool(col.menu, this.options.columnsMenu, false)) {
|
|
7787
|
+
const iconClass = "wb-col-icon-menu " + iconMap.colMenu;
|
|
7788
|
+
const icon = `<i data-command=menu class="wb-col-icon ${iconClass}"></i>`;
|
|
7789
|
+
addMarkup += icon;
|
|
7790
|
+
}
|
|
7791
|
+
if (toBool(col.sortable, this.options.columnsSortable, false)) {
|
|
7792
|
+
let iconClass = "wb-col-icon-sort " + iconMap.colSortable;
|
|
7793
|
+
if (col.sortOrder) {
|
|
7794
|
+
iconClass += `wb-col-sort-${col.sortOrder}`;
|
|
7795
|
+
iconClass +=
|
|
7796
|
+
col.sortOrder === "asc" ? iconMap.colSortAsc : iconMap.colSortDesc;
|
|
7797
|
+
}
|
|
7798
|
+
const icon = `<i data-command=sort class="wb-col-icon ${iconClass}"></i>`;
|
|
7799
|
+
addMarkup += icon;
|
|
7800
|
+
}
|
|
7801
|
+
if (toBool(col.filterable, this.options.columnsFilterable, false)) {
|
|
7802
|
+
colElem.classList.toggle("wb-col-filter", !!col.filterActive);
|
|
7803
|
+
let iconClass = "wb-col-icon-filter " + iconMap.colFilter;
|
|
7804
|
+
if (col.filterActive) {
|
|
7805
|
+
iconClass += iconMap.colFilterActive;
|
|
7806
|
+
}
|
|
7807
|
+
const icon = `<i data-command=filter class="wb-col-icon ${iconClass}"></i>`;
|
|
7808
|
+
addMarkup += icon;
|
|
7809
|
+
}
|
|
7810
|
+
// Add resizer to all but the last column
|
|
7595
7811
|
if (i < colCount - 1) {
|
|
7596
|
-
if (toBool(col.resizable, this.options.
|
|
7597
|
-
|
|
7812
|
+
if (toBool(col.resizable, this.options.columnsResizable, false)) {
|
|
7813
|
+
addMarkup +=
|
|
7598
7814
|
'<span class="wb-col-resizer wb-col-resizer-active"></span>';
|
|
7599
7815
|
}
|
|
7600
7816
|
else {
|
|
7601
|
-
|
|
7817
|
+
addMarkup += '<span class="wb-col-resizer"></span>';
|
|
7602
7818
|
}
|
|
7603
7819
|
}
|
|
7604
|
-
|
|
7820
|
+
// Create column header
|
|
7821
|
+
const title = escapeHtml(col.title || col.id);
|
|
7822
|
+
colElem.innerHTML = `<span class="wb-col-title"${tooltip}>${title}</span>${addMarkup}`;
|
|
7823
|
+
// Highlight active column
|
|
7605
7824
|
if (this.isCellNav()) {
|
|
7606
7825
|
colElem.classList.toggle("wb-active", i === this.activeColIdx);
|
|
7607
7826
|
}
|
|
@@ -7732,19 +7951,19 @@
|
|
|
7732
7951
|
// this.log("_updateRows", opts)
|
|
7733
7952
|
options = Object.assign({ newNodesOnly: false }, options);
|
|
7734
7953
|
const newNodesOnly = !!options.newNodesOnly;
|
|
7735
|
-
const
|
|
7736
|
-
const
|
|
7954
|
+
const rowHeight = this.options.rowHeightPx;
|
|
7955
|
+
const vpHeight = this.element.clientHeight;
|
|
7737
7956
|
const prefetch = RENDER_MAX_PREFETCH;
|
|
7738
7957
|
// const grace_prefetch = RENDER_MAX_PREFETCH - RENDER_MIN_PREFETCH;
|
|
7739
7958
|
const ofs = this.element.scrollTop;
|
|
7740
|
-
let startIdx = Math.max(0, ofs /
|
|
7959
|
+
let startIdx = Math.max(0, ofs / rowHeight - prefetch);
|
|
7741
7960
|
startIdx = Math.floor(startIdx);
|
|
7742
7961
|
// Make sure start is always even, so the alternating row colors don't
|
|
7743
7962
|
// change when scrolling:
|
|
7744
7963
|
if (startIdx % 2) {
|
|
7745
7964
|
startIdx--;
|
|
7746
7965
|
}
|
|
7747
|
-
let endIdx = Math.max(0, (ofs +
|
|
7966
|
+
let endIdx = Math.max(0, (ofs + vpHeight) / rowHeight + prefetch);
|
|
7748
7967
|
endIdx = Math.ceil(endIdx);
|
|
7749
7968
|
// this.debug("render", opts);
|
|
7750
7969
|
const obsoleteNodes = new Set();
|
|
@@ -7773,21 +7992,21 @@
|
|
|
7773
7992
|
else if (rowDiv && newNodesOnly) {
|
|
7774
7993
|
obsoleteNodes.delete(node);
|
|
7775
7994
|
// no need to update existing node markup
|
|
7776
|
-
rowDiv.style.top = idx *
|
|
7995
|
+
rowDiv.style.top = idx * rowHeight + "px";
|
|
7777
7996
|
prevElem = rowDiv;
|
|
7778
7997
|
}
|
|
7779
7998
|
else {
|
|
7780
7999
|
obsoleteNodes.delete(node);
|
|
7781
8000
|
// Create new markup
|
|
7782
8001
|
if (rowDiv) {
|
|
7783
|
-
rowDiv.style.top = idx *
|
|
8002
|
+
rowDiv.style.top = idx * rowHeight + "px";
|
|
7784
8003
|
}
|
|
7785
8004
|
node._render({ top: top, after: prevElem });
|
|
7786
8005
|
// node.log("render", top, prevElem, "=>", node._rowElem);
|
|
7787
8006
|
prevElem = node._rowElem;
|
|
7788
8007
|
}
|
|
7789
8008
|
idx++;
|
|
7790
|
-
top +=
|
|
8009
|
+
top += rowHeight;
|
|
7791
8010
|
});
|
|
7792
8011
|
this.treeRowCount = idx;
|
|
7793
8012
|
for (const n of obsoleteNodes) {
|
|
@@ -8018,6 +8237,7 @@
|
|
|
8018
8237
|
/**
|
|
8019
8238
|
* Return the number of nodes that match the current filter.
|
|
8020
8239
|
* @see {@link Wunderbaum.filterNodes}
|
|
8240
|
+
* @since 0.9.0
|
|
8021
8241
|
*/
|
|
8022
8242
|
countMatches() {
|
|
8023
8243
|
return this.extensions.filter.countMatches();
|
|
@@ -8050,7 +8270,7 @@
|
|
|
8050
8270
|
}
|
|
8051
8271
|
Wunderbaum.sequence = 0;
|
|
8052
8272
|
/** Wunderbaum release version number "MAJOR.MINOR.PATCH". */
|
|
8053
|
-
Wunderbaum.version = "v0.
|
|
8273
|
+
Wunderbaum.version = "v0.11.1"; // Set to semver by 'grunt release'
|
|
8054
8274
|
/** Expose some useful methods of the util.ts module as `Wunderbaum.util`. */
|
|
8055
8275
|
Wunderbaum.util = util;
|
|
8056
8276
|
|