diff --git a/src/LiveDevelopment/BrowserScripts/RemoteFunctions.js b/src/LiveDevelopment/BrowserScripts/RemoteFunctions.js index 95040bc71d..14cc4c584f 100644 --- a/src/LiveDevelopment/BrowserScripts/RemoteFunctions.js +++ b/src/LiveDevelopment/BrowserScripts/RemoteFunctions.js @@ -1147,6 +1147,9 @@ function RemoteFunctions(config = {}) { NodeMoreOptionsBox.prototype = { _registerDragDrop: function() { + // disable dragging on all elements and then enable it on the current element + const allElements = document.querySelectorAll('[data-brackets-id]'); + allElements.forEach(el => el.setAttribute("draggable", false)); this.element.setAttribute("draggable", true); this.element.addEventListener("dragstart", (event) => { @@ -3006,6 +3009,8 @@ function RemoteFunctions(config = {}) { // Make the element editable element.setAttribute("contenteditable", "true"); element.focus(); + // to compare with the new text content, if same we don't make any changes in the editor area + const oldContent = element.textContent; // Move cursor to end if no existing selection const selection = window.getSelection(); @@ -3015,19 +3020,46 @@ function RemoteFunctions(config = {}) { dismissUIAndCleanupState(); + // flag to check if escape is pressed, if pressed we prevent onBlur from handling it as keydown already handles + let isEscapePressed = false; + function onBlur() { - finishEditing(element); + // Small delay so that keydown can handle things first + setTimeout(() => { + if (isEscapePressed) { + isEscapePressed = false; + finishEditingCleanup(element); + return; + } + + const newContent = element.textContent; + if (oldContent !== newContent) { + finishEditing(element); + } else { // if same content, we just cleanup things + finishEditingCleanup(element); + } + }, 10); } function onKeyDown(event) { if (event.key === "Escape") { + isEscapePressed = true; // Cancel editing event.preventDefault(); - finishEditing(element, false); // false means that the edit operation was cancelled + const newContent = element.textContent; + if (oldContent !== newContent) { + finishEditing(element, false); // false means that the edit operation was cancelled + } else { // no content change we can avoid sending details to the editor + finishEditingCleanup(element); + } } else if (event.key === "Enter" && !event.shiftKey) { + isEscapePressed = false; // Finish editing on Enter (unless Shift is held) event.preventDefault(); finishEditing(element); + } else if ((event.key === " " || event.key === "Spacebar") && element.tagName.toLowerCase() === 'button') { + event.preventDefault(); + document.execCommand("insertText", false, " "); } } @@ -3041,9 +3073,7 @@ function RemoteFunctions(config = {}) { }; } - // Function to finish editing and apply changes - // isEditSuccessful: this is a boolean value, defaults to true. false only when the edit operation is cancelled - function finishEditing(element, isEditSuccessful = true) { + function finishEditingCleanup(element) { if (!isElementEditable(element) || !element.hasAttribute("contenteditable")) { return; } @@ -3058,6 +3088,12 @@ function RemoteFunctions(config = {}) { element.removeEventListener("keydown", element._editListeners.keydown); delete element._editListeners; } + } + + // Function to finish editing and apply changes + // isEditSuccessful: this is a boolean value, defaults to true. false only when the edit operation is cancelled + function finishEditing(element, isEditSuccessful = true) { + finishEditingCleanup(element); const tagId = element.getAttribute("data-brackets-id"); window._Brackets_MessageBroker.send({ diff --git a/src/LiveDevelopment/main.js b/src/LiveDevelopment/main.js index 58e897b3e4..451427b2da 100644 --- a/src/LiveDevelopment/main.js +++ b/src/LiveDevelopment/main.js @@ -273,122 +273,6 @@ define(function main(require, exports, module) { return false; } - let $livePreviewPanel = null; // stores the live preview panel, need this as overlay is appended inside this - let $overlayContainer = null; // the overlay container - let shouldShowSyncErrorOverlay = true; // once user closes the overlay we don't show them again - let shouldShowConnectingOverlay = true; - let connectingOverlayTimer = null; // this is needed as we show the connecting overlay after 3s - let connectingOverlayTimeDuration = 3000; - - /** - * this function is responsible to check whether to show the overlay or not and how it should be shown - * because if user has closed the overlay manually, we don't show it again - * secondly, for connecting overlay we show that after a 3s timer, but sync error overlay is shown immediately - * @param {String} textMessage - the text that is written inside the overlay - * @param {Number} status - 1 for connect, 4 for sync error but we match it using MultiBrowserLiveDev - */ - function _handleOverlay(textMessage, status) { - if (!$livePreviewPanel) { - $livePreviewPanel = $("#panel-live-preview"); - } - - // remove any existing overlay & timer - _hideOverlay(); - - // to not show the overlays if user has already closed it before - if(status === MultiBrowserLiveDev.STATUS_CONNECTING && !shouldShowConnectingOverlay) { return; } - if(status === MultiBrowserLiveDev.STATUS_SYNC_ERROR && !shouldShowSyncErrorOverlay) { return; } - - // for connecting status, we delay showing the overlay by 3 seconds - if(status === MultiBrowserLiveDev.STATUS_CONNECTING) { - connectingOverlayTimer = setTimeout(() => { - _createAndShowOverlay(textMessage, status); - connectingOverlayTimer = null; - }, connectingOverlayTimeDuration); - return; - } - - // for sync error status, show immediately - _createAndShowOverlay(textMessage, status); - } - - /** - * this function is responsible to create & show the overlay. - * so overlay is shown when the live preview is connecting or live preview stopped because of some syntax error - * @param {String} textMessage - the text that is written inside the overlay - * @param {Number} status - 1 for connect, 4 for sync error but we match it using MultiBrowserLiveDev - */ - function _createAndShowOverlay(textMessage, status) { - if (!$livePreviewPanel) { - $livePreviewPanel = $("#panel-live-preview"); - } - - // create the overlay element - // styled inside the 'src/extensionsIntegrated/Phoenix-live-preview/live-preview.css' - $overlayContainer = $("