/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ "use strict"; const EventEmitter = require("resource://devtools/shared/event-emitter.js"); const { appendText, createChild, } = require("resource://devtools/client/inspector/shared/utils.js"); const INDENT_SIZE = 2; const INDENT_STR = " ".repeat(INDENT_SIZE); /** * RegisteredPropertyEditor creates a list of TextPropertyEditors for a given * CSS registered property propertyDefinition that can be rendered in the Rules view. * * @param {CssRuleView} ruleView * The CssRuleView containg the document holding this rule editor. * @param {Rule} rule * The Rule object we're editing. */ class RegisteredPropertyEditor extends EventEmitter { /** * @param {CssRuleView} ruleView * The CssRuleView containing the document holding this rule editor. * @param {object} propertyDefinition * The property definition data as returned by PageStyleActor's getRegisteredProperties */ constructor(ruleView, propertyDefinition) { super(); this.#doc = ruleView.styleDocument; this.#propertyDefinition = propertyDefinition; this.#createElement(); } #doc; #propertyDefinition; // The HTMLElement that will represent the registered property. Populated in #createElement. element = null; #createElement() { this.element = this.#doc.createElement("div"); this.element.className = "ruleview-rule devtools-monospace"; this.element.setAttribute("uneditable", true); this.element.setAttribute("unmatched", false); this.element.setAttribute("data-name", this.#propertyDefinition.name); // Give a relative position for the inplace editor's measurement // span to be placed absolutely against. this.element.style.position = "relative"; const code = createChild(this.element, "code", { class: "ruleview-code", }); const header = createChild(code, "header", {}); this.propertyName = createChild(header, "span", { class: "ruleview-registered-property-name", textContent: this.#propertyDefinition.name, }); this.openBrace = createChild(header, "span", { class: "ruleview-ruleopen", textContent: " {", }); // We can't use a proper "ol" as it will mess with selection copy text, // adding spaces on list item instead of the one we craft (.ruleview-rule-indent) this.propertyList = createChild(code, "div", { class: "ruleview-propertylist", role: "list", }); this.#populateProperties(); this.closeBrace = createChild(code, "div", { class: "ruleview-ruleclose", textContent: "}", }); } /** * Sets the content of this.#propertyList with the contents of the registered property . */ #populateProperties() { const properties = [ { name: "syntax", value: `"${this.#propertyDefinition.syntax}"`, }, { name: "inherits", value: this.#propertyDefinition.inherits, }, ]; // The initial value may not be set, when syntax is "*", so let's only display // it when it is actually set. if (this.#propertyDefinition.initialValue !== null) { // For JS-defined properties, we want to display them in the same syntax that // was used in CSS.registerProperty (so we'll show `initialValue` and not `initial-value`). properties.push({ name: this.#propertyDefinition.fromJS ? "initialValue" : "initial-value", value: this.#propertyDefinition.fromJS ? `"${this.#propertyDefinition.initialValue}"` : this.#propertyDefinition.initialValue, }); } // When the property is registered with CSS.registerProperty, we want to match the // object shape of the parameter, so include the "name" property. if (this.#propertyDefinition.fromJS) { properties.unshift({ name: "name", value: `"${this.#propertyDefinition.name}"`, }); } for (const { name, value } of properties) { // XXX: We could use the TextPropertyEditor here. // Pros: // - we'd get the similar markup, so styling would be easier // - the value would be properly parsed so our various swatches and popups would work // out of the box // - rule view filtering would also work out of the box // Cons: // - it is quite tied with the Rules view regular rule, which mean we'd have // to modify it to accept registered properties. const element = createChild(this.propertyList, "div", { role: "listitem", }); const container = createChild(element, "div", { class: "ruleview-propertycontainer", }); createChild(container, "span", { class: "ruleview-rule-indent clipboard-only", textContent: INDENT_STR, }); const nameContainer = createChild(container, "span", { class: "ruleview-namecontainer", }); createChild(nameContainer, "span", { class: "ruleview-propertyname theme-fg-color3", textContent: name, }); appendText(nameContainer, ": "); // Create a span that will hold the property and semicolon. // Use this span to create a slightly larger click target // for the value. const valueContainer = createChild(container, "span", { class: "ruleview-propertyvaluecontainer", }); // Property value, editable when focused. Changes to the // property value are applied as they are typed, and reverted // if the user presses escape. createChild(valueContainer, "span", { class: "ruleview-propertyvalue theme-fg-color1", textContent: value, }); appendText(valueContainer, this.#propertyDefinition.fromJS ? "," : ";"); this.propertyList.appendChild(element); } } } module.exports = RegisteredPropertyEditor;