wunderbaum 0.13.0 → 0.14.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wunderbaum",
3
- "version": "0.13.0",
3
+ "version": "0.14.0",
4
4
  "title": "A treegrid control.",
5
5
  "description": "JavaScript tree/grid/treegrid control.",
6
6
  "homepage": "https://github.com/mar10/wunderbaum",
@@ -52,6 +52,7 @@
52
52
  "@rollup/plugin-replace": "^6.0.2",
53
53
  "@rollup/plugin-terser": "^0.4.4",
54
54
  "@rollup/plugin-typescript": "^12.1.2",
55
+ "@types/firebase": "^2.4.32",
55
56
  "@types/jest": "^29.5.14",
56
57
  "@typescript-eslint/eslint-plugin": "^8.24.0",
57
58
  "@typescript-eslint/parser": "^8.24.0",
@@ -104,7 +105,7 @@
104
105
  "scripts": {
105
106
  "test": "npm run lint && npm run build:js && grunt ci --verbose",
106
107
  "api_docs": "typedoc && touch docs/api/.nojekyll && rm docs/unittest/*.*; cp test/unit/*.* docs/unittest",
107
- "format": "eslint src --fix && prettier src docs/demo -w && npm run lint",
108
+ "format": "eslint src docs/demo --fix && prettier src docs/demo -w && npm run lint",
108
109
  "lint": "prettier src docs/demo --check && eslint src docs/demo && tsc -t esnext --moduleResolution node --noEmit src/wunderbaum.ts",
109
110
  "build:minjs:umd": "terser build/wunderbaum.umd.js --compress --mangle --source-map \"base='build',url='wunderbaum.umd.min.js.map',filename='wunderbaum.umd.js'\" --output build/wunderbaum.umd.min.js",
110
111
  "build:minjs:esm": "terser build/wunderbaum.esm.js --compress --mangle --source-map \"base='build',url='wunderbaum.esm.min.js.map',filename='wunderbaum.esm.js'\" --output build/wunderbaum.esm.min.js",
package/src/common.ts CHANGED
@@ -35,13 +35,23 @@ export const RENDER_MAX_PREFETCH = 5;
35
35
  export const RENDER_MIN_PREFETCH = 5;
36
36
  /** Minimum column width if not set otherwise. */
37
37
  export const DEFAULT_MIN_COL_WIDTH = 4;
38
+ /**
39
+ * A value for `node.type` that by convention may be used to mark a node as directory.
40
+ * It may be used to sort 'directories' to the top.
41
+ */
42
+ export const NODE_TYPE_FOLDER = "folder";
38
43
  /** Regular expression to detect if a string describes an image URL (in contrast
39
44
  * to a class name). Strings are considered image urls if they contain '.' or '/'.
45
+ * `<` is ignored, because it is probably an html tag.
40
46
  */
41
- export const TEST_IMG = new RegExp(/\.|\//);
47
+ export const TEST_FILE_PATH = /^(?!.*<).*[/.]/;
48
+ /** Regular expression to detect if a string describes an HTML element. */
49
+ export const TEST_HTML = /</;
42
50
  // export const RECURSIVE_REQUEST_ERROR = "$recursive_request";
43
51
  // export const INVALID_REQUEST_TARGET_ERROR = "$request_target_invalid";
44
52
 
53
+ /** Currently supported default icon maps. */
54
+ type IconLibrary = "bootstrap" | "fontawesome6";
45
55
  /**
46
56
  * Default node icons for icon libraries
47
57
  *
@@ -49,7 +59,7 @@ export const TEST_IMG = new RegExp(/\.|\//);
49
59
  * - 'fontawesome6' {@link https://fontawesome.com/icons}
50
60
  *
51
61
  */
52
- export const iconMaps: { [key: string]: IconMapType } = {
62
+ export const defaultIconMaps: { [key in IconLibrary]: IconMapType } = {
53
63
  bootstrap: {
54
64
  error: "bi bi-exclamation-triangle",
55
65
  // loading: "bi bi-hourglass-split wb-busy",
@@ -97,7 +107,7 @@ export const iconMaps: { [key: string]: IconMapType } = {
97
107
  radioChecked: "fa-solid fa-circle",
98
108
  radioUnchecked: "fa-regular fa-circle",
99
109
  radioUnknown: "fa-regular fa-circle-question",
100
- folder: "fa-solid fa-folder-closed",
110
+ folder: "fa-regular fa-folder-closed",
101
111
  folderOpen: "fa-regular fa-folder-open",
102
112
  folderLazy: "fa-solid fa-folder-plus",
103
113
  doc: "fa-regular fa-file",
@@ -221,7 +231,9 @@ export function makeNodeTitleStartMatcher(s: string): MatcherCallback {
221
231
  };
222
232
  }
223
233
 
224
- /** Compare two nodes by title (case-insensitive). */
234
+ /** Compare two nodes by title (case-insensitive).
235
+ * @deprecated Use `key` option instead of `cmp` in sort methods.
236
+ */
225
237
  export function nodeTitleSorter(a: WunderbaumNode, b: WunderbaumNode): number {
226
238
  const x = a.title.toLowerCase();
227
239
  const y = b.title.toLowerCase();
@@ -229,6 +241,13 @@ export function nodeTitleSorter(a: WunderbaumNode, b: WunderbaumNode): number {
229
241
  return x === y ? 0 : x > y ? 1 : -1;
230
242
  }
231
243
 
244
+ // /** Compare nodes by title (case-insensitive). */
245
+ // export function nodeTitleKeyGetter(
246
+ // node: WunderbaumNode
247
+ // ): string | number | Array<any> {
248
+ // return node.title.toLowerCase();
249
+ // }
250
+
232
251
  /**
233
252
  * Convert 'flat' to 'nested' format.
234
253
  *
package/src/types.ts CHANGED
@@ -60,8 +60,13 @@ export type MatcherCallback = (node: WunderbaumNode) => boolean;
60
60
  export type WbIconBadgeCallback = (
61
61
  e: WbIconBadgeEventType
62
62
  ) => WbIconBadgeEventResultType;
63
- /** Passed to `sortChildren()` methods. Should return -1, 0, or 1. */
63
+ /**
64
+ * Passed to `sort()` methods. Should return -1, 0, or 1.
65
+ * @deprecated Use SortKeyCallback instead
66
+ */
64
67
  export type SortCallback = (a: WunderbaumNode, b: WunderbaumNode) => number;
68
+ /** Passed to `sort()` methods. Should return a representation that can be compared using `<`. */
69
+ export type SortKeyCallback = (node: WunderbaumNode) => string | number | any[];
65
70
  /** When set as option, called when the value is needed (e.g. `colspan` type definition). */
66
71
  export type BoolOptionResolver = (node: WunderbaumNode) => boolean;
67
72
  /** When set as option, called when the value is needed (e.g. `icon` type definition). */
@@ -99,6 +104,12 @@ export type NodeToDictCallback = (
99
104
  */
100
105
  export type NodeSelectCallback = (node: WunderbaumNode) => boolean | void;
101
106
 
107
+ /** @internal */
108
+ export type DeprecationOptions = {
109
+ since?: string;
110
+ hint?: string;
111
+ };
112
+
102
113
  /**
103
114
  * See also {@link WunderbaumNode.getOption|WunderbaumNode.getOption()}
104
115
  * to evaluate `node.NAME` setting and `tree.types[node.type].NAME`.
@@ -163,7 +174,7 @@ export interface WbNodeData {
163
174
  title: string;
164
175
  /** Pass true to set node tooltip to the node's title. Defaults to {@link WunderbaumOptions.tooltip}. */
165
176
  tooltip?: TooltipOption;
166
- /** Inherit shared settings from the matching entry in {@link WunderbaumOptions.types}. */
177
+ /** Inherit shared settings from the matching entry in `InitWunderbaumOptions.types`. */
167
178
  type?: string;
168
179
  /** Set to `true` to prevent selection. Defaults to {@link WunderbaumOptions.unselectable}. */
169
180
  unselectable?: boolean;
@@ -203,7 +214,7 @@ export interface IconMapType {
203
214
  * EVENT CALLBACK TYPES
204
215
  * ---------------------------------------------------------------------------*/
205
216
 
206
- /** A callback that receives a node instance and returns a string value. */
217
+ /** Retuen value of an event handler that can return `false` to prevent the default action. */
207
218
  export type WbCancelableEventResultType = false | void;
208
219
 
209
220
  export interface WbTreeEventType {
@@ -497,21 +508,6 @@ export interface ColumnDefinition {
497
508
 
498
509
  export type ColumnDefinitionList = Array<ColumnDefinition>;
499
510
 
500
- /**
501
- * Used by {@link Wunderbaum.getState} and {@link Wunderbaum.setState}.
502
- */
503
- export interface TreeStateDefinition {
504
- /** The active node's key if any. */
505
- activeKey: string | null;
506
- /** The active column index if any. */
507
- activeColIdx: number | null;
508
- /** List of selected node's keys. */
509
- selectedKeys: Array<string> | undefined;
510
- /** List of expanded node's keys. */
511
- expandedKeys: Array<string> | undefined;
512
- /** List of checked node's keys. */
513
- }
514
-
515
511
  /**
516
512
  * Column information (passed to the `render` event).
517
513
  */
@@ -753,8 +749,7 @@ export interface FilterNodesOptions {
753
749
  /**Hide expanders if all child nodes are hidden by filter @default false */
754
750
  hideExpanders?: boolean;
755
751
  /** Highlight matches by wrapping inside `<mark>` tags.
756
- * Does not work for filter callbacks.
757
- * @default true
752
+ * Does not work for filter callbacks. @default true
758
753
  */
759
754
  highlight?: boolean;
760
755
  /** Match end nodes only @default false */
@@ -767,11 +762,11 @@ export interface FilterNodesOptions {
767
762
 
768
763
  /** Possible values for {@link Wunderbaum.getState}. */
769
764
  export interface GetStateOptions {
770
- // /** Include the activated key. @default true */
771
- // activeKey?: boolean;
772
- /** Include the expanded keys. @default true */
765
+ /** Include the active node's key (and expand its parents). @default true */
766
+ activeKey?: boolean;
767
+ /** Include the expanded keys. @default false */
773
768
  expandedKeys?: boolean;
774
- /** Include the selected keys. @default true */
769
+ /** Include the selected keys. @default false */
775
770
  selectedKeys?: boolean;
776
771
  }
777
772
 
@@ -781,6 +776,28 @@ export interface SetStateOptions {
781
776
  expandLazy?: boolean;
782
777
  }
783
778
 
779
+ /** Used by {@link Wunderbaum.getState} and {@link Wunderbaum.setState}. */
780
+ export interface TreeStateDefinition {
781
+ /** List of expanded node's keys. */
782
+ expandedKeys: Array<string> | undefined;
783
+ /** The active node's key if any. */
784
+ activeKey: string | null;
785
+ /** The active column index if any. */
786
+ activeColIdx: number | null;
787
+ /** List of selected node's keys. */
788
+ selectedKeys: Array<string> | undefined;
789
+ }
790
+
791
+ /** Possible values for {@link Wunderbaum.loadLazyNodes} `options` argument. */
792
+ export interface LoadLazyNodesOptions {
793
+ /** Expand node (otherwise load, but keep collapsed). @default true */
794
+ expand?: boolean;
795
+ /** Force reloading even if already loaded. @default false */
796
+ force?: boolean;
797
+ /** Do not send events. @default false */
798
+ noEvents?: boolean;
799
+ }
800
+
784
801
  /** Possible values for {@link WunderbaumNode.makeVisible}. */
785
802
  export interface MakeVisibleOptions {
786
803
  /** Do not animate expand (currently not implemented). @default false */
@@ -918,7 +935,17 @@ export interface SetStatusOptions {
918
935
  }
919
936
 
920
937
  /**
921
- * Possible values for {@link WunderbaumNode.sortByProperty} `options` argument.
938
+ * Possible values for {@link Wunderbaum.reload} `options` argument.
939
+ */
940
+ export interface ReloadOptions {
941
+ /** Load this source instead. @default initial source (if loaded via ajax) */
942
+ source?: SourceType;
943
+ /** Reactivate currently active node if any. @default true */
944
+ reactivate?: boolean;
945
+ }
946
+
947
+ /**
948
+ * Possible values for {@link WunderbaumNode.resetNativeChildOrder} `options` argument.
922
949
  */
923
950
  export interface ResetOrderOptions {
924
951
  /** Sort descendants recursively. @default true */
@@ -930,31 +957,37 @@ export interface ResetOrderOptions {
930
957
  }
931
958
 
932
959
  /**
933
- * Possible values for {@link WunderbaumNode.sortByProperty} `options` argument.
960
+ * Possible values for {@link Wunderbaum.sort} and {@link WunderbaumNode.sort}
961
+ * `options` argument.
934
962
  */
935
- export interface SortByPropertyOptions {
936
- /** Column ID as defined in `tree.columns` definition. Required if updateColInfo is true.*/
937
- colId?: string;
963
+ export interface SortOptions {
938
964
  /** The name of the node property that will be used for sorting.
939
- * @default use the `colId` as property name.
965
+ * Mandatory, unless {@link key} or {@link colId} are given.
940
966
  */
941
967
  propName?: string;
942
- // /** If defined, this callback is used to extract the value to be sorted. */
943
- // vallueGetter?: NodePropertyGetterCallback;
944
- /** Sort order. @default Use value from column definition (rotated).*/
968
+ /** Callback that determines a node representation for comparison.
969
+ * @default {@link common.nodeTitleKeyGetter} */
970
+ key?: SortKeyCallback;
971
+ /** Callback that determines the order. @default {@link common.nodeTitleSorter}
972
+ * @deprecated use {@link key} instead
973
+ */
974
+ cmp?: SortCallback;
975
+ /** Sort order 'asc' or 'desc'.
976
+ * @default 'asc' (or if `updateColInfo` is true, the rotated status of the
977
+ * column definition.
978
+ * See also {@link WunderbaumOptions.sortFoldersFirst}.
979
+ */
945
980
  order?: SortOrderType;
981
+ /** Sort descendants recursively. @default true */
982
+ deep?: boolean;
983
+ /** Sort string values case insensitive. @default false */
984
+ caseInsensitive?: boolean;
946
985
  /**
947
986
  * Sort by this property if order is `undefined`.
948
987
  * See also {@link WunderbaumNode.resetNativeChildOrder}.
949
988
  * @default `_nativeIndex`.
950
989
  */
951
990
  nativeOrderPropName?: string;
952
- /** Sort string values case insensitive. @default false */
953
- caseInsensitive?: boolean;
954
- /** Sort descendants recursively. @default true */
955
- deep?: boolean;
956
- // /** Rotate sort order (asc -> desc -> none) before sorting. @default false */
957
- // rotateOrder?: boolean;
958
991
  /**
959
992
  * Rotate sort order (asc -> desc -> none) before sorting.
960
993
  * Update the sort icons in the column header
@@ -965,8 +998,16 @@ export interface SortByPropertyOptions {
965
998
  * @default false
966
999
  */
967
1000
  updateColInfo?: boolean;
1001
+ /** Column ID as defined in `tree.columns` definition. Required if updateColInfo is true.*/
1002
+ colId?: string;
968
1003
  }
969
1004
 
1005
+ /**
1006
+ * Possible values for {@link WunderbaumNode.sortByProperty} `options` argument.
1007
+ * @deprecated
1008
+ */
1009
+ export type SortByPropertyOptions = SortOptions;
1010
+
970
1011
  /** Options passed to {@link Wunderbaum.visitRows}. */
971
1012
  export interface VisitRowsOptions {
972
1013
  /** Skip filtered nodes and children of collapsed nodes. @default false */
@@ -1122,6 +1163,7 @@ export type DropEffectAllowedType =
1122
1163
 
1123
1164
  export type DropRegionType = "over" | "before" | "after";
1124
1165
  export type DropRegionTypeSet = Set<DropRegionType>;
1166
+ export type DropRegionTypeList = Array<DropRegionType>;
1125
1167
  // type AllowedDropRegionType =
1126
1168
  // | "after"
1127
1169
  // | "afterBefore"
@@ -1147,6 +1189,8 @@ export interface DropEventType extends WbNodeEventType {
1147
1189
  node: WunderbaumNode;
1148
1190
  /** The source node if any. */
1149
1191
  sourceNode: WunderbaumNode;
1192
+ /** The DataTransfer object. */
1193
+ dataTransfer: DataTransfer;
1150
1194
  }
1151
1195
 
1152
1196
  export type DndOptionsType = {
@@ -1292,7 +1336,9 @@ export type DndOptionsType = {
1292
1336
  */
1293
1337
  dragEnter?:
1294
1338
  | null
1295
- | ((e: DropEventType) => DropRegionType | DropRegionTypeSet | boolean);
1339
+ | ((
1340
+ e: DropEventType
1341
+ ) => DropRegionType | DropRegionTypeSet | DropRegionTypeList | boolean);
1296
1342
  /**
1297
1343
  * Callback(targetNode, data)
1298
1344
  * @default null
package/src/util.ts CHANGED
@@ -36,6 +36,8 @@ const ENTITY_MAP: { [key: string]: string } = {
36
36
  "/": "&#x2F;",
37
37
  };
38
38
 
39
+ export type NotPromise<T> = T extends Promise<any> ? never : T;
40
+
39
41
  export type FunctionType = (...args: any[]) => any;
40
42
  export type EventCallbackType = (e: Event) => boolean | void;
41
43
  type PromiseCallbackType = (val: any) => void;
@@ -175,7 +177,7 @@ export function each(
175
177
  return obj;
176
178
  }
177
179
 
178
- /** Shortcut for `throw new Error(msg)`.*/
180
+ /** Shortcut for `throw new Error(msg)`. */
179
181
  export function error(msg: string) {
180
182
  throw new Error(msg);
181
183
  }
@@ -783,6 +785,11 @@ export function toPixel(
783
785
  throw new Error(`Expected a string like '123px': ${defaults}`);
784
786
  }
785
787
 
788
+ /** Cast any value to <T>. */
789
+ export function unsafeCast<T>(value: any): T {
790
+ return value as T;
791
+ }
792
+
786
793
  /** Return the the boolean value of the first non-null element.
787
794
  * Example:
788
795
  * ```js
@@ -874,7 +881,7 @@ export function adaptiveThrottle(
874
881
  const throttledFn = (...args: any[]) => {
875
882
  if (waiting) {
876
883
  pendingArgs = args;
877
- // console.log(`adaptiveThrottle() queing request #${waiting}...`, args);
884
+ // console.log(`adaptiveThrottle() queueing request #${waiting}...`, args);
878
885
  waiting += 1;
879
886
  } else {
880
887
  // Prevent invocations while running or blocking
@@ -932,3 +939,69 @@ export function adaptiveThrottle(
932
939
  };
933
940
  return throttledFn;
934
941
  }
942
+
943
+ /**
944
+ * MurmurHash3 implementation for strings.
945
+ * @param key The input string to hash.
946
+ * @param asString Optional convert result to zero-padded string of 8 characters.
947
+ * @param seed Optional seed value.
948
+ * @returns A 32-bit hash as a number or string.
949
+ */
950
+ export function murmurHash3(
951
+ key: string,
952
+ asString = true,
953
+ seed: number = 0
954
+ ): number | string {
955
+ let h1 = seed;
956
+ const remainder = key.length & 3; // key.length % 4
957
+ const bytes = key.length - remainder;
958
+ const c1 = 0xcc9e2d51;
959
+ const c2 = 0x1b873593;
960
+
961
+ let i = 0;
962
+ while (i < bytes) {
963
+ let k1 =
964
+ (key.charCodeAt(i) & 0xff) |
965
+ ((key.charCodeAt(++i) & 0xff) << 8) |
966
+ ((key.charCodeAt(++i) & 0xff) << 16) |
967
+ ((key.charCodeAt(++i) & 0xff) << 24);
968
+ ++i;
969
+
970
+ k1 = Math.imul(k1, c1);
971
+ k1 = (k1 << 15) | (k1 >>> 17);
972
+ k1 = Math.imul(k1, c2);
973
+
974
+ h1 ^= k1;
975
+ h1 = (h1 << 13) | (h1 >>> 19);
976
+ h1 = Math.imul(h1, 5) + 0xe6546b64;
977
+ }
978
+
979
+ let k1 = 0;
980
+ switch (remainder) {
981
+ case 3:
982
+ k1 ^= (key.charCodeAt(i + 2) & 0xff) << 16;
983
+ // fall through
984
+ case 2:
985
+ k1 ^= (key.charCodeAt(i + 1) & 0xff) << 8;
986
+ // fall through
987
+ case 1:
988
+ k1 ^= key.charCodeAt(i) & 0xff;
989
+ k1 = Math.imul(k1, c1);
990
+ k1 = (k1 << 15) | (k1 >>> 17);
991
+ k1 = Math.imul(k1, c2);
992
+ h1 ^= k1;
993
+ }
994
+
995
+ h1 ^= key.length;
996
+ h1 ^= h1 >>> 16;
997
+ h1 = Math.imul(h1, 0x85ebca6b);
998
+ h1 ^= h1 >>> 13;
999
+ h1 = Math.imul(h1, 0xc2b2ae35);
1000
+ h1 ^= h1 >>> 16;
1001
+
1002
+ if (asString) {
1003
+ // Convert to 8 digit hex string
1004
+ return (h1 >>> 0).toString(16).padStart(8, "0");
1005
+ }
1006
+ return h1 >>> 0; // Convert to unsigned 32-bit integer
1007
+ }
package/src/wb_ext_dnd.ts CHANGED
@@ -78,7 +78,7 @@ export class DndExtension extends WunderbaumExtension<DndOptionsType> {
78
78
  // this.$scrollParent = $temp.scrollParent();
79
79
  // $temp.remove();
80
80
  const tree = this.tree;
81
- const dndOpts = tree.options.dnd!;
81
+ const dndOpts = tree.options.dnd;
82
82
 
83
83
  // Enable drag support if dragStart() is specified:
84
84
  if (dndOpts.dragStart) {
@@ -134,7 +134,7 @@ export class DndExtension extends WunderbaumExtension<DndOptionsType> {
134
134
  e: DragEvent,
135
135
  allowed: DropRegionTypeSet | null
136
136
  ): DropRegionType | false {
137
- const rowHeight = this.tree.options.rowHeightPx!;
137
+ const rowHeight = this.tree.options.rowHeightPx;
138
138
  const dy = e.offsetY;
139
139
 
140
140
  if (!allowed) {
@@ -208,7 +208,7 @@ export class DndExtension extends WunderbaumExtension<DndOptionsType> {
208
208
  // `_isVoidDrop: ${srcNode} -> ${dropRegion} ${targetNode}`
209
209
  // );
210
210
  // TODO: should be checked on move only
211
- if (!this.treeOpts.dnd.preventVoidMoves || !srcNode) {
211
+ if (!this.treeOpts.dnd!.preventVoidMoves || !srcNode) {
212
212
  return false;
213
213
  }
214
214
  if (
@@ -225,7 +225,7 @@ export class DndExtension extends WunderbaumExtension<DndOptionsType> {
225
225
  /* Implement auto scrolling when drag cursor is in top/bottom area of scroll parent. */
226
226
  protected _applyScrollDir(): void {
227
227
  if (this.isDragging() && this.currentScrollDir) {
228
- const dndOpts = this.tree.options.dnd!;
228
+ const dndOpts = this.tree.options.dnd;
229
229
  const sp = this.tree.element; // scroll parent
230
230
  const scrollTop = sp.scrollTop;
231
231
  if (this.currentScrollDir < 0) {
@@ -238,7 +238,7 @@ export class DndExtension extends WunderbaumExtension<DndOptionsType> {
238
238
  /* Implement auto scrolling when drag cursor is in top/bottom area of scroll parent. */
239
239
  protected _autoScroll(viewportY: number): number {
240
240
  const tree = this.tree;
241
- const dndOpts = tree.options.dnd!;
241
+ const dndOpts = tree.options.dnd;
242
242
  const sensitivity = dndOpts.scrollSensitivity;
243
243
  const sp = tree.element; // scroll parent
244
244
  const headerHeight = tree.headerElement.clientHeight; // May be 0
@@ -284,7 +284,6 @@ export class DndExtension extends WunderbaumExtension<DndOptionsType> {
284
284
  * Handle dragstart, drag and dragend events for the source node.
285
285
  */
286
286
  protected onDragEvent(e: DragEvent) {
287
- // const tree = this.tree;
288
287
  const dndOpts: DndOptionsType = this.treeOpts.dnd;
289
288
  const srcNode = Wunderbaum.getNode(e);
290
289
 
@@ -312,7 +311,7 @@ export class DndExtension extends WunderbaumExtension<DndOptionsType> {
312
311
  return false;
313
312
  }
314
313
  const nodeData = srcNode.toDict(true, (n: any) => {
315
- // We don't want to re-use the key on drop:
314
+ // We don't want to reuse the key on drop:
316
315
  n._orgKey = n.key;
317
316
  delete n.key;
318
317
  });
@@ -377,6 +376,7 @@ export class DndExtension extends WunderbaumExtension<DndOptionsType> {
377
376
  };
378
377
  if (!targetNode) {
379
378
  this._leaveNode();
379
+ e.preventDefault(); // Don't open file in browser when dropped in empty area
380
380
  return;
381
381
  }
382
382
  if (["drop"].includes(e.type)) {
@@ -518,19 +518,20 @@ export class DndExtension extends WunderbaumExtension<DndOptionsType> {
518
518
  const srcNode = this.srcNode;
519
519
  const lastDropEffect = this.lastDropEffect;
520
520
 
521
- setTimeout(() => {
522
- // Decouple this call, because drop actions may prevent the dragend event
523
- // from being fired on some browsers
524
- targetNode._callEvent("dnd.drop", {
525
- event: e,
526
- region: region,
527
- suggestedDropMode: region === "over" ? "appendChild" : region,
528
- suggestedDropEffect: lastDropEffect,
529
- // suggestedDropEffect: e.dataTransfer?.dropEffect,
530
- sourceNode: srcNode,
531
- sourceNodeData: nodeData,
532
- });
533
- }, 10);
521
+ /* Before v0.14.0, we decoupled `_callEvent` like so:
522
+ Decouple this call, because drop actions may prevent the dragend
523
+ event from being fired on some browsers.
524
+ setTimeout(() => {...}, 10);
525
+ however this made e.dataTransfer.items inaccessible */
526
+ targetNode._callEvent("dnd.drop", {
527
+ event: e,
528
+ region: region,
529
+ suggestedDropMode: region === "over" ? "appendChild" : region,
530
+ suggestedDropEffect: lastDropEffect,
531
+ sourceNode: srcNode,
532
+ sourceNodeData: nodeData,
533
+ dataTransfer: e.dataTransfer,
534
+ });
534
535
  }
535
536
  return false;
536
537
  }
@@ -286,7 +286,7 @@ export class EditExtension extends WunderbaumExtension<EditOptionsType> {
286
286
  newValue = newValue.trim();
287
287
  }
288
288
  if (!node) {
289
- this.tree.logDebug("stopEditTitle: not in edit mode.");
289
+ // this.tree.logDebug("stopEditTitle: not in edit mode.");
290
290
  return;
291
291
  }
292
292
  node.logDebug(`stopEditTitle(${apply})`, options, focusElem, newValue);
@@ -75,7 +75,7 @@ export class FilterExtension extends WunderbaumExtension<FilterOptionsType> {
75
75
  const filterActive = this.tree.filterMode !== null;
76
76
  const activeNode = this.tree.getActiveNode();
77
77
  const matchCount = filterActive ? this.countMatches() : 0;
78
- const strings = this.treeOpts.strings!;
78
+ const strings = this.treeOpts.strings;
79
79
  let matchIdx: string | number = "?";
80
80
 
81
81
  if (this.matchInfoElem) {
@@ -363,6 +363,10 @@ export class FilterExtension extends WunderbaumExtension<FilterOptionsType> {
363
363
  options.matchBranch === undefined,
364
364
  "filterBranches() is deprecated."
365
365
  );
366
+ this.tree.logDeprecate("filterBranches()", {
367
+ since: "0.9.0",
368
+ hint: "Use `filterNodes` instead and set `options.matchBranch: true`",
369
+ });
366
370
  options.matchBranch = true;
367
371
  return this._applyFilterNoUpdate(filter, options);
368
372
  }
@@ -436,7 +440,7 @@ export class FilterExtension extends WunderbaumExtension<FilterOptionsType> {
436
440
  }
437
441
 
438
442
  /**
439
- * @description Marks the matching charecters of `text` either by `mark` or
443
+ * @description Marks the matching characters of `text` either by `mark` or
440
444
  * by exotic*Chars (if `escapeTitles` is `true`) based on `matches`
441
445
  * which is an array of matching groups.
442
446
  * @param {string} text
@@ -77,7 +77,7 @@ export class GridExtension extends WunderbaumExtension<GridOptionsType> {
77
77
  }
78
78
 
79
79
  /**
80
- * Hanldes drag and sragstop events for column resizing.
80
+ * Handles drag and sragstop events for column resizing.
81
81
  */
82
82
  protected handleDrag(e: DragCallbackArgType): void {
83
83
  const custom = e.customData;
@@ -5,10 +5,25 @@
5
5
  */
6
6
 
7
7
  import * as util from "./util";
8
+ import { DndExtension } from "./wb_ext_dnd";
9
+ import { EditExtension } from "./wb_ext_edit";
10
+ import { FilterExtension } from "./wb_ext_filter";
11
+ import { GridExtension } from "./wb_ext_grid";
12
+ import { KeynavExtension } from "./wb_ext_keynav";
13
+ import { LoggerExtension } from "./wb_ext_logger";
8
14
  import { WunderbaumOptions } from "./wb_options";
9
15
  import { Wunderbaum } from "./wunderbaum";
10
16
 
11
- export type ExtensionsDict = { [key: string]: WunderbaumExtension<any> };
17
+ export type ExtensionsDict = {
18
+ dnd: DndExtension;
19
+ edit: EditExtension;
20
+ filter: FilterExtension;
21
+ grid: GridExtension;
22
+ keynav: KeynavExtension;
23
+ logger: LoggerExtension;
24
+
25
+ [key: string]: WunderbaumExtension<any>;
26
+ };
12
27
 
13
28
  export abstract class WunderbaumExtension<TOptions> {
14
29
  public enabled = true;