From 9ea532883623eb0813c201072135f65912d34851 Mon Sep 17 00:00:00 2001 From: Wendelin Date: Wed, 10 Sep 2025 13:14:11 +0200 Subject: [PATCH] Hide scroll buttons if not needed, remove dependencies --- packages/webawesome/package.json | 2 +- .../src/components/tab-group/tab-group.ts | 70 ++++++++++++++----- 2 files changed, 54 insertions(+), 18 deletions(-) diff --git a/packages/webawesome/package.json b/packages/webawesome/package.json index 8ae2b96f3..050f16f76 100644 --- a/packages/webawesome/package.json +++ b/packages/webawesome/package.json @@ -4,7 +4,7 @@ "access": "public" }, "description": "A forward-thinking library of web components.", - "version": "3.0.0-beta.4.ha.1", + "version": "3.0.0-beta.4.ha.2", "homepage": "https://webawesome.com/", "author": "Web Awesome", "license": "MIT", diff --git a/packages/webawesome/src/components/tab-group/tab-group.ts b/packages/webawesome/src/components/tab-group/tab-group.ts index a464676ba..b0a6c95e6 100644 --- a/packages/webawesome/src/components/tab-group/tab-group.ts +++ b/packages/webawesome/src/components/tab-group/tab-group.ts @@ -1,5 +1,5 @@ import { html } from 'lit'; -import { customElement, property, query, state } from 'lit/decorators.js'; +import { customElement, eventOptions, property, query, state } from 'lit/decorators.js'; import { classMap } from 'lit/directives/class-map.js'; import { WaTabHideEvent } from '../../events/tab-hide.js'; import { WaTabShowEvent } from '../../events/tab-show.js'; @@ -8,9 +8,7 @@ import { watch } from '../../internal/watch.js'; import WebAwesomeElement from '../../internal/webawesome-element.js'; import { LocalizeController } from '../../utilities/localize.js'; import '../button/button.js'; -import '../tab-panel/tab-panel.js'; import type WaTabPanel from '../tab-panel/tab-panel.js'; -import '../tab/tab.js'; import type WaTab from '../tab/tab.js'; import styles from './tab-group.css'; @@ -61,6 +59,8 @@ export default class WaTabGroup extends WebAwesomeElement { @query('.nav') nav: HTMLElement; @state() private hasScrollControls = false; + @state() private shouldHideScrollStartButton = false; + @state() private shouldHideScrollEndButton = false; /** Sets the active tab. */ @property({ reflect: true }) active = ''; @@ -78,10 +78,13 @@ export default class WaTabGroup extends WebAwesomeElement { @property({ attribute: 'without-scroll-controls', type: Boolean }) withoutScrollControls = false; /** The tag name of the tab element. Needs to be set if a custom tab element is used */ - @property() tabTag = 'wa-tab'; + @property({ attribute: 'tab-tag' }) tabTag = 'wa-tab'; /** The tag name of the tab panel element. Needs to be set if a custom panel element is used */ - @property() tabPanelTag = 'wa-tab-panel'; + @property({ attribute: 'tab-panel-tag' }) tabPanelTag = 'wa-tab-panel'; + + /** When true, only the tab elements will be rendered and the tab panels will be ignored. */ + @property({ attribute: 'tab-only', type: Boolean }) tabOnly = false; connectedCallback() { super.connectedCallback(); @@ -163,6 +166,9 @@ export default class WaTabGroup extends WebAwesomeElement { } private getAllPanels() { + if (this.tabOnly) { + return []; + } return [...this.body.assignedElements()].filter(el => el.tagName.toLowerCase() === this.tabPanelTag) as [ WaTabPanel, ]; @@ -172,7 +178,7 @@ export default class WaTabGroup extends WebAwesomeElement { return this.tabs.find(el => el.active); } - private handleClick(event: MouseEvent) { + protected handleClick(event: MouseEvent) { const target = event.target as HTMLElement; const tab = target.closest(this.tabTag) as WaTab | null; const tabGroup = tab?.closest(this.localName); @@ -344,14 +350,16 @@ export default class WaTabGroup extends WebAwesomeElement { } private setAriaLabels() { - // Link each tab with its corresponding panel - this.tabs.forEach(tab => { - const panel = this.panels.find(el => el.name === tab.panel); - if (panel) { - tab.setAttribute('aria-controls', panel.getAttribute('id')!); - panel.setAttribute('aria-labelledby', tab.getAttribute('id')!); - } - }); + if (!this.tabOnly) { + // Link each tab with its corresponding panel + this.tabs.forEach(tab => { + const panel = this.panels.find(el => el.name === tab.panel); + if (panel) { + tab.setAttribute('aria-controls', panel.getAttribute('id')!); + panel.setAttribute('aria-labelledby', tab.getAttribute('id')!); + } + }); + } } // This stores tabs and panels so we can refer to a cache instead of calling querySelectorAll() multiple times. @@ -373,6 +381,28 @@ export default class WaTabGroup extends WebAwesomeElement { } } + /** + * The reality of the browser means that we can't expect the scroll position to be exactly what we want it to be, so + * we add one pixel of wiggle room to our calculations. + */ + private scrollOffset = 1; + + @eventOptions({ passive: true }) + private updateScrollButtons() { + if (this.hasScrollControls) { + this.shouldHideScrollStartButton = this.scrollFromStart() <= this.scrollOffset; + this.shouldHideScrollEndButton = this.isScrolledToEnd(); + } + } + + private isScrolledToEnd() { + return this.scrollFromStart() + this.nav.clientWidth >= this.nav.scrollWidth - this.scrollOffset; + } + + private scrollFromStart() { + return this.localize.dir() === 'rtl' ? -this.nav.scrollLeft : this.nav.scrollLeft; + } + @watch('withoutScrollControls', { waitUntilFirstUpdate: true }) updateScrollControls() { if (this.withoutScrollControls) { @@ -386,6 +416,8 @@ export default class WaTabGroup extends WebAwesomeElement { this.hasScrollControls = ['top', 'bottom'].includes(this.placement) && this.nav.scrollWidth > this.nav.clientWidth + 1; } + + this.updateScrollButtons(); } render() { @@ -406,7 +438,7 @@ export default class WaTabGroup extends WebAwesomeElement { @keydown=${this.handleKeyDown} >