UNPKG

@carbon/ibm-products

Version:
164 lines (162 loc) 7.83 kB
/** * Copyright IBM Corp. 2020, 2026 * * This source code is licensed under the Apache-2.0 license found in the * LICENSE file in the root directory of this source tree. */ import { blockClass, checkForHoldingKey, focusThisField, focusThisItem, manageTabIndexAndFocus, traverseClockwise, traverseReverse } from "./util.js"; //#region src/components/ConditionBuilder/utils/handleKeyboardEvents.js /** * Copyright IBM Corp. 2024 * * This source code is licensed under the Apache-2.0 license found in the * LICENSE file in the root directory of this source tree. */ const handleKeyDown = (evt, conditionBuilderRef, variant) => { if (!document.activeElement.closest(`.${blockClass}__popover`)?.querySelector("[role=\"dialog\"]")) handleKeyPressForMainContent(evt, conditionBuilderRef, variant); }; const handleKeyDownForPopover = (evt, conditionBuilderRef, popoverRef, closePopover) => { if (excludeKeyPress(evt)) return; handleKeyPressForPopover(evt, popoverRef?.current, conditionBuilderRef, closePopover); }; const excludeKeyPress = (evt) => { return !["Escape"].includes(evt.key) && evt.target.closest(`.${blockClass}__item-date`); }; const getVisibleOptions = (parentContainer) => { return Array.from(parentContainer.querySelectorAll(`[role="option"]`)).filter((el) => !el.hasAttribute("aria-disabled") && !el.hasAttribute("aria-hidden")); }; const handleKeyPressForPopover = (evt, parentContainer, conditionBuilderRef, closePopover) => { const key = evt.key; const isHoldingShiftKey = checkForHoldingKey(evt, "shiftKey"); const isMultiSelect = parentContainer.querySelector("ul")?.dataset.multiSelect; const isOptionInput = parentContainer.querySelectorAll(`[role="option"]`)?.length; let allItems = []; if (key === "Escape") { closePopover?.(); focusThisField(evt, conditionBuilderRef); evt.preventDefault(); evt.stopPropagation(); } if (isOptionInput) switch (key) { case "ArrowUp": evt.preventDefault(); allItems = getVisibleOptions(parentContainer); allItems.forEach((eachElem, index, allElements) => { traverseReverse(eachElem, index, allElements, null, null, conditionBuilderRef); }); if (Array.from(evt.target.closest("ul")?.querySelectorAll("li") ?? []).indexOf(evt.target) === 1) parentContainer.querySelector(`.${blockClass}__popover-content-wrapper`).scrollTop = 0; break; case "ArrowDown": evt.preventDefault(); allItems = getVisibleOptions(parentContainer); allItems.forEach((eachElem, index, allElements) => { traverseClockwise(eachElem, index, allElements, null, null, conditionBuilderRef); }); break; case "Tab": allItems = [...Array.from(parentContainer.querySelectorAll(`.${blockClass}__selectAll-button,[role="searchbox"]`)), getVisibleOptions(parentContainer)?.[0]]; allItems.forEach((eachElem, index, allElements) => { if (isHoldingShiftKey) traverseReverse(eachElem, index, allElements, true, true, conditionBuilderRef); else traverseClockwise(eachElem, index, allElements, true, true, conditionBuilderRef); }); evt.preventDefault(); break; case " ": if (isMultiSelect === "true") { if (document.activeElement.type !== "button") document.activeElement?.click(); evt.preventDefault(); } break; case "Enter": if (document.activeElement.type !== "button") { focusThisField(evt, conditionBuilderRef); document.activeElement?.click(); } break; default: break; } else if (key === "Enter" && !isHoldingShiftKey) { if (document.activeElement.type !== "button") { closePopover?.(); focusThisField(evt, conditionBuilderRef); } } }; const handleKeyPressForMainContent = (evt, conditionBuilderRef, variant) => { switch (evt.key) { case "ArrowRight": evt.preventDefault(); if (variant == "Hierarchical") { let allCellsInRow = Array.from(evt.target.closest("[role=\"row\"]")?.querySelectorAll("[role=\"gridcell\"] button")); if (allCellsInRow.length === 1) { evt.target = evt.target.closest("[role=\"row\"]"); handleRowNavigationHierarchical(evt, conditionBuilderRef, variant); } else if (evt.target.getAttribute("role") == "row") manageTabIndexAndFocus(allCellsInRow[0], conditionBuilderRef); else { let currentItemIndex = allCellsInRow.indexOf(evt.target); if (currentItemIndex < allCellsInRow.length - 1) focusThisItem(allCellsInRow[currentItemIndex + 1], conditionBuilderRef); } } else handleCellNavigation(evt, conditionBuilderRef); break; case "ArrowLeft": evt.preventDefault(); if (variant == "Hierarchical") { if (evt.target.getAttribute("role") !== "row") { let allCellsInRow = Array.from(evt.target.closest("[role=\"row\"]")?.querySelectorAll("[role=\"gridcell\"] button")); let currentItemIndex = allCellsInRow.indexOf(evt.target); if (currentItemIndex > 0) focusThisItem(allCellsInRow[currentItemIndex - 1], conditionBuilderRef); else manageTabIndexAndFocus(evt.target.closest(`[role="row"]`), conditionBuilderRef); } } else handleCellNavigation(evt, conditionBuilderRef); break; case "ArrowUp": case "ArrowDown": evt.preventDefault(); if (variant == "Hierarchical") handleRowNavigationHierarchical(evt, conditionBuilderRef, variant); else handleRowNavigation(evt, conditionBuilderRef, variant); break; case "Enter": if (document.activeElement?.querySelectorAll(`button`)?.length === 1) document.activeElement?.querySelectorAll(`button`)[0]?.click(); break; default: break; } }; const getRows = (conditionBuilderRef) => { return Array.from(conditionBuilderRef.current?.querySelectorAll("[role=\"row\"]")); }; const getRowIndex = (element, conditionBuilderRef) => { return getRows(conditionBuilderRef).findIndex((row) => row.contains(element)); }; const handleRowNavigation = (evt, conditionBuilderRef, variant) => { const rows = getRows(conditionBuilderRef); navigateToNextRowCell(evt, getRowIndex(evt.target, conditionBuilderRef), rows, variant, conditionBuilderRef); }; const handleRowNavigationHierarchical = (evt, conditionBuilderRef, variant) => { const rows = getRows(conditionBuilderRef); const currentRowIndex = getRowIndex(evt.target, conditionBuilderRef); let nextRowIndex = currentRowIndex; if (evt.target.getAttribute("role") == "row") { if (["ArrowDown", "ArrowRight"].includes(evt.key)) nextRowIndex += 1; else if (evt.key === "ArrowUp") nextRowIndex -= 1; if (nextRowIndex < 0) nextRowIndex = 0; else if (nextRowIndex >= rows.length) nextRowIndex = rows.length - 1; if (nextRowIndex !== currentRowIndex) manageTabIndexAndFocus(rows[nextRowIndex], conditionBuilderRef); } else navigateToNextRowCell(evt, currentRowIndex, rows, variant, conditionBuilderRef); }; const navigateToNextRowCell = (evt, currentRowIndex, rows, variant, conditionBuilderRef) => { let nextRowIndex = currentRowIndex; if (evt.key === "ArrowUp") nextRowIndex = currentRowIndex == 0 ? currentRowIndex : currentRowIndex - 1; if (evt.key === "ArrowDown") nextRowIndex = currentRowIndex === rows.length - 1 ? rows.length - 1 : currentRowIndex + 1; const nextRow = rows[nextRowIndex]; const itemName = evt.target.dataset.name; if (nextRow?.querySelector(`[data-name="${itemName}"]`)) manageTabIndexAndFocus(nextRow?.querySelector(`[data-name="${itemName}"]`), conditionBuilderRef); else if (variant === "Hierarchical") manageTabIndexAndFocus(nextRow, conditionBuilderRef); }; const handleCellNavigation = (evt, conditionBuilderRef) => { conditionBuilderRef.current.querySelectorAll(`[role="gridcell"] button`).forEach((eachElem, index, allElements) => { if (evt.key === "ArrowRight") traverseClockwise(eachElem, index, allElements, null, null, conditionBuilderRef); else traverseReverse(eachElem, index, allElements, null, null, conditionBuilderRef); }); }; //#endregion export { handleKeyDown, handleKeyDownForPopover };