| // Copyright 2019 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.js'; |
| |
| import {CrIconButtonElement} from 'chrome://resources/cr_elements/cr_icon_button/cr_icon_button.js'; |
| import {assert} from 'chrome://resources/js/assert_ts.js'; |
| import {beforeNextRender, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js'; |
| |
| import {getTemplate} from './viewer-pen-options.html.js'; |
| |
| interface Color { |
| name: string; |
| color: string; |
| outline?: boolean; |
| } |
| |
| const colors: Color[] = [ |
| // row 1 |
| {name: 'annotationColorBlack', color: '#000000'}, |
| {name: 'annotationColorRed', color: '#ff5252'}, |
| {name: 'annotationColorYellow', color: '#ffbc00'}, |
| {name: 'annotationColorGreen', color: '#00c853'}, |
| {name: 'annotationColorCyan', color: '#00b0ff'}, |
| {name: 'annotationColorPurple', color: '#d500f9'}, |
| {name: 'annotationColorBrown', color: '#8d6e63'}, |
| // row 2 |
| {name: 'annotationColorWhite', color: '#fafafa', outline: true}, |
| {name: 'annotationColorCrimson', color: '#a52714'}, |
| {name: 'annotationColorAmber', color: '#ee8100'}, |
| {name: 'annotationColorAvocadoGreen', color: '#558b2f'}, |
| {name: 'annotationColorCobaltBlue', color: '#01579b'}, |
| {name: 'annotationColorDeepPurple', color: '#8e24aa'}, |
| {name: 'annotationColorDarkBrown', color: '#4e342e'}, |
| // row 3 |
| {name: 'annotationColorDarkGrey', color: '#90a4ae'}, |
| {name: 'annotationColorHotPink', color: '#ff4081'}, |
| {name: 'annotationColorOrange', color: '#ff6e40'}, |
| {name: 'annotationColorLime', color: '#aeea00'}, |
| {name: 'annotationColorBlue', color: '#304ffe'}, |
| {name: 'annotationColorViolet', color: '#7c4dff'}, |
| {name: 'annotationColorTeal', color: '#1de9b6'}, |
| // row 4 |
| {name: 'annotationColorLightGrey', color: '#cfd8dc'}, |
| {name: 'annotationColorLightPink', color: '#f8bbd0'}, |
| {name: 'annotationColorLightOrange', color: '#ffccbc'}, |
| {name: 'annotationColorLightGreen', color: '#f0f4c3'}, |
| {name: 'annotationColorLightBlue', color: '#9fa8da'}, |
| {name: 'annotationColorLavender', color: '#d1c4e9'}, |
| {name: 'annotationColorLightTeal', color: '#b2dfdb'}, |
| ]; |
| |
| interface Size { |
| name: string; |
| size: number; |
| } |
| |
| const sizes: Size[] = [ |
| {name: 'annotationSize1', size: 0}, |
| {name: 'annotationSize2', size: 0.1429}, |
| {name: 'annotationSize3', size: 0.2857}, |
| {name: 'annotationSize4', size: 0.4286}, |
| {name: 'annotationSize8', size: 0.5714}, |
| {name: 'annotationSize12', size: 0.7143}, |
| {name: 'annotationSize16', size: 0.8571}, |
| {name: 'annotationSize20', size: 1}, |
| ]; |
| |
| export interface ViewerPenOptionsElement { |
| $: { |
| colors: HTMLElement, |
| expand: CrIconButtonElement, |
| separator: HTMLElement, |
| }; |
| } |
| |
| // Displays a set of radio buttons to select from a predefined list of colors |
| // and sizes. |
| export class ViewerPenOptionsElement extends PolymerElement { |
| static get is() { |
| return 'viewer-pen-options'; |
| } |
| |
| static get template() { |
| return getTemplate(); |
| } |
| |
| static get properties() { |
| return { |
| expanded_: { |
| type: Boolean, |
| value: false, |
| }, |
| |
| selectedSize: { |
| type: Number, |
| value: 0.250, |
| notify: true, |
| }, |
| |
| selectedColor: { |
| type: String, |
| value: '#000000', |
| notify: true, |
| }, |
| |
| sizes_: { |
| type: Array, |
| value: sizes, |
| }, |
| |
| colors_: { |
| type: Array, |
| value: colors, |
| }, |
| |
| strings: Object, |
| }; |
| } |
| |
| selectedSize: number; |
| selectedColor: string; |
| strings: any; |
| private colors_: Color[]; |
| private expanded_: boolean; |
| private expandAnimations_: Animation[]|null = null; |
| private sizes_: Size[]; |
| |
| private sizeChanged_(e: Event) { |
| this.selectedSize = Number((e.target as HTMLInputElement).value); |
| } |
| |
| private colorChanged_(e: Event) { |
| this.selectedColor = (e.target as HTMLInputElement).value; |
| } |
| |
| private toggleExpanded_() { |
| this.expanded_ = !this.expanded_; |
| this.updateExpandedState_(); |
| } |
| |
| private updateExpandedStateAndFinishAnimations_() { |
| this.updateExpandedState_(); |
| assert(this.expandAnimations_); |
| for (const animation of this.expandAnimations_) { |
| animation.finish(); |
| } |
| } |
| |
| override connectedCallback() { |
| super.connectedCallback(); |
| beforeNextRender(this, () => { |
| this.updateExpandedStateAndFinishAnimations_(); |
| }); |
| } |
| |
| /** |
| * Updates the state of the UI to reflect the current value of `expanded`. |
| * Starts or reverses animations and enables/disable controls. |
| */ |
| private updateExpandedState_() { |
| const colors = this.$.colors; |
| if (!this.expandAnimations_) { |
| const separator = this.$.separator; |
| const expand = this.$.expand; |
| this.expandAnimations_ = [ |
| colors.animate( |
| [ |
| {height: '32px'}, |
| {height: '188px'}, |
| ], |
| { |
| easing: 'ease-in-out', |
| duration: 250, |
| fill: 'both', |
| }), |
| separator.animate( |
| [ |
| {opacity: 0}, |
| {opacity: 1}, |
| ], |
| { |
| easing: 'ease-in-out', |
| duration: 250, |
| fill: 'both', |
| }), |
| expand.animate( |
| [ |
| {transform: 'rotate(0deg)'}, |
| {transform: 'rotate(180deg)'}, |
| ], |
| { |
| easing: 'ease-in-out', |
| duration: 250, |
| fill: 'forwards', |
| }), |
| ]; |
| } |
| for (const animation of this.expandAnimations_) { |
| // TODO(dstockwell): Ideally we would just set playbackRate, |
| // but there appears to be a web-animations bug that |
| // results in the animation getting stuck in the 'pending' |
| // state sometimes. See crbug.com/938857 |
| const currentTime = animation.currentTime; |
| animation.cancel(); |
| animation.playbackRate = this.expanded_ ? 1 : -1; |
| animation.currentTime = currentTime; |
| animation.play(); |
| } |
| for (const input of colors.querySelectorAll('input:nth-child(n+8)')) { |
| input.toggleAttribute('disabled', !this.expanded_); |
| } |
| } |
| |
| /** Used to determine equality in computed bindings. */ |
| private equal_<T>(a: T, b: T): boolean { |
| return a === b; |
| } |
| |
| /** Used to lookup a string in a computed binding. */ |
| private lookup_(strings: {[key: string]: string}, name: string): string { |
| return strings ? strings[name] : ''; |
| } |
| |
| /** |
| * Used to remove focus when clicking or tapping on a styled input |
| * element. This is a workaround until we can use the :focus-visible |
| * pseudo selector. |
| */ |
| blurOnPointerDown(e: Event) { |
| const target = e.target as HTMLInputElement; |
| setTimeout(() => target.blur(), 0); |
| } |
| } |
| |
| declare global { |
| interface HTMLElementTagNameMap { |
| 'viewer-pen-options': ViewerPenOptionsElement; |
| } |
| } |
| |
| customElements.define(ViewerPenOptionsElement.is, ViewerPenOptionsElement); |