From 2dd4fd5c710775be9409d1e3fc42077c4aef1675 Mon Sep 17 00:00:00 2001 From: Sli Date: Sun, 15 Jun 2025 15:04:07 +0200 Subject: [PATCH] Initial tab concept --- .../bundled/core/components/tabs-index.ts | 100 ++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 core/static/bundled/core/components/tabs-index.ts diff --git a/core/static/bundled/core/components/tabs-index.ts b/core/static/bundled/core/components/tabs-index.ts new file mode 100644 index 00000000..de6ff4f2 --- /dev/null +++ b/core/static/bundled/core/components/tabs-index.ts @@ -0,0 +1,100 @@ +import { registerComponent } from "#core:utils/web-components"; +import { html, render } from "lit-html"; +import { unsafeHTML } from "lit-html/directives/unsafe-html.js"; + +@registerComponent("ui-tab") +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) { + const activeOld = this.active; + this.active = this.hasAttribute("active"); + if (this.active !== activeOld && this.active) { + this.dispatchEvent( + new CustomEvent("ui-tab-activated", { detail: this, bubbles: true }), + ); + } + + if (name === "title") { + this.description = newValue; + } + + this.render(); + } + + render() { + if (!this.initialized) { + return; + } + const active = this.active ? "active" : ""; + const tabContent = this.getContentHtml(); + const content = html` + +
+ ${unsafeHTML(tabContent)} +
+ `; + render(content, this); + } + + setActive(value: boolean) { + if (value) { + this.setAttribute("active", ""); + } else { + this.removeAttribute("active"); + } + } + + connectedCallback() { + this.inner = this.innerHTML; + this.innerHTML = ""; + this.initialized = true; + this.render(); + } + + getContentHtml() { + const content = this.getElementsByClassName("tab-content")[0]; + if (content !== undefined) { + return content.innerHTML; + } + return this.inner; + } + + setContentHtml(value: string) { + const content = this.getElementsByClassName("tab-content")[0]; + if (content !== undefined) { + content.innerHTML = value; + } + this.inner = value; + this.render(); + } +} + +@registerComponent("ui-tab-group") +export class TabGroup extends HTMLElement { + connectedCallback() { + this.classList.add("tabs", "shadow"); + this.addEventListener("ui-tab-activated", (event: CustomEvent) => { + const target = event.detail as Tab; + for (const tab of this.getElementsByTagName("ui-tab") as HTMLCollectionOf) { + if (tab !== target) { + tab.setActive(false); + } + } + }); + } +}