1
Fork 0
mirror of https://github.com/glassflame/glassflame.github.io.git synced 2024-12-11 17:24:27 +00:00

Add callouts

This commit is contained in:
Steffo 2023-11-17 16:50:43 +01:00
parent 9617895c8e
commit 16cf58f9d7
Signed by: steffo
GPG key ID: 2A24051445686895
8 changed files with 113 additions and 154 deletions

View file

@ -249,8 +249,6 @@
<slot name="wikilink-anchor">{Wikilink}</slot></template>
<template id="template-math">
<slot name="math-katex">{Math}</slot></template>
<template id="template-callout">
<slot name="callout-contents">{Callout}</slot></template>
</head>
<body is="x-browse"></body>
</html>

View file

@ -1,5 +1,5 @@
export {CanvasElement, NodeFileElement, NodeGroupElement, NodeTextElement, EdgeElement} from "./canvas/index.mjs"
export {MarkdownElement, HashtagElement, WikilinkElement, FrontMatterElement, MathElement} from "./markdown/index.mjs"
export {MarkdownElement, HashtagElement, WikilinkElement, FrontMatterElement, MathElement, CalloutElement} from "./markdown/index.mjs"
export {DisplayElement} from "./display.mjs"
export {VaultElement} from "./vault.mjs"
export {BrowseElement} from "./browse.mjs"

View file

