wunderbaum 0.0.4 → 0.0.7
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 +2 -0
- package/dist/wunderbaum.css +1 -1
- package/dist/wunderbaum.d.ts +562 -306
- package/dist/wunderbaum.esm.js +995 -473
- package/dist/wunderbaum.esm.min.js +34 -27
- package/dist/wunderbaum.esm.min.js.map +1 -1
- package/dist/wunderbaum.umd.js +995 -473
- package/dist/wunderbaum.umd.min.js +38 -32
- package/dist/wunderbaum.umd.min.js.map +1 -1
- package/package.json +4 -4
- package/src/common.ts +50 -147
- package/src/types.ts +470 -0
- package/src/util.ts +30 -11
- package/src/wb_ext_dnd.ts +12 -163
- package/src/wb_ext_edit.ts +15 -15
- package/src/wb_ext_filter.ts +17 -5
- package/src/wb_ext_keynav.ts +140 -38
- package/src/wb_ext_logger.ts +1 -1
- package/src/wb_node.ts +224 -123
- package/src/wb_options.ts +54 -18
- package/src/wunderbaum.scss +172 -84
- package/src/wunderbaum.ts +504 -243
package/src/wb_ext_dnd.ts
CHANGED
|
@@ -8,162 +8,10 @@ import { EventCallbackType, onEvent } from "./util";
|
|
|
8
8
|
import { Wunderbaum } from "./wunderbaum";
|
|
9
9
|
import { WunderbaumExtension } from "./wb_extension_base";
|
|
10
10
|
import { WunderbaumNode } from "./wb_node";
|
|
11
|
-
import {
|
|
11
|
+
import { DropRegionType, DropRegionTypeSet } from "./types";
|
|
12
|
+
import { ROW_HEIGHT } from "./common";
|
|
12
13
|
|
|
13
14
|
const nodeMimeType = "application/x-wunderbaum-node";
|
|
14
|
-
export type DropRegionType = "over" | "before" | "after";
|
|
15
|
-
type DropRegionTypeSet = Set<DropRegionType>;
|
|
16
|
-
// type AllowedDropRegionType =
|
|
17
|
-
// | "after"
|
|
18
|
-
// | "afterBefore"
|
|
19
|
-
// // | "afterBeforeOver" // == all == true
|
|
20
|
-
// | "afterOver"
|
|
21
|
-
// | "all" // == true
|
|
22
|
-
// | "before"
|
|
23
|
-
// | "beforeOver"
|
|
24
|
-
// | "none" // == false == "" == null
|
|
25
|
-
// | "over"; // == "child"
|
|
26
|
-
|
|
27
|
-
export type DndOptionsType = {
|
|
28
|
-
/**
|
|
29
|
-
* Expand nodes after n milliseconds of hovering
|
|
30
|
-
* Default: 1500
|
|
31
|
-
*/
|
|
32
|
-
autoExpandMS: 1500;
|
|
33
|
-
// /**
|
|
34
|
-
// * Additional offset for drop-marker with hitMode = "before"/"after"
|
|
35
|
-
// * Default:
|
|
36
|
-
// */
|
|
37
|
-
// dropMarkerInsertOffsetX: -16;
|
|
38
|
-
// /**
|
|
39
|
-
// * Absolute position offset for .fancytree-drop-marker relatively to ..fancytree-title (icon/img near a node accepting drop)
|
|
40
|
-
// * Default:
|
|
41
|
-
// */
|
|
42
|
-
// dropMarkerOffsetX: -24;
|
|
43
|
-
// /**
|
|
44
|
-
// * Root Container used for drop marker (could be a shadow root)
|
|
45
|
-
// * (#1021 `document.body` is not available yet)
|
|
46
|
-
// * Default:
|
|
47
|
-
// */
|
|
48
|
-
// dropMarkerParent: "body";
|
|
49
|
-
/**
|
|
50
|
-
* true: Drag multiple (i.e. selected) nodes. Also a callback() is allowed
|
|
51
|
-
* Default: false
|
|
52
|
-
*/
|
|
53
|
-
multiSource: false;
|
|
54
|
-
/**
|
|
55
|
-
* Restrict the possible cursor shapes and modifier operations (can also be set in the dragStart event)
|
|
56
|
-
* Default: "all"
|
|
57
|
-
*/
|
|
58
|
-
effectAllowed: "all";
|
|
59
|
-
// /**
|
|
60
|
-
// * 'copy'|'link'|'move'|'auto'(calculate from `effectAllowed`+modifier keys) or callback(node, data) that returns such string.
|
|
61
|
-
// * Default:
|
|
62
|
-
// */
|
|
63
|
-
// dropEffect: "auto";
|
|
64
|
-
/**
|
|
65
|
-
* Default dropEffect ('copy', 'link', or 'move') when no modifier is pressed (overide in dragDrag, dragOver).
|
|
66
|
-
* Default: "move"
|
|
67
|
-
*/
|
|
68
|
-
dropEffectDefault: string;
|
|
69
|
-
/**
|
|
70
|
-
* Prevent dropping nodes from different Wunderbaum trees
|
|
71
|
-
* Default: false
|
|
72
|
-
*/
|
|
73
|
-
preventForeignNodes: boolean;
|
|
74
|
-
/**
|
|
75
|
-
* Prevent dropping items on unloaded lazy Wunderbaum tree nodes
|
|
76
|
-
* Default: true
|
|
77
|
-
*/
|
|
78
|
-
preventLazyParents: boolean;
|
|
79
|
-
/**
|
|
80
|
-
* Prevent dropping items other than Wunderbaum tree nodes
|
|
81
|
-
* Default: false
|
|
82
|
-
*/
|
|
83
|
-
preventNonNodes: boolean;
|
|
84
|
-
/**
|
|
85
|
-
* Prevent dropping nodes on own descendants
|
|
86
|
-
* Default: true
|
|
87
|
-
*/
|
|
88
|
-
preventRecursion: boolean;
|
|
89
|
-
/**
|
|
90
|
-
* Prevent dropping nodes under same direct parent
|
|
91
|
-
* Default: false
|
|
92
|
-
*/
|
|
93
|
-
preventSameParent: false;
|
|
94
|
-
/**
|
|
95
|
-
* Prevent dropping nodes 'before self', etc. (move only)
|
|
96
|
-
* Default: true
|
|
97
|
-
*/
|
|
98
|
-
preventVoidMoves: boolean;
|
|
99
|
-
/**
|
|
100
|
-
* Enable auto-scrolling while dragging
|
|
101
|
-
* Default: true
|
|
102
|
-
*/
|
|
103
|
-
scroll: boolean;
|
|
104
|
-
/**
|
|
105
|
-
* Active top/bottom margin in pixel
|
|
106
|
-
* Default: 20
|
|
107
|
-
*/
|
|
108
|
-
scrollSensitivity: 20;
|
|
109
|
-
/**
|
|
110
|
-
* Pixel per event
|
|
111
|
-
* Default: 5
|
|
112
|
-
*/
|
|
113
|
-
scrollSpeed: 5;
|
|
114
|
-
// /**
|
|
115
|
-
// * Allow dragging of nodes to different IE windows
|
|
116
|
-
// * Default: false
|
|
117
|
-
// */
|
|
118
|
-
// setTextTypeJson: boolean;
|
|
119
|
-
/**
|
|
120
|
-
* Optional callback passed to `toDict` on dragStart @since 2.38
|
|
121
|
-
* Default: null
|
|
122
|
-
*/
|
|
123
|
-
sourceCopyHook: null;
|
|
124
|
-
// Events (drag support)
|
|
125
|
-
/**
|
|
126
|
-
* Callback(sourceNode, data), return true, to enable dnd drag
|
|
127
|
-
* Default: null
|
|
128
|
-
*/
|
|
129
|
-
dragStart?: WbNodeEventType;
|
|
130
|
-
/**
|
|
131
|
-
* Callback(sourceNode, data)
|
|
132
|
-
* Default: null
|
|
133
|
-
*/
|
|
134
|
-
dragDrag: null;
|
|
135
|
-
/**
|
|
136
|
-
* Callback(sourceNode, data)
|
|
137
|
-
* Default: null
|
|
138
|
-
*/
|
|
139
|
-
dragEnd: null;
|
|
140
|
-
// Events (drop support)
|
|
141
|
-
/**
|
|
142
|
-
* Callback(targetNode, data), return true, to enable dnd drop
|
|
143
|
-
* Default: null
|
|
144
|
-
*/
|
|
145
|
-
dragEnter: null;
|
|
146
|
-
/**
|
|
147
|
-
* Callback(targetNode, data)
|
|
148
|
-
* Default: null
|
|
149
|
-
*/
|
|
150
|
-
dragOver: null;
|
|
151
|
-
/**
|
|
152
|
-
* Callback(targetNode, data), return false to prevent autoExpand
|
|
153
|
-
* Default: null
|
|
154
|
-
*/
|
|
155
|
-
dragExpand: null;
|
|
156
|
-
/**
|
|
157
|
-
* Callback(targetNode, data)
|
|
158
|
-
* Default: null
|
|
159
|
-
*/
|
|
160
|
-
dragDrop: null;
|
|
161
|
-
/**
|
|
162
|
-
* Callback(targetNode, data)
|
|
163
|
-
* Default: null
|
|
164
|
-
*/
|
|
165
|
-
dragLeave: null;
|
|
166
|
-
};
|
|
167
15
|
|
|
168
16
|
export class DndExtension extends WunderbaumExtension {
|
|
169
17
|
// public dropMarkerElem?: HTMLElement;
|
|
@@ -245,8 +93,9 @@ export class DndExtension extends WunderbaumExtension {
|
|
|
245
93
|
const ltn = this.lastTargetNode;
|
|
246
94
|
this.lastEnterStamp = 0;
|
|
247
95
|
if (ltn) {
|
|
248
|
-
ltn.
|
|
249
|
-
"wb-drop-target wb-drop-over wb-drop-after wb-drop-before"
|
|
96
|
+
ltn.setClass(
|
|
97
|
+
"wb-drop-target wb-drop-over wb-drop-after wb-drop-before",
|
|
98
|
+
false
|
|
250
99
|
);
|
|
251
100
|
this.lastTargetNode = null;
|
|
252
101
|
}
|
|
@@ -295,7 +144,7 @@ export class DndExtension extends WunderbaumExtension {
|
|
|
295
144
|
protected autoScroll(event: DragEvent): number {
|
|
296
145
|
let tree = this.tree,
|
|
297
146
|
dndOpts = tree.options.dnd!,
|
|
298
|
-
sp = tree.
|
|
147
|
+
sp = tree.scrollContainerElement,
|
|
299
148
|
sensitivity = dndOpts.scrollSensitivity,
|
|
300
149
|
speed = dndOpts.scrollSpeed,
|
|
301
150
|
scrolled = 0;
|
|
@@ -352,7 +201,7 @@ export class DndExtension extends WunderbaumExtension {
|
|
|
352
201
|
setTimeout(() => {
|
|
353
202
|
// Decouple this call, so the CSS is applied to the node, but not to
|
|
354
203
|
// the system generated drag image
|
|
355
|
-
srcNode.
|
|
204
|
+
srcNode.setClass("wb-drag-source");
|
|
356
205
|
}, 0);
|
|
357
206
|
|
|
358
207
|
// --- drag ---
|
|
@@ -360,7 +209,7 @@ export class DndExtension extends WunderbaumExtension {
|
|
|
360
209
|
// This event occurs very often...
|
|
361
210
|
// --- dragend ---
|
|
362
211
|
} else if (e.type === "dragend") {
|
|
363
|
-
srcNode.
|
|
212
|
+
srcNode.setClass("wb-drag-source", false);
|
|
364
213
|
this.srcNode = null;
|
|
365
214
|
if (this.lastTargetNode) {
|
|
366
215
|
this._leaveNode();
|
|
@@ -424,7 +273,7 @@ export class DndExtension extends WunderbaumExtension {
|
|
|
424
273
|
}
|
|
425
274
|
this.lastAllowedDropRegions = regionSet;
|
|
426
275
|
this.lastDropEffect = dt.dropEffect;
|
|
427
|
-
targetNode.
|
|
276
|
+
targetNode.setClass("wb-drop-target");
|
|
428
277
|
|
|
429
278
|
e.preventDefault(); // Allow drop (Drop operation is denied by default)
|
|
430
279
|
return false;
|
|
@@ -450,9 +299,9 @@ export class DndExtension extends WunderbaumExtension {
|
|
|
450
299
|
if (!region) {
|
|
451
300
|
return; // We already rejected in dragenter
|
|
452
301
|
}
|
|
453
|
-
targetNode.
|
|
454
|
-
targetNode.
|
|
455
|
-
targetNode.
|
|
302
|
+
targetNode.setClass("wb-drop-over", region === "over");
|
|
303
|
+
targetNode.setClass("wb-drop-before", region === "before");
|
|
304
|
+
targetNode.setClass("wb-drop-after", region === "after");
|
|
456
305
|
// console.log("dragover", e);
|
|
457
306
|
|
|
458
307
|
// dt.dropEffect = this.lastDropEffect!;
|
package/src/wb_ext_edit.ts
CHANGED
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
} from "./util";
|
|
18
18
|
import { debounce } from "./debounce";
|
|
19
19
|
import { WunderbaumNode } from "./wb_node";
|
|
20
|
-
import { AddNodeType
|
|
20
|
+
import { AddNodeType } from "./types";
|
|
21
21
|
import { WbNodeData } from "./wb_options";
|
|
22
22
|
|
|
23
23
|
// const START_MARKER = "\uFFF7";
|
|
@@ -63,14 +63,14 @@ export class EditExtension extends WunderbaumExtension {
|
|
|
63
63
|
|
|
64
64
|
node.log(`_applyChange(${eventName})`, extra);
|
|
65
65
|
|
|
66
|
-
colElem.classList.add("wb-
|
|
66
|
+
colElem.classList.add("wb-busy");
|
|
67
67
|
colElem.classList.remove("wb-error");
|
|
68
68
|
try {
|
|
69
69
|
res = node._callEvent(eventName, extra);
|
|
70
70
|
} catch (err) {
|
|
71
71
|
node.logError(`Error in ${eventName} event handler`, err);
|
|
72
72
|
colElem.classList.add("wb-error");
|
|
73
|
-
colElem.classList.remove("wb-
|
|
73
|
+
colElem.classList.remove("wb-busy");
|
|
74
74
|
}
|
|
75
75
|
// Convert scalar return value to a resolved promise
|
|
76
76
|
if (!(res instanceof Promise)) {
|
|
@@ -82,7 +82,7 @@ export class EditExtension extends WunderbaumExtension {
|
|
|
82
82
|
colElem.classList.add("wb-error");
|
|
83
83
|
})
|
|
84
84
|
.finally(() => {
|
|
85
|
-
colElem.classList.remove("wb-
|
|
85
|
+
colElem.classList.remove("wb-busy");
|
|
86
86
|
});
|
|
87
87
|
return res;
|
|
88
88
|
}
|
|
@@ -130,15 +130,14 @@ export class EditExtension extends WunderbaumExtension {
|
|
|
130
130
|
const eventName = eventToString(event);
|
|
131
131
|
const tree = this.tree;
|
|
132
132
|
const trigger = this.getPluginOption("trigger");
|
|
133
|
-
const inputElem =
|
|
134
|
-
|
|
135
|
-
// let handled = true;
|
|
133
|
+
// const inputElem =
|
|
134
|
+
// event.target && event.target.closest("input,[contenteditable]");
|
|
136
135
|
|
|
137
|
-
tree.logDebug(`_preprocessKeyEvent: ${eventName}`);
|
|
136
|
+
// tree.logDebug(`_preprocessKeyEvent: ${eventName}`);
|
|
138
137
|
|
|
139
138
|
// --- Title editing: apply/discard ---
|
|
140
|
-
if (inputElem) {
|
|
141
|
-
|
|
139
|
+
// if (inputElem) {
|
|
140
|
+
if (this.isEditingTitle()) {
|
|
142
141
|
switch (eventName) {
|
|
143
142
|
case "Enter":
|
|
144
143
|
this._stopEditTitle(true, { event: event });
|
|
@@ -152,7 +151,7 @@ export class EditExtension extends WunderbaumExtension {
|
|
|
152
151
|
return false;
|
|
153
152
|
}
|
|
154
153
|
// --- Trigger title editing
|
|
155
|
-
if (tree.
|
|
154
|
+
if (tree.isRowNav() || tree.activeColIdx === 0) {
|
|
156
155
|
switch (eventName) {
|
|
157
156
|
case "Enter":
|
|
158
157
|
if (trigger.indexOf("macEnter") >= 0 && isMac) {
|
|
@@ -193,8 +192,9 @@ export class EditExtension extends WunderbaumExtension {
|
|
|
193
192
|
node.logInfo("beforeEdit canceled operation.");
|
|
194
193
|
return;
|
|
195
194
|
}
|
|
196
|
-
// `beforeEdit(e)` may return an input HTML string. Otherwise use a default
|
|
197
|
-
|
|
195
|
+
// `beforeEdit(e)` may return an input HTML string. Otherwise use a default.
|
|
196
|
+
// (we also treat a `true` return value as 'use default'):
|
|
197
|
+
if (inputHtml === true || !inputHtml) {
|
|
198
198
|
const title = escapeHtml(node.title);
|
|
199
199
|
inputHtml = `<input type=text class="wb-input-edit" value="${title}" required autocorrect=off>`;
|
|
200
200
|
}
|
|
@@ -205,7 +205,7 @@ export class EditExtension extends WunderbaumExtension {
|
|
|
205
205
|
titleSpan.innerHTML = inputHtml;
|
|
206
206
|
const inputElem = titleSpan.firstElementChild as HTMLInputElement;
|
|
207
207
|
if (validity) {
|
|
208
|
-
// Permanently apply
|
|
208
|
+
// Permanently apply input validations (CSS and tooltip)
|
|
209
209
|
inputElem.addEventListener("keydown", (e) => {
|
|
210
210
|
inputElem.setCustomValidity("");
|
|
211
211
|
if (!inputElem.reportValidity()) {
|
|
@@ -336,7 +336,7 @@ export class EditExtension extends WunderbaumExtension {
|
|
|
336
336
|
return;
|
|
337
337
|
}
|
|
338
338
|
const newNode = node.addNode(init, mode);
|
|
339
|
-
newNode.
|
|
339
|
+
newNode.setClass("wb-edit-new");
|
|
340
340
|
this.relatedNode = node;
|
|
341
341
|
|
|
342
342
|
// Don't filter new nodes:
|
package/src/wb_ext_filter.ts
CHANGED
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
extend,
|
|
12
12
|
onEvent,
|
|
13
13
|
} from "./util";
|
|
14
|
-
import { NodeFilterCallback, NodeStatusType } from "./
|
|
14
|
+
import { NodeFilterCallback, NodeStatusType } from "./types";
|
|
15
15
|
import { Wunderbaum } from "./wunderbaum";
|
|
16
16
|
import { WunderbaumNode } from "./wb_node";
|
|
17
17
|
import { WunderbaumExtension } from "./wb_extension_base";
|
|
@@ -28,6 +28,7 @@ export class FilterExtension extends WunderbaumExtension {
|
|
|
28
28
|
|
|
29
29
|
constructor(tree: Wunderbaum) {
|
|
30
30
|
super(tree, "filter", {
|
|
31
|
+
connectInput: null, // Element or selector of an input control for filter query strings
|
|
31
32
|
autoApply: true, // Re-apply last filter if lazy data is loaded
|
|
32
33
|
autoExpand: false, // Expand all branches that contain matches while filtered
|
|
33
34
|
counter: true, // Show a badge with number of matching child nodes near parent icons
|
|
@@ -36,16 +37,16 @@ export class FilterExtension extends WunderbaumExtension {
|
|
|
36
37
|
hideExpanders: false, // Hide expanders if all child nodes are hidden by filter
|
|
37
38
|
highlight: true, // Highlight matches by wrapping inside <mark> tags
|
|
38
39
|
leavesOnly: false, // Match end nodes only
|
|
39
|
-
mode: "
|
|
40
|
+
mode: "dim", // Grayout unmatched nodes (pass "hide" to remove unmatched node instead)
|
|
40
41
|
noData: true, // Display a 'no data' status node if result is empty
|
|
41
42
|
});
|
|
42
43
|
}
|
|
43
44
|
|
|
44
45
|
init() {
|
|
45
46
|
super.init();
|
|
46
|
-
|
|
47
|
-
if (
|
|
48
|
-
this.queryInput = elemFromSelector(
|
|
47
|
+
const connectInput = this.getPluginOption("connectInput");
|
|
48
|
+
if (connectInput) {
|
|
49
|
+
this.queryInput = elemFromSelector(connectInput) as HTMLInputElement;
|
|
49
50
|
onEvent(
|
|
50
51
|
this.queryInput,
|
|
51
52
|
"input",
|
|
@@ -57,6 +58,17 @@ export class FilterExtension extends WunderbaumExtension {
|
|
|
57
58
|
}
|
|
58
59
|
}
|
|
59
60
|
|
|
61
|
+
setPluginOption(name: string, value: any): void {
|
|
62
|
+
// alert("filter opt=" + name + ", " + value)
|
|
63
|
+
super.setPluginOption(name, value);
|
|
64
|
+
switch (name) {
|
|
65
|
+
case "mode":
|
|
66
|
+
this.tree.filterMode = value === "hide" ? "hide" : "dim";
|
|
67
|
+
this.tree.updateFilter();
|
|
68
|
+
break;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
60
72
|
_applyFilterNoUpdate(
|
|
61
73
|
filter: string | NodeFilterCallback,
|
|
62
74
|
branchMode: boolean,
|
package/src/wb_ext_keynav.ts
CHANGED
|
@@ -4,29 +4,62 @@
|
|
|
4
4
|
* @VERSION, @DATE (https://github.com/mar10/wunderbaum)
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import {
|
|
7
|
+
import { NavigationOptions } from "./types";
|
|
8
8
|
import { eventToString } from "./util";
|
|
9
9
|
import { Wunderbaum } from "./wunderbaum";
|
|
10
10
|
import { WunderbaumNode } from "./wb_node";
|
|
11
11
|
import { WunderbaumExtension } from "./wb_extension_base";
|
|
12
12
|
|
|
13
|
+
const QUICKSEARCH_DELAY = 500;
|
|
14
|
+
|
|
13
15
|
export class KeynavExtension extends WunderbaumExtension {
|
|
14
16
|
constructor(tree: Wunderbaum) {
|
|
15
17
|
super(tree, "keynav", {});
|
|
16
18
|
}
|
|
17
19
|
|
|
20
|
+
protected _getEmbeddedInputElem(elem: any): HTMLInputElement | null {
|
|
21
|
+
let input = null;
|
|
22
|
+
|
|
23
|
+
if (elem && elem.type != null) {
|
|
24
|
+
input = elem;
|
|
25
|
+
} else {
|
|
26
|
+
// ,[contenteditable]
|
|
27
|
+
const ace = this.tree.getActiveColElem()?.querySelector("input,select");
|
|
28
|
+
if (ace) {
|
|
29
|
+
input = ace as HTMLInputElement;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return input;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/* Return true if the current cell's embedded input has keyboard focus. */
|
|
36
|
+
protected _isCurInputFocused(): boolean {
|
|
37
|
+
const ace = this.tree
|
|
38
|
+
.getActiveColElem()
|
|
39
|
+
?.querySelector("input:focus,select:focus");
|
|
40
|
+
console.log(`_isCurInputFocused`, ace);
|
|
41
|
+
return !!ace;
|
|
42
|
+
}
|
|
43
|
+
|
|
18
44
|
onKeyEvent(data: any): boolean | undefined {
|
|
19
|
-
|
|
20
|
-
eventName = eventToString(event),
|
|
21
|
-
focusNode,
|
|
22
|
-
node = data.node as WunderbaumNode,
|
|
45
|
+
const event = data.event,
|
|
23
46
|
tree = this.tree,
|
|
24
47
|
opts = data.options,
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
48
|
+
activate = !event.ctrlKey || opts.autoActivate,
|
|
49
|
+
curInput = this._getEmbeddedInputElem(event.target),
|
|
50
|
+
navModeOption = opts.navigationModeOption as NavigationOptions;
|
|
51
|
+
// isCellEditMode = tree.navMode === NavigationMode.cellEdit;
|
|
52
|
+
|
|
53
|
+
let focusNode,
|
|
54
|
+
eventName = eventToString(event),
|
|
55
|
+
node = data.node as WunderbaumNode,
|
|
56
|
+
handled = true;
|
|
28
57
|
|
|
29
|
-
tree.
|
|
58
|
+
// tree.log(`onKeyEvent: ${eventName}, curInput`, curInput);
|
|
59
|
+
if (!tree.isEnabled()) {
|
|
60
|
+
// tree.logDebug(`onKeyEvent ignored for disabled tree: ${eventName}`);
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
30
63
|
|
|
31
64
|
// Let callback prevent default processing
|
|
32
65
|
if (tree._callEvent("keydown", data) === false) {
|
|
@@ -40,34 +73,38 @@ export class KeynavExtension extends WunderbaumExtension {
|
|
|
40
73
|
|
|
41
74
|
// Set focus to active (or first node) if no other node has the focus yet
|
|
42
75
|
if (!node) {
|
|
43
|
-
const
|
|
76
|
+
const currentNode = tree.getFocusNode() || tree.getActiveNode();
|
|
44
77
|
const firstNode = tree.getFirstChild();
|
|
45
78
|
|
|
46
|
-
if (!
|
|
79
|
+
if (!currentNode && firstNode && eventName === "ArrowDown") {
|
|
47
80
|
firstNode.logInfo("Keydown: activate first node.");
|
|
48
81
|
firstNode.setActive();
|
|
49
82
|
return;
|
|
50
83
|
}
|
|
51
84
|
|
|
52
|
-
focusNode =
|
|
85
|
+
focusNode = currentNode || firstNode;
|
|
53
86
|
if (focusNode) {
|
|
54
87
|
focusNode.setFocus();
|
|
55
88
|
node = tree.getFocusNode()!;
|
|
56
89
|
node.logInfo("Keydown: force focus on active node.");
|
|
57
90
|
}
|
|
58
91
|
}
|
|
92
|
+
const isColspan = node.isColspan();
|
|
59
93
|
|
|
60
|
-
if (tree.
|
|
94
|
+
if (tree.isRowNav()) {
|
|
95
|
+
// -----------------------------------------------------------------------
|
|
96
|
+
// --- Row Mode ---
|
|
97
|
+
// -----------------------------------------------------------------------
|
|
61
98
|
// --- Quick-Search
|
|
62
99
|
if (
|
|
63
100
|
opts.quicksearch &&
|
|
64
101
|
eventName.length === 1 &&
|
|
65
|
-
/^\w$/.test(eventName)
|
|
66
|
-
|
|
102
|
+
/^\w$/.test(eventName) &&
|
|
103
|
+
!curInput
|
|
67
104
|
) {
|
|
68
105
|
// Allow to search for longer streaks if typed in quickly
|
|
69
106
|
const stamp = Date.now();
|
|
70
|
-
if (stamp - tree.lastQuicksearchTime >
|
|
107
|
+
if (stamp - tree.lastQuicksearchTime > QUICKSEARCH_DELAY) {
|
|
71
108
|
tree.lastQuicksearchTerm = "";
|
|
72
109
|
}
|
|
73
110
|
tree.lastQuicksearchTime = stamp;
|
|
@@ -93,8 +130,8 @@ export class KeynavExtension extends WunderbaumExtension {
|
|
|
93
130
|
case "ArrowRight":
|
|
94
131
|
if (!node.expanded && (node.children || node.lazy)) {
|
|
95
132
|
eventName = "Add"; // expand
|
|
96
|
-
} else if (navModeOption ===
|
|
97
|
-
tree.
|
|
133
|
+
} else if (navModeOption === NavigationOptions.startRow) {
|
|
134
|
+
tree.setCellNav();
|
|
98
135
|
return;
|
|
99
136
|
}
|
|
100
137
|
break;
|
|
@@ -143,50 +180,111 @@ export class KeynavExtension extends WunderbaumExtension {
|
|
|
143
180
|
handled = false;
|
|
144
181
|
}
|
|
145
182
|
} else {
|
|
146
|
-
//
|
|
183
|
+
// -----------------------------------------------------------------------
|
|
184
|
+
// --- Cell Mode ---
|
|
185
|
+
// -----------------------------------------------------------------------
|
|
186
|
+
// // Standard navigation (cell mode)
|
|
187
|
+
// if (isCellEditMode && INPUT_BREAKOUT_KEYS.has(eventName)) {
|
|
188
|
+
// }
|
|
189
|
+
const ENTER_EDITS = false;
|
|
190
|
+
const curInput = this._getEmbeddedInputElem(null);
|
|
191
|
+
const curInputType = curInput ? curInput.type || curInput.tagName : "";
|
|
192
|
+
const inputHasFocus = curInput && this._isCurInputFocused();
|
|
193
|
+
const inputCanFocus = curInput && curInputType !== "checkbox";
|
|
194
|
+
|
|
195
|
+
if (inputHasFocus) {
|
|
196
|
+
if (eventName === "Escape") {
|
|
197
|
+
// Discard changes
|
|
198
|
+
node.render();
|
|
199
|
+
// } else if (!INPUT_BREAKOUT_KEYS.has(eventName)) {
|
|
200
|
+
} else if (eventName !== "Enter") {
|
|
201
|
+
// Let current `<input>` handle it
|
|
202
|
+
node.logDebug(`Ignored ${eventName} inside focused input`);
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
// const curInputType = curInput.type || curInput.tagName;
|
|
206
|
+
// const breakoutKeys = INPUT_KEYS[curInputType];
|
|
207
|
+
// if (!breakoutKeys.includes(eventName)) {
|
|
208
|
+
// node.logDebug(`Ignored ${eventName} inside ${curInputType} input`);
|
|
209
|
+
// return;
|
|
210
|
+
// }
|
|
211
|
+
} else if (curInput) {
|
|
212
|
+
// On a cell that has an embedded, unfocused <input>
|
|
213
|
+
if (eventName.length === 1 && inputCanFocus) {
|
|
214
|
+
curInput.focus();
|
|
215
|
+
curInput.value = "";
|
|
216
|
+
node.logDebug(`Focus imput: ${eventName}`);
|
|
217
|
+
return false;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
if (eventName === "Tab") {
|
|
221
|
+
eventName = "ArrowRight";
|
|
222
|
+
handled = true;
|
|
223
|
+
} else if (eventName === "Shift+Tab") {
|
|
224
|
+
eventName = tree.activeColIdx > 0 ? "ArrowLeft" : "";
|
|
225
|
+
handled = true;
|
|
226
|
+
} else if (ENTER_EDITS && eventName === "Enter") {
|
|
227
|
+
eventName = "F2";
|
|
228
|
+
}
|
|
229
|
+
|
|
147
230
|
switch (eventName) {
|
|
148
231
|
case " ":
|
|
149
232
|
if (tree.activeColIdx === 0 && node.getOption("checkbox")) {
|
|
150
233
|
node.setSelected(!node.isSelected());
|
|
151
234
|
handled = true;
|
|
152
|
-
} else {
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
235
|
+
} else if (curInput && curInputType === "checkbox") {
|
|
236
|
+
curInput.click();
|
|
237
|
+
// toggleCheckbox(curInput)
|
|
238
|
+
// new Event("change")
|
|
239
|
+
// curInput.change
|
|
240
|
+
handled = true;
|
|
241
|
+
}
|
|
242
|
+
break;
|
|
243
|
+
case "F2":
|
|
244
|
+
if (curInput && !inputHasFocus && inputCanFocus) {
|
|
245
|
+
curInput.focus();
|
|
246
|
+
handled = true;
|
|
159
247
|
}
|
|
160
248
|
break;
|
|
161
249
|
case "Enter":
|
|
250
|
+
tree.setFocus(); // Blur prev. input if any
|
|
162
251
|
if (tree.activeColIdx === 0 && node.isExpandable()) {
|
|
163
252
|
node.setExpanded(!node.isExpanded());
|
|
164
253
|
handled = true;
|
|
254
|
+
} else if (curInput && !inputHasFocus && inputCanFocus) {
|
|
255
|
+
curInput.focus();
|
|
256
|
+
handled = true;
|
|
165
257
|
}
|
|
166
258
|
break;
|
|
167
259
|
case "Escape":
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
} else if (tree.navMode === NavigationMode.cellNav) {
|
|
172
|
-
tree.setNavigationMode(NavigationMode.row);
|
|
260
|
+
tree.setFocus(); // Blur prev. input if any
|
|
261
|
+
if (tree.isCellNav() && navModeOption !== NavigationOptions.cell) {
|
|
262
|
+
tree.setCellNav(false); // row-nav mode
|
|
173
263
|
handled = true;
|
|
174
264
|
}
|
|
175
265
|
break;
|
|
176
266
|
case "ArrowLeft":
|
|
177
|
-
|
|
267
|
+
tree.setFocus(); // Blur prev. input if any
|
|
268
|
+
if (isColspan && node.isExpanded()) {
|
|
269
|
+
node.setExpanded(false);
|
|
270
|
+
} else if (!isColspan && tree.activeColIdx > 0) {
|
|
178
271
|
tree.setColumn(tree.activeColIdx - 1);
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
tree.setNavigationMode(NavigationMode.row);
|
|
182
|
-
handled = true;
|
|
272
|
+
} else if (navModeOption !== NavigationOptions.cell) {
|
|
273
|
+
tree.setCellNav(false); // row-nav mode
|
|
183
274
|
}
|
|
275
|
+
handled = true;
|
|
184
276
|
break;
|
|
185
277
|
case "ArrowRight":
|
|
186
|
-
|
|
278
|
+
tree.setFocus(); // Blur prev. input if any
|
|
279
|
+
if (isColspan && !node.isExpanded()) {
|
|
280
|
+
node.setExpanded();
|
|
281
|
+
} else if (
|
|
282
|
+
!isColspan &&
|
|
283
|
+
tree.activeColIdx < tree.columns.length - 1
|
|
284
|
+
) {
|
|
187
285
|
tree.setColumn(tree.activeColIdx + 1);
|
|
188
|
-
handled = true;
|
|
189
286
|
}
|
|
287
|
+
handled = true;
|
|
190
288
|
break;
|
|
191
289
|
case "ArrowDown":
|
|
192
290
|
case "ArrowUp":
|
|
@@ -200,6 +298,10 @@ export class KeynavExtension extends WunderbaumExtension {
|
|
|
200
298
|
case "PageDown":
|
|
201
299
|
case "PageUp":
|
|
202
300
|
node.navigate(eventName, { activate: activate, event: event });
|
|
301
|
+
// if (isCellEditMode) {
|
|
302
|
+
// this._getEmbeddedInputElem(null, true); // set focus to input
|
|
303
|
+
// }
|
|
304
|
+
handled = true;
|
|
203
305
|
break;
|
|
204
306
|
default:
|
|
205
307
|
handled = false;
|
package/src/wb_ext_logger.ts
CHANGED
|
@@ -35,7 +35,7 @@ export class LoggerExtension extends WunderbaumExtension {
|
|
|
35
35
|
if (ignoreEvents.has(name)) {
|
|
36
36
|
return (<any>tree)._superApply(arguments);
|
|
37
37
|
}
|
|
38
|
-
|
|
38
|
+
const start = Date.now();
|
|
39
39
|
const res = (<any>tree)._superApply(arguments);
|
|
40
40
|
console.debug(
|
|
41
41
|
`${prefix}: callEvent('${name}') took ${Date.now() - start} ms.`,
|