@clayui/shared
Version:
ClayShared component
84 lines (81 loc) • 2.52 kB
JavaScript
/**
* SPDX-FileCopyrightText: © 2019 Liferay, Inc. <https://liferay.com>
* SPDX-License-Identifier: BSD-3-Clause
*/
import React, { Children } from 'react';
import { Keys } from "./Keys.js";
import { useFocusManagement } from "./useFocusManagement.js";
/**
* The context helps to identify if the FocusScope is being declared nested, to
* avoid focus being controlled by more than one focus manager, we stop event
* propagation to prevent the parent focus generator from doing anything.
*/
const FocusConflictContext = /*#__PURE__*/React.createContext(false);
/**
* FocusScope is a component only for controlling focus and listening
* for children's key down events, since the component handles the `onKeyDown`
* event.
*/
export const FocusScope = _ref => {
let {
arrowKeysLeftRight = false,
arrowKeysUpDown = true,
children,
onFocus
} = _ref;
const elRef = React.useRef(null);
const focusManager = useFocusManagement(elRef);
const onKeyDown = event => {
const {
key,
shiftKey
} = event;
if (arrowKeysUpDown && key === Keys.Down || arrowKeysLeftRight && key === Keys.Right || key === Keys.Tab && !shiftKey) {
const next = focusManager.focusNext();
if (next) {
event.preventDefault();
if (onFocus) {
onFocus(next);
}
}
} else if (arrowKeysUpDown && key === Keys.Up || arrowKeysLeftRight && key === Keys.Left || key === Keys.Tab && shiftKey) {
const previous = focusManager.focusPrevious();
if (previous) {
event.preventDefault();
if (onFocus) {
onFocus(previous);
}
}
}
};
const child = typeof children === 'function' ? children(focusManager) : children;
return /*#__PURE__*/React.createElement(FocusConflictContext.Provider, {
value: true
}, /*#__PURE__*/React.cloneElement(child, {
onKeyDown: event => {
event.stopPropagation();
// If the element already exists a `onKeyDown` event should
// invoke it as well.
if (child.props.onKeyDown) {
child.props.onKeyDown(event);
}
onKeyDown(event);
},
ref: r => {
if (r) {
elRef.current = r;
const {
ref
} = child;
if (ref) {
if (typeof ref === 'object') {
// eslint-disable-next-line react-compiler/react-compiler
ref.current = r;
} else if (typeof ref === 'function') {
ref(r);
}
}
}
}
}));
};