@patternfly/react-core
Version:
This library provides a set of common React components for use with the PatternFly reference implementation.
133 lines • 7.12 kB
JavaScript
import { __rest } from "tslib";
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import styles from '@patternfly/react-styles/css/components/Dropdown/dropdown';
import { css } from '@patternfly/react-styles';
import { keyHandler, formatBreakpointMods } from '../../helpers/util';
import { DropdownPosition, DropdownArrowContext, DropdownContext } from './dropdownConstants';
export class DropdownMenu extends React.Component {
constructor() {
super(...arguments);
this.refsCollection = [];
this.componentWillUnmount = () => {
document.removeEventListener('keydown', this.onKeyDown);
};
this.onKeyDown = (event) => {
if (!this.props.isOpen ||
!Array.from(document.activeElement.classList).find(className => DropdownMenu.validToggleClasses.concat(this.context.toggleClass).includes(className))) {
return;
}
const refs = this.refsCollection;
if (event.key === 'ArrowDown') {
const firstFocusTargetCollection = refs.find(ref => ref && ref[0] && !ref[0].hasAttribute('disabled'));
DropdownMenu.focusFirstRef(firstFocusTargetCollection);
event.stopPropagation();
}
else if (event.key === 'ArrowUp') {
const collectionLength = refs.length;
const lastFocusTargetCollection = refs.slice(collectionLength - 1, collectionLength);
const lastFocusTarget = lastFocusTargetCollection && lastFocusTargetCollection[0];
DropdownMenu.focusFirstRef(lastFocusTarget);
event.stopPropagation();
}
};
this.childKeyHandler = (index, innerIndex, position, custom = false) => {
keyHandler(index, innerIndex, position, this.refsCollection, this.props.isGrouped ? this.refsCollection : React.Children.toArray(this.props.children), custom);
};
this.sendRef = (index, nodes, isDisabled, isSeparator) => {
this.refsCollection[index] = [];
nodes.map((node, innerIndex) => {
if (!node) {
this.refsCollection[index][innerIndex] = null;
}
else if (!node.getAttribute) {
// eslint-disable-next-line react/no-find-dom-node
this.refsCollection[index][innerIndex] = ReactDOM.findDOMNode(node);
}
else if (isSeparator) {
this.refsCollection[index][innerIndex] = null;
}
else {
this.refsCollection[index][innerIndex] = node;
}
});
};
}
componentDidMount() {
document.addEventListener('keydown', this.onKeyDown);
const { autoFocus } = this.props;
if (autoFocus) {
// Focus first non-disabled element
const focusTargetCollection = this.refsCollection.find(ref => ref && ref[0] && !ref[0].hasAttribute('disabled'));
const focusTarget = focusTargetCollection && focusTargetCollection[0];
if (focusTarget && focusTarget.focus) {
setTimeout(() => focusTarget.focus());
}
}
}
shouldComponentUpdate() {
// reset refsCollection before updating to account for child removal between mounts
this.refsCollection = [];
return true;
}
extendChildren() {
const { children, isGrouped } = this.props;
if (isGrouped) {
let index = 0;
return React.Children.map(children, groupedChildren => {
const group = groupedChildren;
const props = {};
if (group.props && group.props.children) {
if (Array.isArray(group.props.children)) {
props.children = React.Children.map(group.props.children, option => React.cloneElement(option, {
index: index++
}));
}
else {
props.children = React.cloneElement(group.props.children, {
index: index++
});
}
}
return React.cloneElement(group, props);
});
}
return React.Children.map(children, (child, index) => React.cloneElement(child, {
index
}));
}
render() {
const _a = this.props, { className, isOpen, position, children, component, isGrouped, setMenuComponentRef,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
openedOnEnter, alignments } = _a, props = __rest(_a, ["className", "isOpen", "position", "children", "component", "isGrouped", "setMenuComponentRef", "openedOnEnter", "alignments"]);
return (React.createElement(DropdownArrowContext.Provider, { value: {
keyHandler: this.childKeyHandler,
sendRef: this.sendRef
} }, component === 'div' ? (React.createElement(DropdownContext.Consumer, null, ({ onSelect, menuClass }) => (React.createElement("div", { className: css(menuClass, position === DropdownPosition.right && styles.modifiers.alignRight, formatBreakpointMods(alignments, styles, 'align-'), className), hidden: !isOpen, onClick: event => onSelect && onSelect(event), ref: setMenuComponentRef }, children)))) : ((isGrouped && (React.createElement(DropdownContext.Consumer, null, ({ menuClass, menuComponent }) => {
const MenuComponent = (menuComponent || 'div');
return (React.createElement(MenuComponent, Object.assign({}, props, { className: css(menuClass, position === DropdownPosition.right && styles.modifiers.alignRight, formatBreakpointMods(alignments, styles, 'align-'), className), hidden: !isOpen, role: "menu", ref: setMenuComponentRef }), this.extendChildren()));
}))) || (React.createElement(DropdownContext.Consumer, null, ({ menuClass, menuComponent }) => {
const MenuComponent = (menuComponent || component);
return (React.createElement(MenuComponent, Object.assign({}, props, { className: css(menuClass, position === DropdownPosition.right && styles.modifiers.alignRight, formatBreakpointMods(alignments, styles, 'align-'), className), hidden: !isOpen, role: "menu", ref: setMenuComponentRef }), this.extendChildren()));
})))));
}
}
DropdownMenu.displayName = 'DropdownMenu';
DropdownMenu.defaultProps = {
className: '',
isOpen: true,
openedOnEnter: false,
autoFocus: true,
position: DropdownPosition.left,
component: 'ul',
isGrouped: false,
setMenuComponentRef: null
};
DropdownMenu.validToggleClasses = [styles.dropdownToggle, styles.dropdownToggleButton];
DropdownMenu.focusFirstRef = (refCollection) => {
if (refCollection && refCollection[0] && refCollection[0].focus) {
setTimeout(() => refCollection[0].focus());
}
};
DropdownMenu.contextType = DropdownContext;
//# sourceMappingURL=DropdownMenu.js.map