xref: /aosp_15_r20/external/perfetto/ui/src/base/dom_utils.ts (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
1*6dbdd20aSAndroid Build Coastguard Worker// Copyright (C) 2023 The Android Open Source Project
2*6dbdd20aSAndroid Build Coastguard Worker//
3*6dbdd20aSAndroid Build Coastguard Worker// Licensed under the Apache License, Version 2.0 (the "License");
4*6dbdd20aSAndroid Build Coastguard Worker// you may not use this file except in compliance with the License.
5*6dbdd20aSAndroid Build Coastguard Worker// You may obtain a copy of the License at
6*6dbdd20aSAndroid Build Coastguard Worker//
7*6dbdd20aSAndroid Build Coastguard Worker//      http://www.apache.org/licenses/LICENSE-2.0
8*6dbdd20aSAndroid Build Coastguard Worker//
9*6dbdd20aSAndroid Build Coastguard Worker// Unless required by applicable law or agreed to in writing, software
10*6dbdd20aSAndroid Build Coastguard Worker// distributed under the License is distributed on an "AS IS" BASIS,
11*6dbdd20aSAndroid Build Coastguard Worker// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*6dbdd20aSAndroid Build Coastguard Worker// See the License for the specific language governing permissions and
13*6dbdd20aSAndroid Build Coastguard Worker// limitations under the License.
14*6dbdd20aSAndroid Build Coastguard Worker
15*6dbdd20aSAndroid Build Coastguard Workerimport {Vector2D} from './geom';
16*6dbdd20aSAndroid Build Coastguard Worker
17*6dbdd20aSAndroid Build Coastguard Worker// Check whether a DOM element contains another, or whether they're the same
18*6dbdd20aSAndroid Build Coastguard Workerexport function isOrContains(container: Element, target: Element): boolean {
19*6dbdd20aSAndroid Build Coastguard Worker  return container === target || container.contains(target);
20*6dbdd20aSAndroid Build Coastguard Worker}
21*6dbdd20aSAndroid Build Coastguard Worker
22*6dbdd20aSAndroid Build Coastguard Worker// Find a DOM element with a given "ref" attribute
23*6dbdd20aSAndroid Build Coastguard Workerexport function findRef(root: Element, ref: string): Element | null {
24*6dbdd20aSAndroid Build Coastguard Worker  const query = `[ref=${ref}]`;
25*6dbdd20aSAndroid Build Coastguard Worker  if (root.matches(query)) {
26*6dbdd20aSAndroid Build Coastguard Worker    return root;
27*6dbdd20aSAndroid Build Coastguard Worker  } else {
28*6dbdd20aSAndroid Build Coastguard Worker    return root.querySelector(query);
29*6dbdd20aSAndroid Build Coastguard Worker  }
30*6dbdd20aSAndroid Build Coastguard Worker}
31*6dbdd20aSAndroid Build Coastguard Worker
32*6dbdd20aSAndroid Build Coastguard Worker// Safely cast an Element to an HTMLElement.
33*6dbdd20aSAndroid Build Coastguard Worker// Throws if the element is not an HTMLElement.
34*6dbdd20aSAndroid Build Coastguard Workerexport function toHTMLElement(el: Element): HTMLElement {
35*6dbdd20aSAndroid Build Coastguard Worker  if (!(el instanceof HTMLElement)) {
36*6dbdd20aSAndroid Build Coastguard Worker    throw new Error('Element is not an HTMLElement');
37*6dbdd20aSAndroid Build Coastguard Worker  }
38*6dbdd20aSAndroid Build Coastguard Worker  return el as HTMLElement;
39*6dbdd20aSAndroid Build Coastguard Worker}
40*6dbdd20aSAndroid Build Coastguard Worker
41*6dbdd20aSAndroid Build Coastguard Worker// Return true if EventTarget is or is inside an editable element.
42*6dbdd20aSAndroid Build Coastguard Worker// Editable elements incluce: <input type="text">, <textarea>, or elements with
43*6dbdd20aSAndroid Build Coastguard Worker// the |contenteditable| attribute set.
44*6dbdd20aSAndroid Build Coastguard Workerexport function elementIsEditable(target: EventTarget | null): boolean {
45*6dbdd20aSAndroid Build Coastguard Worker  if (target === null) {
46*6dbdd20aSAndroid Build Coastguard Worker    return false;
47*6dbdd20aSAndroid Build Coastguard Worker  }
48*6dbdd20aSAndroid Build Coastguard Worker
49*6dbdd20aSAndroid Build Coastguard Worker  if (!(target instanceof Element)) {
50*6dbdd20aSAndroid Build Coastguard Worker    return false;
51*6dbdd20aSAndroid Build Coastguard Worker  }
52*6dbdd20aSAndroid Build Coastguard Worker
53*6dbdd20aSAndroid Build Coastguard Worker  const editable = target.closest('input, textarea, [contenteditable=true]');
54*6dbdd20aSAndroid Build Coastguard Worker
55*6dbdd20aSAndroid Build Coastguard Worker  if (editable === null) {
56*6dbdd20aSAndroid Build Coastguard Worker    return false;
57*6dbdd20aSAndroid Build Coastguard Worker  }
58*6dbdd20aSAndroid Build Coastguard Worker
59*6dbdd20aSAndroid Build Coastguard Worker  if (editable instanceof HTMLInputElement) {
60*6dbdd20aSAndroid Build Coastguard Worker    if (['radio', 'checkbox', 'button'].includes(editable.type)) {
61*6dbdd20aSAndroid Build Coastguard Worker      return false;
62*6dbdd20aSAndroid Build Coastguard Worker    }
63*6dbdd20aSAndroid Build Coastguard Worker  }
64*6dbdd20aSAndroid Build Coastguard Worker
65*6dbdd20aSAndroid Build Coastguard Worker  return true;
66*6dbdd20aSAndroid Build Coastguard Worker}
67*6dbdd20aSAndroid Build Coastguard Worker
68*6dbdd20aSAndroid Build Coastguard Worker// Returns the mouse pointer's position relative to |e.currentTarget| for a
69*6dbdd20aSAndroid Build Coastguard Worker// given |MouseEvent|.
70*6dbdd20aSAndroid Build Coastguard Worker// Similar to |offsetX|, |offsetY| but for |currentTarget| rather than |target|.
71*6dbdd20aSAndroid Build Coastguard Worker// If the event has no currentTarget or it is not an element, offsetX & offsetY
72*6dbdd20aSAndroid Build Coastguard Worker// are returned instead.
73*6dbdd20aSAndroid Build Coastguard Workerexport function currentTargetOffset(e: MouseEvent): Vector2D {
74*6dbdd20aSAndroid Build Coastguard Worker  if (e.currentTarget === e.target) {
75*6dbdd20aSAndroid Build Coastguard Worker    return new Vector2D({x: e.offsetX, y: e.offsetY});
76*6dbdd20aSAndroid Build Coastguard Worker  }
77*6dbdd20aSAndroid Build Coastguard Worker
78*6dbdd20aSAndroid Build Coastguard Worker  if (e.currentTarget && e.currentTarget instanceof Element) {
79*6dbdd20aSAndroid Build Coastguard Worker    const rect = e.currentTarget.getBoundingClientRect();
80*6dbdd20aSAndroid Build Coastguard Worker    const offsetX = e.clientX - rect.left;
81*6dbdd20aSAndroid Build Coastguard Worker    const offsetY = e.clientY - rect.top;
82*6dbdd20aSAndroid Build Coastguard Worker    return new Vector2D({x: offsetX, y: offsetY});
83*6dbdd20aSAndroid Build Coastguard Worker  }
84*6dbdd20aSAndroid Build Coastguard Worker
85*6dbdd20aSAndroid Build Coastguard Worker  return new Vector2D({x: e.offsetX, y: e.offsetY});
86*6dbdd20aSAndroid Build Coastguard Worker}
87