wunderbaum 0.11.0 → 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 +4 -5
- package/dist/wunderbaum.css +10 -10
- package/dist/wunderbaum.d.ts +16 -7
- package/dist/wunderbaum.esm.js +675 -611
- package/dist/wunderbaum.esm.min.js +24 -24
- package/dist/wunderbaum.esm.min.js.map +1 -1
- package/dist/wunderbaum.umd.js +675 -611
- 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 +4 -4
- 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 +48 -12
- package/src/wunderbaum.ts +32 -18
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.11.
|
|
291
|
+
* v0.11.1, Fri, 27 Dec 2024 22:58:06 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.11.
|
|
1145
|
+
* v0.11.1, Fri, 27 Dec 2024 22:58:06 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.11.
|
|
1209
|
+
* v0.11.1, Fri, 27 Dec 2024 22:58:06 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.11.
|
|
1268
|
+
* v0.11.1, Fri, 27 Dec 2024 22:58:06 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.11.
|
|
1593
|
+
* v0.11.1, Fri, 27 Dec 2024 22:58:06 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.11.
|
|
1957
|
+
* v0.11.1, Fri, 27 Dec 2024 22:58:06 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.11.
|
|
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.11.1, Fri, 27 Dec 2024 22:58:06 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 ---
|
|
@@ -2728,199 +2403,539 @@ class DndExtension extends WunderbaumExtension {
|
|
|
2728
2403
|
if (!region || this._isVoidDrop(targetNode, srcNode, region)) {
|
|
2729
2404
|
return; // We already rejected in dragenter
|
|
2730
2405
|
}
|
|
2731
|
-
targetNode.setClass("wb-drop-over", region === "over");
|
|
2732
|
-
targetNode.setClass("wb-drop-before", region === "before");
|
|
2733
|
-
targetNode.setClass("wb-drop-after", region === "after");
|
|
2734
|
-
e.preventDefault(); // Allow drop (Drop operation is denied by default)
|
|
2735
|
-
return false;
|
|
2736
|
-
// --- dragleave ---
|
|
2406
|
+
targetNode.setClass("wb-drop-over", region === "over");
|
|
2407
|
+
targetNode.setClass("wb-drop-before", region === "before");
|
|
2408
|
+
targetNode.setClass("wb-drop-after", region === "after");
|
|
2409
|
+
e.preventDefault(); // Allow drop (Drop operation is denied by default)
|
|
2410
|
+
return false;
|
|
2411
|
+
// --- dragleave ---
|
|
2412
|
+
}
|
|
2413
|
+
else if (e.type === "dragleave") {
|
|
2414
|
+
// NOTE: we cannot trust this event, since it is always fired,
|
|
2415
|
+
// Instead we remove the marker on dragenter
|
|
2416
|
+
targetNode._callEvent("dnd.dragLeave", { event: e, sourceNode: srcNode });
|
|
2417
|
+
// --- drop ---
|
|
2418
|
+
}
|
|
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.11.1, Fri, 27 Dec 2024 22:58:06 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.11.1, Fri, 27 Dec 2024 22:58:06 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
|
+
}
|
|
2737
2840
|
}
|
|
2738
|
-
|
|
2739
|
-
|
|
2740
|
-
|
|
2741
|
-
targetNode._callEvent("dnd.dragLeave", { event: e, sourceNode: srcNode });
|
|
2742
|
-
// --- drop ---
|
|
2841
|
+
if (parentNode) {
|
|
2842
|
+
(_c = parentNode[childrenAttrName]) !== null && _c !== void 0 ? _c : (parentNode[childrenAttrName] = []);
|
|
2843
|
+
parentNode[childrenAttrName].push(kwargs);
|
|
2743
2844
|
}
|
|
2744
|
-
else
|
|
2745
|
-
|
|
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);
|
|
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.11.
|
|
2938
|
+
* v0.11.1, Fri, 27 Dec 2024 22:58:06 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.11.
|
|
3029
|
+
* v0.11.1, Fri, 27 Dec 2024 22:58:06 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.11.
|
|
3082
|
+
* v0.11.1, Fri, 27 Dec 2024 22:58:06 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
|
}
|
|
@@ -3975,8 +4010,8 @@ class WunderbaumNode {
|
|
|
3975
4010
|
let elap = 0, elapLoad = 0, elapProcess = 0;
|
|
3976
4011
|
// Check for overlapping requests
|
|
3977
4012
|
if (this._requestId) {
|
|
3978
|
-
this.logWarn(`Recursive load request #${requestId} while #${this._requestId} is pending
|
|
3979
|
-
|
|
4013
|
+
this.logWarn(`Recursive load request #${requestId} while #${this._requestId} is pending. ` +
|
|
4014
|
+
"The previous request will be ignored.");
|
|
3980
4015
|
}
|
|
3981
4016
|
this._requestId = requestId;
|
|
3982
4017
|
// const timerLabel = tree.logTime(this + ".load()");
|
|
@@ -4456,6 +4491,7 @@ class WunderbaumNode {
|
|
|
4456
4491
|
_render_markup(opts) {
|
|
4457
4492
|
const tree = this.tree;
|
|
4458
4493
|
const treeOptions = tree.options;
|
|
4494
|
+
const rowHeight = treeOptions.rowHeightPx;
|
|
4459
4495
|
const checkbox = this.getOption("checkbox");
|
|
4460
4496
|
const columns = tree.columns;
|
|
4461
4497
|
const level = this.getLevel();
|
|
@@ -4470,7 +4506,7 @@ class WunderbaumNode {
|
|
|
4470
4506
|
assert(!this.isRootNode(), "Root node not allowed");
|
|
4471
4507
|
rowDiv = document.createElement("div");
|
|
4472
4508
|
rowDiv.classList.add("wb-row");
|
|
4473
|
-
rowDiv.style.top = this._rowIdx *
|
|
4509
|
+
rowDiv.style.top = this._rowIdx * rowHeight + "px";
|
|
4474
4510
|
this._rowElem = rowDiv;
|
|
4475
4511
|
// Attach a node reference to the DOM Element:
|
|
4476
4512
|
rowDiv._wb_node = this;
|
|
@@ -5432,6 +5468,21 @@ class WunderbaumNode {
|
|
|
5432
5468
|
av = a.data[propName];
|
|
5433
5469
|
bv = b.data[propName];
|
|
5434
5470
|
}
|
|
5471
|
+
if (av == null && bv == null) {
|
|
5472
|
+
return 0;
|
|
5473
|
+
}
|
|
5474
|
+
if (av == null) {
|
|
5475
|
+
av = typeof bv === "string" ? "" : 0;
|
|
5476
|
+
}
|
|
5477
|
+
else if (typeof av === "boolean") {
|
|
5478
|
+
av = av ? 1 : 0;
|
|
5479
|
+
}
|
|
5480
|
+
if (bv == null) {
|
|
5481
|
+
bv = typeof av === "string" ? "" : 0;
|
|
5482
|
+
}
|
|
5483
|
+
else if (typeof bv === "boolean") {
|
|
5484
|
+
bv = bv ? 1 : 0;
|
|
5485
|
+
}
|
|
5435
5486
|
if (caseInsensitive) {
|
|
5436
5487
|
if (typeof av === "string") {
|
|
5437
5488
|
av = av.toLowerCase();
|
|
@@ -5555,7 +5606,7 @@ WunderbaumNode.sequence = 0;
|
|
|
5555
5606
|
/*!
|
|
5556
5607
|
* Wunderbaum - ext-edit
|
|
5557
5608
|
* Copyright (c) 2021-2024, Martin Wendt. Released under the MIT license.
|
|
5558
|
-
* v0.11.
|
|
5609
|
+
* v0.11.1, Fri, 27 Dec 2024 22:58:06 GMT (https://github.com/mar10/wunderbaum)
|
|
5559
5610
|
*/
|
|
5560
5611
|
// const START_MARKER = "\uFFF7";
|
|
5561
5612
|
class EditExtension extends WunderbaumExtension {
|
|
@@ -5719,6 +5770,10 @@ class EditExtension extends WunderbaumExtension {
|
|
|
5719
5770
|
if (!node) {
|
|
5720
5771
|
return;
|
|
5721
5772
|
}
|
|
5773
|
+
if (node.isStatusNode()) {
|
|
5774
|
+
node.logWarn("Cannot edit status node.");
|
|
5775
|
+
return;
|
|
5776
|
+
}
|
|
5722
5777
|
this.tree.logDebug(`startEditTitle(node=${node})`);
|
|
5723
5778
|
let inputHtml = node._callEvent("edit.beforeEdit");
|
|
5724
5779
|
if (inputHtml === false) {
|
|
@@ -5886,8 +5941,8 @@ class EditExtension extends WunderbaumExtension {
|
|
|
5886
5941
|
* https://github.com/mar10/wunderbaum
|
|
5887
5942
|
*
|
|
5888
5943
|
* Released under the MIT license.
|
|
5889
|
-
* @version v0.11.
|
|
5890
|
-
* @date
|
|
5944
|
+
* @version v0.11.1
|
|
5945
|
+
* @date Fri, 27 Dec 2024 22:58:06 GMT
|
|
5891
5946
|
*/
|
|
5892
5947
|
// import "./wunderbaum.scss";
|
|
5893
5948
|
class WbSystemRoot extends WunderbaumNode {
|
|
@@ -5969,7 +6024,7 @@ class Wunderbaum {
|
|
|
5969
6024
|
debugLevel: DEFAULT_DEBUGLEVEL, // 0:quiet, 1:errors, 2:warnings, 3:info, 4:verbose
|
|
5970
6025
|
header: null, // Show/hide header (pass bool or string)
|
|
5971
6026
|
// headerHeightPx: ROW_HEIGHT,
|
|
5972
|
-
rowHeightPx:
|
|
6027
|
+
rowHeightPx: DEFAULT_ROW_HEIGHT,
|
|
5973
6028
|
iconMap: "bootstrap",
|
|
5974
6029
|
columns: null,
|
|
5975
6030
|
types: null,
|
|
@@ -6054,6 +6109,10 @@ class Wunderbaum {
|
|
|
6054
6109
|
if (!this.element.getAttribute("tabindex")) {
|
|
6055
6110
|
this.element.tabIndex = 0;
|
|
6056
6111
|
}
|
|
6112
|
+
if (opts.rowHeightPx !== DEFAULT_ROW_HEIGHT) {
|
|
6113
|
+
this.element.style.setProperty("--wb-row-outer-height", opts.rowHeightPx + "px");
|
|
6114
|
+
this.element.style.setProperty("--wb-row-inner-height", opts.rowHeightPx - 2 + "px");
|
|
6115
|
+
}
|
|
6057
6116
|
// Attach tree instance to <div>
|
|
6058
6117
|
this.element._wb_tree = this;
|
|
6059
6118
|
// Create header markup, or take it from the existing html
|
|
@@ -6463,31 +6522,33 @@ class Wunderbaum {
|
|
|
6463
6522
|
}
|
|
6464
6523
|
/** Return the topmost visible node in the viewport. */
|
|
6465
6524
|
getTopmostVpNode(complete = true) {
|
|
6525
|
+
const rowHeight = this.options.rowHeightPx;
|
|
6466
6526
|
const gracePx = 1; // ignore subpixel scrolling
|
|
6467
6527
|
const scrollParent = this.element;
|
|
6468
6528
|
// const headerHeight = this.headerElement.clientHeight; // May be 0
|
|
6469
6529
|
const scrollTop = scrollParent.scrollTop; // + headerHeight;
|
|
6470
6530
|
let topIdx;
|
|
6471
6531
|
if (complete) {
|
|
6472
|
-
topIdx = Math.ceil((scrollTop - gracePx) /
|
|
6532
|
+
topIdx = Math.ceil((scrollTop - gracePx) / rowHeight);
|
|
6473
6533
|
}
|
|
6474
6534
|
else {
|
|
6475
|
-
topIdx = Math.floor(scrollTop /
|
|
6535
|
+
topIdx = Math.floor(scrollTop / rowHeight);
|
|
6476
6536
|
}
|
|
6477
6537
|
return this._getNodeByRowIdx(topIdx);
|
|
6478
6538
|
}
|
|
6479
6539
|
/** Return the lowest visible node in the viewport. */
|
|
6480
6540
|
getLowestVpNode(complete = true) {
|
|
6541
|
+
const rowHeight = this.options.rowHeightPx;
|
|
6481
6542
|
const scrollParent = this.element;
|
|
6482
6543
|
const headerHeight = this.headerElement.clientHeight; // May be 0
|
|
6483
6544
|
const scrollTop = scrollParent.scrollTop;
|
|
6484
6545
|
const clientHeight = scrollParent.clientHeight - headerHeight;
|
|
6485
6546
|
let bottomIdx;
|
|
6486
6547
|
if (complete) {
|
|
6487
|
-
bottomIdx = Math.floor((scrollTop + clientHeight) /
|
|
6548
|
+
bottomIdx = Math.floor((scrollTop + clientHeight) / rowHeight) - 1;
|
|
6488
6549
|
}
|
|
6489
6550
|
else {
|
|
6490
|
-
bottomIdx = Math.ceil((scrollTop + clientHeight) /
|
|
6551
|
+
bottomIdx = Math.ceil((scrollTop + clientHeight) / rowHeight) - 1;
|
|
6491
6552
|
}
|
|
6492
6553
|
bottomIdx = Math.min(bottomIdx, this.count(true) - 1);
|
|
6493
6554
|
return this._getNodeByRowIdx(bottomIdx);
|
|
@@ -6919,8 +6980,9 @@ class Wunderbaum {
|
|
|
6919
6980
|
* @param includeHidden Not yet implemented
|
|
6920
6981
|
*/
|
|
6921
6982
|
findRelatedNode(node, where, includeHidden = false) {
|
|
6983
|
+
const rowHeight = this.options.rowHeightPx;
|
|
6922
6984
|
let res = null;
|
|
6923
|
-
const pageSize = Math.floor(this.listContainerElement.clientHeight /
|
|
6985
|
+
const pageSize = Math.floor(this.listContainerElement.clientHeight / rowHeight);
|
|
6924
6986
|
switch (where) {
|
|
6925
6987
|
case "parent":
|
|
6926
6988
|
if (node.parent && node.parent.parent) {
|
|
@@ -7243,6 +7305,7 @@ class Wunderbaum {
|
|
|
7243
7305
|
scrollTo(nodeOrOpts) {
|
|
7244
7306
|
const PADDING = 2; // leave some pixels between viewport bounds
|
|
7245
7307
|
let node;
|
|
7308
|
+
// WunderbaumNode;
|
|
7246
7309
|
let options;
|
|
7247
7310
|
if (nodeOrOpts instanceof WunderbaumNode) {
|
|
7248
7311
|
node = nodeOrOpts;
|
|
@@ -7252,14 +7315,15 @@ class Wunderbaum {
|
|
|
7252
7315
|
node = options.node;
|
|
7253
7316
|
}
|
|
7254
7317
|
assert(node && node._rowIdx != null, `Invalid node: ${node}`);
|
|
7318
|
+
const rowHeight = this.options.rowHeightPx;
|
|
7255
7319
|
const scrollParent = this.element;
|
|
7256
7320
|
const headerHeight = this.headerElement.clientHeight; // May be 0
|
|
7257
7321
|
const scrollTop = scrollParent.scrollTop;
|
|
7258
7322
|
const vpHeight = scrollParent.clientHeight;
|
|
7259
|
-
const rowTop = node._rowIdx *
|
|
7323
|
+
const rowTop = node._rowIdx * rowHeight + headerHeight;
|
|
7260
7324
|
const vpTop = headerHeight;
|
|
7261
7325
|
const vpRowTop = rowTop - scrollTop;
|
|
7262
|
-
const vpRowBottom = vpRowTop +
|
|
7326
|
+
const vpRowBottom = vpRowTop + rowHeight;
|
|
7263
7327
|
const topNode = options === null || options === void 0 ? void 0 : options.topNode;
|
|
7264
7328
|
// this.log( `scrollTo(${node.title}), vpTop:${vpTop}px, scrollTop:${scrollTop}, vpHeight:${vpHeight}, rowTop:${rowTop}, vpRowTop:${vpRowTop}`, nodeOrOpts , options);
|
|
7265
7329
|
let newScrollTop = null;
|
|
@@ -7268,7 +7332,7 @@ class Wunderbaum {
|
|
|
7268
7332
|
else {
|
|
7269
7333
|
// Node is below viewport
|
|
7270
7334
|
// this.log("Below viewport");
|
|
7271
|
-
newScrollTop = rowTop +
|
|
7335
|
+
newScrollTop = rowTop + rowHeight - vpHeight + PADDING; // leave some pixels between viewport bounds
|
|
7272
7336
|
}
|
|
7273
7337
|
}
|
|
7274
7338
|
else {
|
|
@@ -7881,19 +7945,19 @@ class Wunderbaum {
|
|
|
7881
7945
|
// this.log("_updateRows", opts)
|
|
7882
7946
|
options = Object.assign({ newNodesOnly: false }, options);
|
|
7883
7947
|
const newNodesOnly = !!options.newNodesOnly;
|
|
7884
|
-
const
|
|
7885
|
-
const
|
|
7948
|
+
const rowHeight = this.options.rowHeightPx;
|
|
7949
|
+
const vpHeight = this.element.clientHeight;
|
|
7886
7950
|
const prefetch = RENDER_MAX_PREFETCH;
|
|
7887
7951
|
// const grace_prefetch = RENDER_MAX_PREFETCH - RENDER_MIN_PREFETCH;
|
|
7888
7952
|
const ofs = this.element.scrollTop;
|
|
7889
|
-
let startIdx = Math.max(0, ofs /
|
|
7953
|
+
let startIdx = Math.max(0, ofs / rowHeight - prefetch);
|
|
7890
7954
|
startIdx = Math.floor(startIdx);
|
|
7891
7955
|
// Make sure start is always even, so the alternating row colors don't
|
|
7892
7956
|
// change when scrolling:
|
|
7893
7957
|
if (startIdx % 2) {
|
|
7894
7958
|
startIdx--;
|
|
7895
7959
|
}
|
|
7896
|
-
let endIdx = Math.max(0, (ofs +
|
|
7960
|
+
let endIdx = Math.max(0, (ofs + vpHeight) / rowHeight + prefetch);
|
|
7897
7961
|
endIdx = Math.ceil(endIdx);
|
|
7898
7962
|
// this.debug("render", opts);
|
|
7899
7963
|
const obsoleteNodes = new Set();
|
|
@@ -7922,21 +7986,21 @@ class Wunderbaum {
|
|
|
7922
7986
|
else if (rowDiv && newNodesOnly) {
|
|
7923
7987
|
obsoleteNodes.delete(node);
|
|
7924
7988
|
// no need to update existing node markup
|
|
7925
|
-
rowDiv.style.top = idx *
|
|
7989
|
+
rowDiv.style.top = idx * rowHeight + "px";
|
|
7926
7990
|
prevElem = rowDiv;
|
|
7927
7991
|
}
|
|
7928
7992
|
else {
|
|
7929
7993
|
obsoleteNodes.delete(node);
|
|
7930
7994
|
// Create new markup
|
|
7931
7995
|
if (rowDiv) {
|
|
7932
|
-
rowDiv.style.top = idx *
|
|
7996
|
+
rowDiv.style.top = idx * rowHeight + "px";
|
|
7933
7997
|
}
|
|
7934
7998
|
node._render({ top: top, after: prevElem });
|
|
7935
7999
|
// node.log("render", top, prevElem, "=>", node._rowElem);
|
|
7936
8000
|
prevElem = node._rowElem;
|
|
7937
8001
|
}
|
|
7938
8002
|
idx++;
|
|
7939
|
-
top +=
|
|
8003
|
+
top += rowHeight;
|
|
7940
8004
|
});
|
|
7941
8005
|
this.treeRowCount = idx;
|
|
7942
8006
|
for (const n of obsoleteNodes) {
|
|
@@ -8200,7 +8264,7 @@ class Wunderbaum {
|
|
|
8200
8264
|
}
|
|
8201
8265
|
Wunderbaum.sequence = 0;
|
|
8202
8266
|
/** Wunderbaum release version number "MAJOR.MINOR.PATCH". */
|
|
8203
|
-
Wunderbaum.version = "v0.11.
|
|
8267
|
+
Wunderbaum.version = "v0.11.1"; // Set to semver by 'grunt release'
|
|
8204
8268
|
/** Expose some useful methods of the util.ts module as `Wunderbaum.util`. */
|
|
8205
8269
|
Wunderbaum.util = util;
|
|
8206
8270
|
|