registered-property-editor.js (6050B)
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 EventEmitter = require("resource://devtools/shared/event-emitter.js"); 8 const { 9 appendText, 10 createChild, 11 } = require("resource://devtools/client/inspector/shared/utils.js"); 12 13 const INDENT_SIZE = 2; 14 const INDENT_STR = " ".repeat(INDENT_SIZE); 15 16 /** 17 * RegisteredPropertyEditor creates a list of TextPropertyEditors for a given 18 * CSS registered property propertyDefinition that can be rendered in the Rules view. 19 * 20 * @param {CssRuleView} ruleView 21 * The CssRuleView containg the document holding this rule editor. 22 * @param {Rule} rule 23 * The Rule object we're editing. 24 */ 25 class RegisteredPropertyEditor extends EventEmitter { 26 /** 27 * @param {CssRuleView} ruleView 28 * The CssRuleView containing the document holding this rule editor. 29 * @param {object} propertyDefinition 30 * The property definition data as returned by PageStyleActor's getRegisteredProperties 31 */ 32 constructor(ruleView, propertyDefinition) { 33 super(); 34 35 this.#doc = ruleView.styleDocument; 36 this.#propertyDefinition = propertyDefinition; 37 this.#createElement(); 38 } 39 40 #doc; 41 #propertyDefinition; 42 // The HTMLElement that will represent the registered property. Populated in #createElement. 43 element = null; 44 45 #createElement() { 46 this.element = this.#doc.createElement("div"); 47 this.element.className = "ruleview-rule devtools-monospace"; 48 this.element.setAttribute("uneditable", true); 49 this.element.setAttribute("unmatched", false); 50 this.element.setAttribute("data-name", this.#propertyDefinition.name); 51 52 // Give a relative position for the inplace editor's measurement 53 // span to be placed absolutely against. 54 this.element.style.position = "relative"; 55 56 const code = createChild(this.element, "code", { 57 class: "ruleview-code", 58 }); 59 60 const header = createChild(code, "header", {}); 61 62 this.propertyName = createChild(header, "span", { 63 class: "ruleview-registered-property-name", 64 textContent: this.#propertyDefinition.name, 65 }); 66 67 this.openBrace = createChild(header, "span", { 68 class: "ruleview-ruleopen", 69 textContent: " {", 70 }); 71 72 // We can't use a proper "ol" as it will mess with selection copy text, 73 // adding spaces on list item instead of the one we craft (.ruleview-rule-indent) 74 this.propertyList = createChild(code, "div", { 75 class: "ruleview-propertylist", 76 role: "list", 77 }); 78 79 this.#populateProperties(); 80 81 this.closeBrace = createChild(code, "div", { 82 class: "ruleview-ruleclose", 83 textContent: "}", 84 }); 85 } 86 87 /** 88 * Sets the content of this.#propertyList with the contents of the registered property . 89 */ 90 #populateProperties() { 91 const properties = [ 92 { 93 name: "syntax", 94 value: `"${this.#propertyDefinition.syntax}"`, 95 }, 96 { 97 name: "inherits", 98 value: this.#propertyDefinition.inherits, 99 }, 100 ]; 101 102 // The initial value may not be set, when syntax is "*", so let's only display 103 // it when it is actually set. 104 if (this.#propertyDefinition.initialValue !== null) { 105 // For JS-defined properties, we want to display them in the same syntax that 106 // was used in CSS.registerProperty (so we'll show `initialValue` and not `initial-value`). 107 properties.push({ 108 name: this.#propertyDefinition.fromJS 109 ? "initialValue" 110 : "initial-value", 111 value: this.#propertyDefinition.fromJS 112 ? `"${this.#propertyDefinition.initialValue}"` 113 : this.#propertyDefinition.initialValue, 114 }); 115 } 116 117 // When the property is registered with CSS.registerProperty, we want to match the 118 // object shape of the parameter, so include the "name" property. 119 if (this.#propertyDefinition.fromJS) { 120 properties.unshift({ 121 name: "name", 122 value: `"${this.#propertyDefinition.name}"`, 123 }); 124 } 125 126 for (const { name, value } of properties) { 127 // XXX: We could use the TextPropertyEditor here. 128 // Pros: 129 // - we'd get the similar markup, so styling would be easier 130 // - the value would be properly parsed so our various swatches and popups would work 131 // out of the box 132 // - rule view filtering would also work out of the box 133 // Cons: 134 // - it is quite tied with the Rules view regular rule, which mean we'd have 135 // to modify it to accept registered properties. 136 137 const element = createChild(this.propertyList, "div", { 138 role: "listitem", 139 }); 140 const container = createChild(element, "div", { 141 class: "ruleview-propertycontainer", 142 }); 143 144 createChild(container, "span", { 145 class: "ruleview-rule-indent clipboard-only", 146 textContent: INDENT_STR, 147 }); 148 149 const nameContainer = createChild(container, "span", { 150 class: "ruleview-namecontainer", 151 }); 152 153 createChild(nameContainer, "span", { 154 class: "ruleview-propertyname theme-fg-color3", 155 textContent: name, 156 }); 157 158 appendText(nameContainer, ": "); 159 160 // Create a span that will hold the property and semicolon. 161 // Use this span to create a slightly larger click target 162 // for the value. 163 const valueContainer = createChild(container, "span", { 164 class: "ruleview-propertyvaluecontainer", 165 }); 166 167 // Property value, editable when focused. Changes to the 168 // property value are applied as they are typed, and reverted 169 // if the user presses escape. 170 createChild(valueContainer, "span", { 171 class: "ruleview-propertyvalue theme-fg-color1", 172 textContent: value, 173 }); 174 175 appendText(valueContainer, this.#propertyDefinition.fromJS ? "," : ";"); 176 177 this.propertyList.appendChild(element); 178 } 179 } 180 } 181 182 module.exports = RegisteredPropertyEditor;