chromium/chrome/browser/resources/bookmarks/mouse_focus_behavior.ts

// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import type {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
import {dedupingMixin} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';

export const HIDE_FOCUS_RING_ATTRIBUTE = 'hide-focus-ring';

type Constructor<T> = new ( ...args: any[]) => T;

/**
 * Behavior which adds the 'hide-focus-ring' attribute to a target element
 * when the user interacts with it using the mouse, allowing the focus outline
 * to be hidden without affecting keyboard users.
 */
export const MouseFocusMixin = dedupingMixin(
    <T extends Constructor<PolymerElement>>(superClass: T): T&
    Constructor<MouseFocusMixinInterface> => {
      class MouseFocusMixin extends superClass {
        private boundOnMousedown_: (e: Event) => void;
        boundOnKeydown: (e: KeyboardEvent) => void;

        override connectedCallback() {
          super.connectedCallback();
          this.boundOnMousedown_ = this.onMousedown_.bind(this);
          this.boundOnKeydown = this.onKeydown_.bind(this);

          // These events are added to the document because capture doesn't work
          // properly when listeners are added to a Polymer element, because the
          // event is considered AT_TARGET for the element, and is evaluated
          // after inner captures.
          document.addEventListener('mousedown', this.boundOnMousedown_, true);
          document.addEventListener('keydown', this.boundOnKeydown, true);
        }

        override disconnectedCallback() {
          super.disconnectedCallback();
          document.removeEventListener(
              'mousedown', this.boundOnMousedown_, true);
          document.removeEventListener(
              'keydown', this.boundOnKeydown, true);
        }

        private onMousedown_() {
          this.setAttribute(HIDE_FOCUS_RING_ATTRIBUTE, '');
        }

        private onKeydown_(e: KeyboardEvent) {
          if (!['Shift', 'Alt', 'Control', 'Meta'].includes(e.key)) {
            this.removeAttribute(HIDE_FOCUS_RING_ATTRIBUTE);
          }
        }
      }

      return MouseFocusMixin;
    });

export interface MouseFocusMixinInterface {
  boundOnKeydown: (e: KeyboardEvent) => void;
}