| // Copyright 2016 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/cr_icons_css.m.js'; |
| import 'chrome://resources/cr_elements/shared_vars_css.m.js'; |
| import './shared_style.js'; |
| import './strings.m.js'; |
| |
| import {assert} from 'chrome://resources/js/assert.m.js'; |
| import {isMac} from 'chrome://resources/js/cr.m.js'; |
| import {focusWithoutInk} from 'chrome://resources/js/cr/ui/focus_without_ink.m.js'; |
| import {getFaviconForPageURL} from 'chrome://resources/js/icon.m.js'; |
| import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js'; |
| import {html, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js'; |
| |
| import {selectItem} from './actions.js'; |
| import {CommandManager} from './command_manager.js'; |
| import {Command, MenuSource} from './constants.js'; |
| import {StoreClient} from './store_client.js'; |
| import {BookmarkNode} from './types.js'; |
| |
| Polymer({ |
| is: 'bookmarks-item', |
| |
| _template: html`{__html_template__}`, |
| |
| behaviors: [ |
| StoreClient, |
| ], |
| |
| properties: { |
| itemId: { |
| type: String, |
| observer: 'onItemIdChanged_', |
| }, |
| |
| ironListTabIndex: Number, |
| |
| /** @private {BookmarkNode} */ |
| item_: { |
| type: Object, |
| observer: 'onItemChanged_', |
| }, |
| |
| /** @private */ |
| isSelectedItem_: { |
| type: Boolean, |
| reflectToAttribute: true, |
| }, |
| |
| /** @private */ |
| isMultiSelect_: Boolean, |
| |
| /** @private */ |
| isFolder_: Boolean, |
| |
| /** @private */ |
| lastTouchPoints_: Number, |
| }, |
| |
| observers: [ |
| 'updateFavicon_(item_.url)', |
| ], |
| |
| listeners: { |
| 'click': 'onClick_', |
| 'dblclick': 'onDblClick_', |
| 'contextmenu': 'onContextMenu_', |
| 'keydown': 'onKeydown_', |
| 'auxclick': 'onMiddleClick_', |
| 'mousedown': 'cancelMiddleMouseBehavior_', |
| 'mouseup': 'cancelMiddleMouseBehavior_', |
| 'touchstart': 'onTouchStart_', |
| }, |
| |
| /** @override */ |
| attached() { |
| this.watch('item_', store => store.nodes[this.itemId]); |
| this.watch( |
| 'isSelectedItem_', store => store.selection.items.has(this.itemId)); |
| this.watch('isMultiSelect_', store => store.selection.items.size > 1); |
| |
| this.updateFromStore(); |
| }, |
| |
| focusMenuButton() { |
| focusWithoutInk(this.$.menuButton); |
| }, |
| |
| /** @return {BookmarksItemElement} */ |
| getDropTarget() { |
| return this; |
| }, |
| |
| /** |
| * @param {Event} e |
| * @private |
| */ |
| onContextMenu_(e) { |
| e.preventDefault(); |
| e.stopPropagation(); |
| |
| // Prevent context menu from appearing after a drag, but allow opening the |
| // context menu through 2 taps |
| if (e.sourceCapabilities && e.sourceCapabilities.firesTouchEvents && |
| this.lastTouchPoints_ !== 2) { |
| return; |
| } |
| |
| this.focus(); |
| if (!this.isSelectedItem_) { |
| this.selectThisItem_(); |
| } |
| |
| this.fire('open-command-menu', { |
| x: e.clientX, |
| y: e.clientY, |
| source: MenuSource.ITEM, |
| }); |
| }, |
| |
| /** |
| * @param {Event} e |
| * @private |
| */ |
| onMenuButtonClick_(e) { |
| e.stopPropagation(); |
| e.preventDefault(); |
| |
| // Skip selecting the item if this item is part of a multi-selected group. |
| if (!this.isMultiSelectMenu_()) { |
| this.selectThisItem_(); |
| } |
| |
| this.fire('open-command-menu', { |
| targetElement: e.target, |
| source: MenuSource.ITEM, |
| }); |
| }, |
| |
| /** @private */ |
| selectThisItem_() { |
| this.dispatch(selectItem(this.itemId, this.getState(), { |
| clear: true, |
| range: false, |
| toggle: false, |
| })); |
| }, |
| |
| /** @private */ |
| onItemIdChanged_() { |
| // TODO(tsergeant): Add a histogram to measure whether this assertion fails |
| // for real users. |
| assert(this.getState().nodes[this.itemId]); |
| this.updateFromStore(); |
| }, |
| |
| /** @private */ |
| onItemChanged_() { |
| this.isFolder_ = !this.item_.url; |
| this.setAttribute( |
| 'aria-label', |
| this.item_.title || this.item_.url || |
| loadTimeData.getString('folderLabel')); |
| }, |
| |
| /** |
| * @param {MouseEvent} e |
| * @private |
| */ |
| onClick_(e) { |
| // Ignore double clicks so that Ctrl double-clicking an item won't deselect |
| // the item before opening. |
| if (e.detail !== 2) { |
| const addKey = isMac ? e.metaKey : e.ctrlKey; |
| this.dispatch(selectItem(this.itemId, this.getState(), { |
| clear: !addKey, |
| range: e.shiftKey, |
| toggle: addKey && !e.shiftKey, |
| })); |
| } |
| e.stopPropagation(); |
| e.preventDefault(); |
| }, |
| |
| /** |
| * @private |
| * @param {KeyboardEvent} e |
| */ |
| onKeydown_(e) { |
| if (e.key === 'ArrowLeft') { |
| this.focus(); |
| } else if (e.key === 'ArrowRight') { |
| this.$.menuButton.focus(); |
| } |
| }, |
| |
| /** |
| * @param {MouseEvent} e |
| * @private |
| */ |
| onDblClick_(e) { |
| if (!this.isSelectedItem_) { |
| this.selectThisItem_(); |
| } |
| |
| const commandManager = CommandManager.getInstance(); |
| const itemSet = this.getState().selection.items; |
| if (commandManager.canExecute(Command.OPEN, itemSet)) { |
| commandManager.handle(Command.OPEN, itemSet); |
| } |
| }, |
| |
| /** |
| * @param {MouseEvent} e |
| * @private |
| */ |
| onMiddleClick_(e) { |
| if (e.button !== 1) { |
| return; |
| } |
| |
| this.selectThisItem_(); |
| if (this.isFolder_) { |
| return; |
| } |
| |
| const commandManager = CommandManager.getInstance(); |
| const itemSet = this.getState().selection.items; |
| const command = e.shiftKey ? Command.OPEN : Command.OPEN_NEW_TAB; |
| if (commandManager.canExecute(command, itemSet)) { |
| commandManager.handle(command, itemSet); |
| } |
| }, |
| |
| /** |
| * @param {TouchEvent} e |
| * @private |
| */ |
| onTouchStart_(e) { |
| this.lastTouchPoints_ = e.touches.length; |
| }, |
| |
| /** |
| * Prevent default middle-mouse behavior. On Windows, this prevents autoscroll |
| * (during mousedown), and on Linux this prevents paste (during mouseup). |
| * @param {MouseEvent} e |
| * @private |
| */ |
| cancelMiddleMouseBehavior_(e) { |
| if (e.button === 1) { |
| e.preventDefault(); |
| } |
| }, |
| |
| /** |
| * @param {string} url |
| * @private |
| */ |
| updateFavicon_(url) { |
| this.$.icon.className = url ? 'website-icon' : 'folder-icon'; |
| this.$.icon.style.backgroundImage = |
| url ? getFaviconForPageURL(url, false) : ''; |
| }, |
| |
| /** |
| * @return {string} |
| * @private |
| */ |
| getButtonAriaLabel_() { |
| if (!this.item_) { |
| return ''; // Item hasn't loaded, skip for now. |
| } |
| |
| if (this.isMultiSelectMenu_()) { |
| return loadTimeData.getStringF('moreActionsMultiButtonAxLabel'); |
| } |
| |
| return loadTimeData.getStringF( |
| 'moreActionsButtonAxLabel', this.item_.title); |
| }, |
| |
| /** |
| * This item is part of a group selection. |
| * @return {boolean} |
| * @private |
| */ |
| isMultiSelectMenu_() { |
| return this.isSelectedItem_ && this.isMultiSelect_; |
| }, |
| }); |