UNPKG

@chayns-components/core

Version:

A set of beautiful React components for developing your own applications with chayns.

219 lines • 8.2 kB
import { Children, isValidElement } from 'react'; import { LIST_ITEM_MARKER } from './list-item/ListItem.utils'; export const isDirectListItemElement = node => /*#__PURE__*/isValidElement(node) && node.type.displayName === 'ListItem'; export const isMarkedListItemElement = node => /*#__PURE__*/isValidElement(node) && typeof node.type !== 'string' && node.type.$$listItem === LIST_ITEM_MARKER; export const shouldShowExpandIndicator = node => Children.toArray(node).some(child => { if (isDirectListItemElement(child)) { return child.props.shouldHideIndicator !== true && child.props.children !== undefined; } if (isMarkedListItemElement(child)) { return child.props.isExpandable === true && child.props.shouldHideIndicator !== true; } if (/*#__PURE__*/isValidElement(child) && child.props.children !== undefined) { return shouldShowExpandIndicator(child.props.children); } return false; }); const LEFT_ELEMENT_FOCUSABLE_SELECTOR = '[data-left-element="true"] a[href], [data-left-element="true"] button, [data-left-element="true"] input, [data-left-element="true"] select, [data-left-element="true"] textarea, [data-left-element="true"] [tabindex], [data-left-element="true"] [contenteditable="true"]'; const RIGHT_ELEMENT_FOCUSABLE_SELECTOR = '[data-right-element="true"] a[href], [data-right-element="true"] button, [data-right-element="true"] input, [data-right-element="true"] select, [data-right-element="true"] textarea, [data-right-element="true"] [tabindex], [data-right-element="true"] [contenteditable="true"]'; const getFocusableElements = (root, selector) => Array.from(root.querySelectorAll(selector)).filter(element => !element.hasAttribute('disabled') && element.getAttribute('tabindex') !== '-1'); const getFocusedElementIndex = (elements, targetElement) => { if (!targetElement || elements.length === 0) { return -1; } return elements.findIndex(element => element === targetElement || element.contains(targetElement)); }; const getListItemElementByIndex = ({ index, listItemUuids, listGroupUuid }) => { const listItemUuid = listItemUuids[index]; if (!listItemUuid || typeof listGroupUuid !== 'string') { return undefined; } return document.querySelector(`[data-uuid="${listGroupUuid}---${listItemUuid}"]`); }; const focusListItemByIndex = ({ index, listItemUuids, listGroupUuid, updateActiveListItemUuid }) => { const listItemUuid = listItemUuids[index]; const listItemElement = getListItemElementByIndex({ index, listItemUuids, listGroupUuid }); if (!listItemUuid || !listItemElement || typeof updateActiveListItemUuid !== 'function') { return false; } updateActiveListItemUuid(listItemUuid); listItemElement.focus(); return true; }; export const handleHorizontalArrowNavigation = ({ event, currentListItemElement, targetElement, isCurrentListItemTarget }) => { if (event.key !== 'ArrowRight' && event.key !== 'ArrowLeft') { return false; } const focusableLeftElements = getFocusableElements(currentListItemElement, LEFT_ELEMENT_FOCUSABLE_SELECTOR); const focusableRightElements = getFocusableElements(currentListItemElement, RIGHT_ELEMENT_FOCUSABLE_SELECTOR); const focusedLeftElementIndex = getFocusedElementIndex(focusableLeftElements, targetElement); if (focusedLeftElementIndex !== -1) { event.preventDefault(); if (event.key === 'ArrowLeft') { const previousLeftElement = focusableLeftElements[focusedLeftElementIndex - 1]; if (previousLeftElement) { previousLeftElement.focus(); } else { currentListItemElement.focus(); } return true; } const nextLeftElement = focusableLeftElements[focusedLeftElementIndex + 1]; if (nextLeftElement) { nextLeftElement.focus(); } else if (focusableRightElements.length > 0) { focusableRightElements[0]?.focus(); } return true; } const focusedRightElementIndex = getFocusedElementIndex(focusableRightElements, targetElement); if (focusedRightElementIndex !== -1) { event.preventDefault(); if (event.key === 'ArrowRight') { focusableRightElements[focusedRightElementIndex + 1]?.focus(); return true; } const previousRightElement = focusableRightElements[focusedRightElementIndex - 1]; if (previousRightElement) { previousRightElement.focus(); } else if (focusableLeftElements.length > 0) { focusableLeftElements[focusableLeftElements.length - 1]?.focus(); } else { currentListItemElement.focus(); } return true; } if (isCurrentListItemTarget) { if (event.key === 'ArrowLeft' && focusableLeftElements.length > 0) { event.preventDefault(); focusableLeftElements[focusableLeftElements.length - 1]?.focus(); return true; } if (event.key === 'ArrowRight' && focusableRightElements.length > 0) { event.preventDefault(); focusableRightElements[0]?.focus(); return true; } } return false; }; export const handleListItemTabNavigation = ({ event, currentListItemElement, targetElement, isCurrentListItemTarget, isInKeyboardNavigationGroup, listItemUuids, currentUuid, listGroupUuid, updateActiveListItemUuid }) => { if (event.key !== 'Tab' || !isInKeyboardNavigationGroup || !listItemUuids?.length) { return false; } const currentIndex = listItemUuids.indexOf(currentUuid); if (currentIndex === -1) { return false; } const focusableRightElements = getFocusableElements(currentListItemElement, RIGHT_ELEMENT_FOCUSABLE_SELECTOR); const focusedRightElementIndex = getFocusedElementIndex(focusableRightElements, targetElement); if (event.shiftKey) { return false; } if (isCurrentListItemTarget) { if (focusableRightElements.length > 0) { event.preventDefault(); focusableRightElements[0]?.focus(); return true; } return false; } if (focusedRightElementIndex === -1) { return false; } const nextRightElement = focusableRightElements[focusedRightElementIndex + 1]; if (nextRightElement) { event.preventDefault(); nextRightElement.focus(); return true; } if (currentIndex < listItemUuids.length - 1) { event.preventDefault(); return focusListItemByIndex({ index: currentIndex + 1, listItemUuids, listGroupUuid, updateActiveListItemUuid }); } return false; }; export const handleVerticalListGroupNavigation = ({ event, isCurrentListItemTarget, isInKeyboardNavigationGroup, listItemUuids, currentUuid, listGroupUuid, updateActiveListItemUuid }) => { if (!isCurrentListItemTarget || !isInKeyboardNavigationGroup || !listItemUuids?.length || event.key !== 'ArrowDown' && event.key !== 'ArrowUp') { return false; } const currentIndex = listItemUuids.indexOf(currentUuid); if (currentIndex === -1) { return true; } const isBackward = event.key === 'ArrowUp'; event.preventDefault(); const nextIndex = isBackward ? (currentIndex - 1 + listItemUuids.length) % listItemUuids.length : (currentIndex + 1) % listItemUuids.length; const nextListItemUuid = listItemUuids[nextIndex]; if (nextListItemUuid && nextListItemUuid !== currentUuid && typeof listGroupUuid === 'string' && typeof updateActiveListItemUuid === 'function') { const nextListItemElement = document.querySelector(`[data-uuid="${listGroupUuid}---${nextListItemUuid}"]`); if (nextListItemElement) { updateActiveListItemUuid(nextListItemUuid); nextListItemElement.focus(); } } return true; }; export const handleListItemToggleKey = ({ event, isCurrentListItemTarget, isExpandable, isItemOpen, shouldEnableKeyboardHighlighting, toggleItem }) => { if (!isCurrentListItemTarget || !shouldEnableKeyboardHighlighting) { return false; } const shouldOpen = isExpandable && event.key === 'ArrowRight' && !isItemOpen; const shouldClose = isExpandable && event.key === 'ArrowLeft' && isItemOpen; const shouldToggle = event.key === 'Enter' || event.key === ' '; if (!shouldOpen && !shouldClose && !shouldToggle) { return false; } event.preventDefault(); toggleItem(); return true; }; //# sourceMappingURL=List.utils.js.map