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.umd.js
CHANGED
|
@@ -294,7 +294,7 @@
|
|
|
294
294
|
/*!
|
|
295
295
|
* Wunderbaum - util
|
|
296
296
|
* Copyright (c) 2021-2024, Martin Wendt. Released under the MIT license.
|
|
297
|
-
* v0.
|
|
297
|
+
* v0.12.0, Sun, 12 Jan 2025 10:51:41 GMT (https://github.com/mar10/wunderbaum)
|
|
298
298
|
*/
|
|
299
299
|
/** @module util */
|
|
300
300
|
/** Readable names for `MouseEvent.button` */
|
|
@@ -986,6 +986,15 @@
|
|
|
986
986
|
}
|
|
987
987
|
throw new Error("No default boolean value provided");
|
|
988
988
|
}
|
|
989
|
+
/**
|
|
990
|
+
* Return `val` unless `val` is a number in which case we convert to boolean.
|
|
991
|
+
* This is useful when a boolean value is stored as a 0/1 (e.g. in JSON) and
|
|
992
|
+
* we still want to maintain string values. null and undefined are returned as
|
|
993
|
+
* is. E.g. `checkbox` may be boolean or 'radio'.
|
|
994
|
+
*/
|
|
995
|
+
function intToBool(val) {
|
|
996
|
+
return typeof val === "number" ? !!val : val;
|
|
997
|
+
}
|
|
989
998
|
// /** Check if a string is contained in an Array or Set. */
|
|
990
999
|
// export function isAnyOf(s: string, items: Array<string>|Set<string>): boolean {
|
|
991
1000
|
// return Array.prototype.includes.call(items, s)
|
|
@@ -1114,6 +1123,7 @@
|
|
|
1114
1123
|
extractHtmlText: extractHtmlText,
|
|
1115
1124
|
getOption: getOption,
|
|
1116
1125
|
getValueFromElem: getValueFromElem,
|
|
1126
|
+
intToBool: intToBool,
|
|
1117
1127
|
isArray: isArray,
|
|
1118
1128
|
isEmptyObject: isEmptyObject,
|
|
1119
1129
|
isFunction: isFunction,
|
|
@@ -1138,7 +1148,7 @@
|
|
|
1138
1148
|
/*!
|
|
1139
1149
|
* Wunderbaum - types
|
|
1140
1150
|
* Copyright (c) 2021-2024, Martin Wendt. Released under the MIT license.
|
|
1141
|
-
* v0.
|
|
1151
|
+
* v0.12.0, Sun, 12 Jan 2025 10:51:41 GMT (https://github.com/mar10/wunderbaum)
|
|
1142
1152
|
*/
|
|
1143
1153
|
/**
|
|
1144
1154
|
* Possible values for {@link WunderbaumNode.update} and {@link Wunderbaum.update}.
|
|
@@ -1202,7 +1212,7 @@
|
|
|
1202
1212
|
/*!
|
|
1203
1213
|
* Wunderbaum - wb_extension_base
|
|
1204
1214
|
* Copyright (c) 2021-2024, Martin Wendt. Released under the MIT license.
|
|
1205
|
-
* v0.
|
|
1215
|
+
* v0.12.0, Sun, 12 Jan 2025 10:51:41 GMT (https://github.com/mar10/wunderbaum)
|
|
1206
1216
|
*/
|
|
1207
1217
|
class WunderbaumExtension {
|
|
1208
1218
|
constructor(tree, id, defaults) {
|
|
@@ -1261,7 +1271,7 @@
|
|
|
1261
1271
|
/*!
|
|
1262
1272
|
* Wunderbaum - ext-filter
|
|
1263
1273
|
* Copyright (c) 2021-2024, Martin Wendt. Released under the MIT license.
|
|
1264
|
-
* v0.
|
|
1274
|
+
* v0.12.0, Sun, 12 Jan 2025 10:51:41 GMT (https://github.com/mar10/wunderbaum)
|
|
1265
1275
|
*/
|
|
1266
1276
|
const START_MARKER = "\uFFF7";
|
|
1267
1277
|
const END_MARKER = "\uFFF8";
|
|
@@ -1586,7 +1596,7 @@
|
|
|
1586
1596
|
/*!
|
|
1587
1597
|
* Wunderbaum - ext-keynav
|
|
1588
1598
|
* Copyright (c) 2021-2024, Martin Wendt. Released under the MIT license.
|
|
1589
|
-
* v0.
|
|
1599
|
+
* v0.12.0, Sun, 12 Jan 2025 10:51:41 GMT (https://github.com/mar10/wunderbaum)
|
|
1590
1600
|
*/
|
|
1591
1601
|
const QUICKSEARCH_DELAY = 500;
|
|
1592
1602
|
class KeynavExtension extends WunderbaumExtension {
|
|
@@ -1950,7 +1960,7 @@
|
|
|
1950
1960
|
/*!
|
|
1951
1961
|
* Wunderbaum - ext-logger
|
|
1952
1962
|
* Copyright (c) 2021-2024, Martin Wendt. Released under the MIT license.
|
|
1953
|
-
* v0.
|
|
1963
|
+
* v0.12.0, Sun, 12 Jan 2025 10:51:41 GMT (https://github.com/mar10/wunderbaum)
|
|
1954
1964
|
*/
|
|
1955
1965
|
class LoggerExtension extends WunderbaumExtension {
|
|
1956
1966
|
constructor(tree) {
|
|
@@ -1990,410 +2000,70 @@
|
|
|
1990
2000
|
}
|
|
1991
2001
|
|
|
1992
2002
|
/*!
|
|
1993
|
-
* Wunderbaum -
|
|
2003
|
+
* Wunderbaum - ext-dnd
|
|
1994
2004
|
* Copyright (c) 2021-2024, Martin Wendt. Released under the MIT license.
|
|
1995
|
-
* v0.
|
|
1996
|
-
*/
|
|
1997
|
-
const DEFAULT_DEBUGLEVEL = 3; // Replaced by rollup script
|
|
1998
|
-
/**
|
|
1999
|
-
* Fixed height of a row in pixel. Must match the SCSS variable `$row-outer-height`.
|
|
2000
|
-
*/
|
|
2001
|
-
const ROW_HEIGHT = 22;
|
|
2002
|
-
/**
|
|
2003
|
-
* Fixed width of node icons in pixel. Must match the SCSS variable `$icon-outer-width`.
|
|
2004
|
-
*/
|
|
2005
|
-
const ICON_WIDTH = 20;
|
|
2006
|
-
/**
|
|
2007
|
-
* Adjust the width of the title span, so overflow ellipsis work.
|
|
2008
|
-
* (2 x `$col-padding-x` + 3px rounding errors).
|
|
2009
|
-
*/
|
|
2010
|
-
const TITLE_SPAN_PAD_Y = 7;
|
|
2011
|
-
/** Render row markup for N nodes above and below the visible viewport. */
|
|
2012
|
-
const RENDER_MAX_PREFETCH = 5;
|
|
2013
|
-
/** Minimum column width if not set otherwise. */
|
|
2014
|
-
const DEFAULT_MIN_COL_WIDTH = 4;
|
|
2015
|
-
/** Regular expression to detect if a string describes an image URL (in contrast
|
|
2016
|
-
* to a class name). Strings are considered image urls if they contain '.' or '/'.
|
|
2017
|
-
*/
|
|
2018
|
-
const TEST_IMG = new RegExp(/\.|\//);
|
|
2019
|
-
// export const RECURSIVE_REQUEST_ERROR = "$recursive_request";
|
|
2020
|
-
// export const INVALID_REQUEST_TARGET_ERROR = "$request_target_invalid";
|
|
2021
|
-
/**
|
|
2022
|
-
* Default node icons.
|
|
2023
|
-
* Requires bootstrap icons https://icons.getbootstrap.com
|
|
2024
|
-
*/
|
|
2025
|
-
const iconMaps = {
|
|
2026
|
-
bootstrap: {
|
|
2027
|
-
error: "bi bi-exclamation-triangle",
|
|
2028
|
-
// loading: "bi bi-hourglass-split wb-busy",
|
|
2029
|
-
loading: "bi bi-chevron-right wb-busy",
|
|
2030
|
-
// loading: "bi bi-arrow-repeat wb-spin",
|
|
2031
|
-
// loading: '<div class="spinner-border spinner-border-sm" role="status"> <span class="visually-hidden">Loading...</span> </div>',
|
|
2032
|
-
// noData: "bi bi-search",
|
|
2033
|
-
noData: "bi bi-question-circle",
|
|
2034
|
-
expanderExpanded: "bi bi-chevron-down",
|
|
2035
|
-
// expanderExpanded: "bi bi-dash-square",
|
|
2036
|
-
expanderCollapsed: "bi bi-chevron-right",
|
|
2037
|
-
// expanderCollapsed: "bi bi-plus-square",
|
|
2038
|
-
expanderLazy: "bi bi-chevron-right wb-helper-lazy-expander",
|
|
2039
|
-
// expanderLazy: "bi bi-chevron-bar-right",
|
|
2040
|
-
checkChecked: "bi bi-check-square",
|
|
2041
|
-
checkUnchecked: "bi bi-square",
|
|
2042
|
-
checkUnknown: "bi bi-dash-square-dotted",
|
|
2043
|
-
radioChecked: "bi bi-circle-fill",
|
|
2044
|
-
radioUnchecked: "bi bi-circle",
|
|
2045
|
-
radioUnknown: "bi bi-record-circle",
|
|
2046
|
-
folder: "bi bi-folder2",
|
|
2047
|
-
folderOpen: "bi bi-folder2-open",
|
|
2048
|
-
folderLazy: "bi bi-folder-symlink",
|
|
2049
|
-
doc: "bi bi-file-earmark",
|
|
2050
|
-
colSortable: "bi bi-chevron-expand",
|
|
2051
|
-
// colSortable: "bi bi-arrow-down-up",
|
|
2052
|
-
// colSortAsc: "bi bi-chevron-down",
|
|
2053
|
-
// colSortDesc: "bi bi-chevron-up",
|
|
2054
|
-
colSortAsc: "bi bi-arrow-down",
|
|
2055
|
-
colSortDesc: "bi bi-arrow-up",
|
|
2056
|
-
colFilter: "bi bi-filter-circle",
|
|
2057
|
-
colFilterActive: "bi bi-filter-circle-fill wb-helper-invalid",
|
|
2058
|
-
colMenu: "bi bi-three-dots-vertical",
|
|
2059
|
-
},
|
|
2060
|
-
fontawesome6: {
|
|
2061
|
-
error: "fa-solid fa-triangle-exclamation",
|
|
2062
|
-
loading: "fa-solid fa-chevron-right fa-beat",
|
|
2063
|
-
noData: "fa-solid fa-circle-question",
|
|
2064
|
-
expanderExpanded: "fa-solid fa-chevron-down",
|
|
2065
|
-
expanderCollapsed: "fa-solid fa-chevron-right",
|
|
2066
|
-
expanderLazy: "fa-solid fa-chevron-right wb-helper-lazy-expander",
|
|
2067
|
-
checkChecked: "fa-regular fa-square-check",
|
|
2068
|
-
checkUnchecked: "fa-regular fa-square",
|
|
2069
|
-
checkUnknown: "fa-regular fa-square-minus",
|
|
2070
|
-
radioChecked: "fa-solid fa-circle",
|
|
2071
|
-
radioUnchecked: "fa-regular fa-circle",
|
|
2072
|
-
radioUnknown: "fa-regular fa-circle-question",
|
|
2073
|
-
folder: "fa-solid fa-folder-closed",
|
|
2074
|
-
folderOpen: "fa-regular fa-folder-open",
|
|
2075
|
-
folderLazy: "fa-solid fa-folder-plus",
|
|
2076
|
-
doc: "fa-regular fa-file",
|
|
2077
|
-
colSortable: "fa-solid fa-fw fa-sort",
|
|
2078
|
-
colSortAsc: "fa-solid fa-fw fa-sort-up",
|
|
2079
|
-
colSortDesc: "fa-solid fa-fw fa-sort-down",
|
|
2080
|
-
colFilter: "fa-solid fa-fw fa-filter",
|
|
2081
|
-
colFilterActive: "fa-solid fa-fw fa-filter wb-helper-invalid",
|
|
2082
|
-
colMenu: "fa-solid fa-fw fa-ellipsis-v",
|
|
2083
|
-
},
|
|
2084
|
-
};
|
|
2085
|
-
/** Dict keys that are evaluated by source loader (others are added to `tree.data` instead). */
|
|
2086
|
-
const RESERVED_TREE_SOURCE_KEYS = new Set([
|
|
2087
|
-
"_format", // reserved for future use
|
|
2088
|
-
"_keyMap", // Used for compressed data format
|
|
2089
|
-
"_positional", // Used for compressed data format
|
|
2090
|
-
"_typeList", // Used for compressed data format @deprecated
|
|
2091
|
-
"_valueMap", // Used for compressed data format
|
|
2092
|
-
"_version", // reserved for future use
|
|
2093
|
-
"children",
|
|
2094
|
-
"columns",
|
|
2095
|
-
"types",
|
|
2096
|
-
]);
|
|
2097
|
-
// /** Key codes that trigger grid navigation, even when inside an input element. */
|
|
2098
|
-
// export const INPUT_BREAKOUT_KEYS: Set<string> = new Set([
|
|
2099
|
-
// // "ArrowDown",
|
|
2100
|
-
// // "ArrowUp",
|
|
2101
|
-
// "Enter",
|
|
2102
|
-
// "Escape",
|
|
2103
|
-
// ]);
|
|
2104
|
-
/** Map `KeyEvent.key` to navigation action. */
|
|
2105
|
-
const KEY_TO_ACTION_DICT = {
|
|
2106
|
-
" ": "toggleSelect",
|
|
2107
|
-
"+": "expand",
|
|
2108
|
-
Add: "expand",
|
|
2109
|
-
ArrowDown: "down",
|
|
2110
|
-
ArrowLeft: "left",
|
|
2111
|
-
ArrowRight: "right",
|
|
2112
|
-
ArrowUp: "up",
|
|
2113
|
-
Backspace: "parent",
|
|
2114
|
-
"/": "collapseAll",
|
|
2115
|
-
Divide: "collapseAll",
|
|
2116
|
-
End: "lastCol",
|
|
2117
|
-
Home: "firstCol",
|
|
2118
|
-
"Control+End": "last",
|
|
2119
|
-
"Control+Home": "first",
|
|
2120
|
-
"Meta+ArrowDown": "last", // macOs
|
|
2121
|
-
"Meta+ArrowUp": "first", // macOs
|
|
2122
|
-
"*": "expandAll",
|
|
2123
|
-
Multiply: "expandAll",
|
|
2124
|
-
PageDown: "pageDown",
|
|
2125
|
-
PageUp: "pageUp",
|
|
2126
|
-
"-": "collapse",
|
|
2127
|
-
Subtract: "collapse",
|
|
2128
|
-
};
|
|
2129
|
-
/** Return a callback that returns true if the node title matches the string
|
|
2130
|
-
* or regular expression.
|
|
2131
|
-
* @see {@link WunderbaumNode.findAll}
|
|
2132
|
-
*/
|
|
2133
|
-
function makeNodeTitleMatcher(match) {
|
|
2134
|
-
if (match instanceof RegExp) {
|
|
2135
|
-
return function (node) {
|
|
2136
|
-
return match.test(node.title);
|
|
2137
|
-
};
|
|
2138
|
-
}
|
|
2139
|
-
assert(typeof match === "string", `Expected a string or RegExp: ${match}`);
|
|
2140
|
-
// s = escapeRegex(s.toLowerCase());
|
|
2141
|
-
return function (node) {
|
|
2142
|
-
return node.title === match;
|
|
2143
|
-
// console.log("match " + node, node.title.toLowerCase().indexOf(match))
|
|
2144
|
-
// return node.title.toLowerCase().indexOf(match) >= 0;
|
|
2145
|
-
};
|
|
2146
|
-
}
|
|
2147
|
-
/** Return a callback that returns true if the node title starts with a string (case-insensitive). */
|
|
2148
|
-
function makeNodeTitleStartMatcher(s) {
|
|
2149
|
-
s = escapeRegex(s);
|
|
2150
|
-
const reMatch = new RegExp("^" + s, "i");
|
|
2151
|
-
return function (node) {
|
|
2152
|
-
return reMatch.test(node.title);
|
|
2153
|
-
};
|
|
2154
|
-
}
|
|
2155
|
-
/** Compare two nodes by title (case-insensitive). */
|
|
2156
|
-
function nodeTitleSorter(a, b) {
|
|
2157
|
-
const x = a.title.toLowerCase();
|
|
2158
|
-
const y = b.title.toLowerCase();
|
|
2159
|
-
return x === y ? 0 : x > y ? 1 : -1;
|
|
2160
|
-
}
|
|
2161
|
-
/**
|
|
2162
|
-
* Convert 'flat' to 'nested' format.
|
|
2163
|
-
*
|
|
2164
|
-
* Flat node entry format:
|
|
2165
|
-
* [PARENT_ID, [POSITIONAL_ARGS]]
|
|
2166
|
-
* or
|
|
2167
|
-
* [PARENT_ID, [POSITIONAL_ARGS], {KEY_VALUE_ARGS}]
|
|
2168
|
-
*
|
|
2169
|
-
* 1. Parent-referencing list is converted to a list of nested dicts with
|
|
2170
|
-
* optional `children` properties.
|
|
2171
|
-
* 2. `[POSITIONAL_ARGS]` are added as dict attributes.
|
|
2005
|
+
* v0.12.0, Sun, 12 Jan 2025 10:51:41 GMT (https://github.com/mar10/wunderbaum)
|
|
2172
2006
|
*/
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2007
|
+
const nodeMimeType = "application/x-wunderbaum-node";
|
|
2008
|
+
class DndExtension extends WunderbaumExtension {
|
|
2009
|
+
constructor(tree) {
|
|
2010
|
+
super(tree, "dnd", {
|
|
2011
|
+
autoExpandMS: 1500, // Expand nodes after n milliseconds of hovering
|
|
2012
|
+
// dropMarkerInsertOffsetX: -16, // Additional offset for drop-marker with hitMode = "before"/"after"
|
|
2013
|
+
// dropMarkerOffsetX: -24, // Absolute position offset for .fancytree-drop-marker relatively to ..fancytree-title (icon/img near a node accepting drop)
|
|
2014
|
+
// #1021 `document.body` is not available yet
|
|
2015
|
+
// dropMarkerParent: "body", // Root Container used for drop marker (could be a shadow root)
|
|
2016
|
+
multiSource: false, // true: Drag multiple (i.e. selected) nodes. Also a callback() is allowed
|
|
2017
|
+
effectAllowed: "all", // Restrict the possible cursor shapes and modifier operations (can also be set in the dragStart event)
|
|
2018
|
+
dropEffectDefault: "move", // Default dropEffect ('copy', 'link', or 'move') when no modifier is pressed (override in drag, dragOver).
|
|
2019
|
+
guessDropEffect: true, // Calculate from `effectAllowed` and modifier keys)
|
|
2020
|
+
preventForeignNodes: false, // Prevent dropping nodes from different Wunderbaum trees
|
|
2021
|
+
preventLazyParents: true, // Prevent dropping items on unloaded lazy Wunderbaum tree nodes
|
|
2022
|
+
preventNonNodes: false, // Prevent dropping items other than Wunderbaum tree nodes
|
|
2023
|
+
preventRecursion: true, // Prevent dropping nodes on own descendants
|
|
2024
|
+
preventSameParent: false, // Prevent dropping nodes under same direct parent
|
|
2025
|
+
preventVoidMoves: true, // Prevent dropping nodes 'before self', etc. (move only)
|
|
2026
|
+
serializeClipboardData: true, // Serialize node data to dataTransfer object
|
|
2027
|
+
scroll: true, // Enable auto-scrolling while dragging
|
|
2028
|
+
scrollSensitivity: 20, // Active top/bottom margin in pixel
|
|
2029
|
+
// scrollnterval: 50, // Generate event every 50 ms
|
|
2030
|
+
scrollSpeed: 5, // Scroll pixel per 50 ms
|
|
2031
|
+
// setTextTypeJson: false, // Allow dragging of nodes to different IE windows
|
|
2032
|
+
sourceCopyHook: null, // Optional callback passed to `toDict` on dragStart @since 2.38
|
|
2033
|
+
// Events (drag support)
|
|
2034
|
+
dragStart: null, // Callback(sourceNode, data), return true, to enable dnd drag
|
|
2035
|
+
drag: null, // Callback(sourceNode, data)
|
|
2036
|
+
dragEnd: null, // Callback(sourceNode, data)
|
|
2037
|
+
// Events (drop support)
|
|
2038
|
+
dragEnter: null, // Callback(targetNode, data), return true, to enable dnd drop
|
|
2039
|
+
dragOver: null, // Callback(targetNode, data)
|
|
2040
|
+
dragExpand: null, // Callback(targetNode, data), return false to prevent autoExpand
|
|
2041
|
+
drop: null, // Callback(targetNode, data)
|
|
2042
|
+
dragLeave: null, // Callback(targetNode, data)
|
|
2043
|
+
});
|
|
2044
|
+
// public dropMarkerElem?: HTMLElement;
|
|
2045
|
+
this.srcNode = null;
|
|
2046
|
+
this.lastTargetNode = null;
|
|
2047
|
+
this.lastEnterStamp = 0;
|
|
2048
|
+
this.lastAllowedDropRegions = null;
|
|
2049
|
+
this.lastDropEffect = null;
|
|
2050
|
+
this.lastDropRegion = false;
|
|
2051
|
+
this.currentScrollDir = 0;
|
|
2052
|
+
this.applyScrollDirThrottled = throttle(this._applyScrollDir, 50);
|
|
2181
2053
|
}
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
//
|
|
2185
|
-
//
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
const keyToNodeMap = {};
|
|
2196
|
-
const indexToNodeMap = {};
|
|
2197
|
-
const keyAttrName = (_a = longToShort["key"]) !== null && _a !== void 0 ? _a : "key";
|
|
2198
|
-
const childrenAttrName = (_b = longToShort["children"]) !== null && _b !== void 0 ? _b : "children";
|
|
2199
|
-
for (const [index, nodeTuple] of children.entries()) {
|
|
2200
|
-
// Node entry format:
|
|
2201
|
-
// [PARENT_ID, [POSITIONAL_ARGS]]
|
|
2202
|
-
// or
|
|
2203
|
-
// [PARENT_ID, [POSITIONAL_ARGS], {KEY_VALUE_ARGS}]
|
|
2204
|
-
const [parentId, args, kwargs = {}] = nodeTuple;
|
|
2205
|
-
// Free up some memory as we go
|
|
2206
|
-
nodeTuple[1] = null;
|
|
2207
|
-
if (nodeTuple[2] != null) {
|
|
2208
|
-
nodeTuple[2] = null;
|
|
2209
|
-
}
|
|
2210
|
-
// console.log("flatten", parentId, args, kwargs)
|
|
2211
|
-
// We keep `kwargs` as our new node definition. Then we add all positional
|
|
2212
|
-
// values to this object:
|
|
2213
|
-
args.forEach((val, positionalIdx) => {
|
|
2214
|
-
kwargs[positionalShort[positionalIdx]] = val;
|
|
2215
|
-
});
|
|
2216
|
-
// Find the parent node. `null` means 'toplevel'. PARENT_ID may be the numeric
|
|
2217
|
-
// index of the source.children list. If PARENT_ID is a string, we search
|
|
2218
|
-
// a parent with node.key of this value.
|
|
2219
|
-
indexToNodeMap[index] = kwargs;
|
|
2220
|
-
const key = kwargs[keyAttrName];
|
|
2221
|
-
if (key != null) {
|
|
2222
|
-
keyToNodeMap[key] = kwargs;
|
|
2223
|
-
}
|
|
2224
|
-
let parentNode = null;
|
|
2225
|
-
if (parentId === null) ;
|
|
2226
|
-
else if (typeof parentId === "number") {
|
|
2227
|
-
parentNode = indexToNodeMap[parentId];
|
|
2228
|
-
if (parentNode === undefined) {
|
|
2229
|
-
throw new Error(`unflattenSource: Could not find parent node by index: ${parentId}.`);
|
|
2230
|
-
}
|
|
2231
|
-
}
|
|
2232
|
-
else {
|
|
2233
|
-
parentNode = keyToNodeMap[parentId];
|
|
2234
|
-
if (parentNode === undefined) {
|
|
2235
|
-
throw new Error(`unflattenSource: Could not find parent node by key: ${parentId}`);
|
|
2236
|
-
}
|
|
2237
|
-
}
|
|
2238
|
-
if (parentNode) {
|
|
2239
|
-
(_c = parentNode[childrenAttrName]) !== null && _c !== void 0 ? _c : (parentNode[childrenAttrName] = []);
|
|
2240
|
-
parentNode[childrenAttrName].push(kwargs);
|
|
2241
|
-
}
|
|
2242
|
-
else {
|
|
2243
|
-
newChildren.push(kwargs);
|
|
2244
|
-
}
|
|
2245
|
-
}
|
|
2246
|
-
source.children = newChildren;
|
|
2247
|
-
}
|
|
2248
|
-
/**
|
|
2249
|
-
* Decompresses the source data by
|
|
2250
|
-
* - converting from 'flat' to 'nested' format
|
|
2251
|
-
* - expanding short alias names to long names (if defined in _keyMap)
|
|
2252
|
-
* - resolving value indexes to value strings (if defined in _valueMap)
|
|
2253
|
-
*
|
|
2254
|
-
* @param source - The source object to be decompressed.
|
|
2255
|
-
* @returns void
|
|
2256
|
-
*/
|
|
2257
|
-
function decompressSourceData(source) {
|
|
2258
|
-
let { _format, _version = 1, _keyMap, _valueMap } = source;
|
|
2259
|
-
assert(_version === 1, `Expected file version 1 instead of ${_version}`);
|
|
2260
|
-
let longToShort = _keyMap;
|
|
2261
|
-
let shortToLong = {};
|
|
2262
|
-
if (longToShort) {
|
|
2263
|
-
for (const [key, value] of Object.entries(longToShort)) {
|
|
2264
|
-
shortToLong[value] = key;
|
|
2265
|
-
}
|
|
2266
|
-
}
|
|
2267
|
-
// Fallback for old format (pre 0.7.0, using _keyMap in reverse direction)
|
|
2268
|
-
// TODO: raise Error on final 1.x release
|
|
2269
|
-
if (longToShort && longToShort.t) {
|
|
2270
|
-
const msg = `source._keyMap maps from long to short since v0.7.0. Flip key/value!`;
|
|
2271
|
-
console.warn(msg); // eslint-disable-line no-console
|
|
2272
|
-
[longToShort, shortToLong] = [shortToLong, longToShort];
|
|
2273
|
-
}
|
|
2274
|
-
// Fallback for old format (pre 0.7.0, using _typeList instead of _valueMap)
|
|
2275
|
-
// TODO: raise Error on final 1.x release
|
|
2276
|
-
if (source._typeList != null) {
|
|
2277
|
-
const msg = `source._typeList is deprecated since v0.7.0: use source._valueMap: {"type": [...]} instead.`;
|
|
2278
|
-
if (_valueMap != null) {
|
|
2279
|
-
throw new Error(msg);
|
|
2280
|
-
}
|
|
2281
|
-
else {
|
|
2282
|
-
console.warn(msg); // eslint-disable-line no-console
|
|
2283
|
-
_valueMap = { type: source._typeList };
|
|
2284
|
-
delete source._typeList;
|
|
2285
|
-
}
|
|
2286
|
-
}
|
|
2287
|
-
if (_format === "flat") {
|
|
2288
|
-
unflattenSource(source);
|
|
2289
|
-
}
|
|
2290
|
-
delete source._format;
|
|
2291
|
-
delete source._version;
|
|
2292
|
-
delete source._keyMap;
|
|
2293
|
-
delete source._valueMap;
|
|
2294
|
-
delete source._positional;
|
|
2295
|
-
function _iter(childList) {
|
|
2296
|
-
for (const node of childList) {
|
|
2297
|
-
// Iterate over a list of names, because we modify inside the loop
|
|
2298
|
-
// (for ... of ... does not allow this)
|
|
2299
|
-
Object.getOwnPropertyNames(node).forEach((propName) => {
|
|
2300
|
-
const value = node[propName];
|
|
2301
|
-
// Replace short names with long names if defined in _keyMap
|
|
2302
|
-
let longName = propName;
|
|
2303
|
-
if (_keyMap && shortToLong[propName] != null) {
|
|
2304
|
-
longName = shortToLong[propName];
|
|
2305
|
-
if (longName !== propName) {
|
|
2306
|
-
node[longName] = value;
|
|
2307
|
-
delete node[propName];
|
|
2308
|
-
}
|
|
2309
|
-
}
|
|
2310
|
-
// Replace type index with type name if defined in _valueMap
|
|
2311
|
-
if (_valueMap &&
|
|
2312
|
-
typeof value === "number" &&
|
|
2313
|
-
_valueMap[longName] != null) {
|
|
2314
|
-
const newValue = _valueMap[longName][value];
|
|
2315
|
-
if (newValue == null) {
|
|
2316
|
-
throw new Error(`Expected valueMap[${longName}][${value}] entry in [${_valueMap[longName]}]`);
|
|
2317
|
-
}
|
|
2318
|
-
node[longName] = newValue;
|
|
2319
|
-
}
|
|
2320
|
-
});
|
|
2321
|
-
// Recursion
|
|
2322
|
-
if (node.children) {
|
|
2323
|
-
_iter(node.children);
|
|
2324
|
-
}
|
|
2325
|
-
}
|
|
2326
|
-
}
|
|
2327
|
-
if (_keyMap || _valueMap) {
|
|
2328
|
-
_iter(source.children);
|
|
2329
|
-
}
|
|
2330
|
-
}
|
|
2331
|
-
|
|
2332
|
-
/*!
|
|
2333
|
-
* Wunderbaum - ext-dnd
|
|
2334
|
-
* Copyright (c) 2021-2024, Martin Wendt. Released under the MIT license.
|
|
2335
|
-
* v0.11.0, Sun, 04 Aug 2024 15:35:53 GMT (https://github.com/mar10/wunderbaum)
|
|
2336
|
-
*/
|
|
2337
|
-
const nodeMimeType = "application/x-wunderbaum-node";
|
|
2338
|
-
class DndExtension extends WunderbaumExtension {
|
|
2339
|
-
constructor(tree) {
|
|
2340
|
-
super(tree, "dnd", {
|
|
2341
|
-
autoExpandMS: 1500, // Expand nodes after n milliseconds of hovering
|
|
2342
|
-
// dropMarkerInsertOffsetX: -16, // Additional offset for drop-marker with hitMode = "before"/"after"
|
|
2343
|
-
// dropMarkerOffsetX: -24, // Absolute position offset for .fancytree-drop-marker relatively to ..fancytree-title (icon/img near a node accepting drop)
|
|
2344
|
-
// #1021 `document.body` is not available yet
|
|
2345
|
-
// dropMarkerParent: "body", // Root Container used for drop marker (could be a shadow root)
|
|
2346
|
-
multiSource: false, // true: Drag multiple (i.e. selected) nodes. Also a callback() is allowed
|
|
2347
|
-
effectAllowed: "all", // Restrict the possible cursor shapes and modifier operations (can also be set in the dragStart event)
|
|
2348
|
-
dropEffectDefault: "move", // Default dropEffect ('copy', 'link', or 'move') when no modifier is pressed (override in drag, dragOver).
|
|
2349
|
-
guessDropEffect: true, // Calculate from `effectAllowed` and modifier keys)
|
|
2350
|
-
preventForeignNodes: false, // Prevent dropping nodes from different Wunderbaum trees
|
|
2351
|
-
preventLazyParents: true, // Prevent dropping items on unloaded lazy Wunderbaum tree nodes
|
|
2352
|
-
preventNonNodes: false, // Prevent dropping items other than Wunderbaum tree nodes
|
|
2353
|
-
preventRecursion: true, // Prevent dropping nodes on own descendants
|
|
2354
|
-
preventSameParent: false, // Prevent dropping nodes under same direct parent
|
|
2355
|
-
preventVoidMoves: true, // Prevent dropping nodes 'before self', etc. (move only)
|
|
2356
|
-
serializeClipboardData: true, // Serialize node data to dataTransfer object
|
|
2357
|
-
scroll: true, // Enable auto-scrolling while dragging
|
|
2358
|
-
scrollSensitivity: 20, // Active top/bottom margin in pixel
|
|
2359
|
-
// scrollnterval: 50, // Generate event every 50 ms
|
|
2360
|
-
scrollSpeed: 5, // Scroll pixel per 50 ms
|
|
2361
|
-
// setTextTypeJson: false, // Allow dragging of nodes to different IE windows
|
|
2362
|
-
sourceCopyHook: null, // Optional callback passed to `toDict` on dragStart @since 2.38
|
|
2363
|
-
// Events (drag support)
|
|
2364
|
-
dragStart: null, // Callback(sourceNode, data), return true, to enable dnd drag
|
|
2365
|
-
drag: null, // Callback(sourceNode, data)
|
|
2366
|
-
dragEnd: null, // Callback(sourceNode, data)
|
|
2367
|
-
// Events (drop support)
|
|
2368
|
-
dragEnter: null, // Callback(targetNode, data), return true, to enable dnd drop
|
|
2369
|
-
dragOver: null, // Callback(targetNode, data)
|
|
2370
|
-
dragExpand: null, // Callback(targetNode, data), return false to prevent autoExpand
|
|
2371
|
-
drop: null, // Callback(targetNode, data)
|
|
2372
|
-
dragLeave: null, // Callback(targetNode, data)
|
|
2373
|
-
});
|
|
2374
|
-
// public dropMarkerElem?: HTMLElement;
|
|
2375
|
-
this.srcNode = null;
|
|
2376
|
-
this.lastTargetNode = null;
|
|
2377
|
-
this.lastEnterStamp = 0;
|
|
2378
|
-
this.lastAllowedDropRegions = null;
|
|
2379
|
-
this.lastDropEffect = null;
|
|
2380
|
-
this.lastDropRegion = false;
|
|
2381
|
-
this.currentScrollDir = 0;
|
|
2382
|
-
this.applyScrollDirThrottled = throttle(this._applyScrollDir, 50);
|
|
2383
|
-
}
|
|
2384
|
-
init() {
|
|
2385
|
-
super.init();
|
|
2386
|
-
// Store the current scroll parent, which may be the tree
|
|
2387
|
-
// container, any enclosing div, or the document.
|
|
2388
|
-
// #761: scrollParent() always needs a container child
|
|
2389
|
-
// $temp = $("<span>").appendTo(this.$container);
|
|
2390
|
-
// this.$scrollParent = $temp.scrollParent();
|
|
2391
|
-
// $temp.remove();
|
|
2392
|
-
const tree = this.tree;
|
|
2393
|
-
const dndOpts = tree.options.dnd;
|
|
2394
|
-
// Enable drag support if dragStart() is specified:
|
|
2395
|
-
if (dndOpts.dragStart) {
|
|
2396
|
-
onEvent(tree.element, "dragstart drag dragend", this.onDragEvent.bind(this));
|
|
2054
|
+
init() {
|
|
2055
|
+
super.init();
|
|
2056
|
+
// Store the current scroll parent, which may be the tree
|
|
2057
|
+
// container, any enclosing div, or the document.
|
|
2058
|
+
// #761: scrollParent() always needs a container child
|
|
2059
|
+
// $temp = $("<span>").appendTo(this.$container);
|
|
2060
|
+
// this.$scrollParent = $temp.scrollParent();
|
|
2061
|
+
// $temp.remove();
|
|
2062
|
+
const tree = this.tree;
|
|
2063
|
+
const dndOpts = tree.options.dnd;
|
|
2064
|
+
// Enable drag support if dragStart() is specified:
|
|
2065
|
+
if (dndOpts.dragStart) {
|
|
2066
|
+
onEvent(tree.element, "dragstart drag dragend", this.onDragEvent.bind(this));
|
|
2397
2067
|
}
|
|
2398
2068
|
// Enable drop support if dragEnter() is specified:
|
|
2399
2069
|
if (dndOpts.dragEnter) {
|
|
@@ -2431,14 +2101,15 @@
|
|
|
2431
2101
|
* Calculates the drop region based on the drag event and the allowed drop regions.
|
|
2432
2102
|
*/
|
|
2433
2103
|
_calcDropRegion(e, allowed) {
|
|
2104
|
+
const rowHeight = this.tree.options.rowHeightPx;
|
|
2434
2105
|
const dy = e.offsetY;
|
|
2435
2106
|
if (!allowed) {
|
|
2436
2107
|
return false;
|
|
2437
2108
|
}
|
|
2438
2109
|
else if (allowed.size === 3) {
|
|
2439
|
-
return dy < 0.25 *
|
|
2110
|
+
return dy < 0.25 * rowHeight
|
|
2440
2111
|
? "before"
|
|
2441
|
-
: dy > 0.75 *
|
|
2112
|
+
: dy > 0.75 * rowHeight
|
|
2442
2113
|
? "after"
|
|
2443
2114
|
: "over";
|
|
2444
2115
|
}
|
|
@@ -2447,7 +2118,7 @@
|
|
|
2447
2118
|
}
|
|
2448
2119
|
else {
|
|
2449
2120
|
// Only 'before' and 'after':
|
|
2450
|
-
return dy >
|
|
2121
|
+
return dy > rowHeight / 2 ? "after" : "before";
|
|
2451
2122
|
}
|
|
2452
2123
|
// return "over";
|
|
2453
2124
|
}
|
|
@@ -2708,7 +2379,11 @@
|
|
|
2708
2379
|
}
|
|
2709
2380
|
this.lastAllowedDropRegions = regionSet;
|
|
2710
2381
|
this.lastDropEffect = dt.dropEffect;
|
|
2382
|
+
const region = this._calcDropRegion(e, this.lastAllowedDropRegions);
|
|
2711
2383
|
targetNode.setClass("wb-drop-target");
|
|
2384
|
+
targetNode.setClass("wb-drop-over", region === "over");
|
|
2385
|
+
targetNode.setClass("wb-drop-before", region === "before");
|
|
2386
|
+
targetNode.setClass("wb-drop-after", region === "after");
|
|
2712
2387
|
e.preventDefault(); // Allow drop (Drop operation is denied by default)
|
|
2713
2388
|
return false;
|
|
2714
2389
|
// --- dragover ---
|
|
@@ -2747,186 +2422,526 @@
|
|
|
2747
2422
|
targetNode._callEvent("dnd.dragLeave", { event: e, sourceNode: srcNode });
|
|
2748
2423
|
// --- drop ---
|
|
2749
2424
|
}
|
|
2750
|
-
else if (e.type === "drop") {
|
|
2751
|
-
e.stopPropagation(); // prevent browser from opening links?
|
|
2752
|
-
e.preventDefault(); // #69 prevent iOS browser from opening links
|
|
2753
|
-
this._leaveNode();
|
|
2754
|
-
const region = this.lastDropRegion;
|
|
2755
|
-
let nodeData = (_a = e.dataTransfer) === null || _a === void 0 ? void 0 : _a.getData(nodeMimeType);
|
|
2756
|
-
nodeData = nodeData ? JSON.parse(nodeData) : null;
|
|
2757
|
-
const srcNode = this.srcNode;
|
|
2758
|
-
const lastDropEffect = this.lastDropEffect;
|
|
2759
|
-
setTimeout(() => {
|
|
2760
|
-
// Decouple this call, because drop actions may prevent the dragend event
|
|
2761
|
-
// from being fired on some browsers
|
|
2762
|
-
targetNode._callEvent("dnd.drop", {
|
|
2763
|
-
event: e,
|
|
2764
|
-
region: region,
|
|
2765
|
-
suggestedDropMode: region === "over" ? "appendChild" : region,
|
|
2766
|
-
suggestedDropEffect: lastDropEffect,
|
|
2767
|
-
// suggestedDropEffect: e.dataTransfer?.dropEffect,
|
|
2768
|
-
sourceNode: srcNode,
|
|
2769
|
-
sourceNodeData: nodeData,
|
|
2770
|
-
});
|
|
2771
|
-
}, 10);
|
|
2425
|
+
else if (e.type === "drop") {
|
|
2426
|
+
e.stopPropagation(); // prevent browser from opening links?
|
|
2427
|
+
e.preventDefault(); // #69 prevent iOS browser from opening links
|
|
2428
|
+
this._leaveNode();
|
|
2429
|
+
const region = this.lastDropRegion;
|
|
2430
|
+
let nodeData = (_a = e.dataTransfer) === null || _a === void 0 ? void 0 : _a.getData(nodeMimeType);
|
|
2431
|
+
nodeData = nodeData ? JSON.parse(nodeData) : null;
|
|
2432
|
+
const srcNode = this.srcNode;
|
|
2433
|
+
const lastDropEffect = this.lastDropEffect;
|
|
2434
|
+
setTimeout(() => {
|
|
2435
|
+
// Decouple this call, because drop actions may prevent the dragend event
|
|
2436
|
+
// from being fired on some browsers
|
|
2437
|
+
targetNode._callEvent("dnd.drop", {
|
|
2438
|
+
event: e,
|
|
2439
|
+
region: region,
|
|
2440
|
+
suggestedDropMode: region === "over" ? "appendChild" : region,
|
|
2441
|
+
suggestedDropEffect: lastDropEffect,
|
|
2442
|
+
// suggestedDropEffect: e.dataTransfer?.dropEffect,
|
|
2443
|
+
sourceNode: srcNode,
|
|
2444
|
+
sourceNodeData: nodeData,
|
|
2445
|
+
});
|
|
2446
|
+
}, 10);
|
|
2447
|
+
}
|
|
2448
|
+
return false;
|
|
2449
|
+
}
|
|
2450
|
+
}
|
|
2451
|
+
|
|
2452
|
+
/*!
|
|
2453
|
+
* Wunderbaum - drag_observer
|
|
2454
|
+
* Copyright (c) 2021-2024, Martin Wendt. Released under the MIT license.
|
|
2455
|
+
* v0.12.0, Sun, 12 Jan 2025 10:51:41 GMT (https://github.com/mar10/wunderbaum)
|
|
2456
|
+
*/
|
|
2457
|
+
/**
|
|
2458
|
+
* Convert mouse- and touch events to 'dragstart', 'drag', and 'dragstop'.
|
|
2459
|
+
*/
|
|
2460
|
+
class DragObserver {
|
|
2461
|
+
constructor(opts) {
|
|
2462
|
+
this.start = {
|
|
2463
|
+
event: null,
|
|
2464
|
+
x: 0,
|
|
2465
|
+
y: 0,
|
|
2466
|
+
altKey: false,
|
|
2467
|
+
ctrlKey: false,
|
|
2468
|
+
metaKey: false,
|
|
2469
|
+
shiftKey: false,
|
|
2470
|
+
};
|
|
2471
|
+
this.dragElem = null;
|
|
2472
|
+
this.dragging = false;
|
|
2473
|
+
this.customData = {};
|
|
2474
|
+
// TODO: touch events
|
|
2475
|
+
this.events = ["mousedown", "mouseup", "mousemove", "keydown"];
|
|
2476
|
+
if (!opts.root) {
|
|
2477
|
+
throw new Error("Missing `root` option.");
|
|
2478
|
+
}
|
|
2479
|
+
this.opts = Object.assign({ thresh: 5 }, opts);
|
|
2480
|
+
this.root = opts.root;
|
|
2481
|
+
this._handler = this.handleEvent.bind(this);
|
|
2482
|
+
this.events.forEach((type) => {
|
|
2483
|
+
this.root.addEventListener(type, this._handler);
|
|
2484
|
+
});
|
|
2485
|
+
}
|
|
2486
|
+
/** Unregister all event listeners. */
|
|
2487
|
+
disconnect() {
|
|
2488
|
+
this.events.forEach((type) => {
|
|
2489
|
+
this.root.removeEventListener(type, this._handler);
|
|
2490
|
+
});
|
|
2491
|
+
}
|
|
2492
|
+
getDragElem() {
|
|
2493
|
+
return this.dragElem;
|
|
2494
|
+
}
|
|
2495
|
+
isDragging() {
|
|
2496
|
+
return this.dragging;
|
|
2497
|
+
}
|
|
2498
|
+
stopDrag(cb_event) {
|
|
2499
|
+
if (this.dragging && this.opts.dragstop && cb_event) {
|
|
2500
|
+
cb_event.type = "dragstop";
|
|
2501
|
+
try {
|
|
2502
|
+
this.opts.dragstop(cb_event);
|
|
2503
|
+
}
|
|
2504
|
+
catch (err) {
|
|
2505
|
+
console.error("dragstop error", err); // eslint-disable-line no-console
|
|
2506
|
+
}
|
|
2507
|
+
}
|
|
2508
|
+
this.dragElem = null;
|
|
2509
|
+
this.dragging = false;
|
|
2510
|
+
this.start.event = null;
|
|
2511
|
+
this.customData = {};
|
|
2512
|
+
}
|
|
2513
|
+
handleEvent(e) {
|
|
2514
|
+
const type = e.type;
|
|
2515
|
+
const opts = this.opts;
|
|
2516
|
+
const cb_event = {
|
|
2517
|
+
type: e.type,
|
|
2518
|
+
startEvent: type === "mousedown" ? e : this.start.event,
|
|
2519
|
+
event: e,
|
|
2520
|
+
customData: this.customData,
|
|
2521
|
+
dragElem: this.dragElem,
|
|
2522
|
+
dx: e.pageX - this.start.x,
|
|
2523
|
+
dy: e.pageY - this.start.y,
|
|
2524
|
+
apply: undefined,
|
|
2525
|
+
};
|
|
2526
|
+
// console.log("handleEvent", type, cb_event);
|
|
2527
|
+
switch (type) {
|
|
2528
|
+
case "keydown":
|
|
2529
|
+
this.stopDrag(cb_event);
|
|
2530
|
+
break;
|
|
2531
|
+
case "mousedown":
|
|
2532
|
+
if (this.dragElem) {
|
|
2533
|
+
this.stopDrag(cb_event);
|
|
2534
|
+
break;
|
|
2535
|
+
}
|
|
2536
|
+
if (opts.selector) {
|
|
2537
|
+
let elem = e.target;
|
|
2538
|
+
if (elem.matches(opts.selector)) {
|
|
2539
|
+
this.dragElem = elem;
|
|
2540
|
+
}
|
|
2541
|
+
else {
|
|
2542
|
+
elem = elem.closest(opts.selector);
|
|
2543
|
+
if (elem) {
|
|
2544
|
+
this.dragElem = elem;
|
|
2545
|
+
}
|
|
2546
|
+
else {
|
|
2547
|
+
break; // no event delegation selector matched
|
|
2548
|
+
}
|
|
2549
|
+
}
|
|
2550
|
+
}
|
|
2551
|
+
this.start.event = e;
|
|
2552
|
+
this.start.x = e.pageX;
|
|
2553
|
+
this.start.y = e.pageY;
|
|
2554
|
+
this.start.altKey = e.altKey;
|
|
2555
|
+
this.start.ctrlKey = e.ctrlKey;
|
|
2556
|
+
this.start.metaKey = e.metaKey;
|
|
2557
|
+
this.start.shiftKey = e.shiftKey;
|
|
2558
|
+
break;
|
|
2559
|
+
case "mousemove":
|
|
2560
|
+
// TODO: debounce/throttle?
|
|
2561
|
+
// TODO: horizontal mode: ignore if dx unchanged
|
|
2562
|
+
if (!this.dragElem) {
|
|
2563
|
+
break;
|
|
2564
|
+
}
|
|
2565
|
+
if (!this.dragging) {
|
|
2566
|
+
if (opts.thresh) {
|
|
2567
|
+
const dist2 = cb_event.dx * cb_event.dx + cb_event.dy * cb_event.dy;
|
|
2568
|
+
if (dist2 < opts.thresh * opts.thresh) {
|
|
2569
|
+
break;
|
|
2570
|
+
}
|
|
2571
|
+
}
|
|
2572
|
+
cb_event.type = "dragstart";
|
|
2573
|
+
if (opts.dragstart(cb_event) === false) {
|
|
2574
|
+
this.stopDrag(cb_event);
|
|
2575
|
+
break;
|
|
2576
|
+
}
|
|
2577
|
+
this.dragging = true;
|
|
2578
|
+
}
|
|
2579
|
+
if (this.dragging && this.opts.drag) {
|
|
2580
|
+
cb_event.type = "drag";
|
|
2581
|
+
this.opts.drag(cb_event);
|
|
2582
|
+
}
|
|
2583
|
+
break;
|
|
2584
|
+
case "mouseup":
|
|
2585
|
+
if (!this.dragging) {
|
|
2586
|
+
this.stopDrag(cb_event);
|
|
2587
|
+
break;
|
|
2588
|
+
}
|
|
2589
|
+
if (e.button === 0) {
|
|
2590
|
+
cb_event.apply = true;
|
|
2591
|
+
}
|
|
2592
|
+
else {
|
|
2593
|
+
cb_event.apply = false;
|
|
2594
|
+
}
|
|
2595
|
+
this.stopDrag(cb_event);
|
|
2596
|
+
break;
|
|
2597
|
+
}
|
|
2598
|
+
}
|
|
2599
|
+
}
|
|
2600
|
+
|
|
2601
|
+
/*!
|
|
2602
|
+
* Wunderbaum - common
|
|
2603
|
+
* Copyright (c) 2021-2024, Martin Wendt. Released under the MIT license.
|
|
2604
|
+
* v0.12.0, Sun, 12 Jan 2025 10:51:41 GMT (https://github.com/mar10/wunderbaum)
|
|
2605
|
+
*/
|
|
2606
|
+
const DEFAULT_DEBUGLEVEL = 3; // Replaced by rollup script
|
|
2607
|
+
/**
|
|
2608
|
+
* Fixed height of a row in pixel. Must match the SCSS variable `$row-outer-height`.
|
|
2609
|
+
*/
|
|
2610
|
+
const DEFAULT_ROW_HEIGHT = 22;
|
|
2611
|
+
/**
|
|
2612
|
+
* Fixed width of node icons in pixel. Must match the SCSS variable `$icon-outer-width`.
|
|
2613
|
+
*/
|
|
2614
|
+
const ICON_WIDTH = 20;
|
|
2615
|
+
/**
|
|
2616
|
+
* Adjust the width of the title span, so overflow ellipsis work.
|
|
2617
|
+
* (2 x `$col-padding-x` + 3px rounding errors).
|
|
2618
|
+
*/
|
|
2619
|
+
const TITLE_SPAN_PAD_Y = 7;
|
|
2620
|
+
/** Render row markup for N nodes above and below the visible viewport. */
|
|
2621
|
+
const RENDER_MAX_PREFETCH = 5;
|
|
2622
|
+
/** Minimum column width if not set otherwise. */
|
|
2623
|
+
const DEFAULT_MIN_COL_WIDTH = 4;
|
|
2624
|
+
/** Regular expression to detect if a string describes an image URL (in contrast
|
|
2625
|
+
* to a class name). Strings are considered image urls if they contain '.' or '/'.
|
|
2626
|
+
*/
|
|
2627
|
+
const TEST_IMG = new RegExp(/\.|\//);
|
|
2628
|
+
// export const RECURSIVE_REQUEST_ERROR = "$recursive_request";
|
|
2629
|
+
// export const INVALID_REQUEST_TARGET_ERROR = "$request_target_invalid";
|
|
2630
|
+
/**
|
|
2631
|
+
* Default node icons.
|
|
2632
|
+
* Requires bootstrap icons https://icons.getbootstrap.com
|
|
2633
|
+
*/
|
|
2634
|
+
const iconMaps = {
|
|
2635
|
+
bootstrap: {
|
|
2636
|
+
error: "bi bi-exclamation-triangle",
|
|
2637
|
+
// loading: "bi bi-hourglass-split wb-busy",
|
|
2638
|
+
loading: "bi bi-chevron-right wb-busy",
|
|
2639
|
+
// loading: "bi bi-arrow-repeat wb-spin",
|
|
2640
|
+
// loading: '<div class="spinner-border spinner-border-sm" role="status"> <span class="visually-hidden">Loading...</span> </div>',
|
|
2641
|
+
// noData: "bi bi-search",
|
|
2642
|
+
noData: "bi bi-question-circle",
|
|
2643
|
+
expanderExpanded: "bi bi-chevron-down",
|
|
2644
|
+
// expanderExpanded: "bi bi-dash-square",
|
|
2645
|
+
expanderCollapsed: "bi bi-chevron-right",
|
|
2646
|
+
// expanderCollapsed: "bi bi-plus-square",
|
|
2647
|
+
expanderLazy: "bi bi-chevron-right wb-helper-lazy-expander",
|
|
2648
|
+
// expanderLazy: "bi bi-chevron-bar-right",
|
|
2649
|
+
checkChecked: "bi bi-check-square",
|
|
2650
|
+
checkUnchecked: "bi bi-square",
|
|
2651
|
+
checkUnknown: "bi bi-dash-square-dotted",
|
|
2652
|
+
radioChecked: "bi bi-circle-fill",
|
|
2653
|
+
radioUnchecked: "bi bi-circle",
|
|
2654
|
+
radioUnknown: "bi bi-record-circle",
|
|
2655
|
+
folder: "bi bi-folder2",
|
|
2656
|
+
folderOpen: "bi bi-folder2-open",
|
|
2657
|
+
folderLazy: "bi bi-folder-symlink",
|
|
2658
|
+
doc: "bi bi-file-earmark",
|
|
2659
|
+
colSortable: "bi bi-chevron-expand",
|
|
2660
|
+
// colSortable: "bi bi-arrow-down-up",
|
|
2661
|
+
// colSortAsc: "bi bi-chevron-down",
|
|
2662
|
+
// colSortDesc: "bi bi-chevron-up",
|
|
2663
|
+
colSortAsc: "bi bi-arrow-down",
|
|
2664
|
+
colSortDesc: "bi bi-arrow-up",
|
|
2665
|
+
colFilter: "bi bi-filter-circle",
|
|
2666
|
+
colFilterActive: "bi bi-filter-circle-fill wb-helper-invalid",
|
|
2667
|
+
colMenu: "bi bi-three-dots-vertical",
|
|
2668
|
+
},
|
|
2669
|
+
fontawesome6: {
|
|
2670
|
+
error: "fa-solid fa-triangle-exclamation",
|
|
2671
|
+
loading: "fa-solid fa-chevron-right fa-beat",
|
|
2672
|
+
noData: "fa-solid fa-circle-question",
|
|
2673
|
+
expanderExpanded: "fa-solid fa-chevron-down",
|
|
2674
|
+
expanderCollapsed: "fa-solid fa-chevron-right",
|
|
2675
|
+
expanderLazy: "fa-solid fa-chevron-right wb-helper-lazy-expander",
|
|
2676
|
+
checkChecked: "fa-regular fa-square-check",
|
|
2677
|
+
checkUnchecked: "fa-regular fa-square",
|
|
2678
|
+
checkUnknown: "fa-regular fa-square-minus",
|
|
2679
|
+
radioChecked: "fa-solid fa-circle",
|
|
2680
|
+
radioUnchecked: "fa-regular fa-circle",
|
|
2681
|
+
radioUnknown: "fa-regular fa-circle-question",
|
|
2682
|
+
folder: "fa-solid fa-folder-closed",
|
|
2683
|
+
folderOpen: "fa-regular fa-folder-open",
|
|
2684
|
+
folderLazy: "fa-solid fa-folder-plus",
|
|
2685
|
+
doc: "fa-regular fa-file",
|
|
2686
|
+
colSortable: "fa-solid fa-fw fa-sort",
|
|
2687
|
+
colSortAsc: "fa-solid fa-fw fa-sort-up",
|
|
2688
|
+
colSortDesc: "fa-solid fa-fw fa-sort-down",
|
|
2689
|
+
colFilter: "fa-solid fa-fw fa-filter",
|
|
2690
|
+
colFilterActive: "fa-solid fa-fw fa-filter wb-helper-invalid",
|
|
2691
|
+
colMenu: "fa-solid fa-fw fa-ellipsis-v",
|
|
2692
|
+
},
|
|
2693
|
+
};
|
|
2694
|
+
/** Dict keys that are evaluated by source loader (others are added to `tree.data` instead). */
|
|
2695
|
+
const RESERVED_TREE_SOURCE_KEYS = new Set([
|
|
2696
|
+
"_format", // reserved for future use
|
|
2697
|
+
"_keyMap", // Used for compressed data format
|
|
2698
|
+
"_positional", // Used for compressed data format
|
|
2699
|
+
"_typeList", // Used for compressed data format @deprecated
|
|
2700
|
+
"_valueMap", // Used for compressed data format
|
|
2701
|
+
"_version", // reserved for future use
|
|
2702
|
+
"children",
|
|
2703
|
+
"columns",
|
|
2704
|
+
"types",
|
|
2705
|
+
]);
|
|
2706
|
+
// /** Key codes that trigger grid navigation, even when inside an input element. */
|
|
2707
|
+
// export const INPUT_BREAKOUT_KEYS: Set<string> = new Set([
|
|
2708
|
+
// // "ArrowDown",
|
|
2709
|
+
// // "ArrowUp",
|
|
2710
|
+
// "Enter",
|
|
2711
|
+
// "Escape",
|
|
2712
|
+
// ]);
|
|
2713
|
+
/** Map `KeyEvent.key` to navigation action. */
|
|
2714
|
+
const KEY_TO_ACTION_DICT = {
|
|
2715
|
+
" ": "toggleSelect",
|
|
2716
|
+
"+": "expand",
|
|
2717
|
+
Add: "expand",
|
|
2718
|
+
ArrowDown: "down",
|
|
2719
|
+
ArrowLeft: "left",
|
|
2720
|
+
ArrowRight: "right",
|
|
2721
|
+
ArrowUp: "up",
|
|
2722
|
+
Backspace: "parent",
|
|
2723
|
+
"/": "collapseAll",
|
|
2724
|
+
Divide: "collapseAll",
|
|
2725
|
+
End: "lastCol",
|
|
2726
|
+
Home: "firstCol",
|
|
2727
|
+
"Control+End": "last",
|
|
2728
|
+
"Control+Home": "first",
|
|
2729
|
+
"Meta+ArrowDown": "last", // macOs
|
|
2730
|
+
"Meta+ArrowUp": "first", // macOs
|
|
2731
|
+
"*": "expandAll",
|
|
2732
|
+
Multiply: "expandAll",
|
|
2733
|
+
PageDown: "pageDown",
|
|
2734
|
+
PageUp: "pageUp",
|
|
2735
|
+
"-": "collapse",
|
|
2736
|
+
Subtract: "collapse",
|
|
2737
|
+
};
|
|
2738
|
+
/** Return a callback that returns true if the node title matches the string
|
|
2739
|
+
* or regular expression.
|
|
2740
|
+
* @see {@link WunderbaumNode.findAll}
|
|
2741
|
+
*/
|
|
2742
|
+
function makeNodeTitleMatcher(match) {
|
|
2743
|
+
if (match instanceof RegExp) {
|
|
2744
|
+
return function (node) {
|
|
2745
|
+
return match.test(node.title);
|
|
2746
|
+
};
|
|
2747
|
+
}
|
|
2748
|
+
assert(typeof match === "string", `Expected a string or RegExp: ${match}`);
|
|
2749
|
+
// s = escapeRegex(s.toLowerCase());
|
|
2750
|
+
return function (node) {
|
|
2751
|
+
return node.title === match;
|
|
2752
|
+
// console.log("match " + node, node.title.toLowerCase().indexOf(match))
|
|
2753
|
+
// return node.title.toLowerCase().indexOf(match) >= 0;
|
|
2754
|
+
};
|
|
2755
|
+
}
|
|
2756
|
+
/** Return a callback that returns true if the node title starts with a string (case-insensitive). */
|
|
2757
|
+
function makeNodeTitleStartMatcher(s) {
|
|
2758
|
+
s = escapeRegex(s);
|
|
2759
|
+
const reMatch = new RegExp("^" + s, "i");
|
|
2760
|
+
return function (node) {
|
|
2761
|
+
return reMatch.test(node.title);
|
|
2762
|
+
};
|
|
2763
|
+
}
|
|
2764
|
+
/** Compare two nodes by title (case-insensitive). */
|
|
2765
|
+
function nodeTitleSorter(a, b) {
|
|
2766
|
+
const x = a.title.toLowerCase();
|
|
2767
|
+
const y = b.title.toLowerCase();
|
|
2768
|
+
return x === y ? 0 : x > y ? 1 : -1;
|
|
2769
|
+
}
|
|
2770
|
+
/**
|
|
2771
|
+
* Convert 'flat' to 'nested' format.
|
|
2772
|
+
*
|
|
2773
|
+
* Flat node entry format:
|
|
2774
|
+
* [PARENT_ID, [POSITIONAL_ARGS]]
|
|
2775
|
+
* or
|
|
2776
|
+
* [PARENT_ID, [POSITIONAL_ARGS], {KEY_VALUE_ARGS}]
|
|
2777
|
+
*
|
|
2778
|
+
* 1. Parent-referencing list is converted to a list of nested dicts with
|
|
2779
|
+
* optional `children` properties.
|
|
2780
|
+
* 2. `[POSITIONAL_ARGS]` are added as dict attributes.
|
|
2781
|
+
*/
|
|
2782
|
+
function unflattenSource(source) {
|
|
2783
|
+
var _a, _b, _c;
|
|
2784
|
+
const { _format, _keyMap = {}, _positional = [], children } = source;
|
|
2785
|
+
if (_format !== "flat") {
|
|
2786
|
+
throw new Error(`Expected source._format: "flat", but got ${_format}`);
|
|
2787
|
+
}
|
|
2788
|
+
if (_positional && _positional.includes("children")) {
|
|
2789
|
+
throw new Error(`source._positional must not include "children": ${_positional}`);
|
|
2790
|
+
}
|
|
2791
|
+
let longToShort = _keyMap;
|
|
2792
|
+
if (_keyMap.t) {
|
|
2793
|
+
// Inverse keyMap was used (pre 0.7.0)
|
|
2794
|
+
// TODO: raise Error on final 1.x release
|
|
2795
|
+
const msg = `source._keyMap maps from long to short since v0.7.0. Flip key/value!`;
|
|
2796
|
+
console.warn(msg); // eslint-disable-line no-console
|
|
2797
|
+
longToShort = {};
|
|
2798
|
+
for (const [key, value] of Object.entries(_keyMap)) {
|
|
2799
|
+
longToShort[value] = key;
|
|
2800
|
+
}
|
|
2801
|
+
}
|
|
2802
|
+
const positionalShort = _positional.map((e) => longToShort[e]);
|
|
2803
|
+
const newChildren = [];
|
|
2804
|
+
const keyToNodeMap = {};
|
|
2805
|
+
const indexToNodeMap = {};
|
|
2806
|
+
const keyAttrName = (_a = longToShort["key"]) !== null && _a !== void 0 ? _a : "key";
|
|
2807
|
+
const childrenAttrName = (_b = longToShort["children"]) !== null && _b !== void 0 ? _b : "children";
|
|
2808
|
+
for (const [index, nodeTuple] of children.entries()) {
|
|
2809
|
+
// Node entry format:
|
|
2810
|
+
// [PARENT_ID, [POSITIONAL_ARGS]]
|
|
2811
|
+
// or
|
|
2812
|
+
// [PARENT_ID, [POSITIONAL_ARGS], {KEY_VALUE_ARGS}]
|
|
2813
|
+
const [parentId, args, kwargs = {}] = nodeTuple;
|
|
2814
|
+
// Free up some memory as we go
|
|
2815
|
+
nodeTuple[1] = null;
|
|
2816
|
+
if (nodeTuple[2] != null) {
|
|
2817
|
+
nodeTuple[2] = null;
|
|
2818
|
+
}
|
|
2819
|
+
// console.log("flatten", parentId, args, kwargs)
|
|
2820
|
+
// We keep `kwargs` as our new node definition. Then we add all positional
|
|
2821
|
+
// values to this object:
|
|
2822
|
+
args.forEach((val, positionalIdx) => {
|
|
2823
|
+
kwargs[positionalShort[positionalIdx]] = val;
|
|
2824
|
+
});
|
|
2825
|
+
// Find the parent node. `null` means 'toplevel'. PARENT_ID may be the numeric
|
|
2826
|
+
// index of the source.children list. If PARENT_ID is a string, we search
|
|
2827
|
+
// a parent with node.key of this value.
|
|
2828
|
+
indexToNodeMap[index] = kwargs;
|
|
2829
|
+
const key = kwargs[keyAttrName];
|
|
2830
|
+
if (key != null) {
|
|
2831
|
+
keyToNodeMap[key] = kwargs;
|
|
2832
|
+
}
|
|
2833
|
+
let parentNode = null;
|
|
2834
|
+
if (parentId === null) ;
|
|
2835
|
+
else if (typeof parentId === "number") {
|
|
2836
|
+
parentNode = indexToNodeMap[parentId];
|
|
2837
|
+
if (parentNode === undefined) {
|
|
2838
|
+
throw new Error(`unflattenSource: Could not find parent node by index: ${parentId}.`);
|
|
2839
|
+
}
|
|
2840
|
+
}
|
|
2841
|
+
else {
|
|
2842
|
+
parentNode = keyToNodeMap[parentId];
|
|
2843
|
+
if (parentNode === undefined) {
|
|
2844
|
+
throw new Error(`unflattenSource: Could not find parent node by key: ${parentId}`);
|
|
2845
|
+
}
|
|
2846
|
+
}
|
|
2847
|
+
if (parentNode) {
|
|
2848
|
+
(_c = parentNode[childrenAttrName]) !== null && _c !== void 0 ? _c : (parentNode[childrenAttrName] = []);
|
|
2849
|
+
parentNode[childrenAttrName].push(kwargs);
|
|
2850
|
+
}
|
|
2851
|
+
else {
|
|
2852
|
+
newChildren.push(kwargs);
|
|
2772
2853
|
}
|
|
2773
|
-
return false;
|
|
2774
2854
|
}
|
|
2855
|
+
source.children = newChildren;
|
|
2775
2856
|
}
|
|
2776
|
-
|
|
2777
|
-
/*!
|
|
2778
|
-
* Wunderbaum - drag_observer
|
|
2779
|
-
* Copyright (c) 2021-2024, Martin Wendt. Released under the MIT license.
|
|
2780
|
-
* v0.11.0, Sun, 04 Aug 2024 15:35:53 GMT (https://github.com/mar10/wunderbaum)
|
|
2781
|
-
*/
|
|
2782
2857
|
/**
|
|
2783
|
-
*
|
|
2858
|
+
* Decompresses the source data by
|
|
2859
|
+
* - converting from 'flat' to 'nested' format
|
|
2860
|
+
* - expanding short alias names to long names (if defined in _keyMap)
|
|
2861
|
+
* - resolving value indexes to value strings (if defined in _valueMap)
|
|
2862
|
+
*
|
|
2863
|
+
* @param source - The source object to be decompressed.
|
|
2864
|
+
* @returns void
|
|
2784
2865
|
*/
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
metaKey: false,
|
|
2794
|
-
shiftKey: false,
|
|
2795
|
-
};
|
|
2796
|
-
this.dragElem = null;
|
|
2797
|
-
this.dragging = false;
|
|
2798
|
-
this.customData = {};
|
|
2799
|
-
// TODO: touch events
|
|
2800
|
-
this.events = ["mousedown", "mouseup", "mousemove", "keydown"];
|
|
2801
|
-
if (!opts.root) {
|
|
2802
|
-
throw new Error("Missing `root` option.");
|
|
2866
|
+
function decompressSourceData(source) {
|
|
2867
|
+
let { _format, _version = 1, _keyMap, _valueMap } = source;
|
|
2868
|
+
assert(_version === 1, `Expected file version 1 instead of ${_version}`);
|
|
2869
|
+
let longToShort = _keyMap;
|
|
2870
|
+
let shortToLong = {};
|
|
2871
|
+
if (longToShort) {
|
|
2872
|
+
for (const [key, value] of Object.entries(longToShort)) {
|
|
2873
|
+
shortToLong[value] = key;
|
|
2803
2874
|
}
|
|
2804
|
-
this.opts = Object.assign({ thresh: 5 }, opts);
|
|
2805
|
-
this.root = opts.root;
|
|
2806
|
-
this._handler = this.handleEvent.bind(this);
|
|
2807
|
-
this.events.forEach((type) => {
|
|
2808
|
-
this.root.addEventListener(type, this._handler);
|
|
2809
|
-
});
|
|
2810
|
-
}
|
|
2811
|
-
/** Unregister all event listeners. */
|
|
2812
|
-
disconnect() {
|
|
2813
|
-
this.events.forEach((type) => {
|
|
2814
|
-
this.root.removeEventListener(type, this._handler);
|
|
2815
|
-
});
|
|
2816
|
-
}
|
|
2817
|
-
getDragElem() {
|
|
2818
|
-
return this.dragElem;
|
|
2819
2875
|
}
|
|
2820
|
-
|
|
2821
|
-
|
|
2876
|
+
// Fallback for old format (pre 0.7.0, using _keyMap in reverse direction)
|
|
2877
|
+
// TODO: raise Error on final 1.x release
|
|
2878
|
+
if (longToShort && longToShort.t) {
|
|
2879
|
+
const msg = `source._keyMap maps from long to short since v0.7.0. Flip key/value!`;
|
|
2880
|
+
console.warn(msg); // eslint-disable-line no-console
|
|
2881
|
+
[longToShort, shortToLong] = [shortToLong, longToShort];
|
|
2822
2882
|
}
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
2883
|
+
// Fallback for old format (pre 0.7.0, using _typeList instead of _valueMap)
|
|
2884
|
+
// TODO: raise Error on final 1.x release
|
|
2885
|
+
if (source._typeList != null) {
|
|
2886
|
+
const msg = `source._typeList is deprecated since v0.7.0: use source._valueMap: {"type": [...]} instead.`;
|
|
2887
|
+
if (_valueMap != null) {
|
|
2888
|
+
throw new Error(msg);
|
|
2889
|
+
}
|
|
2890
|
+
else {
|
|
2891
|
+
console.warn(msg); // eslint-disable-line no-console
|
|
2892
|
+
_valueMap = { type: source._typeList };
|
|
2893
|
+
delete source._typeList;
|
|
2832
2894
|
}
|
|
2833
|
-
this.dragElem = null;
|
|
2834
|
-
this.dragging = false;
|
|
2835
|
-
this.start.event = null;
|
|
2836
|
-
this.customData = {};
|
|
2837
2895
|
}
|
|
2838
|
-
|
|
2839
|
-
|
|
2840
|
-
|
|
2841
|
-
|
|
2842
|
-
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
break;
|
|
2860
|
-
}
|
|
2861
|
-
if (opts.selector) {
|
|
2862
|
-
let elem = e.target;
|
|
2863
|
-
if (elem.matches(opts.selector)) {
|
|
2864
|
-
this.dragElem = elem;
|
|
2865
|
-
}
|
|
2866
|
-
else {
|
|
2867
|
-
elem = elem.closest(opts.selector);
|
|
2868
|
-
if (elem) {
|
|
2869
|
-
this.dragElem = elem;
|
|
2870
|
-
}
|
|
2871
|
-
else {
|
|
2872
|
-
break; // no event delegation selector matched
|
|
2873
|
-
}
|
|
2896
|
+
if (_format === "flat") {
|
|
2897
|
+
unflattenSource(source);
|
|
2898
|
+
}
|
|
2899
|
+
delete source._format;
|
|
2900
|
+
delete source._version;
|
|
2901
|
+
delete source._keyMap;
|
|
2902
|
+
delete source._valueMap;
|
|
2903
|
+
delete source._positional;
|
|
2904
|
+
function _iter(childList) {
|
|
2905
|
+
for (const node of childList) {
|
|
2906
|
+
// Iterate over a list of names, because we modify inside the loop
|
|
2907
|
+
// (for ... of ... does not allow this)
|
|
2908
|
+
Object.getOwnPropertyNames(node).forEach((propName) => {
|
|
2909
|
+
const value = node[propName];
|
|
2910
|
+
// Replace short names with long names if defined in _keyMap
|
|
2911
|
+
let longName = propName;
|
|
2912
|
+
if (_keyMap && shortToLong[propName] != null) {
|
|
2913
|
+
longName = shortToLong[propName];
|
|
2914
|
+
if (longName !== propName) {
|
|
2915
|
+
node[longName] = value;
|
|
2916
|
+
delete node[propName];
|
|
2874
2917
|
}
|
|
2875
2918
|
}
|
|
2876
|
-
|
|
2877
|
-
|
|
2878
|
-
|
|
2879
|
-
|
|
2880
|
-
|
|
2881
|
-
|
|
2882
|
-
|
|
2883
|
-
break;
|
|
2884
|
-
case "mousemove":
|
|
2885
|
-
// TODO: debounce/throttle?
|
|
2886
|
-
// TODO: horizontal mode: ignore if dx unchanged
|
|
2887
|
-
if (!this.dragElem) {
|
|
2888
|
-
break;
|
|
2889
|
-
}
|
|
2890
|
-
if (!this.dragging) {
|
|
2891
|
-
if (opts.thresh) {
|
|
2892
|
-
const dist2 = cb_event.dx * cb_event.dx + cb_event.dy * cb_event.dy;
|
|
2893
|
-
if (dist2 < opts.thresh * opts.thresh) {
|
|
2894
|
-
break;
|
|
2895
|
-
}
|
|
2896
|
-
}
|
|
2897
|
-
cb_event.type = "dragstart";
|
|
2898
|
-
if (opts.dragstart(cb_event) === false) {
|
|
2899
|
-
this.stopDrag(cb_event);
|
|
2900
|
-
break;
|
|
2919
|
+
// Replace type index with type name if defined in _valueMap
|
|
2920
|
+
if (_valueMap &&
|
|
2921
|
+
typeof value === "number" &&
|
|
2922
|
+
_valueMap[longName] != null) {
|
|
2923
|
+
const newValue = _valueMap[longName][value];
|
|
2924
|
+
if (newValue == null) {
|
|
2925
|
+
throw new Error(`Expected valueMap[${longName}][${value}] entry in [${_valueMap[longName]}]`);
|
|
2901
2926
|
}
|
|
2902
|
-
|
|
2903
|
-
}
|
|
2904
|
-
if (this.dragging && this.opts.drag) {
|
|
2905
|
-
cb_event.type = "drag";
|
|
2906
|
-
this.opts.drag(cb_event);
|
|
2907
|
-
}
|
|
2908
|
-
break;
|
|
2909
|
-
case "mouseup":
|
|
2910
|
-
if (!this.dragging) {
|
|
2911
|
-
this.stopDrag(cb_event);
|
|
2912
|
-
break;
|
|
2913
|
-
}
|
|
2914
|
-
if (e.button === 0) {
|
|
2915
|
-
cb_event.apply = true;
|
|
2916
|
-
}
|
|
2917
|
-
else {
|
|
2918
|
-
cb_event.apply = false;
|
|
2927
|
+
node[longName] = newValue;
|
|
2919
2928
|
}
|
|
2920
|
-
|
|
2921
|
-
|
|
2929
|
+
});
|
|
2930
|
+
// Recursion
|
|
2931
|
+
if (node.children) {
|
|
2932
|
+
_iter(node.children);
|
|
2933
|
+
}
|
|
2922
2934
|
}
|
|
2923
2935
|
}
|
|
2936
|
+
if (_keyMap || _valueMap) {
|
|
2937
|
+
_iter(source.children);
|
|
2938
|
+
}
|
|
2924
2939
|
}
|
|
2925
2940
|
|
|
2926
2941
|
/*!
|
|
2927
2942
|
* Wunderbaum - ext-grid
|
|
2928
2943
|
* Copyright (c) 2021-2024, Martin Wendt. Released under the MIT license.
|
|
2929
|
-
* v0.
|
|
2944
|
+
* v0.12.0, Sun, 12 Jan 2025 10:51:41 GMT (https://github.com/mar10/wunderbaum)
|
|
2930
2945
|
*/
|
|
2931
2946
|
class GridExtension extends WunderbaumExtension {
|
|
2932
2947
|
constructor(tree) {
|
|
@@ -3017,7 +3032,7 @@
|
|
|
3017
3032
|
/*!
|
|
3018
3033
|
* Wunderbaum - deferred
|
|
3019
3034
|
* Copyright (c) 2021-2024, Martin Wendt. Released under the MIT license.
|
|
3020
|
-
* v0.
|
|
3035
|
+
* v0.12.0, Sun, 12 Jan 2025 10:51:41 GMT (https://github.com/mar10/wunderbaum)
|
|
3021
3036
|
*/
|
|
3022
3037
|
/**
|
|
3023
3038
|
* Implement a ES6 Promise, that exposes a resolve() and reject() method.
|
|
@@ -3070,7 +3085,7 @@
|
|
|
3070
3085
|
/*!
|
|
3071
3086
|
* Wunderbaum - wunderbaum_node
|
|
3072
3087
|
* Copyright (c) 2021-2024, Martin Wendt. Released under the MIT license.
|
|
3073
|
-
* v0.
|
|
3088
|
+
* v0.12.0, Sun, 12 Jan 2025 10:51:41 GMT (https://github.com/mar10/wunderbaum)
|
|
3074
3089
|
*/
|
|
3075
3090
|
/** WunderbaumNode properties that can be passed with source data.
|
|
3076
3091
|
* (Any other source properties will be stored as `node.data.PROP`.)
|
|
@@ -3098,6 +3113,20 @@
|
|
|
3098
3113
|
const NODE_DICT_PROPS = new Set(NODE_PROPS);
|
|
3099
3114
|
NODE_DICT_PROPS.delete("_partsel");
|
|
3100
3115
|
NODE_DICT_PROPS.delete("unselectable");
|
|
3116
|
+
// /** Node properties that are of type bool (or boolean & string).
|
|
3117
|
+
// * When parsing, we accept 0 for false and 1 for true for better JSON compression.
|
|
3118
|
+
// */
|
|
3119
|
+
// export const NODE_BOOL_PROPS: Set<string> = new Set([
|
|
3120
|
+
// "checkbox",
|
|
3121
|
+
// "colspan",
|
|
3122
|
+
// "expanded",
|
|
3123
|
+
// "icon",
|
|
3124
|
+
// "iconTooltip",
|
|
3125
|
+
// "radiogroup",
|
|
3126
|
+
// "selected",
|
|
3127
|
+
// "tooltip",
|
|
3128
|
+
// "unselectable",
|
|
3129
|
+
// ]);
|
|
3101
3130
|
/**
|
|
3102
3131
|
* A single tree node.
|
|
3103
3132
|
*
|
|
@@ -3139,20 +3168,26 @@
|
|
|
3139
3168
|
this.parent = parent;
|
|
3140
3169
|
this.key = "" + ((_a = data.key) !== null && _a !== void 0 ? _a : ++WunderbaumNode.sequence);
|
|
3141
3170
|
this.title = "" + ((_b = data.title) !== null && _b !== void 0 ? _b : "<" + this.key + ">");
|
|
3171
|
+
this.expanded = !!data.expanded;
|
|
3172
|
+
this.lazy = !!data.lazy;
|
|
3173
|
+
// We set the following node properties only if a matching data value is
|
|
3174
|
+
// passed
|
|
3142
3175
|
data.refKey != null ? (this.refKey = "" + data.refKey) : 0;
|
|
3143
3176
|
data.type != null ? (this.type = "" + data.type) : 0;
|
|
3144
|
-
this.
|
|
3145
|
-
data.
|
|
3146
|
-
|
|
3177
|
+
data.icon != null ? (this.icon = intToBool(data.icon)) : 0;
|
|
3178
|
+
data.tooltip != null ? (this.tooltip = intToBool(data.tooltip)) : 0;
|
|
3179
|
+
data.iconTooltip != null
|
|
3180
|
+
? (this.iconTooltip = intToBool(data.iconTooltip))
|
|
3181
|
+
: 0;
|
|
3147
3182
|
data.statusNodeType != null
|
|
3148
3183
|
? (this.statusNodeType = ("" + data.statusNodeType))
|
|
3149
3184
|
: 0;
|
|
3150
3185
|
data.colspan != null ? (this.colspan = !!data.colspan) : 0;
|
|
3151
3186
|
// Selection
|
|
3152
|
-
data.checkbox != null ? (
|
|
3187
|
+
data.checkbox != null ? intToBool(data.checkbox) : 0;
|
|
3153
3188
|
data.radiogroup != null ? (this.radiogroup = !!data.radiogroup) : 0;
|
|
3154
|
-
this.selected = data.selected
|
|
3155
|
-
data.unselectable
|
|
3189
|
+
data.selected != null ? (this.selected = !!data.selected) : 0;
|
|
3190
|
+
data.unselectable != null ? (this.unselectable = !!data.unselectable) : 0;
|
|
3156
3191
|
if (data.classes) {
|
|
3157
3192
|
this.setClass(data.classes);
|
|
3158
3193
|
}
|
|
@@ -3382,54 +3417,65 @@
|
|
|
3382
3417
|
startEditTitle() {
|
|
3383
3418
|
this.tree._callMethod("edit.startEditTitle", this);
|
|
3384
3419
|
}
|
|
3385
|
-
/**
|
|
3420
|
+
/**
|
|
3421
|
+
* Call `setExpanded()` on all descendant nodes.
|
|
3422
|
+
*
|
|
3423
|
+
* @param flag true to expand, false to collapse.
|
|
3424
|
+
* @param options Additional options.
|
|
3425
|
+
* @see {@link Wunderbaum.expandAll}
|
|
3426
|
+
* @see {@link WunderbaumNode.setExpanded}
|
|
3427
|
+
*/
|
|
3386
3428
|
async expandAll(flag = true, options) {
|
|
3387
3429
|
const tree = this.tree;
|
|
3388
|
-
const
|
|
3389
|
-
|
|
3430
|
+
const { collapseOthers, deep, depth, force, keepActiveNodeVisible = true, loadLazy, resetLazy, } = options !== null && options !== void 0 ? options : {};
|
|
3431
|
+
// limit expansion level to `depth` (or tree.minExpandLevel). Default: unlimited
|
|
3432
|
+
const treeLevel = this.tree.options.minExpandLevel || null; // 0 -> null
|
|
3433
|
+
const minLevel = depth !== null && depth !== void 0 ? depth : (force ? null : treeLevel);
|
|
3390
3434
|
const expandOpts = {
|
|
3391
|
-
|
|
3435
|
+
deep: deep,
|
|
3392
3436
|
force: force,
|
|
3393
3437
|
loadLazy: loadLazy,
|
|
3438
|
+
resetLazy: resetLazy,
|
|
3439
|
+
scrollIntoView: false, // don't scroll every node while iterating
|
|
3394
3440
|
};
|
|
3395
|
-
|
|
3441
|
+
this.logInfo(`expandAll(${flag}, depth=${depth}, minLevel=${minLevel})`);
|
|
3442
|
+
assert(!(flag && deep != null && !collapseOthers), "Expanding with `deep` option is not supported (implied by the `depth` option).");
|
|
3396
3443
|
// Expand all direct children in parallel:
|
|
3397
3444
|
async function _iter(n, level) {
|
|
3398
3445
|
var _a;
|
|
3399
|
-
// n.logInfo(` _iter(
|
|
3400
|
-
if (level === 0) {
|
|
3401
|
-
return;
|
|
3402
|
-
}
|
|
3403
|
-
// if (!flag && minExpandLevel && !force && n.getLevel() <= minExpandLevel) {
|
|
3404
|
-
// return; // Do not collapse until minExpandLevel
|
|
3405
|
-
// }
|
|
3406
|
-
const level_1 = level == null ? null : level - 1;
|
|
3446
|
+
// n.logInfo(` _iter(level=${level})`);
|
|
3407
3447
|
const promises = [];
|
|
3408
3448
|
(_a = n.children) === null || _a === void 0 ? void 0 : _a.forEach((cn) => {
|
|
3409
3449
|
if (flag) {
|
|
3410
|
-
if (!cn.expanded &&
|
|
3450
|
+
if (!cn.expanded &&
|
|
3451
|
+
(minLevel == null || level < minLevel) &&
|
|
3452
|
+
(cn.children || (loadLazy && cn.lazy))) {
|
|
3411
3453
|
// Node is collapsed and may be expanded (i.e. has children or is lazy)
|
|
3412
3454
|
// Expanding may be async, so we store the promise.
|
|
3413
3455
|
// Also the recursion is delayed until expansion finished.
|
|
3414
3456
|
const p = cn.setExpanded(true, expandOpts);
|
|
3415
3457
|
promises.push(p);
|
|
3416
|
-
|
|
3417
|
-
|
|
3418
|
-
|
|
3458
|
+
if (depth == null) {
|
|
3459
|
+
p.then(async () => {
|
|
3460
|
+
await _iter(cn, level + 1);
|
|
3461
|
+
});
|
|
3462
|
+
}
|
|
3419
3463
|
}
|
|
3420
3464
|
else {
|
|
3421
3465
|
// We don't expand the node, but still visit descendants.
|
|
3422
3466
|
// There we may find lazy nodes, so we
|
|
3423
|
-
promises.push(_iter(cn,
|
|
3467
|
+
promises.push(_iter(cn, level + 1));
|
|
3424
3468
|
}
|
|
3425
3469
|
}
|
|
3426
3470
|
else {
|
|
3427
3471
|
// Collapsing is always synchronous, so no promises required
|
|
3428
|
-
|
|
3429
|
-
|
|
3472
|
+
// Do not collapse until minExpandLevel
|
|
3473
|
+
if (minLevel == null || level >= minLevel) {
|
|
3430
3474
|
cn.setExpanded(false, expandOpts);
|
|
3431
3475
|
}
|
|
3432
|
-
|
|
3476
|
+
if ((minLevel != null && level < minLevel) || deep) {
|
|
3477
|
+
_iter(cn, level + 1); // recursion, even if cn was already collapsed
|
|
3478
|
+
}
|
|
3433
3479
|
}
|
|
3434
3480
|
});
|
|
3435
3481
|
return new Promise((resolve) => {
|
|
@@ -3438,10 +3484,15 @@
|
|
|
3438
3484
|
});
|
|
3439
3485
|
});
|
|
3440
3486
|
}
|
|
3441
|
-
const tag = tree.logTime(`${this}.expandAll(${flag})`);
|
|
3487
|
+
const tag = tree.logTime(`${this}.expandAll(${flag}, depth=${depth})`);
|
|
3442
3488
|
try {
|
|
3443
3489
|
tree.enableUpdate(false);
|
|
3444
|
-
await _iter(this,
|
|
3490
|
+
await _iter(this, 0);
|
|
3491
|
+
if (collapseOthers) {
|
|
3492
|
+
assert(flag, "Option `collapseOthers` requires flag=true");
|
|
3493
|
+
assert(minLevel != null, "Option `collapseOthers` requires `depth` or `minExpandLevel`");
|
|
3494
|
+
this.expandAll(false, { depth: minLevel });
|
|
3495
|
+
}
|
|
3445
3496
|
}
|
|
3446
3497
|
finally {
|
|
3447
3498
|
tree.enableUpdate(true);
|
|
@@ -3981,8 +4032,8 @@
|
|
|
3981
4032
|
let elap = 0, elapLoad = 0, elapProcess = 0;
|
|
3982
4033
|
// Check for overlapping requests
|
|
3983
4034
|
if (this._requestId) {
|
|
3984
|
-
this.logWarn(`Recursive load request #${requestId} while #${this._requestId} is pending
|
|
3985
|
-
|
|
4035
|
+
this.logWarn(`Recursive load request #${requestId} while #${this._requestId} is pending. ` +
|
|
4036
|
+
"The previous request will be ignored.");
|
|
3986
4037
|
}
|
|
3987
4038
|
this._requestId = requestId;
|
|
3988
4039
|
// const timerLabel = tree.logTime(this + ".load()");
|
|
@@ -4462,6 +4513,7 @@
|
|
|
4462
4513
|
_render_markup(opts) {
|
|
4463
4514
|
const tree = this.tree;
|
|
4464
4515
|
const treeOptions = tree.options;
|
|
4516
|
+
const rowHeight = treeOptions.rowHeightPx;
|
|
4465
4517
|
const checkbox = this.getOption("checkbox");
|
|
4466
4518
|
const columns = tree.columns;
|
|
4467
4519
|
const level = this.getLevel();
|
|
@@ -4476,7 +4528,7 @@
|
|
|
4476
4528
|
assert(!this.isRootNode(), "Root node not allowed");
|
|
4477
4529
|
rowDiv = document.createElement("div");
|
|
4478
4530
|
rowDiv.classList.add("wb-row");
|
|
4479
|
-
rowDiv.style.top = this._rowIdx *
|
|
4531
|
+
rowDiv.style.top = this._rowIdx * rowHeight + "px";
|
|
4480
4532
|
this._rowElem = rowDiv;
|
|
4481
4533
|
// Attach a node reference to the DOM Element:
|
|
4482
4534
|
rowDiv._wb_node = this;
|
|
@@ -4929,7 +4981,7 @@
|
|
|
4929
4981
|
const orgEvent = options === null || options === void 0 ? void 0 : options.event; // Default: null
|
|
4930
4982
|
const colIdx = options === null || options === void 0 ? void 0 : options.colIdx; // Default: null
|
|
4931
4983
|
const edit = options === null || options === void 0 ? void 0 : options.edit; // Default: false
|
|
4932
|
-
assert(!colIdx || tree.isCellNav(), "colIdx requires cellNav");
|
|
4984
|
+
// util.assert(!colIdx || tree.isCellNav(), "colIdx requires cellNav");
|
|
4933
4985
|
assert(!edit || colIdx != null, "edit requires colIdx");
|
|
4934
4986
|
if (!noEvents) {
|
|
4935
4987
|
if (flag) {
|
|
@@ -4983,7 +5035,7 @@
|
|
|
4983
5035
|
* Expand or collapse this node.
|
|
4984
5036
|
*/
|
|
4985
5037
|
async setExpanded(flag = true, options) {
|
|
4986
|
-
const { force, scrollIntoView, immediate } = options !== null && options !== void 0 ? options : {};
|
|
5038
|
+
const { force, scrollIntoView, immediate, resetLazy } = options !== null && options !== void 0 ? options : {};
|
|
4987
5039
|
const sendEvents = !(options === null || options === void 0 ? void 0 : options.noEvents); // Default: send events
|
|
4988
5040
|
if (!flag &&
|
|
4989
5041
|
this.isExpanded() &&
|
|
@@ -5006,6 +5058,9 @@
|
|
|
5006
5058
|
if (flag && this.lazy && this.children == null) {
|
|
5007
5059
|
await this.loadLazy();
|
|
5008
5060
|
}
|
|
5061
|
+
else if (!flag && resetLazy && this.lazy && this.children) {
|
|
5062
|
+
this.resetLazy();
|
|
5063
|
+
}
|
|
5009
5064
|
this.expanded = flag;
|
|
5010
5065
|
const updateOpts = { immediate: immediate };
|
|
5011
5066
|
// const updateOpts = { immediate: !!util.getOption(options, "immediate") };
|
|
@@ -5438,6 +5493,21 @@
|
|
|
5438
5493
|
av = a.data[propName];
|
|
5439
5494
|
bv = b.data[propName];
|
|
5440
5495
|
}
|
|
5496
|
+
if (av == null && bv == null) {
|
|
5497
|
+
return 0;
|
|
5498
|
+
}
|
|
5499
|
+
if (av == null) {
|
|
5500
|
+
av = typeof bv === "string" ? "" : 0;
|
|
5501
|
+
}
|
|
5502
|
+
else if (typeof av === "boolean") {
|
|
5503
|
+
av = av ? 1 : 0;
|
|
5504
|
+
}
|
|
5505
|
+
if (bv == null) {
|
|
5506
|
+
bv = typeof av === "string" ? "" : 0;
|
|
5507
|
+
}
|
|
5508
|
+
else if (typeof bv === "boolean") {
|
|
5509
|
+
bv = bv ? 1 : 0;
|
|
5510
|
+
}
|
|
5441
5511
|
if (caseInsensitive) {
|
|
5442
5512
|
if (typeof av === "string") {
|
|
5443
5513
|
av = av.toLowerCase();
|
|
@@ -5561,7 +5631,7 @@
|
|
|
5561
5631
|
/*!
|
|
5562
5632
|
* Wunderbaum - ext-edit
|
|
5563
5633
|
* Copyright (c) 2021-2024, Martin Wendt. Released under the MIT license.
|
|
5564
|
-
* v0.
|
|
5634
|
+
* v0.12.0, Sun, 12 Jan 2025 10:51:41 GMT (https://github.com/mar10/wunderbaum)
|
|
5565
5635
|
*/
|
|
5566
5636
|
// const START_MARKER = "\uFFF7";
|
|
5567
5637
|
class EditExtension extends WunderbaumExtension {
|
|
@@ -5725,6 +5795,10 @@
|
|
|
5725
5795
|
if (!node) {
|
|
5726
5796
|
return;
|
|
5727
5797
|
}
|
|
5798
|
+
if (node.isStatusNode()) {
|
|
5799
|
+
node.logWarn("Cannot edit status node.");
|
|
5800
|
+
return;
|
|
5801
|
+
}
|
|
5728
5802
|
this.tree.logDebug(`startEditTitle(node=${node})`);
|
|
5729
5803
|
let inputHtml = node._callEvent("edit.beforeEdit");
|
|
5730
5804
|
if (inputHtml === false) {
|
|
@@ -5892,8 +5966,8 @@
|
|
|
5892
5966
|
* https://github.com/mar10/wunderbaum
|
|
5893
5967
|
*
|
|
5894
5968
|
* Released under the MIT license.
|
|
5895
|
-
* @version v0.
|
|
5896
|
-
* @date Sun,
|
|
5969
|
+
* @version v0.12.0
|
|
5970
|
+
* @date Sun, 12 Jan 2025 10:51:41 GMT
|
|
5897
5971
|
*/
|
|
5898
5972
|
// import "./wunderbaum.scss";
|
|
5899
5973
|
class WbSystemRoot extends WunderbaumNode {
|
|
@@ -5975,7 +6049,7 @@
|
|
|
5975
6049
|
debugLevel: DEFAULT_DEBUGLEVEL, // 0:quiet, 1:errors, 2:warnings, 3:info, 4:verbose
|
|
5976
6050
|
header: null, // Show/hide header (pass bool or string)
|
|
5977
6051
|
// headerHeightPx: ROW_HEIGHT,
|
|
5978
|
-
rowHeightPx:
|
|
6052
|
+
rowHeightPx: DEFAULT_ROW_HEIGHT,
|
|
5979
6053
|
iconMap: "bootstrap",
|
|
5980
6054
|
columns: null,
|
|
5981
6055
|
types: null,
|
|
@@ -6060,6 +6134,10 @@
|
|
|
6060
6134
|
if (!this.element.getAttribute("tabindex")) {
|
|
6061
6135
|
this.element.tabIndex = 0;
|
|
6062
6136
|
}
|
|
6137
|
+
if (opts.rowHeightPx !== DEFAULT_ROW_HEIGHT) {
|
|
6138
|
+
this.element.style.setProperty("--wb-row-outer-height", opts.rowHeightPx + "px");
|
|
6139
|
+
this.element.style.setProperty("--wb-row-inner-height", opts.rowHeightPx - 2 + "px");
|
|
6140
|
+
}
|
|
6063
6141
|
// Attach tree instance to <div>
|
|
6064
6142
|
this.element._wb_tree = this;
|
|
6065
6143
|
// Create header markup, or take it from the existing html
|
|
@@ -6222,7 +6300,10 @@
|
|
|
6222
6300
|
false) {
|
|
6223
6301
|
return false;
|
|
6224
6302
|
}
|
|
6225
|
-
if (node &&
|
|
6303
|
+
if (node &&
|
|
6304
|
+
info.colIdx === 0 &&
|
|
6305
|
+
node.isExpandable() &&
|
|
6306
|
+
info.region !== NodeRegion.expander) {
|
|
6226
6307
|
this._callMethod("edit._stopEditTitle");
|
|
6227
6308
|
node.setExpanded(!node.isExpanded());
|
|
6228
6309
|
}
|
|
@@ -6469,31 +6550,33 @@
|
|
|
6469
6550
|
}
|
|
6470
6551
|
/** Return the topmost visible node in the viewport. */
|
|
6471
6552
|
getTopmostVpNode(complete = true) {
|
|
6553
|
+
const rowHeight = this.options.rowHeightPx;
|
|
6472
6554
|
const gracePx = 1; // ignore subpixel scrolling
|
|
6473
6555
|
const scrollParent = this.element;
|
|
6474
6556
|
// const headerHeight = this.headerElement.clientHeight; // May be 0
|
|
6475
6557
|
const scrollTop = scrollParent.scrollTop; // + headerHeight;
|
|
6476
6558
|
let topIdx;
|
|
6477
6559
|
if (complete) {
|
|
6478
|
-
topIdx = Math.ceil((scrollTop - gracePx) /
|
|
6560
|
+
topIdx = Math.ceil((scrollTop - gracePx) / rowHeight);
|
|
6479
6561
|
}
|
|
6480
6562
|
else {
|
|
6481
|
-
topIdx = Math.floor(scrollTop /
|
|
6563
|
+
topIdx = Math.floor(scrollTop / rowHeight);
|
|
6482
6564
|
}
|
|
6483
6565
|
return this._getNodeByRowIdx(topIdx);
|
|
6484
6566
|
}
|
|
6485
6567
|
/** Return the lowest visible node in the viewport. */
|
|
6486
6568
|
getLowestVpNode(complete = true) {
|
|
6569
|
+
const rowHeight = this.options.rowHeightPx;
|
|
6487
6570
|
const scrollParent = this.element;
|
|
6488
6571
|
const headerHeight = this.headerElement.clientHeight; // May be 0
|
|
6489
6572
|
const scrollTop = scrollParent.scrollTop;
|
|
6490
6573
|
const clientHeight = scrollParent.clientHeight - headerHeight;
|
|
6491
6574
|
let bottomIdx;
|
|
6492
6575
|
if (complete) {
|
|
6493
|
-
bottomIdx = Math.floor((scrollTop + clientHeight) /
|
|
6576
|
+
bottomIdx = Math.floor((scrollTop + clientHeight) / rowHeight) - 1;
|
|
6494
6577
|
}
|
|
6495
6578
|
else {
|
|
6496
|
-
bottomIdx = Math.ceil((scrollTop + clientHeight) /
|
|
6579
|
+
bottomIdx = Math.ceil((scrollTop + clientHeight) / rowHeight) - 1;
|
|
6497
6580
|
}
|
|
6498
6581
|
bottomIdx = Math.min(bottomIdx, this.count(true) - 1);
|
|
6499
6582
|
return this._getNodeByRowIdx(bottomIdx);
|
|
@@ -6925,8 +7008,9 @@
|
|
|
6925
7008
|
* @param includeHidden Not yet implemented
|
|
6926
7009
|
*/
|
|
6927
7010
|
findRelatedNode(node, where, includeHidden = false) {
|
|
7011
|
+
const rowHeight = this.options.rowHeightPx;
|
|
6928
7012
|
let res = null;
|
|
6929
|
-
const pageSize = Math.floor(this.listContainerElement.clientHeight /
|
|
7013
|
+
const pageSize = Math.floor(this.listContainerElement.clientHeight / rowHeight);
|
|
6930
7014
|
switch (where) {
|
|
6931
7015
|
case "parent":
|
|
6932
7016
|
if (node.parent && node.parent.parent) {
|
|
@@ -7249,6 +7333,7 @@
|
|
|
7249
7333
|
scrollTo(nodeOrOpts) {
|
|
7250
7334
|
const PADDING = 2; // leave some pixels between viewport bounds
|
|
7251
7335
|
let node;
|
|
7336
|
+
// WunderbaumNode;
|
|
7252
7337
|
let options;
|
|
7253
7338
|
if (nodeOrOpts instanceof WunderbaumNode) {
|
|
7254
7339
|
node = nodeOrOpts;
|
|
@@ -7258,14 +7343,15 @@
|
|
|
7258
7343
|
node = options.node;
|
|
7259
7344
|
}
|
|
7260
7345
|
assert(node && node._rowIdx != null, `Invalid node: ${node}`);
|
|
7346
|
+
const rowHeight = this.options.rowHeightPx;
|
|
7261
7347
|
const scrollParent = this.element;
|
|
7262
7348
|
const headerHeight = this.headerElement.clientHeight; // May be 0
|
|
7263
7349
|
const scrollTop = scrollParent.scrollTop;
|
|
7264
7350
|
const vpHeight = scrollParent.clientHeight;
|
|
7265
|
-
const rowTop = node._rowIdx *
|
|
7351
|
+
const rowTop = node._rowIdx * rowHeight + headerHeight;
|
|
7266
7352
|
const vpTop = headerHeight;
|
|
7267
7353
|
const vpRowTop = rowTop - scrollTop;
|
|
7268
|
-
const vpRowBottom = vpRowTop +
|
|
7354
|
+
const vpRowBottom = vpRowTop + rowHeight;
|
|
7269
7355
|
const topNode = options === null || options === void 0 ? void 0 : options.topNode;
|
|
7270
7356
|
// this.log( `scrollTo(${node.title}), vpTop:${vpTop}px, scrollTop:${scrollTop}, vpHeight:${vpHeight}, rowTop:${rowTop}, vpRowTop:${vpRowTop}`, nodeOrOpts , options);
|
|
7271
7357
|
let newScrollTop = null;
|
|
@@ -7274,7 +7360,7 @@
|
|
|
7274
7360
|
else {
|
|
7275
7361
|
// Node is below viewport
|
|
7276
7362
|
// this.log("Below viewport");
|
|
7277
|
-
newScrollTop = rowTop +
|
|
7363
|
+
newScrollTop = rowTop + rowHeight - vpHeight + PADDING; // leave some pixels between viewport bounds
|
|
7278
7364
|
}
|
|
7279
7365
|
}
|
|
7280
7366
|
else {
|
|
@@ -7887,19 +7973,19 @@
|
|
|
7887
7973
|
// this.log("_updateRows", opts)
|
|
7888
7974
|
options = Object.assign({ newNodesOnly: false }, options);
|
|
7889
7975
|
const newNodesOnly = !!options.newNodesOnly;
|
|
7890
|
-
const
|
|
7891
|
-
const
|
|
7976
|
+
const rowHeight = this.options.rowHeightPx;
|
|
7977
|
+
const vpHeight = this.element.clientHeight;
|
|
7892
7978
|
const prefetch = RENDER_MAX_PREFETCH;
|
|
7893
7979
|
// const grace_prefetch = RENDER_MAX_PREFETCH - RENDER_MIN_PREFETCH;
|
|
7894
7980
|
const ofs = this.element.scrollTop;
|
|
7895
|
-
let startIdx = Math.max(0, ofs /
|
|
7981
|
+
let startIdx = Math.max(0, ofs / rowHeight - prefetch);
|
|
7896
7982
|
startIdx = Math.floor(startIdx);
|
|
7897
7983
|
// Make sure start is always even, so the alternating row colors don't
|
|
7898
7984
|
// change when scrolling:
|
|
7899
7985
|
if (startIdx % 2) {
|
|
7900
7986
|
startIdx--;
|
|
7901
7987
|
}
|
|
7902
|
-
let endIdx = Math.max(0, (ofs +
|
|
7988
|
+
let endIdx = Math.max(0, (ofs + vpHeight) / rowHeight + prefetch);
|
|
7903
7989
|
endIdx = Math.ceil(endIdx);
|
|
7904
7990
|
// this.debug("render", opts);
|
|
7905
7991
|
const obsoleteNodes = new Set();
|
|
@@ -7928,21 +8014,21 @@
|
|
|
7928
8014
|
else if (rowDiv && newNodesOnly) {
|
|
7929
8015
|
obsoleteNodes.delete(node);
|
|
7930
8016
|
// no need to update existing node markup
|
|
7931
|
-
rowDiv.style.top = idx *
|
|
8017
|
+
rowDiv.style.top = idx * rowHeight + "px";
|
|
7932
8018
|
prevElem = rowDiv;
|
|
7933
8019
|
}
|
|
7934
8020
|
else {
|
|
7935
8021
|
obsoleteNodes.delete(node);
|
|
7936
8022
|
// Create new markup
|
|
7937
8023
|
if (rowDiv) {
|
|
7938
|
-
rowDiv.style.top = idx *
|
|
8024
|
+
rowDiv.style.top = idx * rowHeight + "px";
|
|
7939
8025
|
}
|
|
7940
8026
|
node._render({ top: top, after: prevElem });
|
|
7941
8027
|
// node.log("render", top, prevElem, "=>", node._rowElem);
|
|
7942
8028
|
prevElem = node._rowElem;
|
|
7943
8029
|
}
|
|
7944
8030
|
idx++;
|
|
7945
|
-
top +=
|
|
8031
|
+
top += rowHeight;
|
|
7946
8032
|
});
|
|
7947
8033
|
this.treeRowCount = idx;
|
|
7948
8034
|
for (const n of obsoleteNodes) {
|
|
@@ -8206,7 +8292,7 @@
|
|
|
8206
8292
|
}
|
|
8207
8293
|
Wunderbaum.sequence = 0;
|
|
8208
8294
|
/** Wunderbaum release version number "MAJOR.MINOR.PATCH". */
|
|
8209
|
-
Wunderbaum.version = "v0.
|
|
8295
|
+
Wunderbaum.version = "v0.12.0"; // Set to semver by 'grunt release'
|
|
8210
8296
|
/** Expose some useful methods of the util.ts module as `Wunderbaum.util`. */
|
|
8211
8297
|
Wunderbaum.util = util;
|
|
8212
8298
|
|