mirror of
https://github.com/glassflame/glassflame.github.io.git
synced 2024-11-22 08:04:27 +00:00
Another iteration!
This commit is contained in:
parent
45c3afd7d4
commit
93b91c5b6c
11 changed files with 481 additions and 178 deletions
|
@ -7,7 +7,7 @@
|
|||
<inspection_tool class="HtmlUnknownTag" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="myValues">
|
||||
<value>
|
||||
<list size="9">
|
||||
<list size="11">
|
||||
<item index="0" class="java.lang.String" itemvalue="nobr" />
|
||||
<item index="1" class="java.lang.String" itemvalue="noembed" />
|
||||
<item index="2" class="java.lang.String" itemvalue="comment" />
|
||||
|
@ -17,6 +17,8 @@
|
|||
<item index="6" class="java.lang.String" itemvalue="x-card" />
|
||||
<item index="7" class="java.lang.String" itemvalue="x-hashtag" />
|
||||
<item index="8" class="java.lang.String" itemvalue="x-node-file" />
|
||||
<item index="9" class="java.lang.String" itemvalue="x-canvas" />
|
||||
<item index="10" class="java.lang.String" itemvalue="x-display" />
|
||||
</list>
|
||||
</value>
|
||||
</option>
|
||||
|
|
118
index.html
118
index.html
|
@ -8,6 +8,24 @@
|
|||
@import "style/light.css";
|
||||
@import "style/dark.css";
|
||||
|
||||
:root {
|
||||
--color-background: #ffffff;
|
||||
--color-foreground: #000000;
|
||||
--color-accent: #ff7f00;
|
||||
|
||||
--color-gray: #7E7E7E;
|
||||
--color-red: #FB464C;
|
||||
--color-orange: #E9973F;
|
||||
--color-yellow: #E0DE71;
|
||||
--color-green: #44CF6E;
|
||||
--color-blue: #53DFDD;
|
||||
--color-purple: #A882FF;
|
||||
|
||||
--edge-width: 2px;
|
||||
--node-group-border-width: 2px;
|
||||
--node-file-border-width: 2px;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
|
||||
|
@ -17,47 +35,101 @@
|
|||
</style>
|
||||
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
|
||||
<script type="module" src="src/index.mjs"></script>
|
||||
<template id="template-node-file">
|
||||
<template id="template-display">
|
||||
<slot name="display-container"></slot>
|
||||
</template>
|
||||
<template id="template-node-group">
|
||||
<style>
|
||||
.node {
|
||||
.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 {
|
||||
position: relative;
|
||||
bottom: 14px;
|
||||
left: -12px;
|
||||
transform: translateY(-100%);
|
||||
|
||||
display: inline-block;
|
||||
|
||||
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: 8px 8px 0 0;
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.node-group-label h1 {
|
||||
margin: 0;
|
||||
}
|
||||
</style>
|
||||
<section class="node-group">
|
||||
<aside class="node-group-label">
|
||||
<h1><slot name="node-label">{Group label}</slot></h1>
|
||||
</aside>
|
||||
<slot name="node-contents">{Node contents}</slot>
|
||||
</section>
|
||||
</template>
|
||||
<template id="template-node-file">
|
||||
<style>
|
||||
.node-file {
|
||||
outline-width: 2px;
|
||||
position: absolute;
|
||||
box-sizing: border-box;
|
||||
|
||||
--color-node: var(--color-gray);
|
||||
outline: var(--node-file-border-width) solid var(--color-node);
|
||||
background-color: color-mix(in srgb, var(--color-node) 10%, var(--color-background));
|
||||
border-radius: 8px;
|
||||
padding: 12px;
|
||||
|
||||
overflow-x: clip;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
.node-empty {
|
||||
outline-style: dashed;
|
||||
}
|
||||
|
||||
.node-full {
|
||||
outline-style: solid;
|
||||
}
|
||||
</style>
|
||||
<article class="node node-file">
|
||||
<article class="node-file">
|
||||
<h1>
|
||||
<slot name="node-title">Node title</slot>
|
||||
<slot name="node-title">{Node title}</slot>
|
||||
</h1>
|
||||
<slot name="node-contents">Node contents</slot>
|
||||
<slot name="node-contents">{Node contents}</slot>
|
||||
</article>
|
||||
</template>
|
||||
<template id="template-markdown">
|
||||
<slot name="markdown-contents"></slot>
|
||||
</template>
|
||||
<template id="template-canvas">
|
||||
<template id="template-node-text">
|
||||
<style>
|
||||
.canvas {
|
||||
.node-text {
|
||||
position: absolute;
|
||||
box-sizing: border-box;
|
||||
|
||||
--color-node: var(--color-gray);
|
||||
outline: var(--node-file-border-width) solid var(--color-node);
|
||||
background-color: color-mix(in srgb, var(--color-node) 10%, var(--color-background));
|
||||
border-radius: 8px;
|
||||
padding: 12px;
|
||||
|
||||
overflow-x: clip;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
</style>
|
||||
<div class="canvas"></div>
|
||||
<article class="node-text">
|
||||
<slot name="node-contents">{Node contents}</slot>
|
||||
</article>
|
||||
</template>
|
||||
<template id="template-edge">
|
||||
<slot name="edge-svg">{Edge SVG}</slot>
|
||||
</template>
|
||||
<template id="template-markdown">
|
||||
<slot name="markdown-contents">{Markdown text}</slot>
|
||||
</template>
|
||||
<template id="template-canvas">
|
||||
<slot name="canvas-contents">{Canvas}</slot>
|
||||
</template>
|
||||
<template id="template-wikilink">
|
||||
<style>
|
||||
|
@ -67,7 +139,7 @@
|
|||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
<a class="wikilink"><slot name="wikilink-text">Wikilink text</slot></a>
|
||||
<a class="wikilink"><slot name="wikilink-text">{Wikilink text}</slot></a>
|
||||
</template>
|
||||
<template id="template-hashtag">
|
||||
<style>
|
||||
|
@ -76,10 +148,10 @@
|
|||
color: var(--color-accent);
|
||||
}
|
||||
</style>
|
||||
<span class="hashtag"><slot name="hashtag-text">#Hashtag</slot></span>
|
||||
<span class="hashtag"><slot name="hashtag-text">{#Hashtag}</slot></span>
|
||||
</template>
|
||||
</head>
|
||||
<body>
|
||||
<x-canvas contents=""/>
|
||||
<x-display root vref="https://raw.githubusercontent.com/Steffo99/appunti-magistrali/main/" wref="8 - Crittografia applicata/1 - Concetti/3 - Casualità ed entropia/★ mappa concettuale.canvas"/>
|
||||
</body>
|
||||
</html>
|
|
@ -1,25 +1,45 @@
|
|||
import { fileDetails } from "../utils/file.mjs";
|
||||
|
||||
|
||||
export class CanvasElement extends HTMLElement {
|
||||
/**
|
||||
* 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() {
|
||||
return document.getElementById("template-canvas")
|
||||
}
|
||||
|
||||
parsedJSON
|
||||
|
||||
canvasElement
|
||||
nodeElements = []
|
||||
edgeElements = []
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
this.parsedJSON = JSON.parse(this.getAttribute("contents"))
|
||||
}
|
||||
contentsSlotted
|
||||
nodeElements = {}
|
||||
edgeElements = {}
|
||||
|
||||
// noinspection JSUnusedGlobalSymbols
|
||||
connectedCallback() {
|
||||
const instanceDocument = CanvasElement.getTemplate().content.cloneNode(true)
|
||||
const shadow = this.attachShadow({ mode: "open" })
|
||||
|
||||
this.canvasElement = instanceDocument.querySelector(".canvas")
|
||||
this.parsedJSON = JSON.parse(this.getAttribute("contents"))
|
||||
|
||||
this.contentsSlotted = document.createElement("div")
|
||||
this.contentsSlotted.slot = "canvas-contents"
|
||||
|
||||
let minX = { x: Infinity, width: 0 }
|
||||
let minY = { y: Infinity, height: 0 }
|
||||
|
@ -27,41 +47,95 @@ export class CanvasElement extends HTMLElement {
|
|||
let maxY = { y: -Infinity, height: 0 }
|
||||
|
||||
for(const node of this.parsedJSON["nodes"]) {
|
||||
if(node["type"] === "file") {
|
||||
if(node["x"] < minX["x"]) minX = node
|
||||
if(node["y"] < minY["y"]) minY = node
|
||||
if(node["x"] + node["width"] > maxX["x"] + node["width"]) maxX = node
|
||||
if(node["y"] + node["height"] > maxY["y"] + node["height"]) maxY = node
|
||||
}
|
||||
else {
|
||||
console.warn("Encountered node of unimplemented type: ", node["type"])
|
||||
}
|
||||
if(node["x"] < minX["x"]) minX = node
|
||||
if(node["y"] < minY["y"]) minY = node
|
||||
if(node["x"] + node["width"] > maxX["x"] + node["width"]) maxX = node
|
||||
if(node["y"] + node["height"] > maxY["y"] + node["height"]) maxY = node
|
||||
}
|
||||
|
||||
console.debug("minX:", minX, "| minY:", minY, "| maxX:", maxX, "| maxY:", maxY)
|
||||
|
||||
for(const node of this.parsedJSON["nodes"]) {
|
||||
if(node["type"] === "file") {
|
||||
const element = document.createElement("x-node-file")
|
||||
element.setAttribute("file", node["file"])
|
||||
element.setAttribute("id", node["id"])
|
||||
element.setAttribute("x", node["x"] - minX["x"])
|
||||
element.setAttribute("y", node["y"] - minY["y"])
|
||||
element.setAttribute("width", node["width"])
|
||||
element.setAttribute("height", node["height"])
|
||||
element.setAttribute("color", node["color"])
|
||||
const element = document.createElement(`x-node-${node["type"]}`)
|
||||
|
||||
this.nodeElements.push(element)
|
||||
this.canvasElement.appendChild(element)
|
||||
}
|
||||
else {
|
||||
console.warn("Encountered node of unimplemented type: ", node["type"])
|
||||
element.setAttribute("id", node["id"])
|
||||
element.setAttribute("x", node["x"] - minX["x"])
|
||||
element.setAttribute("y", node["y"] - minY["y"])
|
||||
element.setAttribute("width", node["width"])
|
||||
element.setAttribute("height", node["height"])
|
||||
element.setAttribute("color", node["color"])
|
||||
|
||||
switch(node["type"]) {
|
||||
case "text":
|
||||
element.setAttribute("text", node["text"])
|
||||
break
|
||||
|
||||
case "file":
|
||||
element.setAttribute("file", node["file"])
|
||||
element.setAttribute("fileName", fileDetails(node["file"])[0])
|
||||
break
|
||||
|
||||
case "group":
|
||||
element.setAttribute("label", node["label"])
|
||||
break
|
||||
|
||||
default:
|
||||
console.warn("Encountered node of unimplemented type: ", node["type"])
|
||||
break
|
||||
}
|
||||
|
||||
this.nodeElements[node["id"]] = element
|
||||
this.contentsSlotted.appendChild(element)
|
||||
}
|
||||
|
||||
this.canvasElement.style["width"] = `${maxX["x"] + maxX["width"] - minX["x"]}px`
|
||||
this.canvasElement.style["height"] = `${maxY["y"] + maxY["height"] - minY["y"]}px`
|
||||
for(const edge of this.parsedJSON["edges"]) {
|
||||
const element = document.createElement("x-edge")
|
||||
element.setAttribute("id", edge["id"])
|
||||
element.setAttribute("node-from", edge["fromNode"])
|
||||
element.setAttribute("node-from-side", edge["fromSide"])
|
||||
element.setAttribute("node-to", edge["toNode"])
|
||||
element.setAttribute("node-to-side", edge["toSide"])
|
||||
element.setAttribute("color", edge["color"])
|
||||
element.setAttribute("arrows", edge["toEnd"])
|
||||
|
||||
this.edgeElements[edge["id"]] = element
|
||||
this.contentsSlotted.appendChild(element)
|
||||
}
|
||||
|
||||
console.log(Object.values(this.nodeElements))
|
||||
|
||||
this.contentsSlotted.style["width"] = `${maxX["x"] + maxX["width"] - minX["x"]}px`
|
||||
this.contentsSlotted.style["height"] = `${maxY["y"] + maxY["height"] - minY["y"]}px`
|
||||
|
||||
this.appendChild(this.contentsSlotted)
|
||||
|
||||
shadow.appendChild(instanceDocument)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Element representing the generic skeleton of an Obsidian Canvas item.
|
||||
*/
|
||||
export class CanvasItemElement extends HTMLElement {
|
||||
colorToHex() {
|
||||
const color = this.getAttribute("color")
|
||||
|
||||
if(color?.startsWith("#")) {
|
||||
// This is an hex color
|
||||
return color
|
||||
}
|
||||
else {
|
||||
// TODO: Check which colors correspond to what
|
||||
return {
|
||||
[undefined]: "var(--color-gray)",
|
||||
"undefined": "var(--color-gray)",
|
||||
"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]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
82
src/elements/display.mjs
Normal file
82
src/elements/display.mjs
Normal file
|
@ -0,0 +1,82 @@
|
|||
import { fileDetails } from "../utils/file.mjs";
|
||||
import { CanvasElement } from "./canvas.mjs";
|
||||
import { MarkdownElement } from "./markdown.mjs";
|
||||
import { FetchError } from "./node.mjs";
|
||||
|
||||
|
||||
export class DisplayElement extends HTMLElement {
|
||||
/**
|
||||
* 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() {
|
||||
return document.getElementById("template-display")
|
||||
}
|
||||
|
||||
containerSlotted
|
||||
loadButton
|
||||
|
||||
// noinspection JSUnusedGlobalSymbols
|
||||
connectedCallback() {
|
||||
const instanceDocument = DisplayElement.getTemplate().content.cloneNode(true)
|
||||
const shadow = this.attachShadow({ mode: "open" })
|
||||
|
||||
this.containerSlotted = document.createElement("div")
|
||||
this.containerSlotted.slot = "display-container"
|
||||
this.loadButton = document.createElement("button")
|
||||
this.loadButton.innerText = "Load"
|
||||
this.loadButton.addEventListener("click", this.load.bind(this))
|
||||
this.containerSlotted.appendChild(this.loadButton)
|
||||
this.appendChild(this.containerSlotted)
|
||||
|
||||
shadow.appendChild(instanceDocument)
|
||||
}
|
||||
|
||||
data
|
||||
|
||||
async fetchData() {
|
||||
const vref = this.getAttribute("vref")
|
||||
const wref = this.getAttribute("wref")
|
||||
const url = new URL(wref, vref)
|
||||
|
||||
const response = await fetch(url, {})
|
||||
|
||||
if(!response.ok) throw new FetchError(response, "Fetch response is not ok")
|
||||
|
||||
this.data = await response.text()
|
||||
}
|
||||
|
||||
async load() {
|
||||
this.loadButton.disabled = true
|
||||
|
||||
await this.fetchData()
|
||||
|
||||
this.containerSlotted.remove()
|
||||
this.containerSlotted = null
|
||||
|
||||
const [, fileExtension] = fileDetails(this.getAttribute("wref"))
|
||||
|
||||
this.containerSlotted = document.createElement({
|
||||
"md": customElements.getName(MarkdownElement),
|
||||
"canvas": this.getAttribute("root") !== undefined ? customElements.getName(CanvasElement) : "div",
|
||||
}[fileExtension] ?? "div")
|
||||
this.containerSlotted.slot = "display-container"
|
||||
this.containerSlotted.setAttribute("contents", this.data)
|
||||
this.appendChild(this.containerSlotted)
|
||||
}
|
||||
}
|
54
src/elements/edge.mjs
Normal file
54
src/elements/edge.mjs
Normal file
|
@ -0,0 +1,54 @@
|
|||
import { CanvasElement, CanvasItemElement } from "./canvas.mjs";
|
||||
|
||||
|
||||
export class EdgeElement extends CanvasItemElement {
|
||||
static getTemplate() {
|
||||
return document.getElementById("template-edge")
|
||||
}
|
||||
|
||||
svgSlotted
|
||||
lineElement
|
||||
|
||||
// noinspection JSUnusedGlobalSymbols
|
||||
connectedCallback() {
|
||||
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 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)
|
||||
|
||||
const minX = Math.min(x1, x2)
|
||||
const minY = Math.min(y1, y2)
|
||||
const diffX = Math.abs(x1 - x2)
|
||||
const diffY = Math.abs(y1 - y2)
|
||||
|
||||
const arrows = this.getAttribute("arrows") ?? "end"
|
||||
|
||||
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.colorToHex())
|
||||
this.lineElement.style.setProperty("stroke-width", "var(--edge-width)")
|
||||
|
||||
this.svgSlotted.appendChild(this.lineElement)
|
||||
this.appendChild(this.svgSlotted)
|
||||
|
||||
shadow.appendChild(instanceDocument)
|
||||
}
|
||||
}
|
|
@ -1,3 +1,5 @@
|
|||
export {NodeFileElement} from "./node.mjs"
|
||||
export {NodeFileElement, NodeGroupElement, NodeTextElement} from "./node.mjs"
|
||||
export {MarkdownElement, HashtagElement, WikilinkElement} from "./markdown.mjs"
|
||||
export {CanvasElement} from "./canvas.mjs"
|
||||
export {DisplayElement} from "./display.mjs"
|
||||
export {EdgeElement} from "./edge.mjs"
|
|
@ -1,38 +1,7 @@
|
|||
import { configFromWindow } from "../config.mjs";
|
||||
import { CanvasItemElement } from "./canvas.mjs";
|
||||
import { DisplayElement } from "./display.mjs";
|
||||
|
||||
|
||||
/**
|
||||
* Element representing the generic skeleton of an Obsidian Canvas node.
|
||||
*/
|
||||
export class NodeElement extends HTMLElement {
|
||||
x
|
||||
y
|
||||
width
|
||||
height
|
||||
color
|
||||
|
||||
// noinspection JSUnusedGlobalSymbols
|
||||
connectedCallback() {
|
||||
this.id = this.getAttribute("id")
|
||||
this.x = this.getAttribute("x")
|
||||
this.y = this.getAttribute("y")
|
||||
this.width = this.getAttribute("width")
|
||||
this.height = this.getAttribute("height")
|
||||
this.color = this.getAttribute("color")
|
||||
}
|
||||
|
||||
colorToHex() {
|
||||
if(this?.color?.startsWith("#")) {
|
||||
// This is an hex color
|
||||
return this.color
|
||||
}
|
||||
else {
|
||||
// TODO: Check which colors correspond to what
|
||||
return {}[this.color]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Error in the fetching of a file.
|
||||
*/
|
||||
|
@ -48,114 +17,130 @@ export class FetchError extends Error {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Element representing the skeleton of an Obsidian Canvas node pointing to a file.
|
||||
*
|
||||
* Requires the following attributes:
|
||||
* - `file`: wref to the target file
|
||||
* - `id`: id unique to the node
|
||||
* - `x`: horizontal translation
|
||||
* - `y`: vertical translation
|
||||
* - `width`: width of the card in px
|
||||
* - `height`: height of the card in px
|
||||
* - (optional) `color`: custom Obsidian color of the card
|
||||
*/
|
||||
|
||||
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
|
||||
|
||||
// noinspection JSUnusedGlobalSymbols
|
||||
connectedCallback() {
|
||||
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("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.colorToHex())
|
||||
|
||||
this.labelSlotted = document.createElement("span")
|
||||
this.labelSlotted.slot = "node-label"
|
||||
this.labelSlotted.innerText = this.getAttribute("label")
|
||||
this.appendChild(this.labelSlotted)
|
||||
|
||||
shadow.appendChild(instanceDocument)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export class NodeFileElement extends NodeElement {
|
||||
static type = "file"
|
||||
|
||||
file
|
||||
fileName
|
||||
fileExtension
|
||||
|
||||
fileLeaf() {
|
||||
return this.file.split("/").at(-1)
|
||||
}
|
||||
|
||||
fileDetails() {
|
||||
const split = this.fileLeaf().split(".")
|
||||
const name = split.slice(0, -1)
|
||||
const extension = split.at(-1)
|
||||
return [name, extension]
|
||||
}
|
||||
|
||||
static getTemplate() {
|
||||
return document.getElementById("template-node-file")
|
||||
}
|
||||
|
||||
instanceElement
|
||||
nameSlotted
|
||||
placeholderSlotted
|
||||
contentsSlotted
|
||||
loadButton
|
||||
|
||||
// noinspection JSUnusedGlobalSymbols
|
||||
connectedCallback() {
|
||||
super.connectedCallback()
|
||||
this.file = this.getAttribute("file")
|
||||
const [fileName, fileExtension] = this.fileDetails()
|
||||
this.fileName = fileName
|
||||
this.fileExtension = fileExtension
|
||||
|
||||
const instanceDocument = NodeFileElement.getTemplate().content.cloneNode(true)
|
||||
const shadow = this.attachShadow({ mode: "open" })
|
||||
|
||||
this.instanceElement = instanceDocument.querySelector(".node.node-file")
|
||||
this.instanceElement = instanceDocument.querySelector(".node-file")
|
||||
|
||||
this.instanceElement.style["left"] = `${this.x}px`
|
||||
this.instanceElement.style["top"] = `${this.y}px`
|
||||
this.instanceElement.style["width"] = `${this.width}px`
|
||||
this.instanceElement.style["height"] = `${this.height}px`
|
||||
this.instanceElement.style["--node-color"] = this.colorToHex()
|
||||
|
||||
this.instanceElement.classList.add("node-empty")
|
||||
this.instanceElement.classList.remove("node-full")
|
||||
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.colorToHex())
|
||||
|
||||
this.nameSlotted = document.createElement("span")
|
||||
this.nameSlotted.slot = "node-title"
|
||||
this.nameSlotted.innerText = this.fileName
|
||||
this.nameSlotted.innerText = this.getAttribute("fileName")
|
||||
this.appendChild(this.nameSlotted)
|
||||
|
||||
this.placeholderSlotted = document.createElement("div")
|
||||
this.placeholderSlotted.slot = "node-contents"
|
||||
this.loadButton = document.createElement("button")
|
||||
this.loadButton.innerText = "Load"
|
||||
this.loadButton.addEventListener("click", this.fillNode.bind(this))
|
||||
this.placeholderSlotted.appendChild(this.loadButton)
|
||||
this.appendChild(this.placeholderSlotted)
|
||||
this.contentsSlotted = document.createElement(customElements.getName(DisplayElement))
|
||||
this.contentsSlotted.slot = "node-contents"
|
||||
|
||||
const firstDisplayAncestor = DisplayElement.findFirstDisplayAncestor(this)
|
||||
this.contentsSlotted.setAttribute("vref", firstDisplayAncestor.getAttribute("vref"))
|
||||
this.contentsSlotted.setAttribute("wref", this.getAttribute("file"))
|
||||
this.appendChild(this.contentsSlotted)
|
||||
|
||||
shadow.appendChild(instanceDocument)
|
||||
}
|
||||
}
|
||||
|
||||
contents
|
||||
|
||||
async fetchContents() {
|
||||
console.info("Fetching:", this.file)
|
||||
|
||||
const url = new URL(this.file, configFromWindow()["vault"])
|
||||
const response = await fetch(url, {})
|
||||
|
||||
if(!response.ok) throw new FetchError(response, "Fetch response is not ok")
|
||||
|
||||
this.contents = await response.text()
|
||||
export class NodeTextElement extends NodeElement {
|
||||
static getTemplate() {
|
||||
return document.getElementById("template-node-text")
|
||||
}
|
||||
|
||||
async fillNode() {
|
||||
this.loadButton.disabled = true
|
||||
instanceElement
|
||||
contentsSlotted
|
||||
|
||||
this.placeholderSlotted.remove()
|
||||
this.placeholderSlotted = null
|
||||
// noinspection JSUnusedGlobalSymbols
|
||||
connectedCallback() {
|
||||
const instanceDocument = NodeTextElement.getTemplate().content.cloneNode(true)
|
||||
const shadow = this.attachShadow({ mode: "open" })
|
||||
|
||||
await this.fetchContents()
|
||||
this.instanceElement = instanceDocument.querySelector(".node-text")
|
||||
|
||||
this.instanceElement.classList.remove("node-empty")
|
||||
this.instanceElement.classList.add("node-full")
|
||||
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.colorToHex())
|
||||
|
||||
this.contentsSlotted = document.createElement({
|
||||
"md": "x-markdown",
|
||||
"canvas": "x-canvas",
|
||||
}[this.fileExtension] ?? "div")
|
||||
this.contentsSlotted = document.createElement("x-markdown")
|
||||
this.contentsSlotted.slot = "node-contents"
|
||||
this.contentsSlotted.setAttribute("contents", this.contents)
|
||||
this.appendChild(this.contentsSlotted)
|
||||
this.contentsSlotted.setAttribute("contents", this.getAttribute("text"))
|
||||
|
||||
shadow.appendChild(instanceDocument)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
import { CanvasElement, HashtagElement, MarkdownElement, NodeFileElement, WikilinkElement } from "./elements/index.mjs";
|
||||
import { CanvasElement, HashtagElement, MarkdownElement, NodeFileElement, WikilinkElement, DisplayElement, EdgeElement, NodeGroupElement, NodeTextElement } from "./elements/index.mjs";
|
||||
|
||||
customElements.define("x-node-file", NodeFileElement)
|
||||
customElements.define("x-node-text", NodeTextElement)
|
||||
customElements.define("x-node-group", NodeGroupElement)
|
||||
customElements.define("x-markdown", MarkdownElement)
|
||||
customElements.define("x-wikilink", WikilinkElement)
|
||||
customElements.define("x-hashtag", HashtagElement)
|
||||
customElements.define("x-canvas", CanvasElement)
|
||||
customElements.define("x-display", DisplayElement)
|
||||
customElements.define("x-edge", EdgeElement)
|
||||
|
|
12
src/utils/file.mjs
Normal file
12
src/utils/file.mjs
Normal file
|
@ -0,0 +1,12 @@
|
|||
/**
|
||||
* Parse a file path to get the file name and extension.
|
||||
*
|
||||
* @param file The file path to parse.
|
||||
* @returns {[String, String]} The file's name and extension without the dot respectively.
|
||||
*/
|
||||
export function fileDetails(file) {
|
||||
const split = file.split("/").at(-1).split(".")
|
||||
const name = split.slice(0, -1)
|
||||
const extension = split.at(-1)
|
||||
return [name, extension]
|
||||
}
|
|
@ -1,7 +1,15 @@
|
|||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
@media screen and (prefers-color-scheme: dark) {
|
||||
:root:root {
|
||||
--color-background: #1e1e1e;
|
||||
--color-foreground: #ffffff;
|
||||
--color-accent: #ff7f00;
|
||||
|
||||
--color-gray: #7E7E7E;
|
||||
--color-red: #FB464C;
|
||||
--color-orange: #E9973F;
|
||||
--color-yellow: #E0DE71;
|
||||
--color-green: #44CF6E;
|
||||
--color-blue: #53DFDD;
|
||||
--color-purple: #A882FF;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,15 @@
|
|||
@media (prefers-color-scheme: light) {
|
||||
:root {
|
||||
@media screen and (prefers-color-scheme: light) {
|
||||
:root:root {
|
||||
--color-background: #ffffff;
|
||||
--color-foreground: #1e1e1e;
|
||||
--color-accent: #ff7f00;
|
||||
|
||||
--color-gray: #7E7E7E;
|
||||
--color-red: #FB464C;
|
||||
--color-orange: #E9973F;
|
||||
--color-yellow: #E0DE71;
|
||||
--color-green: #44CF6E;
|
||||
--color-blue: #53DFDD;
|
||||
--color-purple: #A882FF;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue