1
Fork 0
mirror of https://github.com/glassflame/glassflame.github.io.git synced 2024-10-16 14:37:33 +00:00

Abstract connectedCallback into onConnected

This commit is contained in:
Steffo 2023-10-24 11:04:57 +02:00
parent 1bb664b593
commit dd4ddb7620
6 changed files with 100 additions and 131 deletions

64
src/elements/base.mjs Normal file
View file

@ -0,0 +1,64 @@
export class NotImplementedError extends Error {}
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() {
super();
if(this.constructor === CustomElement) {
throw new NotImplementedError("CustomElement is being used as-is.")
}
}
onConnected() {}
static getTemplate() {
throw new NotImplementedError("CustomElement.getTemplate has not been overridden.")
}
}

View file

@ -1,26 +1,8 @@
import { fileDetails } from "../utils/file.mjs"; import { fileDetails } from "../utils/file.mjs";
import { CustomElement, NotImplementedError } from "./base.mjs";
export class CanvasElement extends HTMLElement { export class CanvasElement extends CustomElement {
/**
* Return the closest {@link CanvasElement} ancestor in the tree.
*
* @param initial {HTMLElement} The element to start the search from.
*/
static findFirstCanvasAncestor(initial) {
let current = initial
while(current) {
if(current instanceof ShadowRoot) {
current = current.host
}
if(current instanceof CanvasElement) {
return current
}
current = current.parentNode
}
return null
}
static getTemplate() { static getTemplate() {
return document.getElementById("template-canvas") return document.getElementById("template-canvas")
} }
@ -32,10 +14,8 @@ export class CanvasElement extends HTMLElement {
nodeElements = {} nodeElements = {}
edgeElements = {} edgeElements = {}
// noinspection JSUnusedGlobalSymbols onConnected() {
connectedCallback() { super.onConnected();
const instanceDocument = CanvasElement.getTemplate().content.cloneNode(true)
const shadow = this.attachShadow({ mode: "open" })
this.parsedJSON = JSON.parse(this.getAttribute("contents")) this.parsedJSON = JSON.parse(this.getAttribute("contents"))
@ -112,16 +92,11 @@ export class CanvasElement extends HTMLElement {
this.appendChild(this.nodesSlotted) this.appendChild(this.nodesSlotted)
this.appendChild(this.edgesSlotted) this.appendChild(this.edgesSlotted)
shadow.appendChild(instanceDocument)
} }
} }
/** export class CanvasItemElement extends CustomElement {
* Element representing the generic skeleton of an Obsidian Canvas item.
*/
export class CanvasItemElement extends HTMLElement {
colorToHex() { colorToHex() {
const color = this.getAttribute("color") const color = this.getAttribute("color")
@ -133,7 +108,6 @@ export class CanvasItemElement extends HTMLElement {
return color return color
} }
else { else {
// TODO: Check which colors correspond to what
return { return {
"0": "var(--color-gray)", "0": "var(--color-gray)",
"1": "var(--color-red)", "1": "var(--color-red)",
@ -145,4 +119,12 @@ export class CanvasItemElement extends HTMLElement {
}[color] }[color]
} }
} }
constructor() {
super();
if(this.constructor === CanvasItemElement) {
throw new NotImplementedError("CanvasItemElement is being used as-is.")
}
}
} }

View file

