text-editor.js (3325B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 "use strict"; 6 7 const { 8 createFactory, 9 } = require("resource://devtools/client/shared/vendor/react.mjs"); 10 11 const TextNode = createFactory( 12 require("resource://devtools/client/inspector/markup/components/TextNode.js") 13 ); 14 15 loader.lazyRequireGetter( 16 this, 17 "getAutocompleteMaxWidth", 18 "resource://devtools/client/inspector/markup/utils.js", 19 true 20 ); 21 loader.lazyRequireGetter( 22 this, 23 "getLongString", 24 "resource://devtools/client/inspector/shared/utils.js", 25 true 26 ); 27 loader.lazyRequireGetter( 28 this, 29 "InplaceEditor", 30 "resource://devtools/client/shared/inplace-editor.js", 31 true 32 ); 33 34 /** 35 * Creates a simple text editor node, used for TEXT and COMMENT 36 * nodes. 37 */ 38 class TextEditor { 39 /** 40 * @param {MarkupContainer} container 41 * The container owning this editor. 42 * @param {DOMNode} node 43 * The node being edited. 44 * @param {string} type 45 * The type of editor to build. This can be either 'text' or 'comment'. 46 */ 47 constructor(container, node, type) { 48 this.container = container; 49 this.markup = this.container.markup; 50 this.node = node; 51 this._selected = false; 52 53 this.showTextEditor = this.showTextEditor.bind(this); 54 55 this.buildMarkup(type); 56 } 57 buildMarkup(type) { 58 const doc = this.markup.doc; 59 60 this.elt = doc.createElement("span"); 61 this.elt.classList.add("editor", type); 62 63 getLongString(this.node.getNodeValue()).then(value => { 64 this.textNode = this.ReactDOM.render( 65 TextNode({ 66 showTextEditor: this.showTextEditor, 67 type, 68 value, 69 }), 70 this.elt 71 ); 72 }); 73 } 74 75 get ReactDOM() { 76 // Reuse the toolbox's ReactDOM to avoid loading react-dom.js again in the 77 // Inspector's BrowserLoader. 78 return this.container.markup.inspector.ReactDOM; 79 } 80 81 get selected() { 82 return this._selected; 83 } 84 85 set selected(value) { 86 if (value === this._selected) { 87 return; 88 } 89 this._selected = value; 90 this.update(); 91 } 92 93 showTextEditor(element) { 94 new InplaceEditor({ 95 cssProperties: this.markup.inspector.cssProperties, 96 done: (val, commit) => { 97 if (!commit) { 98 return; 99 } 100 getLongString(this.node.getNodeValue()).then(oldValue => { 101 this.container.undo.do( 102 () => { 103 this.node.setNodeValue(val); 104 }, 105 () => { 106 this.node.setNodeValue(oldValue); 107 } 108 ); 109 }); 110 }, 111 element, 112 maxWidth: () => getAutocompleteMaxWidth(element, this.container.elt), 113 multiline: true, 114 stopOnReturn: true, 115 trimOutput: false, 116 }); 117 } 118 119 async update() { 120 try { 121 const value = await getLongString(this.node.getNodeValue()); 122 123 if (this.textNode.state.value !== value) { 124 this.textNode.setState({ value }); 125 } 126 } catch (e) { 127 console.error(e); 128 } 129 } 130 131 destroy() { 132 this.ReactDOM.unmountComponentAtNode(this.elt); 133 } 134 135 /** 136 * Stub method for consistency with ElementEditor. 137 */ 138 getInfoAtNode() { 139 return null; 140 } 141 } 142 143 module.exports = TextEditor;