mirror of
https://github.com/glassflame/glassflame.github.io.git
synced 2024-11-25 17:44:26 +00:00
Continue refactoring
This commit is contained in:
parent
a1a8b89a2b
commit
8b45e78f4c
14 changed files with 594 additions and 249 deletions
15
index.html
15
index.html
|
@ -42,17 +42,10 @@
|
|||
<template id="template-node-group">
|
||||
<style>
|
||||
.node-group {
|
||||
position: absolute;
|
||||
box-sizing: border-box;
|
||||
|
||||
--color-node: var(--color-gray);
|
||||
outline: var(--node-group-border-width) solid var(--color-node);
|
||||
background-color: color-mix(in srgb, var(--color-node) 20%, var(--color-background));
|
||||
border-radius: 0 8px 8px 8px;
|
||||
padding: 12px;
|
||||
|
||||
overflow-x: visible;
|
||||
overflow-y: visible;
|
||||
}
|
||||
|
||||
.node-group-label {
|
||||
|
@ -73,11 +66,9 @@
|
|||
margin: 0;
|
||||
}
|
||||
</style>
|
||||
<section class="node-group">
|
||||
<aside class="node-group-label">
|
||||
<h1><slot name="node-label">{Group label}</slot></h1>
|
||||
</aside>
|
||||
</section>
|
||||
<aside class="canvas-item node node-group">
|
||||
<h1><slot name="node-group-label">{Group label}</slot></h1>
|
||||
</aside>
|
||||
</template>
|
||||
<template id="template-node-file">
|
||||
<style>
|
||||
|
|
|
@ -30,7 +30,7 @@ export class CustomElement extends HTMLElement {
|
|||
|
||||
/**
|
||||
* The local cloned instance of the template node.
|
||||
* @returns {Node}
|
||||
* @returns {DocumentFragment}
|
||||
*/
|
||||
get instance() {
|
||||
return this.#instance
|
||||
|
|
|
@ -42,7 +42,7 @@ export class CanvasElement extends CustomElement {
|
|||
* 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() {
|
||||
recalculateContents() {
|
||||
this.#contents = this.getAttribute("contents")
|
||||
this.#parsedContents = JSON.parse(this.#contents)
|
||||
}
|
||||
|
@ -50,7 +50,7 @@ export class CanvasElement extends CustomElement {
|
|||
/**
|
||||
* 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}.
|
||||
* Can be computed from {@link contents} with {@link recalculateMinMax}.
|
||||
* @type {{x: number, width: number}}
|
||||
*/
|
||||
minX
|
||||
|
@ -58,7 +58,7 @@ export class CanvasElement extends CustomElement {
|
|||
/**
|
||||
* 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}.
|
||||
* Can be computed from {@link contents} with {@link recalculateMinMax}.
|
||||
* @type {{y: number, height: number}}
|
||||
*/
|
||||
minY
|
||||
|
@ -66,7 +66,7 @@ export class CanvasElement extends CustomElement {
|
|||
/**
|
||||
* 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}.
|
||||
* Can be computed from {@link contents} with {@link recalculateMinMax}.
|
||||
* @type {{x: number, width: number}}
|
||||
*/
|
||||
maxX
|
||||
|
@ -74,7 +74,7 @@ export class CanvasElement extends CustomElement {
|
|||
/**
|
||||
* 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}.
|
||||
* Can be computed from {@link contents} with {@link recalculateMinMax}.
|
||||
* @type {{y: number, height: number}}
|
||||
*/
|
||||
maxY
|
||||
|
@ -83,7 +83,7 @@ export class CanvasElement extends CustomElement {
|
|||
* Compute {@link minX}, {@link minY}, {@link maxX}, {@link maxY} from {@link contents}.
|
||||
* @returns {void}
|
||||
*/
|
||||
computeMinMax() {
|
||||
recalculateMinMax() {
|
||||
// Define initial values.
|
||||
this.minX = { x: Infinity, width: 0 }
|
||||
this.minY = { y: Infinity, height: 0 }
|
||||
|
@ -196,7 +196,11 @@ export class CanvasElement extends CustomElement {
|
|||
|
||||
this.nodesContainer.appendChild(element)
|
||||
}
|
||||
// TODO: You were here last time!
|
||||
|
||||
this.nodesContainer.style["width"] = `${this.maxX.x + this.maxX.width - this.minX.x}px`
|
||||
this.nodesContainer.style["height"] = `${this.maxY.y + this.maxY.height - this.minY.y}px`
|
||||
|
||||
this.appendChild(this.nodesContainer)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -211,20 +215,35 @@ export class CanvasElement extends CustomElement {
|
|||
*/
|
||||
edgeElementsById = {}
|
||||
|
||||
/**
|
||||
* Name of the slot where the edge container should be placed.
|
||||
* @type {string}
|
||||
*/
|
||||
static EDGES_SLOT_NAME = "canvas-nodes"
|
||||
|
||||
onConnect() {
|
||||
this.updateContents()
|
||||
this.computeMinMax()
|
||||
this.recreateNodes()
|
||||
/**
|
||||
* Prefix to the name of the element to create for each edge.
|
||||
* @type {string}
|
||||
*/
|
||||
static EDGE_ELEMENT_NAME = "x-edge"
|
||||
|
||||
/**
|
||||
* Destroy and recreate the {@link edgesContainer} with the current {@link parsedContents}, {@link minX}, {@link minY}, {@link maxX}, {@link maxY}.
|
||||
* @returns {void}
|
||||
*/
|
||||
recreateEdges() {
|
||||
if(this.edgesContainer) {
|
||||
this.edgesContainer.remove()
|
||||
this.edgesContainer = null
|
||||
}
|
||||
|
||||
this.edgesContainer = document.createElement("div")
|
||||
this.edgesContainer.slot = "canvas-edges"
|
||||
|
||||
this.edgesContainer.slot = this.constructor.EDGES_SLOT_NAME
|
||||
|
||||
for(const edge of this.parsedContents["edges"]) {
|
||||
let {id, fromNode, fromSide, toNode, toSide, color, toEnd: arrows} = edge
|
||||
|
||||
const element = document.createElement("x-edge")
|
||||
const element = document.createElement(this.constructor.EDGE_ELEMENT_NAME)
|
||||
element.setAttribute("id", `edge-${id}`)
|
||||
element.setAttribute("node-from", fromNode)
|
||||
element.setAttribute("node-from-side", fromSide)
|
||||
|
@ -237,54 +256,16 @@ export class CanvasElement extends CustomElement {
|
|||
this.edgesContainer.appendChild(element)
|
||||
}
|
||||
|
||||
this.nodesContainer.style["width"] = `${maxX.x + maxX.width - minX.x}px`
|
||||
this.nodesContainer.style["height"] = `${maxY.y + maxY.height - minY.y}px`
|
||||
this.edgesContainer.style["width"] = `${this.maxX.x + this.maxX.width - this.minX.x}px`
|
||||
this.edgesContainer.style["height"] = `${this.maxY.y + this.maxY.height - this.minY.y}px`
|
||||
|
||||
this.edgesContainer.style["width"] = `${maxX.x + maxX.width - minX.x}px`
|
||||
this.edgesContainer.style["height"] = `${maxY.y + maxY.height - minY.y}px`
|
||||
|
||||
this.appendChild(this.nodesContainer)
|
||||
this.appendChild(this.edgesContainer)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Abstract base class for elements drawn on a {@link CanvasElement}.
|
||||
* @abstract
|
||||
*/
|
||||
export class CanvasItemElement extends CustomElement {
|
||||
/**
|
||||
* Given an Obsidian Canvas color, return its corresponding CSS color.
|
||||
* @param color {string} The color, as serialized on an Obsidian Canvas.
|
||||
* @returns {string} The corresponding CSS color.
|
||||
*/
|
||||
static colorToCSS(color) {
|
||||
if(color === undefined || color === null || color === "") {
|
||||
return "var(--color-gray)"
|
||||
}
|
||||
else if(color.startsWith("#")) {
|
||||
// This is an hex color
|
||||
return color
|
||||
}
|
||||
else {
|
||||
return {
|
||||
"0": "var(--color-gray)",
|
||||
"1": "var(--color-red)",
|
||||
"2": "var(--color-orange)",
|
||||
"3": "var(--color-yellow)",
|
||||
"4": "var(--color-green)",
|
||||
"5": "var(--color-blue)",
|
||||
"6": "var(--color-purple)",
|
||||
}[color]
|
||||
}
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
if(this.constructor === CanvasItemElement) {
|
||||
throw new NotImplementedError("CanvasItemElement is being used as-is.")
|
||||
}
|
||||
onConnect() {
|
||||
this.recalculateContents()
|
||||
this.recalculateMinMax()
|
||||
this.recreateNodes()
|
||||
this.recreateEdges()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,123 @@
|
|||
import { CustomElement } from "src/elements/base.mjs";
|
||||
|
||||
|
||||
/**
|
||||
* Abstract base class for elements drawn on a {@link CanvasElement}.
|
||||
* @abstract
|
||||
*/
|
||||
export class CanvasItemElement extends CustomElement {
|
||||
/**
|
||||
* @returns {number} The X coordinate of the top left vertex of this element.
|
||||
*/
|
||||
get x() {
|
||||
return Number(this.getAttribute("x"))
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {number} The Y coordinate of the top left vertex of this element.
|
||||
*/
|
||||
get y() {
|
||||
return Number(this.getAttribute("y"))
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {number} The horizontal width of this element.
|
||||
*/
|
||||
get width() {
|
||||
return Number(this.getAttribute("width"))
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {number} The vertical height of this element.
|
||||
*/
|
||||
get height() {
|
||||
return Number(this.getAttribute("height"))
|
||||
}
|
||||
|
||||
/**
|
||||
* The color of this element, as stored in Obsidian Canvas files.
|
||||
* Can be either `null`, a `number`, or a `string`.
|
||||
* @returns {null|number|string} The value in question.
|
||||
*/
|
||||
get obsidianColor() {
|
||||
const color = this.getAttribute("color")
|
||||
if(color === null) return null // No color specified
|
||||
|
||||
const maybeNumber = Number(color)
|
||||
if(!isNaN(maybeNumber)) return maybeNumber // Numeric color specified
|
||||
|
||||
return color // Hex color specified
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an Obsidian Canvas color, return its corresponding CSS color.
|
||||
* @param color {null|number|string} The color, as serialized in Obsidian Canvas files, or as returned by {@link obsidianColor}.
|
||||
* @returns {string} The corresponding CSS color.
|
||||
*/
|
||||
static obsidianColorToCssColor(color) {
|
||||
if(color === null || color === "") {
|
||||
return "var(--color-gray)"
|
||||
}
|
||||
else if(color.match(/^#[0-9A-F]{3}$|^#[0-9A-F]{6}$/i)) {
|
||||
return color
|
||||
}
|
||||
else {
|
||||
return {
|
||||
"0": "var(--color-gray)",
|
||||
"1": "var(--color-red)",
|
||||
"2": "var(--color-orange)",
|
||||
"3": "var(--color-yellow)",
|
||||
"4": "var(--color-green)",
|
||||
"5": "var(--color-blue)",
|
||||
"6": "var(--color-purple)",
|
||||
}[color]
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The CSS color of this element, converted from {@link obsidianColor} with {@link obsidianColorToCssColor}.
|
||||
*/
|
||||
get cssColor() {
|
||||
return this.constructor.obsidianColorToCssColor(this.obsidianColor)
|
||||
}
|
||||
|
||||
/**
|
||||
* The CSS selector of the element in the template representing the canvas item.
|
||||
* @type {string}
|
||||
*/
|
||||
static CANVAS_ITEM_SELECTOR = ".canvas-item"
|
||||
|
||||
/**
|
||||
* The element in the instance representing the canvas item.
|
||||
* Can be set via {@link recalculateCanvasItemElement}.
|
||||
* @type {HTMLElement}
|
||||
*/
|
||||
canvasItemElement
|
||||
|
||||
/**
|
||||
* Update the value of the {@link canvasItemElement} by querying the current {@link instance} with {@link CANVAS_ITEM_SELECTOR}.
|
||||
*/
|
||||
recalculateCanvasItemElement() {
|
||||
this.canvasItemElement = this.instance.querySelector(this.constructor.CANVAS_ITEM_SELECTOR)
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the CSS properties of {@link canvasItemElement}, making sure it gets rendered properly.
|
||||
* @returns {void}
|
||||
*/
|
||||
resetCanvasItemCssProperties() {
|
||||
this.canvasItemElement.style.setProperty("box-sizing", "border-box")
|
||||
this.canvasItemElement.style.setProperty("position", "absolute")
|
||||
this.canvasItemElement.style.setProperty("left", `${this.getAttribute("x")}px`)
|
||||
this.canvasItemElement.style.setProperty("top", `${this.getAttribute("y")}px`)
|
||||
this.canvasItemElement.style.setProperty("width", `${this.getAttribute("width")}px`)
|
||||
this.canvasItemElement.style.setProperty("height", `${this.getAttribute("height")}px`)
|
||||
this.canvasItemElement.style.setProperty("--color-node", this.constructor.obsidianColorToCssColor(this.getAttribute("color")))
|
||||
}
|
||||
|
||||
onConnect() {
|
||||
super.onConnect()
|
||||
this.recalculateCanvasItemElement()
|
||||
this.resetCanvasItemCssProperties()
|
||||
}
|
||||
}
|
140
src/elements/canvas/edge/base.mjs
Normal file
140
src/elements/canvas/edge/base.mjs
Normal file
|
@ -0,0 +1,140 @@
|
|||
import { CanvasElement } from "src/elements/canvas/canvas.mjs";
|
||||
import { CanvasItemElement } from "src/elements/canvas/canvasitem.mjs";
|
||||
import { findFirstAncestor } from "src/utils/trasversal.mjs";
|
||||
|
||||
|
||||
/**
|
||||
* An edge of a {@link CanvasElement}.
|
||||
*/
|
||||
export class EdgeElement extends CanvasItemElement {
|
||||
static getTemplate() {
|
||||
return document.getElementById("template-edge")
|
||||
}
|
||||
|
||||
/**
|
||||
* The canvas this element is contained in.
|
||||
* Can be recalculated with {@link recalculateCanvas}.
|
||||
* @type {CanvasElement}
|
||||
*/
|
||||
canvas
|
||||
|
||||
/**
|
||||
* Recalculate the value of {@link canvas}.
|
||||
*/
|
||||
recalculateCanvas() {
|
||||
findFirstAncestor(this, CanvasElement)
|
||||
}
|
||||
|
||||
/**
|
||||
* The id of the node this edge starts at, obtained from the `node-from` attribute.
|
||||
* @returns {string}
|
||||
*/
|
||||
get fromNodeId() {
|
||||
return this.getAttribute("node-from")
|
||||
}
|
||||
|
||||
/**
|
||||
* The id of the node this edge ends at, obtained from the `node-to` attribute.
|
||||
* @returns {string}
|
||||
*/
|
||||
get toNodeId() {
|
||||
return this.getAttribute("node-to")
|
||||
}
|
||||
|
||||
/**
|
||||
* The node this edge starts at.
|
||||
* Can be recalculated with {@link recalculateFromTo}.
|
||||
* @type {NodeElement}
|
||||
*/
|
||||
fromNode
|
||||
|
||||
/**
|
||||
* The node this edge ends at.
|
||||
* Can be recalculated with {@link recalculateFromTo}.
|
||||
* @type {NodeElement}
|
||||
*/
|
||||
toNode
|
||||
|
||||
/**
|
||||
* Recalculate the values of {@link fromNode} and {@link toNode} using the current values of {@link fromNodeId}, {@link toNodeId}, and {@link canvas}.
|
||||
* @returns {void}
|
||||
*/
|
||||
recalculateFromTo() {
|
||||
this.fromNode = this.canvas.nodeElementsById[this.fromNodeId]
|
||||
this.toNode = this.canvas.nodeElementsById[this.toNodeId]
|
||||
}
|
||||
|
||||
/**
|
||||
* The side of the node this edge starts at, obtained from the `node-from-side` attribute.
|
||||
* @returns {"top"|"bottom"|"left"|"right"}
|
||||
*/
|
||||
get nodeFromSide() {
|
||||
return this.getAttribute("node-from-side")
|
||||
}
|
||||
|
||||
/**
|
||||
* The side of the node this edge ends at, obtained from the `node-to-side` attribute.
|
||||
* @returns {"top"|"bottom"|"left"|"right"}
|
||||
*/
|
||||
get nodeToSide() {
|
||||
return this.getAttribute("node-to-side")
|
||||
}
|
||||
|
||||
/**
|
||||
* The SVG element rendering the edge.
|
||||
* Can be recreated with {@link recreateSvgElement}.
|
||||
* @type {SVGElement}
|
||||
*/
|
||||
svgElement
|
||||
|
||||
/**
|
||||
* The line element rendering the edge.
|
||||
* Can be recreated with {@link recreateSvgElement}.
|
||||
* @type {SVGLineElement}
|
||||
*/
|
||||
lineElement
|
||||
|
||||
// TODO: Last time, you were here!
|
||||
|
||||
/**
|
||||
* Recreate {@link svgElement} and {@link lineElement} with the current values of the element.
|
||||
* @returns {void}
|
||||
*/
|
||||
recreateSvgElement() {
|
||||
const [x1, y1] = this.fromNode.edgeHandle(this.nodeFromSide)
|
||||
const [x2, y2] = this.toNode.edgeHandle(this.nodeToSide)
|
||||
}
|
||||
|
||||
onConnect() {
|
||||
super.onConnect()
|
||||
this.recalculateCanvas()
|
||||
this.recalculateFromTo()
|
||||
this.recreateSvgElement()
|
||||
|
||||
const fromNode = canvas.nodeElements[this.getAttribute("node-from")]
|
||||
const fromSide = this.getAttribute("node-from-side")
|
||||
const [x1, y1] = fromNode.getCenterCoordinatesOfSide(fromSide)
|
||||
|
||||
const toNode = canvas.nodeElements[this.getAttribute("node-to")]
|
||||
const toSide = this.getAttribute("node-to-side")
|
||||
const [x2, y2] = toNode.getCenterCoordinatesOfSide(toSide)
|
||||
|
||||
this.svgSlotted = document.createElementNS("http://www.w3.org/2000/svg", "svg")
|
||||
this.svgSlotted.slot = "edge-svg"
|
||||
this.svgSlotted.style.setProperty("position", "absolute")
|
||||
this.svgSlotted.style.setProperty("left", "0")
|
||||
this.svgSlotted.style.setProperty("top", "0")
|
||||
this.svgSlotted.style.setProperty("overflow", "visible")
|
||||
|
||||
this.lineElement = document.createElementNS("http://www.w3.org/2000/svg", "line")
|
||||
this.lineElement.setAttribute("x1", x1)
|
||||
this.lineElement.setAttribute("y1", y1)
|
||||
this.lineElement.setAttribute("x2", x2)
|
||||
this.lineElement.setAttribute("y2", y2)
|
||||
this.lineElement.style.setProperty("stroke", this.constructor.colorToCSS(this.getAttribute("color")))
|
||||
this.lineElement.style.setProperty("stroke-width", "var(--edge-width)")
|
||||
|
||||
this.svgSlotted.appendChild(this.lineElement)
|
||||
this.appendChild(this.svgSlotted)
|
||||
}
|
||||
}
|
77
src/elements/canvas/node/base.mjs
Normal file
77
src/elements/canvas/node/base.mjs
Normal file
|
@ -0,0 +1,77 @@
|
|||
import { CanvasItemElement } from "src/elements/canvas/canvasitem.mjs";
|
||||
|
||||
|
||||
/**
|
||||
* Abstract base class for nodes of a {@link CanvasElement}.
|
||||
* @abstract
|
||||
*/
|
||||
export class NodeElement extends CanvasItemElement {
|
||||
/**
|
||||
* Coordinates of the point where edges connected to the top of this node should attach to.
|
||||
* @type {[number, number]}
|
||||
*/
|
||||
edgeHandleTop
|
||||
|
||||
/**
|
||||
* Coordinates of the point where edges connected to the bottom of this node should attach to.
|
||||
* @type {[number, number]}
|
||||
*/
|
||||
edgeHandleBottom
|
||||
|
||||
/**
|
||||
* Coordinates of the point where edges connected to the left of this node should attach to.
|
||||
* @type {[number, number]}
|
||||
*/
|
||||
edgeHandleLeft
|
||||
|
||||
/**
|
||||
* Coordinates of the point where edges connected to the right of this node should attach to.
|
||||
* @type {[number, number]}
|
||||
*/
|
||||
edgeHandleRight
|
||||
|
||||
/**
|
||||
* Recalculate the values of {@link edgeHandleTop}, {@link edgeHandleBottom}, {@link edgeHandleLeft}, {@link edgeHandleRight} using the current values of {@link x}, {@link y}, {@link height}, {@link width}.
|
||||
*/
|
||||
recalculateEdgeHandles() {
|
||||
this.edgeHandleTop = [
|
||||
this.x + this.width / 2,
|
||||
this.y,
|
||||
]
|
||||
this.edgeHandleBottom = [
|
||||
this.x + this.width / 2,
|
||||
this.y + this.height,
|
||||
]
|
||||
this.edgeHandleLeft = [
|
||||
this.x,
|
||||
this.y + this.height / 2,
|
||||
]
|
||||
this.edgeHandleRight = [
|
||||
this.x + this.width,
|
||||
this.y + this.height / 2,
|
||||
]
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the edge handle at the given side, selecting one of {@link edgeHandleTop}, {@link edgeHandleBottom}, {@link edgeHandleLeft}, or {@link edgeHandleRight}.
|
||||
* @param side {"top"|"bottom"|"left"|"right"} The side whose edge handle to get.
|
||||
* @returns {[number,number]} Coordinates of the edge handle.
|
||||
*/
|
||||
edgeHandle(side) {
|
||||
switch(side) {
|
||||
case "top":
|
||||
return this.edgeHandleTop
|
||||
case "bottom":
|
||||
return this.edgeHandleBottom
|
||||
case "left":
|
||||
return this.edgeHandleLeft
|
||||
case "right":
|
||||
return this.edgeHandleRight
|
||||
}
|
||||
}
|
||||
|
||||
onConnect() {
|
||||
super.onConnect()
|
||||
this.recalculateEdgeHandles()
|
||||
}
|
||||
}
|
89
src/elements/canvas/node/file.mjs
Normal file
89
src/elements/canvas/node/file.mjs
Normal file
|
@ -0,0 +1,89 @@
|
|||
import { NodeElement } from "src/elements/canvas/node/base.mjs";
|
||||
import { DisplayElement } from "src/elements/display.mjs";
|
||||
import { fileDetails } from "src/utils/file.mjs";
|
||||
import { findFirstAncestor } from "src/utils/trasversal.mjs";
|
||||
|
||||
|
||||
export class NodeFileElement extends NodeElement {
|
||||
static getTemplate() {
|
||||
return document.getElementById("template-node-file")
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the path of the file displayed by this node, relative to the root of the vault, from the `path` attribute.
|
||||
* @returns {string} The path.
|
||||
*/
|
||||
get pathRelativeToVault() {
|
||||
return this.getAttribute("path")
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name of the file displayed by this node, with no extension.
|
||||
* @returns {string} The file name.
|
||||
*/
|
||||
get fileName() {
|
||||
const {name} = fileDetails(this.pathRelativeToVault)
|
||||
return name
|
||||
}
|
||||
|
||||
/**
|
||||
* The element displaying the name of the file.
|
||||
* @type {HTMLSpanElement}
|
||||
*/
|
||||
labelElement
|
||||
|
||||
/**
|
||||
* The name of the slot where {@link labelElement} should be placed in.
|
||||
* @type {string}
|
||||
*/
|
||||
static LABEL_ELEMENT_SLOT = "node-file-label"
|
||||
|
||||
/**
|
||||
* Recreate {@link labelElement} with the current value of {@link fileName}.
|
||||
*/
|
||||
recreateLabelElement() {
|
||||
if(this.labelElement) {
|
||||
this.labelElement.remove()
|
||||
this.labelElement = null
|
||||
}
|
||||
|
||||
this.labelSlotted = document.createElement("span")
|
||||
this.labelSlotted.slot = this.constructor.LABEL_ELEMENT_SLOT
|
||||
this.labelSlotted.innerText = this.fileName
|
||||
this.appendChild(this.labelSlotted)
|
||||
}
|
||||
|
||||
/**
|
||||
* The element displaying the contents of the file.
|
||||
* @type {DisplayElement}
|
||||
*/
|
||||
contentsElement
|
||||
|
||||
/**
|
||||
* The name of the slot where {@link contentsElement} should be placed in.
|
||||
* @type {string}
|
||||
*/
|
||||
static CONTENTS_ELEMENT_SLOT = "node-file-contents"
|
||||
|
||||
/**
|
||||
* Recreate {@link labelElement} with the current value of {@link fileName}.
|
||||
*/
|
||||
recreateContentsElement() {
|
||||
if(this.contentsElement) {
|
||||
this.contentsElement.remove()
|
||||
this.contentsElement = null
|
||||
}
|
||||
|
||||
this.contentsElement = document.createElement("x-display")
|
||||
this.contentsElement.slot = this.constructor.CONTENTS_ELEMENT_SLOT
|
||||
this.contentsElement.setAttribute("vault", findFirstAncestor(this, DisplayElement).vault) // TODO: Add a vault attribute to DisplayElement
|
||||
this.contentsElement.setAttribute("path", this.pathRelativeToVault) // TODO: Add a path attribute to DisplayElement
|
||||
this.appendChild(this.contentsElement)
|
||||
}
|
||||
|
||||
onConnect() {
|
||||
super.onConnect()
|
||||
this.recreateLabelElement()
|
||||
this.recreateContentsElement()
|
||||
}
|
||||
}
|
52
src/elements/canvas/node/group.mjs
Normal file
52
src/elements/canvas/node/group.mjs
Normal file
|
@ -0,0 +1,52 @@
|
|||
import { NodeElement } from "src/elements/canvas/node/base.mjs";
|
||||
|
||||
|
||||
/**
|
||||
* A {@link NodeElement} representing a group of nodes.
|
||||
* Visual only, does not actually contain any other nodes.
|
||||
*/
|
||||
export class NodeGroupElement extends NodeElement {
|
||||
/**
|
||||
* The label text of the group.
|
||||
* Obtained from the `label` attribute of the element.
|
||||
*/
|
||||
get label() {
|
||||
return this.getAttribute("label")
|
||||
}
|
||||
|
||||
/**
|
||||
* The element displaying the name of the group.
|
||||
* @type {HTMLSpanElement}
|
||||
*/
|
||||
labelElement
|
||||
|
||||
/**
|
||||
* The name of the slot where {@link labelElement} should be placed in.
|
||||
* @type {string}
|
||||
*/
|
||||
static LABEL_ELEMENT_SLOT = "node-group-label"
|
||||
|
||||
/**
|
||||
* Recreate {@link labelElement} with the current value of {@link label}.
|
||||
*/
|
||||
recreateLabelElement() {
|
||||
if(this.labelElement) {
|
||||
this.labelElement.remove()
|
||||
this.labelElement = null
|
||||
}
|
||||
|
||||
this.labelSlotted = document.createElement("span")
|
||||
this.labelSlotted.slot = this.constructor.LABEL_ELEMENT_SLOT
|
||||
this.labelSlotted.innerText = this.label
|
||||
this.appendChild(this.labelSlotted)
|
||||
}
|
||||
|
||||
static getTemplate() {
|
||||
return document.getElementById("template-node-group")
|
||||
}
|
||||
|
||||
onConnect() {
|
||||
super.onConnect()
|
||||
this.recreateLabelElement()
|
||||
}
|
||||
}
|
51
src/elements/canvas/node/text.mjs
Normal file
51
src/elements/canvas/node/text.mjs
Normal file
|
@ -0,0 +1,51 @@
|
|||
import { NodeElement } from "src/elements/canvas/node/base.mjs";
|
||||
import { DisplayElement } from "src/elements/display.mjs";
|
||||
|
||||
|
||||
/**
|
||||
* A {@link NodeElement} directly rendering a Markdown document.
|
||||
*/
|
||||
export class NodeTextElement extends NodeElement {
|
||||
static getTemplate() {
|
||||
return document.getElementById("template-node-text")
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Markdown source of this node from the `document` attribute.
|
||||
*/
|
||||
get markdownDocument() {
|
||||
return this.getAttribute("text")
|
||||
}
|
||||
|
||||
/**
|
||||
* The element displaying the contents of the node.
|
||||
* @type {MarkdownElement}
|
||||
*/
|
||||
contentsElement
|
||||
|
||||
/**
|
||||
* The name of the slot where {@link contentsElement} should be placed in.
|
||||
* @type {string}
|
||||
*/
|
||||
static CONTENTS_ELEMENT_SLOT = "node-file-contents"
|
||||
|
||||
/**
|
||||
* Recreate {@link labelElement} with the current value of {@link fileName}.
|
||||
*/
|
||||
recreateContentsElement() {
|
||||
if(this.contentsElement) {
|
||||
this.contentsElement.remove()
|
||||
this.contentsElement = null
|
||||
}
|
||||
|
||||
this.contentsElement = document.createElement("x-markdown")
|
||||
this.contentsElement.slot = this.constructor.CONTENTS_ELEMENT_SLOT
|
||||
this.contentsElement.setAttribute("document", this.markdownDocument) // TODO: Rename the property of x-markdown to "document"
|
||||
this.appendChild(this.contentsElement)
|
||||
}
|
||||
|
||||
onConnect() {
|
||||
super.onConnect()
|
||||
this.recreateContentsElement()
|
||||
}
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
import { fileDetails } from "../utils/file.mjs";
|
||||
import { CanvasElement } from "./canvas/canvas.mjs";
|
||||
import { MarkdownElement } from "./markdown.mjs";
|
||||
import { FetchError } from "./node.mjs";
|
||||
import { FetchError } from "src/elements/canvas/node/base.mjs";
|
||||
import { CustomElement } from "./base.mjs";
|
||||
|
||||
|
||||
|
|
|
@ -1,41 +0,0 @@
|
|||
import { CanvasElement, CanvasItemElement } from "./canvas/canvas.mjs";
|
||||
|
||||
|
||||
export class EdgeElement extends CanvasItemElement {
|
||||
static getTemplate() {
|
||||
return document.getElementById("template-edge")
|
||||
}
|
||||
|
||||
svgSlotted
|
||||
lineElement
|
||||
|
||||
onConnect() {
|
||||
const canvas = this.findFirstAncestor(CanvasElement)
|
||||
|
||||
const fromNode = canvas.nodeElements[this.getAttribute("node-from")]
|
||||
const fromSide = this.getAttribute("node-from-side")
|
||||
const [x1, y1] = fromNode.getCenterCoordinatesOfSide(fromSide)
|
||||
|
||||
const toNode = canvas.nodeElements[this.getAttribute("node-to")]
|
||||
const toSide = this.getAttribute("node-to-side")
|
||||
const [x2, y2] = toNode.getCenterCoordinatesOfSide(toSide)
|
||||
|
||||
this.svgSlotted = document.createElementNS("http://www.w3.org/2000/svg", "svg")
|
||||
this.svgSlotted.slot = "edge-svg"
|
||||
this.svgSlotted.style.setProperty("position", "absolute")
|
||||
this.svgSlotted.style.setProperty("left", "0")
|
||||
this.svgSlotted.style.setProperty("top", "0")
|
||||
this.svgSlotted.style.setProperty("overflow", "visible")
|
||||
|
||||
this.lineElement = document.createElementNS("http://www.w3.org/2000/svg", "line")
|
||||
this.lineElement.setAttribute("x1", x1)
|
||||
this.lineElement.setAttribute("y1", y1)
|
||||
this.lineElement.setAttribute("x2", x2)
|
||||
this.lineElement.setAttribute("y2", y2)
|
||||
this.lineElement.style.setProperty("stroke", this.constructor.colorToCSS(this.getAttribute("color")))
|
||||
this.lineElement.style.setProperty("stroke-width", "var(--edge-width)")
|
||||
|
||||
this.svgSlotted.appendChild(this.lineElement)
|
||||
this.appendChild(this.svgSlotted)
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
export {NodeFileElement, NodeGroupElement, NodeTextElement} from "./node.mjs"
|
||||
export {NodeFileElement, NodeGroupElement, NodeTextElement} from "src/elements/canvas/node/base.mjs"
|
||||
export {MarkdownElement, HashtagElement, WikilinkElement, FrontMatterElement} from "./markdown.mjs"
|
||||
export {CanvasElement} from "./canvas/canvas.mjs"
|
||||
export {DisplayElement} from "./display.mjs"
|
||||
export {EdgeElement} from "./edge.mjs"
|
||||
export {EdgeElement} from "src/elements/canvas/edge/base.mjs"
|
|
@ -1,133 +0,0 @@
|
|||
import { CanvasItemElement } from "./canvas/canvas.mjs";
|
||||
import { DisplayElement } from "./display.mjs";
|
||||
|
||||
|
||||
/**
|
||||
* Error in the fetching of a file.
|
||||
*/
|
||||
export class FetchError extends Error {
|
||||
/**
|
||||
* The {@link Response} object of the failed request.
|
||||
*/
|
||||
response
|
||||
|
||||
constructor(response, message) {
|
||||
super(message)
|
||||
this.response = response
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export class NodeElement extends CanvasItemElement {
|
||||
getCenterCoordinatesOfSide(side) {
|
||||
switch(side) {
|
||||
case "top":
|
||||
return [
|
||||
Number(this.getAttribute("x")) + Number(this.getAttribute("width")) / 2,
|
||||
Number(this.getAttribute("y")),
|
||||
]
|
||||
case "bottom":
|
||||
return [
|
||||
Number(this.getAttribute("x")) + Number(this.getAttribute("width")) / 2,
|
||||
Number(this.getAttribute("y")) + Number(this.getAttribute("height")),
|
||||
]
|
||||
case "left":
|
||||
return [
|
||||
Number(this.getAttribute("x")),
|
||||
Number(this.getAttribute("y")) + Number(this.getAttribute("height")) / 2,
|
||||
]
|
||||
case "right":
|
||||
return [
|
||||
Number(this.getAttribute("x")) + Number(this.getAttribute("width")),
|
||||
Number(this.getAttribute("y")) + Number(this.getAttribute("height")) / 2,
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export class NodeGroupElement extends NodeElement {
|
||||
static getTemplate() {
|
||||
return document.getElementById("template-node-group")
|
||||
}
|
||||
|
||||
instanceElement
|
||||
labelSlotted
|
||||
|
||||
onConnect() {
|
||||
this.instanceElement = this.instance.querySelector(".node-group")
|
||||
|
||||
this.instanceElement.style.setProperty("left", `${this.getAttribute("x")}px`)
|
||||
this.instanceElement.style.setProperty("top", `${this.getAttribute("y")}px`)
|
||||
this.instanceElement.style.setProperty("width", `${this.getAttribute("width")}px`)
|
||||
this.instanceElement.style.setProperty("height", `${this.getAttribute("height")}px`)
|
||||
this.instanceElement.style.setProperty("--color-node", this.constructor.colorToCSS(this.getAttribute("color")))
|
||||
|
||||
this.labelSlotted = document.createElement("span")
|
||||
this.labelSlotted.slot = "node-label"
|
||||
this.labelSlotted.innerText = this.getAttribute("label")
|
||||
this.appendChild(this.labelSlotted)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export class NodeFileElement extends NodeElement {
|
||||
static getTemplate() {
|
||||
return document.getElementById("template-node-file")
|
||||
}
|
||||
|
||||
instanceElement
|
||||
nameSlotted
|
||||
contentsSlotted
|
||||
|
||||
onConnect() {
|
||||
this.instanceElement = this.instance.querySelector(".node-file")
|
||||
|
||||
this.instanceElement.style.setProperty("left", `${this.getAttribute("x")}px`)
|
||||
this.instanceElement.style.setProperty("top", `${this.getAttribute("y")}px`)
|
||||
this.instanceElement.style.setProperty("width", `${this.getAttribute("width")}px`)
|
||||
this.instanceElement.style.setProperty("height", `${this.getAttribute("height")}px`)
|
||||
this.instanceElement.style.setProperty("--color-node", this.constructor.colorToCSS(this.getAttribute("color")))
|
||||
|
||||
this.nameSlotted = document.createElement("x-wikilink")
|
||||
this.nameSlotted.slot = "node-title"
|
||||
this.nameSlotted.setAttribute("wref", this.getAttribute("file"))
|
||||
const nameSlottedText = document.createElement("span")
|
||||
nameSlottedText.slot = "wikilink-text"
|
||||
nameSlottedText.innerText = this.getAttribute("file-name")
|
||||
this.nameSlotted.appendChild(nameSlottedText)
|
||||
this.appendChild(this.nameSlotted)
|
||||
|
||||
this.contentsSlotted = document.createElement(customElements.getName(DisplayElement))
|
||||
this.contentsSlotted.slot = "node-contents"
|
||||
|
||||
const firstDisplayAncestor = this.findFirstAncestor(DisplayElement)
|
||||
this.contentsSlotted.setAttribute("vref", firstDisplayAncestor.getAttribute("vref"))
|
||||
this.contentsSlotted.setAttribute("wref", this.getAttribute("file"))
|
||||
this.appendChild(this.contentsSlotted)
|
||||
}
|
||||
}
|
||||
|
||||
export class NodeTextElement extends NodeElement {
|
||||
static getTemplate() {
|
||||
return document.getElementById("template-node-text")
|
||||
}
|
||||
|
||||
instanceElement
|
||||
contentsSlotted
|
||||
|
||||
onConnect() {
|
||||
this.instanceElement = this.instance.querySelector(".node-text")
|
||||
|
||||
this.instanceElement.style.setProperty("left", `${this.getAttribute("x")}px`)
|
||||
this.instanceElement.style.setProperty("top", `${this.getAttribute("y")}px`)
|
||||
this.instanceElement.style.setProperty("width", `${this.getAttribute("width")}px`)
|
||||
this.instanceElement.style.setProperty("height", `${this.getAttribute("height")}px`)
|
||||
this.instanceElement.style.setProperty("--color-node", this.constructor.colorToCSS(this.getAttribute("color")))
|
||||
|
||||
this.contentsSlotted = document.createElement("x-markdown")
|
||||
this.contentsSlotted.slot = "node-contents"
|
||||
this.contentsSlotted.setAttribute("contents", this.getAttribute("text"))
|
||||
this.appendChild(this.contentsSlotted)
|
||||
}
|
||||
}
|
|
@ -6,3 +6,18 @@
|
|||
* The called method is abstract, but has not been overridden by the child class.
|
||||
*/
|
||||
export class NotImplementedError extends Error {}
|
||||
|
||||
/**
|
||||
* Error in a {@link fetch} request.
|
||||
*/
|
||||
export class FetchError extends Error {
|
||||
/**
|
||||
* The {@link Response} object of the failed request.
|
||||
*/
|
||||
response
|
||||
|
||||
constructor(response, message) {
|
||||
super(message)
|
||||
this.response = response
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue