blob: 424ada4cdddaf03dc95b28129d284db145ab2287 [file] [log] [blame]
// Copyright 2022 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.
/**
* @fileoverview
* The breadcrumb that displays the current view stack and allows users to
* navigate.
*/
import '/strings.m.js';
import 'chrome://resources/cr_elements/cr_icons_css.m.js';
import 'chrome://resources/cr_elements/cr_icon_button/cr_icon_button.js';
import 'chrome://resources/cr_elements/icons.m.js';
import 'chrome://resources/polymer/v3_0/iron-icon/iron-icon.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 '../css/common.css.js';
import '../css/cros_button_style.css.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 {GooglePhotosAlbum, TopicSource, WallpaperCollection} from './personalization_app.mojom-webui.js';
import {getTemplate} from './personalization_breadcrumb_element.html.js';
import {isPathValid, isPersonalizationHubEnabled, Paths, PersonalizationRouter} from './personalization_router_element.js';
import {WithPersonalizationStore} from './personalization_store.js';
import {inBetween, isNonEmptyArray} from './utils.js';
/**
* Checks if argument is a string with non-zero length.
*/
function isNonEmptyString(maybeString: unknown): maybeString is string {
return typeof maybeString === 'string' && maybeString.length > 0;
}
/** Event interface for dom-repeat. */
interface RepeaterEvent extends CustomEvent {
model: {
index: number,
};
}
export function stringToTopicSource(x: string): TopicSource|null {
const num = parseInt(x, 10);
if (!isNaN(num) &&
inBetween(num, TopicSource.MIN_VALUE, TopicSource.MAX_VALUE)) {
return num;
}
return null;
}
export interface PersonalizationBreadcrumb {
$: {
container: HTMLElement,
keys: IronA11yKeysElement,
selector: IronSelectorElement,
};
}
export class PersonalizationBreadcrumb extends WithPersonalizationStore {
static get is() {
return 'personalization-breadcrumb';
}
static get template() {
return getTemplate();
}
static get properties() {
return {
/**
* The current collection id to display.
*/
collectionId: {
type: String,
},
/** The current Google Photos album id to display. */
googlePhotosAlbumId: String,
/** The topic source of the selected album(s) for screensaver. */
topicSource: String,
/**
* The current path of the page.
*/
path: {
type: String,
},
breadcrumbs_: {
type: Array,
computed:
'computeBreadcrumbs_(path, collections_, collectionId, googlePhotosAlbums_, googlePhotosAlbumId, topicSource)',
},
collections_: {
type: Array,
},
/** The list of Google Photos albums. */
googlePhotosAlbums_: Array,
showBackButton_: {
type: Boolean,
computed: 'computeShowBackButton_(path)',
},
/** The breadcrumb being highlighted by keyboard navigation. */
selectedBreadcrumb_: {
type: Object,
notify: true,
},
};
}
collectionId: string;
googlePhotosAlbumId: string;
topicSource: string;
path: string;
private breadcrumbs_: string[];
private collections_: WallpaperCollection[]|null;
private googlePhotosAlbums_: GooglePhotosAlbum[]|null;
private showBackButton_: boolean;
private selectedBreadcrumb_: HTMLElement;
override ready() {
super.ready();
this.$.keys.target = this.$.selector;
}
override connectedCallback() {
super.connectedCallback();
this.watch('collections_', state => state.wallpaper.backdrop.collections);
this.watch(
'googlePhotosAlbums_', state => state.wallpaper.googlePhotos.albums);
this.updateFromStore();
}
/** Handle keyboard navigation. */
private onKeysPress_(
e: CustomEvent<{key: string, keyboardEvent: KeyboardEvent}>) {
const selector = this.$.selector;
const prevBreadcrumb = this.selectedBreadcrumb_;
switch (e.detail.key) {
case 'left':
selector.selectPrevious();
break;
case 'right':
selector.selectNext();
break;
default:
return;
}
// Remove focus state of previous breadcrumb.
if (prevBreadcrumb) {
prevBreadcrumb.removeAttribute('tabindex');
}
// Add focus state for new breadcrumb.
if (this.selectedBreadcrumb_) {
this.selectedBreadcrumb_.setAttribute('tabindex', '0');
this.selectedBreadcrumb_.focus();
}
e.detail.keyboardEvent.preventDefault();
}
/**
* Returns the aria-current status of the breadcrumb. The last breadcrumb is
* considered the "current" breadcrumb representing the active page.
*/
private getBreadcrumbAriaCurrent_(index: number, breadcrumbs: string[]):
'page'|'false' {
if (index === (breadcrumbs.length - 1)) {
return 'page';
}
return 'false';
}
private computeBreadcrumbs_(): string[] {
const breadcrumbs = [];
switch (this.path) {
case Paths.COLLECTIONS:
breadcrumbs.push(this.i18n('wallpaperLabel'));
break;
case Paths.COLLECTION_IMAGES:
breadcrumbs.push(this.i18n('wallpaperLabel'));
if (isNonEmptyArray(this.collections_)) {
const collection = this.collections_.find(
collection => collection.id === this.collectionId);
if (collection) {
breadcrumbs.push(collection.name);
}
}
break;
case Paths.GOOGLE_PHOTOS_COLLECTION:
breadcrumbs.push(this.i18n('wallpaperLabel'));
breadcrumbs.push(this.i18n('googlePhotosLabel'));
if (isNonEmptyString(this.googlePhotosAlbumId) &&
isNonEmptyArray(this.googlePhotosAlbums_)) {
const googlePhotosAlbum = this.googlePhotosAlbums_.find(
googlePhotosAlbum =>
googlePhotosAlbum.id === this.googlePhotosAlbumId);
if (googlePhotosAlbum) {
breadcrumbs.push(googlePhotosAlbum.title);
}
}
break;
case Paths.LOCAL_COLLECTION:
breadcrumbs.push(this.i18n('wallpaperLabel'));
breadcrumbs.push(this.i18n('myImagesLabel'));
break;
case Paths.USER:
breadcrumbs.push(this.i18n('avatarLabel'));
break;
case Paths.AMBIENT:
breadcrumbs.push(this.i18n('screensaverLabel'));
break;
case Paths.AMBIENT_ALBUMS:
breadcrumbs.push(this.i18n('screensaverLabel'));
const topicSourceVal = stringToTopicSource(this.topicSource);
if (topicSourceVal === TopicSource.kGooglePhotos) {
breadcrumbs.push(this.i18n('ambientModeTopicSourceGooglePhotos'));
} else if (topicSourceVal === TopicSource.kArtGallery) {
breadcrumbs.push(this.i18n('ambientModeTopicSourceArtGallery'));
} else {
console.warn('Invalid TopicSource value.', topicSourceVal);
}
break;
}
return breadcrumbs;
}
private computeShowBackButton_(): boolean {
// Do not show the back button if hub is enabled.
return !isPersonalizationHubEnabled() && this.path !== Paths.COLLECTIONS;
}
private showHomeButton_(): boolean {
return isPersonalizationHubEnabled();
}
private getBackButtonAriaLabel_(): string {
return this.i18n('back', this.i18n('wallpaperLabel'));
}
private getHomeButtonAriaLabel_(): string {
return this.i18n('ariaLabelHome');
}
private onBackClick_() {
window.history.back();
}
private onBreadcrumbClick_(e: RepeaterEvent) {
const index = e.model.index;
// stay in same page if the user clicks on the last breadcrumb,
// else navigate to the corresponding page.
if (index < this.breadcrumbs_.length - 1) {
const pathElements = this.path.split('/');
const newPath = pathElements.slice(0, index + 2).join('/');
if (isPathValid(newPath)) {
// Unfocus the breadcrumb to focus on the page
// with new path.
const breadcrumb = e.target as HTMLElement;
breadcrumb.blur();
PersonalizationRouter.instance().goToRoute(newPath as Paths);
}
}
}
private onHomeIconClick_() {
PersonalizationRouter.instance().goToRoute(Paths.ROOT);
}
}
customElements.define(PersonalizationBreadcrumb.is, PersonalizationBreadcrumb);