wunderbaum 0.11.0 → 0.12.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/README.md +4 -5
- package/dist/wunderbaum.css +10 -10
- package/dist/wunderbaum.css.map +1 -1
- package/dist/wunderbaum.d.ts +52 -12
- package/dist/wunderbaum.esm.js +712 -626
- package/dist/wunderbaum.esm.min.js +24 -24
- package/dist/wunderbaum.esm.min.js.map +1 -1
- package/dist/wunderbaum.umd.js +712 -626
- package/dist/wunderbaum.umd.min.js +30 -30
- package/dist/wunderbaum.umd.min.js.map +1 -1
- package/package.json +7 -5
- package/src/common.ts +1 -1
- package/src/types.ts +32 -8
- package/src/util.ts +12 -0
- package/src/wb_ext_dnd.ts +9 -4
- package/src/wb_ext_edit.ts +4 -0
- package/src/wb_node.ts +108 -40
- package/src/wunderbaum.scss +18 -16
- package/src/wunderbaum.ts +38 -19
package/dist/wunderbaum.esm.js
CHANGED
|
@@ -288,7 +288,7 @@ function throttle(func, wait = 0, options = {}) {
|
|
|
288
288
|
/*!
|
|
289
289
|
* Wunderbaum - util
|
|
290
290
|
* Copyright (c) 2021-2024, Martin Wendt. Released under the MIT license.
|
|
291
|
-
* v0.
|
|
291
|
+
* v0.12.0, Sun, 12 Jan 2025 10:51:41 GMT (https://github.com/mar10/wunderbaum)
|
|
292
292
|
*/
|
|
293
293
|
/** @module util */
|
|
294
294
|
/** Readable names for `MouseEvent.button` */
|
|
@@ -980,6 +980,15 @@ function toBool(...boolDefaults) {
|
|
|
980
980
|
}
|
|
981
981
|
throw new Error("No default boolean value provided");
|
|
982
982
|
}
|
|
983
|
+
/**
|
|
984
|
+
* Return `val` unless `val` is a number in which case we convert to boolean.
|
|
985
|
+
* This is useful when a boolean value is stored as a 0/1 (e.g. in JSON) and
|
|
986
|
+
* we still want to maintain string values. null and undefined are returned as
|
|
987
|
+
* is. E.g. `checkbox` may be boolean or 'radio'.
|
|
988
|
+
*/
|
|
989
|
+
function intToBool(val) {
|
|
990
|
+
return typeof val === "number" ? !!val : val;
|
|
991
|
+
}
|
|
983
992
|
// /** Check if a string is contained in an Array or Set. */
|
|
984
993
|
// export function isAnyOf(s: string, items: Array<string>|Set<string>): boolean {
|
|
985
994
|
// return Array.prototype.includes.call(items, s)
|
|
@@ -1108,6 +1117,7 @@ var util = /*#__PURE__*/Object.freeze({
|
|
|
1108
1117
|
extractHtmlText: extractHtmlText,
|
|
1109
1118
|
getOption: getOption,
|
|
1110
1119
|
getValueFromElem: getValueFromElem,
|
|
1120
|
+
intToBool: intToBool,
|
|
1111
1121
|
isArray: isArray,
|
|
1112
1122
|
isEmptyObject: isEmptyObject,
|
|
1113
1123
|
isFunction: isFunction,
|
|
@@ -1132,7 +1142,7 @@ var util = /*#__PURE__*/Object.freeze({
|
|
|
1132
1142
|
/*!
|
|
1133
1143
|
* Wunderbaum - types
|
|
1134
1144
|
* Copyright (c) 2021-2024, Martin Wendt. Released under the MIT license.
|
|
1135
|
-
* v0.
|
|
1145
|
+
* v0.12.0, Sun, 12 Jan 2025 10:51:41 GMT (https://github.com/mar10/wunderbaum)
|
|
1136
1146
|
*/
|
|
1137
1147
|
/**
|
|
1138
1148
|
* Possible values for {@link WunderbaumNode.update} and {@link Wunderbaum.update}.
|
|
@@ -1196,7 +1206,7 @@ var NavModeEnum;
|
|
|
1196
1206
|
/*!
|
|
1197
1207
|
* Wunderbaum - wb_extension_base
|
|
1198
1208
|
* Copyright (c) 2021-2024, Martin Wendt. Released under the MIT license.
|
|
1199
|
-
* v0.
|
|
1209
|
+
* v0.12.0, Sun, 12 Jan 2025 10:51:41 GMT (https://github.com/mar10/wunderbaum)
|
|
1200
1210
|
*/
|
|
1201
1211
|
class WunderbaumExtension {
|
|
1202
1212
|
constructor(tree, id, defaults) {
|
|
@@ -1255,7 +1265,7 @@ class WunderbaumExtension {
|
|
|
1255
1265
|
/*!
|
|
1256
1266
|
* Wunderbaum - ext-filter
|
|
1257
1267
|
* Copyright (c) 2021-2024, Martin Wendt. Released under the MIT license.
|
|
1258
|
-
* v0.
|
|
1268
|
+
* v0.12.0, Sun, 12 Jan 2025 10:51:41 GMT (https://github.com/mar10/wunderbaum)
|
|
1259
1269
|
*/
|
|
1260
1270
|
const START_MARKER = "\uFFF7";
|
|
1261
1271
|
const END_MARKER = "\uFFF8";
|
|
@@ -1580,7 +1590,7 @@ function _markFuzzyMatchedChars(text, matches, escapeTitles = true) {
|
|
|
1580
1590
|
/*!
|
|
1581
1591
|
* Wunderbaum - ext-keynav
|
|
1582
1592
|
* Copyright (c) 2021-2024, Martin Wendt. Released under the MIT license.
|
|
1583
|
-
* v0.
|
|
1593
|
+
* v0.12.0, Sun, 12 Jan 2025 10:51:41 GMT (https://github.com/mar10/wunderbaum)
|
|
1584
1594
|
*/
|
|
1585
1595
|
const QUICKSEARCH_DELAY = 500;
|
|
1586
1596
|
class KeynavExtension extends WunderbaumExtension {
|
|
@@ -1944,7 +1954,7 @@ class KeynavExtension extends WunderbaumExtension {
|
|
|
1944
1954
|
/*!
|
|
1945
1955
|
* Wunderbaum - ext-logger
|
|
1946
1956
|
* Copyright (c) 2021-2024, Martin Wendt. Released under the MIT license.
|
|
1947
|
-
* v0.
|
|
1957
|
+
* v0.12.0, Sun, 12 Jan 2025 10:51:41 GMT (https://github.com/mar10/wunderbaum)
|
|
1948
1958
|
*/
|
|
1949
1959
|
class LoggerExtension extends WunderbaumExtension {
|
|
1950
1960
|
constructor(tree) {
|
|
@@ -1984,410 +1994,70 @@ class LoggerExtension extends WunderbaumExtension {
|
|
|
1984
1994
|
}
|
|
1985
1995
|
|
|
1986
1996
|
/*!
|
|
1987
|
-
* Wunderbaum -
|
|
1997
|
+
* Wunderbaum - ext-dnd
|
|
1988
1998
|
* Copyright (c) 2021-2024, Martin Wendt. Released under the MIT license.
|
|
1989
|
-
* v0.
|
|
1990
|
-
*/
|
|
1991
|
-
const DEFAULT_DEBUGLEVEL = 3; // Replaced by rollup script
|
|
1992
|
-
/**
|
|
1993
|
-
* Fixed height of a row in pixel. Must match the SCSS variable `$row-outer-height`.
|
|
1994
|
-
*/
|
|
1995
|
-
const ROW_HEIGHT = 22;
|
|
1996
|
-
/**
|
|
1997
|
-
* Fixed width of node icons in pixel. Must match the SCSS variable `$icon-outer-width`.
|
|
1998
|
-
*/
|
|
1999
|
-
const ICON_WIDTH = 20;
|
|
2000
|
-
/**
|
|
2001
|
-
* Adjust the width of the title span, so overflow ellipsis work.
|
|
2002
|
-
* (2 x `$col-padding-x` + 3px rounding errors).
|
|
2003
|
-
*/
|
|
2004
|
-
const TITLE_SPAN_PAD_Y = 7;
|
|
2005
|
-
/** Render row markup for N nodes above and below the visible viewport. */
|
|
2006
|
-
const RENDER_MAX_PREFETCH = 5;
|
|
2007
|
-
/** Minimum column width if not set otherwise. */
|
|
2008
|
-
const DEFAULT_MIN_COL_WIDTH = 4;
|
|
2009
|
-
/** Regular expression to detect if a string describes an image URL (in contrast
|
|
2010
|
-
* to a class name). Strings are considered image urls if they contain '.' or '/'.
|
|
2011
|
-
*/
|
|
2012
|
-
const TEST_IMG = new RegExp(/\.|\//);
|
|
2013
|
-
// export const RECURSIVE_REQUEST_ERROR = "$recursive_request";
|
|
2014
|
-
// export const INVALID_REQUEST_TARGET_ERROR = "$request_target_invalid";
|
|
2015
|
-
/**
|
|
2016
|
-
* Default node icons.
|
|
2017
|
-
* Requires bootstrap icons https://icons.getbootstrap.com
|
|
2018
|
-
*/
|
|
2019
|
-
const iconMaps = {
|
|
2020
|
-
bootstrap: {
|
|
2021
|
-
error: "bi bi-exclamation-triangle",
|
|
2022
|
-
// loading: "bi bi-hourglass-split wb-busy",
|
|
2023
|
-
loading: "bi bi-chevron-right wb-busy",
|
|
2024
|
-
// loading: "bi bi-arrow-repeat wb-spin",
|
|
2025
|
-
// loading: '<div class="spinner-border spinner-border-sm" role="status"> <span class="visually-hidden">Loading...</span> </div>',
|
|
2026
|
-
// noData: "bi bi-search",
|
|
2027
|
-
noData: "bi bi-question-circle",
|
|
2028
|
-
expanderExpanded: "bi bi-chevron-down",
|
|
2029
|
-
// expanderExpanded: "bi bi-dash-square",
|
|
2030
|
-
expanderCollapsed: "bi bi-chevron-right",
|
|
2031
|
-
// expanderCollapsed: "bi bi-plus-square",
|
|
2032
|
-
expanderLazy: "bi bi-chevron-right wb-helper-lazy-expander",
|
|
2033
|
-
// expanderLazy: "bi bi-chevron-bar-right",
|
|
2034
|
-
checkChecked: "bi bi-check-square",
|
|
2035
|
-
checkUnchecked: "bi bi-square",
|
|
2036
|
-
checkUnknown: "bi bi-dash-square-dotted",
|
|
2037
|
-
radioChecked: "bi bi-circle-fill",
|
|
2038
|
-
radioUnchecked: "bi bi-circle",
|
|
2039
|
-
radioUnknown: "bi bi-record-circle",
|
|
2040
|
-
folder: "bi bi-folder2",
|
|
2041
|
-
folderOpen: "bi bi-folder2-open",
|
|
2042
|
-
folderLazy: "bi bi-folder-symlink",
|
|
2043
|
-
doc: "bi bi-file-earmark",
|
|
2044
|
-
colSortable: "bi bi-chevron-expand",
|
|
2045
|
-
// colSortable: "bi bi-arrow-down-up",
|
|
2046
|
-
// colSortAsc: "bi bi-chevron-down",
|
|
2047
|
-
// colSortDesc: "bi bi-chevron-up",
|
|
2048
|
-
colSortAsc: "bi bi-arrow-down",
|
|
2049
|
-
colSortDesc: "bi bi-arrow-up",
|
|
2050
|
-
colFilter: "bi bi-filter-circle",
|
|
2051
|
-
colFilterActive: "bi bi-filter-circle-fill wb-helper-invalid",
|
|
2052
|
-
colMenu: "bi bi-three-dots-vertical",
|
|
2053
|
-
},
|
|
2054
|
-
fontawesome6: {
|
|
2055
|
-
error: "fa-solid fa-triangle-exclamation",
|
|
2056
|
-
loading: "fa-solid fa-chevron-right fa-beat",
|
|
2057
|
-
noData: "fa-solid fa-circle-question",
|
|
2058
|
-
expanderExpanded: "fa-solid fa-chevron-down",
|
|
2059
|
-
expanderCollapsed: "fa-solid fa-chevron-right",
|
|
2060
|
-
expanderLazy: "fa-solid fa-chevron-right wb-helper-lazy-expander",
|
|
2061
|
-
checkChecked: "fa-regular fa-square-check",
|
|
2062
|
-
checkUnchecked: "fa-regular fa-square",
|
|
2063
|
-
checkUnknown: "fa-regular fa-square-minus",
|
|
2064
|
-
radioChecked: "fa-solid fa-circle",
|
|
2065
|
-
radioUnchecked: "fa-regular fa-circle",
|
|
2066
|
-
radioUnknown: "fa-regular fa-circle-question",
|
|
2067
|
-
folder: "fa-solid fa-folder-closed",
|
|
2068
|
-
folderOpen: "fa-regular fa-folder-open",
|
|
2069
|
-
folderLazy: "fa-solid fa-folder-plus",
|
|
2070
|
-
doc: "fa-regular fa-file",
|
|
2071
|
-
colSortable: "fa-solid fa-fw fa-sort",
|
|
2072
|
-
colSortAsc: "fa-solid fa-fw fa-sort-up",
|
|
2073
|
-
colSortDesc: "fa-solid fa-fw fa-sort-down",
|
|
2074
|
-
colFilter: "fa-solid fa-fw fa-filter",
|
|
2075
|
-
colFilterActive: "fa-solid fa-fw fa-filter wb-helper-invalid",
|
|
2076
|
-
colMenu: "fa-solid fa-fw fa-ellipsis-v",
|
|
2077
|
-
},
|
|
2078
|
-
};
|
|
2079
|
-
/** Dict keys that are evaluated by source loader (others are added to `tree.data` instead). */
|
|
2080
|
-
const RESERVED_TREE_SOURCE_KEYS = new Set([
|
|
2081
|
-
"_format", // reserved for future use
|
|
2082
|
-
"_keyMap", // Used for compressed data format
|
|
2083
|
-
"_positional", // Used for compressed data format
|
|
2084
|
-
"_typeList", // Used for compressed data format @deprecated
|
|
2085
|
-
"_valueMap", // Used for compressed data format
|
|
2086
|
-
"_version", // reserved for future use
|
|
2087
|
-
"children",
|
|
2088
|
-
"columns",
|
|
2089
|
-
"types",
|
|
2090
|
-
]);
|
|
2091
|
-
// /** Key codes that trigger grid navigation, even when inside an input element. */
|
|
2092
|
-
// export const INPUT_BREAKOUT_KEYS: Set<string> = new Set([
|
|
2093
|
-
// // "ArrowDown",
|
|
2094
|
-
// // "ArrowUp",
|
|
2095
|
-
// "Enter",
|
|
2096
|
-
// "Escape",
|
|
2097
|
-
// ]);
|
|
2098
|
-
/** Map `KeyEvent.key` to navigation action. */
|
|
2099
|
-
const KEY_TO_ACTION_DICT = {
|
|
2100
|
-
" ": "toggleSelect",
|
|
2101
|
-
"+": "expand",
|
|
2102
|
-
Add: "expand",
|
|
2103
|
-
ArrowDown: "down",
|
|
2104
|
-
ArrowLeft: "left",
|
|
2105
|
-
ArrowRight: "right",
|
|
2106
|
-
ArrowUp: "up",
|
|
2107
|
-
Backspace: "parent",
|
|
2108
|
-
"/": "collapseAll",
|
|
2109
|
-
Divide: "collapseAll",
|
|
2110
|
-
End: "lastCol",
|
|
2111
|
-
Home: "firstCol",
|
|
2112
|
-
"Control+End": "last",
|
|
2113
|
-
"Control+Home": "first",
|
|
2114
|
-
"Meta+ArrowDown": "last", // macOs
|
|
2115
|
-
"Meta+ArrowUp": "first", // macOs
|
|
2116
|
-
"*": "expandAll",
|
|
2117
|
-
Multiply: "expandAll",
|
|
2118
|
-
PageDown: "pageDown",
|
|
2119
|
-
PageUp: "pageUp",
|
|
2120
|
-
"-": "collapse",
|
|
2121
|
-
Subtract: "collapse",
|
|
2122
|
-
};
|
|
2123
|
-
/** Return a callback that returns true if the node title matches the string
|
|
2124
|
-
* or regular expression.
|
|
2125
|
-
* @see {@link WunderbaumNode.findAll}
|
|
2126
|
-
*/
|
|
2127
|
-
function makeNodeTitleMatcher(match) {
|
|
2128
|
-
if (match instanceof RegExp) {
|
|
2129
|
-
return function (node) {
|
|
2130
|
-
return match.test(node.title);
|
|
2131
|
-
};
|
|
2132
|
-
}
|
|
2133
|
-
assert(typeof match === "string", `Expected a string or RegExp: ${match}`);
|
|
2134
|
-
// s = escapeRegex(s.toLowerCase());
|
|
2135
|
-
return function (node) {
|
|
2136
|
-
return node.title === match;
|
|
2137
|
-
// console.log("match " + node, node.title.toLowerCase().indexOf(match))
|
|
2138
|
-
// return node.title.toLowerCase().indexOf(match) >= 0;
|
|
2139
|
-
};
|
|
2140
|
-
}
|
|
2141
|
-
/** Return a callback that returns true if the node title starts with a string (case-insensitive). */
|
|
2142
|
-
function makeNodeTitleStartMatcher(s) {
|
|
2143
|
-
s = escapeRegex(s);
|
|
2144
|
-
const reMatch = new RegExp("^" + s, "i");
|
|
2145
|
-
return function (node) {
|
|
2146
|
-
return reMatch.test(node.title);
|
|
2147
|
-
};
|
|
2148
|
-
}
|
|
2149
|
-
/** Compare two nodes by title (case-insensitive). */
|
|
2150
|
-
function nodeTitleSorter(a, b) {
|
|
2151
|
-
const x = a.title.toLowerCase();
|
|
2152
|
-
const y = b.title.toLowerCase();
|
|
2153
|
-
return x === y ? 0 : x > y ? 1 : -1;
|
|
2154
|
-
}
|
|
2155
|
-
/**
|
|
2156
|
-
* Convert 'flat' to 'nested' format.
|
|
2157
|
-
*
|
|
2158
|
-
* Flat node entry format:
|
|
2159
|
-
* [PARENT_ID, [POSITIONAL_ARGS]]
|
|
2160
|
-
* or
|
|
2161
|
-
* [PARENT_ID, [POSITIONAL_ARGS], {KEY_VALUE_ARGS}]
|
|
2162
|
-
*
|
|
2163
|
-
* 1. Parent-referencing list is converted to a list of nested dicts with
|
|
2164
|
-
* optional `children` properties.
|
|
2165
|
-
* 2. `[POSITIONAL_ARGS]` are added as dict attributes.
|
|
1999
|
+
* v0.12.0, Sun, 12 Jan 2025 10:51:41 GMT (https://github.com/mar10/wunderbaum)
|
|
2166
2000
|
*/
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2001
|
+
const nodeMimeType = "application/x-wunderbaum-node";
|
|
2002
|
+
class DndExtension extends WunderbaumExtension {
|
|
2003
|
+
constructor(tree) {
|
|
2004
|
+
super(tree, "dnd", {
|
|
2005
|
+
autoExpandMS: 1500, // Expand nodes after n milliseconds of hovering
|
|
2006
|
+
// dropMarkerInsertOffsetX: -16, // Additional offset for drop-marker with hitMode = "before"/"after"
|
|
2007
|
+
// dropMarkerOffsetX: -24, // Absolute position offset for .fancytree-drop-marker relatively to ..fancytree-title (icon/img near a node accepting drop)
|
|
2008
|
+
// #1021 `document.body` is not available yet
|
|
2009
|
+
// dropMarkerParent: "body", // Root Container used for drop marker (could be a shadow root)
|
|
2010
|
+
multiSource: false, // true: Drag multiple (i.e. selected) nodes. Also a callback() is allowed
|
|
2011
|
+
effectAllowed: "all", // Restrict the possible cursor shapes and modifier operations (can also be set in the dragStart event)
|
|
2012
|
+
dropEffectDefault: "move", // Default dropEffect ('copy', 'link', or 'move') when no modifier is pressed (override in drag, dragOver).
|
|
2013
|
+
guessDropEffect: true, // Calculate from `effectAllowed` and modifier keys)
|
|
2014
|
+
preventForeignNodes: false, // Prevent dropping nodes from different Wunderbaum trees
|
|
2015
|
+
preventLazyParents: true, // Prevent dropping items on unloaded lazy Wunderbaum tree nodes
|
|
2016
|
+
preventNonNodes: false, // Prevent dropping items other than Wunderbaum tree nodes
|
|
2017
|
+
preventRecursion: true, // Prevent dropping nodes on own descendants
|
|
2018
|
+
preventSameParent: false, // Prevent dropping nodes under same direct parent
|
|
2019
|
+
preventVoidMoves: true, // Prevent dropping nodes 'before self', etc. (move only)
|
|
2020
|
+
serializeClipboardData: true, // Serialize node data to dataTransfer object
|
|
2021
|
+
scroll: true, // Enable auto-scrolling while dragging
|
|
2022
|
+
scrollSensitivity: 20, // Active top/bottom margin in pixel
|
|
2023
|
+
// scrollnterval: 50, // Generate event every 50 ms
|
|
2024
|
+
scrollSpeed: 5, // Scroll pixel per 50 ms
|
|
2025
|
+
// setTextTypeJson: false, // Allow dragging of nodes to different IE windows
|
|
2026
|
+
sourceCopyHook: null, // Optional callback passed to `toDict` on dragStart @since 2.38
|
|
2027
|
+
// Events (drag support)
|
|
2028
|
+
dragStart: null, // Callback(sourceNode, data), return true, to enable dnd drag
|
|
2029
|
+
drag: null, // Callback(sourceNode, data)
|
|
2030
|
+
dragEnd: null, // Callback(sourceNode, data)
|
|
2031
|
+
// Events (drop support)
|
|
2032
|
+
dragEnter: null, // Callback(targetNode, data), return true, to enable dnd drop
|
|
2033
|
+
dragOver: null, // Callback(targetNode, data)
|
|
2034
|
+
dragExpand: null, // Callback(targetNode, data), return false to prevent autoExpand
|
|
2035
|
+
drop: null, // Callback(targetNode, data)
|
|
2036
|
+
dragLeave: null, // Callback(targetNode, data)
|
|
2037
|
+
});
|
|
2038
|
+
// public dropMarkerElem?: HTMLElement;
|
|
2039
|
+
this.srcNode = null;
|
|
2040
|
+
this.lastTargetNode = null;
|
|
2041
|
+
this.lastEnterStamp = 0;
|
|
2042
|
+
this.lastAllowedDropRegions = null;
|
|
2043
|
+
this.lastDropEffect = null;
|
|
2044
|
+
this.lastDropRegion = false;
|
|
2045
|
+
this.currentScrollDir = 0;
|
|
2046
|
+
this.applyScrollDirThrottled = throttle(this._applyScrollDir, 50);
|
|
2175
2047
|
}
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
//
|
|
2179
|
-
//
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
const keyToNodeMap = {};
|
|
2190
|
-
const indexToNodeMap = {};
|
|
2191
|
-
const keyAttrName = (_a = longToShort["key"]) !== null && _a !== void 0 ? _a : "key";
|
|
2192
|
-
const childrenAttrName = (_b = longToShort["children"]) !== null && _b !== void 0 ? _b : "children";
|
|
2193
|
-
for (const [index, nodeTuple] of children.entries()) {
|
|
2194
|
-
// Node entry format:
|
|
2195
|
-
// [PARENT_ID, [POSITIONAL_ARGS]]
|
|
2196
|
-
// or
|
|
2197
|
-
// [PARENT_ID, [POSITIONAL_ARGS], {KEY_VALUE_ARGS}]
|
|
2198
|
-
const [parentId, args, kwargs = {}] = nodeTuple;
|
|
2199
|
-
// Free up some memory as we go
|
|
2200
|
-
nodeTuple[1] = null;
|
|
2201
|
-
if (nodeTuple[2] != null) {
|
|
2202
|
-
nodeTuple[2] = null;
|
|
2203
|
-
}
|
|
2204
|
-
// console.log("flatten", parentId, args, kwargs)
|
|
2205
|
-
// We keep `kwargs` as our new node definition. Then we add all positional
|
|
2206
|
-
// values to this object:
|
|
2207
|
-
args.forEach((val, positionalIdx) => {
|
|
2208
|
-
kwargs[positionalShort[positionalIdx]] = val;
|
|
2209
|
-
});
|
|
2210
|
-
// Find the parent node. `null` means 'toplevel'. PARENT_ID may be the numeric
|
|
2211
|
-
// index of the source.children list. If PARENT_ID is a string, we search
|
|
2212
|
-
// a parent with node.key of this value.
|
|
2213
|
-
indexToNodeMap[index] = kwargs;
|
|
2214
|
-
const key = kwargs[keyAttrName];
|
|
2215
|
-
if (key != null) {
|
|
2216
|
-
keyToNodeMap[key] = kwargs;
|
|
2217
|
-
}
|
|
2218
|
-
let parentNode = null;
|
|
2219
|
-
if (parentId === null) ;
|
|
2220
|
-
else if (typeof parentId === "number") {
|
|
2221
|
-
parentNode = indexToNodeMap[parentId];
|
|
2222
|
-
if (parentNode === undefined) {
|
|
2223
|
-
throw new Error(`unflattenSource: Could not find parent node by index: ${parentId}.`);
|
|
2224
|
-
}
|
|
2225
|
-
}
|
|
2226
|
-
else {
|
|
2227
|
-
parentNode = keyToNodeMap[parentId];
|
|
2228
|
-
if (parentNode === undefined) {
|
|
2229
|
-
throw new Error(`unflattenSource: Could not find parent node by key: ${parentId}`);
|
|
2230
|
-
}
|
|
2231
|
-
}
|
|
2232
|
-
if (parentNode) {
|
|
2233
|
-
(_c = parentNode[childrenAttrName]) !== null && _c !== void 0 ? _c : (parentNode[childrenAttrName] = []);
|
|
2234
|
-
parentNode[childrenAttrName].push(kwargs);
|
|
2235
|
-
}
|
|
2236
|
-
else {
|
|
2237
|
-
newChildren.push(kwargs);
|
|
2238
|
-
}
|
|
2239
|
-
}
|
|
2240
|
-
source.children = newChildren;
|
|
2241
|
-
}
|
|
2242
|
-
/**
|
|
2243
|
-
* Decompresses the source data by
|
|
2244
|
-
* - converting from 'flat' to 'nested' format
|
|
2245
|
-
* - expanding short alias names to long names (if defined in _keyMap)
|
|
2246
|
-
* - resolving value indexes to value strings (if defined in _valueMap)
|
|
2247
|
-
*
|
|
2248
|
-
* @param source - The source object to be decompressed.
|
|
2249
|
-
* @returns void
|
|
2250
|
-
*/
|
|
2251
|
-
function decompressSourceData(source) {
|
|
2252
|
-
let { _format, _version = 1, _keyMap, _valueMap } = source;
|
|
2253
|
-
assert(_version === 1, `Expected file version 1 instead of ${_version}`);
|
|
2254
|
-
let longToShort = _keyMap;
|
|
2255
|
-
let shortToLong = {};
|
|
2256
|
-
if (longToShort) {
|
|
2257
|
-
for (const [key, value] of Object.entries(longToShort)) {
|
|
2258
|
-
shortToLong[value] = key;
|
|
2259
|
-
}
|
|
2260
|
-
}
|
|
2261
|
-
// Fallback for old format (pre 0.7.0, using _keyMap in reverse direction)
|
|
2262
|
-
// TODO: raise Error on final 1.x release
|
|
2263
|
-
if (longToShort && longToShort.t) {
|
|
2264
|
-
const msg = `source._keyMap maps from long to short since v0.7.0. Flip key/value!`;
|
|
2265
|
-
console.warn(msg); // eslint-disable-line no-console
|
|
2266
|
-
[longToShort, shortToLong] = [shortToLong, longToShort];
|
|
2267
|
-
}
|
|
2268
|
-
// Fallback for old format (pre 0.7.0, using _typeList instead of _valueMap)
|
|
2269
|
-
// TODO: raise Error on final 1.x release
|
|
2270
|
-
if (source._typeList != null) {
|
|
2271
|
-
const msg = `source._typeList is deprecated since v0.7.0: use source._valueMap: {"type": [...]} instead.`;
|
|
2272
|
-
if (_valueMap != null) {
|
|
2273
|
-
throw new Error(msg);
|
|
2274
|
-
}
|
|
2275
|
-
else {
|
|
2276
|
-
console.warn(msg); // eslint-disable-line no-console
|
|
2277
|
-
_valueMap = { type: source._typeList };
|
|
2278
|
-
delete source._typeList;
|
|
2279
|
-
}
|
|
2280
|
-
}
|
|
2281
|
-
if (_format === "flat") {
|
|
2282
|
-
unflattenSource(source);
|
|
2283
|
-
}
|
|
2284
|
-
delete source._format;
|
|
2285
|
-
delete source._version;
|
|
2286
|
-
delete source._keyMap;
|
|
2287
|
-
delete source._valueMap;
|
|
2288
|
-
delete source._positional;
|
|
2289
|
-
function _iter(childList) {
|
|
2290
|
-
for (const node of childList) {
|
|
2291
|
-
// Iterate over a list of names, because we modify inside the loop
|
|
2292
|
-
// (for ... of ... does not allow this)
|
|
2293
|
-
Object.getOwnPropertyNames(node).forEach((propName) => {
|
|
2294
|
-
const value = node[propName];
|
|
2295
|
-
// Replace short names with long names if defined in _keyMap
|
|
2296
|
-
let longName = propName;
|
|
2297
|
-
if (_keyMap && shortToLong[propName] != null) {
|
|
2298
|
-
longName = shortToLong[propName];
|
|
2299
|
-
if (longName !== propName) {
|
|
2300
|
-
node[longName] = value;
|
|
2301
|
-
delete node[propName];
|
|
2302
|
-
}
|
|
2303
|
-
}
|
|
2304
|
-
// Replace type index with type name if defined in _valueMap
|
|
2305
|
-
if (_valueMap &&
|
|
2306
|
-
typeof value === "number" &&
|
|
2307
|
-
_valueMap[longName] != null) {
|
|
2308
|
-
const newValue = _valueMap[longName][value];
|
|
2309
|
-
if (newValue == null) {
|
|
2310
|
-
throw new Error(`Expected valueMap[${longName}][${value}] entry in [${_valueMap[longName]}]`);
|
|
2311
|
-
}
|
|
2312
|
-
node[longName] = newValue;
|
|
2313
|
-
}
|
|
2314
|
-
});
|
|
2315
|
-
// Recursion
|
|
2316
|
-
if (node.children) {
|
|
2317
|
-
_iter(node.children);
|
|
2318
|
-
}
|
|
2319
|
-
}
|
|
2320
|
-
}
|
|
2321
|
-
if (_keyMap || _valueMap) {
|
|
2322
|
-
_iter(source.children);
|
|
2323
|
-
}
|
|
2324
|
-
}
|
|
2325
|
-
|
|
2326
|
-
/*!
|
|
2327
|
-
* Wunderbaum - ext-dnd
|
|
2328
|
-
* Copyright (c) 2021-2024, Martin Wendt. Released under the MIT license.
|
|
2329
|
-
* v0.11.0, Sun, 04 Aug 2024 15:35:53 GMT (https://github.com/mar10/wunderbaum)
|
|
2330
|
-
*/
|
|
2331
|
-
const nodeMimeType = "application/x-wunderbaum-node";
|
|
2332
|
-
class DndExtension extends WunderbaumExtension {
|
|
2333
|
-
constructor(tree) {
|
|
2334
|
-
super(tree, "dnd", {
|
|
2335
|
-
autoExpandMS: 1500, // Expand nodes after n milliseconds of hovering
|
|
2336
|
-
// dropMarkerInsertOffsetX: -16, // Additional offset for drop-marker with hitMode = "before"/"after"
|
|
2337
|
-
// dropMarkerOffsetX: -24, // Absolute position offset for .fancytree-drop-marker relatively to ..fancytree-title (icon/img near a node accepting drop)
|
|
2338
|
-
// #1021 `document.body` is not available yet
|
|
2339
|
-
// dropMarkerParent: "body", // Root Container used for drop marker (could be a shadow root)
|
|
2340
|
-
multiSource: false, // true: Drag multiple (i.e. selected) nodes. Also a callback() is allowed
|
|
2341
|
-
effectAllowed: "all", // Restrict the possible cursor shapes and modifier operations (can also be set in the dragStart event)
|
|
2342
|
-
dropEffectDefault: "move", // Default dropEffect ('copy', 'link', or 'move') when no modifier is pressed (override in drag, dragOver).
|
|
2343
|
-
guessDropEffect: true, // Calculate from `effectAllowed` and modifier keys)
|
|
2344
|
-
preventForeignNodes: false, // Prevent dropping nodes from different Wunderbaum trees
|
|
2345
|
-
preventLazyParents: true, // Prevent dropping items on unloaded lazy Wunderbaum tree nodes
|
|
2346
|
-
preventNonNodes: false, // Prevent dropping items other than Wunderbaum tree nodes
|
|
2347
|
-
preventRecursion: true, // Prevent dropping nodes on own descendants
|
|
2348
|
-
preventSameParent: false, // Prevent dropping nodes under same direct parent
|
|
2349
|
-
preventVoidMoves: true, // Prevent dropping nodes 'before self', etc. (move only)
|
|
2350
|
-
serializeClipboardData: true, // Serialize node data to dataTransfer object
|
|
2351
|
-
scroll: true, // Enable auto-scrolling while dragging
|
|
2352
|
-
scrollSensitivity: 20, // Active top/bottom margin in pixel
|
|
2353
|
-
// scrollnterval: 50, // Generate event every 50 ms
|
|
2354
|
-
scrollSpeed: 5, // Scroll pixel per 50 ms
|
|
2355
|
-
// setTextTypeJson: false, // Allow dragging of nodes to different IE windows
|
|
2356
|
-
sourceCopyHook: null, // Optional callback passed to `toDict` on dragStart @since 2.38
|
|
2357
|
-
// Events (drag support)
|
|
2358
|
-
dragStart: null, // Callback(sourceNode, data), return true, to enable dnd drag
|
|
2359
|
-
drag: null, // Callback(sourceNode, data)
|
|
2360
|
-
dragEnd: null, // Callback(sourceNode, data)
|
|
2361
|
-
// Events (drop support)
|
|
2362
|
-
dragEnter: null, // Callback(targetNode, data), return true, to enable dnd drop
|
|
2363
|
-
dragOver: null, // Callback(targetNode, data)
|
|
2364
|
-
dragExpand: null, // Callback(targetNode, data), return false to prevent autoExpand
|
|
2365
|
-
drop: null, // Callback(targetNode, data)
|
|
2366
|
-
dragLeave: null, // Callback(targetNode, data)
|
|
2367
|
-
});
|
|
2368
|
-
// public dropMarkerElem?: HTMLElement;
|
|
2369
|
-
this.srcNode = null;
|
|
2370
|
-
this.lastTargetNode = null;
|
|
2371
|
-
this.lastEnterStamp = 0;
|
|
2372
|
-
this.lastAllowedDropRegions = null;
|
|
2373
|
-
this.lastDropEffect = null;
|
|
2374
|
-
this.lastDropRegion = false;
|
|
2375
|
-
this.currentScrollDir = 0;
|
|
2376
|
-
this.applyScrollDirThrottled = throttle(this._applyScrollDir, 50);
|
|
2377
|
-
}
|
|
2378
|
-
init() {
|
|
2379
|
-
super.init();
|
|
2380
|
-
// Store the current scroll parent, which may be the tree
|
|
2381
|
-
// container, any enclosing div, or the document.
|
|
2382
|
-
// #761: scrollParent() always needs a container child
|
|
2383
|
-
// $temp = $("<span>").appendTo(this.$container);
|
|
2384
|
-
// this.$scrollParent = $temp.scrollParent();
|
|
2385
|
-
// $temp.remove();
|
|
2386
|
-
const tree = this.tree;
|
|
2387
|
-
const dndOpts = tree.options.dnd;
|
|
2388
|
-
// Enable drag support if dragStart() is specified:
|
|
2389
|
-
if (dndOpts.dragStart) {
|
|
2390
|
-
onEvent(tree.element, "dragstart drag dragend", this.onDragEvent.bind(this));
|
|
2048
|
+
init() {
|
|
2049
|
+
super.init();
|
|
2050
|
+
// Store the current scroll parent, which may be the tree
|
|
2051
|
+
// container, any enclosing div, or the document.
|
|
2052
|
+
// #761: scrollParent() always needs a container child
|
|
2053
|
+
// $temp = $("<span>").appendTo(this.$container);
|
|
2054
|
+
// this.$scrollParent = $temp.scrollParent();
|
|
2055
|
+
// $temp.remove();
|
|
2056
|
+
const tree = this.tree;
|
|
2057
|
+
const dndOpts = tree.options.dnd;
|
|
2058
|
+
// Enable drag support if dragStart() is specified:
|
|
2059
|
+
if (dndOpts.dragStart) {
|
|
2060
|
+
onEvent(tree.element, "dragstart drag dragend", this.onDragEvent.bind(this));
|
|
2391
2061
|
}
|
|
2392
2062
|
// Enable drop support if dragEnter() is specified:
|
|
2393
2063
|
if (dndOpts.dragEnter) {
|
|
@@ -2425,14 +2095,15 @@ class DndExtension extends WunderbaumExtension {
|
|
|
2425
2095
|
* Calculates the drop region based on the drag event and the allowed drop regions.
|
|
2426
2096
|
*/
|
|
2427
2097
|
_calcDropRegion(e, allowed) {
|
|
2098
|
+
const rowHeight = this.tree.options.rowHeightPx;
|
|
2428
2099
|
const dy = e.offsetY;
|
|
2429
2100
|
if (!allowed) {
|
|
2430
2101
|
return false;
|
|
2431
2102
|
}
|
|
2432
2103
|
else if (allowed.size === 3) {
|
|
2433
|
-
return dy < 0.25 *
|
|
2104
|
+
return dy < 0.25 * rowHeight
|
|
2434
2105
|
? "before"
|
|
2435
|
-
: dy > 0.75 *
|
|
2106
|
+
: dy > 0.75 * rowHeight
|
|
2436
2107
|
? "after"
|
|
2437
2108
|
: "over";
|
|
2438
2109
|
}
|
|
@@ -2441,7 +2112,7 @@ class DndExtension extends WunderbaumExtension {
|
|
|
2441
2112
|
}
|
|
2442
2113
|
else {
|
|
2443
2114
|
// Only 'before' and 'after':
|
|
2444
|
-
return dy >
|
|
2115
|
+
return dy > rowHeight / 2 ? "after" : "before";
|
|
2445
2116
|
}
|
|
2446
2117
|
// return "over";
|
|
2447
2118
|
}
|
|
@@ -2702,7 +2373,11 @@ class DndExtension extends WunderbaumExtension {
|
|
|
2702
2373
|
}
|
|
2703
2374
|
this.lastAllowedDropRegions = regionSet;
|
|
2704
2375
|
this.lastDropEffect = dt.dropEffect;
|
|
2376
|
+
const region = this._calcDropRegion(e, this.lastAllowedDropRegions);
|
|
2705
2377
|
targetNode.setClass("wb-drop-target");
|
|
2378
|
+
targetNode.setClass("wb-drop-over", region === "over");
|
|
2379
|
+
targetNode.setClass("wb-drop-before", region === "before");
|
|
2380
|
+
targetNode.setClass("wb-drop-after", region === "after");
|
|
2706
2381
|
e.preventDefault(); // Allow drop (Drop operation is denied by default)
|
|
2707
2382
|
return false;
|
|
2708
2383
|
// --- dragover ---
|
|
@@ -2741,186 +2416,526 @@ class DndExtension extends WunderbaumExtension {
|
|
|
2741
2416
|
targetNode._callEvent("dnd.dragLeave", { event: e, sourceNode: srcNode });
|
|
2742
2417
|
// --- drop ---
|
|
2743
2418
|
}
|
|
2744
|
-
else if (e.type === "drop") {
|
|
2745
|
-
e.stopPropagation(); // prevent browser from opening links?
|
|
2746
|
-
e.preventDefault(); // #69 prevent iOS browser from opening links
|
|
2747
|
-
this._leaveNode();
|
|
2748
|
-
const region = this.lastDropRegion;
|
|
2749
|
-
let nodeData = (_a = e.dataTransfer) === null || _a === void 0 ? void 0 : _a.getData(nodeMimeType);
|
|
2750
|
-
nodeData = nodeData ? JSON.parse(nodeData) : null;
|
|
2751
|
-
const srcNode = this.srcNode;
|
|
2752
|
-
const lastDropEffect = this.lastDropEffect;
|
|
2753
|
-
setTimeout(() => {
|
|
2754
|
-
// Decouple this call, because drop actions may prevent the dragend event
|
|
2755
|
-
// from being fired on some browsers
|
|
2756
|
-
targetNode._callEvent("dnd.drop", {
|
|
2757
|
-
event: e,
|
|
2758
|
-
region: region,
|
|
2759
|
-
suggestedDropMode: region === "over" ? "appendChild" : region,
|
|
2760
|
-
suggestedDropEffect: lastDropEffect,
|
|
2761
|
-
// suggestedDropEffect: e.dataTransfer?.dropEffect,
|
|
2762
|
-
sourceNode: srcNode,
|
|
2763
|
-
sourceNodeData: nodeData,
|
|
2764
|
-
});
|
|
2765
|
-
}, 10);
|
|
2419
|
+
else if (e.type === "drop") {
|
|
2420
|
+
e.stopPropagation(); // prevent browser from opening links?
|
|
2421
|
+
e.preventDefault(); // #69 prevent iOS browser from opening links
|
|
2422
|
+
this._leaveNode();
|
|
2423
|
+
const region = this.lastDropRegion;
|
|
2424
|
+
let nodeData = (_a = e.dataTransfer) === null || _a === void 0 ? void 0 : _a.getData(nodeMimeType);
|
|
2425
|
+
nodeData = nodeData ? JSON.parse(nodeData) : null;
|
|
2426
|
+
const srcNode = this.srcNode;
|
|
2427
|
+
const lastDropEffect = this.lastDropEffect;
|
|
2428
|
+
setTimeout(() => {
|
|
2429
|
+
// Decouple this call, because drop actions may prevent the dragend event
|
|
2430
|
+
// from being fired on some browsers
|
|
2431
|
+
targetNode._callEvent("dnd.drop", {
|
|
2432
|
+
event: e,
|
|
2433
|
+
region: region,
|
|
2434
|
+
suggestedDropMode: region === "over" ? "appendChild" : region,
|
|
2435
|
+
suggestedDropEffect: lastDropEffect,
|
|
2436
|
+
// suggestedDropEffect: e.dataTransfer?.dropEffect,
|
|
2437
|
+
sourceNode: srcNode,
|
|
2438
|
+
sourceNodeData: nodeData,
|
|
2439
|
+
});
|
|
2440
|
+
}, 10);
|
|
2441
|
+
}
|
|
2442
|
+
return false;
|
|
2443
|
+
}
|
|
2444
|
+
}
|
|
2445
|
+
|
|
2446
|
+
/*!
|
|
2447
|
+
* Wunderbaum - drag_observer
|
|
2448
|
+
* Copyright (c) 2021-2024, Martin Wendt. Released under the MIT license.
|
|
2449
|
+
* v0.12.0, Sun, 12 Jan 2025 10:51:41 GMT (https://github.com/mar10/wunderbaum)
|
|
2450
|
+
*/
|
|
2451
|
+
/**
|
|
2452
|
+
* Convert mouse- and touch events to 'dragstart', 'drag', and 'dragstop'.
|
|
2453
|
+
*/
|
|
2454
|
+
class DragObserver {
|
|
2455
|
+
constructor(opts) {
|
|
2456
|
+
this.start = {
|
|
2457
|
+
event: null,
|
|
2458
|
+
x: 0,
|
|
2459
|
+
y: 0,
|
|
2460
|
+
altKey: false,
|
|
2461
|
+
ctrlKey: false,
|
|
2462
|
+
metaKey: false,
|
|
2463
|
+
shiftKey: false,
|
|
2464
|
+
};
|
|
2465
|
+
this.dragElem = null;
|
|
2466
|
+
this.dragging = false;
|
|
2467
|
+
this.customData = {};
|
|
2468
|
+
// TODO: touch events
|
|
2469
|
+
this.events = ["mousedown", "mouseup", "mousemove", "keydown"];
|
|
2470
|
+
if (!opts.root) {
|
|
2471
|
+
throw new Error("Missing `root` option.");
|
|
2472
|
+
}
|
|
2473
|
+
this.opts = Object.assign({ thresh: 5 }, opts);
|
|
2474
|
+
this.root = opts.root;
|
|
2475
|
+
this._handler = this.handleEvent.bind(this);
|
|
2476
|
+
this.events.forEach((type) => {
|
|
2477
|
+
this.root.addEventListener(type, this._handler);
|
|
2478
|
+
});
|
|
2479
|
+
}
|
|
2480
|
+
/** Unregister all event listeners. */
|
|
2481
|
+
disconnect() {
|
|
2482
|
+
this.events.forEach((type) => {
|
|
2483
|
+
this.root.removeEventListener(type, this._handler);
|
|
2484
|
+
});
|
|
2485
|
+
}
|
|
2486
|
+
getDragElem() {
|
|
2487
|
+
return this.dragElem;
|
|
2488
|
+
}
|
|
2489
|
+
isDragging() {
|
|
2490
|
+
return this.dragging;
|
|
2491
|
+
}
|
|
2492
|
+
stopDrag(cb_event) {
|
|
2493
|
+
if (this.dragging && this.opts.dragstop && cb_event) {
|
|
2494
|
+
cb_event.type = "dragstop";
|
|
2495
|
+
try {
|
|
2496
|
+
this.opts.dragstop(cb_event);
|
|
2497
|
+
}
|
|
2498
|
+
catch (err) {
|
|
2499
|
+
console.error("dragstop error", err); // eslint-disable-line no-console
|
|
2500
|
+
}
|
|
2501
|
+
}
|
|
2502
|
+
this.dragElem = null;
|
|
2503
|
+
this.dragging = false;
|
|
2504
|
+
this.start.event = null;
|
|
2505
|
+
this.customData = {};
|
|
2506
|
+
}
|
|
2507
|
+
handleEvent(e) {
|
|
2508
|
+
const type = e.type;
|
|
2509
|
+
const opts = this.opts;
|
|
2510
|
+
const cb_event = {
|
|
2511
|
+
type: e.type,
|
|
2512
|
+
startEvent: type === "mousedown" ? e : this.start.event,
|
|
2513
|
+
event: e,
|
|
2514
|
+
customData: this.customData,
|
|
2515
|
+
dragElem: this.dragElem,
|
|
2516
|
+
dx: e.pageX - this.start.x,
|
|
2517
|
+
dy: e.pageY - this.start.y,
|
|
2518
|
+
apply: undefined,
|
|
2519
|
+
};
|
|
2520
|
+
// console.log("handleEvent", type, cb_event);
|
|
2521
|
+
switch (type) {
|
|
2522
|
+
case "keydown":
|
|
2523
|
+
this.stopDrag(cb_event);
|
|
2524
|
+
break;
|
|
2525
|
+
case "mousedown":
|
|
2526
|
+
if (this.dragElem) {
|
|
2527
|
+
this.stopDrag(cb_event);
|
|
2528
|
+
break;
|
|
2529
|
+
}
|
|
2530
|
+
if (opts.selector) {
|
|
2531
|
+
let elem = e.target;
|
|
2532
|
+
if (elem.matches(opts.selector)) {
|
|
2533
|
+
this.dragElem = elem;
|
|
2534
|
+
}
|
|
2535
|
+
else {
|
|
2536
|
+
elem = elem.closest(opts.selector);
|
|
2537
|
+
if (elem) {
|
|
2538
|
+
this.dragElem = elem;
|
|
2539
|
+
}
|
|
2540
|
+
else {
|
|
2541
|
+
break; // no event delegation selector matched
|
|
2542
|
+
}
|
|
2543
|
+
}
|
|
2544
|
+
}
|
|
2545
|
+
this.start.event = e;
|
|
2546
|
+
this.start.x = e.pageX;
|
|
2547
|
+
this.start.y = e.pageY;
|
|
2548
|
+
this.start.altKey = e.altKey;
|
|
2549
|
+
this.start.ctrlKey = e.ctrlKey;
|
|
2550
|
+
this.start.metaKey = e.metaKey;
|
|
2551
|
+
this.start.shiftKey = e.shiftKey;
|
|
2552
|
+
break;
|
|
2553
|
+
case "mousemove":
|
|
2554
|
+
// TODO: debounce/throttle?
|
|
2555
|
+
// TODO: horizontal mode: ignore if dx unchanged
|
|
2556
|
+
if (!this.dragElem) {
|
|
2557
|
+
break;
|
|
2558
|
+
}
|
|
2559
|
+
if (!this.dragging) {
|
|
2560
|
+
if (opts.thresh) {
|
|
2561
|
+
const dist2 = cb_event.dx * cb_event.dx + cb_event.dy * cb_event.dy;
|
|
2562
|
+
if (dist2 < opts.thresh * opts.thresh) {
|
|
2563
|
+
break;
|
|
2564
|
+
}
|
|
2565
|
+
}
|
|
2566
|
+
cb_event.type = "dragstart";
|
|
2567
|
+
if (opts.dragstart(cb_event) === false) {
|
|
2568
|
+
this.stopDrag(cb_event);
|
|
2569
|
+
break;
|
|
2570
|
+
}
|
|
2571
|
+
this.dragging = true;
|
|
2572
|
+
}
|
|
2573
|
+
if (this.dragging && this.opts.drag) {
|
|
2574
|
+
cb_event.type = "drag";
|
|
2575
|
+
this.opts.drag(cb_event);
|
|
2576
|
+
}
|
|
2577
|
+
break;
|
|
2578
|
+
case "mouseup":
|
|
2579
|
+
if (!this.dragging) {
|
|
2580
|
+
this.stopDrag(cb_event);
|
|
2581
|
+
break;
|
|
2582
|
+
}
|
|
2583
|
+
if (e.button === 0) {
|
|
2584
|
+
cb_event.apply = true;
|
|
2585
|
+
}
|
|
2586
|
+
else {
|
|
2587
|
+
cb_event.apply = false;
|
|
2588
|
+
}
|
|
2589
|
+
this.stopDrag(cb_event);
|
|
2590
|
+
break;
|
|
2591
|
+
}
|
|
2592
|
+
}
|
|
2593
|
+
}
|
|
2594
|
+
|
|
2595
|
+
/*!
|
|
2596
|
+
* Wunderbaum - common
|
|
2597
|
+
* Copyright (c) 2021-2024, Martin Wendt. Released under the MIT license.
|
|
2598
|
+
* v0.12.0, Sun, 12 Jan 2025 10:51:41 GMT (https://github.com/mar10/wunderbaum)
|
|
2599
|
+
*/
|
|
2600
|
+
const DEFAULT_DEBUGLEVEL = 3; // Replaced by rollup script
|
|
2601
|
+
/**
|
|
2602
|
+
* Fixed height of a row in pixel. Must match the SCSS variable `$row-outer-height`.
|
|
2603
|
+
*/
|
|
2604
|
+
const DEFAULT_ROW_HEIGHT = 22;
|
|
2605
|
+
/**
|
|
2606
|
+
* Fixed width of node icons in pixel. Must match the SCSS variable `$icon-outer-width`.
|
|
2607
|
+
*/
|
|
2608
|
+
const ICON_WIDTH = 20;
|
|
2609
|
+
/**
|
|
2610
|
+
* Adjust the width of the title span, so overflow ellipsis work.
|
|
2611
|
+
* (2 x `$col-padding-x` + 3px rounding errors).
|
|
2612
|
+
*/
|
|
2613
|
+
const TITLE_SPAN_PAD_Y = 7;
|
|
2614
|
+
/** Render row markup for N nodes above and below the visible viewport. */
|
|
2615
|
+
const RENDER_MAX_PREFETCH = 5;
|
|
2616
|
+
/** Minimum column width if not set otherwise. */
|
|
2617
|
+
const DEFAULT_MIN_COL_WIDTH = 4;
|
|
2618
|
+
/** Regular expression to detect if a string describes an image URL (in contrast
|
|
2619
|
+
* to a class name). Strings are considered image urls if they contain '.' or '/'.
|
|
2620
|
+
*/
|
|
2621
|
+
const TEST_IMG = new RegExp(/\.|\//);
|
|
2622
|
+
// export const RECURSIVE_REQUEST_ERROR = "$recursive_request";
|
|
2623
|
+
// export const INVALID_REQUEST_TARGET_ERROR = "$request_target_invalid";
|
|
2624
|
+
/**
|
|
2625
|
+
* Default node icons.
|
|
2626
|
+
* Requires bootstrap icons https://icons.getbootstrap.com
|
|
2627
|
+
*/
|
|
2628
|
+
const iconMaps = {
|
|
2629
|
+
bootstrap: {
|
|
2630
|
+
error: "bi bi-exclamation-triangle",
|
|
2631
|
+
// loading: "bi bi-hourglass-split wb-busy",
|
|
2632
|
+
loading: "bi bi-chevron-right wb-busy",
|
|
2633
|
+
// loading: "bi bi-arrow-repeat wb-spin",
|
|
2634
|
+
// loading: '<div class="spinner-border spinner-border-sm" role="status"> <span class="visually-hidden">Loading...</span> </div>',
|
|
2635
|
+
// noData: "bi bi-search",
|
|
2636
|
+
noData: "bi bi-question-circle",
|
|
2637
|
+
expanderExpanded: "bi bi-chevron-down",
|
|
2638
|
+
// expanderExpanded: "bi bi-dash-square",
|
|
2639
|
+
expanderCollapsed: "bi bi-chevron-right",
|
|
2640
|
+
// expanderCollapsed: "bi bi-plus-square",
|
|
2641
|
+
expanderLazy: "bi bi-chevron-right wb-helper-lazy-expander",
|
|
2642
|
+
// expanderLazy: "bi bi-chevron-bar-right",
|
|
2643
|
+
checkChecked: "bi bi-check-square",
|
|
2644
|
+
checkUnchecked: "bi bi-square",
|
|
2645
|
+
checkUnknown: "bi bi-dash-square-dotted",
|
|
2646
|
+
radioChecked: "bi bi-circle-fill",
|
|
2647
|
+
radioUnchecked: "bi bi-circle",
|
|
2648
|
+
radioUnknown: "bi bi-record-circle",
|
|
2649
|
+
folder: "bi bi-folder2",
|
|
2650
|
+
folderOpen: "bi bi-folder2-open",
|
|
2651
|
+
folderLazy: "bi bi-folder-symlink",
|
|
2652
|
+
doc: "bi bi-file-earmark",
|
|
2653
|
+
colSortable: "bi bi-chevron-expand",
|
|
2654
|
+
// colSortable: "bi bi-arrow-down-up",
|
|
2655
|
+
// colSortAsc: "bi bi-chevron-down",
|
|
2656
|
+
// colSortDesc: "bi bi-chevron-up",
|
|
2657
|
+
colSortAsc: "bi bi-arrow-down",
|
|
2658
|
+
colSortDesc: "bi bi-arrow-up",
|
|
2659
|
+
colFilter: "bi bi-filter-circle",
|
|
2660
|
+
colFilterActive: "bi bi-filter-circle-fill wb-helper-invalid",
|
|
2661
|
+
colMenu: "bi bi-three-dots-vertical",
|
|
2662
|
+
},
|
|
2663
|
+
fontawesome6: {
|
|
2664
|
+
error: "fa-solid fa-triangle-exclamation",
|
|
2665
|
+
loading: "fa-solid fa-chevron-right fa-beat",
|
|
2666
|
+
noData: "fa-solid fa-circle-question",
|
|
2667
|
+
expanderExpanded: "fa-solid fa-chevron-down",
|
|
2668
|
+
expanderCollapsed: "fa-solid fa-chevron-right",
|
|
2669
|
+
expanderLazy: "fa-solid fa-chevron-right wb-helper-lazy-expander",
|
|
2670
|
+
checkChecked: "fa-regular fa-square-check",
|
|
2671
|
+
checkUnchecked: "fa-regular fa-square",
|
|
2672
|
+
checkUnknown: "fa-regular fa-square-minus",
|
|
2673
|
+
radioChecked: "fa-solid fa-circle",
|
|
2674
|
+
radioUnchecked: "fa-regular fa-circle",
|
|
2675
|
+
radioUnknown: "fa-regular fa-circle-question",
|
|
2676
|
+
folder: "fa-solid fa-folder-closed",
|
|
2677
|
+
folderOpen: "fa-regular fa-folder-open",
|
|
2678
|
+
folderLazy: "fa-solid fa-folder-plus",
|
|
2679
|
+
doc: "fa-regular fa-file",
|
|
2680
|
+
colSortable: "fa-solid fa-fw fa-sort",
|
|
2681
|
+
colSortAsc: "fa-solid fa-fw fa-sort-up",
|
|
2682
|
+
colSortDesc: "fa-solid fa-fw fa-sort-down",
|
|
2683
|
+
colFilter: "fa-solid fa-fw fa-filter",
|
|
2684
|
+
colFilterActive: "fa-solid fa-fw fa-filter wb-helper-invalid",
|
|
2685
|
+
colMenu: "fa-solid fa-fw fa-ellipsis-v",
|
|
2686
|
+
},
|
|
2687
|
+
};
|
|
2688
|
+
/** Dict keys that are evaluated by source loader (others are added to `tree.data` instead). */
|
|
2689
|
+
const RESERVED_TREE_SOURCE_KEYS = new Set([
|
|
2690
|
+
"_format", // reserved for future use
|
|
2691
|
+
"_keyMap", // Used for compressed data format
|
|
2692
|
+
"_positional", // Used for compressed data format
|
|
2693
|
+
"_typeList", // Used for compressed data format @deprecated
|
|
2694
|
+
"_valueMap", // Used for compressed data format
|
|
2695
|
+
"_version", // reserved for future use
|
|
2696
|
+
"children",
|
|
2697
|
+
"columns",
|
|
2698
|
+
"types",
|
|
2699
|
+
]);
|
|
2700
|
+
// /** Key codes that trigger grid navigation, even when inside an input element. */
|
|
2701
|
+
// export const INPUT_BREAKOUT_KEYS: Set<string> = new Set([
|
|
2702
|
+
// // "ArrowDown",
|
|
2703
|
+
// // "ArrowUp",
|
|
2704
|
+
// "Enter",
|
|
2705
|
+
// "Escape",
|
|
2706
|
+
// ]);
|
|
2707
|
+
/** Map `KeyEvent.key` to navigation action. */
|
|
2708
|
+
const KEY_TO_ACTION_DICT = {
|
|
2709
|
+
" ": "toggleSelect",
|
|
2710
|
+
"+": "expand",
|
|
2711
|
+
Add: "expand",
|
|
2712
|
+
ArrowDown: "down",
|
|
2713
|
+
ArrowLeft: "left",
|
|
2714
|
+
ArrowRight: "right",
|
|
2715
|
+
ArrowUp: "up",
|
|
2716
|
+
Backspace: "parent",
|
|
2717
|
+
"/": "collapseAll",
|
|
2718
|
+
Divide: "collapseAll",
|
|
2719
|
+
End: "lastCol",
|
|
2720
|
+
Home: "firstCol",
|
|
2721
|
+
"Control+End": "last",
|
|
2722
|
+
"Control+Home": "first",
|
|
2723
|
+
"Meta+ArrowDown": "last", // macOs
|
|
2724
|
+
"Meta+ArrowUp": "first", // macOs
|
|
2725
|
+
"*": "expandAll",
|
|
2726
|
+
Multiply: "expandAll",
|
|
2727
|
+
PageDown: "pageDown",
|
|
2728
|
+
PageUp: "pageUp",
|
|
2729
|
+
"-": "collapse",
|
|
2730
|
+
Subtract: "collapse",
|
|
2731
|
+
};
|
|
2732
|
+
/** Return a callback that returns true if the node title matches the string
|
|
2733
|
+
* or regular expression.
|
|
2734
|
+
* @see {@link WunderbaumNode.findAll}
|
|
2735
|
+
*/
|
|
2736
|
+
function makeNodeTitleMatcher(match) {
|
|
2737
|
+
if (match instanceof RegExp) {
|
|
2738
|
+
return function (node) {
|
|
2739
|
+
return match.test(node.title);
|
|
2740
|
+
};
|
|
2741
|
+
}
|
|
2742
|
+
assert(typeof match === "string", `Expected a string or RegExp: ${match}`);
|
|
2743
|
+
// s = escapeRegex(s.toLowerCase());
|
|
2744
|
+
return function (node) {
|
|
2745
|
+
return node.title === match;
|
|
2746
|
+
// console.log("match " + node, node.title.toLowerCase().indexOf(match))
|
|
2747
|
+
// return node.title.toLowerCase().indexOf(match) >= 0;
|
|
2748
|
+
};
|
|
2749
|
+
}
|
|
2750
|
+
/** Return a callback that returns true if the node title starts with a string (case-insensitive). */
|
|
2751
|
+
function makeNodeTitleStartMatcher(s) {
|
|
2752
|
+
s = escapeRegex(s);
|
|
2753
|
+
const reMatch = new RegExp("^" + s, "i");
|
|
2754
|
+
return function (node) {
|
|
2755
|
+
return reMatch.test(node.title);
|
|
2756
|
+
};
|
|
2757
|
+
}
|
|
2758
|
+
/** Compare two nodes by title (case-insensitive). */
|
|
2759
|
+
function nodeTitleSorter(a, b) {
|
|
2760
|
+
const x = a.title.toLowerCase();
|
|
2761
|
+
const y = b.title.toLowerCase();
|
|
2762
|
+
return x === y ? 0 : x > y ? 1 : -1;
|
|
2763
|
+
}
|
|
2764
|
+
/**
|
|
2765
|
+
* Convert 'flat' to 'nested' format.
|
|
2766
|
+
*
|
|
2767
|
+
* Flat node entry format:
|
|
2768
|
+
* [PARENT_ID, [POSITIONAL_ARGS]]
|
|
2769
|
+
* or
|
|
2770
|
+
* [PARENT_ID, [POSITIONAL_ARGS], {KEY_VALUE_ARGS}]
|
|
2771
|
+
*
|
|
2772
|
+
* 1. Parent-referencing list is converted to a list of nested dicts with
|
|
2773
|
+
* optional `children` properties.
|
|
2774
|
+
* 2. `[POSITIONAL_ARGS]` are added as dict attributes.
|
|
2775
|
+
*/
|
|
2776
|
+
function unflattenSource(source) {
|
|
2777
|
+
var _a, _b, _c;
|
|
2778
|
+
const { _format, _keyMap = {}, _positional = [], children } = source;
|
|
2779
|
+
if (_format !== "flat") {
|
|
2780
|
+
throw new Error(`Expected source._format: "flat", but got ${_format}`);
|
|
2781
|
+
}
|
|
2782
|
+
if (_positional && _positional.includes("children")) {
|
|
2783
|
+
throw new Error(`source._positional must not include "children": ${_positional}`);
|
|
2784
|
+
}
|
|
2785
|
+
let longToShort = _keyMap;
|
|
2786
|
+
if (_keyMap.t) {
|
|
2787
|
+
// Inverse keyMap was used (pre 0.7.0)
|
|
2788
|
+
// TODO: raise Error on final 1.x release
|
|
2789
|
+
const msg = `source._keyMap maps from long to short since v0.7.0. Flip key/value!`;
|
|
2790
|
+
console.warn(msg); // eslint-disable-line no-console
|
|
2791
|
+
longToShort = {};
|
|
2792
|
+
for (const [key, value] of Object.entries(_keyMap)) {
|
|
2793
|
+
longToShort[value] = key;
|
|
2794
|
+
}
|
|
2795
|
+
}
|
|
2796
|
+
const positionalShort = _positional.map((e) => longToShort[e]);
|
|
2797
|
+
const newChildren = [];
|
|
2798
|
+
const keyToNodeMap = {};
|
|
2799
|
+
const indexToNodeMap = {};
|
|
2800
|
+
const keyAttrName = (_a = longToShort["key"]) !== null && _a !== void 0 ? _a : "key";
|
|
2801
|
+
const childrenAttrName = (_b = longToShort["children"]) !== null && _b !== void 0 ? _b : "children";
|
|
2802
|
+
for (const [index, nodeTuple] of children.entries()) {
|
|
2803
|
+
// Node entry format:
|
|
2804
|
+
// [PARENT_ID, [POSITIONAL_ARGS]]
|
|
2805
|
+
// or
|
|
2806
|
+
// [PARENT_ID, [POSITIONAL_ARGS], {KEY_VALUE_ARGS}]
|
|
2807
|
+
const [parentId, args, kwargs = {}] = nodeTuple;
|
|
2808
|
+
// Free up some memory as we go
|
|
2809
|
+
nodeTuple[1] = null;
|
|
2810
|
+
if (nodeTuple[2] != null) {
|
|
2811
|
+
nodeTuple[2] = null;
|
|
2812
|
+
}
|
|
2813
|
+
// console.log("flatten", parentId, args, kwargs)
|
|
2814
|
+
// We keep `kwargs` as our new node definition. Then we add all positional
|
|
2815
|
+
// values to this object:
|
|
2816
|
+
args.forEach((val, positionalIdx) => {
|
|
2817
|
+
kwargs[positionalShort[positionalIdx]] = val;
|
|
2818
|
+
});
|
|
2819
|
+
// Find the parent node. `null` means 'toplevel'. PARENT_ID may be the numeric
|
|
2820
|
+
// index of the source.children list. If PARENT_ID is a string, we search
|
|
2821
|
+
// a parent with node.key of this value.
|
|
2822
|
+
indexToNodeMap[index] = kwargs;
|
|
2823
|
+
const key = kwargs[keyAttrName];
|
|
2824
|
+
if (key != null) {
|
|
2825
|
+
keyToNodeMap[key] = kwargs;
|
|
2826
|
+
}
|
|
2827
|
+
let parentNode = null;
|
|
2828
|
+
if (parentId === null) ;
|
|
2829
|
+
else if (typeof parentId === "number") {
|
|
2830
|
+
parentNode = indexToNodeMap[parentId];
|
|
2831
|
+
if (parentNode === undefined) {
|
|
2832
|
+
throw new Error(`unflattenSource: Could not find parent node by index: ${parentId}.`);
|
|
2833
|
+
}
|
|
2834
|
+
}
|
|
2835
|
+
else {
|
|
2836
|
+
parentNode = keyToNodeMap[parentId];
|
|
2837
|
+
if (parentNode === undefined) {
|
|
2838
|
+
throw new Error(`unflattenSource: Could not find parent node by key: ${parentId}`);
|
|
2839
|
+
}
|
|
2840
|
+
}
|
|
2841
|
+
if (parentNode) {
|
|
2842
|
+
(_c = parentNode[childrenAttrName]) !== null && _c !== void 0 ? _c : (parentNode[childrenAttrName] = []);
|
|
2843
|
+
parentNode[childrenAttrName].push(kwargs);
|
|
2844
|
+
}
|
|
2845
|
+
else {
|
|
2846
|
+
newChildren.push(kwargs);
|
|
2766
2847
|
}
|
|
2767
|
-
return false;
|
|
2768
2848
|
}
|
|
2849
|
+
source.children = newChildren;
|
|
2769
2850
|
}
|
|
2770
|
-
|
|
2771
|
-
/*!
|
|
2772
|
-
* Wunderbaum - drag_observer
|
|
2773
|
-
* Copyright (c) 2021-2024, Martin Wendt. Released under the MIT license.
|
|
2774
|
-
* v0.11.0, Sun, 04 Aug 2024 15:35:53 GMT (https://github.com/mar10/wunderbaum)
|
|
2775
|
-
*/
|
|
2776
2851
|
/**
|
|
2777
|
-
*
|
|
2852
|
+
* Decompresses the source data by
|
|
2853
|
+
* - converting from 'flat' to 'nested' format
|
|
2854
|
+
* - expanding short alias names to long names (if defined in _keyMap)
|
|
2855
|
+
* - resolving value indexes to value strings (if defined in _valueMap)
|
|
2856
|
+
*
|
|
2857
|
+
* @param source - The source object to be decompressed.
|
|
2858
|
+
* @returns void
|
|
2778
2859
|
*/
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
metaKey: false,
|
|
2788
|
-
shiftKey: false,
|
|
2789
|
-
};
|
|
2790
|
-
this.dragElem = null;
|
|
2791
|
-
this.dragging = false;
|
|
2792
|
-
this.customData = {};
|
|
2793
|
-
// TODO: touch events
|
|
2794
|
-
this.events = ["mousedown", "mouseup", "mousemove", "keydown"];
|
|
2795
|
-
if (!opts.root) {
|
|
2796
|
-
throw new Error("Missing `root` option.");
|
|
2860
|
+
function decompressSourceData(source) {
|
|
2861
|
+
let { _format, _version = 1, _keyMap, _valueMap } = source;
|
|
2862
|
+
assert(_version === 1, `Expected file version 1 instead of ${_version}`);
|
|
2863
|
+
let longToShort = _keyMap;
|
|
2864
|
+
let shortToLong = {};
|
|
2865
|
+
if (longToShort) {
|
|
2866
|
+
for (const [key, value] of Object.entries(longToShort)) {
|
|
2867
|
+
shortToLong[value] = key;
|
|
2797
2868
|
}
|
|
2798
|
-
this.opts = Object.assign({ thresh: 5 }, opts);
|
|
2799
|
-
this.root = opts.root;
|
|
2800
|
-
this._handler = this.handleEvent.bind(this);
|
|
2801
|
-
this.events.forEach((type) => {
|
|
2802
|
-
this.root.addEventListener(type, this._handler);
|
|
2803
|
-
});
|
|
2804
|
-
}
|
|
2805
|
-
/** Unregister all event listeners. */
|
|
2806
|
-
disconnect() {
|
|
2807
|
-
this.events.forEach((type) => {
|
|
2808
|
-
this.root.removeEventListener(type, this._handler);
|
|
2809
|
-
});
|
|
2810
|
-
}
|
|
2811
|
-
getDragElem() {
|
|
2812
|
-
return this.dragElem;
|
|
2813
2869
|
}
|
|
2814
|
-
|
|
2815
|
-
|
|
2870
|
+
// Fallback for old format (pre 0.7.0, using _keyMap in reverse direction)
|
|
2871
|
+
// TODO: raise Error on final 1.x release
|
|
2872
|
+
if (longToShort && longToShort.t) {
|
|
2873
|
+
const msg = `source._keyMap maps from long to short since v0.7.0. Flip key/value!`;
|
|
2874
|
+
console.warn(msg); // eslint-disable-line no-console
|
|
2875
|
+
[longToShort, shortToLong] = [shortToLong, longToShort];
|
|
2816
2876
|
}
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
|
|
2877
|
+
// Fallback for old format (pre 0.7.0, using _typeList instead of _valueMap)
|
|
2878
|
+
// TODO: raise Error on final 1.x release
|
|
2879
|
+
if (source._typeList != null) {
|
|
2880
|
+
const msg = `source._typeList is deprecated since v0.7.0: use source._valueMap: {"type": [...]} instead.`;
|
|
2881
|
+
if (_valueMap != null) {
|
|
2882
|
+
throw new Error(msg);
|
|
2883
|
+
}
|
|
2884
|
+
else {
|
|
2885
|
+
console.warn(msg); // eslint-disable-line no-console
|
|
2886
|
+
_valueMap = { type: source._typeList };
|
|
2887
|
+
delete source._typeList;
|
|
2826
2888
|
}
|
|
2827
|
-
this.dragElem = null;
|
|
2828
|
-
this.dragging = false;
|
|
2829
|
-
this.start.event = null;
|
|
2830
|
-
this.customData = {};
|
|
2831
2889
|
}
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
|
|
2839
|
-
|
|
2840
|
-
|
|
2841
|
-
|
|
2842
|
-
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
break;
|
|
2854
|
-
}
|
|
2855
|
-
if (opts.selector) {
|
|
2856
|
-
let elem = e.target;
|
|
2857
|
-
if (elem.matches(opts.selector)) {
|
|
2858
|
-
this.dragElem = elem;
|
|
2859
|
-
}
|
|
2860
|
-
else {
|
|
2861
|
-
elem = elem.closest(opts.selector);
|
|
2862
|
-
if (elem) {
|
|
2863
|
-
this.dragElem = elem;
|
|
2864
|
-
}
|
|
2865
|
-
else {
|
|
2866
|
-
break; // no event delegation selector matched
|
|
2867
|
-
}
|
|
2890
|
+
if (_format === "flat") {
|
|
2891
|
+
unflattenSource(source);
|
|
2892
|
+
}
|
|
2893
|
+
delete source._format;
|
|
2894
|
+
delete source._version;
|
|
2895
|
+
delete source._keyMap;
|
|
2896
|
+
delete source._valueMap;
|
|
2897
|
+
delete source._positional;
|
|
2898
|
+
function _iter(childList) {
|
|
2899
|
+
for (const node of childList) {
|
|
2900
|
+
// Iterate over a list of names, because we modify inside the loop
|
|
2901
|
+
// (for ... of ... does not allow this)
|
|
2902
|
+
Object.getOwnPropertyNames(node).forEach((propName) => {
|
|
2903
|
+
const value = node[propName];
|
|
2904
|
+
// Replace short names with long names if defined in _keyMap
|
|
2905
|
+
let longName = propName;
|
|
2906
|
+
if (_keyMap && shortToLong[propName] != null) {
|
|
2907
|
+
longName = shortToLong[propName];
|
|
2908
|
+
if (longName !== propName) {
|
|
2909
|
+
node[longName] = value;
|
|
2910
|
+
delete node[propName];
|
|
2868
2911
|
}
|
|
2869
2912
|
}
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
|
|
2873
|
-
|
|
2874
|
-
|
|
2875
|
-
|
|
2876
|
-
|
|
2877
|
-
break;
|
|
2878
|
-
case "mousemove":
|
|
2879
|
-
// TODO: debounce/throttle?
|
|
2880
|
-
// TODO: horizontal mode: ignore if dx unchanged
|
|
2881
|
-
if (!this.dragElem) {
|
|
2882
|
-
break;
|
|
2883
|
-
}
|
|
2884
|
-
if (!this.dragging) {
|
|
2885
|
-
if (opts.thresh) {
|
|
2886
|
-
const dist2 = cb_event.dx * cb_event.dx + cb_event.dy * cb_event.dy;
|
|
2887
|
-
if (dist2 < opts.thresh * opts.thresh) {
|
|
2888
|
-
break;
|
|
2889
|
-
}
|
|
2890
|
-
}
|
|
2891
|
-
cb_event.type = "dragstart";
|
|
2892
|
-
if (opts.dragstart(cb_event) === false) {
|
|
2893
|
-
this.stopDrag(cb_event);
|
|
2894
|
-
break;
|
|
2913
|
+
// Replace type index with type name if defined in _valueMap
|
|
2914
|
+
if (_valueMap &&
|
|
2915
|
+
typeof value === "number" &&
|
|
2916
|
+
_valueMap[longName] != null) {
|
|
2917
|
+
const newValue = _valueMap[longName][value];
|
|
2918
|
+
if (newValue == null) {
|
|
2919
|
+
throw new Error(`Expected valueMap[${longName}][${value}] entry in [${_valueMap[longName]}]`);
|
|
2895
2920
|
}
|
|
2896
|
-
|
|
2897
|
-
}
|
|
2898
|
-
if (this.dragging && this.opts.drag) {
|
|
2899
|
-
cb_event.type = "drag";
|
|
2900
|
-
this.opts.drag(cb_event);
|
|
2901
|
-
}
|
|
2902
|
-
break;
|
|
2903
|
-
case "mouseup":
|
|
2904
|
-
if (!this.dragging) {
|
|
2905
|
-
this.stopDrag(cb_event);
|
|
2906
|
-
break;
|
|
2907
|
-
}
|
|
2908
|
-
if (e.button === 0) {
|
|
2909
|
-
cb_event.apply = true;
|
|
2910
|
-
}
|
|
2911
|
-
else {
|
|
2912
|
-
cb_event.apply = false;
|
|
2921
|
+
node[longName] = newValue;
|
|
2913
2922
|
}
|
|
2914
|
-
|
|
2915
|
-
|
|
2923
|
+
});
|
|
2924
|
+
// Recursion
|
|
2925
|
+
if (node.children) {
|
|
2926
|
+
_iter(node.children);
|
|
2927
|
+
}
|
|
2916
2928
|
}
|
|
2917
2929
|
}
|
|
2930
|
+
if (_keyMap || _valueMap) {
|
|
2931
|
+
_iter(source.children);
|
|
2932
|
+
}
|
|
2918
2933
|
}
|
|
2919
2934
|
|
|
2920
2935
|
/*!
|
|
2921
2936
|
* Wunderbaum - ext-grid
|
|
2922
2937
|
* Copyright (c) 2021-2024, Martin Wendt. Released under the MIT license.
|
|
2923
|
-
* v0.
|
|
2938
|
+
* v0.12.0, Sun, 12 Jan 2025 10:51:41 GMT (https://github.com/mar10/wunderbaum)
|
|
2924
2939
|
*/
|
|
2925
2940
|
class GridExtension extends WunderbaumExtension {
|
|
2926
2941
|
constructor(tree) {
|
|
@@ -3011,7 +3026,7 @@ class GridExtension extends WunderbaumExtension {
|
|
|
3011
3026
|
/*!
|
|
3012
3027
|
* Wunderbaum - deferred
|
|
3013
3028
|
* Copyright (c) 2021-2024, Martin Wendt. Released under the MIT license.
|
|
3014
|
-
* v0.
|
|
3029
|
+
* v0.12.0, Sun, 12 Jan 2025 10:51:41 GMT (https://github.com/mar10/wunderbaum)
|
|
3015
3030
|
*/
|
|
3016
3031
|
/**
|
|
3017
3032
|
* Implement a ES6 Promise, that exposes a resolve() and reject() method.
|
|
@@ -3064,7 +3079,7 @@ class Deferred {
|
|
|
3064
3079
|
/*!
|
|
3065
3080
|
* Wunderbaum - wunderbaum_node
|
|
3066
3081
|
* Copyright (c) 2021-2024, Martin Wendt. Released under the MIT license.
|
|
3067
|
-
* v0.
|
|
3082
|
+
* v0.12.0, Sun, 12 Jan 2025 10:51:41 GMT (https://github.com/mar10/wunderbaum)
|
|
3068
3083
|
*/
|
|
3069
3084
|
/** WunderbaumNode properties that can be passed with source data.
|
|
3070
3085
|
* (Any other source properties will be stored as `node.data.PROP`.)
|
|
@@ -3092,6 +3107,20 @@ const NODE_PROPS = new Set([
|
|
|
3092
3107
|
const NODE_DICT_PROPS = new Set(NODE_PROPS);
|
|
3093
3108
|
NODE_DICT_PROPS.delete("_partsel");
|
|
3094
3109
|
NODE_DICT_PROPS.delete("unselectable");
|
|
3110
|
+
// /** Node properties that are of type bool (or boolean & string).
|
|
3111
|
+
// * When parsing, we accept 0 for false and 1 for true for better JSON compression.
|
|
3112
|
+
// */
|
|
3113
|
+
// export const NODE_BOOL_PROPS: Set<string> = new Set([
|
|
3114
|
+
// "checkbox",
|
|
3115
|
+
// "colspan",
|
|
3116
|
+
// "expanded",
|
|
3117
|
+
// "icon",
|
|
3118
|
+
// "iconTooltip",
|
|
3119
|
+
// "radiogroup",
|
|
3120
|
+
// "selected",
|
|
3121
|
+
// "tooltip",
|
|
3122
|
+
// "unselectable",
|
|
3123
|
+
// ]);
|
|
3095
3124
|
/**
|
|
3096
3125
|
* A single tree node.
|
|
3097
3126
|
*
|
|
@@ -3133,20 +3162,26 @@ class WunderbaumNode {
|
|
|
3133
3162
|
this.parent = parent;
|
|
3134
3163
|
this.key = "" + ((_a = data.key) !== null && _a !== void 0 ? _a : ++WunderbaumNode.sequence);
|
|
3135
3164
|
this.title = "" + ((_b = data.title) !== null && _b !== void 0 ? _b : "<" + this.key + ">");
|
|
3165
|
+
this.expanded = !!data.expanded;
|
|
3166
|
+
this.lazy = !!data.lazy;
|
|
3167
|
+
// We set the following node properties only if a matching data value is
|
|
3168
|
+
// passed
|
|
3136
3169
|
data.refKey != null ? (this.refKey = "" + data.refKey) : 0;
|
|
3137
3170
|
data.type != null ? (this.type = "" + data.type) : 0;
|
|
3138
|
-
this.
|
|
3139
|
-
data.
|
|
3140
|
-
|
|
3171
|
+
data.icon != null ? (this.icon = intToBool(data.icon)) : 0;
|
|
3172
|
+
data.tooltip != null ? (this.tooltip = intToBool(data.tooltip)) : 0;
|
|
3173
|
+
data.iconTooltip != null
|
|
3174
|
+
? (this.iconTooltip = intToBool(data.iconTooltip))
|
|
3175
|
+
: 0;
|
|
3141
3176
|
data.statusNodeType != null
|
|
3142
3177
|
? (this.statusNodeType = ("" + data.statusNodeType))
|
|
3143
3178
|
: 0;
|
|
3144
3179
|
data.colspan != null ? (this.colspan = !!data.colspan) : 0;
|
|
3145
3180
|
// Selection
|
|
3146
|
-
data.checkbox != null ? (
|
|
3181
|
+
data.checkbox != null ? intToBool(data.checkbox) : 0;
|
|
3147
3182
|
data.radiogroup != null ? (this.radiogroup = !!data.radiogroup) : 0;
|
|
3148
|
-
this.selected = data.selected
|
|
3149
|
-
data.unselectable
|
|
3183
|
+
data.selected != null ? (this.selected = !!data.selected) : 0;
|
|
3184
|
+
data.unselectable != null ? (this.unselectable = !!data.unselectable) : 0;
|
|
3150
3185
|
if (data.classes) {
|
|
3151
3186
|
this.setClass(data.classes);
|
|
3152
3187
|
}
|
|
@@ -3376,54 +3411,65 @@ class WunderbaumNode {
|
|
|
3376
3411
|
startEditTitle() {
|
|
3377
3412
|
this.tree._callMethod("edit.startEditTitle", this);
|
|
3378
3413
|
}
|
|
3379
|
-
/**
|
|
3414
|
+
/**
|
|
3415
|
+
* Call `setExpanded()` on all descendant nodes.
|
|
3416
|
+
*
|
|
3417
|
+
* @param flag true to expand, false to collapse.
|
|
3418
|
+
* @param options Additional options.
|
|
3419
|
+
* @see {@link Wunderbaum.expandAll}
|
|
3420
|
+
* @see {@link WunderbaumNode.setExpanded}
|
|
3421
|
+
*/
|
|
3380
3422
|
async expandAll(flag = true, options) {
|
|
3381
3423
|
const tree = this.tree;
|
|
3382
|
-
const
|
|
3383
|
-
|
|
3424
|
+
const { collapseOthers, deep, depth, force, keepActiveNodeVisible = true, loadLazy, resetLazy, } = options !== null && options !== void 0 ? options : {};
|
|
3425
|
+
// limit expansion level to `depth` (or tree.minExpandLevel). Default: unlimited
|
|
3426
|
+
const treeLevel = this.tree.options.minExpandLevel || null; // 0 -> null
|
|
3427
|
+
const minLevel = depth !== null && depth !== void 0 ? depth : (force ? null : treeLevel);
|
|
3384
3428
|
const expandOpts = {
|
|
3385
|
-
|
|
3429
|
+
deep: deep,
|
|
3386
3430
|
force: force,
|
|
3387
3431
|
loadLazy: loadLazy,
|
|
3432
|
+
resetLazy: resetLazy,
|
|
3433
|
+
scrollIntoView: false, // don't scroll every node while iterating
|
|
3388
3434
|
};
|
|
3389
|
-
|
|
3435
|
+
this.logInfo(`expandAll(${flag}, depth=${depth}, minLevel=${minLevel})`);
|
|
3436
|
+
assert(!(flag && deep != null && !collapseOthers), "Expanding with `deep` option is not supported (implied by the `depth` option).");
|
|
3390
3437
|
// Expand all direct children in parallel:
|
|
3391
3438
|
async function _iter(n, level) {
|
|
3392
3439
|
var _a;
|
|
3393
|
-
// n.logInfo(` _iter(
|
|
3394
|
-
if (level === 0) {
|
|
3395
|
-
return;
|
|
3396
|
-
}
|
|
3397
|
-
// if (!flag && minExpandLevel && !force && n.getLevel() <= minExpandLevel) {
|
|
3398
|
-
// return; // Do not collapse until minExpandLevel
|
|
3399
|
-
// }
|
|
3400
|
-
const level_1 = level == null ? null : level - 1;
|
|
3440
|
+
// n.logInfo(` _iter(level=${level})`);
|
|
3401
3441
|
const promises = [];
|
|
3402
3442
|
(_a = n.children) === null || _a === void 0 ? void 0 : _a.forEach((cn) => {
|
|
3403
3443
|
if (flag) {
|
|
3404
|
-
if (!cn.expanded &&
|
|
3444
|
+
if (!cn.expanded &&
|
|
3445
|
+
(minLevel == null || level < minLevel) &&
|
|
3446
|
+
(cn.children || (loadLazy && cn.lazy))) {
|
|
3405
3447
|
// Node is collapsed and may be expanded (i.e. has children or is lazy)
|
|
3406
3448
|
// Expanding may be async, so we store the promise.
|
|
3407
3449
|
// Also the recursion is delayed until expansion finished.
|
|
3408
3450
|
const p = cn.setExpanded(true, expandOpts);
|
|
3409
3451
|
promises.push(p);
|
|
3410
|
-
|
|
3411
|
-
|
|
3412
|
-
|
|
3452
|
+
if (depth == null) {
|
|
3453
|
+
p.then(async () => {
|
|
3454
|
+
await _iter(cn, level + 1);
|
|
3455
|
+
});
|
|
3456
|
+
}
|
|
3413
3457
|
}
|
|
3414
3458
|
else {
|
|
3415
3459
|
// We don't expand the node, but still visit descendants.
|
|
3416
3460
|
// There we may find lazy nodes, so we
|
|
3417
|
-
promises.push(_iter(cn,
|
|
3461
|
+
promises.push(_iter(cn, level + 1));
|
|
3418
3462
|
}
|
|
3419
3463
|
}
|
|
3420
3464
|
else {
|
|
3421
3465
|
// Collapsing is always synchronous, so no promises required
|
|
3422
|
-
|
|
3423
|
-
|
|
3466
|
+
// Do not collapse until minExpandLevel
|
|
3467
|
+
if (minLevel == null || level >= minLevel) {
|
|
3424
3468
|
cn.setExpanded(false, expandOpts);
|
|
3425
3469
|
}
|
|
3426
|
-
|
|
3470
|
+
if ((minLevel != null && level < minLevel) || deep) {
|
|
3471
|
+
_iter(cn, level + 1); // recursion, even if cn was already collapsed
|
|
3472
|
+
}
|
|
3427
3473
|
}
|
|
3428
3474
|
});
|
|
3429
3475
|
return new Promise((resolve) => {
|
|
@@ -3432,10 +3478,15 @@ class WunderbaumNode {
|
|
|
3432
3478
|
});
|
|
3433
3479
|
});
|
|
3434
3480
|
}
|
|
3435
|
-
const tag = tree.logTime(`${this}.expandAll(${flag})`);
|
|
3481
|
+
const tag = tree.logTime(`${this}.expandAll(${flag}, depth=${depth})`);
|
|
3436
3482
|
try {
|
|
3437
3483
|
tree.enableUpdate(false);
|
|
3438
|
-
await _iter(this,
|
|
3484
|
+
await _iter(this, 0);
|
|
3485
|
+
if (collapseOthers) {
|
|
3486
|
+
assert(flag, "Option `collapseOthers` requires flag=true");
|
|
3487
|
+
assert(minLevel != null, "Option `collapseOthers` requires `depth` or `minExpandLevel`");
|
|
3488
|
+
this.expandAll(false, { depth: minLevel });
|
|
3489
|
+
}
|
|
3439
3490
|
}
|
|
3440
3491
|
finally {
|
|
3441
3492
|
tree.enableUpdate(true);
|
|
@@ -3975,8 +4026,8 @@ class WunderbaumNode {
|
|
|
3975
4026
|
let elap = 0, elapLoad = 0, elapProcess = 0;
|
|
3976
4027
|
// Check for overlapping requests
|
|
3977
4028
|
if (this._requestId) {
|
|
3978
|
-
this.logWarn(`Recursive load request #${requestId} while #${this._requestId} is pending
|
|
3979
|
-
|
|
4029
|
+
this.logWarn(`Recursive load request #${requestId} while #${this._requestId} is pending. ` +
|
|
4030
|
+
"The previous request will be ignored.");
|
|
3980
4031
|
}
|
|
3981
4032
|
this._requestId = requestId;
|
|
3982
4033
|
// const timerLabel = tree.logTime(this + ".load()");
|
|
@@ -4456,6 +4507,7 @@ class WunderbaumNode {
|
|
|
4456
4507
|
_render_markup(opts) {
|
|
4457
4508
|
const tree = this.tree;
|
|
4458
4509
|
const treeOptions = tree.options;
|
|
4510
|
+
const rowHeight = treeOptions.rowHeightPx;
|
|
4459
4511
|
const checkbox = this.getOption("checkbox");
|
|
4460
4512
|
const columns = tree.columns;
|
|
4461
4513
|
const level = this.getLevel();
|
|
@@ -4470,7 +4522,7 @@ class WunderbaumNode {
|
|
|
4470
4522
|
assert(!this.isRootNode(), "Root node not allowed");
|
|
4471
4523
|
rowDiv = document.createElement("div");
|
|
4472
4524
|
rowDiv.classList.add("wb-row");
|
|
4473
|
-
rowDiv.style.top = this._rowIdx *
|
|
4525
|
+
rowDiv.style.top = this._rowIdx * rowHeight + "px";
|
|
4474
4526
|
this._rowElem = rowDiv;
|
|
4475
4527
|
// Attach a node reference to the DOM Element:
|
|
4476
4528
|
rowDiv._wb_node = this;
|
|
@@ -4923,7 +4975,7 @@ class WunderbaumNode {
|
|
|
4923
4975
|
const orgEvent = options === null || options === void 0 ? void 0 : options.event; // Default: null
|
|
4924
4976
|
const colIdx = options === null || options === void 0 ? void 0 : options.colIdx; // Default: null
|
|
4925
4977
|
const edit = options === null || options === void 0 ? void 0 : options.edit; // Default: false
|
|
4926
|
-
assert(!colIdx || tree.isCellNav(), "colIdx requires cellNav");
|
|
4978
|
+
// util.assert(!colIdx || tree.isCellNav(), "colIdx requires cellNav");
|
|
4927
4979
|
assert(!edit || colIdx != null, "edit requires colIdx");
|
|
4928
4980
|
if (!noEvents) {
|
|
4929
4981
|
if (flag) {
|
|
@@ -4977,7 +5029,7 @@ class WunderbaumNode {
|
|
|
4977
5029
|
* Expand or collapse this node.
|
|
4978
5030
|
*/
|
|
4979
5031
|
async setExpanded(flag = true, options) {
|
|
4980
|
-
const { force, scrollIntoView, immediate } = options !== null && options !== void 0 ? options : {};
|
|
5032
|
+
const { force, scrollIntoView, immediate, resetLazy } = options !== null && options !== void 0 ? options : {};
|
|
4981
5033
|
const sendEvents = !(options === null || options === void 0 ? void 0 : options.noEvents); // Default: send events
|
|
4982
5034
|
if (!flag &&
|
|
4983
5035
|
this.isExpanded() &&
|
|
@@ -5000,6 +5052,9 @@ class WunderbaumNode {
|
|
|
5000
5052
|
if (flag && this.lazy && this.children == null) {
|
|
5001
5053
|
await this.loadLazy();
|
|
5002
5054
|
}
|
|
5055
|
+
else if (!flag && resetLazy && this.lazy && this.children) {
|
|
5056
|
+
this.resetLazy();
|
|
5057
|
+
}
|
|
5003
5058
|
this.expanded = flag;
|
|
5004
5059
|
const updateOpts = { immediate: immediate };
|
|
5005
5060
|
// const updateOpts = { immediate: !!util.getOption(options, "immediate") };
|
|
@@ -5432,6 +5487,21 @@ class WunderbaumNode {
|
|
|
5432
5487
|
av = a.data[propName];
|
|
5433
5488
|
bv = b.data[propName];
|
|
5434
5489
|
}
|
|
5490
|
+
if (av == null && bv == null) {
|
|
5491
|
+
return 0;
|
|
5492
|
+
}
|
|
5493
|
+
if (av == null) {
|
|
5494
|
+
av = typeof bv === "string" ? "" : 0;
|
|
5495
|
+
}
|
|
5496
|
+
else if (typeof av === "boolean") {
|
|
5497
|
+
av = av ? 1 : 0;
|
|
5498
|
+
}
|
|
5499
|
+
if (bv == null) {
|
|
5500
|
+
bv = typeof av === "string" ? "" : 0;
|
|
5501
|
+
}
|
|
5502
|
+
else if (typeof bv === "boolean") {
|
|
5503
|
+
bv = bv ? 1 : 0;
|
|
5504
|
+
}
|
|
5435
5505
|
if (caseInsensitive) {
|
|
5436
5506
|
if (typeof av === "string") {
|
|
5437
5507
|
av = av.toLowerCase();
|
|
@@ -5555,7 +5625,7 @@ WunderbaumNode.sequence = 0;
|
|
|
5555
5625
|
/*!
|
|
5556
5626
|
* Wunderbaum - ext-edit
|
|
5557
5627
|
* Copyright (c) 2021-2024, Martin Wendt. Released under the MIT license.
|
|
5558
|
-
* v0.
|
|
5628
|
+
* v0.12.0, Sun, 12 Jan 2025 10:51:41 GMT (https://github.com/mar10/wunderbaum)
|
|
5559
5629
|
*/
|
|
5560
5630
|
// const START_MARKER = "\uFFF7";
|
|
5561
5631
|
class EditExtension extends WunderbaumExtension {
|
|
@@ -5719,6 +5789,10 @@ class EditExtension extends WunderbaumExtension {
|
|
|
5719
5789
|
if (!node) {
|
|
5720
5790
|
return;
|
|
5721
5791
|
}
|
|
5792
|
+
if (node.isStatusNode()) {
|
|
5793
|
+
node.logWarn("Cannot edit status node.");
|
|
5794
|
+
return;
|
|
5795
|
+
}
|
|
5722
5796
|
this.tree.logDebug(`startEditTitle(node=${node})`);
|
|
5723
5797
|
let inputHtml = node._callEvent("edit.beforeEdit");
|
|
5724
5798
|
if (inputHtml === false) {
|
|
@@ -5886,8 +5960,8 @@ class EditExtension extends WunderbaumExtension {
|
|
|
5886
5960
|
* https://github.com/mar10/wunderbaum
|
|
5887
5961
|
*
|
|
5888
5962
|
* Released under the MIT license.
|
|
5889
|
-
* @version v0.
|
|
5890
|
-
* @date Sun,
|
|
5963
|
+
* @version v0.12.0
|
|
5964
|
+
* @date Sun, 12 Jan 2025 10:51:41 GMT
|
|
5891
5965
|
*/
|
|
5892
5966
|
// import "./wunderbaum.scss";
|
|
5893
5967
|
class WbSystemRoot extends WunderbaumNode {
|
|
@@ -5969,7 +6043,7 @@ class Wunderbaum {
|
|
|
5969
6043
|
debugLevel: DEFAULT_DEBUGLEVEL, // 0:quiet, 1:errors, 2:warnings, 3:info, 4:verbose
|
|
5970
6044
|
header: null, // Show/hide header (pass bool or string)
|
|
5971
6045
|
// headerHeightPx: ROW_HEIGHT,
|
|
5972
|
-
rowHeightPx:
|
|
6046
|
+
rowHeightPx: DEFAULT_ROW_HEIGHT,
|
|
5973
6047
|
iconMap: "bootstrap",
|
|
5974
6048
|
columns: null,
|
|
5975
6049
|
types: null,
|
|
@@ -6054,6 +6128,10 @@ class Wunderbaum {
|
|
|
6054
6128
|
if (!this.element.getAttribute("tabindex")) {
|
|
6055
6129
|
this.element.tabIndex = 0;
|
|
6056
6130
|
}
|
|
6131
|
+
if (opts.rowHeightPx !== DEFAULT_ROW_HEIGHT) {
|
|
6132
|
+
this.element.style.setProperty("--wb-row-outer-height", opts.rowHeightPx + "px");
|
|
6133
|
+
this.element.style.setProperty("--wb-row-inner-height", opts.rowHeightPx - 2 + "px");
|
|
6134
|
+
}
|
|
6057
6135
|
// Attach tree instance to <div>
|
|
6058
6136
|
this.element._wb_tree = this;
|
|
6059
6137
|
// Create header markup, or take it from the existing html
|
|
@@ -6216,7 +6294,10 @@ class Wunderbaum {
|
|
|
6216
6294
|
false) {
|
|
6217
6295
|
return false;
|
|
6218
6296
|
}
|
|
6219
|
-
if (node &&
|
|
6297
|
+
if (node &&
|
|
6298
|
+
info.colIdx === 0 &&
|
|
6299
|
+
node.isExpandable() &&
|
|
6300
|
+
info.region !== NodeRegion.expander) {
|
|
6220
6301
|
this._callMethod("edit._stopEditTitle");
|
|
6221
6302
|
node.setExpanded(!node.isExpanded());
|
|
6222
6303
|
}
|
|
@@ -6463,31 +6544,33 @@ class Wunderbaum {
|
|
|
6463
6544
|
}
|
|
6464
6545
|
/** Return the topmost visible node in the viewport. */
|
|
6465
6546
|
getTopmostVpNode(complete = true) {
|
|
6547
|
+
const rowHeight = this.options.rowHeightPx;
|
|
6466
6548
|
const gracePx = 1; // ignore subpixel scrolling
|
|
6467
6549
|
const scrollParent = this.element;
|
|
6468
6550
|
// const headerHeight = this.headerElement.clientHeight; // May be 0
|
|
6469
6551
|
const scrollTop = scrollParent.scrollTop; // + headerHeight;
|
|
6470
6552
|
let topIdx;
|
|
6471
6553
|
if (complete) {
|
|
6472
|
-
topIdx = Math.ceil((scrollTop - gracePx) /
|
|
6554
|
+
topIdx = Math.ceil((scrollTop - gracePx) / rowHeight);
|
|
6473
6555
|
}
|
|
6474
6556
|
else {
|
|
6475
|
-
topIdx = Math.floor(scrollTop /
|
|
6557
|
+
topIdx = Math.floor(scrollTop / rowHeight);
|
|
6476
6558
|
}
|
|
6477
6559
|
return this._getNodeByRowIdx(topIdx);
|
|
6478
6560
|
}
|
|
6479
6561
|
/** Return the lowest visible node in the viewport. */
|
|
6480
6562
|
getLowestVpNode(complete = true) {
|
|
6563
|
+
const rowHeight = this.options.rowHeightPx;
|
|
6481
6564
|
const scrollParent = this.element;
|
|
6482
6565
|
const headerHeight = this.headerElement.clientHeight; // May be 0
|
|
6483
6566
|
const scrollTop = scrollParent.scrollTop;
|
|
6484
6567
|
const clientHeight = scrollParent.clientHeight - headerHeight;
|
|
6485
6568
|
let bottomIdx;
|
|
6486
6569
|
if (complete) {
|
|
6487
|
-
bottomIdx = Math.floor((scrollTop + clientHeight) /
|
|
6570
|
+
bottomIdx = Math.floor((scrollTop + clientHeight) / rowHeight) - 1;
|
|
6488
6571
|
}
|
|
6489
6572
|
else {
|
|
6490
|
-
bottomIdx = Math.ceil((scrollTop + clientHeight) /
|
|
6573
|
+
bottomIdx = Math.ceil((scrollTop + clientHeight) / rowHeight) - 1;
|
|
6491
6574
|
}
|
|
6492
6575
|
bottomIdx = Math.min(bottomIdx, this.count(true) - 1);
|
|
6493
6576
|
return this._getNodeByRowIdx(bottomIdx);
|
|
@@ -6919,8 +7002,9 @@ class Wunderbaum {
|
|
|
6919
7002
|
* @param includeHidden Not yet implemented
|
|
6920
7003
|
*/
|
|
6921
7004
|
findRelatedNode(node, where, includeHidden = false) {
|
|
7005
|
+
const rowHeight = this.options.rowHeightPx;
|
|
6922
7006
|
let res = null;
|
|
6923
|
-
const pageSize = Math.floor(this.listContainerElement.clientHeight /
|
|
7007
|
+
const pageSize = Math.floor(this.listContainerElement.clientHeight / rowHeight);
|
|
6924
7008
|
switch (where) {
|
|
6925
7009
|
case "parent":
|
|
6926
7010
|
if (node.parent && node.parent.parent) {
|
|
@@ -7243,6 +7327,7 @@ class Wunderbaum {
|
|
|
7243
7327
|
scrollTo(nodeOrOpts) {
|
|
7244
7328
|
const PADDING = 2; // leave some pixels between viewport bounds
|
|
7245
7329
|
let node;
|
|
7330
|
+
// WunderbaumNode;
|
|
7246
7331
|
let options;
|
|
7247
7332
|
if (nodeOrOpts instanceof WunderbaumNode) {
|
|
7248
7333
|
node = nodeOrOpts;
|
|
@@ -7252,14 +7337,15 @@ class Wunderbaum {
|
|
|
7252
7337
|
node = options.node;
|
|
7253
7338
|
}
|
|
7254
7339
|
assert(node && node._rowIdx != null, `Invalid node: ${node}`);
|
|
7340
|
+
const rowHeight = this.options.rowHeightPx;
|
|
7255
7341
|
const scrollParent = this.element;
|
|
7256
7342
|
const headerHeight = this.headerElement.clientHeight; // May be 0
|
|
7257
7343
|
const scrollTop = scrollParent.scrollTop;
|
|
7258
7344
|
const vpHeight = scrollParent.clientHeight;
|
|
7259
|
-
const rowTop = node._rowIdx *
|
|
7345
|
+
const rowTop = node._rowIdx * rowHeight + headerHeight;
|
|
7260
7346
|
const vpTop = headerHeight;
|
|
7261
7347
|
const vpRowTop = rowTop - scrollTop;
|
|
7262
|
-
const vpRowBottom = vpRowTop +
|
|
7348
|
+
const vpRowBottom = vpRowTop + rowHeight;
|
|
7263
7349
|
const topNode = options === null || options === void 0 ? void 0 : options.topNode;
|
|
7264
7350
|
// this.log( `scrollTo(${node.title}), vpTop:${vpTop}px, scrollTop:${scrollTop}, vpHeight:${vpHeight}, rowTop:${rowTop}, vpRowTop:${vpRowTop}`, nodeOrOpts , options);
|
|
7265
7351
|
let newScrollTop = null;
|
|
@@ -7268,7 +7354,7 @@ class Wunderbaum {
|
|
|
7268
7354
|
else {
|
|
7269
7355
|
// Node is below viewport
|
|
7270
7356
|
// this.log("Below viewport");
|
|
7271
|
-
newScrollTop = rowTop +
|
|
7357
|
+
newScrollTop = rowTop + rowHeight - vpHeight + PADDING; // leave some pixels between viewport bounds
|
|
7272
7358
|
}
|
|
7273
7359
|
}
|
|
7274
7360
|
else {
|
|
@@ -7881,19 +7967,19 @@ class Wunderbaum {
|
|
|
7881
7967
|
// this.log("_updateRows", opts)
|
|
7882
7968
|
options = Object.assign({ newNodesOnly: false }, options);
|
|
7883
7969
|
const newNodesOnly = !!options.newNodesOnly;
|
|
7884
|
-
const
|
|
7885
|
-
const
|
|
7970
|
+
const rowHeight = this.options.rowHeightPx;
|
|
7971
|
+
const vpHeight = this.element.clientHeight;
|
|
7886
7972
|
const prefetch = RENDER_MAX_PREFETCH;
|
|
7887
7973
|
// const grace_prefetch = RENDER_MAX_PREFETCH - RENDER_MIN_PREFETCH;
|
|
7888
7974
|
const ofs = this.element.scrollTop;
|
|
7889
|
-
let startIdx = Math.max(0, ofs /
|
|
7975
|
+
let startIdx = Math.max(0, ofs / rowHeight - prefetch);
|
|
7890
7976
|
startIdx = Math.floor(startIdx);
|
|
7891
7977
|
// Make sure start is always even, so the alternating row colors don't
|
|
7892
7978
|
// change when scrolling:
|
|
7893
7979
|
if (startIdx % 2) {
|
|
7894
7980
|
startIdx--;
|
|
7895
7981
|
}
|
|
7896
|
-
let endIdx = Math.max(0, (ofs +
|
|
7982
|
+
let endIdx = Math.max(0, (ofs + vpHeight) / rowHeight + prefetch);
|
|
7897
7983
|
endIdx = Math.ceil(endIdx);
|
|
7898
7984
|
// this.debug("render", opts);
|
|
7899
7985
|
const obsoleteNodes = new Set();
|
|
@@ -7922,21 +8008,21 @@ class Wunderbaum {
|
|
|
7922
8008
|
else if (rowDiv && newNodesOnly) {
|
|
7923
8009
|
obsoleteNodes.delete(node);
|
|
7924
8010
|
// no need to update existing node markup
|
|
7925
|
-
rowDiv.style.top = idx *
|
|
8011
|
+
rowDiv.style.top = idx * rowHeight + "px";
|
|
7926
8012
|
prevElem = rowDiv;
|
|
7927
8013
|
}
|
|
7928
8014
|
else {
|
|
7929
8015
|
obsoleteNodes.delete(node);
|
|
7930
8016
|
// Create new markup
|
|
7931
8017
|
if (rowDiv) {
|
|
7932
|
-
rowDiv.style.top = idx *
|
|
8018
|
+
rowDiv.style.top = idx * rowHeight + "px";
|
|
7933
8019
|
}
|
|
7934
8020
|
node._render({ top: top, after: prevElem });
|
|
7935
8021
|
// node.log("render", top, prevElem, "=>", node._rowElem);
|
|
7936
8022
|
prevElem = node._rowElem;
|
|
7937
8023
|
}
|
|
7938
8024
|
idx++;
|
|
7939
|
-
top +=
|
|
8025
|
+
top += rowHeight;
|
|
7940
8026
|
});
|
|
7941
8027
|
this.treeRowCount = idx;
|
|
7942
8028
|
for (const n of obsoleteNodes) {
|
|
@@ -8200,7 +8286,7 @@ class Wunderbaum {
|
|
|
8200
8286
|
}
|
|
8201
8287
|
Wunderbaum.sequence = 0;
|
|
8202
8288
|
/** Wunderbaum release version number "MAJOR.MINOR.PATCH". */
|
|
8203
|
-
Wunderbaum.version = "v0.
|
|
8289
|
+
Wunderbaum.version = "v0.12.0"; // Set to semver by 'grunt release'
|
|
8204
8290
|
/** Expose some useful methods of the util.ts module as `Wunderbaum.util`. */
|
|
8205
8291
|
Wunderbaum.util = util;
|
|
8206
8292
|
|