react-widgets
Version:
An à la carte set of polished, extensible, and accessible inputs built for React
281 lines (211 loc) • 8.38 kB
JavaScript
'use strict';
var babelHelpers = require('./util/babelHelpers.js');
exports.__esModule = true;
var _react = require('react');
var _react2 = babelHelpers.interopRequireDefault(_react);
var _ListOption = require('./ListOption');
var _ListOption2 = babelHelpers.interopRequireDefault(_ListOption);
var _utilPropTypes = require('./util/propTypes');
var _utilPropTypes2 = babelHelpers.interopRequireDefault(_utilPropTypes);
var _utilCompat = require('./util/compat');
var _utilCompat2 = babelHelpers.interopRequireDefault(_utilCompat);
var _classnames = require('classnames');
var _classnames2 = babelHelpers.interopRequireDefault(_classnames);
var _util_ = require('./util/_');
var _util_2 = babelHelpers.interopRequireDefault(_util_);
var _warning = require('warning');
var _warning2 = babelHelpers.interopRequireDefault(_warning);
var _utilDataHelpers = require('./util/dataHelpers');
var _utilWidgetHelpers = require('./util/widgetHelpers');
var _utilInteraction = require('./util/interaction');
var optionId = function optionId(id, idx) {
return id + '__option__' + idx;
};
exports['default'] = _react2['default'].createClass({
displayName: 'List',
mixins: [require('./mixins/ListMovementMixin'), require('./mixins/AriaDescendantMixin')()],
propTypes: {
data: _react2['default'].PropTypes.array,
onSelect: _react2['default'].PropTypes.func,
onMove: _react2['default'].PropTypes.func,
optionComponent: _utilPropTypes2['default'].elementType,
itemComponent: _utilPropTypes2['default'].elementType,
groupComponent: _utilPropTypes2['default'].elementType,
selected: _react2['default'].PropTypes.any,
focused: _react2['default'].PropTypes.any,
valueField: _utilPropTypes2['default'].accessor,
textField: _utilPropTypes2['default'].accessor,
disabled: _utilPropTypes2['default'].disabled.acceptsArray,
readOnly: _utilPropTypes2['default'].readOnly.acceptsArray,
groupBy: _utilPropTypes2['default'].accessor,
messages: _react2['default'].PropTypes.shape({
emptyList: _utilPropTypes2['default'].message
})
},
getDefaultProps: function getDefaultProps() {
return {
onSelect: function onSelect() {},
data: [],
optionComponent: _ListOption2['default'],
ariaActiveDescendantKey: 'groupedList',
messages: {
emptyList: 'There are no items in this list'
}
};
},
getInitialState: function getInitialState() {
var keys = [];
return {
groups: this._group(this.props.groupBy, this.props.data, keys),
sortedKeys: keys
};
},
componentWillReceiveProps: function componentWillReceiveProps(nextProps) {
var keys = [];
if (nextProps.data !== this.props.data || nextProps.groupBy !== this.props.groupBy) this.setState({
groups: this._group(nextProps.groupBy, nextProps.data, keys),
sortedKeys: keys
});
},
componentDidMount: function componentDidMount() {
this.move();
},
componentDidUpdate: function componentDidUpdate() {
this.ariaActiveDescendant(this._currentActiveID);
this.move();
},
render: function render() {
var _this = this;
var _props = this.props;
var className = _props.className;
var role = _props.role;
var data = _props.data;
var messages = _props.messages;
var onSelect = _props.onSelect;
var selectedIndex = _props.selectedIndex;
var props = babelHelpers.objectWithoutProperties(_props, ['className', 'role', 'data', 'messages', 'onSelect', 'selectedIndex']);
var id = _utilWidgetHelpers.instanceId(this);var _state = this.state;
var sortedKeys = _state.sortedKeys;
var groups = _state.groups;
var items = [],
idx = -1,
group = undefined;
this._currentActiveID = null;
if (data.length) {
items = sortedKeys.reduce(function (items, key) {
group = groups[key];
items.push(_this._renderGroupHeader(key));
for (var itemIdx = 0; itemIdx < group.length; itemIdx++) items.push(_this._renderItem(key, group[itemIdx], ++idx));
return items;
}, []);
} else items = _react2['default'].createElement(
'li',
{ className: 'rw-list-empty' },
_util_2['default'].result(messages.emptyList, this.props)
);
return _react2['default'].createElement(
'ul',
babelHelpers._extends({
ref: 'scrollable',
id: id,
tabIndex: '-1',
className: _classnames2['default'](className, 'rw-list', 'rw-list-grouped'),
role: role === undefined ? 'listbox' : role
}, props),
items
);
},
_renderGroupHeader: function _renderGroupHeader(group) {
var GroupComponent = this.props.groupComponent,
id = _utilWidgetHelpers.instanceId(this);
return _react2['default'].createElement(
'li',
{
key: 'item_' + group,
tabIndex: '-1',
role: 'separator',
id: id + '_group_' + group,
className: 'rw-list-optgroup'
},
GroupComponent ? _react2['default'].createElement(GroupComponent, { item: group }) : group
);
},
_renderItem: function _renderItem(group, item, idx) {
var _props2 = this.props;
var focused = _props2.focused;
var selected = _props2.selected;
var onSelect = _props2.onSelect;
var textField = _props2.textField;
var valueField = _props2.valueField;
var ItemComponent = _props2.itemComponent;
var Option = _props2.optionComponent;
var currentID = optionId(_utilWidgetHelpers.instanceId(this), idx),
isDisabled = _utilInteraction.isDisabledItem(item, this.props),
isReadOnly = _utilInteraction.isReadOnlyItem(item, this.props);
if (focused === item) this._currentActiveID = currentID;
return _react2['default'].createElement(
Option,
{
key: 'item_' + group + '_' + idx,
id: currentID,
dataItem: item,
focused: focused === item,
selected: selected === item,
disabled: isDisabled,
readOnly: isReadOnly,
onClick: isDisabled || isReadOnly ? undefined : onSelect.bind(null, item)
},
ItemComponent ? _react2['default'].createElement(ItemComponent, {
item: item,
value: _utilDataHelpers.dataValue(item, valueField),
text: _utilDataHelpers.dataText(item, textField),
disabled: isDisabled,
readOnly: isReadOnly
}) : _utilDataHelpers.dataText(item, textField)
);
},
_isIndexOf: function _isIndexOf(idx, item) {
return this.props.data[idx] === item;
},
_group: function _group(groupBy, data, keys) {
var iter = typeof groupBy === 'function' ? groupBy : function (item) {
return item[groupBy];
};
// the keys array ensures that groups are rendered in the order they came in
// which means that if you sort the data array it will render sorted,
// so long as you also sorted by group
keys = keys || [];
_warning2['default'](typeof groupBy !== 'string' || !data.length || _util_2['default'].has(data[0], groupBy), '[React Widgets] You are seem to be trying to group this list by a ' + ('property `' + groupBy + '` that doesn\'t exist in the dataset items, this may be a typo'));
return data.reduce(function (grps, item) {
var group = iter(item);
_util_2['default'].has(grps, group) ? grps[group].push(item) : (keys.push(group), grps[group] = [item]);
return grps;
}, {});
},
_data: function _data() {
var groups = this.state.groups;
return this.state.sortedKeys.reduce(function (flat, grp) {
return flat.concat(groups[grp]);
}, []);
},
move: function move() {
var selected = this.getItemDOMNode(this.props.focused);
if (!selected) return;
_utilWidgetHelpers.notify(this.props.onMove, [selected, _utilCompat2['default'].findDOMNode(this), this.props.focused]);
},
getItemDOMNode: function getItemDOMNode(item) {
var list = _utilCompat2['default'].findDOMNode(this),
groups = this.state.groups,
idx = -1,
itemIdx,
child;
this.state.sortedKeys.some(function (group) {
itemIdx = groups[group].indexOf(item);
idx++;
if (itemIdx !== -1) return !!(child = list.children[idx + itemIdx + 1]);
idx += groups[group].length;
});
return child;
}
});
module.exports = exports['default'];