UNPKG

noph-ui

Version:

Noph UI is a modern, powerful UI library for Svelte 5, fully aligned with the Material 3 guidelines. Build stunning, consistent user interfaces with the efficiency and flexibility of Svelte and Google’s Material Design framework.

70 lines (69 loc) 2.56 kB
export const rovingTabindex = (itemSelector, options = {}) => { const { currentAttr = 'aria-current', currentValue = 'page' } = options; return (node) => { const getItems = () => Array.from(node.querySelectorAll(itemSelector)); const setTabstop = (target) => { for (const i of getItems()) { const wanted = i === target ? 0 : -1; if (i.tabIndex !== wanted) i.tabIndex = wanted; } }; const sync = () => { const items = getItems(); if (items.length === 0) return; const focused = node.querySelector(`${itemSelector}:focus`); const current = items.find((i) => i.getAttribute(currentAttr) === currentValue); setTabstop(focused ?? current ?? items[0]); }; const onFocusIn = (event) => { const target = event.target.closest(itemSelector); if (!target || !node.contains(target) || target.tabIndex === 0) return; setTabstop(target); }; sync(); node.addEventListener('focusin', onFocusIn); const observer = new MutationObserver(sync); observer.observe(node, { attributes: true, attributeFilter: [currentAttr], subtree: true, childList: true, }); return () => { node.removeEventListener('focusin', onFocusIn); observer.disconnect(); }; }; }; export const arrowKeyNav = (itemSelector, orientation = 'vertical') => (event) => { const [prev, next] = orientation === 'vertical' ? ['ArrowUp', 'ArrowDown'] : ['ArrowLeft', 'ArrowRight']; const { key } = event; if (key !== prev && key !== next && key !== 'Home' && key !== 'End') return; const items = Array.from(event.currentTarget.querySelectorAll(itemSelector)); if (items.length === 0) return; const focused = event.currentTarget.querySelector(`${itemSelector}:focus`); if (!focused) return; const currentIndex = items.indexOf(focused); let target; if (key === 'Home') { target = items[0]; } else if (key === 'End') { target = items[items.length - 1]; } else { const delta = key === next ? 1 : -1; const index = currentIndex + delta; target = index < 0 ? items[items.length - 1] : index >= items.length ? items[0] : items[index]; } target.focus(); event.preventDefault(); };