@momentum-ui/react-collaboration
Version:
Cisco Momentum UI Framework for React Collaboration Applications
382 lines • 21 kB
JavaScript
/** @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