UNPKG

@hitachivantara/uikit-react-core

Version:

Core React components for the NEXT Design System.

310 lines (309 loc) 9.14 kB
"use strict"; Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" }); const React = require("react"); const browser = require("../utils/browser.cjs"); const keyboardUtils = require("../utils/keyboardUtils.cjs"); const Focus_styles = require("./Focus.styles.cjs"); const utils = require("./utils.cjs"); const HvFocus = ({ classes: classesProp, children, configuration = {}, disabledClass = false, selected = false, disabled = false, rootRef = void 0, focusOnClick = false, focusDisabled = true, strategy = "listbox", filterClass, navigationJump = 4 }) => { const [childFocus, setChildFocus] = React.useState(); const [hasRunConfig, setHasRunConfig] = React.useState(false); const { classes, cx } = Focus_styles.useClasses(classesProp); const getFocuses = () => { const focuses = rootRef?.current ? Array.from( rootRef.current.getElementsByClassName( filterClass || Focus_styles.staticClasses.root || "root" ) ) : []; return focuses; }; const setTabIndex = (el, tabIndex = 0) => { if (!el) return; const elChildFocus = utils.getFocusableChildren(el)[0]; if (elChildFocus) { el.tabIndex = -1; elChildFocus.tabIndex = tabIndex; } else { el.tabIndex = tabIndex; } }; const setSelectedTabIndex = () => { const focuses = getFocuses(); const firstSelected = focuses.find( (focus) => focus.classList.contains(classes.selected || "selected") ); if (!firstSelected) return; focuses.forEach((focus) => setTabIndex(focus, -1)); setTabIndex(firstSelected, 0); }; const clearTabSiblings = (el) => { getFocuses().forEach((focus) => setTabIndex(focus, -1)); setTabIndex(el, 0); }; const onFocusStrategy = (evt) => { if (strategy === "listbox") { clearTabSiblings(evt.currentTarget); } }; const onBlurStrategy = () => { if (strategy === "listbox" && rootRef && rootRef.current && !rootRef.current.contains(document.activeElement)) { setTimeout(() => { setSelectedTabIndex(); }, 10); } }; const config = (el) => { const { tabIndex } = configuration; if (!el || hasRunConfig) return; if (strategy === "card") { setChildFocus(children); return; } if (strategy === "grid") { return; } const focusableChildren = utils.getFocusableChildren(el); if (focusableChildren.length) { focusableChildren.forEach((child) => setTabIndex(child, -1)); setChildFocus(focusableChildren[0]); } if (tabIndex != null) setTabIndex(el, tabIndex); setHasRunConfig(true); }; const addFocusClass = (evt) => { classes.focused.split(" ").forEach((c) => evt.currentTarget.classList.add(c)); evt.currentTarget.classList.add("HvIsFocused"); classes?.focus?.split(" ").forEach((c) => evt.currentTarget.classList.add(c)); }; const removeFocusClass = () => { getFocuses().forEach((element) => { classes.focused.split(" ").forEach((c) => element.classList.remove(c)); element.classList.remove("HvIsFocused"); classes?.focus?.split(" ").forEach((c) => element.classList.remove(c)); }); }; const onFocus = (evt) => { addFocusClass(evt); childFocus?.focus?.(); onFocusStrategy(evt); }; const onBlur = () => { removeFocusClass(); onBlurStrategy(); }; const onMouseDown = (evt) => { const hasCard = !!evt.currentTarget?.querySelector(".HvIsCardGridElement"); if (strategy === "grid" && hasCard) return; utils.setFocusTo(evt.currentTarget); setTabIndex(evt.currentTarget, 0); if (!focusOnClick) { removeFocusClass(); } }; const focusAndUpdateIndex = (nextFocus, previousFocus, focusesList) => { if (focusesList?.includes(previousFocus)) { setTabIndex(previousFocus, -1); } setTabIndex(nextFocus, 0); utils.setFocusTo(nextFocus); }; const getEnabledKeys = (currentFocusIndex, jump, listSize) => ({ right: (currentFocusIndex + 1) % jump === 0 || currentFocusIndex + 1 > listSize - 1, left: currentFocusIndex % jump === 0, up: currentFocusIndex - jump < 0, down: currentFocusIndex + jump > listSize || currentFocusIndex + jump > listSize - 1 }); const onGridKeyDownHandler = (evt, focuses, focusesList, currentFocusIndex, jump) => { const childFocusIsInput = childFocus && childFocus.nodeName === "INPUT"; if (!keyboardUtils.isOneOfKeys(evt, [ "ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight", "Home", "End", "Space", "Enter" ]) || childFocusIsInput && keyboardUtils.isKey(evt, "Enter")) { return; } if (!keyboardUtils.isOneOfKeys(evt, ["Enter", "Space"])) { evt.preventDefault(); evt.stopPropagation(); } const blockedKeys = getEnabledKeys( currentFocusIndex, jump, focusesList.length ); switch (evt.code) { case "Space": case "Enter": if (browser.isBrowser("firefox")) { evt.target.click(); } else { evt.currentTarget.click(); } break; case "ArrowUp": if (!blockedKeys.up) { focusAndUpdateIndex( focuses.jump || focuses.last, evt.current, focusesList ); } break; case "ArrowDown": if (!blockedKeys.down) { focusAndUpdateIndex( focuses.fall || focuses.first, evt.current, focusesList ); } break; case "ArrowLeft": if (!blockedKeys.left) { focusAndUpdateIndex( focuses.previous || focuses.last, evt.current, focusesList ); } break; case "ArrowRight": if (!blockedKeys.right) { focusAndUpdateIndex( focuses.next || focuses.first, evt.current, focusesList ); } break; case "Home": focusAndUpdateIndex(focuses.first, evt.current, focusesList); break; case "End": focusAndUpdateIndex(focuses.last, evt.current, focusesList); break; } }; const onVerticalArrangementHandler = (evt, focuses, focusesList) => { const childFocusIsInput = childFocus && childFocus.nodeName === "INPUT"; if (!keyboardUtils.isOneOfKeys(evt, [ "ArrowUp", "ArrowDown", "Home", "End", "Space", "Enter" ]) || childFocusIsInput && keyboardUtils.isKey(evt, "Enter")) { return; } evt.preventDefault(); evt.stopPropagation(); switch (evt.code) { case "Space": case "Enter": evt.target.click(); break; case "ArrowUp": focusAndUpdateIndex( focuses.previous || focuses.last, evt.current, focusesList ); break; case "ArrowDown": focusAndUpdateIndex( focuses.next || focuses.first, evt.current, focusesList ); break; case "Home": focusAndUpdateIndex(focuses.first, evt.current, focusesList); break; case "End": focusAndUpdateIndex(focuses.last, evt.current, focusesList); break; } }; const onSingleHandler = (evt) => { const childFocusIsInput = childFocus && childFocus.nodeName === "INPUT"; if (!keyboardUtils.isOneOfKeys(evt, ["Space", "Enter"]) || childFocusIsInput && keyboardUtils.isKey(evt, "Enter")) { return; } evt.preventDefault(); evt.stopPropagation(); evt.currentTarget.click(); }; const onKeyDown = (evt) => { if (rootRef?.current == null) { onSingleHandler(evt); return; } const isDisabledFocusable = strategy === "menu"; const focusesList = getFocuses().filter( (el) => isDisabledFocusable || !el.classList.contains(classes?.disabled) ); const currentFocus = focusesList.indexOf(evt.currentTarget); const focuses = { first: focusesList[0], last: focusesList[focusesList.length - 1], previous: focusesList[currentFocus - 1], next: focusesList[currentFocus + 1], fall: focusesList[currentFocus + navigationJump], jump: focusesList[currentFocus - navigationJump] }; if (strategy === "grid") { onGridKeyDownHandler( evt, focuses, focusesList, currentFocus, navigationJump ); return; } onVerticalArrangementHandler(evt, focuses, focusesList); }; const onKeyUp = (evt) => { if (browser.isBrowser("firefox")) evt.preventDefault(); }; if (disabled) return children; return React.cloneElement(children, { className: cx( [classes.root, filterClass], { [classes.selected]: selected, [classes.disabled]: disabledClass, [classes.focusDisabled]: focusDisabled }, children.props.className ), ref: config, onFocus, onBlur, onMouseDown, onKeyDown, onKeyUp, selected }); }; exports.focusClasses = Focus_styles.staticClasses; exports.HvFocus = HvFocus;