UNPKG

@momentum-ui/react-collaboration

Version:

Cisco Momentum UI Framework for React Collaboration Applications

382 lines 21 kB
/** @component list-item */ var __extends = (this && this.__extends) || (function () { var extendStatics = function (d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; return extendStatics(d, b); }; return function (d, b) { if (typeof b !== "function" && b !== null) throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; })(); var __assign = (this && this.__assign) || function () { __assign = Object.assign || function(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; var __rest = (this && this.__rest) || function (s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; } return t; }; 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'; /** * @deprecated - Components in the legacy folder (/src/legacy) are deprecated. Please use a component from the components folder (/src/components) instead. Legacy components may not follow accessibility standards. **/ var ListItem = /** @class */ (function (_super) { __extends(ListItem, _super); function ListItem() { var _this = _super !== null && _super.apply(this, arguments) || this; _this.checkElements = function (tag) { var children = Object.values(ReactDOM.findDOMNode(_this).childNodes); return _this.countDOMChildren(children, tag); }; _this.countDOMChildren = function (children, tag) { return children.reduce(function (agg, child) { return (child.tagName === tag ? __assign(__assign({}, 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 _a = _this.props, disabled = _a.disabled, label = _a.label, onClick = _a.onClick, parentOnSelect = _a.parentOnSelect, value = _a.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 _a = _this.props, disabled = _a.disabled, onKeyDown = _a.onKeyDown, parentKeyDown = _a.parentKeyDown, value = _a.value, label = _a.label, focusLockTabbableChildren = _a.focusLockTabbableChildren, focusLockTabbableChildrenProps = _a.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 _a = _this.props, onBlur = _a.onBlur, focusLockTabbableChildren = _a.focusLockTabbableChildren, focusLockTabbableChildrenProps = _a.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(_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; } ListItem.prototype.componentDidMount = function () { var _a = this.props, focus = _a.focus, refName = _a.refName, focusOnLoad = _a.focusOnLoad; this.verifyStructure(); focusOnLoad && focus && this[refName] && this[refName].focus(); }; ListItem.prototype.verifyStructure = function () { 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 ".concat(checkSectionChildren.length)); } }; ListItem.prototype.render = function () { var _this = this; var _a = this.props, active = _a.active, children = _a.children, className = _a.className, customAnchorNode = _a.customAnchorNode, customRefProp = _a.customRefProp, disabled = _a.disabled, eventKey = _a.eventKey, focus = _a.focus, isReadOnly = _a.isReadOnly, keyboardKey = _a.keyboardKey, label = _a.label, link = _a.link, refName = _a.refName, role = _a.role, separator = _a.separator, specifyRoleWithoutList = _a.specifyRoleWithoutList, title = _a.title, type = _a.type, props = __rest(_a, ["active", "children", "className", "customAnchorNode", "customRefProp", "disabled", "eventKey", "focus", "isReadOnly", "keyboardKey", "label", "link", "refName", "role", "separator", "specifyRoleWithoutList", "title", "type"]); var keyboardNavKey = makeKeyboardKey(keyboardKey || title || label); var otherProps = omit(__assign({}, props), [ 'focusLockTabbableChildren', 'focusLockTabbableChildrenProps', 'focusOnLoad', 'id', 'itemIndex', 'onBlur', 'onClick', 'onKeyDown', 'parentKeyDown', 'parentOnSelect', 'value', ]); var setProps = function (cxtProps) { var _a; var _b; return (__assign(__assign(__assign(__assign(__assign(__assign(__assign(__assign({ className: 'md-list-item' + "".concat((cxtProps.type && " md-list-item--".concat(cxtProps.type)) || '') + "".concat((cxtProps.active && " active") || '') + "".concat((disabled && " disabled") || '') + "".concat((isReadOnly && " md-list-item--read-only") || '') + "".concat((separator && " md-list-item--separator") || '') + "".concat((className && " ".concat(className)) || '') + "".concat((customAnchorNode && customAnchorNode.props.className && " ".concat(customAnchorNode.props.className)) || ''), id: cxtProps.id, role: cxtProps.role }, (!customAnchorNode && __assign({ ref: function (ref) { return (_this[refName] = ref); } }, (link && { href: link })))), (customAnchorNode && customRefProp && (_a = {}, _a[customRefProp] = function (ref) { return (_this[refName] = ref); }, _a))), (!isReadOnly && { onClick: function (e) { return _this.handleClick(e, cxtProps.uniqueKey); }, onKeyDown: function (e) { return _this.handleKeyDown(e, cxtProps.uniqueKey); }, onBlur: function (e) { return _this.handleBlur(e); }, tabIndex: !disabled && cxtProps.focus ? 0 : -1, })), { 'data-md-event-key': cxtProps.uniqueKey }), (!((_b = cxtProps === null || cxtProps === void 0 ? void 0 : cxtProps.ariaConfig) === null || _b === void 0 ? void 0 : _b.disableAriaCurrent) && __assign({}, (cxtProps.focus && { 'aria-current': "".concat(cxtProps.focus) })))), (keyboardNavKey && { 'data-md-keyboard-key': keyboardNavKey })), ((title || label) && { title: title || label })), otherProps)); }; var addRefToAnchor = function (cxtProps) { return React.cloneElement(customAnchorNode, setProps(cxtProps), children || customAnchorNode.props.children || label); }; var createElement = function (cxtProps) { return React.createElement(link ? 'a' : 'div', setProps(cxtProps), children || label); }; return (React.createElement(UIDConsumer, { name: function (id) { return "md-list-item-".concat(id); } }, function (id) { return (React.createElement(ListContext.Consumer, null, function (listContext) { var contextProps = {}; contextProps.id = _this.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); //# sourceMappingURL=index.js.map