| // Copyright 2012 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| /** |
| * The class name to set on the document element. |
| */ |
| const CLASS_NAME = 'focus-outline-visible'; |
| |
| const docsToManager: Map<Document, FocusOutlineManager> = new Map(); |
| |
| /** |
| * This class sets a CSS class name on the HTML element of |doc| when the user |
| * presses a key. It removes the class name when the user clicks anywhere. |
| * |
| * This allows you to write CSS like this: |
| * |
| * html.focus-outline-visible my-element:focus { |
| * outline: 5px auto -webkit-focus-ring-color; |
| * } |
| * |
| * And the outline will only be shown if the user uses the keyboard to get to |
| * it. |
| * |
| */ |
| export class FocusOutlineManager { |
| // Whether focus change is triggered by a keyboard event. |
| private focusByKeyboard_: boolean = true; |
| private classList_: DOMTokenList; |
| |
| /** |
| * @param doc The document to attach the focus outline manager to. |
| */ |
| constructor(doc: Document) { |
| this.classList_ = doc.documentElement.classList; |
| |
| doc.addEventListener('keydown', (e) => this.onEvent_(true, e), true); |
| doc.addEventListener('mousedown', (e) => this.onEvent_(false, e), true); |
| |
| this.updateVisibility(); |
| } |
| |
| private onEvent_(focusByKeyboard: boolean, e: MouseEvent|KeyboardEvent) { |
| if (this.focusByKeyboard_ === focusByKeyboard) { |
| return; |
| } |
| if (e instanceof KeyboardEvent && e.repeat) { |
| // A repeated keydown should not trigger the focus state. For example, |
| // there is a repeated ALT keydown if ALT+CLICK is used to open the |
| // context menu and ALT is not released. |
| return; |
| } |
| this.focusByKeyboard_ = focusByKeyboard; |
| this.updateVisibility(); |
| } |
| |
| updateVisibility() { |
| this.visible = this.focusByKeyboard_; |
| } |
| |
| /** |
| * Whether the focus outline should be visible. |
| */ |
| set visible(visible: boolean) { |
| this.classList_.toggle(CLASS_NAME, visible); |
| } |
| |
| get visible(): boolean { |
| return this.classList_.contains(CLASS_NAME); |
| } |
| |
| /** |
| * Gets a per document singleton focus outline manager. |
| * @param doc The document to get the |FocusOutlineManager| for. |
| * @return The per document singleton focus outline manager. |
| */ |
| static forDocument(doc: Document): FocusOutlineManager { |
| let manager = docsToManager.get(doc); |
| if (!manager) { |
| manager = new FocusOutlineManager(doc); |
| docsToManager.set(doc, manager); |
| } |
| return manager; |
| } |
| } |