Bug 1067318 - live preview when editing attributes draft
authorJulian Descottes <jdescottes@mozilla.com>
Mon, 13 Feb 2017 09:46:44 +0100
changeset 581150 298a10a1df94a52b92f9b18dd6fa3b9f6fdfd7c3
parent 581149 c5586226441ec288d3e7cbbaac7b6007dc6e5858
child 629496 a3d4e4a9e11dc21ca9420abf4792005e86be70ed
push id59782
push userjdescottes@mozilla.com
push dateFri, 19 May 2017 10:02:20 +0000
bugs1067318
milestone55.0a1
Bug 1067318 - live preview when editing attributes MozReview-Commit-ID: 6a8wu3irKw5
devtools/client/inspector/markup/views/element-editor.js
--- a/devtools/client/inspector/markup/views/element-editor.js
+++ b/devtools/client/inspector/markup/views/element-editor.js
@@ -78,16 +78,26 @@ function ElementEditor(container, node) 
   this.newAttr.editMode = editableField({
     element: this.newAttr,
     multiline: true,
     maxWidth: () => getAutocompleteMaxWidth(this.newAttr, this.container.elt),
     trigger: "dblclick",
     stopOnReturn: true,
     contentType: InplaceEditor.CONTENT_TYPES.CSS_MIXED,
     popup: this.markup.popup,
+    change: (val) => {
+      let doMods = this._startModifyingAttributes();
+      let undoMods = this._startModifyingAttributes();
+      this._applyAttributes(val, null, doMods, undoMods);
+      this.container.undo.do(() => {
+        doMods.apply();
+      }, function () {
+        undoMods.apply();
+      });
+    },
     done: (val, commit) => {
       if (!commit) {
         return;
       }
 
       let doMods = this._startModifyingAttributes();
       let undoMods = this._startModifyingAttributes();
       this._applyAttributes(val, null, doMods, undoMods);
@@ -210,52 +220,48 @@ ElementEditor.prototype = {
    * Update the state of the editor from the node.
    */
   update: function () {
     let nodeAttributes = this.node.attributes || [];
 
     // Keep the data model in sync with attributes on the node.
     let currentAttributes = new Set(nodeAttributes.map(a => a.name));
     for (let name of this.attrElements.keys()) {
-      if (!currentAttributes.has(name)) {
+      if (!this._isEditingAttribute && !currentAttributes.has(name)) {
         this.removeAttribute(name);
       }
     }
 
     // Only loop through the current attributes on the node.  Missing
     // attributes have already been removed at this point.
     for (let attr of nodeAttributes) {
       let el = this.attrElements.get(attr.name);
-      let valueChanged = el &&
-        el.dataset.value !== attr.value;
-      let isEditing = el && el.querySelector(".editable").inplaceEditor;
-      let canSimplyShowEditor = el && (!valueChanged || isEditing);
-
+      let valueChanged = el && el.dataset.value !== attr.value;
+      let canSimplyShowEditor = el && !valueChanged;
       if (canSimplyShowEditor) {
         // Element already exists and doesn't need to be recreated.
         // Just show it (it's hidden by default).
         el.style.removeProperty("display");
-      } else {
+      } else if (!this._isEditingAttribute) {
         // Create a new editor, because the value of an existing attribute
         // has changed.
         let attribute = this._createAttribute(attr, el);
         attribute.style.removeProperty("display");
 
         // Temporarily flash the attribute to highlight the change.
         // But not if this is the first time the editor instance has
         // been created.
         if (this.initialized) {
           this.flashAttribute(attr.name);
         }
       }
     }
 
     // Update the event bubble display
-    this.eventNode.style.display = this.node.hasEventListeners ?
-      "inline-block" : "none";
+    this.eventNode.style.display = this.node.hasEventListeners ? "inline-block" : "none";
 
     this.updateTextEditor();
   },
 
   /**
    * Update the inline text editor in case of a single text child node.
    */
   updateTextEditor: function () {
@@ -376,21 +382,40 @@ ElementEditor.prototype = {
           let length = editValueDisplayed.length;
           let editorLength = editor.input.value.length;
           let start = editorLength - (length + 1);
           editor.input.setSelectionRange(start, start + length);
         } else {
           editor.input.select();
         }
       },
+      change: (newValue) => {
+        let doMods = this._startModifyingAttributes();
+        let undoMods = this._startModifyingAttributes();
+
+        // Remove the attribute stored in this editor and re-add any attributes
+        // parsed out of the input element. Restore original attribute if
+        // parsing fails.
+        this._saveAttribute(attribute.name, undoMods);
+        doMods.removeAttribute(attribute.name);
+        this._isEditingAttribute = true;
+        this._applyAttributes(newValue, attr, doMods, undoMods);
+        this.container.undo.do(() => {
+          doMods.apply();
+        }, () => {
+          undoMods.apply();
+        });
+      },
       done: (newValue, commit, direction) => {
         if (!commit || newValue === initial) {
           return;
         }
 
+        this._isEditingAttribute = false;
+
         let doMods = this._startModifyingAttributes();
         let undoMods = this._startModifyingAttributes();
 
         // Remove the attribute stored in this editor and re-add any attributes
         // parsed out of the input element. Restore original attribute if
         // parsing fails.
         this.refocusOnEdit(attribute.name, attr, direction);
         this._saveAttribute(attribute.name, undoMods);
@@ -469,17 +494,19 @@ ElementEditor.prototype = {
    *         The attribute editor that created this
    *         set of attributes, used to place new attributes where the
    *         user put them.
    */
   _applyAttributes: function (value, attrNode, doMods, undoMods) {
     let attrs = parseAttributeValues(value, this.doc);
     for (let attr of attrs) {
       // Create an attribute editor next to the current attribute if needed.
-      this._createAttribute(attr, attrNode ? attrNode.nextSibling : null);
+      if (!this._isEditingAttribute) {
+        this._createAttribute(attr, attrNode ? attrNode.nextSibling : null);
+      }
       this._saveAttribute(attr.name, undoMods);
       doMods.setAttribute(attr.name, attr.value);
     }
   },
 
   /**
    * Saves the current state of the given attribute into an attribute
    * modification list.