@momentum-ui/react-collaboration
Version:
Cisco Momentum UI Framework for React Collaboration Applications
422 lines • 19.9 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 PropTypes from 'prop-types';
import omit from 'lodash/omit';
import uniqueId from 'lodash/uniqueId';
import qsa from 'dom-helpers/query/querySelectorAll';
import SelectableContext from '../SelectableContext';
import ListContext from '../ListContext';
/**
* @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 List = /** @class */ (function (_super) {
__extends(List, _super);
function List(props) {
var _this = _super.call(this, props) || this;
_this.determineInitialFocus = function () {
var _a;
var _b = _this.props, focusFirstQuery = _b.focusFirstQuery, shouldFocusInitial = _b.shouldFocusInitial;
var listContext = _this.state.listContext;
var items = qsa(_this.listNode, focusFirstQuery || ".md-list-item:not(.disabled):not(:disabled):not(.md-list-item--read-only)");
var focus = listContext.focus;
if (items.length) {
if (!focus) {
focus = _this.getNextFocusedChild(items, items[0], 0);
}
if (focus && shouldFocusInitial) {
(_a = _this.listNode.querySelector("[data-md-event-key=\"".concat(focus, "\"]"))) === null || _a === void 0 ? void 0 : _a.focus();
}
}
};
_this.getIncludesFirstCharacter = function (str, char) { return str.charAt(0).toLowerCase().includes(char); };
_this.getValue = function (arr, index, attribute) {
return arr[index].attributes["data-md-".concat(attribute, "-key")] &&
arr[index].attributes["data-md-".concat(attribute, "-key")].value;
};
_this.getFocusableItems = function () {
if (!_this.listNode)
return null;
var focusQuery = _this.props.focusQuery;
var defaultItems = qsa(_this.listNode, ".md-list-item:not(.disabled):not(:disabled):not(.md-list-item--read-only)");
var customItems = (focusQuery && qsa(_this.listNode, focusQuery)) || [];
return customItems.length
? customItems.filter(function (item) { return customItems.indexOf(item) >= 0; })
: defaultItems;
};
_this.handleKeyDown = function (e) {
var _a = _this.props, shouldFocusActive = _a.shouldFocusActive, shouldPropagateKeyDown = _a.shouldPropagateKeyDown, navigationDirection = _a.navigationDirection;
var focus = _this.state.listContext.focus;
var clickEvent;
var tgt = e.currentTarget;
var char = e.key;
var items = _this.getFocusableItems();
var length = (items.length && items.length - 1) || 0;
var focusIdx = (focus && items.indexOf(_this.listNode.querySelector("[data-md-event-key=\"".concat(focus, "\"]")))) || 0;
var flag = false;
var isPrintableCharacter = function (str) {
return str.length === 1 && str.match(/\S/);
};
switch (e.which) {
case 9: // TAB
if (shouldFocusActive) {
_this._needsRefocus = false;
_this.setFocusToActive();
}
break;
case 32: // SPACE
case 13: // ENTER
try {
clickEvent = new MouseEvent('click', {
view: window,
bubbles: true,
cancelable: true,
});
}
catch (err) {
if (document.createEvent) {
// DOM Level 3 for IE 9+
clickEvent = document.createEvent('MouseEvents');
clickEvent.initEvent('click', true, true);
}
}
tgt.dispatchEvent(clickEvent);
flag = true;
break;
case 38: // UP
if (navigationDirection === 'both' || navigationDirection === 'vertical') {
_this.getNextFocusedChild(items, tgt, -1);
_this._needsRefocus = true;
if (!shouldPropagateKeyDown)
flag = true;
}
break;
case 37: // LEFT
if (navigationDirection === 'both' || navigationDirection === 'horizontal') {
_this.getNextFocusedChild(items, tgt, -1);
_this._needsRefocus = true;
if (!shouldPropagateKeyDown)
flag = true;
}
break;
case 39: // RIGHT
if (navigationDirection === 'both' || navigationDirection === 'horizontal') {
_this.getNextFocusedChild(items, tgt, 1);
_this._needsRefocus = true;
if (!shouldPropagateKeyDown)
flag = true;
}
break;
case 40: // DOWN
if (navigationDirection === 'both' || navigationDirection === 'vertical') {
_this.getNextFocusedChild(items, tgt, 1);
_this._needsRefocus = true;
if (!shouldPropagateKeyDown)
flag = true;
}
break;
case 33: // PAGE UP
case 36: // HOME
_this.setFocusToLimit('start', items, length);
_this._needsRefocus = true;
flag = true;
break;
case 34: // PAGE DOWN
case 35: // END
_this.setFocusToLimit('end', items, length);
_this._needsRefocus = true;
flag = true;
break;
default:
if (isPrintableCharacter(char)) {
_this.setFocusByFirstCharacter(char, focusIdx, items, length);
_this._needsRefocus = true;
flag = true;
}
break;
}
if (flag) {
e.stopPropagation();
e.preventDefault();
}
};
_this.handleSelect = function (e, opts) {
var _a = _this.props, onSelect = _a.onSelect, trackActive = _a.trackActive;
var active = _this.state.listContext.active;
var eventKey = opts.eventKey, label = opts.label, value = opts.value;
var items = _this.getFocusableItems();
var index = items.findIndex(function (item) {
return item.getAttribute('data-md-event-key') === eventKey ||
item.querySelector("[data-md-event-key=\"".concat(eventKey, "\"]"));
});
// Don't do anything if index is the same or outside of the bounds
if (eventKey === active || index < 0 || index > items.length - 1)
return;
_this.setFocus(items, index);
// Don't do anything if onSelect Event Handler is present
if (onSelect) {
return onSelect(e, {
keyboardKey: _this.getValue(items, index, 'keyboard'),
eventKey: _this.getValue(items, index, 'event'),
label: label,
value: value,
});
}
// Keep reference to last index for event handler
var last = active;
// Call change event handler
trackActive &&
_this.setState(function (state) { return ({
last: last,
listContext: __assign(__assign({}, state.listContext), { active: _this.getValue(items, index, 'event') }),
}); });
};
_this.setFocus = function (items, index) {
_this.setState(function (state) { return ({
listContext: __assign(__assign({}, state.listContext), { focus: _this.getValue(items, index, 'event') }),
}); });
};
_this.setActiveAndFocus = function (active, focus) {
_this._needsRefocus = false;
_this.setState(function (state) { return ({
listContext: __assign(__assign({}, state.listContext), { active: active, focus: (state.shouldFocusActive && active) || focus }),
}); });
};
_this.setFocusByFirstCharacter = function (char, focusIdx, items, length) {
var listContext = _this.state.listContext;
var newIndex = items.reduce(function (agg, item, idx, arr) {
var index = focusIdx + idx + 1 > length ? Math.abs(focusIdx + idx - length) : focusIdx + idx + 1;
return !agg.length &&
_this.getValue(arr, index, 'keyboard') &&
_this.getIncludesFirstCharacter(_this.getValue(arr, index, 'keyboard'), char)
? agg.concat(_this.getValue(arr, index, 'event'))
: agg;
}, []);
typeof newIndex[0] === 'string' &&
listContext.focus !== newIndex[0] &&
_this.setState(function (state) { return ({
listContext: __assign(__assign({}, state.listContext), { focus: newIndex[0] }),
}); });
};
_this.state = {
id: props.id || uniqueId('md-list-'),
last: 0,
listContext: {
active: props.active,
focus: (props.shouldFocusActive && props.active) || props.focus,
role: props.itemRole,
type: props.type,
ariaConfig: props.ariaConfig,
},
selectContext: {
parentKeyDown: _this.handleKeyDown,
parentOnSelect: _this.handleSelect,
},
};
return _this;
}
List.getDerivedStateFromProps = function (_a, state) {
var active = _a.active;
return active
? __assign(__assign({}, state), { listContext: __assign(__assign({}, state.listContext), { active: active }) }) : state;
};
List.prototype.componentDidMount = function () {
var focusFirst = this.props.focusFirst;
focusFirst && this.listNode && this.determineInitialFocus();
};
List.prototype.componentDidUpdate = function (prevProps, prevState) {
var _a;
var listContext = this.state.listContext;
var _b = this.props, active = _b.active, focus = _b.focus, shouldFocusActive = _b.shouldFocusActive;
if (shouldFocusActive && (prevProps.focus !== focus || prevProps.active !== active)) {
this.setActiveAndFocus(active, focus);
}
if (!this._needsRefocus || !this.listNode)
return;
if (listContext.focus && prevState.listContext.focus !== listContext.focus) {
(_a = this.listNode.querySelector("[data-md-event-key=\"".concat(listContext.focus, "\"]"))) === null || _a === void 0 ? void 0 : _a.focus();
}
};
List.prototype.getNextFocusedChild = function (items, current, offset) {
if (!this.listNode)
return null;
var shouldLoop = this.props.shouldLoop;
var listContext = this.state.listContext;
var possibleIndex = items.indexOf(current) + offset;
var getIndex = function () {
if (possibleIndex < 0) {
return shouldLoop ? items.length - 1 : 0;
}
else if (possibleIndex > items.length - 1) {
return shouldLoop ? 0 : items.length - 1;
}
else
return possibleIndex;
};
listContext.focus !== this.getValue(items, getIndex(), 'event') &&
this.setState({
listContext: __assign(__assign({}, listContext), { focus: this.getValue(items, getIndex(), 'event') }),
});
return this.getValue(items, getIndex(), 'event');
};
List.prototype.setFocusToActive = function () {
var focus = this.state.listContext.active;
if (!focus) {
var items = this.getFocusableItems();
focus = this.getValue(items, 0, 'event');
}
this.setState({
listContext: __assign(__assign({}, this.state.listContext), { focus: focus }),
});
};
List.prototype.setFocusToLimit = function (target, items, length) {
var focus = this.state.listContext.focus;
var index = target === 'start' ? 0 : length;
var newFocusKey = this.getValue(items, index, 'event');
newFocusKey !== focus &&
this.setState({
listContext: __assign(__assign({}, this.state.listContext), { focus: newFocusKey }),
});
};
List.prototype.render = function () {
var _this = this;
var _a = this.props, active = _a.active, children = _a.children, className = _a.className, role = _a.role, tabType = _a.tabType, wrap = _a.wrap, props = __rest(_a, ["active", "children", "className", "role", "tabType", "wrap"]);
var _b = this.state, listContext = _b.listContext, selectContext = _b.selectContext;
var otherProps = omit(__assign({}, props), [
'ariaConfig',
'focusFirst',
'focusFirstQuery',
'focusQuery',
'itemRole',
'navigationDirection',
'shouldPropagateKeyDown',
'shouldFocusActive',
'shouldFocusInitial',
'shouldLoop',
'trackActive',
'type',
]);
var getActiveId = function () {
var activeNode = active &&
active.length &&
_this.listNode &&
_this.listNode.querySelector("[data-md-event-key=\"".concat(active[0], "\"]"));
return activeNode && activeNode.id;
};
/* eslint-disable jsx-a11y/aria-activedescendant-has-tabindex */
return (React.createElement(SelectableContext.Provider, { value: selectContext },
React.createElement(ListContext.Provider, { value: listContext },
React.createElement("div", __assign({ className: 'md-list' +
"".concat((tabType && " md-list--".concat(tabType)) || '') +
"".concat((wrap && " md-list--wrap") || '') +
"".concat((className && " ".concat(className)) || ''), role: role, "aria-activedescendant": getActiveId(), ref: function (ref) { return (_this.listNode = ref); } }, otherProps), children))));
/* eslint-enable*/
};
return List;
}(React.Component));
List.propTypes = {
/** @prop Optional active prop to pass active prop to children | null */
active: PropTypes.oneOfType([PropTypes.string, PropTypes.array, PropTypes.number]),
/** @prop Optional parameter for accessibility configuration | null */
ariaConfig: PropTypes.oneOfType([PropTypes.bool, PropTypes.object]),
/** @prop Children nodes to render inside List | null */
children: PropTypes.node,
/** @prop Optional css class string | '' */
className: PropTypes.string,
/** @prop Optional focus prop to pass focus prop to children | null */
focus: PropTypes.string,
/** @prop Sets first List item to have focus within List context | true */
focusFirst: PropTypes.bool,
/** @prop Queries children to find matching item to have focus | '' */
focusFirstQuery: PropTypes.string,
/** @prop Additional elements that can be focused by selector | '' */
focusQuery: PropTypes.string,
/** @prop Optional ID value of List | null */
id: PropTypes.string,
/** @prop Optional tabType prop type to manually set child role | 'listitem' */
itemRole: PropTypes.string,
/** @prop Restricts the traversal of the list with either UP/DOWN, LEFT/RIGHT, or both | 'both' */
navigationDirection: PropTypes.oneOf(['vertical', 'horizontal', 'both']),
/** @prop Callback function invoked by user selecting an interactive item within List | null */
onSelect: PropTypes.func,
/** @prop Disables the stopPropagation() & preventDefault() for ArrowKey Events | false */
shouldPropagateKeyDown: PropTypes.bool,
/** @prop Sets the ARIA role for the Nav, in the context of a TabContainer | 'list' */
role: PropTypes.string,
/** @prop Invokes dom focus method on mount | true */
shouldFocusInitial: PropTypes.bool,
/** @prop Determines if focus should revert to active on list exit | false */
shouldFocusActive: PropTypes.bool,
/** @prop Determines if keyboard navigation should loop through list | true */
shouldLoop: PropTypes.bool,
/** @prop Sets the orientation of the List | 'vertical' */
tabType: PropTypes.oneOf(['vertical', 'horizontal']),
/** @prop Determines if List wrapper should track active | true */
trackActive: PropTypes.bool,
/** @prop Sets List size | null */
type: PropTypes.oneOf(['small', 'large', 'space', 'xlarge']),
/** @prop Optional wrap prop type to wrap items to next row */
wrap: PropTypes.bool,
};
List.defaultProps = {
active: null,
ariaConfig: null,
children: null,
className: '',
id: null,
itemRole: 'listitem',
focus: null,
focusFirst: true,
focusFirstQuery: '',
focusQuery: '',
navigationDirection: 'both',
onSelect: null,
shouldPropagateKeyDown: false,
role: 'list',
shouldFocusActive: false,
shouldFocusInitial: true,
shouldLoop: true,
tabType: 'vertical',
trackActive: true,
type: null,
wrap: false,
};
List.displayName = 'List';
export default List;
//# sourceMappingURL=index.js.map