@ -2,28 +2,10 @@ import { fileDetails } from "../utils/file.mjs";
import { CanvasElement } from "./canvas.mjs"; import { CanvasElement } from "./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";
export class DisplayElement extends HTMLElement { export class DisplayElement extends CustomElement {
/**
* Return the closest {@link DisplayElement} ancestor in the tree.
*
* @param initial {HTMLElement} The element to start the search from.
*/
static findFirstDisplayAncestor(initial) {
let current = initial
while(current) {
if(current instanceof ShadowRoot) {
current = current.host
}
if(current instanceof DisplayElement) {
return current
}
current = current.parentNode
}
return null
}
static getTemplate() { static getTemplate() {
return document.getElementById("template-display") return document.getElementById("template-display")
} }
@ -31,11 +13,7 @@ export class DisplayElement extends HTMLElement {
containerSlotted containerSlotted
loadButton loadButton
// noinspection JSUnusedGlobalSymbols onConnected() {
connectedCallback() {
const instanceDocument = DisplayElement.getTemplate().content.cloneNode(true)
const shadow = this.attachShadow({ mode: "open" })
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")
@ -43,8 +21,6 @@ export class DisplayElement extends HTMLElement {
this.loadButton.addEventListener("click", this.load.bind(this)) this.loadButton.addEventListener("click", this.load.bind(this))
this.containerSlotted.appendChild(this.loadButton) this.containerSlotted.appendChild(this.loadButton)
this.appendChild(this.containerSlotted) this.appendChild(this.containerSlotted)
shadow.appendChild(instanceDocument)
} }
data data

View file

@ -9,12 +9,8 @@ export class EdgeElement extends CanvasItemElement {
svgSlotted svgSlotted
lineElement lineElement
// noinspection JSUnusedGlobalSymbols onConnected() {
connectedCallback() { const canvas = this.findFirstAncestor(CanvasElement)
const instanceDocument = EdgeElement.getTemplate().content.cloneNode(true)
const shadow = this.attachShadow({ mode: "open" })
const canvas = CanvasElement.findFirstCanvasAncestor(this)
const fromNode = canvas.nodeElements[this.getAttribute("node-from")] const fromNode = canvas.nodeElements[this.getAttribute("node-from")]
const fromSide = this.getAttribute("node-from-side") const fromSide = this.getAttribute("node-from-side")
@ -48,7 +44,5 @@ export class EdgeElement extends CanvasItemElement {
this.svgSlotted.appendChild(this.lineElement) this.svgSlotted.appendChild(this.lineElement)
this.appendChild(this.svgSlotted) this.appendChild(this.svgSlotted)
shadow.appendChild(instanceDocument)
} }
} }

View file

@ -1,12 +1,11 @@
import { Marked } from "https://unpkg.com/marked@9.1.2/lib/marked.esm.js"; import { Marked } from "https://unpkg.com/marked@9.1.2/lib/marked.esm.js";
import { fileDetails } from "../utils/file.mjs"; import { CustomElement } from "./base.mjs";
import { DisplayElement } from "./display.mjs";
/** /**
* Element rendering the Markdown contents of an Obsidian page. * Element rendering the Markdown contents of an Obsidian page.
*/ */
export class MarkdownElement extends HTMLElement { export class MarkdownElement extends CustomElement {
static marked = new Marked({ static marked = new Marked({
extensions: [ extensions: [
{ {
@ -15,7 +14,7 @@ export class MarkdownElement extends HTMLElement {
start(src) { start(src) {
return src.match(/^(-{3,})/)?.index return src.match(/^(-{3,})/)?.index
}, },
tokenizer(src, tokens) { tokenizer(src, _) {
const match = src.match(/^(-{3,})(.+)?\n((?:.+\n)*)\1\n/) const match = src.match(/^(-{3,})(.+)?\n((?:.+\n)*)\1\n/)
if(match) { if(match) {
return { return {
@ -36,7 +35,7 @@ export class MarkdownElement extends HTMLElement {
start(src) { start(src) {
return src.match(/^\[\[/)?.index return src.match(/^\[\[/)?.index
}, },
tokenizer(src, tokens) { tokenizer(src, _) {
const match = src.match(/^\[\[([^|\]]+)(?:\|([^\]]+))?]]/) const match = src.match(/^\[\[([^|\]]+)(?:\|([^\]]+))?]]/)
if(match) { if(match) {
return { return {
@ -57,7 +56,7 @@ export class MarkdownElement extends HTMLElement {
start(src) { start(src) {
return src.match(/^#/)?.index return src.match(/^#/)?.index
}, },
tokenizer(src, tokens) { tokenizer(src, _) {
const match = src.match(/^#([A-Za-z0-9]+)/) const match = src.match(/^#([A-Za-z0-9]+)/)
if(match) { if(match) {
return { return {
@ -80,11 +79,7 @@ export class MarkdownElement extends HTMLElement {
return document.getElementById("template-markdown") return document.getElementById("template-markdown")
} }
// noinspection JSUnusedGlobalSymbols onConnected() {
connectedCallback() {
const instanceDocument = MarkdownElement.getTemplate().content.cloneNode(true)
const shadow = this.attachShadow({ mode: "open" })
const markdown = this.getAttribute("contents") const markdown = this.getAttribute("contents")
this.contentsElement = document.createElement("div") this.contentsElement = document.createElement("div")
@ -92,65 +87,41 @@ export class MarkdownElement extends HTMLElement {
this.contentsElement.innerHTML = MarkdownElement.marked.parse(markdown) this.contentsElement.innerHTML = MarkdownElement.marked.parse(markdown)
this.appendChild(this.contentsElement) this.appendChild(this.contentsElement)
shadow.appendChild(instanceDocument)
} }
} }
/** /**
* Element rendering Obsidian front matter. * Element rendering Obsidian front matter.
*/ */
export class FrontMatterElement extends HTMLElement { export class FrontMatterElement extends CustomElement {
static getTemplate() { static getTemplate() {
return document.getElementById("template-frontmatter") return document.getElementById("template-frontmatter")
} }
// noinspection JSUnusedGlobalSymbols
connectedCallback() {
const instanceDocument = FrontMatterElement.getTemplate().content.cloneNode(true)
const shadow = this.attachShadow({ mode: "open" })
shadow.appendChild(instanceDocument)
}
} }
/** /**
* Element rendering an Obsidian Hashtag. * Element rendering an Obsidian Hashtag.
*/ */
export class HashtagElement extends HTMLElement { export class HashtagElement extends CustomElement {
static getTemplate() { static getTemplate() {
return document.getElementById("template-hashtag") return document.getElementById("template-hashtag")
} }
// noinspection JSUnusedGlobalSymbols
connectedCallback() {
const instanceDocument = HashtagElement.getTemplate().content.cloneNode(true)
const shadow = this.attachShadow({ mode: "open" })
shadow.appendChild(instanceDocument)
}
} }
/** /**
* Element rendering an Obsidian Wikilink. * Element rendering an Obsidian Wikilink.
*/ */
export class WikilinkElement extends HTMLElement { export class WikilinkElement extends CustomElement {
static getTemplate() { static getTemplate() {
return document.getElementById("template-wikilink") return document.getElementById("template-wikilink")
} }
// noinspection JSUnusedGlobalSymbols onConnected() {
connectedCallback() { const instanceElement = this.instance.querySelector(".wikilink")
const instanceDocument = WikilinkElement.getTemplate().content.cloneNode(true)
const shadow = this.attachShadow({ mode: "open" })
const instanceElement = instanceDocument.querySelector(".wikilink")
const destinationURL = new URL(window.location) const destinationURL = new URL(window.location)
destinationURL.hash = this.getAttribute("wref") destinationURL.hash = this.getAttribute("wref")
instanceElement.href = destinationURL instanceElement.href = destinationURL
shadow.appendChild(instanceDocument)
} }
} }

View file

@ -54,12 +54,8 @@ export class NodeGroupElement extends NodeElement {
instanceElement instanceElement
labelSlotted labelSlotted
// noinspection JSUnusedGlobalSymbols onConnected() {
connectedCallback() { this.instanceElement = this.instance.querySelector(".node-group")
const instanceDocument = NodeGroupElement.getTemplate().content.cloneNode(true)
const shadow = this.attachShadow({ mode: "open" })
this.instanceElement = instanceDocument.querySelector(".node-group")
this.instanceElement.style.setProperty("left", `${this.getAttribute("x")}px`) this.instanceElement.style.setProperty("left", `${this.getAttribute("x")}px`)
this.instanceElement.style.setProperty("top", `${this.getAttribute("y")}px`) this.instanceElement.style.setProperty("top", `${this.getAttribute("y")}px`)
@ -71,8 +67,6 @@ export class NodeGroupElement extends NodeElement {
this.labelSlotted.slot = "node-label" this.labelSlotted.slot = "node-label"
this.labelSlotted.innerText = this.getAttribute("label") this.labelSlotted.innerText = this.getAttribute("label")
this.appendChild(this.labelSlotted) this.appendChild(this.labelSlotted)
shadow.appendChild(instanceDocument)
} }
} }
@ -86,12 +80,8 @@ export class NodeFileElement extends NodeElement {
nameSlotted nameSlotted
contentsSlotted contentsSlotted
// noinspection JSUnusedGlobalSymbols onConnect() {
connectedCallback() { this.instanceElement = this.instance.querySelector(".node-file")
const instanceDocument = NodeFileElement.getTemplate().content.cloneNode(true)
const shadow = this.attachShadow({ mode: "open" })
this.instanceElement = instanceDocument.querySelector(".node-file")
this.instanceElement.style.setProperty("left", `${this.getAttribute("x")}px`) this.instanceElement.style.setProperty("left", `${this.getAttribute("x")}px`)
this.instanceElement.style.setProperty("top", `${this.getAttribute("y")}px`) this.instanceElement.style.setProperty("top", `${this.getAttribute("y")}px`)
@ -111,12 +101,10 @@ export class NodeFileElement extends NodeElement {
this.contentsSlotted = document.createElement(customElements.getName(DisplayElement)) this.contentsSlotted = document.createElement(customElements.getName(DisplayElement))
this.contentsSlotted.slot = "node-contents" this.contentsSlotted.slot = "node-contents"
const firstDisplayAncestor = DisplayElement.findFirstDisplayAncestor(this) const firstDisplayAncestor = this.findFirstAncestor(DisplayElement)
this.contentsSlotted.setAttribute("vref", firstDisplayAncestor.getAttribute("vref")) this.contentsSlotted.setAttribute("vref", firstDisplayAncestor.getAttribute("vref"))
this.contentsSlotted.setAttribute("wref", this.getAttribute("file")) this.contentsSlotted.setAttribute("wref", this.getAttribute("file"))
this.appendChild(this.contentsSlotted) this.appendChild(this.contentsSlotted)
shadow.appendChild(instanceDocument)
} }
} }
@ -128,12 +116,8 @@ export class NodeTextElement extends NodeElement {
instanceElement instanceElement
contentsSlotted contentsSlotted
// noinspection JSUnusedGlobalSymbols onConnected() {
connectedCallback() { this.instanceElement = this.instance.querySelector(".node-text")
const instanceDocument = NodeTextElement.getTemplate().content.cloneNode(true)
const shadow = this.attachShadow({ mode: "open" })
this.instanceElement = instanceDocument.querySelector(".node-text")
this.instanceElement.style.setProperty("left", `${this.getAttribute("x")}px`) this.instanceElement.style.setProperty("left", `${this.getAttribute("x")}px`)
this.instanceElement.style.setProperty("top", `${this.getAttribute("y")}px`) this.instanceElement.style.setProperty("top", `${this.getAttribute("y")}px`)
@ -145,7 +129,5 @@ export class NodeTextElement extends NodeElement {
this.contentsSlotted.slot = "node-contents" this.contentsSlotted.slot = "node-contents"
this.contentsSlotted.setAttribute("contents", this.getAttribute("text")) this.contentsSlotted.setAttribute("contents", this.getAttribute("text"))
this.appendChild(this.contentsSlotted) this.appendChild(this.contentsSlotted)
shadow.appendChild(instanceDocument)
} }
} }