aura-glass
Version:
A comprehensive glassmorphism design system for React applications with 142+ production-ready components
155 lines (152 loc) • 4.33 kB
JavaScript
import { useState, useRef, useCallback } from 'react';
import { KEYS } from './a11y.js';
/**
* AuraGlass Accessibility Hooks
* Enhanced accessibility hooks for comprehensive WCAG 2.1 AA compliance
*/
function useKeyboardNavigation(options) {
const {
items,
orientation = 'vertical',
loop = true,
homeEndKeys = true,
pageKeys = false,
onActivate,
onSelect
} = options;
const [focusedIndex, setFocusedIndex] = useState(0);
const [selectedIds, setSelectedIds] = useState(new Set());
const itemRefs = useRef(new Map());
const enabledItems = items.filter(item => !item.disabled);
const focusItem = useCallback(index => {
if (index < 0 || index >= enabledItems.length) return;
const item = enabledItems[index];
const element = itemRefs.current.get(item.id);
if (element) {
element.focus();
setFocusedIndex(index);
}
}, [enabledItems]);
const moveNext = useCallback(() => {
const nextIndex = focusedIndex + 1;
if (nextIndex < enabledItems.length) {
focusItem(nextIndex);
} else if (loop) {
focusItem(0);
}
}, [focusedIndex, enabledItems.length, loop, focusItem]);
const movePrevious = useCallback(() => {
const prevIndex = focusedIndex - 1;
if (prevIndex >= 0) {
focusItem(prevIndex);
} else if (loop) {
focusItem(enabledItems.length - 1);
}
}, [focusedIndex, enabledItems.length, loop, focusItem]);
const moveToFirst = useCallback(() => {
focusItem(0);
}, [focusItem]);
const moveToLast = useCallback(() => {
focusItem(enabledItems.length - 1);
}, [enabledItems.length, focusItem]);
const handleKeyDown = useCallback(event => {
const {
key
} = event;
const isVertical = orientation === 'vertical' || orientation === 'both';
const isHorizontal = orientation === 'horizontal' || orientation === 'both';
switch (key) {
case KEYS.ARROW_DOWN:
if (isVertical) {
event.preventDefault();
moveNext();
}
break;
case KEYS.ARROW_UP:
if (isVertical) {
event.preventDefault();
movePrevious();
}
break;
case KEYS.ARROW_RIGHT:
if (isHorizontal) {
event.preventDefault();
moveNext();
}
break;
case KEYS.ARROW_LEFT:
if (isHorizontal) {
event.preventDefault();
movePrevious();
}
break;
case KEYS.HOME:
if (homeEndKeys) {
event.preventDefault();
moveToFirst();
}
break;
case KEYS.END:
if (homeEndKeys) {
event.preventDefault();
moveToLast();
}
break;
case KEYS.PAGE_UP:
if (pageKeys) {
event.preventDefault();
const jumpIndex = Math.max(0, focusedIndex - 10);
focusItem(jumpIndex);
}
break;
case KEYS.PAGE_DOWN:
if (pageKeys) {
event.preventDefault();
const jumpIndex = Math.min(enabledItems.length - 1, focusedIndex + 10);
focusItem(jumpIndex);
}
break;
case KEYS.ENTER:
case KEYS.SPACE:
if (focusedIndex >= 0 && focusedIndex < enabledItems.length) {
event.preventDefault();
const item = enabledItems[focusedIndex];
onActivate?.(item.id);
}
break;
}
}, [orientation, moveNext, movePrevious, moveToFirst, moveToLast, homeEndKeys, pageKeys, focusedIndex, enabledItems, focusItem, onActivate]);
const registerItem = useCallback((id, element) => {
if (element) {
itemRefs.current.set(id, element);
} else {
itemRefs.current.delete(id);
}
}, []);
const selectItem = useCallback((id, multiSelect = false) => {
setSelectedIds(prev => {
const newSelection = new Set(multiSelect ? prev : []);
if (newSelection.has(id)) {
newSelection.delete(id);
} else {
newSelection.add(id);
}
return newSelection;
});
onSelect?.(id);
}, [onSelect]);
return {
focusedIndex,
selectedIds,
handleKeyDown,
registerItem,
focusItem,
selectItem,
moveNext,
movePrevious,
moveToFirst,
moveToLast
};
}
export { useKeyboardNavigation };
//# sourceMappingURL=a11yHooks.js.map