From c31a40c749a4d5993d69edd78915ef30a241738d Mon Sep 17 00:00:00 2001 From: Abe Jellinek Date: Tue, 12 Mar 2024 01:38:00 -0400 Subject: [PATCH] editable-text: Make dropping text work when field is unfocused (#3730) --- chrome/content/zotero/elements/abstractBox.js | 8 +++- .../content/zotero/elements/editableText.js | 44 +++++++++++++++++-- chrome/content/zotero/elements/paneHeader.js | 8 +++- 3 files changed, 52 insertions(+), 8 deletions(-) diff --git a/chrome/content/zotero/elements/abstractBox.js b/chrome/content/zotero/elements/abstractBox.js index 7d898b4575..ad4b78a2a9 100644 --- a/chrome/content/zotero/elements/abstractBox.js +++ b/chrome/content/zotero/elements/abstractBox.js @@ -114,9 +114,13 @@ let abstract = this.item.getField('abstractNote'); this._section.summary = abstract; - if (!this._abstractField.initialValue || this._abstractField.initialValue !== abstract) { + // If focused, update the value that will be restored on Escape; + // otherwise, update the displayed value + if (this._abstractField.focused) { + this._abstractField.initialValue = abstract; + } + else { this._abstractField.value = abstract; - this._abstractField.initialValue = ''; } this._abstractField.readOnly = this._mode == 'view'; this._abstractField.setAttribute('aria-label', Zotero.ItemFields.getLocalizedString('abstractNote')); diff --git a/chrome/content/zotero/elements/editableText.js b/chrome/content/zotero/elements/editableText.js index 86ef933175..9abc466edb 100644 --- a/chrome/content/zotero/elements/editableText.js +++ b/chrome/content/zotero/elements/editableText.js @@ -109,11 +109,11 @@ } get initialValue() { - return this._input?.dataset.initialValue || ''; + return this._input?.dataset.initialValue ?? ''; } set initialValue(initialValue) { - this._input.dataset.initialValue = initialValue || ''; + this._input.dataset.initialValue = initialValue ?? ''; } get autocomplete() { @@ -204,7 +204,7 @@ input.addEventListener('change', handleChange); input.addEventListener('focus', () => { // If the last blur was ignored because it was caused by the window becoming inactive, - // ignore this focus event as well, so we don't reset initialValue + // ignore this focus event as well if (this._ignoredWindowInactiveBlur) { this._ignoredWindowInactiveBlur = false; return; @@ -216,7 +216,9 @@ if (!this.getAttribute("mousedown")) { this._input.setSelectionRange(0, this._input.value.length, "backward"); } - this._input.dataset.initialValue = this._input.value; + if (!('initialValue' in this._input.dataset)) { + this._input.dataset.initialValue = this._input.value; + } }); input.addEventListener('blur', () => { // Ignore this blur if it was caused by the window becoming inactive (see above) @@ -255,6 +257,31 @@ event.preventDefault(); } }); + input.addEventListener('dragover', (event) => { + // If the input is not focused, override the default drop behavior + if ((document.activeElement !== this._input || Services.focus.activeWindow !== window) + && event.dataTransfer.getData('text/plain')) { + event.preventDefault(); + event.dataTransfer.dropEffect = 'copy'; + } + }); + input.addEventListener('drop', (event) => { + let text = event.dataTransfer.getData('text/plain'); + // If the input is not focused, replace its entire value with the dropped text + // Otherwise, the normal drop effect takes place and the text is inserted at the cursor + if ((document.activeElement !== this._input || Services.focus.activeWindow !== window) + && text) { + event.preventDefault(); + document.activeElement?.blur(); + // Wait a tick to work around an apparent Firefox bug where the cursor stays inside the old + // input even though the new input becomes visually focused + setTimeout(() => { + this.focus(); + this._input.value = text; + handleInput(); + }); + } + }); let focused = false; let selectionStart = this._input?.selectionStart; @@ -340,12 +367,21 @@ } focus(options) { + // If the window isn't active, the focus event won't fire yet, + // so store the initial value now + if (this._input && Services.focus.activeWindow !== window && !('initialValue' in this._input.dataset)) { + this._input.dataset.initialValue = this._input.value; + } this._input?.focus(options); } blur() { this._input?.blur(); } + + get focused() { + return this._input && document.activeElement === this._input; + } } customElements.define("editable-text", EditableText); diff --git a/chrome/content/zotero/elements/paneHeader.js b/chrome/content/zotero/elements/paneHeader.js index 11b2dd6c76..7bcca97930 100644 --- a/chrome/content/zotero/elements/paneHeader.js +++ b/chrome/content/zotero/elements/paneHeader.js @@ -142,9 +142,13 @@ this._titleFieldID = Zotero.ItemFields.getFieldIDFromTypeAndBase(this.item.itemTypeID, 'title'); let title = this.item.getField(this._titleFieldID); - if (!this.titleField.initialValue || this.titleField.initialValue !== title) { + // If focused, update the value that will be restored on Escape; + // otherwise, update the displayed value + if (this.titleField.focused) { + this.titleField.initialValue = title; + } + else { this.titleField.value = title; - this.titleField.initialValue = ''; } this.titleField.readOnly = this._mode == 'view'; if (this._titleFieldID) {