From c904e41ea3ee00f6ca78daefe573773822c7c367 Mon Sep 17 00:00:00 2001 From: Sli Date: Sun, 15 Jun 2025 16:11:08 +0200 Subject: [PATCH] Replace tab macro with new tab web component --- .../bundled/core/components/tabs-index.ts | 64 ++++++++++++------- core/static/core/components/tabs.scss | 53 +++++++++++++++ core/static/core/style.scss | 46 ------------- core/templates/core/macros.jinja | 62 ------------------ .../templates/subscription/subscription.jinja | 18 +++--- 5 files changed, 105 insertions(+), 138 deletions(-) create mode 100644 core/static/core/components/tabs.scss diff --git a/core/static/bundled/core/components/tabs-index.ts b/core/static/bundled/core/components/tabs-index.ts index de6ff4f2..94dbe544 100644 --- a/core/static/bundled/core/components/tabs-index.ts +++ b/core/static/bundled/core/components/tabs-index.ts @@ -7,7 +7,6 @@ export class Tab extends HTMLElement { static observedAttributes = ["title", "active"]; private description = ""; private inner = ""; - private initialized = false; private active = false; attributeChangedCallback(name: string, _oldValue?: string, newValue?: string) { @@ -22,33 +21,30 @@ export class Tab extends HTMLElement { if (name === "title") { this.description = newValue; } - - this.render(); + this.dispatchEvent(new CustomEvent("ui-tab-updated", { bubbles: true })); } - render() { - if (!this.initialized) { - return; - } - const active = this.active ? "active" : ""; - const tabContent = this.getContentHtml(); - const content = html` + getButtonTemplate() { + return html` + `; + } + getContentTemplate() { + return html`
- ${unsafeHTML(tabContent)} + ${unsafeHTML(this.getContentHtml())}
`; - render(content, this); } setActive(value: boolean) { @@ -62,12 +58,10 @@ export class Tab extends HTMLElement { connectedCallback() { this.inner = this.innerHTML; this.innerHTML = ""; - this.initialized = true; - this.render(); } getContentHtml() { - const content = this.getElementsByClassName("tab-content")[0]; + const content = this.getElementsByClassName("tab-section")[0]; if (content !== undefined) { return content.innerHTML; } @@ -75,19 +69,23 @@ export class Tab extends HTMLElement { } setContentHtml(value: string) { - const content = this.getElementsByClassName("tab-content")[0]; + const content = this.getElementsByClassName("tab-section")[0]; if (content !== undefined) { content.innerHTML = value; } this.inner = value; - this.render(); } } @registerComponent("ui-tab-group") export class TabGroup extends HTMLElement { + private node: HTMLDivElement; + connectedCallback() { - this.classList.add("tabs", "shadow"); + this.node = document.createElement("div"); + this.node.classList.add("tabs", "shadow"); + this.appendChild(this.node); + this.addEventListener("ui-tab-activated", (event: CustomEvent) => { const target = event.detail as Tab; for (const tab of this.getElementsByTagName("ui-tab") as HTMLCollectionOf) { @@ -96,5 +94,27 @@ export class TabGroup extends HTMLElement { } } }); + this.addEventListener("ui-tab-updated", () => { + this.render(); + }); + + this.render(); + } + + render() { + const tabs = Array.prototype.slice.call( + this.getElementsByTagName("ui-tab"), + ) as Tab[]; + render( + html` +
+ ${tabs.map((tab) => tab.getButtonTemplate())} +
+
+ ${tabs.map((tab) => tab.getContentTemplate())} +
+ `, + this.node, + ); } } diff --git a/core/static/core/components/tabs.scss b/core/static/core/components/tabs.scss new file mode 100644 index 00000000..e8665967 --- /dev/null +++ b/core/static/core/components/tabs.scss @@ -0,0 +1,53 @@ +@import "core/static/core/colors"; + +ui-tab-group { + *[hidden] { + display: none; + } + + .tabs { + border-radius: 5px; + + .tab-headers { + display: flex; + flex-flow: row wrap; + background-color: $primary-neutral-light-color; + padding: 3px 12px 12px; + column-gap: 20px; + border-top-left-radius: 5px; + border-top-right-radius: 5px; + + .tab-header { + border: none; + padding-right: 0; + padding-left: 0; + font-size: 120%; + background-color: unset; + position: relative; + + &:after { + content: ''; + position: absolute; + bottom: 0; + left: 0; + width: 100%; + border-bottom: 4px solid darken($primary-neutral-light-color, 10%); + border-radius: 2px; + transition: all 0.2s ease-in-out; + } + + &:hover:after { + border-bottom-color: darken($primary-neutral-light-color, 20%); + } + + &.active:after { + border-bottom-color: $primary-dark-color; + } + } + } + + section { + padding: 20px; + } + } +} \ No newline at end of file diff --git a/core/static/core/style.scss b/core/static/core/style.scss index 107126ec..cbc125d7 100644 --- a/core/static/core/style.scss +++ b/core/static/core/style.scss @@ -352,52 +352,6 @@ body { text-align: center; } - .tabs { - border-radius: 5px; - - .tab-headers { - display: flex; - flex-flow: row wrap; - background-color: $primary-neutral-light-color; - padding: 3px 12px 12px; - column-gap: 20px; - border-top-left-radius: 5px; - border-top-right-radius: 5px; - - .tab-header { - border: none; - padding-right: 0; - padding-left: 0; - font-size: 120%; - background-color: unset; - position: relative; - - &:after { - content: ''; - position: absolute; - bottom: 0; - left: 0; - width: 100%; - border-bottom: 4px solid darken($primary-neutral-light-color, 10%); - border-radius: 2px; - transition: all 0.2s ease-in-out; - } - - &:hover:after { - border-bottom-color: darken($primary-neutral-light-color, 20%); - } - - &.active:after { - border-bottom-color: $primary-dark-color; - } - } - } - - section { - padding: 20px; - } - } - .tool_bar { overflow: auto; padding: 4px; diff --git a/core/templates/core/macros.jinja b/core/templates/core/macros.jinja index 40676258..ba42046f 100644 --- a/core/templates/core/macros.jinja +++ b/core/templates/core/macros.jinja @@ -245,65 +245,3 @@ {% endmacro %} - -{% macro tabs(tab_list, attrs = "") %} - {# Tab component - - Parameters: - tab_list: list[tuple[str, str]] The list of tabs to display. - Each element of the list is a tuple which first element - is the title of the tab and the second element its content - attrs: str Additional attributes to put on the enclosing div - - Example: - A basic usage would be as follow : - - {{ tabs([("title 1", "content 1"), ("title 2", "content 2")]) }} - - If you want to display more complex logic, you can define macros - and use those macros in parameters : - - {{ tabs([("title", my_macro())]) }} - - It's also possible to get and set the currently selected tab using Alpine. - Here, the title of the currently selected tab will be displayed. - Moreover, on page load, the tab will be opened on "tab 2". - -
-

- {{ tabs([("tab 1", "Hello"), ("tab 2", "World")], "x-model=current_tab") }} -
- - If you want to have translated tab titles, you can enclose the macro call - in a with block : - - {% with title=_("title"), content=_("Content") %} - {{ tabs([(tab1, content)]) }} - {% endwith %} - #} -
-
- {% for title, _ in tab_list %} - - {% endfor %} -
-
- {% for title, content in tab_list %} -
- {{ content }} -
- {% endfor %} -
-
-{% endmacro %} diff --git a/subscription/templates/subscription/subscription.jinja b/subscription/templates/subscription/subscription.jinja index 98916827..9046c296 100644 --- a/subscription/templates/subscription/subscription.jinja +++ b/subscription/templates/subscription/subscription.jinja @@ -12,6 +12,7 @@ So we give them here. If the aforementioned bug is resolved, you can remove this. #} {% block additional_js %} + {% endblock %} {% block additional_css %} + @@ -34,12 +36,12 @@ {% block content %}

{% trans %}New subscription{% endtrans %}

-
- {% with title1=_("Existing member"), title2=_("New member") %} - {{ tabs([ - (title1, form_fragment(existing_user_form, existing_user_post_url)), - (title2, form_fragment(new_user_form, new_user_post_url)), - ]) }} - {% endwith %} -
+ + + {{ form_fragment(existing_user_form, existing_user_post_url) }} + + + {{ form_fragment(new_user_form, new_user_post_url) }} + + {% endblock %}