chrome-devtools-frontend
Version: 
Chrome DevTools UI
158 lines (144 loc) • 6.3 kB
text/typescript
// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/* eslint-disable rulesdir/no-imperative-dom-api */
/*
 * Copyright (C) 2007, 2008 Apple Inc.  All rights reserved.
 * Copyright (C) 2008 Matt Lilek <webkit@mattlilek.com>
 * Copyright (C) 2009 Joseph Pecoraro
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1.  Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 * 2.  Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
 *     its contributors may be used to endorse or promote products derived
 *     from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
import * as Common from '../../core/common/common.js';
import * as i18n from '../../core/i18n/i18n.js';
import type * as SDK from '../../core/sdk/sdk.js';
import * as Adorners from '../../ui/components/adorners/adorners.js';
import * as IconButton from '../../ui/components/icon_button/icon_button.js';
import * as UI from '../../ui/legacy/legacy.js';
import * as ElementsComponents from './components/components.js';
import {ElementsPanel} from './ElementsPanel.js';
import {ElementsTreeElement} from './ElementsTreeElement.js';
import {ElementsTreeOutline} from './ElementsTreeOutline.js';
const UIStrings = {
  /**
   * @description Link text content in Elements Tree Outline of the Elements panel
   */
  reveal: 'reveal',
} as const;
const str_ = i18n.i18n.registerUIStrings('panels/elements/ShortcutTreeElement.ts', UIStrings);
const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
export class ShortcutTreeElement extends UI.TreeOutline.TreeElement {
  private readonly nodeShortcut: SDK.DOMModel.DOMNodeShortcut;
  #hovered?: boolean;
  constructor(nodeShortcut: SDK.DOMModel.DOMNodeShortcut) {
    super('');
    this.listItemElement.createChild('div', 'selection fill');
    const title = this.listItemElement.createChild('span', 'elements-tree-shortcut-title');
    let text = nodeShortcut.nodeName.toLowerCase();
    if (nodeShortcut.nodeType === Node.ELEMENT_NODE) {
      text = '<' + text + '>';
    }
    title.textContent = '\u21AA ' + text;
    this.nodeShortcut = nodeShortcut;
    this.addRevealAdorner();
  }
  addRevealAdorner(): void {
    const adorner = new Adorners.Adorner.Adorner();
    adorner.classList.add('adorner-reveal');
    const config = ElementsComponents.AdornerManager.getRegisteredAdorner(
        ElementsComponents.AdornerManager.RegisteredAdorners.REVEAL);
    const name = config.name;
    const adornerContent = document.createElement('span');
    const linkIcon = IconButton.Icon.create('select-element');
    const slotText = document.createElement('span');
    slotText.textContent = name;
    adornerContent.append(linkIcon);
    adornerContent.append(slotText);
    adornerContent.classList.add('adorner-with-icon');
    adorner.data = {
      name,
      content: adornerContent,
      jslogContext: 'reveal',
    };
    this.listItemElement.appendChild(adorner);
    const onClick = ((() => {
                       this.nodeShortcut.deferredNode.resolve(
                           node => {
                             void Common.Revealer.reveal(node);
                           },
                       );
                     }) as EventListener);
    adorner.addInteraction(onClick, {
      isToggle: false,
      shouldPropagateOnKeydown: false,
      ariaLabelDefault: i18nString(UIStrings.reveal),
      ariaLabelActive: i18nString(UIStrings.reveal),
    });
    adorner.addEventListener('mousedown', e => e.consume(), false);
    ElementsPanel.instance().registerAdorner(adorner);
  }
  get hovered(): boolean {
    return Boolean(this.#hovered);
  }
  set hovered(x: boolean) {
    if (this.#hovered === x) {
      return;
    }
    this.#hovered = x;
    this.listItemElement.classList.toggle('hovered', x);
  }
  deferredNode(): SDK.DOMModel.DeferredDOMNode {
    return this.nodeShortcut.deferredNode;
  }
  domModel(): SDK.DOMModel.DOMModel {
    return this.nodeShortcut.deferredNode.domModel();
  }
  private setLeftIndentOverlay(): void {
    // We use parent's `--indent` value and add 24px to account for an extra level of indent.
    let indent = 24;
    if (this.parent && this.parent instanceof ElementsTreeElement) {
      const parentIndent = parseFloat(this.parent.listItemElement.style.getPropertyValue('--indent')) || 0;
      indent += parentIndent;
    }
    this.listItemElement.style.setProperty('--indent', indent + 'px');
  }
  override onattach(): void {
    this.setLeftIndentOverlay();
  }
  override onselect(selectedByUser?: boolean): boolean {
    if (!selectedByUser) {
      return true;
    }
    this.nodeShortcut.deferredNode.highlight();
    this.nodeShortcut.deferredNode.resolve(resolved.bind(this));
    function resolved(this: ShortcutTreeElement, node: SDK.DOMModel.DOMNode|null): void {
      if (node && this.treeOutline instanceof ElementsTreeOutline) {
        this.treeOutline.selectedDOMNodeInternal = node;
        this.treeOutline.selectedNodeChanged(false);
      }
    }
    return true;
  }
}