/** * `materialTabs`: the behaviour of `` — WAI-ARIA's tabs with automatic activation. * * The arrow keys move along the tablist (mirrored on a right-to-left page), Home and End jump to * its ends, disabled tabs are passed over, focus follows the choice, and only the chosen tab is in * the Tab order. A change of tab runs in a view transition, which moves the active indicator from * the old tab to the new one and cross-fades the panels — unless the visitor prefers reduced motion * or the browser has no view transitions, when it simply changes. */ const reducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)') document.addEventListener('alpine:init', () => { window.Alpine.data('materialTabs', () => ({ tabs() { return [...this.$refs.bar.querySelectorAll('[role="tab"]:not(:disabled)')] }, choose(name, focus = false) { const apply = () => { this.selected = name return this.$nextTick() } if (this.selected === name || reducedMotion.matches || !document.startViewTransition) { apply() } else { document.startViewTransition(apply) } if (focus) { this.$refs.bar.querySelector(`[data-md-tab="${CSS.escape(name)}"]`)?.focus() } }, step(by) { const tabs = this.tabs() const rtl = getComputedStyle(this.$refs.bar).direction === 'rtl' const at = tabs.findIndex((tab) => tab.dataset.mdTab === this.selected) const next = tabs[(at + (rtl ? -by : by) + tabs.length) % tabs.length] if (next) { this.choose(next.dataset.mdTab, true) } }, edge(last) { const tabs = this.tabs() const tab = last ? tabs.at(-1) : tabs[0] if (tab) { this.choose(tab.dataset.mdTab, true) } }, })) })