wunderbaum 0.0.1-0 → 0.0.3
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 +6 -5
- package/dist/wunderbaum.css +1 -1
- package/dist/wunderbaum.d.ts +634 -171
- package/dist/wunderbaum.esm.js +818 -436
- package/dist/wunderbaum.esm.min.js +31 -21
- package/dist/wunderbaum.esm.min.js.map +1 -1
- package/dist/wunderbaum.umd.js +820 -438
- package/dist/wunderbaum.umd.min.js +34 -24
- package/dist/wunderbaum.umd.min.js.map +1 -1
- package/package.json +35 -32
- package/src/common.ts +37 -5
- package/src/drag_observer.ts +169 -0
- package/src/util.ts +48 -13
- package/src/wb_ext_dnd.ts +145 -4
- package/src/wb_ext_edit.ts +10 -1
- package/src/wb_ext_filter.ts +35 -40
- package/src/wb_ext_grid.ts +45 -0
- package/src/wb_ext_keynav.ts +8 -4
- package/src/wb_node.ts +142 -78
- package/src/wb_options.ts +138 -25
- package/src/wunderbaum.scss +28 -5
- package/src/wunderbaum.ts +481 -321
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Wunderbaum - drag_observer
|
|
3
|
+
* Copyright (c) 2021-2022, Martin Wendt. Released under the MIT license.
|
|
4
|
+
* @VERSION, @DATE (https://github.com/mar10/wunderbaum)
|
|
5
|
+
*/
|
|
6
|
+
import * as util from "./util";
|
|
7
|
+
|
|
8
|
+
export type DragCallbackArgType = {
|
|
9
|
+
/** "dragstart", "drag", or "dragstop". */
|
|
10
|
+
type: string;
|
|
11
|
+
/** Original mouse or touch event that triggered the drag event. */
|
|
12
|
+
event: MouseEvent | TouchEvent;
|
|
13
|
+
/** Element which is currently dragged. */
|
|
14
|
+
dragElem: HTMLElement | null;
|
|
15
|
+
/** Relative horizontal drag distance since start. */
|
|
16
|
+
dx: number;
|
|
17
|
+
/** Relative vertical drag distance since start. */
|
|
18
|
+
dy: number;
|
|
19
|
+
/** False if drag was canceled. */
|
|
20
|
+
apply?: boolean;
|
|
21
|
+
};
|
|
22
|
+
export type DragCallbackType = (e: DragCallbackArgType) => boolean | void;
|
|
23
|
+
type DragObserverOptionsType = {
|
|
24
|
+
/**Event target (typically `window.document`). */
|
|
25
|
+
root: EventTarget;
|
|
26
|
+
/**Event delegation selector.*/
|
|
27
|
+
selector?: string;
|
|
28
|
+
/**Minimum drag distance in px. */
|
|
29
|
+
thresh?: number;
|
|
30
|
+
/**Return `false` to cancel drag. */
|
|
31
|
+
dragstart: DragCallbackType;
|
|
32
|
+
drag?: DragCallbackType;
|
|
33
|
+
dragstop?: DragCallbackType;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Convert mouse- and touch events to 'dragstart', 'drag', and 'dragstop'.
|
|
38
|
+
*/
|
|
39
|
+
export class DragObserver {
|
|
40
|
+
protected _handler;
|
|
41
|
+
protected root: EventTarget;
|
|
42
|
+
protected start = {
|
|
43
|
+
x: 0,
|
|
44
|
+
y: 0,
|
|
45
|
+
altKey: false,
|
|
46
|
+
ctrlKey: false,
|
|
47
|
+
metaKey: false,
|
|
48
|
+
shiftKey: false,
|
|
49
|
+
};
|
|
50
|
+
protected dragElem: HTMLElement | null = null;
|
|
51
|
+
protected dragging: boolean = false;
|
|
52
|
+
// TODO: touch events
|
|
53
|
+
protected events = ["mousedown", "mouseup", "mousemove", "keydown"];
|
|
54
|
+
protected opts: DragObserverOptionsType;
|
|
55
|
+
|
|
56
|
+
constructor(opts: DragObserverOptionsType) {
|
|
57
|
+
util.assert(opts.root);
|
|
58
|
+
this.opts = util.extend({ thresh: 5 }, opts);
|
|
59
|
+
this.root = opts.root;
|
|
60
|
+
this._handler = this.handleEvent.bind(this) as EventListener;
|
|
61
|
+
this.events.forEach((type) => {
|
|
62
|
+
this.root.addEventListener(type, this._handler);
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
/** Unregister all event listeners. */
|
|
66
|
+
disconnect() {
|
|
67
|
+
this.events.forEach((type) => {
|
|
68
|
+
this.root.removeEventListener(type, this._handler);
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
public getDragElem(): HTMLElement | null {
|
|
73
|
+
return this.dragElem;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
public isDragging(): boolean {
|
|
77
|
+
return this.dragging;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
public stopDrag(cb_event?: DragCallbackArgType): void {
|
|
81
|
+
if (this.dragging && this.opts.dragstop && cb_event) {
|
|
82
|
+
cb_event.type = "dragstop";
|
|
83
|
+
this.opts.dragstop(cb_event);
|
|
84
|
+
}
|
|
85
|
+
this.dragElem = null;
|
|
86
|
+
this.dragging = false;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
protected handleEvent(e: MouseEvent): boolean | void {
|
|
90
|
+
const type = e.type;
|
|
91
|
+
const opts = this.opts;
|
|
92
|
+
const cb_event: DragCallbackArgType = {
|
|
93
|
+
type: e.type,
|
|
94
|
+
event: e,
|
|
95
|
+
dragElem: this.dragElem,
|
|
96
|
+
dx: e.pageX - this.start.x,
|
|
97
|
+
dy: e.pageY - this.start.y,
|
|
98
|
+
apply: undefined,
|
|
99
|
+
};
|
|
100
|
+
switch (type) {
|
|
101
|
+
case "keydown":
|
|
102
|
+
this.stopDrag(cb_event);
|
|
103
|
+
break;
|
|
104
|
+
case "mousedown":
|
|
105
|
+
if (this.dragElem) {
|
|
106
|
+
this.stopDrag(cb_event);
|
|
107
|
+
break;
|
|
108
|
+
}
|
|
109
|
+
if (opts.selector) {
|
|
110
|
+
let elem = e.target as HTMLElement;
|
|
111
|
+
if (elem.matches(opts.selector)) {
|
|
112
|
+
this.dragElem = elem;
|
|
113
|
+
} else {
|
|
114
|
+
elem = elem.closest(opts.selector) as HTMLElement;
|
|
115
|
+
if (elem) {
|
|
116
|
+
this.dragElem = elem;
|
|
117
|
+
} else {
|
|
118
|
+
break; // no event delegation selector matched
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
this.start.x = e.pageX;
|
|
123
|
+
this.start.y = e.pageY;
|
|
124
|
+
this.start.altKey = e.altKey;
|
|
125
|
+
this.start.ctrlKey = e.ctrlKey;
|
|
126
|
+
this.start.metaKey = e.metaKey;
|
|
127
|
+
this.start.shiftKey = e.shiftKey;
|
|
128
|
+
break;
|
|
129
|
+
|
|
130
|
+
case "mousemove":
|
|
131
|
+
// TODO: debounce/throttle?
|
|
132
|
+
// TODO: horizontal mode: ignore if dx unchanged
|
|
133
|
+
if (!this.dragElem) {
|
|
134
|
+
break;
|
|
135
|
+
}
|
|
136
|
+
if (!this.dragging) {
|
|
137
|
+
if (opts.thresh) {
|
|
138
|
+
const dist2 = cb_event.dx * cb_event.dx + cb_event.dy * cb_event.dy;
|
|
139
|
+
if (dist2 < opts.thresh * opts.thresh) {
|
|
140
|
+
break;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
cb_event.type = "dragstart";
|
|
144
|
+
if (opts.dragstart(cb_event) === false) {
|
|
145
|
+
this.stopDrag(cb_event);
|
|
146
|
+
break;
|
|
147
|
+
}
|
|
148
|
+
this.dragging = true;
|
|
149
|
+
}
|
|
150
|
+
if (this.dragging && this.opts.drag) {
|
|
151
|
+
cb_event.type = "drag";
|
|
152
|
+
this.opts.drag(cb_event);
|
|
153
|
+
}
|
|
154
|
+
break;
|
|
155
|
+
case "mouseup":
|
|
156
|
+
if (!this.dragging) {
|
|
157
|
+
this.stopDrag(cb_event);
|
|
158
|
+
break;
|
|
159
|
+
}
|
|
160
|
+
if (e.button === 0) {
|
|
161
|
+
cb_event.apply = true;
|
|
162
|
+
} else {
|
|
163
|
+
cb_event.apply = false;
|
|
164
|
+
}
|
|
165
|
+
this.stopDrag(cb_event);
|
|
166
|
+
break;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
package/src/util.ts
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* Copyright (c) 2021-2022, Martin Wendt. Released under the MIT license.
|
|
4
4
|
* @VERSION, @DATE (https://github.com/mar10/wunderbaum)
|
|
5
5
|
*/
|
|
6
|
+
/** @module util */
|
|
6
7
|
|
|
7
8
|
/** Readable names for `MouseEvent.button` */
|
|
8
9
|
export const MOUSE_BUTTONS: { [key: number]: string } = {
|
|
@@ -16,7 +17,7 @@ export const MOUSE_BUTTONS: { [key: number]: string } = {
|
|
|
16
17
|
|
|
17
18
|
export const MAX_INT = 9007199254740991;
|
|
18
19
|
const userInfo = _getUserInfo();
|
|
19
|
-
/**True if the
|
|
20
|
+
/**True if the client is using a macOS platform. */
|
|
20
21
|
export const isMac = userInfo.isMac;
|
|
21
22
|
|
|
22
23
|
const REX_HTML = /[&<>"'/]/g; // Escape those characters
|
|
@@ -186,7 +187,7 @@ export function escapeTooltip(s: string): string {
|
|
|
186
187
|
/** TODO */
|
|
187
188
|
export function extractHtmlText(s: string) {
|
|
188
189
|
if (s.indexOf(">") >= 0) {
|
|
189
|
-
error("
|
|
190
|
+
error("Not implemented");
|
|
190
191
|
// return $("<div/>").html(s).text();
|
|
191
192
|
}
|
|
192
193
|
return s;
|
|
@@ -315,7 +316,7 @@ export function setValueToElem(elem: HTMLElement, value: any): void {
|
|
|
315
316
|
input.valueAsNumber = value;
|
|
316
317
|
break;
|
|
317
318
|
case "radio":
|
|
318
|
-
|
|
319
|
+
error("Not implemented");
|
|
319
320
|
// const name = input.name;
|
|
320
321
|
// const checked = input.parentElement!.querySelector(
|
|
321
322
|
// `input[name="${name}"]:checked`
|
|
@@ -338,7 +339,7 @@ export function setValueToElem(elem: HTMLElement, value: any): void {
|
|
|
338
339
|
// return value;
|
|
339
340
|
}
|
|
340
341
|
|
|
341
|
-
/**
|
|
342
|
+
/** Create and return an unconnected `HTMLElement` from a HTML string. */
|
|
342
343
|
export function elemFromHtml(html: string): HTMLElement {
|
|
343
344
|
const t = document.createElement("template");
|
|
344
345
|
t.innerHTML = html.trim();
|
|
@@ -358,11 +359,26 @@ export function elemFromSelector(obj: string | Element): HTMLElement | null {
|
|
|
358
359
|
return obj as HTMLElement;
|
|
359
360
|
}
|
|
360
361
|
|
|
362
|
+
/** Return a EventTarget from selector or cast an existing element. */
|
|
363
|
+
export function eventTargetFromSelector(
|
|
364
|
+
obj: string | EventTarget
|
|
365
|
+
): EventTarget | null {
|
|
366
|
+
if (!obj) {
|
|
367
|
+
return null;
|
|
368
|
+
}
|
|
369
|
+
if (typeof obj === "string") {
|
|
370
|
+
return document.querySelector(obj) as EventTarget;
|
|
371
|
+
}
|
|
372
|
+
return obj as EventTarget;
|
|
373
|
+
}
|
|
374
|
+
|
|
361
375
|
/**
|
|
362
|
-
* Return a descriptive string for a keyboard or mouse event.
|
|
376
|
+
* Return a canonical descriptive string for a keyboard or mouse event.
|
|
363
377
|
*
|
|
364
378
|
* The result also contains a prefix for modifiers if any, for example
|
|
365
379
|
* `"x"`, `"F2"`, `"Control+Home"`, or `"Shift+clickright"`.
|
|
380
|
+
* This is especially useful in `switch` statements, to make sure that modifier
|
|
381
|
+
* keys are considered and handled correctly.
|
|
366
382
|
*/
|
|
367
383
|
export function eventToString(event: Event): string {
|
|
368
384
|
let key = (<KeyboardEvent>event).key,
|
|
@@ -397,6 +413,13 @@ export function eventToString(event: Event): string {
|
|
|
397
413
|
return s.join("+");
|
|
398
414
|
}
|
|
399
415
|
|
|
416
|
+
/**
|
|
417
|
+
* Copy allproperties from one or more source objects to a target object.
|
|
418
|
+
*
|
|
419
|
+
* @returns the modified target object.
|
|
420
|
+
*/
|
|
421
|
+
// TODO: use Object.assign()? --> https://stackoverflow.com/a/42740894
|
|
422
|
+
// TODO: support deep merge --> https://stackoverflow.com/a/42740894
|
|
400
423
|
export function extend(...args: any[]) {
|
|
401
424
|
for (let i = 1; i < args.length; i++) {
|
|
402
425
|
let arg = args[i];
|
|
@@ -412,18 +435,22 @@ export function extend(...args: any[]) {
|
|
|
412
435
|
return args[0];
|
|
413
436
|
}
|
|
414
437
|
|
|
438
|
+
/** Return true if `obj` is of type `array`. */
|
|
415
439
|
export function isArray(obj: any) {
|
|
416
440
|
return Array.isArray(obj);
|
|
417
441
|
}
|
|
418
442
|
|
|
443
|
+
/** Return true if `obj` is of type `Object` and has no propertied. */
|
|
419
444
|
export function isEmptyObject(obj: any) {
|
|
420
445
|
return Object.keys(obj).length === 0 && obj.constructor === Object;
|
|
421
446
|
}
|
|
422
447
|
|
|
448
|
+
/** Return true if `obj` is of type `function`. */
|
|
423
449
|
export function isFunction(obj: any) {
|
|
424
450
|
return typeof obj === "function";
|
|
425
451
|
}
|
|
426
452
|
|
|
453
|
+
/** Return true if `obj` is of type `Object`. */
|
|
427
454
|
export function isPlainObject(obj: any) {
|
|
428
455
|
return Object.prototype.toString.call(obj) === "[object Object]";
|
|
429
456
|
}
|
|
@@ -432,14 +459,14 @@ export function isPlainObject(obj: any) {
|
|
|
432
459
|
export function noop(...args: any[]): any {}
|
|
433
460
|
|
|
434
461
|
/**
|
|
435
|
-
* Bind one or more event handlers directly to an [[
|
|
462
|
+
* Bind one or more event handlers directly to an [[EventTarget]].
|
|
436
463
|
*
|
|
437
|
-
* @param element
|
|
464
|
+
* @param element EventTarget or selector
|
|
438
465
|
* @param eventNames
|
|
439
466
|
* @param handler
|
|
440
467
|
*/
|
|
441
468
|
export function onEvent(
|
|
442
|
-
|
|
469
|
+
rootTarget: EventTarget | string,
|
|
443
470
|
eventNames: string,
|
|
444
471
|
handler: EventCallbackType
|
|
445
472
|
): void;
|
|
@@ -455,26 +482,26 @@ export function onEvent(
|
|
|
455
482
|
* });
|
|
456
483
|
* ```
|
|
457
484
|
*
|
|
458
|
-
* @param element
|
|
485
|
+
* @param element EventTarget or selector
|
|
459
486
|
* @param eventNames
|
|
460
487
|
* @param selector
|
|
461
488
|
* @param handler
|
|
462
489
|
*/
|
|
463
490
|
export function onEvent(
|
|
464
|
-
|
|
491
|
+
rootTarget: EventTarget | string,
|
|
465
492
|
eventNames: string,
|
|
466
493
|
selector: string,
|
|
467
494
|
handler: EventCallbackType
|
|
468
495
|
): void;
|
|
469
496
|
|
|
470
497
|
export function onEvent(
|
|
471
|
-
|
|
498
|
+
rootTarget: EventTarget | string,
|
|
472
499
|
eventNames: string,
|
|
473
500
|
selectorOrHandler: string | EventCallbackType,
|
|
474
501
|
handlerOrNone?: EventCallbackType
|
|
475
502
|
): void {
|
|
476
503
|
let selector: string | null, handler: EventCallbackType;
|
|
477
|
-
|
|
504
|
+
rootTarget = eventTargetFromSelector(rootTarget)!;
|
|
478
505
|
|
|
479
506
|
if (handlerOrNone) {
|
|
480
507
|
selector = selectorOrHandler as string;
|
|
@@ -485,7 +512,7 @@ export function onEvent(
|
|
|
485
512
|
}
|
|
486
513
|
|
|
487
514
|
eventNames.split(" ").forEach((evn) => {
|
|
488
|
-
(<
|
|
515
|
+
(<EventTarget>rootTarget).addEventListener(evn, function (e) {
|
|
489
516
|
if (!selector) {
|
|
490
517
|
return handler!(e); // no event delegation
|
|
491
518
|
} else if (e.target) {
|
|
@@ -578,6 +605,13 @@ export async function sleep(ms: number) {
|
|
|
578
605
|
|
|
579
606
|
/**
|
|
580
607
|
* Set or rotate checkbox status with support for tri-state.
|
|
608
|
+
*
|
|
609
|
+
* An initial 'indeterminate' state becomes 'checked' on the first call.
|
|
610
|
+
*
|
|
611
|
+
* If the input element has the class 'wb-tristate' assigned, the sequence is:<br>
|
|
612
|
+
* 'indeterminate' -> 'checked' -> 'unchecked' -> 'indeterminate' -> ...<br>
|
|
613
|
+
* Otherwise we toggle like <br>
|
|
614
|
+
* 'checked' -> 'unchecked' -> 'checked' -> ...
|
|
581
615
|
*/
|
|
582
616
|
export function toggleCheckbox(
|
|
583
617
|
element: HTMLElement | string,
|
|
@@ -648,6 +682,7 @@ export function toSet(val: any): Set<string> {
|
|
|
648
682
|
throw new Error("Cannot convert to Set<string>: " + val);
|
|
649
683
|
}
|
|
650
684
|
|
|
685
|
+
/**Return a canonical string representation for an object's type (e.g. 'array', 'number', ...) */
|
|
651
686
|
export function type(obj: any): string {
|
|
652
687
|
return Object.prototype.toString
|
|
653
688
|
.call(obj)
|
package/src/wb_ext_dnd.ts
CHANGED
|
@@ -8,9 +8,9 @@ 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 { ROW_HEIGHT } from "./common";
|
|
11
|
+
import { ROW_HEIGHT, WbNodeEventType } from "./common";
|
|
12
12
|
|
|
13
|
-
const nodeMimeType = "application/x-
|
|
13
|
+
const nodeMimeType = "application/x-wunderbaum-node";
|
|
14
14
|
export type DropRegionType = "over" | "before" | "after";
|
|
15
15
|
type DropRegionTypeSet = Set<DropRegionType>;
|
|
16
16
|
// type AllowedDropRegionType =
|
|
@@ -24,6 +24,147 @@ type DropRegionTypeSet = Set<DropRegionType>;
|
|
|
24
24
|
// | "none" // == false == "" == null
|
|
25
25
|
// | "over"; // == "child"
|
|
26
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
|
+
|
|
27
168
|
export class DndExtension extends WunderbaumExtension {
|
|
28
169
|
// public dropMarkerElem?: HTMLElement;
|
|
29
170
|
protected srcNode: WunderbaumNode | null = null;
|
|
@@ -78,7 +219,7 @@ export class DndExtension extends WunderbaumExtension {
|
|
|
78
219
|
// this.$scrollParent = $temp.scrollParent();
|
|
79
220
|
// $temp.remove();
|
|
80
221
|
const tree = this.tree;
|
|
81
|
-
const dndOpts = tree.options.dnd
|
|
222
|
+
const dndOpts = tree.options.dnd!;
|
|
82
223
|
|
|
83
224
|
// Enable drag support if dragStart() is specified:
|
|
84
225
|
if (dndOpts.dragStart) {
|
|
@@ -153,7 +294,7 @@ export class DndExtension extends WunderbaumExtension {
|
|
|
153
294
|
/* Implement auto scrolling when drag cursor is in top/bottom area of scroll parent. */
|
|
154
295
|
protected autoScroll(event: DragEvent): number {
|
|
155
296
|
let tree = this.tree,
|
|
156
|
-
dndOpts = tree.options.dnd
|
|
297
|
+
dndOpts = tree.options.dnd!,
|
|
157
298
|
sp = tree.scrollContainer,
|
|
158
299
|
sensitivity = dndOpts.scrollSensitivity,
|
|
159
300
|
speed = dndOpts.scrollSpeed,
|
package/src/wb_ext_edit.ts
CHANGED
|
@@ -162,7 +162,7 @@ export class EditExtension extends WunderbaumExtension {
|
|
|
162
162
|
break;
|
|
163
163
|
case "F2":
|
|
164
164
|
if (trigger.indexOf("F2") >= 0) {
|
|
165
|
-
// tree.
|
|
165
|
+
// tree.setNavigationMode(NavigationMode.cellEdit);
|
|
166
166
|
this.startEditTitle();
|
|
167
167
|
return false;
|
|
168
168
|
}
|
|
@@ -207,6 +207,7 @@ export class EditExtension extends WunderbaumExtension {
|
|
|
207
207
|
if (validity) {
|
|
208
208
|
// Permanently apply input validations (CSS and tooltip)
|
|
209
209
|
inputElem.addEventListener("keydown", (e) => {
|
|
210
|
+
inputElem.setCustomValidity("");
|
|
210
211
|
if (!inputElem.reportValidity()) {
|
|
211
212
|
// node?.logInfo(`Invalid input: '${inputElem.value}'`);
|
|
212
213
|
}
|
|
@@ -252,6 +253,14 @@ export class EditExtension extends WunderbaumExtension {
|
|
|
252
253
|
node.logDebug(`stopEditTitle(${apply})`, opts, focusElem, newValue);
|
|
253
254
|
|
|
254
255
|
if (apply && newValue !== null && newValue !== node.title) {
|
|
256
|
+
const errMsg = focusElem.validationMessage;
|
|
257
|
+
if (errMsg) {
|
|
258
|
+
// input element's native validation failed
|
|
259
|
+
throw new Error(
|
|
260
|
+
`Input validation failed for "${newValue}": ${errMsg}.`
|
|
261
|
+
);
|
|
262
|
+
}
|
|
263
|
+
|
|
255
264
|
const colElem = node.getColElem(0)!;
|
|
256
265
|
|
|
257
266
|
this._applyChange("edit.apply", node, colElem, {
|
package/src/wb_ext_filter.ts
CHANGED
|
@@ -9,7 +9,6 @@ import {
|
|
|
9
9
|
escapeHtml,
|
|
10
10
|
escapeRegex,
|
|
11
11
|
extend,
|
|
12
|
-
extractHtmlText,
|
|
13
12
|
onEvent,
|
|
14
13
|
} from "./util";
|
|
15
14
|
import { NodeFilterCallback, NodeStatusType } from "./common";
|
|
@@ -79,7 +78,7 @@ export class FilterExtension extends WunderbaumExtension {
|
|
|
79
78
|
count = 0,
|
|
80
79
|
tree = this.tree,
|
|
81
80
|
treeOpts = tree.options,
|
|
82
|
-
escapeTitles = treeOpts.escapeTitles,
|
|
81
|
+
// escapeTitles = treeOpts.escapeTitles,
|
|
83
82
|
prevAutoCollapse = treeOpts.autoCollapse,
|
|
84
83
|
opts = extend({}, treeOpts.filter, _opts),
|
|
85
84
|
hideMode = opts.mode === "hide",
|
|
@@ -118,35 +117,36 @@ export class FilterExtension extends WunderbaumExtension {
|
|
|
118
117
|
if (!node.title) {
|
|
119
118
|
return false;
|
|
120
119
|
}
|
|
121
|
-
let text = escapeTitles ? node.title : extractHtmlText(node.title);
|
|
120
|
+
// let text = escapeTitles ? node.title : extractHtmlText(node.title);
|
|
121
|
+
let text = node.title;
|
|
122
122
|
// `.match` instead of `.test` to get the capture groups
|
|
123
123
|
let res = text.match(re);
|
|
124
124
|
|
|
125
125
|
if (res && opts.highlight) {
|
|
126
|
-
if (escapeTitles) {
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
} else {
|
|
130
|
-
// #740: we must not apply the marks to escaped entity names, e.g. `"`
|
|
131
|
-
// Use some exotic characters to mark matches:
|
|
132
|
-
temp = text.replace(reHighlight, function (s) {
|
|
133
|
-
return START_MARKER + s + END_MARKER;
|
|
134
|
-
});
|
|
135
|
-
}
|
|
136
|
-
// now we can escape the title...
|
|
137
|
-
node.titleWithHighlight = escapeHtml(temp)
|
|
138
|
-
// ... and finally insert the desired `<mark>` tags
|
|
139
|
-
.replace(RE_START_MARKER, "<mark>")
|
|
140
|
-
.replace(RE_END_MARTKER, "</mark>");
|
|
126
|
+
// if (escapeTitles) {
|
|
127
|
+
if (opts.fuzzy) {
|
|
128
|
+
temp = _markFuzzyMatchedChars(text, res, true);
|
|
141
129
|
} else {
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
});
|
|
148
|
-
}
|
|
130
|
+
// #740: we must not apply the marks to escaped entity names, e.g. `"`
|
|
131
|
+
// Use some exotic characters to mark matches:
|
|
132
|
+
temp = text.replace(reHighlight, function (s) {
|
|
133
|
+
return START_MARKER + s + END_MARKER;
|
|
134
|
+
});
|
|
149
135
|
}
|
|
136
|
+
// now we can escape the title...
|
|
137
|
+
node.titleWithHighlight = escapeHtml(temp)
|
|
138
|
+
// ... and finally insert the desired `<mark>` tags
|
|
139
|
+
.replace(RE_START_MARKER, "<mark>")
|
|
140
|
+
.replace(RE_END_MARTKER, "</mark>");
|
|
141
|
+
// } else {
|
|
142
|
+
// if (opts.fuzzy) {
|
|
143
|
+
// node.titleWithHighlight = _markFuzzyMatchedChars(text, res);
|
|
144
|
+
// } else {
|
|
145
|
+
// node.titleWithHighlight = text.replace(reHighlight, function (s) {
|
|
146
|
+
// return "<mark>" + s + "</mark>";
|
|
147
|
+
// });
|
|
148
|
+
// }
|
|
149
|
+
// }
|
|
150
150
|
// node.debug("filter", escapeTitles, text, node.titleWithHighlight);
|
|
151
151
|
}
|
|
152
152
|
return !!res;
|
|
@@ -248,8 +248,6 @@ export class FilterExtension extends WunderbaumExtension {
|
|
|
248
248
|
|
|
249
249
|
/**
|
|
250
250
|
* [ext-filter] Re-apply current filter.
|
|
251
|
-
*
|
|
252
|
-
* @requires jquery.fancytree.filter.js
|
|
253
251
|
*/
|
|
254
252
|
updateFilter() {
|
|
255
253
|
let tree = this.tree;
|
|
@@ -266,14 +264,11 @@ export class FilterExtension extends WunderbaumExtension {
|
|
|
266
264
|
|
|
267
265
|
/**
|
|
268
266
|
* [ext-filter] Reset the filter.
|
|
269
|
-
*
|
|
270
|
-
* @alias Fancytree#clearFilter
|
|
271
|
-
* @requires jquery.fancytree.filter.js
|
|
272
267
|
*/
|
|
273
268
|
clearFilter() {
|
|
274
|
-
let tree = this.tree
|
|
275
|
-
|
|
276
|
-
|
|
269
|
+
let tree = this.tree;
|
|
270
|
+
// statusNode = tree.root.findDirectChild(KEY_NODATA),
|
|
271
|
+
// escapeTitles = tree.options.escapeTitles;
|
|
277
272
|
// enhanceTitle = tree.options.enhanceTitle,
|
|
278
273
|
tree.enableUpdate(false);
|
|
279
274
|
|
|
@@ -289,11 +284,11 @@ export class FilterExtension extends WunderbaumExtension {
|
|
|
289
284
|
if (node.match && node._rowElem) {
|
|
290
285
|
// #491, #601
|
|
291
286
|
let titleElem = node._rowElem.querySelector("span.wb-title")!;
|
|
292
|
-
if (escapeTitles) {
|
|
293
|
-
|
|
294
|
-
} else {
|
|
295
|
-
|
|
296
|
-
}
|
|
287
|
+
// if (escapeTitles) {
|
|
288
|
+
titleElem.textContent = node.title;
|
|
289
|
+
// } else {
|
|
290
|
+
// titleElem.innerHTML = node.title;
|
|
291
|
+
// }
|
|
297
292
|
node._callEvent("enhanceTitle", { titleElem: titleElem });
|
|
298
293
|
}
|
|
299
294
|
delete node.match;
|
|
@@ -335,7 +330,7 @@ export class FilterExtension extends WunderbaumExtension {
|
|
|
335
330
|
function _markFuzzyMatchedChars(
|
|
336
331
|
text: string,
|
|
337
332
|
matches: RegExpMatchArray,
|
|
338
|
-
escapeTitles =
|
|
333
|
+
escapeTitles = true
|
|
339
334
|
) {
|
|
340
335
|
let matchingIndices = [];
|
|
341
336
|
// get the indices of matched characters (Iterate through `RegExpMatchArray`)
|
|
@@ -355,7 +350,7 @@ function _markFuzzyMatchedChars(
|
|
|
355
350
|
// Map each `text` char to its position and store in `textPoses`.
|
|
356
351
|
let textPoses = text.split("");
|
|
357
352
|
if (escapeTitles) {
|
|
358
|
-
// If escaping the title, then wrap the
|
|
353
|
+
// If escaping the title, then wrap the matching char within exotic chars
|
|
359
354
|
matchingIndices.forEach(function (v) {
|
|
360
355
|
textPoses[v] = START_MARKER + textPoses[v] + END_MARKER;
|
|
361
356
|
});
|