/* * Copyright (C) 2013, 2015 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ WI.ColorPicker = class ColorPicker extends WI.Object { constructor() { super(); this._colorWheel = new WI.ColorWheel; this._colorWheel.delegate = this; this._colorWheel.dimension = 200; this._brightnessSlider = new WI.Slider; this._brightnessSlider.delegate = this; this._brightnessSlider.element.classList.add("brightness"); this._opacitySlider = new WI.Slider; this._opacitySlider.delegate = this; this._opacitySlider.element.classList.add("opacity"); let colorInputsContainerElement = document.createElement("div"); colorInputsContainerElement.classList.add("color-inputs"); function createColorInput(label, {min: min = 0, max: max = 100, step: step = 1, units} = {}) { let containerElement = colorInputsContainerElement.createChild("div"); containerElement.append(label); let numberInputElement = containerElement.createChild("input"); numberInputElement.type = "number"; numberInputElement.min = min; numberInputElement.max = max; numberInputElement.step = step; numberInputElement.addEventListener("input", this._handleColorInputInput.bind(this)); if (units && units.length) containerElement.append(units); return {containerElement, numberInputElement}; } this._colorInputs = new Map([ ["R", createColorInput.call(this, "R", {max: 255})], ["G", createColorInput.call(this, "G", {max: 255})], ["B", createColorInput.call(this, "B", {max: 255})], ["H", createColorInput.call(this, "H", {max: 360})], ["S", createColorInput.call(this, "S", {units: "%"})], ["L", createColorInput.call(this, "L", {units: "%"})], ["A", createColorInput.call(this, "A"), {max: 1, step: 0.01}] ]); this._element = document.createElement("div"); this._element.classList.add("color-picker"); this._element.appendChild(this._colorWheel.element); this._element.appendChild(this._brightnessSlider.element); this._element.appendChild(this._opacitySlider.element); this._element.appendChild(colorInputsContainerElement); this._opacity = 0; this._opacityPattern = "url(Images/Checkers.svg)"; this._color = WI.Color.fromString("white"); this._dontUpdateColor = false; this._enableColorComponentInputs = true; } // Public get element() { return this._element; } set brightness(brightness) { if (brightness === this._brightness) return; this._colorWheel.brightness = brightness; this._updateColor(); this._updateSliders(this._colorWheel.rawColor, this._colorWheel.tintedColor); } set opacity(opacity) { if (opacity === this._opacity) return; this._opacity = opacity; this._updateColor(); } get colorWheel() { return this._colorWheel; } get color() { return this._color; } set color(color) { console.assert(color instanceof WI.Color); this._dontUpdateColor = true; let formatChanged = !this._color || this._color.format !== color.format; this._color = color; this._colorWheel.tintedColor = this._color; this._brightnessSlider.value = this._colorWheel.brightness; this._opacitySlider.value = this._color.alpha; this._updateSliders(this._colorWheel.rawColor, this._color); this._showColorComponentInputs(); if (formatChanged) this._handleFormatChange(); this._dontUpdateColor = false; } set enableColorComponentInputs(value) { this._enableColorComponentInputs = value; this._showColorComponentInputs(); } colorWheelColorDidChange(colorWheel) { this._updateColor(); this._updateSliders(this._colorWheel.rawColor, this._colorWheel.tintedColor); } sliderValueDidChange(slider, value) { if (slider === this._opacitySlider) this.opacity = value; else if (slider === this._brightnessSlider) this.brightness = value; } // Private _updateColor() { if (this._dontUpdateColor) return; let opacity = Math.round(this._opacity * 100) / 100; let format = this._color.format; let components = null; if (format === WI.Color.Format.HSL || format === WI.Color.Format.HSLA) { components = this._colorWheel.tintedColor.hsl.concat(opacity); if (opacity !== 1) format = WI.Color.Format.HSLA; } else { components = this._colorWheel.tintedColor.rgb.concat(opacity); if (opacity !== 1 && format === WI.Color.Format.RGB) format = WI.Color.Format.RGBA; } let formatChanged = this._color.format === format; this._color = new WI.Color(format, components); this._showColorComponentInputs(); this.dispatchEventToListeners(WI.ColorPicker.Event.ColorChanged, {color: this._color}); if (formatChanged) this._handleFormatChange(); } _updateSliders(rawColor, tintedColor) { var rgb = this._colorWheel.tintedColor.rgb; var opaque = new WI.Color(WI.Color.Format.RGBA, rgb.concat(1)).toString(); var transparent = new WI.Color(WI.Color.Format.RGBA, rgb.concat(0)).toString(); this._opacitySlider.element.style.backgroundImage = "linear-gradient(90deg, " + transparent + ", " + opaque + "), " + this._opacityPattern; this._brightnessSlider.element.style.backgroundImage = "linear-gradient(90deg, black, " + rawColor + ")"; } _handleFormatChange() { this._element.classList.toggle("hide-inputs", this._color.format !== WI.Color.Format.Keyword && this._color.format !== WI.Color.Format.RGB && this._color.format !== WI.Color.Format.RGBA && this._color.format !== WI.Color.Format.HEX && this._color.format !== WI.Color.Format.ShortHEX && this._color.format !== WI.Color.Format.HEXAlpha && this._color.format !== WI.Color.Format.ShortHEXAlpha && this._color.format !== WI.Color.Format.HSL && this._color.format !== WI.Color.Format.HSLA); this.dispatchEventToListeners(WI.ColorPicker.Event.FormatChanged); } _showColorComponentInputs() { for (let {containerElement} of this._colorInputs.values()) containerElement.hidden = true; if (!this._enableColorComponentInputs) return; function updateColorInput(key, value) { let {containerElement, numberInputElement} = this._colorInputs.get(key); numberInputElement.value = value; containerElement.hidden = false; } switch (this._color.format) { case WI.Color.Format.RGB: case WI.Color.Format.RGBA: case WI.Color.Format.HEX: case WI.Color.Format.ShortHEX: case WI.Color.Format.HEXAlpha: case WI.Color.Format.ShortHEXAlpha: case WI.Color.Format.Keyword: var [r, g, b] = this._color.rgb; updateColorInput.call(this, "R", r); updateColorInput.call(this, "G", g); updateColorInput.call(this, "B", b); break; case WI.Color.Format.HSL: case WI.Color.Format.HSLA: var [h, s, l] = this._color.hsl; updateColorInput.call(this, "H", h); updateColorInput.call(this, "S", s); updateColorInput.call(this, "L", l); break; default: return; } if ((this._color.format === WI.Color.Format.Keyword && this._color.alpha !== 1) || this._color.format === WI.Color.Format.RGBA || this._color.format === WI.Color.Format.HSLA || this._color.format === WI.Color.Format.HEXAlpha || this._color.format === WI.Color.Format.ShortHEXAlpha) { updateColorInput.call(this, "A", this._color.alpha); } } _handleColorInputInput(event) { if (!this._enableColorComponentInputs) { WI.reportInternalError("Input event fired for disabled color component input"); return; } let r = this._colorInputs.get("R").numberInputElement.value; let g = this._colorInputs.get("G").numberInputElement.value; let b = this._colorInputs.get("B").numberInputElement.value; let h = this._colorInputs.get("H").numberInputElement.value; let s = this._colorInputs.get("S").numberInputElement.value; let l = this._colorInputs.get("L").numberInputElement.value; let a = this._colorInputs.get("A").numberInputElement.value; let colorString = ""; let oldFormat = this._color.format; switch (oldFormat) { case WI.Color.Format.RGB: case WI.Color.Format.HEX: case WI.Color.Format.ShortHEX: case WI.Color.Format.Keyword: colorString = `rgb(${r}, ${g}, ${b})`; break; case WI.Color.Format.RGBA: case WI.Color.Format.HEXAlpha: case WI.Color.Format.ShortHEXAlpha: colorString = `rgba(${r}, ${g}, ${b}, ${a})`; break; case WI.Color.Format.HSL: colorString = `hsl(${h}, ${s}%, ${l}%)`; break; case WI.Color.Format.HSLA: colorString = `hsla(${h}, ${s}%, ${l}%, ${a})`; break; default: WI.reportInternalError(`Input event fired for invalid color format "${this._color.format}"`); return; } this.color = WI.Color.fromString(colorString); this._color.format = oldFormat; this.dispatchEventToListeners(WI.ColorPicker.Event.ColorChanged, {color: this._color}); } }; WI.ColorPicker.Event = { ColorChanged: "css-color-picker-color-changed", FormatChanged: "css-color-picker-format-changed", };