@hitachivantara/uikit-react-core
Version:
Core React components for the NEXT Design System.
310 lines (309 loc) • 9.14 kB
JavaScript
;
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;