| // Copyright 2020 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. |
| |
| import 'chrome://resources/cr_elements/cr_button/cr_button.m.js'; |
| import 'chrome://resources/cr_elements/cr_checkbox/cr_checkbox.m.js'; |
| import 'chrome://resources/cr_elements/cr_dialog/cr_dialog.m.js'; |
| import 'chrome://resources/cr_elements/cr_icon_button/cr_icon_button.m.js'; |
| import 'chrome://resources/cr_elements/cr_input/cr_input.m.js'; |
| import 'chrome://resources/cr_elements/cr_profile_avatar_selector/cr_profile_avatar_selector.js'; |
| import 'chrome://resources/cr_elements/shared_vars_css.m.js'; |
| import 'chrome://resources/cr_elements/shared_style_css.m.js'; |
| import 'chrome://resources/cr_components/customize_themes/customize_themes.js'; |
| import './profile_creation_shared.css.js'; |
| import '../icons.js'; |
| import '../profile_picker_shared.css.js'; |
| |
| import {Theme, ThemeInfo, ThemeType} from 'chrome://resources/cr_components/customize_themes/customize_themes.mojom-webui.js'; |
| import {CrButtonElement} from 'chrome://resources/cr_elements/cr_button/cr_button.m.js'; |
| import {CrDialogElement} from 'chrome://resources/cr_elements/cr_dialog/cr_dialog.m.js'; |
| import {CrInputElement} from 'chrome://resources/cr_elements/cr_input/cr_input.m.js'; |
| import {AvatarIcon} from 'chrome://resources/cr_elements/cr_profile_avatar_selector/cr_profile_avatar_selector.js'; |
| import {I18nMixin} from 'chrome://resources/js/i18n_mixin.js'; |
| import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js'; |
| import {WebUIListenerMixin} from 'chrome://resources/js/web_ui_listener_mixin.js'; |
| import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js'; |
| |
| import {AutogeneratedThemeColorInfo, ManageProfilesBrowserProxy, ManageProfilesBrowserProxyImpl, UserThemeChoice} from '../manage_profiles_browser_proxy.js'; |
| import {navigateTo, navigateToPreviousRoute, Routes} from '../navigation_mixin.js'; |
| import {isProfileCreationAllowed} from '../policy_helper.js'; |
| |
| import {getTemplate} from './local_profile_customization.html.js'; |
| |
| export interface LocalProfileCustomizationElement { |
| $: { |
| backButton: HTMLElement, |
| doneButton: CrButtonElement, |
| nameInput: CrInputElement, |
| save: CrButtonElement, |
| selectAvatarDialog: CrDialogElement, |
| wrapperContainer: HTMLElement, |
| wrapper: HTMLElement, |
| }; |
| } |
| |
| const LocalProfileCustomizationElementBase = |
| WebUIListenerMixin(I18nMixin(PolymerElement)); |
| |
| export class LocalProfileCustomizationElement extends |
| LocalProfileCustomizationElementBase { |
| static get is() { |
| return 'local-profile-customization'; |
| } |
| |
| static get template() { |
| return getTemplate(); |
| } |
| |
| static get properties() { |
| return { |
| /** |
| * The theme info used to display colored UI elements. |
| */ |
| profileThemeInfo: { |
| type: Object, |
| observer: 'onProfileThemeInfoChange_', |
| notify: true, |
| }, |
| |
| /** |
| * The currently selected theme in the color picker. |
| */ |
| selectedTheme_: { |
| type: Object, |
| observer: 'onSelectedThemeChange_', |
| }, |
| |
| /** |
| * True if `selectedTheme_` doesn't need to be updated when |
| * `profileThemeInfo` changes. |
| */ |
| disableSelectedThemeUpdates_: { |
| type: Boolean, |
| value: false, |
| }, |
| |
| /** |
| * Colored default generic avatar in the format expected by |
| * 'cr-profile-avatar-selector' |
| */ |
| genericDefaultAvatar_: { |
| type: Object, |
| computed: |
| 'getGenericDefaultAvatar_(profileThemeInfo.themeGenericAvatar)', |
| observer: 'onGenericDefaultAvatarChange_', |
| }, |
| |
| /** |
| * List of available profile icon Urls and labels. |
| */ |
| availableIcons_: { |
| type: Array, |
| value() { |
| return []; |
| }, |
| }, |
| |
| /** |
| * The currently selected profile avatar, if any. |
| */ |
| selectedAvatar_: Object, |
| |
| /** |
| * The current profile name. |
| */ |
| profileName_: { |
| type: String, |
| value: '', |
| }, |
| |
| /** |
| * if true, a desktop shortcut will be created for the new profile. |
| */ |
| createShortcut_: { |
| type: Boolean, |
| value: true, |
| }, |
| |
| /** |
| * True if the profile shortcuts feature is enabled. |
| */ |
| isProfileShortcutsEnabled_: { |
| type: Boolean, |
| value: () => loadTimeData.getBoolean('profileShortcutsEnabled'), |
| }, |
| |
| /** |
| * True if a profile is being created or imported. |
| */ |
| createInProgress_: { |
| type: Boolean, |
| value: false, |
| }, |
| |
| pattern_: { |
| type: String, |
| value: '.*\\S.*', |
| }, |
| |
| defaultAvatarIndex_: { |
| type: Number, |
| value: () => loadTimeData.getInteger('placeholderAvatarIndex'), |
| }, |
| }; |
| } |
| |
| profileThemeInfo: AutogeneratedThemeColorInfo; |
| private selectedTheme_: Theme; |
| private disableSelectedThemeUpdates_: boolean; |
| private genericDefaultAvatar_: AvatarIcon; |
| private availableIcons_: AvatarIcon[]; |
| private selectedAvatar_: AvatarIcon|null; |
| private profileName_: string; |
| private createShortcut_: boolean; |
| private isProfileShortcutsEnabled_: boolean; |
| private createInProgress_: boolean; |
| private pattern_: string; |
| private defaultAvatarIndex_: number; |
| private manageProfilesBrowserProxy_: ManageProfilesBrowserProxy = |
| ManageProfilesBrowserProxyImpl.getInstance(); |
| private resizeObserver_: ResizeObserver|null = null; |
| |
| override ready() { |
| super.ready(); |
| this.sanityCheck_(); |
| this.addWebUIListener( |
| 'create-profile-finished', () => this.handleCreateProfileFinished_()); |
| this.manageProfilesBrowserProxy_.getAvailableIcons().then( |
| icons => this.setAvailableIcons_(icons)); |
| |
| this.addEventListener('view-enter-start', this.onViewEnterStart_); |
| } |
| |
| override connectedCallback() { |
| super.connectedCallback(); |
| this.addResizeObserver_(); |
| } |
| |
| override disconnectedCallback() { |
| super.disconnectedCallback(); |
| this.resizeObserver_!.disconnect(); |
| } |
| |
| private addResizeObserver_() { |
| const wrapperContainer = this.$.wrapperContainer; |
| this.resizeObserver_ = new ResizeObserver(() => { |
| this.shadowRoot!.querySelector('.footer')!.classList.toggle( |
| 'division-line', |
| wrapperContainer.scrollHeight > wrapperContainer.clientHeight); |
| }); |
| this.resizeObserver_.observe(wrapperContainer); |
| } |
| |
| private onViewEnterStart_() { |
| if (this.profileName_.length === 0) { |
| this.$.nameInput.invalid = false; |
| } |
| this.$.nameInput.focusInput(); |
| this.$.wrapper.scrollTop = 0; |
| } |
| |
| private getBackButtonAriaLabel_(): string { |
| return this.i18n( |
| 'backButtonAriaLabel', this.i18n('localProfileCreationTitle')); |
| } |
| |
| private sanityCheck_(): boolean { |
| if (!isProfileCreationAllowed()) { |
| this.onBackClick_(); |
| return false; |
| } |
| return true; |
| } |
| |
| /** |
| * Handler for profile name blur. |
| */ |
| private onProfileNameInputBlur_() { |
| this.$.nameInput.validate(); |
| } |
| |
| /** |
| * Determining whether 'Save' button is disabled. |
| */ |
| private isSaveDisabled_(): boolean { |
| return this.createInProgress_ || !this.profileName_ || |
| !this.$.nameInput.validate(); |
| } |
| |
| /** |
| * Handler for the 'Save' button click event. |
| */ |
| private onSaveClick_() { |
| if (!this.sanityCheck_()) { |
| return; |
| } |
| |
| if (this.createInProgress_) { |
| return; |
| } |
| this.createInProgress_ = true; |
| const createShortcut = |
| this.isProfileShortcutsEnabled_ && this.createShortcut_; |
| this.manageProfilesBrowserProxy_.createProfile( |
| this.profileName_, this.profileThemeInfo.color, |
| this.selectedAvatar_!.index, createShortcut); |
| } |
| |
| private onBackClick_() { |
| navigateToPreviousRoute(); |
| } |
| |
| private onCustomizeAvatarClick_() { |
| this.$.selectAvatarDialog.showModal(); |
| } |
| |
| private onDoneSelectAvatarClick_() { |
| this.$.selectAvatarDialog.close(); |
| } |
| |
| private getGenericDefaultAvatar_(): AvatarIcon { |
| return { |
| url: this.profileThemeInfo.themeGenericAvatar, |
| label: this.i18n('defaultAvatarLabel'), |
| index: this.defaultAvatarIndex_, |
| isGaiaAvatar: false, |
| selected: false, |
| }; |
| } |
| |
| private onGenericDefaultAvatarChange_() { |
| this.setAvailableIcons_([...this.availableIcons_]); |
| if (!this.selectedAvatar_ || |
| this.selectedAvatar_.index === this.defaultAvatarIndex_) { |
| this.selectedAvatar_ = this.genericDefaultAvatar_; |
| } |
| } |
| |
| private setAvailableIcons_(icons: AvatarIcon[]) { |
| if (!this.genericDefaultAvatar_) { |
| this.availableIcons_ = icons; |
| return; |
| } |
| const offset = |
| icons.length > 0 && icons[0].index === this.defaultAvatarIndex_ ? 1 : 0; |
| this.availableIcons_ = [this.genericDefaultAvatar_, ...icons.slice(offset)]; |
| } |
| |
| private onProfileThemeInfoChange_() { |
| if (this.disableSelectedThemeUpdates_) { |
| return; |
| } |
| |
| this.selectedTheme_ = { |
| type: ThemeType.kChrome, |
| info: { |
| chromeThemeId: this.profileThemeInfo.colorId, |
| } as ThemeInfo, |
| isForced: false, |
| }; |
| } |
| |
| private async onSelectedThemeChange_() { |
| let theme: UserThemeChoice|null = null; |
| if (this.selectedTheme_.type === ThemeType.kAutogenerated) { |
| theme = { |
| colorId: 0, |
| color: this.selectedTheme_.info.autogeneratedThemeColors!.frame.value, |
| }; |
| } else if (this.selectedTheme_.type === ThemeType.kChrome) { |
| theme = { |
| colorId: this.selectedTheme_.info.chromeThemeId!, |
| }; |
| } else if (this.selectedTheme_.type === ThemeType.kDefault) { |
| theme = { |
| colorId: -1, |
| }; |
| } |
| |
| const newThemeInfo = |
| await this.manageProfilesBrowserProxy_.getProfileThemeInfo(theme!); |
| this.disableSelectedThemeUpdates_ = true; |
| this.profileThemeInfo = newThemeInfo; |
| this.disableSelectedThemeUpdates_ = false; |
| } |
| |
| private handleCreateProfileFinished_() { |
| // On profile creation, the picker window is closed. |
| // 'navigateTo' is meaningful if the picker is shown in a tab. |
| navigateTo(Routes.MAIN); |
| this.createInProgress_ = false; |
| } |
| } |
| |
| declare global { |
| interface HTMLElementTagNameMap { |
| 'local-profile-customization': LocalProfileCustomizationElement; |
| } |
| } |
| |
| customElements.define( |
| LocalProfileCustomizationElement.is, LocalProfileCustomizationElement); |