Changeset 0.37.2 (#214)
Co-authored-by: gobha <jasafpro@gmail.com> Co-committed-by: gobha <jasafpro@gmail.com>
This commit is contained in:
60
src/js/sw/primitives/tabs.js
Normal file
60
src/js/sw/primitives/tabs.js
Normal file
@@ -0,0 +1,60 @@
|
||||
/**
|
||||
* Tabs primitive — horizontal tab bar with overflow scroll arrows
|
||||
* tabs: [{ label, value, icon?, disabled? }]
|
||||
*/
|
||||
const { html } = window;
|
||||
const { useRef, useEffect, useState, useCallback } = hooks;
|
||||
|
||||
export function Tabs({ tabs = [], active, onChange }) {
|
||||
const scrollRef = useRef(null);
|
||||
const [overflow, setOverflow] = useState({ left: false, right: false });
|
||||
|
||||
const checkOverflow = useCallback(() => {
|
||||
const el = scrollRef.current;
|
||||
if (!el) return;
|
||||
setOverflow({
|
||||
left: el.scrollLeft > 0,
|
||||
right: el.scrollLeft + el.clientWidth < el.scrollWidth - 1,
|
||||
});
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
checkOverflow();
|
||||
const el = scrollRef.current;
|
||||
if (el) el.addEventListener('scroll', checkOverflow, { passive: true });
|
||||
window.addEventListener('resize', checkOverflow);
|
||||
return () => {
|
||||
if (el) el.removeEventListener('scroll', checkOverflow);
|
||||
window.removeEventListener('resize', checkOverflow);
|
||||
};
|
||||
}, [checkOverflow, tabs]);
|
||||
|
||||
const scroll = (dir) => {
|
||||
const el = scrollRef.current;
|
||||
if (el) el.scrollBy({ left: dir * 150, behavior: 'smooth' });
|
||||
};
|
||||
|
||||
return html`
|
||||
<div class="sw-tabs">
|
||||
${overflow.left && html`
|
||||
<button class="sw-tabs__arrow sw-tabs__arrow--left" onClick=${() => scroll(-1)} aria-label="Scroll left">\u2039</button>
|
||||
`}
|
||||
<div class="sw-tabs__scroll" ref=${scrollRef}>
|
||||
${tabs.map(tab => html`
|
||||
<button
|
||||
class="sw-tabs__tab ${tab.value === active ? 'sw-tabs__tab--active' : ''} ${tab.disabled ? 'sw-tabs__tab--disabled' : ''}"
|
||||
disabled=${tab.disabled}
|
||||
onClick=${() => !tab.disabled && onChange && onChange(tab.value)}
|
||||
role="tab"
|
||||
aria-selected=${tab.value === active}>
|
||||
${tab.icon && html`<span class="sw-tabs__icon">${tab.icon}</span>`}
|
||||
${tab.label}
|
||||
</button>
|
||||
`)}
|
||||
</div>
|
||||
${overflow.right && html`
|
||||
<button class="sw-tabs__arrow sw-tabs__arrow--right" onClick=${() => scroll(1)} aria-label="Scroll right">\u203a</button>
|
||||
`}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
Reference in New Issue
Block a user