Replace tab macro with new tab web component

This commit is contained in:
2025-06-15 16:11:08 +02:00
parent 2dd4fd5c71
commit c904e41ea3
5 changed files with 105 additions and 138 deletions

View File

@ -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`
<button
role="tab"
?aria-selected=${active}
class="tab-header clickable ${active}"
?aria-selected=${this.active}
class="tab-header clickable ${this.active ? "active" : ""}"
@click="${() => this.setActive(true)}"
>
${this.description}
</button>
`;
}
getContentTemplate() {
return html`
<section
class="tab-content"
?hidden=${!active}
class="tab-section"
?hidden=${!this.active}
>
${unsafeHTML(tabContent)}
${unsafeHTML(this.getContentHtml())}
</section>
`;
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<Tab>) {
@ -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`
<div class="tab-headers">
${tabs.map((tab) => tab.getButtonTemplate())}
</div>
<div class="tab-content">
${tabs.map((tab) => tab.getContentTemplate())}
</div>
`,
this.node,
);
}
}

View File

@ -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;
}
}
}

View File

@ -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;