From 2ebd55b31d0b55b1c223ef410a41b0485c81dede Mon Sep 17 00:00:00 2001 From: barsdeveloper Date: Mon, 18 Apr 2022 22:38:50 +0200 Subject: [PATCH] Text edit input behavior fixed --- dist/ueblueprint.js | 104 ++++++++++++++++-------- js/input/IInput.js | 7 +- js/input/common/Copy.js | 1 + js/input/common/Paste.js | 1 + js/input/keybaord/IKeyboardShortcut.js | 16 ++-- js/input/keybaord/KeyboardCanc.js | 5 +- js/input/keybaord/KeyboardEnableZoom.js | 5 +- js/input/keybaord/KeyboardSelectAll.js | 5 +- js/input/mouse/IMouseClickDrag.js | 3 +- js/input/mouse/MouseCreateLink.js | 2 +- js/input/mouse/MouseIgnore.js | 16 ++++ js/template/ITemplate.js | 3 +- js/template/PinTemplate.js | 3 +- js/template/StringPinTemplate.js | 39 ++++++--- rollup.config.js | 2 +- 15 files changed, 141 insertions(+), 71 deletions(-) create mode 100644 js/input/mouse/MouseIgnore.js diff --git a/dist/ueblueprint.js b/dist/ueblueprint.js index 33d3587..82080a0 100755 --- a/dist/ueblueprint.js +++ b/dist/ueblueprint.js @@ -179,9 +179,10 @@ class IInput { constructor(target, blueprint, options) { this.#target = target; this.#blueprint = blueprint; + options.consumeEvent ??= false; + options.listenOnFocus ??= false; + options.unlistenOnTextEdit ??= false; this.options = options; - this.options.listenOnFocus = this.options?.listenOnFocus ?? false; - this.options.unlistenOnTextEdit = this.options?.unlistenOnTextEdit ?? true; let self = this; this.listenHandler = _ => self.listenEvents(); this.unlistenHandler = _ => self.unlistenEvents(); @@ -189,7 +190,7 @@ class IInput { this.blueprint.addEventListener(Configuration.focusEventName.begin, this.listenHandler); this.blueprint.addEventListener(Configuration.focusEventName.end, this.unlistenHandler); } - if (options?.unlistenOnTextEdit ?? false) { + if (this.options.unlistenOnTextEdit) { this.blueprint.addEventListener(Configuration.editTextEventName.begin, this.unlistenHandler); this.blueprint.addEventListener(Configuration.editTextEventName.end, this.listenHandler); } @@ -1318,6 +1319,7 @@ class Copy extends IInput { constructor(target, blueprint, options = {}) { options.listenOnFocus = true; + options.unlistenOnTextEdit = true; super(target, blueprint, options); this.serializer = new ObjectSerializer(); let self = this; @@ -1391,9 +1393,10 @@ class ITemplate { /** * @param {T} element + * @returns {IInput[]} */ createInputObjects(element) { - return /** @type {IInput[]} */([]) + return [] } } @@ -1405,8 +1408,10 @@ class IKeyboardShortcut extends IInput { #activationKeys constructor(target, blueprint, options = {}) { - options.listenOnFocus = true; + options.activateAnyKey ??= false; options.activationKeys ??= []; + options.listenOnFocus ??= true; + options.unlistenOnTextEdit ??= true; if (!(options.activationKeys instanceof Array)) { options.activationKeys = [options.activationKeys]; } @@ -1436,12 +1441,14 @@ class IKeyboardShortcut extends IInput { /** @param {KeyboardEvent} e */ this.keyDownHandler = e => { if ( - self.#activationKeys.some(keyEntry => + this.options.activateAnyKey + || self.#activationKeys.some(keyEntry => wantsShift(keyEntry) == e.shiftKey && wantsCtrl(keyEntry) == e.ctrlKey && wantsAlt(keyEntry) == e.altKey && Configuration.Keys[keyEntry.Key] == e.code - )) { + ) + ) { if (options.consumeEvent) { e.stopImmediatePropagation(); } @@ -1454,13 +1461,15 @@ class IKeyboardShortcut extends IInput { /** @param {KeyboardEvent} e */ this.keyUpHandler = e => { if ( - self.#activationKeys.some(keyEntry => + this.options.activateAnyKey + || self.#activationKeys.some(keyEntry => keyEntry.bShift && e.key == "Shift" || keyEntry.bCtrl && e.key == "Control" || keyEntry.bAlt && e.key == "Alt" || keyEntry.bCmd && e.key == "Meta" || Configuration.Keys[keyEntry.Key] == e.code - )) { + ) + ) { if (options.consumeEvent) { e.stopImmediatePropagation(); } @@ -1499,10 +1508,7 @@ class KeyboardCanc extends IKeyboardShortcut { * @param {Object} options */ constructor(target, blueprint, options = {}) { - options = { - ...options, - activationKeys: Configuration.deleteNodesKeyboardKey - }; + options.activationKeys = Configuration.deleteNodesKeyboardKey; super(target, blueprint, options); } @@ -1631,10 +1637,7 @@ class KeyboardEnableZoom extends IKeyboardShortcut { * @param {Object} options */ constructor(target, blueprint, options = {}) { - options = { - ...options, - activationKeys: Configuration.enableZoomIn - }; + options.activationKeys = Configuration.enableZoomIn; super(target, blueprint, options); } @@ -1661,10 +1664,7 @@ class KeyboardSelectAll extends IKeyboardShortcut { * @param {Object} options */ constructor(target, blueprint, options = {}) { - options = { - ...options, - activationKeys: Configuration.selectAllKeyboardKey - }; + options.activationKeys = Configuration.selectAllKeyboardKey; super(target, blueprint, options); } @@ -1702,8 +1702,7 @@ class IMouseClickDrag extends IPointing { started = false - constructor(target, blueprint, options) { - options.unlistenOnTextEdit; + constructor(target, blueprint, options = {}) { super(target, blueprint, options); this.clickButton = options?.clickButton ?? 0; this.exitAnyButton = options?.exitAnyButton ?? true; @@ -2689,7 +2688,7 @@ class MouseCreateLink extends IMouseClickDrag { pin.removeEventListener("mouseenter", this.#mouseenterHandler); pin.removeEventListener("mouseleave", this.#mouseleaveHandler); }); - if (this.enteredPin) { + if (this.enteredPin && this.linkValid) { this.blueprint.addGraphElement(this.link); this.link.destinationPin = this.enteredPin; this.link.setLinkMessage(null); @@ -2711,6 +2710,7 @@ class MouseCreateLink extends IMouseClickDrag { // @ts-check /** + * @typedef {import ("../input/IInput").default} IInput * @typedef {import("../element/NodeElement").default} NodeElement * @typedef {import("../element/PinElement").default} PinElement */ @@ -2719,7 +2719,7 @@ class PinTemplate extends ITemplate { /** * @param {PinElement} pin - * + * @returns {IInput[]} */ createInputObjects(pin) { return [ @@ -2831,6 +2831,21 @@ class ExecPinTemplate extends PinTemplate { } } +/** + * @typedef {import("../../element/PinElement").default} PinElement + */ + +/** + * @extends IMouseClickDrag + */ +class MouseIgnore extends IMouseClickDrag { + + constructor(target, blueprint, options = {}) { + options.consumeEvent = true; + super(target, blueprint, options); + } +} + // @ts-check /** @@ -2839,6 +2854,9 @@ class ExecPinTemplate extends PinTemplate { class StringPinTemplate extends PinTemplate { + /** @type {HTMLElement} */ + input = null + hasInput() { return true } @@ -2849,7 +2867,7 @@ class StringPinTemplate extends PinTemplate { renderInput(pin) { return html`
-
+
` } @@ -2859,17 +2877,17 @@ class StringPinTemplate extends PinTemplate { */ setup(pin) { super.setup(pin); - const input = pin.querySelector(".ueb-pin-input-content"); - if (input) { - this.onFocusHandler = () => { + if (this.input = pin.querySelector(".ueb-pin-input-content")) { + this.onFocusHandler = (e) => { pin.blueprint.dispatchEditTextEvent(true); }; - this.onFocusOutHandler = () => { - pin.blueprint.dispatchEditTextEvent(false); + this.onFocusOutHandler = (e) => { + e.preventDefault(); document.getSelection().removeAllRanges(); // Deselect text inside the input + pin.blueprint.dispatchEditTextEvent(false); }; - input.addEventListener("onfocus", this.onFocusHandler); - input.addEventListener("onfocusout", this.onFocusOutHandler); + this.input.addEventListener("focus", this.onFocusHandler); + this.input.addEventListener("focusout", this.onFocusOutHandler); } } @@ -2878,8 +2896,23 @@ class StringPinTemplate extends PinTemplate { */ cleanup(pin) { super.cleanup(pin); - pin.blueprint.removeEventListener("onfocus", this.onFocusHandler); - pin.blueprint.removeEventListener("onfocusout", this.onFocusOutHandler); + if (this.input) { + this.input.removeEventListener("focus", this.onFocusHandler); + this.input.removeEventListener("focusout", this.onFocusOutHandler); + } + } + + /** + * @param {PinElement} pin + */ + createInputObjects(pin) { + if (pin.isInput()) { + return [ + ...super.createInputObjects(pin), + new MouseIgnore(pin.querySelector(".ueb-pin-input-content"), pin.blueprint), + ] + } + return super.createInputObjects(pin) } } @@ -3318,6 +3351,7 @@ class Paste extends IInput { constructor(target, blueprint, options = {}) { options.listenOnFocus = true; + options.unlistenOnTextEdit = true; super(target, blueprint, options); this.serializer = new ObjectSerializer(); let self = this; diff --git a/js/input/IInput.js b/js/input/IInput.js index ce4a5d9..8326a0f 100644 --- a/js/input/IInput.js +++ b/js/input/IInput.js @@ -34,9 +34,10 @@ export default class IInput { constructor(target, blueprint, options) { this.#target = target this.#blueprint = blueprint + options.consumeEvent ??= false + options.listenOnFocus ??= false + options.unlistenOnTextEdit ??= false this.options = options - this.options.listenOnFocus = this.options?.listenOnFocus ?? false - this.options.unlistenOnTextEdit = this.options?.unlistenOnTextEdit ?? true let self = this this.listenHandler = _ => self.listenEvents() this.unlistenHandler = _ => self.unlistenEvents() @@ -44,7 +45,7 @@ export default class IInput { this.blueprint.addEventListener(Configuration.focusEventName.begin, this.listenHandler) this.blueprint.addEventListener(Configuration.focusEventName.end, this.unlistenHandler) } - if (options?.unlistenOnTextEdit ?? false) { + if (this.options.unlistenOnTextEdit) { this.blueprint.addEventListener(Configuration.editTextEventName.begin, this.unlistenHandler) this.blueprint.addEventListener(Configuration.editTextEventName.end, this.listenHandler) } diff --git a/js/input/common/Copy.js b/js/input/common/Copy.js index 43adf7d..3aa61dc 100755 --- a/js/input/common/Copy.js +++ b/js/input/common/Copy.js @@ -10,6 +10,7 @@ export default class Copy extends IInput { constructor(target, blueprint, options = {}) { options.listenOnFocus = true + options.unlistenOnTextEdit = true super(target, blueprint, options) this.serializer = new ObjectSerializer() let self = this diff --git a/js/input/common/Paste.js b/js/input/common/Paste.js index 2e3bf01..02f8f51 100755 --- a/js/input/common/Paste.js +++ b/js/input/common/Paste.js @@ -11,6 +11,7 @@ export default class Paste extends IInput { constructor(target, blueprint, options = {}) { options.listenOnFocus = true + options.unlistenOnTextEdit = true super(target, blueprint, options) this.serializer = new ObjectSerializer() let self = this diff --git a/js/input/keybaord/IKeyboardShortcut.js b/js/input/keybaord/IKeyboardShortcut.js index 95fd62a..450c5d2 100644 --- a/js/input/keybaord/IKeyboardShortcut.js +++ b/js/input/keybaord/IKeyboardShortcut.js @@ -11,8 +11,10 @@ export default class IKeyboardShortcut extends IInput { #activationKeys constructor(target, blueprint, options = {}) { - options.listenOnFocus = true + options.activateAnyKey ??= false options.activationKeys ??= [] + options.listenOnFocus ??= true + options.unlistenOnTextEdit ??= true if (!(options.activationKeys instanceof Array)) { options.activationKeys = [options.activationKeys] } @@ -42,12 +44,14 @@ export default class IKeyboardShortcut extends IInput { /** @param {KeyboardEvent} e */ this.keyDownHandler = e => { if ( - self.#activationKeys.some(keyEntry => + this.options.activateAnyKey + || self.#activationKeys.some(keyEntry => wantsShift(keyEntry) == e.shiftKey && wantsCtrl(keyEntry) == e.ctrlKey && wantsAlt(keyEntry) == e.altKey && Configuration.Keys[keyEntry.Key] == e.code - )) { + ) + ) { if (options.consumeEvent) { e.stopImmediatePropagation() } @@ -60,13 +64,15 @@ export default class IKeyboardShortcut extends IInput { /** @param {KeyboardEvent} e */ this.keyUpHandler = e => { if ( - self.#activationKeys.some(keyEntry => + this.options.activateAnyKey + || self.#activationKeys.some(keyEntry => keyEntry.bShift && e.key == "Shift" || keyEntry.bCtrl && e.key == "Control" || keyEntry.bAlt && e.key == "Alt" || keyEntry.bCmd && e.key == "Meta" || Configuration.Keys[keyEntry.Key] == e.code - )) { + ) + ) { if (options.consumeEvent) { e.stopImmediatePropagation() } diff --git a/js/input/keybaord/KeyboardCanc.js b/js/input/keybaord/KeyboardCanc.js index 15942f6..13bc9e0 100755 --- a/js/input/keybaord/KeyboardCanc.js +++ b/js/input/keybaord/KeyboardCanc.js @@ -11,10 +11,7 @@ export default class KeyboardCanc extends IKeyboardShortcut { * @param {Object} options */ constructor(target, blueprint, options = {}) { - options = { - ...options, - activationKeys: Configuration.deleteNodesKeyboardKey - } + options.activationKeys = Configuration.deleteNodesKeyboardKey super(target, blueprint, options) } diff --git a/js/input/keybaord/KeyboardEnableZoom.js b/js/input/keybaord/KeyboardEnableZoom.js index 5994c48..04633a4 100644 --- a/js/input/keybaord/KeyboardEnableZoom.js +++ b/js/input/keybaord/KeyboardEnableZoom.js @@ -15,10 +15,7 @@ export default class KeyboardEnableZoom extends IKeyboardShortcut { * @param {Object} options */ constructor(target, blueprint, options = {}) { - options = { - ...options, - activationKeys: Configuration.enableZoomIn - } + options.activationKeys = Configuration.enableZoomIn super(target, blueprint, options) } diff --git a/js/input/keybaord/KeyboardSelectAll.js b/js/input/keybaord/KeyboardSelectAll.js index e2e3b45..ca8272c 100755 --- a/js/input/keybaord/KeyboardSelectAll.js +++ b/js/input/keybaord/KeyboardSelectAll.js @@ -14,10 +14,7 @@ export default class KeyboardSelectAll extends IKeyboardShortcut { * @param {Object} options */ constructor(target, blueprint, options = {}) { - options = { - ...options, - activationKeys: Configuration.selectAllKeyboardKey - } + options.activationKeys = Configuration.selectAllKeyboardKey super(target, blueprint, options) } diff --git a/js/input/mouse/IMouseClickDrag.js b/js/input/mouse/IMouseClickDrag.js index 0912cd6..43bcad5 100644 --- a/js/input/mouse/IMouseClickDrag.js +++ b/js/input/mouse/IMouseClickDrag.js @@ -30,8 +30,7 @@ export default class IMouseClickDrag extends IPointing { started = false - constructor(target, blueprint, options) { - options.unlistenOnTextEdit + constructor(target, blueprint, options = {}) { super(target, blueprint, options) this.clickButton = options?.clickButton ?? 0 this.exitAnyButton = options?.exitAnyButton ?? true diff --git a/js/input/mouse/MouseCreateLink.js b/js/input/mouse/MouseCreateLink.js index d4a8625..9390d2c 100755 --- a/js/input/mouse/MouseCreateLink.js +++ b/js/input/mouse/MouseCreateLink.js @@ -87,7 +87,7 @@ export default class MouseCreateLink extends IMouseClickDrag { pin.removeEventListener("mouseenter", this.#mouseenterHandler) pin.removeEventListener("mouseleave", this.#mouseleaveHandler) }) - if (this.enteredPin) { + if (this.enteredPin && this.linkValid) { this.blueprint.addGraphElement(this.link) this.link.destinationPin = this.enteredPin this.link.setLinkMessage(null) diff --git a/js/input/mouse/MouseIgnore.js b/js/input/mouse/MouseIgnore.js new file mode 100644 index 0000000..d090cb5 --- /dev/null +++ b/js/input/mouse/MouseIgnore.js @@ -0,0 +1,16 @@ +import IMouseClickDrag from "./IMouseClickDrag"; + +/** + * @typedef {import("../../element/PinElement").default} PinElement + */ + +/** + * @extends IMouseClickDrag + */ +export default class MouseIgnore extends IMouseClickDrag { + + constructor(target, blueprint, options = {}) { + options.consumeEvent = true + super(target, blueprint, options) + } +} \ No newline at end of file diff --git a/js/template/ITemplate.js b/js/template/ITemplate.js index 5d4e379..1ef1407 100644 --- a/js/template/ITemplate.js +++ b/js/template/ITemplate.js @@ -44,8 +44,9 @@ export default class ITemplate { /** * @param {T} element + * @returns {IInput[]} */ createInputObjects(element) { - return /** @type {IInput[]} */([]) + return [] } } diff --git a/js/template/PinTemplate.js b/js/template/PinTemplate.js index e203765..cf1118c 100755 --- a/js/template/PinTemplate.js +++ b/js/template/PinTemplate.js @@ -7,6 +7,7 @@ import sanitizeText from "./sanitizeText" import Utility from "../Utility" /** + * @typedef {import ("../input/IInput").default} IInput * @typedef {import("../element/NodeElement").default} NodeElement * @typedef {import("../element/PinElement").default} PinElement */ @@ -15,7 +16,7 @@ export default class PinTemplate extends ITemplate { /** * @param {PinElement} pin - * + * @returns {IInput[]} */ createInputObjects(pin) { return [ diff --git a/js/template/StringPinTemplate.js b/js/template/StringPinTemplate.js index 07e76b0..fbcaf89 100644 --- a/js/template/StringPinTemplate.js +++ b/js/template/StringPinTemplate.js @@ -1,6 +1,7 @@ // @ts-check import html from "./html" +import MouseIgnore from "../input/mouse/MouseIgnore" import PinTemplate from "./PinTemplate" /** @@ -9,6 +10,9 @@ import PinTemplate from "./PinTemplate" export default class StringPinTemplate extends PinTemplate { + /** @type {HTMLElement} */ + input = null + hasInput() { return true } @@ -19,7 +23,7 @@ export default class StringPinTemplate extends PinTemplate { renderInput(pin) { return html`
-
+
` } @@ -29,17 +33,17 @@ export default class StringPinTemplate extends PinTemplate { */ setup(pin) { super.setup(pin) - const input = pin.querySelector(".ueb-pin-input-content") - if (input) { - this.onFocusHandler = () => { + if (this.input = pin.querySelector(".ueb-pin-input-content")) { + this.onFocusHandler = (e) => { pin.blueprint.dispatchEditTextEvent(true) } - this.onFocusOutHandler = () => { - pin.blueprint.dispatchEditTextEvent(false) + this.onFocusOutHandler = (e) => { + e.preventDefault() document.getSelection().removeAllRanges() // Deselect text inside the input + pin.blueprint.dispatchEditTextEvent(false) } - input.addEventListener("onfocus", this.onFocusHandler) - input.addEventListener("onfocusout", this.onFocusOutHandler) + this.input.addEventListener("focus", this.onFocusHandler) + this.input.addEventListener("focusout", this.onFocusOutHandler) } } @@ -48,7 +52,22 @@ export default class StringPinTemplate extends PinTemplate { */ cleanup(pin) { super.cleanup(pin) - pin.blueprint.removeEventListener("onfocus", this.onFocusHandler) - pin.blueprint.removeEventListener("onfocusout", this.onFocusOutHandler) + if (this.input) { + this.input.removeEventListener("focus", this.onFocusHandler) + this.input.removeEventListener("focusout", this.onFocusOutHandler) + } + } + + /** + * @param {PinElement} pin + */ + createInputObjects(pin) { + if (pin.isInput()) { + return [ + ...super.createInputObjects(pin), + new MouseIgnore(pin.querySelector(".ueb-pin-input-content"), pin.blueprint), + ] + } + return super.createInputObjects(pin) } } diff --git a/rollup.config.js b/rollup.config.js index 15f78e7..977e180 100755 --- a/rollup.config.js +++ b/rollup.config.js @@ -12,8 +12,8 @@ export default { }, plugins: [ nodeResolve({ browser: true }), - //minifyHTML(), commonjs(), + //minifyHTML(), //terser(), copy({ targets: [