| // Copyright 2017 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| export class ContrastDetails extends Common.Object { |
| /** |
| * @param {!ColorPicker.ContrastInfo} contrastInfo |
| * @param {!Element} contentElement |
| * @param {function(boolean=, !Common.Event=)} toggleMainColorPickerCallback |
| * @param {function()} expandedChangedCallback |
| */ |
| constructor(contrastInfo, contentElement, toggleMainColorPickerCallback, expandedChangedCallback) { |
| super(); |
| /** @type {!ColorPicker.ContrastInfo} */ |
| this._contrastInfo = contrastInfo; |
| |
| /** @type {!Element} */ |
| this._element = contentElement.createChild('div', 'spectrum-contrast-details collapsed'); |
| |
| /** @type {function(boolean=, !Common.Event=)} */ |
| this._toggleMainColorPicker = toggleMainColorPickerCallback; |
| |
| /** @type {function()} */ |
| this._expandedChangedCallback = expandedChangedCallback; |
| |
| /** @type {boolean} */ |
| this._expanded = false; |
| |
| /** @type {boolean} */ |
| this._passesAA = true; |
| |
| /** @type {boolean} */ |
| this._contrastUnknown = false; |
| |
| // This will not be visible if we don't get ContrastInfo, |
| // e.g. for a non-font color property such as border-color. |
| /** @type {boolean} */ |
| this._visible = false; |
| |
| const contrastValueRow = this._element.createChild('div'); |
| contrastValueRow.addEventListener('click', this._topRowClicked.bind(this)); |
| const contrastValueRowContents = contrastValueRow.createChild('div', 'container'); |
| contrastValueRowContents.createTextChild(Common.UIString('Contrast ratio')); |
| |
| this._contrastValueBubble = contrastValueRowContents.createChild('span', 'contrast-details-value'); |
| this._contrastValue = this._contrastValueBubble.createChild('span'); |
| this._contrastValueBubbleIcons = []; |
| this._contrastValueBubbleIcons.push( |
| this._contrastValueBubble.appendChild(UI.Icon.create('smallicon-checkmark-square'))); |
| this._contrastValueBubbleIcons.push( |
| this._contrastValueBubble.appendChild(UI.Icon.create('smallicon-checkmark-behind'))); |
| this._contrastValueBubbleIcons.push(this._contrastValueBubble.appendChild(UI.Icon.create('smallicon-no'))); |
| this._contrastValueBubbleIcons.forEach(button => button.addEventListener('click', event => { |
| ColorPicker.ContrastDetails._showHelp(); |
| event.consume(false); |
| })); |
| |
| const expandToolbar = new UI.Toolbar('expand', contrastValueRowContents); |
| this._expandButton = new UI.ToolbarButton(Common.UIString('Show more'), 'smallicon-expand-more'); |
| this._expandButton.addEventListener(UI.ToolbarButton.Events.Click, this._expandButtonClicked.bind(this)); |
| UI.ARIAUtils.setExpanded(this._expandButton.element, false); |
| expandToolbar.appendToolbarItem(this._expandButton); |
| |
| this._expandedDetails = this._element.createChild('div', 'expanded-details'); |
| UI.ARIAUtils.setControls(this._expandButton.element, this._expandedDetails); |
| |
| this._contrastThresholds = this._expandedDetails.createChild('div', 'contrast-thresholds'); |
| |
| this._contrastAA = this._contrastThresholds.createChild('div', 'contrast-threshold'); |
| this._contrastPassFailAA = this._contrastAA.createChild('span', 'contrast-pass-fail'); |
| |
| this._contrastAAA = this._contrastThresholds.createChild('div', 'contrast-threshold'); |
| this._contrastPassFailAAA = this._contrastAAA.createChild('span', 'contrast-pass-fail'); |
| |
| this._chooseBgColor = this._expandedDetails.createChild('div', 'contrast-choose-bg-color'); |
| this._chooseBgColor.textContent = Common.UIString('Pick background color'); |
| |
| const bgColorContainer = this._expandedDetails.createChild('div', 'background-color'); |
| |
| const pickerToolbar = new UI.Toolbar('spectrum-eye-dropper', bgColorContainer); |
| this._bgColorPickerButton = |
| new UI.ToolbarToggle(Common.UIString('Toggle background color picker'), 'largeicon-eyedropper'); |
| this._bgColorPickerButton.addEventListener( |
| UI.ToolbarButton.Events.Click, this._toggleBackgroundColorPicker.bind(this, undefined, true)); |
| pickerToolbar.appendToolbarItem(this._bgColorPickerButton); |
| this._bgColorPickedBound = this._bgColorPicked.bind(this); |
| |
| this._bgColorSwatch = new Swatch(bgColorContainer); |
| |
| this._contrastInfo.addEventListener(ColorPicker.ContrastInfo.Events.ContrastInfoUpdated, this._update.bind(this)); |
| } |
| |
| _update() { |
| if (this._contrastInfo.isNull()) { |
| this.setVisible(false); |
| return; |
| } |
| |
| this.setVisible(true); |
| |
| const contrastRatio = this._contrastInfo.contrastRatio(); |
| const fgColor = this._contrastInfo.color(); |
| const bgColor = this._contrastInfo.bgColor(); |
| if (!contrastRatio || !bgColor || !fgColor) { |
| this._contrastUnknown = true; |
| this._contrastValue.textContent = ''; |
| this._contrastValueBubble.classList.add('contrast-unknown'); |
| this._chooseBgColor.classList.remove('hidden'); |
| this._contrastThresholds.classList.add('hidden'); |
| return; |
| } |
| |
| this._contrastUnknown = false; |
| this._chooseBgColor.classList.add('hidden'); |
| this._contrastThresholds.classList.remove('hidden'); |
| this._contrastValueBubble.classList.remove('contrast-unknown'); |
| this._contrastValue.textContent = contrastRatio.toFixed(2); |
| |
| this._bgColorSwatch.setColors(fgColor, bgColor); |
| |
| const aa = this._contrastInfo.contrastRatioThreshold('aa'); |
| this._passesAA = this._contrastInfo.contrastRatio() >= aa; |
| this._contrastPassFailAA.removeChildren(); |
| const labelAA = this._contrastPassFailAA.createChild('span', 'contrast-link-label'); |
| labelAA.textContent = Common.UIString('AA'); |
| this._contrastPassFailAA.createChild('span').textContent = Common.UIString(': %s', aa.toFixed(1)); |
| if (this._passesAA) { |
| this._contrastPassFailAA.appendChild(UI.Icon.create('smallicon-checkmark-square')); |
| } else { |
| this._contrastPassFailAA.appendChild(UI.Icon.create('smallicon-no')); |
| } |
| |
| const aaa = this._contrastInfo.contrastRatioThreshold('aaa'); |
| const passesAAA = this._contrastInfo.contrastRatio() >= aaa; |
| this._contrastPassFailAAA.removeChildren(); |
| const labelAAA = this._contrastPassFailAAA.createChild('span', 'contrast-link-label'); |
| labelAAA.textContent = Common.UIString('AAA'); |
| this._contrastPassFailAAA.createChild('span').textContent = Common.UIString(': %s', aaa.toFixed(1)); |
| if (passesAAA) { |
| this._contrastPassFailAAA.appendChild(UI.Icon.create('smallicon-checkmark-square')); |
| } else { |
| this._contrastPassFailAAA.appendChild(UI.Icon.create('smallicon-no')); |
| } |
| |
| [labelAA, labelAAA].forEach(e => e.addEventListener('click', event => ColorPicker.ContrastDetails._showHelp())); |
| |
| this._element.classList.toggle('contrast-fail', !this._passesAA); |
| this._contrastValueBubble.classList.toggle('contrast-aa', this._passesAA); |
| this._contrastValueBubble.classList.toggle('contrast-aaa', passesAAA); |
| } |
| |
| static _showHelp() { |
| Host.InspectorFrontendHost.openInNewTab( |
| 'https://developers.google.com/web/fundamentals/accessibility/accessible-styles#color_and_contrast'); |
| } |
| |
| /** |
| * @param {boolean} visible |
| */ |
| setVisible(visible) { |
| this._visible = visible; |
| this._element.classList.toggle('hidden', !visible); |
| } |
| |
| /** |
| * @return {boolean} |
| */ |
| visible() { |
| return this._visible; |
| } |
| |
| /** |
| * @return {!Element} |
| */ |
| element() { |
| return this._element; |
| } |
| |
| /** |
| * @param {!Common.Event} event |
| */ |
| _expandButtonClicked(event) { |
| this._contrastValueBubble.getComponentSelection().empty(); |
| this._toggleExpanded(); |
| } |
| |
| /** |
| * @param {!Event} event |
| */ |
| _topRowClicked(event) { |
| this._contrastValueBubble.getComponentSelection().empty(); |
| this._toggleExpanded(); |
| event.consume(true); |
| } |
| |
| _toggleExpanded() { |
| this._expanded = !this._expanded; |
| UI.ARIAUtils.setExpanded(this._expandButton.element, this._expanded); |
| this._element.classList.toggle('collapsed', !this._expanded); |
| if (this._expanded) { |
| this._toggleMainColorPicker(false); |
| this._expandButton.setGlyph('smallicon-expand-less'); |
| this._expandButton.setTitle(Common.UIString('Show less')); |
| if (this._contrastUnknown) { |
| this._toggleBackgroundColorPicker(true); |
| } |
| } else { |
| this._toggleBackgroundColorPicker(false); |
| this._expandButton.setGlyph('smallicon-expand-more'); |
| this._expandButton.setTitle(Common.UIString('Show more')); |
| } |
| this._expandedChangedCallback(); |
| } |
| |
| collapse() { |
| this._element.classList.remove('expanded'); |
| this._toggleBackgroundColorPicker(false); |
| this._toggleMainColorPicker(false); |
| } |
| |
| /** |
| * @return {boolean} |
| */ |
| expanded() { |
| return this._expanded; |
| } |
| |
| /** |
| * @returns {boolean} |
| */ |
| backgroundColorPickerEnabled() { |
| return this._bgColorPickerButton.toggled(); |
| } |
| |
| /** |
| * @param {boolean} enabled |
| */ |
| |
| toggleBackgroundColorPicker(enabled) { |
| this._toggleBackgroundColorPicker(enabled, false); |
| } |
| |
| /** |
| * @param {boolean=} enabled |
| * @param {boolean=} shouldTriggerEvent |
| */ |
| _toggleBackgroundColorPicker(enabled, shouldTriggerEvent = true) { |
| if (enabled === undefined) { |
| enabled = !this._bgColorPickerButton.toggled(); |
| } |
| this._bgColorPickerButton.setToggled(enabled); |
| |
| if (shouldTriggerEvent) { |
| this.dispatchEventToListeners(Events.BackgroundColorPickerWillBeToggled, enabled); |
| } |
| |
| Host.InspectorFrontendHost.setEyeDropperActive(enabled); |
| if (enabled) { |
| Host.InspectorFrontendHost.events.addEventListener( |
| Host.InspectorFrontendHostAPI.Events.EyeDropperPickedColor, this._bgColorPickedBound); |
| } else { |
| Host.InspectorFrontendHost.events.removeEventListener( |
| Host.InspectorFrontendHostAPI.Events.EyeDropperPickedColor, this._bgColorPickedBound); |
| } |
| } |
| |
| /** |
| * @param {!Common.Event} event |
| */ |
| _bgColorPicked(event) { |
| const rgbColor = /** @type {!{r: number, g: number, b: number, a: number}} */ (event.data); |
| const rgba = [rgbColor.r, rgbColor.g, rgbColor.b, (rgbColor.a / 2.55 | 0) / 100]; |
| const color = Common.Color.fromRGBA(rgba); |
| this._contrastInfo.setBgColor(color); |
| this._toggleBackgroundColorPicker(false); |
| Host.InspectorFrontendHost.bringToFront(); |
| } |
| } |
| |
| export const Events = { |
| BackgroundColorPickerWillBeToggled: Symbol('BackgroundColorPickerWillBeToggled') |
| }; |
| |
| export class Swatch { |
| /** |
| * @param {!Element} parentElement |
| */ |
| constructor(parentElement) { |
| this._parentElement = parentElement; |
| this._swatchElement = parentElement.createChild('span', 'swatch contrast swatch-inner-white'); |
| this._swatchInnerElement = this._swatchElement.createChild('span', 'swatch-inner'); |
| this._textPreview = this._swatchElement.createChild('div', 'text-preview'); |
| this._textPreview.textContent = 'Aa'; |
| } |
| |
| /** |
| * @param {!Common.Color} fgColor |
| * @param {!Common.Color} bgColor |
| */ |
| setColors(fgColor, bgColor) { |
| this._textPreview.style.color = /** @type {string} */ (fgColor.asString(Common.Color.Format.RGBA)); |
| this._swatchInnerElement.style.backgroundColor = |
| /** @type {string} */ (bgColor.asString(Common.Color.Format.RGBA)); |
| // Show border if the swatch is white. |
| this._swatchElement.classList.toggle('swatch-inner-white', bgColor.hsla()[2] > 0.9); |
| } |
| } |
| |
| /* Legacy exported object */ |
| self.ColorPicker = self.ColorPicker || {}; |
| |
| /* Legacy exported object */ |
| ColorPicker = ColorPicker || {}; |
| |
| /** @constructor */ |
| ColorPicker.ContrastDetails = ContrastDetails; |
| |
| ColorPicker.ContrastDetails.Events = Events; |
| |
| /** @constructor */ |
| ColorPicker.ContrastDetails.Swatch = Swatch; |