commit 16b534c337a94be8bc0b8bc3758eaf8cbb4318cd
parent 50e1dbc65f81cd314170888bdb8e193d949a0434
Author: Sandor Molnar <smolnar@mozilla.com>
Date: Sat, 20 Dec 2025 20:13:51 +0200
Revert "Bug 2000644 - Introduce a superclass of color picker to prepare for multiple subclasses r=devtools-reviewers,nchevobbe,desktop-theme-reviewers,frontend-codestyle-reviewers,hjones" for causing dt failures @ browser_spectrum.js
This reverts commit d2bbd50a608074359ef8ade15948b2d270c05e16.
Diffstat:
9 files changed, 524 insertions(+), 573 deletions(-)
diff --git a/.stylelintrc.js b/.stylelintrc.js
@@ -421,7 +421,6 @@ module.exports = {
// Testing does not use design tokens
"testing/**",
// UA Widgets should not use design tokens
- "toolkit/themes/shared/colorpicker-common.css",
"toolkit/themes/shared/media/pipToggle.css",
"toolkit/themes/shared/media/videocontrols.css",
"toolkit/content/widgets/datetimebox.css",
diff --git a/devtools/client/shared/test/browser_spectrum.js b/devtools/client/shared/test/browser_spectrum.js
@@ -192,8 +192,8 @@ async function testChangingColorShouldEmitEvents(container, doc) {
);
testChangingColorShouldEmitEventsHelper(s, sendDownKey, [125, 62, 62, 1]);
testChangingColorShouldEmitEventsHelper(s, sendLeftKey, [125, 63, 63, 1]);
- testChangingColorShouldEmitEventsHelper(s, sendUpKey, [127, 64, 64, 1]);
- testChangingColorShouldEmitEventsHelper(s, sendRightKey, [128, 63, 63, 1]);
+ testChangingColorShouldEmitEventsHelper(s, sendUpKey, [128, 64, 64, 1]);
+ testChangingColorShouldEmitEventsHelper(s, sendRightKey, [127, 63, 63, 1]);
info(
"Test that moving the hue slider with arrow keys emits color changed event."
diff --git a/devtools/client/shared/widgets/Spectrum.js b/devtools/client/shared/widgets/Spectrum.js
@@ -4,10 +4,6 @@
"use strict";
-const { ColorPickerCommon } = ChromeUtils.importESModule(
- "chrome://global/content/bindings/colorpicker-common.mjs"
-);
-
const EventEmitter = require("resource://devtools/shared/event-emitter.js");
const {
MultiLocalizationHelper,
@@ -27,7 +23,21 @@ const L10N = new MultiLocalizationHelper(
"devtools/client/locales/accessibility.properties",
"devtools/client/locales/inspector.properties"
);
+const ARROW_KEYS = ["ArrowUp", "ArrowRight", "ArrowDown", "ArrowLeft"];
+const [ArrowUp, ArrowRight, ArrowDown, ArrowLeft] = ARROW_KEYS;
const XHTML_NS = "http://www.w3.org/1999/xhtml";
+const SLIDER = {
+ hue: {
+ MIN: "0",
+ MAX: "128",
+ STEP: "1",
+ },
+ alpha: {
+ MIN: "0",
+ MAX: "1",
+ STEP: "0.01",
+ },
+};
/**
* Spectrum creates a color picker widget in any container you give it.
@@ -52,15 +62,22 @@ const XHTML_NS = "http://www.w3.org/1999/xhtml";
* Fires the following events:
* - changed : When the user changes the current color
*/
-class Spectrum extends ColorPickerCommon {
+class Spectrum {
constructor(parentEl, rgb) {
- const element = parentEl.ownerDocument.createElement("div");
+ EventEmitter.decorate(this);
+
+ this.document = parentEl.ownerDocument;
+ this.element = parentEl.ownerDocument.createElementNS(XHTML_NS, "div");
+ this.parentEl = parentEl;
+
+ this.element.className = "spectrum-container";
// eslint-disable-next-line no-unsanitized/property
- element.innerHTML = `
+ this.element.innerHTML = `
<section class="spectrum-color-picker">
<div class="spectrum-color spectrum-box"
tabindex="0"
role="slider"
+ title="${ColorPickerBundle.formatValueSync("colorpicker-tooltip-spectrum-dragger-title")}"
aria-describedby="spectrum-dragger">
<div class="spectrum-sat">
<div class="spectrum-val">
@@ -94,10 +111,20 @@ class Spectrum extends ColorPickerCommon {
</div>
</section>
`;
- super(element);
- EventEmitter.decorate(this);
- parentEl.appendChild(this.element);
+ this.onElementClick = this.onElementClick.bind(this);
+ this.element.addEventListener("click", this.onElementClick);
+
+ this.parentEl.appendChild(this.element);
+
+ // Color spectrum dragger.
+ this.dragger = this.element.querySelector(".spectrum-color");
+ this.dragHelper = this.element.querySelector(".spectrum-dragger");
+ draggable(this.dragger, this.dragHelper, this.onDraggerMove.bind(this));
+
+ // Here we define the components for the "controls" section of the color picker.
+ this.controls = this.element.querySelector(".spectrum-controls");
+ this.colorPreview = this.element.querySelector(".spectrum-color-preview");
// Create the eyedropper.
const eyedropper = this.document.createElementNS(XHTML_NS, "button");
@@ -110,6 +137,14 @@ class Spectrum extends ColorPickerCommon {
);
this.controls.insertBefore(eyedropper, this.colorPreview);
+ // Hue slider and alpha slider
+ this.hueSlider = this.createSlider("hue", this.onHueSliderMove.bind(this));
+ this.hueSlider.setAttribute("aria-describedby", this.dragHelper.id);
+ this.alphaSlider = this.createSlider(
+ "alpha",
+ this.onAlphaSliderMove.bind(this)
+ );
+
// Color contrast
this.spectrumContrast = this.element.querySelector(
".spectrum-color-contrast"
@@ -143,6 +178,11 @@ class Spectrum extends ColorPickerCommon {
: null;
}
+ /** @param {[number, number, number, number]} color */
+ set rgb([r, g, b, a]) {
+ this.hsv = [...InspectorUtils.rgbToHsv(r / 255, g / 255, b / 255), a];
+ }
+
set backgroundColorData(colorData) {
this._backgroundColorData = colorData;
}
@@ -155,11 +195,117 @@ class Spectrum extends ColorPickerCommon {
return this._textProps;
}
+ #toRgbInt(rgbFloat) {
+ return rgbFloat.map(c => Math.round(c * 255));
+ }
+
+ get rgbFloat() {
+ const [h, s, v, a] = this.hsv;
+ return [...InspectorUtils.hsvToRgb(h, s, v), a];
+ }
+
+ get rgb() {
+ const [r, g, b, a] = this.rgbFloat;
+ return [...this.#toRgbInt([r, g, b]), a];
+ }
+
+ /**
+ * Map current rgb to the closest color available in the database by
+ * calculating the delta-E between each available color and the current rgb
+ *
+ * @return {string}
+ * Color name or closest color name
+ */
+ get colorName() {
+ const [r, g, b] = this.rgbFloat;
+ const { exact, colorName } = InspectorUtils.rgbToNearestColorName(r, g, b);
+ return exact
+ ? colorName
+ : ColorPickerBundle.formatValueSync(
+ "colorpicker-tooltip-color-name-title",
+ { colorName }
+ );
+ }
+
+ get rgbNoSatVal() {
+ return [
+ ...this.#toRgbInt(InspectorUtils.hsvToRgb(this.hsv[0], 1, 1)),
+ this.hsv[3],
+ ];
+ }
+
+ get rgbCssString() {
+ const rgb = this.rgb;
+ return (
+ "rgba(" + rgb[0] + ", " + rgb[1] + ", " + rgb[2] + ", " + rgb[3] + ")"
+ );
+ }
+
+ show() {
+ this.dragWidth = this.dragger.offsetWidth;
+ this.dragHeight = this.dragger.offsetHeight;
+ this.dragHelperHeight = this.dragHelper.offsetHeight;
+
+ this.updateUI();
+ }
+
+ onElementClick(e) {
+ e.stopPropagation();
+ }
+
+ onHueSliderMove() {
+ this.hsv[0] = this.hueSlider.value / this.hueSlider.max;
+ this.updateUI();
+ this.onChange();
+ }
+
+ onDraggerMove(dragX, dragY) {
+ this.hsv[1] = dragX / this.dragWidth;
+ this.hsv[2] = (this.dragHeight - dragY) / this.dragHeight;
+ this.updateUI();
+ this.onChange();
+ }
+
+ onAlphaSliderMove() {
+ this.hsv[3] = this.alphaSlider.value / this.alphaSlider.max;
+ this.updateUI();
+ this.onChange();
+ }
+
onChange() {
this.emit("changed", this.rgb, this.rgbCssString);
}
/**
+ * Creates and initializes a slider element, attaches it to its parent container
+ * based on the slider type and returns it
+ *
+ * @param {string} sliderType
+ * The type of the slider (i.e. alpha or hue)
+ * @param {Function} onSliderMove
+ * The function to tie the slider to on input
+ * @return {DOMNode}
+ * Newly created slider
+ */
+ createSlider(sliderType, onSliderMove) {
+ const container = this.element.querySelector(`.spectrum-${sliderType}`);
+
+ const slider = this.document.createElementNS(XHTML_NS, "input");
+ slider.className = `spectrum-${sliderType}-input`;
+ slider.type = "range";
+ slider.min = SLIDER[sliderType].MIN;
+ slider.max = SLIDER[sliderType].MAX;
+ slider.step = SLIDER[sliderType].STEP;
+ slider.title = ColorPickerBundle.formatValueSync(
+ `colorpicker-tooltip-${sliderType}-slider-title`
+ );
+ slider.addEventListener("input", onSliderMove);
+
+ container.appendChild(slider);
+ return slider;
+ }
+
+ /**
* Updates the contrast label with appropriate content (i.e. large text indicator
* if the contrast is calculated for large text, or a base label otherwise)
*
@@ -228,6 +374,81 @@ class Spectrum extends ColorPickerCommon {
);
}
+ updateAlphaSlider() {
+ // Set alpha slider background
+ const rgb = this.rgb;
+
+ const rgbNoAlpha = "rgb(" + rgb[0] + "," + rgb[1] + "," + rgb[2] + ")";
+ const rgbAlpha0 = "rgba(" + rgb[0] + "," + rgb[1] + "," + rgb[2] + ", 0)";
+ const alphaGradient =
+ "linear-gradient(to right, " + rgbAlpha0 + ", " + rgbNoAlpha + ")";
+ this.alphaSlider.style.background = alphaGradient;
+ }
+
+ updateColorPreview() {
+ // Overlay the rgba color over a checkered image background.
+ this.colorPreview.style.setProperty("--overlay-color", this.rgbCssString);
+
+ // We should be able to distinguish the color preview on high luminance rgba values.
+ // Give the color preview a light grey border if the luminance of the current rgba
+ // tuple is great.
+ const colorLuminance = InspectorUtils.relativeLuminance(...this.rgbFloat);
+ this.colorPreview.classList.toggle("high-luminance", colorLuminance > 0.85);
+
+ // Set title on color preview for better UX
+ this.colorPreview.title = this.colorName;
+ }
+
+ updateDragger() {
+ // Set dragger background color
+ const flatColor =
+ "rgb(" +
+ this.rgbNoSatVal[0] +
+ ", " +
+ this.rgbNoSatVal[1] +
+ ", " +
+ this.rgbNoSatVal[2] +
+ ")";
+ this.dragger.style.backgroundColor = flatColor;
+
+ // Set dragger aria attributes
+ this.dragger.setAttribute("aria-valuetext", this.rgbCssString);
+ }
+
+ updateHueSlider() {
+ // Set hue slider aria attributes
+ this.hueSlider.setAttribute("aria-valuetext", this.rgbCssString);
+ }
+
+ updateHelperLocations() {
+ const h = this.hsv[0];
+ const s = this.hsv[1];
+ const v = this.hsv[2];
+
+ // Placing the color dragger
+ let dragX = s * this.dragWidth;
+ let dragY = this.dragHeight - v * this.dragHeight;
+ const helperDim = this.dragHelperHeight / 2;
+
+ dragX = Math.max(
+ -helperDim,
+ Math.min(this.dragWidth - helperDim, dragX - helperDim)
+ );
+ dragY = Math.max(
+ -helperDim,
+ Math.min(this.dragHeight - helperDim, dragY - helperDim)
+ );
+
+ this.dragHelper.style.top = dragY + "px";
+ this.dragHelper.style.left = dragX + "px";
+
+ // Placing the hue slider
+ this.hueSlider.value = h * this.hueSlider.max;
+
+ // Placing the alpha slider
+ this.alphaSlider.value = this.hsv[3] * this.alphaSlider.max;
+ }
+
/* Calculates the contrast ratio for the currently selected
* color against a single or range of background colors and displays contrast ratio section
* components depending on the contrast ratio calculated.
@@ -324,18 +545,128 @@ class Spectrum extends ColorPickerCommon {
}
updateUI() {
- super.updateUI();
+ this.updateHelperLocations();
+
+ this.updateColorPreview();
+ this.updateDragger();
+ this.updateHueSlider();
+ this.updateAlphaSlider();
this.updateContrast();
}
destroy() {
- super.destroy();
+ this.element.removeEventListener("click", this.onElementClick);
+ this.hueSlider.removeEventListener("input", this.onHueSliderMove);
+ this.alphaSlider.removeEventListener("input", this.onAlphaSliderMove);
+
+ this.parentEl.removeChild(this.element);
+
+ this.dragger = this.dragHelper = null;
+ this.alphaSlider = null;
+ this.hueSlider = null;
+ this.colorPreview = null;
+ this.element = null;
+ this.parentEl = null;
this.spectrumContrast = null;
this.contrastValue = this.contrastValueMin = this.contrastValueMax = null;
this.contrastLabel = null;
}
}
+function draggable(element, dragHelper, onmove) {
+ onmove = onmove || function () {};
+
+ const doc = element.ownerDocument;
+ let dragging = false;
+ let offset = {};
+ let maxHeight = 0;
+ let maxWidth = 0;
+
+ function setDraggerDimensionsAndOffset() {
+ maxHeight = element.offsetHeight;
+ maxWidth = element.offsetWidth;
+ offset = element.getBoundingClientRect();
+ }
+
+ function prevent(e) {
+ e.stopPropagation();
+ e.preventDefault();
+ }
+
+ function move(e) {
+ if (dragging) {
+ if (e.buttons === 0) {
+ // The button is no longer pressed but we did not get a mouseup event.
+ stop();
+ return;
+ }
+ const pageX = e.pageX;
+ const pageY = e.pageY;
+
+ const dragX = Math.max(0, Math.min(pageX - offset.left, maxWidth));
+ const dragY = Math.max(0, Math.min(pageY - offset.top, maxHeight));
+
+ onmove.apply(element, [dragX, dragY]);
+ }
+ }
+
+ function start(e) {
+ const rightClick = e.which === 3;
+
+ if (!rightClick && !dragging) {
+ dragging = true;
+ setDraggerDimensionsAndOffset();
+
+ move(e);
+
+ doc.addEventListener("selectstart", prevent);
+ doc.addEventListener("dragstart", prevent);
+ doc.addEventListener("mousemove", move);
+ doc.addEventListener("mouseup", stop);
+
+ prevent(e);
+ }
+ }
+
+ function stop() {
+ if (dragging) {
+ doc.removeEventListener("selectstart", prevent);
+ doc.removeEventListener("dragstart", prevent);
+ doc.removeEventListener("mousemove", move);
+ doc.removeEventListener("mouseup", stop);
+ }
+ dragging = false;
+ }
+
+ function onKeydown(e) {
+ const { key } = e;
+
+ if (!ARROW_KEYS.includes(key)) {
+ return;
+ }
+
+ setDraggerDimensionsAndOffset();
+ const { offsetHeight, offsetTop, offsetLeft } = dragHelper;
+ let dragX = offsetLeft + offsetHeight / 2;
+ let dragY = offsetTop + offsetHeight / 2;
+
+ if (key === ArrowLeft && dragX > 0) {
+ dragX -= 1;
+ } else if (key === ArrowRight && dragX < maxWidth) {
+ dragX += 1;
+ } else if (key === ArrowUp && dragY > 0) {
+ dragY -= 1;
+ } else if (key === ArrowDown && dragY < maxHeight) {
+ dragY += 1;
+ }
+
+ onmove.apply(element, [dragX, dragY]);
+ }
+
+ element.addEventListener("mousedown", start);
+ element.addEventListener("keydown", onKeydown);
+}
+
/**
* Calculates the contrast ratio for a DOM node's computed style against
* a given background.
diff --git a/devtools/client/shared/widgets/spectrum.css b/devtools/client/shared/widgets/spectrum.css
@@ -2,8 +2,6 @@
* 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/. */
-@import "chrome://global/skin/colorpicker-common.css";
-
:root {
--accessibility-contrast-swatch-border-color: var(--grey-40);
--learn-more-underline: light-dark(var(--grey-30), var(--grey-50));
@@ -29,10 +27,45 @@
/* Mix-in classes */
+.spectrum-checker {
+ background-color: #eee;
+ background-image:
+ linear-gradient(45deg, #ccc 25%, transparent 25%, transparent 75%, #ccc 75%, #ccc),
+ linear-gradient(45deg, #ccc 25%, transparent 25%, transparent 75%, #ccc 75%, #ccc);
+ background-size: 12px 12px;
+ background-position:
+ 0 0,
+ 6px 6px;
+ /* Make sure that the background color is properly set in High Contrast Mode */
+ forced-color-adjust: none;
+}
+
+.spectrum-box {
+ border: 1px solid rgba(0, 0, 0, 0.2);
+ border-radius: 2px;
+ background-clip: content-box;
+
+ :root[forced-colors-active] & {
+ border-color: initial;
+ }
+}
+
+/* Elements */
+
#spectrum-tooltip {
padding: 5px;
}
+/**
+ * Spectrum controls set the layout for the controls section of the color picker.
+ */
+.spectrum-controls {
+ display: flex;
+ justify-content: space-between;
+ margin-block-start: 10px;
+ margin-inline-end: 5px;
+}
+
.spectrum-controls {
width: 200px;
}
@@ -44,6 +77,147 @@
padding-block-end: 6px;
}
+/**
+ * This styles the color preview and adds a checkered background overlay inside of it. The overlay
+ * can be manipulated using the --overlay-color variable.
+ */
+.spectrum-color-preview {
+ --overlay-color: transparent;
+ border: 1px solid transparent;
+ border-radius: 50%;
+ width: 27px;
+ height: 27px;
+ background-color: #fff;
+ background-image:
+ linear-gradient(var(--overlay-color), var(--overlay-color)), linear-gradient(45deg, #ccc 25%, transparent 25%, transparent 75%, #ccc 75%),
+ linear-gradient(45deg, #ccc 25%, transparent 25%, transparent 75%, #ccc 75%);
+ background-size: 12px 12px;
+ background-position:
+ 0 0,
+ 6px 6px;
+ /* Make sure that the background color is properly set in High Contrast Mode */
+ forced-color-adjust: none;
+
+ :root[forced-colors-active] & {
+ border-color: CanvasText;
+ }
+}
+
+.spectrum-color-preview.high-luminance {
+ border-color: #ccc;
+}
+
+.spectrum-slider-container {
+ display: flex;
+ flex-direction: column;
+ justify-content: space-around;
+ width: 130px;
+ margin-inline-start: 10px;
+ height: 30px;
+}
+
+/* Keep aspect ratio:
+http://www.briangrinstead.com/blog/keep-aspect-ratio-with-html-and-css */
+.spectrum-color-picker {
+ position: relative;
+ width: 205px;
+ height: 120px;
+ /* Make sure that the background color is properly set in High Contrast Mode */
+ forced-color-adjust: none;
+}
+
+.spectrum-color {
+ position: absolute;
+ top: 0;
+ left: 0;
+ bottom: 0;
+ width: 100%;
+}
+
+.spectrum-sat,
+.spectrum-val {
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+}
+
+.spectrum-alpha {
+ margin-block-start: 3px;
+}
+
+.spectrum-alpha,
+.spectrum-hue {
+ position: relative;
+ height: 8px;
+}
+
+.spectrum-alpha-input,
+.spectrum-hue-input {
+ width: 100%;
+ margin: 0;
+ position: absolute;
+ height: 8px;
+ border-radius: 2px;
+ direction: initial;
+}
+
+.spectrum-hue-input,
+.spectrum-alpha-input {
+ outline-offset: 4px;
+}
+
+.spectrum-hue-input::-moz-range-thumb,
+.spectrum-alpha-input::-moz-range-thumb {
+ cursor: pointer;
+ height: 12px;
+ width: 12px;
+ box-shadow: 0 0 2px rgba(0, 0, 0, 0.6);
+ background: #fff;
+ border-radius: 50%;
+ opacity: 0.9;
+ border: none;
+}
+
+:root[forced-colors-active] :is(.spectrum-hue-input, .spectrum-alpha-input)::-moz-range-thumb {
+ background: ButtonFace;
+ border: 2px solid ButtonText;
+}
+
+:root[forced-colors-active] :is(.spectrum-hue-input, .spectrum-alpha-input):is(:hover, :focus-visible)::-moz-range-thumb {
+ border-color: SelectedItem;
+}
+
+.spectrum-hue-input::-moz-range-track {
+ border-radius: 2px;
+ height: 8px;
+ background: linear-gradient(to right, #ff0000 0%, #ffff00 17%, #00ff00 33%, #00ffff 50%, #0000ff 67%, #ff00ff 83%, #ff0000 100%);
+ /* Make sure that the background color is properly set in High Contrast Mode */
+ forced-color-adjust: none;
+}
+
+.spectrum-sat {
+ background-image: linear-gradient(to right, #fff, rgba(204, 154, 129, 0));
+}
+
+.spectrum-val {
+ background-image: linear-gradient(to top, #000000, rgba(204, 154, 129, 0));
+}
+
+.spectrum-dragger {
+ user-select: none;
+ position: absolute;
+ top: 0;
+ left: 0;
+ cursor: pointer;
+ border-radius: 50%;
+ height: 8px;
+ width: 8px;
+ border: 1px solid white;
+ box-shadow: 0 0 2px rgba(0, 0, 0, 0.6);
+}
+
.spectrum-color-contrast {
padding-block-start: 8px;
padding-inline-start: 4px;
diff --git a/devtools/client/shared/widgets/tooltip/SwatchColorPickerTooltip.js b/devtools/client/shared/widgets/tooltip/SwatchColorPickerTooltip.js
@@ -189,6 +189,9 @@ class SwatchColorPickerTooltip extends SwatchBasedEditorTooltip {
learnMoreButton.addEventListener("keydown", e => e.stopPropagation());
}
+ // Add focus to the first focusable element in the tooltip and attach keydown
+ // event listener to tooltip
+ this.focusableElements[0].focus();
this.tooltip.container.addEventListener(
"keydown",
this._onTooltipKeydown,
diff --git a/toolkit/content/jar.mn b/toolkit/content/jar.mn
@@ -77,7 +77,6 @@ toolkit.jar:
#endif
content/global/widgets.css
content/global/bindings/calendar.js (widgets/calendar.js)
- content/global/bindings/colorpicker-common.mjs (widgets/colorpicker-common.mjs)
content/global/bindings/datekeeper.js (widgets/datekeeper.js)
content/global/bindings/datepicker.js (widgets/datepicker.js)
content/global/bindings/datetimebox.css (widgets/datetimebox.css)
diff --git a/toolkit/content/widgets/colorpicker-common.mjs b/toolkit/content/widgets/colorpicker-common.mjs
@@ -1,371 +0,0 @@
-/* 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/. */
-
-// @ts-nocheck Do this after migration from devtools
-
-const lazy = {};
-ChromeUtils.defineLazyGetter(lazy, "l10n", function () {
- return new Localization(["devtools/client/inspector.ftl"], true);
-});
-
-const ARROW_KEYS = ["ArrowUp", "ArrowRight", "ArrowDown", "ArrowLeft"];
-const [ArrowUp, ArrowRight, ArrowDown, ArrowLeft] = ARROW_KEYS;
-const SLIDER = {
- hue: {
- MIN: "0",
- MAX: "128",
- STEP: "1",
- },
- alpha: {
- MIN: "0",
- MAX: "1",
- STEP: "0.01",
- },
-};
-
-/**
- * ColorPickerCommon creates a color picker widget in a container you give it.
- */
-export class ColorPickerCommon {
- constructor(element) {
- this.document = element.ownerDocument;
- this.element = element;
-
- this.element.className = "spectrum-container";
-
- this.onElementClick = this.onElementClick.bind(this);
- this.element.addEventListener("click", this.onElementClick);
-
- // Color spectrum dragger.
- this.dragger = this.element.querySelector(".spectrum-color");
- this.dragger.title = lazy.l10n.formatValueSync(
- "colorpicker-tooltip-spectrum-dragger-title"
- );
-
- this.dragHelper = this.element.querySelector(".spectrum-dragger");
- draggable(this.dragger, this.dragHelper, this.onDraggerMove.bind(this));
-
- // Here we define the components for the "controls" section of the color picker.
- this.controls = this.element.querySelector(".spectrum-controls");
- this.colorPreview = this.element.querySelector(".spectrum-color-preview");
-
- // Hue slider and alpha slider
- this.hueSlider = this.createSlider("hue", this.onHueSliderMove.bind(this));
- this.hueSlider.setAttribute("aria-describedby", this.dragHelper.id);
- this.alphaSlider = this.createSlider(
- "alpha",
- this.onAlphaSliderMove.bind(this)
- );
- }
-
- /** @param {[number, number, number, number]} color */
- set rgb([r, g, b, a]) {
- this.rgbFloat = [r / 255, g / 255, b / 255, a];
- }
-
- /** @param {[number, number, number, number]} color */
- set rgbFloat([r, g, b, a]) {
- this.hsv = [...InspectorUtils.rgbToHsv(r, g, b), a];
- }
-
- #toRgbInt(rgbFloat) {
- return rgbFloat.map(c => Math.round(c * 255));
- }
-
- get rgbFloat() {
- const [h, s, v, a] = this.hsv;
- return [...InspectorUtils.hsvToRgb(h, s, v), a];
- }
-
- get rgb() {
- const [r, g, b, a] = this.rgbFloat;
- return [...this.#toRgbInt([r, g, b]), a];
- }
-
- /**
- * Map current rgb to the closest color available in the database by
- * calculating the delta-E between each available color and the current rgb
- *
- * @return {string}
- * Color name or closest color name
- */
- get colorName() {
- const [r, g, b] = this.rgbFloat;
- const { exact, colorName } = InspectorUtils.rgbToNearestColorName(r, g, b);
- return exact
- ? colorName
- : lazy.l10n.formatValueSync("colorpicker-tooltip-color-name-title", {
- colorName,
- });
- }
-
- get rgbNoSatVal() {
- return [
- ...this.#toRgbInt(InspectorUtils.hsvToRgb(this.hsv[0], 1, 1)),
- this.hsv[3],
- ];
- }
-
- get rgbCssString() {
- const rgb = this.rgb;
- return (
- "rgba(" + rgb[0] + ", " + rgb[1] + ", " + rgb[2] + ", " + rgb[3] + ")"
- );
- }
-
- show() {
- this.dragWidth = this.dragger.offsetWidth;
- this.dragHeight = this.dragger.offsetHeight;
- this.dragHelperHeight = this.dragHelper.offsetHeight;
- this.dragger.focus({ focusVisible: false });
-
- this.updateUI();
- }
-
- onElementClick(e) {
- e.stopPropagation();
- }
-
- onHueSliderMove() {
- this.hsv[0] = this.hueSlider.value / this.hueSlider.max;
- this.updateUI();
- this.onChange();
- }
-
- onDraggerMove(dragX, dragY) {
- this.hsv[1] = dragX / this.dragWidth;
- this.hsv[2] = (this.dragHeight - dragY) / this.dragHeight;
- this.updateUI();
- this.onChange();
- }
-
- onAlphaSliderMove() {
- this.hsv[3] = this.alphaSlider.value / this.alphaSlider.max;
- this.updateUI();
- this.onChange();
- }
-
- onChange() {
- throw new Error("Not implemented");
- }
-
- /**
- * Creates and initializes a slider element, attaches it to its parent container
- * based on the slider type and returns it
- *
- * @param {"alpha" | "hue"} sliderType
- * The type of the slider (i.e. alpha or hue)
- * @param {Function} onSliderMove
- * The function to tie the slider to on input
- * @return {HTMLInputElement}
- * Newly created slider
- */
- createSlider(sliderType, onSliderMove) {
- const container = this.element.querySelector(`.spectrum-${sliderType}`);
-
- const slider = this.document.createElement("input");
- slider.className = `spectrum-${sliderType}-input`;
- slider.type = "range";
- slider.min = SLIDER[sliderType].MIN;
- slider.max = SLIDER[sliderType].MAX;
- slider.step = SLIDER[sliderType].STEP;
- slider.title = lazy.l10n.formatValueSync(
- `colorpicker-tooltip-${sliderType}-slider-title`
- );
- slider.addEventListener("input", onSliderMove);
-
- container.appendChild(slider);
- return slider;
- }
-
- updateAlphaSlider() {
- // Set alpha slider background
- const rgb = this.rgb;
-
- const rgbNoAlpha = "rgb(" + rgb[0] + "," + rgb[1] + "," + rgb[2] + ")";
- const rgbAlpha0 = "rgba(" + rgb[0] + "," + rgb[1] + "," + rgb[2] + ", 0)";
- const alphaGradient =
- "linear-gradient(to right, " + rgbAlpha0 + ", " + rgbNoAlpha + ")";
- this.alphaSlider.style.background = alphaGradient;
- }
-
- updateColorPreview() {
- // Overlay the rgba color over a checkered image background.
- this.colorPreview.style.setProperty("--overlay-color", this.rgbCssString);
-
- // We should be able to distinguish the color preview on high luminance rgba values.
- // Give the color preview a light grey border if the luminance of the current rgba
- // tuple is great.
- const colorLuminance = InspectorUtils.relativeLuminance(...this.rgbFloat);
- this.colorPreview.classList.toggle("high-luminance", colorLuminance > 0.85);
-
- // Set title on color preview for better UX
- this.colorPreview.title = this.colorName;
- }
-
- updateDragger() {
- // Set dragger background color
- const flatColor =
- "rgb(" +
- this.rgbNoSatVal[0] +
- ", " +
- this.rgbNoSatVal[1] +
- ", " +
- this.rgbNoSatVal[2] +
- ")";
- this.dragger.style.backgroundColor = flatColor;
-
- // Set dragger aria attributes
- this.dragger.setAttribute("aria-valuetext", this.rgbCssString);
- }
-
- updateHueSlider() {
- // Set hue slider aria attributes
- this.hueSlider.setAttribute("aria-valuetext", this.rgbCssString);
- }
-
- updateHelperLocations() {
- const h = this.hsv[0];
- const s = this.hsv[1];
- const v = this.hsv[2];
-
- // Placing the color dragger
- let dragX = s * this.dragWidth;
- let dragY = this.dragHeight - v * this.dragHeight;
- const helperDim = this.dragHelperHeight / 2;
-
- dragX = Math.max(
- -helperDim,
- Math.min(this.dragWidth - helperDim, dragX - helperDim)
- );
- dragY = Math.max(
- -helperDim,
- Math.min(this.dragHeight - helperDim, dragY - helperDim)
- );
-
- this.dragHelper.style.top = dragY + "px";
- this.dragHelper.style.left = dragX + "px";
-
- // Placing the hue slider
- this.hueSlider.value = h * this.hueSlider.max;
-
- // Placing the alpha slider
- this.alphaSlider.value = this.hsv[3] * this.alphaSlider.max;
- }
-
- updateUI() {
- this.updateHelperLocations();
-
- this.updateColorPreview();
- this.updateDragger();
- this.updateHueSlider();
- this.updateAlphaSlider();
- }
-
- destroy() {
- this.element.removeEventListener("click", this.onElementClick);
- this.hueSlider.removeEventListener("input", this.onHueSliderMove);
- this.alphaSlider.removeEventListener("input", this.onAlphaSliderMove);
-
- this.element.remove();
-
- this.dragger = this.dragHelper = null;
- this.alphaSlider = null;
- this.hueSlider = null;
- this.colorPreview = null;
- this.element = null;
- }
-}
-
-function draggable(element, dragHelper, onmove) {
- const doc = element.ownerDocument;
- let dragging = false;
- let offset = {};
- let maxHeight = 0;
- let maxWidth = 0;
-
- function setDraggerDimensionsAndOffset() {
- maxHeight = element.offsetHeight;
- maxWidth = element.offsetWidth;
- offset = element.getBoundingClientRect();
- }
-
- function prevent(e) {
- e.stopPropagation();
- e.preventDefault();
- }
-
- function move(e) {
- if (dragging) {
- if (e.buttons === 0) {
- // The button is no longer pressed but we did not get a pointerup event.
- stop();
- return;
- }
- const pageX = e.pageX;
- const pageY = e.pageY;
-
- const dragX = Math.max(0, Math.min(pageX - offset.left, maxWidth));
- const dragY = Math.max(0, Math.min(pageY - offset.top, maxHeight));
-
- onmove.apply(element, [dragX, dragY]);
- }
- }
-
- function start(e) {
- const rightClick = e.which === 3;
-
- if (!rightClick && !dragging) {
- dragging = true;
- setDraggerDimensionsAndOffset();
-
- move(e);
-
- doc.addEventListener("selectstart", prevent);
- doc.addEventListener("dragstart", prevent);
- doc.addEventListener("mousemove", move);
- doc.addEventListener("mouseup", stop);
-
- prevent(e);
- }
- }
-
- function stop() {
- if (dragging) {
- doc.removeEventListener("selectstart", prevent);
- doc.removeEventListener("dragstart", prevent);
- doc.removeEventListener("mousemove", move);
- doc.removeEventListener("mouseup", stop);
- }
- dragging = false;
- }
-
- function onKeydown(e) {
- const { key } = e;
-
- if (!ARROW_KEYS.includes(key)) {
- return;
- }
-
- setDraggerDimensionsAndOffset();
- const { offsetHeight, offsetTop, offsetLeft } = dragHelper;
- let dragX = offsetLeft + offsetHeight / 2;
- let dragY = offsetTop + offsetHeight / 2;
-
- if (key === ArrowLeft && dragX > 0) {
- dragX -= 1;
- } else if (key === ArrowRight && dragX < maxWidth) {
- dragX += 1;
- } else if (key === ArrowUp && dragY > 0) {
- dragY -= 1;
- } else if (key === ArrowDown && dragY < maxHeight) {
- dragY += 1;
- }
-
- onmove.apply(element, [dragX, dragY]);
- }
-
- element.addEventListener("mousedown", start);
- element.addEventListener("keydown", onKeydown);
-}
diff --git a/toolkit/themes/shared/colorpicker-common.css b/toolkit/themes/shared/colorpicker-common.css
@@ -1,183 +0,0 @@
-/* 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/. */
-
-/* Mix-in classes */
-
-.spectrum-checker {
- background-color: #eee;
- background-image:
- linear-gradient(45deg, #ccc 25%, transparent 25%, transparent 75%, #ccc 75%, #ccc),
- linear-gradient(45deg, #ccc 25%, transparent 25%, transparent 75%, #ccc 75%, #ccc);
- background-size: 12px 12px;
- background-position:
- 0 0,
- 6px 6px;
- /* Make sure that the background color is properly set in High Contrast Mode */
- forced-color-adjust: none;
-}
-
-.spectrum-box {
- background-clip: content-box;
-
- :root[forced-colors-active] & {
- border-color: initial;
- }
-}
-
-/* Elements */
-
-/**
- * Spectrum controls set the layout for the controls section of the color picker.
- */
-.spectrum-controls {
- display: flex;
- justify-content: space-between;
- align-items: center;
- margin-block-start: 10px;
-}
-
-/**
- * This styles the color preview and adds a checkered background overlay inside of it. The overlay
- * can be manipulated using the --overlay-color variable.
- */
-.spectrum-color-preview {
- --overlay-color: transparent;
- border: 1px solid transparent;
- border-radius: 50%;
- box-sizing: border-box;
- width: 25px;
- height: 25px;
- background-color: #fff;
- background-image:
- linear-gradient(var(--overlay-color), var(--overlay-color)), linear-gradient(45deg, #ccc 25%, transparent 25%, transparent 75%, #ccc 75%),
- linear-gradient(45deg, #ccc 25%, transparent 25%, transparent 75%, #ccc 75%);
- background-size: 12px 12px;
- background-position:
- 0 0,
- 6px 6px;
- /* Make sure that the background color is properly set in High Contrast Mode */
- forced-color-adjust: none;
-
- :root[forced-colors-active] & {
- border-color: CanvasText;
- }
-}
-
-.spectrum-color-preview.high-luminance {
- border-color: #ccc;
-}
-
-.spectrum-slider-container {
- display: flex;
- flex-direction: column;
- justify-content: space-around;
- flex: 1;
- margin-inline-start: 10px;
- height: 30px;
-}
-
-/* Keep aspect ratio:
-http://www.briangrinstead.com/blog/keep-aspect-ratio-with-html-and-css */
-.spectrum-color-picker {
- position: relative;
- border: 1px solid rgba(0, 0, 0, 0.2);
- border-radius: 2px;
- box-sizing: border-box;
- width: 205px;
- height: 120px;
- /* Make sure that the background color is properly set in High Contrast Mode */
- forced-color-adjust: none;
-}
-
-.spectrum-color {
- position: absolute;
- top: 0;
- left: 0;
- bottom: 0;
- width: 100%;
-}
-
-.spectrum-sat,
-.spectrum-val {
- position: absolute;
- top: 0;
- left: 0;
- right: 0;
- bottom: 0;
-}
-
-.spectrum-alpha {
- margin-block-start: 3px;
-}
-
-.spectrum-alpha,
-.spectrum-hue {
- position: relative;
- height: 8px;
-}
-
-.spectrum-alpha-input,
-.spectrum-hue-input {
- width: 100%;
- margin: 0;
- position: absolute;
- height: 8px;
- border-radius: 2px;
- direction: initial;
-}
-
-.spectrum-hue-input,
-.spectrum-alpha-input {
- outline-offset: 4px;
-}
-
-.spectrum-hue-input::-moz-range-thumb,
-.spectrum-alpha-input::-moz-range-thumb {
- cursor: pointer;
- height: 12px;
- width: 12px;
- box-shadow: 0 0 2px rgba(0, 0, 0, 0.6);
- background: #fff;
- border-radius: 50%;
- opacity: 0.9;
- border: none;
-}
-
-:root[forced-colors-active] :is(.spectrum-hue-input, .spectrum-alpha-input)::-moz-range-thumb {
- background: ButtonFace;
- border: 2px solid ButtonText;
-}
-
-:root[forced-colors-active] :is(.spectrum-hue-input, .spectrum-alpha-input):is(:hover, :focus-visible)::-moz-range-thumb {
- border-color: SelectedItem;
-}
-
-.spectrum-hue-input::-moz-range-track {
- border-radius: 2px;
- height: 8px;
- background: linear-gradient(to right, #ff0000 0%, #ffff00 17%, #00ff00 33%, #00ffff 50%, #0000ff 67%, #ff00ff 83%, #ff0000 100%);
- /* Make sure that the background color is properly set in High Contrast Mode */
- forced-color-adjust: none;
-}
-
-.spectrum-sat {
- background-image: linear-gradient(to right, #fff, rgba(204, 154, 129, 0));
-}
-
-.spectrum-val {
- background-image: linear-gradient(to top, #000000, rgba(204, 154, 129, 0));
-}
-
-.spectrum-dragger {
- user-select: none;
- position: absolute;
- top: 0;
- left: 0;
- cursor: pointer;
- border-radius: 50%;
- height: 8px;
- width: 8px;
- border: 1px solid white;
- box-shadow: 0 0 2px rgba(0, 0, 0, 0.6);
-}
diff --git a/toolkit/themes/shared/desktop-jar.inc.mn b/toolkit/themes/shared/desktop-jar.inc.mn
@@ -25,7 +25,6 @@
skin/classic/global/checkbox.css (../../shared/checkbox.css)
skin/classic/global/radio.css (../../shared/radio.css)
skin/classic/global/close-icon.css (../../shared/close-icon.css)
- skin/classic/global/colorpicker-common.css (../../shared/colorpicker-common.css)
skin/classic/global/commonDialog.css (../../shared/commonDialog.css)
skin/classic/global/datetimeinputpickers.css (../../shared/datetimeinputpickers.css)
skin/classic/global/design-system/text-and-typography.css(../../shared/design-system/src/text-and-typography.css)