blob: cc25d70bb0ecad5915dd13fa22ebfa5e6318277c [file] [log] [blame]
// 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_icon_button/cr_icon_button.m.js';
import 'chrome://resources/cr_elements/icons.m.js';
import 'chrome://resources/cr_elements/shared_vars_css.m.js';
import 'chrome://resources/polymer/v3_0/iron-icon/iron-icon.js';
import 'chrome://resources/polymer/v3_0/paper-progress/paper-progress.js';
import './icons.html.js';
import './viewer-download-controls.js';
import './viewer-page-selector.js';
import './pdf-shared.css.js';
import './shared-vars.css.js';
// <if expr="enable_ink">
import './viewer-annotations-bar.js';
import './viewer-annotations-mode-dialog.js';
// </if>
import {AnchorAlignment, CrActionMenuElement} from 'chrome://resources/cr_elements/cr_action_menu/cr_action_menu.js';
import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
import {FittingType} from '../constants.js';
import {record, UserAction} from '../metrics.js';
import {getTemplate} from './viewer-toolbar.html.js';
declare global {
interface HTMLElementEventMap {
'annotation-mode-toggled': CustomEvent<boolean>;
'display-annotations-changed': CustomEvent<boolean>;
'fit-to-changed': CustomEvent<FittingType>;
}
}
export interface ViewerToolbarElement {
$: {
sidenavToggle: HTMLElement,
menu: CrActionMenuElement,
'two-page-view-button': HTMLButtonElement,
};
}
export class ViewerToolbarElement extends PolymerElement {
static get is() {
return 'viewer-toolbar';
}
static get template() {
return getTemplate();
}
static get properties() {
return {
// <if expr="enable_ink">
annotationAvailable: Boolean,
annotationMode: {
type: Boolean,
value: false,
reflectToAttribute: true,
},
// </if>
docTitle: String,
docLength: Number,
hasEdits: Boolean,
hasEnteredAnnotationMode: Boolean,
isFormFieldFocused: Boolean,
loadProgress: {
type: Number,
observer: 'loadProgressChanged_',
},
loading_: {
type: Boolean,
reflectToAttribute: true,
},
pageNo: Number,
pdfAnnotationsEnabled: Boolean,
printingEnabled: Boolean,
rotated: Boolean,
viewportZoom: Number,
zoomBounds: Object,
sidenavCollapsed: Boolean,
twoUpViewEnabled: Boolean,
moreMenuOpen_: {
type: Boolean,
reflectToAttribute: true,
},
fittingType_: Number,
fitToButtonIcon_: {
type: String,
computed: 'computeFitToButtonIcon_(fittingType_)',
},
viewportZoomPercent_: {
type: Number,
computed: 'computeViewportZoomPercent_(viewportZoom)',
observer: 'viewportZoomPercentChanged_',
},
// <if expr="enable_ink">
showAnnotationsModeDialog_: {
type: Boolean,
value: false,
},
showAnnotationsBar_: {
type: Boolean,
computed: 'computeShowAnnotationsBar_(' +
'loading_, annotationMode, pdfAnnotationsEnabled)',
},
// </if>
};
}
docTitle: string;
docLength: number;
hasEdits: boolean;
hasEnteredAnnotationMode: boolean;
isFormFieldFocused: boolean;
loadProgress: number;
pageNo: number;
pdfAnnotationsEnabled: boolean;
printingEnabled: boolean;
rotated: boolean;
viewportZoom: number;
zoomBounds: {min: number, max: number};
sidenavCollapsed: boolean = false;
twoUpViewEnabled: boolean;
private displayAnnotations_: boolean = true;
private fittingType_: FittingType = FittingType.FIT_TO_PAGE;
private fitToButtonIcon_: string;
private moreMenuOpen_: boolean = false;
private loading_: boolean = true;
private viewportZoomPercent_: number;
// <if expr="enable_ink">
annotationAvailable: boolean;
annotationMode: boolean;
private showAnnotationsModeDialog_: boolean;
private showAnnotationsBar_: boolean;
// </if>
private onSidenavToggleClick_() {
record(UserAction.TOGGLE_SIDENAV);
this.dispatchEvent(new CustomEvent('sidenav-toggle-click'));
}
private computeFitToButtonIcon_(): string {
return this.fittingType_ === FittingType.FIT_TO_PAGE ? 'pdf:fit-to-height' :
'pdf:fit-to-width';
}
private computeViewportZoomPercent_(): number {
return Math.round(100 * this.viewportZoom);
}
/** @return The appropriate tooltip for the current state. */
private getFitToButtonTooltip_(
fitToPageTooltip: string, fitToWidthTooltip: string): string {
return this.fittingType_ === FittingType.FIT_TO_PAGE ? fitToPageTooltip :
fitToWidthTooltip;
}
private loadProgressChanged_() {
this.loading_ = this.loadProgress < 100;
}
private viewportZoomPercentChanged_() {
this.getZoomInput_().value = `${this.viewportZoomPercent_}%`;
}
// <if expr="enable_ink">
private computeShowAnnotationsBar_(): boolean {
return this.pdfAnnotationsEnabled && !this.loading_ && this.annotationMode;
}
// </if>
private onPrintClick_() {
this.dispatchEvent(new CustomEvent('print'));
}
private onRotateClick_() {
this.dispatchEvent(new CustomEvent('rotate-left'));
}
private toggleDisplayAnnotations_() {
record(UserAction.TOGGLE_DISPLAY_ANNOTATIONS);
this.displayAnnotations_ = !this.displayAnnotations_;
this.dispatchEvent(new CustomEvent(
'display-annotations-changed', {detail: this.displayAnnotations_}));
this.$.menu.close();
// <if expr="enable_ink">
if (!this.displayAnnotations_ && this.annotationMode) {
this.toggleAnnotation();
}
// </if>
}
private onPresentClick_() {
record(UserAction.PRESENT);
this.$.menu.close();
this.dispatchEvent(new CustomEvent('present-click'));
}
private onPropertiesClick_() {
record(UserAction.PROPERTIES);
this.$.menu.close();
this.dispatchEvent(new CustomEvent('properties-click'));
}
private getSinglePageAriaChecked_(checked: boolean): string {
return checked ? 'false' : 'true';
}
private getTwoPageViewAriaChecked_(checked: boolean): string {
return checked ? 'true' : 'false';
}
private getShowAnnotationsAriaChecked_(checked: boolean): string {
return checked ? 'true' : 'false';
}
private getAriaExpanded_(): string {
return this.sidenavCollapsed ? 'false' : 'true';
}
private toggleTwoPageViewClick_() {
const newTwoUpViewEnabled = !this.twoUpViewEnabled;
this.dispatchEvent(
new CustomEvent('two-up-view-changed', {detail: newTwoUpViewEnabled}));
this.$.menu.close();
}
private onZoomInClick_() {
this.dispatchEvent(new CustomEvent('zoom-in'));
}
private onZoomOutClick_() {
this.dispatchEvent(new CustomEvent('zoom-out'));
}
forceFit(fittingType: FittingType) {
// The fitting type is the new state. We want to set the button fitting type
// to the opposite value.
this.fittingType_ = fittingType === FittingType.FIT_TO_WIDTH ?
FittingType.FIT_TO_PAGE :
FittingType.FIT_TO_WIDTH;
}
fitToggle() {
const newState = this.fittingType_ === FittingType.FIT_TO_PAGE ?
FittingType.FIT_TO_WIDTH :
FittingType.FIT_TO_PAGE;
this.dispatchEvent(
new CustomEvent('fit-to-changed', {detail: this.fittingType_}));
this.fittingType_ = newState;
}
private onFitToButtonClick_() {
this.fitToggle();
}
private getZoomInput_(): HTMLInputElement {
return this.shadowRoot!.querySelector('#zoom-controls input')!;
}
private onZoomChange_() {
const input = this.getZoomInput_();
let value = Number.parseInt(input.value, 10);
value = Math.max(Math.min(value, this.zoomBounds.max), this.zoomBounds.min);
if (this.sendZoomChanged_(value)) {
return;
}
const zoomString = `${this.viewportZoomPercent_}%`;
input.value = zoomString;
}
/**
* @param value The new zoom value
* @return Whether the zoom-changed event was sent.
*/
private sendZoomChanged_(value: number): boolean {
if (Number.isNaN(value)) {
return false;
}
// The viewport can have non-integer zoom values.
if (Math.abs(this.viewportZoom * 100 - value) < 0.5) {
return false;
}
this.dispatchEvent(new CustomEvent('zoom-changed', {detail: value}));
return true;
}
private onZoomInputPointerup_(e: Event) {
(e.target as HTMLInputElement).select();
}
private onMoreClick_() {
const anchor = this.shadowRoot!.querySelector<HTMLElement>('#more')!;
this.$.menu.showAt(anchor, {
anchorAlignmentX: AnchorAlignment.CENTER,
anchorAlignmentY: AnchorAlignment.AFTER_END,
noOffset: true,
});
}
private onMoreOpenChanged_(e: CustomEvent<{value: boolean}>) {
this.moreMenuOpen_ = e.detail.value;
}
private isAtMinimumZoom_(): boolean {
return this.zoomBounds !== undefined &&
this.viewportZoomPercent_ === this.zoomBounds.min;
}
private isAtMaximumZoom_(): boolean {
return this.zoomBounds !== undefined &&
this.viewportZoomPercent_ === this.zoomBounds.max;
}
// <if expr="enable_ink">
private onDialogClose_() {
const confirmed =
this.shadowRoot!.querySelector(
'viewer-annotations-mode-dialog')!.wasConfirmed();
this.showAnnotationsModeDialog_ = false;
if (confirmed) {
this.dispatchEvent(new CustomEvent('annotation-mode-dialog-confirmed'));
this.toggleAnnotation();
}
}
private onAnnotationClick_() {
if (!this.rotated && !this.twoUpViewEnabled) {
this.toggleAnnotation();
return;
}
this.showAnnotationsModeDialog_ = true;
}
toggleAnnotation() {
const newAnnotationMode = !this.annotationMode;
this.dispatchEvent(new CustomEvent(
'annotation-mode-toggled', {detail: newAnnotationMode}));
if (newAnnotationMode && !this.displayAnnotations_) {
this.toggleDisplayAnnotations_();
}
}
// </if>
}
declare global {
interface HTMLElementTagNameMap {
'viewer-toolbar': ViewerToolbarElement;
}
}
customElements.define(ViewerToolbarElement.is, ViewerToolbarElement);