mirror of
https://github.com/glassflame/glassflame.github.io.git
synced 2024-11-26 01:54:26 +00:00
WIP refactor of CanvasElement
This commit is contained in:
parent
fd8ea9c494
commit
a1a8b89a2b
10 changed files with 243 additions and 104 deletions
|
@ -1,64 +1,61 @@
|
||||||
export class NotImplementedError extends Error {}
|
import { NotImplementedError } from "../utils/errors.mjs";
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abstract base utility class to simplify the construction of custom elements.
|
||||||
|
* @abstract Implementors must override {@link template}.
|
||||||
|
*/
|
||||||
export class CustomElement extends HTMLElement {
|
export class CustomElement extends HTMLElement {
|
||||||
|
|
||||||
template
|
|
||||||
shadow
|
|
||||||
instance
|
|
||||||
|
|
||||||
// noinspection JSUnusedGlobalSymbols
|
|
||||||
connectedCallback() {
|
|
||||||
// The template to duplicate.
|
|
||||||
this.template = this.constructor.getTemplate()
|
|
||||||
// The shadow root, the inner contents of the element..
|
|
||||||
this.shadow = this.attachShadow({ mode: "open" })
|
|
||||||
// The element contained inside the shadow root..
|
|
||||||
this.instance = this.template.content.cloneNode(true)
|
|
||||||
|
|
||||||
// Call the custom callback.
|
|
||||||
this.onConnected()
|
|
||||||
|
|
||||||
// Add the instance to the DOM.
|
|
||||||
this.shadow.appendChild(this.instance)
|
|
||||||
}
|
|
||||||
|
|
||||||
findFirstAncestor(constructor) {
|
|
||||||
let current = this
|
|
||||||
// Keep iterating over nodes
|
|
||||||
while(current) {
|
|
||||||
// The ancestor has been found!
|
|
||||||
if(current instanceof constructor) {
|
|
||||||
return current
|
|
||||||
}
|
|
||||||
// Use .host to access the parent of a ShadowRoot
|
|
||||||
else if(current instanceof ShadowRoot) {
|
|
||||||
current = current.host
|
|
||||||
}
|
|
||||||
// Use .parentNode to access the parent of a HTMLElement
|
|
||||||
else if(current instanceof HTMLElement) {
|
|
||||||
current = current.parentNode
|
|
||||||
}
|
|
||||||
// Something went wrong?
|
|
||||||
else {
|
|
||||||
console.warn("[findFirstAncestor] Reached unknown node:", current)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// The ancestor has NOT been found...
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
// Prevent accidental instantiation of this class.
|
||||||
if(this.constructor === CustomElement) {
|
if(this.constructor === CustomElement) {
|
||||||
throw new NotImplementedError("CustomElement is being used as-is.")
|
throw new NotImplementedError("CustomElement is being used as-is, but is an abstract class.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onConnected() {}
|
/**
|
||||||
|
* Get the `<template>` to use when instantiating this element.
|
||||||
static getTemplate() {
|
* @abstract Must be overridden!
|
||||||
throw new NotImplementedError("CustomElement.getTemplate has not been overridden.")
|
*/
|
||||||
|
static get template() {
|
||||||
|
throw new NotImplementedError("template has not been overridden.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The local cloned instance of the template node.
|
||||||
|
* @type {Node}
|
||||||
|
*/
|
||||||
|
#instance
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The local cloned instance of the template node.
|
||||||
|
* @returns {Node}
|
||||||
|
*/
|
||||||
|
get instance() {
|
||||||
|
return this.#instance
|
||||||
|
}
|
||||||
|
|
||||||
|
// noinspection JSUnusedGlobalSymbols
|
||||||
|
/**
|
||||||
|
* Callback automatically called when this element is added to the DOM.
|
||||||
|
*/
|
||||||
|
connectedCallback() {
|
||||||
|
// The template to duplicate.
|
||||||
|
const template = this.constructor.getTemplate()
|
||||||
|
// The shadow root, the inner contents of the element..
|
||||||
|
const shadow = this.attachShadow({ mode: "open" })
|
||||||
|
// The element contained inside the shadow root..
|
||||||
|
this.#instance = template.content.cloneNode(true)
|
||||||
|
// Call the custom callback.
|
||||||
|
this.onConnect()
|
||||||
|
// Add the instance to the DOM.
|
||||||
|
shadow.appendChild(this.#instance)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Do something just before `instance` is added to
|
||||||
|
* @abstract Will do nothing if not overridden.
|
||||||
|
*/
|
||||||
|
onConnect() {}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,20 +1,109 @@
|
||||||
import { fileDetails } from "../utils/file.mjs";
|
import { fileDetails } from "../../utils/file.mjs";
|
||||||
import { CustomElement, NotImplementedError } from "./base.mjs";
|
import { CustomElement } from "../base.mjs";
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The renderer of an Obsidian Canvas.
|
* The renderer of an Obsidian Canvas.
|
||||||
*/
|
*/
|
||||||
export class CanvasElement extends CustomElement {
|
export class CanvasElement extends CustomElement {
|
||||||
static getTemplate() {
|
static get template() {
|
||||||
return document.getElementById("template-canvas")
|
return document.getElementById("template-canvas")
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parsed value of the `contents` attribute at the moment of connection to the DOM.
|
* The contents of the Canvas, as they were the last time they were updated.
|
||||||
* @type {any}
|
* @type {string}
|
||||||
*/
|
*/
|
||||||
parsedContents
|
#contents
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The contents of the Canvas, as they were the last time they were updated.
|
||||||
|
* @returns {string} The raw contents.
|
||||||
|
*/
|
||||||
|
get contents() {
|
||||||
|
return this.#contents
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The parsed contents of the Canvas, as they were the last time they were updated.
|
||||||
|
* @type {Object}
|
||||||
|
*/
|
||||||
|
#parsedContents
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The parsed contents of the Canvas, as they were the last time they were updated.
|
||||||
|
* @returns {Object} The parsed contents.
|
||||||
|
*/
|
||||||
|
get parsedContents() {
|
||||||
|
return this.#parsedContents
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the values of {@link contents} and {@link parsedContents} from the `contents` attribute of the element.
|
||||||
|
* @throws SyntaxError If `contents` is not valid JSON.
|
||||||
|
*/
|
||||||
|
updateContents() {
|
||||||
|
this.#contents = this.getAttribute("contents")
|
||||||
|
this.#parsedContents = JSON.parse(this.#contents)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The minimum X node found in the items of this Canvas.
|
||||||
|
* Used to compute this element's rect.
|
||||||
|
* Can be computed from {@link contents} with {@link computeMinMax}.
|
||||||
|
* @type {{x: number, width: number}}
|
||||||
|
*/
|
||||||
|
minX
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The minimum Y node found in the items of this Canvas.
|
||||||
|
* Used to compute this element's rect.
|
||||||
|
* Can be computed from {@link contents} with {@link computeMinMax}.
|
||||||
|
* @type {{y: number, height: number}}
|
||||||
|
*/
|
||||||
|
minY
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The maximum X node found in the items of this Canvas.
|
||||||
|
* Used to compute this element's rect.
|
||||||
|
* Can be computed from {@link contents} with {@link computeMinMax}.
|
||||||
|
* @type {{x: number, width: number}}
|
||||||
|
*/
|
||||||
|
maxX
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The maximum Y node found in the items of this Canvas.
|
||||||
|
* Used to compute this element's rect.
|
||||||
|
* Can be computed from {@link contents} with {@link computeMinMax}.
|
||||||
|
* @type {{y: number, height: number}}
|
||||||
|
*/
|
||||||
|
maxY
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compute {@link minX}, {@link minY}, {@link maxX}, {@link maxY} from {@link contents}.
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
computeMinMax() {
|
||||||
|
// Define initial values.
|
||||||
|
this.minX = { x: Infinity, width: 0 }
|
||||||
|
this.minY = { y: Infinity, height: 0 }
|
||||||
|
this.maxX = { x: -Infinity, width: 0 }
|
||||||
|
this.maxY = { y: -Infinity, height: 0 }
|
||||||
|
// Iterate over nodes.
|
||||||
|
for(const node of this.parsedContents["nodes"]) {
|
||||||
|
// Convert node values from strings to numbers.
|
||||||
|
let {x, y, width, height} = node
|
||||||
|
x, y, width, height = Number(x), Number(y), Number(width), Number(height)
|
||||||
|
// Update minX.
|
||||||
|
if(x < this.minX.x) this.minX = node
|
||||||
|
// Update minY.
|
||||||
|
if(y < this.minY.y) this.minY = node
|
||||||
|
// Update maxX.
|
||||||
|
if(x + width > this.maxX.x + width) this.maxX = node
|
||||||
|
// Update maxY.
|
||||||
|
if(y + height > this.maxY.y + height) this.maxY = node
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* `<div>` containing all the {@link NodeElement}s of this Canvas.
|
* `<div>` containing all the {@link NodeElement}s of this Canvas.
|
||||||
|
@ -22,12 +111,6 @@ export class CanvasElement extends CustomElement {
|
||||||
*/
|
*/
|
||||||
nodesContainer
|
nodesContainer
|
||||||
|
|
||||||
/**
|
|
||||||
* `<div>` containing all the {@link NodeElement}s of this Canvas.
|
|
||||||
* @type {HTMLDivElement}
|
|
||||||
*/
|
|
||||||
edgesContainer
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mapping associating ids to their respective {@link NodeElement}s of this Canvas.
|
* Mapping associating ids to their respective {@link NodeElement}s of this Canvas.
|
||||||
* @type {{[id: string]: NodeElement}}
|
* @type {{[id: string]: NodeElement}}
|
||||||
|
@ -47,46 +130,39 @@ export class CanvasElement extends CustomElement {
|
||||||
nodeElementsByPath = {}
|
nodeElementsByPath = {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mapping associating ids to their respective {@link EdgeElement}s of this Canvas.
|
* Name of the slot where the node container should be placed.
|
||||||
* @type {{[id: string]: EdgeElement}}
|
* @type {string}
|
||||||
*/
|
*/
|
||||||
edgeElementsById = {}
|
static NODES_SLOT_NAME = "canvas-nodes"
|
||||||
|
|
||||||
onConnected() {
|
/**
|
||||||
super.onConnected();
|
* Prefix to the name of the element to create for each node.
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
static NODE_ELEMENT_NAME_PREFIX = "x-node-"
|
||||||
|
|
||||||
this.parsedContents = JSON.parse(this.getAttribute("contents"))
|
/**
|
||||||
|
* Destroy and recreate the {@link nodesContainer} with the current {@link parsedContents}, {@link minX}, {@link minY}, {@link maxX}, {@link maxY}.
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
recreateNodes() {
|
||||||
|
if(this.nodesContainer) {
|
||||||
|
this.nodesContainer.remove()
|
||||||
|
this.nodesContainer = null
|
||||||
|
}
|
||||||
|
|
||||||
this.nodesContainer = document.createElement("div")
|
this.nodesContainer = document.createElement("div")
|
||||||
this.nodesContainer.slot = "canvas-nodes"
|
this.nodesContainer.slot = this.constructor.NODES_SLOT_NAME
|
||||||
|
|
||||||
this.edgesContainer = document.createElement("div")
|
|
||||||
this.edgesContainer.slot = "canvas-edges"
|
|
||||||
|
|
||||||
let minX = { x: Infinity, width: 0 }
|
|
||||||
let minY = { y: Infinity, height: 0 }
|
|
||||||
let maxX = { x: -Infinity, width: 0 }
|
|
||||||
let maxY = { y: -Infinity, height: 0 }
|
|
||||||
|
|
||||||
for(const node of this.parsedContents["nodes"]) {
|
|
||||||
let {x, y, width, height} = node
|
|
||||||
x, y, width, height = Number(x), Number(y), Number(width), Number(height)
|
|
||||||
|
|
||||||
if(x < minX.x) minX = node
|
|
||||||
if(y < minY.y) minY = node
|
|
||||||
if(x + width > maxX.x + width) maxX = node
|
|
||||||
if(y + height > maxY.y + height) maxY = node
|
|
||||||
}
|
|
||||||
|
|
||||||
for(const node of this.parsedContents["nodes"]) {
|
for(const node of this.parsedContents["nodes"]) {
|
||||||
let {id, type, color, x, y, width, height} = node
|
let {id, type, color, x, y, width, height} = node
|
||||||
x, y, width, height = Number(x), Number(y), Number(width), Number(height)
|
x, y, width, height = Number(x), Number(y), Number(width), Number(height)
|
||||||
|
|
||||||
const element = document.createElement(`x-node-${type}`)
|
const element = document.createElement(`${this.constructor.NODE_ELEMENT_NAME_PREFIX}${type}`)
|
||||||
|
|
||||||
element.setAttribute("id", `node-${id}`)
|
element.setAttribute("id", `node-${id}`)
|
||||||
element.setAttribute("x", `${x - minX.x}`)
|
element.setAttribute("x", `${x - this.minX.x}`)
|
||||||
element.setAttribute("y", `${y - minY.y}`)
|
element.setAttribute("y", `${y - this.minY.y}`)
|
||||||
element.setAttribute("width", `${width}`)
|
element.setAttribute("width", `${width}`)
|
||||||
element.setAttribute("height", `${height}`)
|
element.setAttribute("height", `${height}`)
|
||||||
if(color) element.setAttribute("color", color)
|
if(color) element.setAttribute("color", color)
|
||||||
|
@ -120,6 +196,30 @@ export class CanvasElement extends CustomElement {
|
||||||
|
|
||||||
this.nodesContainer.appendChild(element)
|
this.nodesContainer.appendChild(element)
|
||||||
}
|
}
|
||||||
|
// TODO: You were here last time!
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* `<div>` containing all the {@link NodeElement}s of this Canvas.
|
||||||
|
* @type {HTMLDivElement}
|
||||||
|
*/
|
||||||
|
edgesContainer
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mapping associating ids to their respective {@link EdgeElement}s of this Canvas.
|
||||||
|
* @type {{[id: string]: EdgeElement}}
|
||||||
|
*/
|
||||||
|
edgeElementsById = {}
|
||||||
|
|
||||||
|
|
||||||
|
onConnect() {
|
||||||
|
this.updateContents()
|
||||||
|
this.computeMinMax()
|
||||||
|
this.recreateNodes()
|
||||||
|
|
||||||
|
this.edgesContainer = document.createElement("div")
|
||||||
|
this.edgesContainer.slot = "canvas-edges"
|
||||||
|
|
||||||
|
|
||||||
for(const edge of this.parsedContents["edges"]) {
|
for(const edge of this.parsedContents["edges"]) {
|
||||||
let {id, fromNode, fromSide, toNode, toSide, color, toEnd: arrows} = edge
|
let {id, fromNode, fromSide, toNode, toSide, color, toEnd: arrows} = edge
|
0
src/elements/canvas/canvasitem.mjs
Normal file
0
src/elements/canvas/canvasitem.mjs
Normal file
|
@ -1,5 +1,5 @@
|
||||||
import { fileDetails } from "../utils/file.mjs";
|
import { fileDetails } from "../utils/file.mjs";
|
||||||
import { CanvasElement } from "./canvas.mjs";
|
import { CanvasElement } from "./canvas/canvas.mjs";
|
||||||
import { MarkdownElement } from "./markdown.mjs";
|
import { MarkdownElement } from "./markdown.mjs";
|
||||||
import { FetchError } from "./node.mjs";
|
import { FetchError } from "./node.mjs";
|
||||||
import { CustomElement } from "./base.mjs";
|
import { CustomElement } from "./base.mjs";
|
||||||
|
@ -13,7 +13,7 @@ export class DisplayElement extends CustomElement {
|
||||||
containerSlotted
|
containerSlotted
|
||||||
loadButton
|
loadButton
|
||||||
|
|
||||||
onConnected() {
|
onConnect() {
|
||||||
this.containerSlotted = document.createElement("div")
|
this.containerSlotted = document.createElement("div")
|
||||||
this.containerSlotted.slot = "display-container"
|
this.containerSlotted.slot = "display-container"
|
||||||
this.loadButton = document.createElement("button")
|
this.loadButton = document.createElement("button")
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { CanvasElement, CanvasItemElement } from "./canvas.mjs";
|
import { CanvasElement, CanvasItemElement } from "./canvas/canvas.mjs";
|
||||||
|
|
||||||
|
|
||||||
export class EdgeElement extends CanvasItemElement {
|
export class EdgeElement extends CanvasItemElement {
|
||||||
|
@ -9,7 +9,7 @@ export class EdgeElement extends CanvasItemElement {
|
||||||
svgSlotted
|
svgSlotted
|
||||||
lineElement
|
lineElement
|
||||||
|
|
||||||
onConnected() {
|
onConnect() {
|
||||||
const canvas = this.findFirstAncestor(CanvasElement)
|
const canvas = this.findFirstAncestor(CanvasElement)
|
||||||
|
|
||||||
const fromNode = canvas.nodeElements[this.getAttribute("node-from")]
|
const fromNode = canvas.nodeElements[this.getAttribute("node-from")]
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
export {NodeFileElement, NodeGroupElement, NodeTextElement} from "./node.mjs"
|
export {NodeFileElement, NodeGroupElement, NodeTextElement} from "./node.mjs"
|
||||||
export {MarkdownElement, HashtagElement, WikilinkElement, FrontMatterElement} from "./markdown.mjs"
|
export {MarkdownElement, HashtagElement, WikilinkElement, FrontMatterElement} from "./markdown.mjs"
|
||||||
export {CanvasElement} from "./canvas.mjs"
|
export {CanvasElement} from "./canvas/canvas.mjs"
|
||||||
export {DisplayElement} from "./display.mjs"
|
export {DisplayElement} from "./display.mjs"
|
||||||
export {EdgeElement} from "./edge.mjs"
|
export {EdgeElement} from "./edge.mjs"
|
|
@ -79,7 +79,7 @@ export class MarkdownElement extends CustomElement {
|
||||||
return document.getElementById("template-markdown")
|
return document.getElementById("template-markdown")
|
||||||
}
|
}
|
||||||
|
|
||||||
onConnected() {
|
onConnect() {
|
||||||
const markdown = this.getAttribute("contents")
|
const markdown = this.getAttribute("contents")
|
||||||
|
|
||||||
this.contentsElement = document.createElement("div")
|
this.contentsElement = document.createElement("div")
|
||||||
|
@ -116,7 +116,7 @@ export class WikilinkElement extends CustomElement {
|
||||||
return document.getElementById("template-wikilink")
|
return document.getElementById("template-wikilink")
|
||||||
}
|
}
|
||||||
|
|
||||||
onConnected() {
|
onConnect() {
|
||||||
const instanceElement = this.instance.querySelector(".wikilink")
|
const instanceElement = this.instance.querySelector(".wikilink")
|
||||||
|
|
||||||
const destinationURL = new URL(window.location)
|
const destinationURL = new URL(window.location)
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { CanvasItemElement } from "./canvas.mjs";
|
import { CanvasItemElement } from "./canvas/canvas.mjs";
|
||||||
import { DisplayElement } from "./display.mjs";
|
import { DisplayElement } from "./display.mjs";
|
||||||
|
|
||||||
|
|
||||||
|
@ -54,7 +54,7 @@ export class NodeGroupElement extends NodeElement {
|
||||||
instanceElement
|
instanceElement
|
||||||
labelSlotted
|
labelSlotted
|
||||||
|
|
||||||
onConnected() {
|
onConnect() {
|
||||||
this.instanceElement = this.instance.querySelector(".node-group")
|
this.instanceElement = this.instance.querySelector(".node-group")
|
||||||
|
|
||||||
this.instanceElement.style.setProperty("left", `${this.getAttribute("x")}px`)
|
this.instanceElement.style.setProperty("left", `${this.getAttribute("x")}px`)
|
||||||
|
@ -80,7 +80,7 @@ export class NodeFileElement extends NodeElement {
|
||||||
nameSlotted
|
nameSlotted
|
||||||
contentsSlotted
|
contentsSlotted
|
||||||
|
|
||||||
onConnected() {
|
onConnect() {
|
||||||
this.instanceElement = this.instance.querySelector(".node-file")
|
this.instanceElement = this.instance.querySelector(".node-file")
|
||||||
|
|
||||||
this.instanceElement.style.setProperty("left", `${this.getAttribute("x")}px`)
|
this.instanceElement.style.setProperty("left", `${this.getAttribute("x")}px`)
|
||||||
|
@ -116,7 +116,7 @@ export class NodeTextElement extends NodeElement {
|
||||||
instanceElement
|
instanceElement
|
||||||
contentsSlotted
|
contentsSlotted
|
||||||
|
|
||||||
onConnected() {
|
onConnect() {
|
||||||
this.instanceElement = this.instance.querySelector(".node-text")
|
this.instanceElement = this.instance.querySelector(".node-text")
|
||||||
|
|
||||||
this.instanceElement.style.setProperty("left", `${this.getAttribute("x")}px`)
|
this.instanceElement.style.setProperty("left", `${this.getAttribute("x")}px`)
|
||||||
|
|
8
src/utils/errors.mjs
Normal file
8
src/utils/errors.mjs
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
/**
|
||||||
|
* Various common errors that can be thrown.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The called method is abstract, but has not been overridden by the child class.
|
||||||
|
*/
|
||||||
|
export class NotImplementedError extends Error {}
|
34
src/utils/trasversal.mjs
Normal file
34
src/utils/trasversal.mjs
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
/**
|
||||||
|
* Utilities to trasverse the DOM.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find the first ancestor which is an `instanceof` the given constructor.
|
||||||
|
* @param start The node to start at.
|
||||||
|
* @param constructor The constructor to match.
|
||||||
|
* @returns {HTMLElement|null} The found ancestor, or `null` if no such ancestor is found.
|
||||||
|
*/
|
||||||
|
export function findFirstAncestor(start, constructor) {
|
||||||
|
let current = start
|
||||||
|
// Keep iterating over nodes
|
||||||
|
while(current) {
|
||||||
|
// The ancestor has been found!
|
||||||
|
if(current instanceof constructor) {
|
||||||
|
return current
|
||||||
|
}
|
||||||
|
// Use .host to access the parent of a ShadowRoot
|
||||||
|
else if(current instanceof ShadowRoot) {
|
||||||
|
current = current.host
|
||||||
|
}
|
||||||
|
// Use .parentNode to access the parent of a HTMLElement
|
||||||
|
else if(current instanceof HTMLElement) {
|
||||||
|
current = current.parentNode
|
||||||
|
}
|
||||||
|
// Something went wrong?
|
||||||
|
else {
|
||||||
|
console.warn("[findFirstAncestor] Reached unknown node:", current)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// The ancestor has NOT been found...
|
||||||
|
return null
|
||||||
|
}
|
Loading…
Reference in a new issue