81 lines
2.8 KiB
JavaScript
81 lines
2.8 KiB
JavaScript
export default class SlidingTabs {
|
|
/**
|
|
* @param {HTMLElement} containerElement - The main container element with the `data-sliding-tabs-container` attribute.
|
|
*/
|
|
constructor(containerElement) {
|
|
this.container = containerElement;
|
|
this.indicator = this.container.querySelector('[data-tab-indicator]');
|
|
this.tabs = this.container.querySelectorAll('[data-tab-item]');
|
|
|
|
// Find the initially active tab and store it as the component's state
|
|
this.activeTab = this.container.querySelector('.tab-active');
|
|
|
|
if (!this.indicator || this.tabs.length === 0) {
|
|
console.error('SlidingTabs component is missing required elements (indicator or items).', this.container);
|
|
return;
|
|
}
|
|
|
|
this.init();
|
|
}
|
|
|
|
init() {
|
|
// Set initial indicator position
|
|
if (this.activeTab) {
|
|
// Use a small delay to ensure layout is fully calculated
|
|
setTimeout(() => this.updateIndicator(this.activeTab), 50);
|
|
}
|
|
|
|
// Bind all necessary event listeners
|
|
this.bindEvents();
|
|
}
|
|
|
|
updateIndicator(targetTab) {
|
|
if (!targetTab) return;
|
|
|
|
const containerRect = this.container.getBoundingClientRect();
|
|
const targetRect = targetTab.getBoundingClientRect();
|
|
|
|
const left = targetRect.left - containerRect.left;
|
|
const width = targetRect.width;
|
|
|
|
this.indicator.style.left = `${left}px`;
|
|
this.indicator.style.width = `${width}px`;
|
|
}
|
|
|
|
bindEvents() {
|
|
this.tabs.forEach(tab => {
|
|
// On click, update the active state
|
|
tab.addEventListener('click', (e) => {
|
|
// e.preventDefault(); // Uncomment if using <a> tags for SPA routing
|
|
|
|
if (this.activeTab) {
|
|
this.activeTab.classList.remove('tab-active');
|
|
}
|
|
|
|
tab.classList.add('tab-active');
|
|
this.activeTab = tab; // Update the component's state
|
|
this.updateIndicator(this.activeTab);
|
|
});
|
|
|
|
// On hover, preview the indicator position
|
|
tab.addEventListener('mouseenter', () => {
|
|
this.updateIndicator(tab);
|
|
});
|
|
});
|
|
|
|
// When the mouse leaves the entire container, reset indicator to the active tab
|
|
this.container.addEventListener('mouseleave', () => {
|
|
this.updateIndicator(this.activeTab);
|
|
});
|
|
}
|
|
}
|
|
|
|
// ---- Auto-Initialization Logic ----
|
|
// This is the "bootstrapper". It finds all components on the page and brings them to life.
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
const allTabContainers = document.querySelectorAll('[data-sliding-tabs-container]');
|
|
allTabContainers.forEach(container => {
|
|
new SlidingTabs(container);
|
|
});
|
|
});
|