UNPKG

@momentum-ui/react

Version:

Cisco Momentum UI framework for ReactJs applications

472 lines (393 loc) 19.4 kB
var _excluded = ["active", "children", "className", "customAnchorNode", "customRefProp", "disabled", "eventKey", "focus", "isReadOnly", "keyboardKey", "label", "link", "refName", "role", "separator", "specifyRoleWithoutList", "title", "type"]; function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; } function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); } function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } function _inheritsLoose(subClass, superClass) { subClass.prototype = Object.create(superClass.prototype); subClass.prototype.constructor = subClass; _setPrototypeOf(subClass, superClass); } function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } /** @component list-item */ import React from 'react'; import ReactDOM from 'react-dom'; import PropTypes from 'prop-types'; import omit from 'lodash/omit'; import { UIDConsumer } from 'react-uid'; import SelectableContext, { makeKeyboardKey } from "../SelectableContext"; import ListContext from "../ListContext"; import mapContextToProps from '@restart/context/mapContextToProps'; var ListItem = /*#__PURE__*/function (_React$Component) { _inheritsLoose(ListItem, _React$Component); function ListItem() { var _this; for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { args[_key] = arguments[_key]; } _this = _React$Component.call.apply(_React$Component, [this].concat(args)) || this; _this.checkElements = function (tag) { var children = Object.values(ReactDOM.findDOMNode(_assertThisInitialized(_this)).childNodes); return _this.countDOMChildren(children, tag); }; _this.countDOMChildren = function (children, tag) { return children.reduce(function (agg, child) { return child.tagName === tag ? _extends({}, agg, { count: agg.count += 1 }) : agg; }, { count: 0, children: children.length }); }; _this.getChildrenElements = function (nameArr) { var children = _this.props.children; var elementCount = 0; React.Children.forEach(children, function (child) { if (child && child.type && nameArr.includes(child.type.displayName)) { return elementCount++; } }); return elementCount && { length: elementCount }; }; _this.handleClick = function (e, eventKey) { var _this$props = _this.props, disabled = _this$props.disabled, label = _this$props.label, onClick = _this$props.onClick, parentOnSelect = _this$props.parentOnSelect, value = _this$props.value; if (disabled) { e.preventDefault(); e.stopPropagation(); } e.persist(); onClick && onClick(e); parentOnSelect && parentOnSelect(e, { value: value, label: label, eventKey: eventKey }); }; _this.changeTabIndex = function (tabbableChildren, index) { for (var i = 0; i < tabbableChildren.length; i++) { if (tabbableChildren[i].tabIndex === (index === 0 ? -1 : 0)) { tabbableChildren[i].tabIndex = index; } } }; _this.handleKeyDown = function (e, eventKey) { var _this$props2 = _this.props, disabled = _this$props2.disabled, onKeyDown = _this$props2.onKeyDown, parentKeyDown = _this$props2.parentKeyDown, value = _this$props2.value, label = _this$props2.label, focusLockTabbableChildren = _this$props2.focusLockTabbableChildren, focusLockTabbableChildrenProps = _this$props2.focusLockTabbableChildrenProps; if (disabled) { e.preventDefault(); e.stopPropagation(); } var tabbableChildrenQuery = focusLockTabbableChildrenProps.tabbableChildrenQuery; if (focusLockTabbableChildren && e.target) { var currListItem = e.target.closest('.md-list-item'); if (currListItem) { var tabbableChildren = currListItem.querySelectorAll(tabbableChildrenQuery); if (tabbableChildren.length) { if (e.keyCode === 9 && !e.shiftKey) { // TAB only // only allow focus of tabbable children if TAB on the current listitem if (e.target.classList.contains('md-list-item')) { _this.changeTabIndex(tabbableChildren, 0); } } else if (e.keyCode === 9 && e.shiftKey) { // SHIFT + TAB // If we are on one of the tabbable children if (e.target === tabbableChildren[0]) { e.preventDefault(); e.stopPropagation(); // focus on the tabbable children's associated lisitem e.target.closest('.md-list-item').focus(); // If we are on a listitem, SHIFT + TAB exits the list // so we change tabindex of children to -1 } else if (e.target.classList.contains('md-list-item')) { _this.changeTabIndex(tabbableChildren, -1); } } else if (e.keyCode === 38 || e.keyCode == 40) { // UP/DOWN guarantees change of listitem, so we change tabindex of children to -1 _this.changeTabIndex(tabbableChildren, -1); } } } } e.persist(); onKeyDown && onKeyDown(e); parentKeyDown && parentKeyDown(e, { value: value, label: label, eventKey: eventKey }); }; _this.handleBlur = function (e) { var _this$props3 = _this.props, onBlur = _this$props3.onBlur, focusLockTabbableChildren = _this$props3.focusLockTabbableChildren, focusLockTabbableChildrenProps = _this$props3.focusLockTabbableChildrenProps; var tabbableChildrenQuery = focusLockTabbableChildrenProps.tabbableChildrenQuery, portalNodeQuery = focusLockTabbableChildrenProps.portalNodeQuery, tabbableChildrenHasPopover = focusLockTabbableChildrenProps.tabbableChildrenHasPopover; // For when you click or navigate away from the current listitem // Cleans up tabindex="0" before you navigate away if (focusLockTabbableChildren) { if (e.target && e.relatedTarget) { var isInThisList = e.relatedTarget.closest(portalNodeQuery); // The elt getting focus is in the current List if not undefined var listItemNode = ReactDOM.findDOMNode(_assertThisInitialized(_this)); var tabbableChildren = listItemNode.querySelectorAll(tabbableChildrenQuery); if (tabbableChildren.length) { if (isInThisList) { var relatedTargetListItem = e.relatedTarget.closest('.md-list-item'); // The new focus is a ListItem if not undefined var targetListItem = e.target.closest('.md-list-item'); // The current focus is a ListItem if not undefined if (tabbableChildrenHasPopover && focusLockTabbableChildrenProps.tabbableChildSpawnedPopoverQuery) { // If the tabbable children in this ListItem has Popovers var tabbableChildSpawnedPopoverQuery = focusLockTabbableChildrenProps.tabbableChildSpawnedPopoverQuery; var targetIsSpawnedPopover = e.target.closest(tabbableChildSpawnedPopoverQuery); // The current focus is a EventOverlay if not undefined var relatedTargetIsSpawnedPopover = e.relatedTarget.closest(tabbableChildSpawnedPopoverQuery); // The new focus is a EventOverlay if not undefined // from this ListItem or a EventOverlay spawned by one of the tabbable children in this ListItem if (targetListItem === listItemNode || targetIsSpawnedPopover) { // If the new focus is not the same as this ListItem or not a spawned EventOverlay, we left the current ListItem // Make tabindex="-1" if (!relatedTargetIsSpawnedPopover && relatedTargetListItem !== listItemNode) { _this.changeTabIndex(tabbableChildren, -1); } } } else { // If the tabbable children in this ListItem has no Popovers // If the new focus is not the same as this ListItem or the current focus is not the same as this ListItem, we left the current ListItem // Make tabindex="-1" if (targetListItem !== listItemNode && relatedTargetListItem !== listItemNode) { _this.changeTabIndex(tabbableChildren, -1); } } } else { _this.changeTabIndex(tabbableChildren, -1); } } } } onBlur && onBlur(e); }; return _this; } var _proto = ListItem.prototype; _proto.componentDidMount = function componentDidMount() { var _this$props4 = this.props, focus = _this$props4.focus, refName = _this$props4.refName, focusOnLoad = _this$props4.focusOnLoad; this.verifyStructure(); focusOnLoad && focus && this[refName] && this[refName].focus(); }; _proto.verifyStructure = function verifyStructure() { if (!this.props.children) return; var anchorCount = this.checkElements('A'); var checkAllChildren = this.getChildrenElements(['ListItemSection', 'EventOverlay']); var checkSectionChildren = this.getChildrenElements(['ListItemSection']); if (anchorCount.count > 1) { throw new Error('Only 1 primary child anchor tag may be used with ListItem component'); } else if (anchorCount.count === 1 && anchorCount.children > 1) { throw new Error('Anchor tag can not have sibling'); } if (!checkAllChildren) { return; } else if (checkSectionChildren.length > 3) { throw new Error("Only 3 ListItemSection components can be used as children. You've used " + checkSectionChildren.length); } }; _proto.render = function render() { var _this2 = this; var _this$props5 = this.props, active = _this$props5.active, children = _this$props5.children, className = _this$props5.className, customAnchorNode = _this$props5.customAnchorNode, customRefProp = _this$props5.customRefProp, disabled = _this$props5.disabled, eventKey = _this$props5.eventKey, focus = _this$props5.focus, isReadOnly = _this$props5.isReadOnly, keyboardKey = _this$props5.keyboardKey, label = _this$props5.label, link = _this$props5.link, refName = _this$props5.refName, role = _this$props5.role, separator = _this$props5.separator, specifyRoleWithoutList = _this$props5.specifyRoleWithoutList, title = _this$props5.title, type = _this$props5.type, props = _objectWithoutPropertiesLoose(_this$props5, _excluded); var keyboardNavKey = makeKeyboardKey(keyboardKey || title || label); var otherProps = omit(_extends({}, props), ['focusLockTabbableChildren', 'focusLockTabbableChildrenProps', 'focusOnLoad', 'id', 'itemIndex', 'onBlur', 'onClick', 'onKeyDown', 'parentKeyDown', 'parentOnSelect', 'value']); var setProps = function setProps(cxtProps) { var _ref2, _cxtProps$ariaConfig; return _extends({ className: 'md-list-item' + ("" + (cxtProps.type && " md-list-item--" + cxtProps.type || '')) + ("" + (cxtProps.active && " active" || '')) + ("" + (disabled && " disabled" || '')) + ("" + (isReadOnly && " md-list-item--read-only" || '')) + ("" + (separator && " md-list-item--separator" || '')) + ("" + (className && " " + className || '')) + ("" + (customAnchorNode && customAnchorNode.props.className && " " + customAnchorNode.props.className || '')), id: cxtProps.id, role: cxtProps.role }, !customAnchorNode && _extends({ ref: function ref(_ref) { return _this2[refName] = _ref; } }, link && { href: link }), customAnchorNode && customRefProp && (_ref2 = {}, _ref2[customRefProp] = function (ref) { return _this2[refName] = ref; }, _ref2), !isReadOnly && { onClick: function onClick(e) { return _this2.handleClick(e, cxtProps.uniqueKey); }, onKeyDown: function onKeyDown(e) { return _this2.handleKeyDown(e, cxtProps.uniqueKey); }, onBlur: function onBlur(e) { return _this2.handleBlur(e); }, tabIndex: !disabled && cxtProps.focus ? 0 : -1 }, { 'data-md-event-key': cxtProps.uniqueKey }, !(cxtProps !== null && cxtProps !== void 0 && (_cxtProps$ariaConfig = cxtProps.ariaConfig) !== null && _cxtProps$ariaConfig !== void 0 && _cxtProps$ariaConfig.disableAriaCurrent) && _extends({}, cxtProps.focus && { 'aria-current': "" + cxtProps.focus }), keyboardNavKey && { 'data-md-keyboard-key': keyboardNavKey }, (title || label) && { title: title || label }, otherProps); }; var addRefToAnchor = function addRefToAnchor(cxtProps) { return /*#__PURE__*/React.cloneElement(customAnchorNode, setProps(cxtProps), children || customAnchorNode.props.children || label); }; var createElement = function createElement(cxtProps) { return /*#__PURE__*/React.createElement(link ? 'a' : 'div', setProps(cxtProps), children || label); }; return /*#__PURE__*/React.createElement(UIDConsumer, { name: function name(id) { return "md-list-item-" + id; } }, function (id) { return /*#__PURE__*/React.createElement(ListContext.Consumer, null, function (listContext) { var contextProps = {}; contextProps.id = _this2.props.id || id; contextProps.uniqueKey = eventKey || contextProps.id; contextProps.type = type || listContext && listContext.type; contextProps.focus = focus || listContext && listContext.focus === contextProps.uniqueKey; contextProps.active = active || listContext && listContext.active === contextProps.uniqueKey; contextProps.role = specifyRoleWithoutList ? role : listContext && listContext.role || role; contextProps.ariaConfig = listContext && listContext.ariaConfig; return customAnchorNode ? addRefToAnchor(contextProps) : createElement(contextProps); }); }); }; return ListItem; }(React.Component); ListItem.propTypes = { /** @prop Active prop to help determine styles | false */ active: PropTypes.bool, /** @prop Children nodes to render inside ListItem | null */ children: PropTypes.node, /** @prop Optional css class string | '' */ className: PropTypes.string, /** @prop Node in which the selection begins | null */ customAnchorNode: PropTypes.element, /** @prop ListItem Custom Prop Name for child with custom Ref | null */ customRefProp: PropTypes.string, /** @prop Disabled attribute for ListItem to determine styles | false */ disabled: PropTypes.bool, /** @prop Unique string used for tracking events among ancestors | '' */ eventKey: PropTypes.string, /** @prop Specifies if ListItem should automatically get focus | false */ focus: PropTypes.bool, /** @prop Locks focus to cycle between all tabbable children | false */ focusLockTabbableChildren: PropTypes.bool, focusLockTabbableChildrenProps: PropTypes.shape({ /** @prop Query for focusLockTabbableChildren | '' */ tabbableChildrenQuery: PropTypes.string.isRequired, /** @prop Indicates whether this ListItem has tabbable children that spawn Popovers | false */ tabbableChildrenHasPopover: PropTypes.bool.isRequired, /** @prop Only for when using tabbableChildrenHasPopover. Need to checkout the EventOverlay for blur purposes | '' */ portalNodeQuery: PropTypes.string.isRequired, /** @prop Used for tabbableChildrenHasPopover to find the DOM element of Popovers */ tabbableChildSpawnedPopoverQuery: PropTypes.string }), /** @prop Specifies if ListItem should automatically get focus when page loads | false */ focusOnLoad: PropTypes.bool, /** @prop Sets ListItem id | null */ id: PropTypes.string, /** @prop Determines if ListItem is clickable | false */ isReadOnly: PropTypes.bool, /** @prop ListItem index number | null */ itemIndex: PropTypes.number, /** @prop Unique string used for keyboard navigation | '' */ keyboardKey: PropTypes.string, /** @prop ListItem label text | '' */ label: PropTypes.string, /** @prop external link associated input | '' */ link: PropTypes.string, /** @prop Callback function invoked by user changing focus from current ListItem ListItem | null */ onBlur: PropTypes.func, /** @prop Callback function invoked by user tapping on ListItem | null */ onClick: PropTypes.func, /** @prop Callback function invoked by user pressing on a key | null */ onKeyDown: PropTypes.func, // Internal Context Use Only parentKeyDown: PropTypes.func, // Internal Context Use Only parentOnSelect: PropTypes.func, /** @prop ListItem ref name | 'navLink' */ refName: PropTypes.string, /** @prop Aria role | 'listitem' */ role: PropTypes.string, /** @prop Prop that controls whether to show separator or not | false */ separator: PropTypes.bool, /** @prop Prop that enables assigning role per listitem instead of the default provided by List context */ /** Allows Lists to have listitem with different roles */ specifyRoleWithoutList: PropTypes.bool, /** @prop ListItem Title | '' */ title: PropTypes.string, /** @prop ListItem size | '' */ type: PropTypes.oneOf(['', 'small', 'large', 'xlarge', 'space', 'header', 36, 52, 60]), /** @prop ListItem value for OnSelect value | '' */ value: PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.object, PropTypes.array]) }; ListItem.defaultProps = { active: false, children: null, className: '', customAnchorNode: null, customRefProp: '', disabled: false, eventKey: '', focus: false, focusLockTabbableChildren: false, focusLockTabbableChildrenProps: { tabbableChildrenQuery: '', tabbableChildrenHasPopover: false, portalNodeQuery: '' }, focusOnLoad: false, id: null, itemIndex: null, isReadOnly: false, keyboardKey: '', label: '', link: '', onBlur: null, onClick: null, onKeyDown: null, parentKeyDown: null, parentOnSelect: null, refName: 'navLink', role: 'listitem', separator: false, specifyRoleWithoutList: false, title: '', type: '', value: '' }; ListItem.displayName = 'ListItem'; export default mapContextToProps(SelectableContext, function (context) { return context; }, ListItem);