UNPKG

@create-figma-plugin/ui

Version:

Production-grade Preact components that replicate the Figma UI design

76 lines 3.33 kB
import { useCallback } from 'preact/hooks'; import { getCurrentFromRef } from '../utilities/get-current-from-ref.js'; export function useScrollableMenu(options) { const { itemIdDataAttributeName, menuElementRef, selectedId, setSelectedId } = options; const getItemElements = useCallback(function () { return Array.from(getCurrentFromRef(menuElementRef).querySelectorAll(`[${itemIdDataAttributeName}]`)).filter(function (element) { return element.hasAttribute('disabled') === false; }); }, [itemIdDataAttributeName, menuElementRef]); const findIndexByItemId = useCallback(function (id) { if (id === null) { return -1; } const index = getItemElements().findIndex(function (element) { return element.getAttribute(itemIdDataAttributeName) === id; }); if (index === -1) { throw new Error('`index` is `-1`'); } return index; }, [getItemElements, itemIdDataAttributeName]); const updateScrollPosition = useCallback(function (id) { const itemElements = getItemElements(); const index = findIndexByItemId(id); const selectedElement = itemElements[index]; const selectedElementOffsetTop = selectedElement.getBoundingClientRect().top; const menuElement = getCurrentFromRef(menuElementRef); const menuElementOffsetTop = menuElement.getBoundingClientRect().top; if (selectedElementOffsetTop < menuElementOffsetTop) { selectedElement.scrollIntoView(); return; } const offsetBottom = selectedElementOffsetTop + selectedElement.offsetHeight; if (offsetBottom > menuElementOffsetTop + menuElement.offsetHeight) { selectedElement.scrollIntoView(); } }, [findIndexByItemId, getItemElements, menuElementRef]); const handleScrollableMenuKeyDown = useCallback(function (event) { const key = event.key; if (key === 'ArrowDown' || key === 'ArrowUp') { const itemElements = getItemElements(); const index = findIndexByItemId(selectedId); let newIndex; if (key === 'ArrowDown') { newIndex = index === -1 || index === itemElements.length - 1 ? 0 : index + 1; } else { newIndex = index === -1 || index === 0 ? itemElements.length - 1 : index - 1; } const selectedElement = itemElements[newIndex]; const newSelectedId = selectedElement.getAttribute(itemIdDataAttributeName); setSelectedId(newSelectedId); updateScrollPosition(newSelectedId); } }, [ getItemElements, findIndexByItemId, itemIdDataAttributeName, setSelectedId, selectedId, updateScrollPosition ]); const handleScrollableMenuItemMouseMove = useCallback(function (event) { const id = event.currentTarget.getAttribute(itemIdDataAttributeName); if (id !== selectedId) { setSelectedId(id); } }, [itemIdDataAttributeName, selectedId, setSelectedId]); return { handleScrollableMenuItemMouseMove, handleScrollableMenuKeyDown }; } //# sourceMappingURL=use-scrollable-menu.js.map