blob: 346c23500549bfcfb4ccba359da6c135753e681a [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.
import '//resources/cr_components/searchbox/searchbox_dropdown.js';
import '/strings.m.js';
import {ColorChangeUpdater} from '//resources/cr_components/color_change_listener/colors_css_updater.js';
import {SearchboxBrowserProxy} from '//resources/cr_components/searchbox/searchbox_browser_proxy.js';
import type {SearchboxDropdownElement} from '//resources/cr_components/searchbox/searchbox_dropdown.js';
import {assert} from '//resources/js/assert.js';
import {MetricsReporterImpl} from '//resources/js/metrics_reporter/metrics_reporter.js';
import {CrLitElement} from '//resources/lit/v3_0/lit.rollup.js';
import type {AutocompleteResult, OmniboxPopupSelection, PageCallbackRouter} from '//resources/mojo/components/omnibox/browser/searchbox.mojom-webui.js';
import {getCss} from './app.css.js';
import {getHtml} from './app.html.js';
// 675px ~= 449px (--cr-realbox-primary-side-min-width) * 1.5 + some margin.
const canShowSecondarySideMediaQueryList =
window.matchMedia('(min-width: 675px)');
export interface OmniboxPopupAppElement {
$: {
matches: SearchboxDropdownElement,
};
}
// Displays the autocomplete matches in the autocomplete result.
export class OmniboxPopupAppElement extends CrLitElement {
static get is() {
return 'omnibox-popup-app';
}
static override get styles() {
return getCss();
}
override render() {
return getHtml.bind(this)();
}
static override get properties() {
return {
/**
* Whether the secondary side can be shown based on the feature state and
* the width available to the dropdown.
*/
canShowSecondarySide: {
type: Boolean,
reflect: true,
},
/*
* Whether the secondary side is currently available to be shown.
*/
hasSecondarySide: {
type: Boolean,
reflect: true,
},
result_: {type: Object},
};
}
accessor canShowSecondarySide: boolean =
canShowSecondarySideMediaQueryList.matches;
accessor hasSecondarySide: boolean = false;
protected accessor result_: AutocompleteResult|null = null;
private callbackRouter_: PageCallbackRouter;
private autocompleteResultChangedListenerId_: number|null = null;
private selectionChangedListenerId_: number|null = null;
constructor() {
super();
this.callbackRouter_ = SearchboxBrowserProxy.getInstance().callbackRouter;
ColorChangeUpdater.forDocument().start();
}
override connectedCallback() {
super.connectedCallback();
this.autocompleteResultChangedListenerId_ =
this.callbackRouter_.autocompleteResultChanged.addListener(
this.onAutocompleteResultChanged_.bind(this));
this.selectionChangedListenerId_ =
this.callbackRouter_.updateSelection.addListener(
this.onUpdateSelection_.bind(this));
canShowSecondarySideMediaQueryList.addEventListener(
'change', this.onCanShowSecondarySideChanged_.bind(this));
}
override disconnectedCallback() {
super.disconnectedCallback();
assert(this.autocompleteResultChangedListenerId_);
this.callbackRouter_.removeListener(
this.autocompleteResultChangedListenerId_);
assert(this.selectionChangedListenerId_);
this.callbackRouter_.removeListener(this.selectionChangedListenerId_);
canShowSecondarySideMediaQueryList.removeEventListener(
'change', this.onCanShowSecondarySideChanged_.bind(this));
}
private onCanShowSecondarySideChanged_(e: MediaQueryListEvent) {
this.canShowSecondarySide = e.matches;
}
private onAutocompleteResultChanged_(result: AutocompleteResult) {
this.result_ = result;
if (result.matches[0]?.allowedToBeDefaultMatch) {
this.$.matches.selectFirst();
} else if (this.$.matches.selectedMatchIndex >= result.matches.length) {
this.$.matches.unselect();
}
}
protected onResultRepaint_() {
const metricsReporter = MetricsReporterImpl.getInstance();
metricsReporter.measure('ResultChanged')
.then(
duration => metricsReporter.umaReportTime(
'WebUIOmnibox.ResultChangedToRepaintLatency.ToPaint', duration))
.then(() => metricsReporter.clearMark('ResultChanged'))
// Ignore silently if mark 'ResultChanged' is missing.
.catch(() => {});
}
private onUpdateSelection_(
oldSelection: OmniboxPopupSelection, selection: OmniboxPopupSelection) {
this.$.matches.updateSelection(oldSelection, selection);
}
protected onHasSecondarySideChanged_(e: CustomEvent<{value: boolean}>) {
this.hasSecondarySide = e.detail.value;
}
}
declare global {
interface HTMLElementTagNameMap {
'omnibox-popup-app': OmniboxPopupAppElement;
}
}
customElements.define(OmniboxPopupAppElement.is, OmniboxPopupAppElement);