From 0a0092e1896243111b2dcae70de4fbcf249cf926 Mon Sep 17 00:00:00 2001 From: Sli Date: Sat, 19 Oct 2024 17:54:34 +0200 Subject: [PATCH] Add link-once and script-once web components --- .../webpack/core/components/include-index.ts | 33 +++++++++++++++++++ core/static/webpack/easymde-index.ts | 6 ++-- core/static/webpack/utils/web-components.ts | 8 +++-- core/templates/core/base.jinja | 1 + .../core/widgets/markdown_textarea.jinja | 10 +++--- 5 files changed, 47 insertions(+), 11 deletions(-) create mode 100644 core/static/webpack/core/components/include-index.ts diff --git a/core/static/webpack/core/components/include-index.ts b/core/static/webpack/core/components/include-index.ts new file mode 100644 index 00000000..ea8dbb6b --- /dev/null +++ b/core/static/webpack/core/components/include-index.ts @@ -0,0 +1,33 @@ +import { inheritHtmlElement, registerComponent } from "#core:utils/web-components"; + +/** + * Web component used to import css files only once + * If called multiple times or the file was already imported, it does nothing + **/ +@registerComponent("link-once") +export class LinkOnce extends inheritHtmlElement("link") { + connectedCallback() { + super.connectedCallback(false); + // We get href from node.attributes instead of node.href to avoid getting the domain part + const href = this.node.attributes.getNamedItem("href").nodeValue; + if (document.querySelectorAll(`link[href='${href}']`).length === 0) { + this.appendChild(this.node); + } + } +} + +/** + * Web component used to import javascript files only once + * If called multiple times or the file was already imported, it does nothing + **/ +@registerComponent("script-once") +export class ScriptOnce extends inheritHtmlElement("script") { + connectedCallback() { + super.connectedCallback(false); + // We get src from node.attributes instead of node.src to avoid getting the domain part + const src = this.node.attributes.getNamedItem("src").nodeValue; + if (document.querySelectorAll(`script[src='${src}']`).length === 0) { + this.appendChild(this.node); + } + } +} diff --git a/core/static/webpack/easymde-index.ts b/core/static/webpack/easymde-index.ts index 04a33f17..734aa9ec 100644 --- a/core/static/webpack/easymde-index.ts +++ b/core/static/webpack/easymde-index.ts @@ -185,8 +185,8 @@ const loadEasyMde = (textarea: HTMLTextAreaElement) => { @registerComponent("markdown-input") class MarkdownInput extends inheritHtmlElement("textarea") { - constructor() { - super(); - window.addEventListener("DOMContentLoaded", () => loadEasyMde(this.node)); + connectedCallback() { + super.connectedCallback(); + loadEasyMde(this.node); } } diff --git a/core/static/webpack/utils/web-components.ts b/core/static/webpack/utils/web-components.ts index 2899a5af..f6949731 100644 --- a/core/static/webpack/utils/web-components.ts +++ b/core/static/webpack/utils/web-components.ts @@ -30,8 +30,7 @@ export function inheritHtmlElement(tagNam return class Inherited extends HTMLElement { protected node: HTMLElementTagNameMap[K]; - constructor() { - super(); + connectedCallback(autoAddNode?: boolean) { this.node = document.createElement(tagName); const attributes: Attr[] = []; // We need to make a copy to delete while iterating for (const attr of this.attributes) { @@ -44,7 +43,10 @@ export function inheritHtmlElement(tagNam this.removeAttributeNode(attr); this.node.setAttributeNode(attr); } - this.appendChild(this.node); + // Atuomatically add node to DOM if autoAddNode is true or unspecified + if (autoAddNode === undefined || autoAddNode) { + this.appendChild(this.node); + } } }; } diff --git a/core/templates/core/base.jinja b/core/templates/core/base.jinja index 8ce2eb80..fbd4c997 100644 --- a/core/templates/core/base.jinja +++ b/core/templates/core/base.jinja @@ -21,6 +21,7 @@ + diff --git a/core/templates/core/widgets/markdown_textarea.jinja b/core/templates/core/widgets/markdown_textarea.jinja index 287e4521..1131d5bd 100644 --- a/core/templates/core/widgets/markdown_textarea.jinja +++ b/core/templates/core/widgets/markdown_textarea.jinja @@ -1,7 +1,7 @@
- {% if widget.value %}{{ widget.value }}{% endif %} + + - {# The easymde script can be included twice, it's safe in the code #} - - -
+ {% if widget.value %}{{ widget.value }}{% endif %} + +