UNPKG

chrome-devtools-frontend

Version:
159 lines (136 loc) 5.4 kB
// 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 * as Common from '../../../../core/common/common.js'; import * as Platform from '../../../../core/platform/platform.js'; import * as VisualLogging from '../../../visual_logging/visual_logging.js'; import * as UI from '../../legacy.js'; import {ColorSwatch} from './ColorSwatch.js'; import swatchPopoverStyles from './swatchPopover.css.js'; export class SwatchPopoverHelper extends Common.ObjectWrapper.ObjectWrapper<EventTypes> { private readonly popover: UI.GlassPane.GlassPane; private readonly hideProxy: () => void; private readonly boundOnKeyDown: (event: KeyboardEvent) => void; private readonly boundFocusOut: (event: FocusEvent) => void; private isHidden: boolean; private anchorElement: Element|null; private view?: UI.Widget.Widget; private hiddenCallback?: ((arg0: boolean) => void); private focusRestorer?: UI.Widget.WidgetFocusRestorer; constructor() { super(); this.popover = new UI.GlassPane.GlassPane(); this.popover.setSizeBehavior(UI.GlassPane.SizeBehavior.MEASURE_CONTENT); this.popover.setMarginBehavior(UI.GlassPane.MarginBehavior.DEFAULT_MARGIN); this.popover.element.addEventListener('mousedown', e => e.consume(), false); this.hideProxy = this.hide.bind(this, true); this.boundOnKeyDown = this.onKeyDown.bind(this); this.boundFocusOut = this.onFocusOut.bind(this); this.isHidden = true; this.anchorElement = null; } private onFocusOut(event: FocusEvent): void { const relatedTarget = (event.relatedTarget as Element | null); if (this.isHidden || !relatedTarget || !this.view || relatedTarget.isSelfOrDescendant(this.view.contentElement)) { return; } this.hideProxy(); } setAnchorElement(anchorElement: Element): void { this.anchorElement = anchorElement; } isShowing(view?: UI.Widget.Widget): boolean { return this.popover.isShowing() && ((view && this.view === view) || !view); } show(view: UI.Widget.Widget, anchorElement: Element, hiddenCallback?: ((arg0: boolean) => void)): void { if (this.popover.isShowing()) { if (this.anchorElement === anchorElement) { return; } // Reopen the picker for another anchor element. this.hide(true); } VisualLogging.setMappedParent(view.contentElement, anchorElement); this.popover.registerRequiredCSS(swatchPopoverStyles); this.dispatchEventToListeners(Events.WILL_SHOW_POPOVER); this.isHidden = false; this.anchorElement = anchorElement; this.view = view; this.hiddenCallback = hiddenCallback; this.reposition(); view.focus(); const document = this.popover.element.ownerDocument; document.addEventListener('mousedown', this.hideProxy, false); if (document.defaultView) { document.defaultView.addEventListener('resize', this.hideProxy, false); } this.view.contentElement.addEventListener('keydown', this.boundOnKeyDown, false); } reposition(): void { // This protects against trying to reposition the popover after it has been hidden. if (this.isHidden || !this.view) { return; } // Unbind "blur" listener to avoid reenterability: |popover.show()| will hide the popover and trigger it synchronously. this.view.contentElement.removeEventListener('focusout', this.boundFocusOut, false); this.view.show(this.popover.contentElement); if (this.anchorElement) { let anchorBox = this.anchorElement.boxInWindow(); if (ColorSwatch.isColorSwatch(this.anchorElement)) { const swatch = (this.anchorElement); if (!swatch.anchorBox) { return; } anchorBox = swatch.anchorBox; } this.popover.setContentAnchorBox(anchorBox); this.popover.show((this.anchorElement.ownerDocument)); } this.view.contentElement.addEventListener('focusout', this.boundFocusOut, false); if (!this.focusRestorer) { this.focusRestorer = new UI.Widget.WidgetFocusRestorer(this.view); } } hide(commitEdit?: boolean): void { if (this.isHidden) { return; } const document = this.popover.element.ownerDocument; this.isHidden = true; this.popover.hide(); document.removeEventListener('mousedown', this.hideProxy, false); if (document.defaultView) { document.defaultView.removeEventListener('resize', this.hideProxy, false); } if (this.hiddenCallback) { this.hiddenCallback.call(null, Boolean(commitEdit)); } if (this.focusRestorer) { this.focusRestorer.restore(); } this.anchorElement = null; if (this.view) { this.view.detach(); this.view.contentElement.removeEventListener('keydown', this.boundOnKeyDown, false); this.view.contentElement.removeEventListener('focusout', this.boundFocusOut, false); delete this.view; } } private onKeyDown(event: KeyboardEvent): void { if (event.key === 'Enter') { this.hide(true); event.consume(true); return; } if (event.key === Platform.KeyboardUtilities.ESCAPE_KEY) { this.hide(false); event.consume(true); } } } export const enum Events { WILL_SHOW_POPOVER = 'WillShowPopover', } export interface EventTypes { [Events.WILL_SHOW_POPOVER]: void; }