mirror of
https://github.com/ae-utbm/sith.git
synced 2026-03-27 05:49:42 +00:00
Compare commits
2 Commits
taiste
...
fix-link-s
| Author | SHA1 | Date | |
|---|---|---|---|
|
efdf71d69e
|
|||
|
3bc4f1300e
|
@@ -1,11 +1,7 @@
|
|||||||
{% extends "core/base.jinja" %}
|
{% extends "core/base.jinja" %}
|
||||||
{% from 'core/macros.jinja' import user_profile_link, select_all_checkbox %}
|
{% from 'core/macros.jinja' import user_profile_link, select_all_checkbox %}
|
||||||
|
|
||||||
{% block additional_js %}
|
|
||||||
<script type="module" src="{{ static("bundled/core/components/ajax-select-index.ts") }}"></script>
|
|
||||||
{% endblock %}
|
|
||||||
{% block additional_css %}
|
{% block additional_css %}
|
||||||
<link rel="stylesheet" href="{{ static("bundled/core/components/ajax-select-index.css") }}">
|
|
||||||
<link rel="stylesheet" href="{{ static("club/members.scss") }}">
|
<link rel="stylesheet" href="{{ static("club/members.scss") }}">
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|||||||
@@ -1,18 +1,106 @@
|
|||||||
import { inheritHtmlElement, registerComponent } from "#core:utils/web-components.ts";
|
import { inheritHtmlElement, registerComponent } from "#core:utils/web-components.ts";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an abstract class for ElementOnce types Web Components
|
||||||
|
*
|
||||||
|
* Those class aren't really abstract because that would be complicated with the
|
||||||
|
* multiple inheritance involved
|
||||||
|
* Instead, we just raise an unimplemented error
|
||||||
|
**/
|
||||||
|
function elementOnce<K extends keyof HTMLElementTagNameMap>(tagName: K) {
|
||||||
|
return class ElementOnce extends inheritHtmlElement(tagName) {
|
||||||
|
getElementQuerySelector(): string {
|
||||||
|
throw new Error("Unimplemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
clearNode() {
|
||||||
|
while (this.firstChild) {
|
||||||
|
this.removeChild(this.lastChild);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
refresh() {
|
||||||
|
this.clearNode();
|
||||||
|
if (document.querySelectorAll(this.getElementQuerySelector()).length === 0) {
|
||||||
|
this.appendChild(this.node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
connectedCallback() {
|
||||||
|
super.connectedCallback(false);
|
||||||
|
this.refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
disconnectedCallback() {
|
||||||
|
// The MutationObserver can't see web components being removed
|
||||||
|
// It also can't see if something is removed inside after the component gets deleted
|
||||||
|
// We need to manually clear the containing node to trigger the observer
|
||||||
|
this.clearNode();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set of ElementOnce type components to refresh with the observer
|
||||||
|
const registeredComponents: Set<string> = new Set();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper to register ElementOnce types Web Components
|
||||||
|
* It's a wrapper around registerComponent that registers that component on
|
||||||
|
* a MutationObserver that activates a refresh on them when elements are removed
|
||||||
|
**/
|
||||||
|
function registerElementOnce(name: string, options?: ElementDefinitionOptions) {
|
||||||
|
registeredComponents.add(name);
|
||||||
|
return registerComponent(name, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
const startObserver = (observer: MutationObserver) => {
|
||||||
|
observer.observe(document, {
|
||||||
|
// We want to also listen for elements contained in the header (eg: link)
|
||||||
|
subtree: true,
|
||||||
|
childList: true,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Refresh *-once components when changes happens
|
||||||
|
const observer = new MutationObserver((mutations: MutationRecord[]) => {
|
||||||
|
observer.disconnect();
|
||||||
|
for (const mutation of mutations) {
|
||||||
|
for (const node of mutation.removedNodes) {
|
||||||
|
if (node.nodeType !== node.ELEMENT_NODE) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const refreshElement = (componentName: string, tagName: string) => {
|
||||||
|
for (const element of document.getElementsByTagName(componentName)) {
|
||||||
|
// We can't guess if an element is compatible before we get one
|
||||||
|
// We exit the function completely if it's not compatible
|
||||||
|
if (
|
||||||
|
(element as any).inheritedTagName.toUpperCase() !== tagName.toUpperCase()
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
(element as any).refresh();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
for (const registered of registeredComponents) {
|
||||||
|
refreshElement(registered, (node as HTMLElement).tagName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
startObserver(observer);
|
||||||
|
});
|
||||||
|
|
||||||
|
startObserver(observer);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Web component used to import css files only once
|
* Web component used to import css files only once
|
||||||
* If called multiple times or the file was already imported, it does nothing
|
* If called multiple times or the file was already imported, it does nothing
|
||||||
**/
|
**/
|
||||||
@registerComponent("link-once")
|
@registerElementOnce("link-once")
|
||||||
export class LinkOnce extends inheritHtmlElement("link") {
|
export class LinkOnce extends elementOnce("link") {
|
||||||
connectedCallback() {
|
getElementQuerySelector(): string {
|
||||||
super.connectedCallback(false);
|
|
||||||
// We get href from node.attributes instead of node.href to avoid getting the domain part
|
// We get href from node.attributes instead of node.href to avoid getting the domain part
|
||||||
const href = this.node.attributes.getNamedItem("href").nodeValue;
|
return `link[href='${this.node.attributes.getNamedItem("href").nodeValue}']`;
|
||||||
if (document.querySelectorAll(`link[href='${href}']`).length === 0) {
|
|
||||||
this.appendChild(this.node);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -20,14 +108,10 @@ export class LinkOnce extends inheritHtmlElement("link") {
|
|||||||
* Web component used to import javascript files only once
|
* Web component used to import javascript files only once
|
||||||
* If called multiple times or the file was already imported, it does nothing
|
* If called multiple times or the file was already imported, it does nothing
|
||||||
**/
|
**/
|
||||||
@registerComponent("script-once")
|
@registerElementOnce("script-once")
|
||||||
export class ScriptOnce extends inheritHtmlElement("script") {
|
export class ScriptOnce extends inheritHtmlElement("script") {
|
||||||
connectedCallback() {
|
getElementQuerySelector(): string {
|
||||||
super.connectedCallback(false);
|
// We get href from node.attributes instead of node.src to avoid getting the domain part
|
||||||
// We get src from node.attributes instead of node.src to avoid getting the domain part
|
return `script[src='${this.node.attributes.getNamedItem("src").nodeValue}']`;
|
||||||
const src = this.node.attributes.getNamedItem("src").nodeValue;
|
|
||||||
if (document.querySelectorAll(`script[src='${src}']`).length === 0) {
|
|
||||||
this.appendChild(this.node);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ export function registerComponent(name: string, options?: ElementDefinitionOptio
|
|||||||
**/
|
**/
|
||||||
export function inheritHtmlElement<K extends keyof HTMLElementTagNameMap>(tagName: K) {
|
export function inheritHtmlElement<K extends keyof HTMLElementTagNameMap>(tagName: K) {
|
||||||
return class Inherited extends HTMLElement {
|
return class Inherited extends HTMLElement {
|
||||||
|
readonly inheritedTagName = tagName;
|
||||||
protected node: HTMLElementTagNameMap[K];
|
protected node: HTMLElementTagNameMap[K];
|
||||||
|
|
||||||
connectedCallback(autoAddNode?: boolean) {
|
connectedCallback(autoAddNode?: boolean) {
|
||||||
|
|||||||
@@ -1,14 +1,7 @@
|
|||||||
{% extends "core/base.jinja" %}
|
{% extends "core/base.jinja" %}
|
||||||
|
|
||||||
{%- block additional_js -%}
|
|
||||||
<script type="module" src="{{ static("bundled/core/components/ajax-select-index.ts") }}"></script>
|
|
||||||
{%- endblock -%}
|
|
||||||
|
|
||||||
{%- block additional_css -%}
|
{%- block additional_css -%}
|
||||||
<link rel="stylesheet" href="{{ static('user/user_preferences.scss') }}">
|
<link rel="stylesheet" href="{{ static('user/user_preferences.scss') }}">
|
||||||
{# importing ajax-select-index is necessary for it to be applied after HTMX reload #}
|
|
||||||
<link rel="stylesheet" href="{{ static("bundled/core/components/ajax-select-index.css") }}">
|
|
||||||
<link rel="stylesheet" href="{{ static("core/components/ajax-select.scss") }}">
|
|
||||||
{%- endblock -%}
|
{%- endblock -%}
|
||||||
|
|
||||||
{% block title %}
|
{% block title %}
|
||||||
|
|||||||
@@ -6,14 +6,8 @@
|
|||||||
{% trans %}New subscription{% endtrans %}
|
{% trans %}New subscription{% endtrans %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{# The following statics are bundled with our autocomplete select.
|
|
||||||
However, if one tries to swap a form by another, then the urls in script-once
|
|
||||||
and link-once disappear.
|
|
||||||
So we give them here.
|
|
||||||
If the aforementioned bug is resolved, you can remove this. #}
|
|
||||||
{% block additional_js %}
|
{% block additional_js %}
|
||||||
<script type="module" src="{{ static('bundled/core/components/tabs-index.ts') }}"></script>
|
<script type="module" src="{{ static('bundled/core/components/tabs-index.ts') }}"></script>
|
||||||
<script type="module" src="{{ static("bundled/core/components/ajax-select-index.ts") }}"></script>
|
|
||||||
<script
|
<script
|
||||||
type="module"
|
type="module"
|
||||||
src="{{ static("bundled/subscription/creation-form-existing-user-index.ts") }}"
|
src="{{ static("bundled/subscription/creation-form-existing-user-index.ts") }}"
|
||||||
@@ -21,8 +15,6 @@
|
|||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block additional_css %}
|
{% block additional_css %}
|
||||||
<link rel="stylesheet" href="{{ static("core/components/tabs.scss") }}">
|
<link rel="stylesheet" href="{{ static("core/components/tabs.scss") }}">
|
||||||
<link rel="stylesheet" href="{{ static("bundled/core/components/ajax-select-index.css") }}">
|
|
||||||
<link rel="stylesheet" href="{{ static("core/components/ajax-select.scss") }}">
|
|
||||||
<link rel="stylesheet" href="{{ static("subscription/css/subscription.scss") }}">
|
<link rel="stylesheet" href="{{ static("subscription/css/subscription.scss") }}">
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user