@ -1,10 +1,6 @@
import {CustomElement} from "../base.mjs";
export class CalloutElement extends CustomElement {
static get template() {
return document.getElementById("template-callout")
}
export class CalloutElement extends HTMLElement {
/**
* The kind of callout this element represents, in UPPERCASE.
*
@ -44,71 +40,6 @@ export class CalloutElement extends CustomElement {
this.setAttribute("collapse", value)
}
/**
* The title of this callout, in UPPERCASE, as casing is handled by CSS, or `undefined`, if the {@link kind} should be used.
*
* @return {string|undefined}
*/
get admonition() {
return this.getAttribute("admonition")?.toUpperCase()
}
set admonition(value) {
if(value === undefined) {
this.removeAttribute("admonition")
return
}
this.setAttribute("admonition", value.toUpperCase())
}
/**
* The contents of this callout, or `undefined`, if there are none.
*
* @return {string}
*/
get contents() {
const value = this.getAttribute("contents")
if(value === "undefined") {
return undefined
}
return value
}
set contents(value) {
if(value === undefined) {
this.removeAttribute("contents")
return
}
this.setAttribute("contents", value)
}
/**
* Whether this element should consume its parent {@link onConnect}.
* @return {boolean}
*/
get cronus() {
return this.hasAttribute("cronus")
}
set cronus(value) {
if(value) {
this.setAttribute("cronus", "")
}
else {
this.removeAttribute("cronus")
}
}
/**
* Replace the contents of the {@link parentElement} with this element.
* Also sets {@link cronus} to `false`.
* @returns {void}
*/
replaceParentElement() {
const grandpa = this.parentElement.parentElement
this.remove()
this.parentElement.remove()
this.cronus = false
grandpa.appendChild(this)
}
/**
* The element displaying the admonition of this callout, or `null` if {@link admonition} is `undefined`.
* Can be recreated with {@link recreateAdmonitionElement}.
@ -116,6 +47,12 @@ export class CalloutElement extends CustomElement {
*/
admonitionElement
/**
* The slot where the admonition of this callout will be inserted in.
* @type {HTMLSlotElement}
*/
admonitionSlotElement
/**
* Recreate {@link collapseElement} with the current value of {@link collapse}.
* {@link collapseElement} must not be null.
@ -125,42 +62,38 @@ export class CalloutElement extends CustomElement {
if(this.admonitionElement) {
this.admonitionElement.remove()
this.admonitionElement = null
this.admonitionSlotElement = null
}
this.admonitionElement = document.createElement("summary")
this.admonitionElement.innerText = this.admonition
this.admonitionElement = document.createElement("summary")
this.collapseElement.appendChild(this.admonitionElement)
this.admonitionSlotElement = document.createElement("slot")
this.admonitionSlotElement.name = "callout-admonition"
this.admonitionElement.appendChild(this.admonitionSlotElement)
this.collapseElement.appendChild(this.admonitionElement)
}
/**
* The element displaying the contents of this callout, or `null` if {@link contents} is `undefined`.
* Can be recreated with {@link recreateContentsElement}.
* @type {HTMLDivElement|null}
* The slot where the contents of this callout will be inserted in.
* @type {HTMLSlotElement}
*/
contentsElement
contentsSlotElement
/**
* Recreate {@link contentsElement} with the current value of {@link contents} and {@link collapseElement} or {@link containerElement}.
* Recreate {@link contentsElement} with the current value of {@link contents} and {@link collapseElement}.
* @returns {void}
*/
recreateContentsElement() {
if(this.contentsElement) {
this.contentsElement.remove()
recreateContentsSlotElement() {
if(this.contentsSlotElement) {
this.contentsSlotElement.remove()
this.contentsElement = null
}
if(this.contents) {
this.contentsElement = document.createElement("summary")
this.contentsElement.innerText = this.contents
this.contentsSlotElement = document.createElement("slot")
this.contentsSlotElement.name = "callout-contents"
if(this.collapseElement) {
this.collapseElement.appendChild(this.contentsElement)
}
else {
this.containerElement.appendChild(this.contentsElement)
}
}
this.collapseElement.appendChild(this.contentsSlotElement)
}
/**
@ -178,51 +111,51 @@ export class CalloutElement extends CustomElement {
if(this.collapseElement) {
this.collapseElement.remove()
this.collapseElement = null
this.admonitionElement = null
this.admonitionSlotElement = null
this.contentsElement = null
this.contentsSlotElement = null
}
if(this.collapse !== undefined) {
this.collapseElement = document.createElement("details")
this.containerElement.appendChild(this.collapseElement)
}
}
/**
* The element containing this callout.
* Can be recreated with {@link recreateContainerElement}.
* @type {HTMLQuoteElement}
*/
containerElement
/**
* The name of the slot where {@link containerElement} should be placed in.
* @type {string}
*/
static CONTAINER_ELEMENT_SLOT = "callout-contents"
/**
* @returns {void}
*/
recreateContainerElement() {
if(this.containerElement) {
this.containerElement.remove()
this.containerElement = null
}
this.containerElement = document.createElement("blockquote")
this.containerElement.slot = this.constructor.CONTAINER_ELEMENT_SLOT
this.appendChild(this.containerElement)
}
onConnect() {
super.onConnect()
if(this.cronus) {
this.replaceParentElement()
if(this.collapse === undefined) {
this.collapseElement = document.createElement("div")
}
else {
this.recreateContainerElement()
this.recreateCollapseElement()
this.recreateAdmonitionElement()
this.recreateContentsElement()
this.collapseElement = document.createElement("details")
if(this.collapse === "+") {
this.collapseElement.open = true
}
if(this.collapse === "-") {
this.collapseElement.open = false
}
}
this.shadowRoot.appendChild(this.collapseElement)
}
/**
* Reset the style of the parent {@link HTMLQuoteElement}.
* @returns {void}
*/
resetParentBlockquoteStyle() {
const parentClassList = this.parentElement.classList
for(const className in parentClassList.entries()) {
if(className.startsWith("callout")) {
parentClassList.remove(className)
}
}
parentClassList.add("callout")
parentClassList.add(`callout-${this.kind.toLowerCase()}`)
}
// noinspection JSUnusedGlobalSymbols
connectedCallback() {
this.attachShadow({ mode: "open" })
this.recreateCollapseElement()
this.recreateAdmonitionElement()
this.recreateContentsSlotElement()
this.resetParentBlockquoteStyle()
}
}

View file

@ -3,3 +3,4 @@ export {FrontMatterElement} from "./frontmatter.mjs"
export {HashtagElement} from "./hashtag.mjs"
export {WikilinkElement} from "./wikilink.mjs"
export {MathElement} from "./math.mjs"
export {CalloutElement} from "./callout.mjs"

View file

@ -1,5 +1,6 @@
import { Marked } from "https://unpkg.com/marked@9.1.2/lib/marked.esm.js";
import { CustomElement } from "../base.mjs";
import {toTitleCase} from "../../utils/case.mjs";
/**
@ -31,10 +32,13 @@ export class MarkdownElement extends CustomElement {
}
},
blockquote(raw) {
console.log(raw)
const calloutMatch = raw.match(/^\[!(.+)]([-+])? ?([^\n]+)?(?:\n+(.*))?/)
if(calloutMatch) {
const [, kind, collapse, admonition, contents] = calloutMatch
const [, kind, collapse, rawAdmonition, rawContents] = calloutMatch
const admonition = []
const contents = []
this.lexer.inlineTokens(rawAdmonition, admonition)
this.lexer.blockTokens(rawContents, contents)
const result = {
type: "callout",
raw,
@ -176,8 +180,14 @@ export class MarkdownElement extends CustomElement {
name: "callout",
level: "block",
renderer(token) {
console.log(token)
return `<x-callout kind="${token.kind}" collapse="${token.collapse}" admonition="${token.admonition}" contents="${token.contents}" cronus></x-callout>`
let admonition = this.parser.parseInline(token.admonition)
const contents = this.parser.parse(token.contents)
if(admonition === "") {
admonition = toTitleCase(token.kind)
}
return `<x-callout kind="${token.kind}" collapse="${token.collapse}"><span slot="callout-admonition">${admonition}</span><div slot="callout-contents">${contents}</div></x-callout>`
}
}
],

View file

@ -11,7 +11,9 @@ import {
MarkdownElement,
VaultElement,
BrowseElement,
LandingElement, MathElement,
LandingElement,
MathElement,
CalloutElement,
} from "./elements/index.mjs";
customElements.define("x-landing", LandingElement)
@ -27,4 +29,5 @@ customElements.define("x-frontmatter", FrontMatterElement)
customElements.define("x-hashtag", HashtagElement)
customElements.define("x-wikilink", WikilinkElement)
customElements.define("x-math", MathElement)
customElements.define("x-callout", CalloutElement)
customElements.define("x-browse", BrowseElement, {extends: "body"})

9
src/utils/case.mjs Normal file
View file

@ -0,0 +1,9 @@
// https://stackoverflow.com/a/196991/4334568
export function toTitleCase(str) {
return str.replace(
/\w\S*/g,
function(txt) {
return txt.charAt(0).toUpperCase() + txt.substring(1).toLowerCase();
}
);
}

View file

@ -85,7 +85,7 @@ blockquote {
margin-block-end: 0;
margin-inline-start: 0;
margin-inline-end: 0;
padding-left: 1.5ex;
padding-left: 12px;
}
.math {
@ -128,53 +128,58 @@ h1 a:hover, .wikilink.wikilink-heading:hover {
}
.callout {
background-color: color-mix(in srgb, var(--color-accent) 15%, transparent);
}
.callout [slot="callout-admonition"] {
color: var(--color-accent);
font-weight: bold;
}
.callout-abstract, .callout-summary, .callout-tldr {
--color-callout: var(--color-callout-cyan);
--color-accent: var(--color-callout-cyan);
}
.callout-info {
--color-callout: var(--color-callout-blue);
.callout-info, .callout-note, .callout-default {
--color-accent: var(--color-callout-blue);
}
.callout-todo {
--color-callout: var(--color-callout-blue)
--color-accent: var(--color-callout-blue)
}
.callout-tip, .callout-hint, .callout-important {
--color-callout: var(--color-callout-cyan);
--color-accent: var(--color-callout-cyan);
}
.callout-success, .callout-check, .callout-done {
--color-callout: var(--color-callout-green);
--color-accent: var(--color-callout-green);
}
.callout-question, .callout-help, .callout-faq {
--color-callout: var(--color-callout-orange);
--color-accent: var(--color-callout-orange);
}
.callout-warning, .callout-caution, .callout-attention {
--color-callout: var(--color-callout-orange);
--color-accent: var(--color-callout-orange);
}
.callout-failure, .callout-fail, .callout-mission {
--color-callout: var(--color-callout-red);
--color-accent: var(--color-callout-red);
}
.callout-danger, .callout-error {
--color-callout: var(--color-callout-red);
--color-accent: var(--color-callout-red);
}
.callout-bug {
--color-callout: var(--color-callout-red);
--color-accent: var(--color-callout-red);
}
.callout-example {
--color-callout: var(--color-callout-purple);
--color-accent: var(--color-callout-purple);
}
.callout-quote, .callout-cite {
--color-callout: var(--color-callout-gray);
--color-accent: var(--color-callout-gray);
}