blob: 2506c885ad0f9a4b92d8ba325ee754fbb9adc78a [file] [log] [blame]
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/**
* @fileoverview This component displays the dynamic color options.
*/
import 'chrome://resources/cr_elements/cr_button/cr_button.js';
import '../../css/common.css.js';
import 'chrome://resources/cr_elements/cr_toggle/cr_toggle.js';
import 'chrome://resources/polymer/v3_0/iron-a11y-keys/iron-a11y-keys.js';
import 'chrome://resources/polymer/v3_0/iron-selector/iron-selector.js';
import {CrButtonElement} from 'chrome://resources/cr_elements/cr_button/cr_button.js';
import {hexColorToSkColor} from 'chrome://resources/js/color_utils.js';
import {SkColor} from 'chrome://resources/mojo/skia/public/mojom/skcolor.mojom-webui.js';
import {IronA11yKeysElement} from 'chrome://resources/polymer/v3_0/iron-a11y-keys/iron-a11y-keys.js';
import {IronSelectorElement} from 'chrome://resources/polymer/v3_0/iron-selector/iron-selector.js';
import {ColorScheme, SampleColorScheme} from '../personalization_app.mojom-webui.js';
import {WithPersonalizationStore} from '../personalization_store.js';
import {convertToRgbHexStr} from '../utils.js';
import {getTemplate} from './dynamic_color_element.html.js';
import {initializeDynamicColorData, setColorSchemePref, setStaticColorPref} from './theme_controller.js';
import {getThemeProvider} from './theme_interface_provider.js';
import {ThemeObserver} from './theme_observer.js';
export interface DynamicColorElement {
$: {
staticColorKeys: IronA11yKeysElement,
colorSchemeKeys: IronA11yKeysElement,
colorSchemeSelector: IronSelectorElement,
staticColorSelector: IronSelectorElement,
};
}
const DEFAULT_STATIC_COLOR = hexColorToSkColor('#4285f4');
const DEFAULT_COLOR_SCHEME = ColorScheme.kTonalSpot;
export class DynamicColorElement extends WithPersonalizationStore {
static get is() {
return 'dynamic-color';
}
static get template() {
return getTemplate();
}
static get properties() {
return {
// Whether or not to use the wallpaper to calculate the seed color.
automaticSeedColorEnabled: {
type: Boolean,
computed: 'isAutomaticSeedColorEnabled_(colorSchemeSelected_)',
},
// The static color stored in the backend.
staticColorSelected_: Object,
// The color scheme stored in the backend.
colorSchemeSelected_: Object,
staticColors_: {
type: Object,
readOnly: true,
value: [
// TODO(b/254479499): Replace colors when the spec is ready.
'#4285f4',
'#bdc1c6',
'#edd0e4',
'#eadecd',
],
},
sampleColorSchemes_: {
type: Array,
notify: true,
},
// The color scheme button currently highlighted by keyboard navigation.
colorSchemeHighlightedButton_: {
type: Object,
notify: true,
},
// The static color button currently highlighted by keyboard navigation.
staticColorHighlightedButton_: {
type: Object,
notify: true,
},
};
}
automaticSeedColorEnabled: boolean;
private previousStaticColorSelected_: SkColor|null;
private previousColorSchemeSelected_: ColorScheme|null;
private staticColorSelected_: SkColor|null;
private colorSchemeSelected_: ColorScheme|null;
private staticColors_: string[];
private sampleColorSchemes_: SampleColorScheme[];
private colorSchemeHighlightedButton_: CrButtonElement;
private staticColorHighlightedButton_: CrButtonElement;
override ready() {
super.ready();
this.$.staticColorKeys.target = this.$.staticColorSelector;
this.$.colorSchemeKeys.target = this.$.colorSchemeSelector;
}
override connectedCallback() {
super.connectedCallback();
ThemeObserver.initThemeObserverIfNeeded();
this.watch<DynamicColorElement['staticColorSelected_']>(
'staticColorSelected_', state => state.theme.staticColorSelected);
this.watch<DynamicColorElement['colorSchemeSelected_']>(
'colorSchemeSelected_', state => state.theme.colorSchemeSelected);
this.watch<DynamicColorElement['sampleColorSchemes_']>(
'sampleColorSchemes_', state => state.theme.sampleColorSchemes);
this.updateFromStore();
initializeDynamicColorData(getThemeProvider(), this.getStore());
}
private onClickColorSchemeButton_(event: Event) {
const eventTarget = event.currentTarget as HTMLElement;
const colorScheme = Number(eventTarget.dataset['colorSchemeId']);
setColorSchemePref(colorScheme, getThemeProvider(), this.getStore());
}
private onClickStaticColorButton_(event: Event) {
const eventTarget = event.currentTarget as HTMLElement;
const staticColorHexStr = String(eventTarget.dataset['staticColor']);
const staticColor = hexColorToSkColor(staticColorHexStr);
setStaticColorPref(staticColor, getThemeProvider(), this.getStore());
}
private onToggleChanged_() {
if (this.automaticSeedColorEnabled) {
this.previousColorSchemeSelected_ = this.colorSchemeSelected_;
const staticColor =
this.previousStaticColorSelected_ || DEFAULT_STATIC_COLOR;
setStaticColorPref(staticColor, getThemeProvider(), this.getStore());
} else {
this.previousStaticColorSelected_ = this.staticColorSelected_;
const colorScheme =
this.previousColorSchemeSelected_ || DEFAULT_COLOR_SCHEME;
setColorSchemePref(colorScheme, getThemeProvider(), this.getStore());
}
}
private isAutomaticSeedColorEnabled_(colorScheme: ColorScheme|null) {
return colorScheme === null || colorScheme !== ColorScheme.kStatic;
}
private getColorSchemeAriaChecked_(
colorScheme: string, colorSchemeSelected: string): 'true'|'false' {
if (!colorSchemeSelected) {
return 'false';
}
return colorSchemeSelected === colorScheme ? 'true' : 'false';
}
private getStaticColorAriaChecked_(
staticColor: string, staticColorSelected: SkColor|null): 'true'|'false' {
if (!staticColorSelected) {
return 'false';
}
return staticColor === convertToRgbHexStr(staticColorSelected.value) ?
'true' :
'false';
}
private onStaticColorKeysPress_(
e: CustomEvent<{key: string, keyboardEvent: KeyboardEvent}>) {
this.onKeysPress_(
e, this.$.staticColorSelector, this.staticColorHighlightedButton_);
}
private onColorSchemeKeysPress_(
e: CustomEvent<{key: string, keyboardEvent: KeyboardEvent}>) {
this.onKeysPress_(
e, this.$.colorSchemeSelector, this.colorSchemeHighlightedButton_);
}
/** Handle keyboard navigation. */
private onKeysPress_(
e: CustomEvent<{key: string, keyboardEvent: KeyboardEvent}>,
selector: IronSelectorElement, prevButton: CrButtonElement) {
switch (e.detail.key) {
case 'left':
selector.selectPrevious();
break;
case 'right':
selector.selectNext();
break;
default:
return;
}
// Remove focus state of previous button.
if (prevButton) {
prevButton.removeAttribute('tabindex');
}
// Add focus state for new button.
const highlightedButton = this.automaticSeedColorEnabled ?
this.colorSchemeHighlightedButton_ :
this.staticColorHighlightedButton_;
highlightedButton.setAttribute('tabindex', '0');
highlightedButton.focus();
e.detail.keyboardEvent.preventDefault();
}
private getTabIndex_(id: string): string {
return id === String(DEFAULT_COLOR_SCHEME) ||
hexColorToSkColor(id) === DEFAULT_STATIC_COLOR ?
'0' :
'-1';
}
}
customElements.define(DynamicColorElement.is, DynamicColorElement);