@checksub_team/react-widgets
Version:
559 lines (467 loc) • 21.2 kB
JavaScript
"use strict";
exports.__esModule = true;
exports.default = void 0;
var _react = _interopRequireDefault(require("react"));
var _propTypes = _interopRequireDefault(require("prop-types"));
var _classnames = _interopRequireDefault(require("classnames"));
var _reactLifecyclesCompat = require("react-lifecycles-compat");
var _uncontrollable = _interopRequireDefault(require("uncontrollable"));
var _Widget = _interopRequireDefault(require("./Widget"));
var _WidgetPicker = _interopRequireDefault(require("./WidgetPicker"));
var _List = _interopRequireDefault(require("./List"));
var _Popup = _interopRequireDefault(require("./Popup"));
var _Select = _interopRequireDefault(require("./Select"));
var _ComboboxInput = _interopRequireDefault(require("./ComboboxInput"));
var _messages = require("./messages");
var _focusManager = _interopRequireDefault(require("./util/focusManager"));
var _reduceToListState = _interopRequireDefault(require("./util/reduceToListState"));
var _getAccessors = _interopRequireDefault(require("./util/getAccessors"));
var CustomPropTypes = _interopRequireWildcard(require("./util/PropTypes"));
var _scrollManager = _interopRequireDefault(require("./util/scrollManager"));
var _ = require("./util/_");
var Props = _interopRequireWildcard(require("./util/Props"));
var Filter = _interopRequireWildcard(require("./util/Filter"));
var _interaction = require("./util/interaction");
var _widgetHelpers = require("./util/widgetHelpers");
var _Icon = require("./Icon");
var _class, _class2, _descriptor, _descriptor2, _descriptor3, _class3, _temp;
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } }
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _initializerDefineProperty(target, property, descriptor, context) { if (!descriptor) return; Object.defineProperty(target, property, { enumerable: descriptor.enumerable, configurable: descriptor.configurable, writable: descriptor.writable, value: descriptor.initializer ? descriptor.initializer.call(context) : void 0 }); }
function _inheritsLoose(subClass, superClass) { subClass.prototype = Object.create(superClass.prototype); subClass.prototype.constructor = subClass; subClass.__proto__ = superClass; }
function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }
function _applyDecoratedDescriptor(target, property, decorators, descriptor, context) { var desc = {}; Object['ke' + 'ys'](descriptor).forEach(function (key) { desc[key] = descriptor[key]; }); desc.enumerable = !!desc.enumerable; desc.configurable = !!desc.configurable; if ('value' in desc || desc.initializer) { desc.writable = true; } desc = decorators.slice().reverse().reduce(function (desc, decorator) { return decorator(target, property, desc) || desc; }, desc); if (context && desc.initializer !== void 0) { desc.value = desc.initializer ? desc.initializer.call(context) : void 0; desc.initializer = undefined; } if (desc.initializer === void 0) { Object['define' + 'Property'](target, property, desc); desc = null; } return desc; }
function _initializerWarningHelper(descriptor, context) { throw new Error('Decorating class property failed. Please ensure that ' + 'proposal-class-properties is enabled and set to use loose mode. ' + 'To use proposal-class-properties in spec mode with decorators, wait for ' + 'the next major version of decorators in stage 2.'); }
function _extends() { _extends = Object.assign || 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); }
var propTypes = _extends({}, Filter.propTypes, {
value: _propTypes.default.any,
onChange: _propTypes.default.func,
open: _propTypes.default.bool,
onToggle: _propTypes.default.func,
itemComponent: CustomPropTypes.elementType,
listComponent: CustomPropTypes.elementType,
groupComponent: CustomPropTypes.elementType,
groupBy: CustomPropTypes.accessor,
data: _propTypes.default.array,
valueField: CustomPropTypes.accessor,
textField: CustomPropTypes.accessor,
name: _propTypes.default.string,
/**
*
* @type {(dataItem: ?any, metadata: { originalEvent: SyntheticEvent }) => void}
*/
onSelect: _propTypes.default.func,
autoFocus: _propTypes.default.bool,
disabled: CustomPropTypes.disabled.acceptsArray,
readOnly: CustomPropTypes.disabled,
/**
* When `true` the Combobox will suggest, or fill in, values as you type. The suggestions
* are always "startsWith", meaning it will search from the start of the `textField` property
*/
suggest: Filter.propTypes.filter,
busy: _propTypes.default.bool,
/** Specify the element used to render the select (down arrow) icon. */
selectIcon: _propTypes.default.node,
/** Specify the element used to render the busy indicator */
busySpinner: _propTypes.default.node,
delay: _propTypes.default.number,
dropUp: _propTypes.default.bool,
popupTransition: CustomPropTypes.elementType,
placeholder: _propTypes.default.string,
/** Adds a css class to the input container element. */
containerClassName: _propTypes.default.string,
inputProps: _propTypes.default.object,
listProps: _propTypes.default.object,
isRtl: _propTypes.default.bool,
messages: _propTypes.default.shape({
openCombobox: CustomPropTypes.message,
emptyList: CustomPropTypes.message,
emptyFilter: CustomPropTypes.message
})
/**
* ---
* shortcuts:
* - { key: alt + down arrow, label: open combobox }
* - { key: alt + up arrow, label: close combobox }
* - { key: down arrow, label: move focus to next item }
* - { key: up arrow, label: move focus to previous item }
* - { key: home, label: move focus to first item }
* - { key: end, label: move focus to last item }
* - { key: enter, label: select focused item }
* - { key: any key, label: search list for item starting with key }
* ---
*
* Select an item from the list, or input a custom value. The Combobox can also make suggestions as you type.
* @public
*/
});
var Combobox = (0, _reactLifecyclesCompat.polyfill)(_class = (_class2 = (_temp = _class3 =
/*#__PURE__*/
function (_React$Component) {
_inheritsLoose(Combobox, _React$Component);
function Combobox(props, context) {
var _this;
_this = _React$Component.call(this, props, context) || this;
_this.handleFocusWillChange = function (focused) {
if (!focused && _this.inputRef) _this.inputRef.accept();
if (focused) _this.focus();
};
_this.handleFocusChanged = function (focused) {
if (!focused) _this.close();
};
_initializerDefineProperty(_this, "handleSelect", _descriptor, _assertThisInitialized(_assertThisInitialized(_this)));
_this.handleInputKeyDown = function (_ref) {
var key = _ref.key;
_this._deleting = key === 'Backspace' || key === 'Delete';
_this._isTyping = true;
};
_this.handleInputChange = function (event) {
var suggestion = _this.suggest(event.target.value);
_this.change(suggestion, true, event);
_this.open();
};
_initializerDefineProperty(_this, "handleKeyDown", _descriptor2, _assertThisInitialized(_assertThisInitialized(_this)));
_this.attachListRef = function (ref) {
_this.listRef = ref;
};
_this.attachInputRef = function (ref) {
_this.inputRef = ref;
};
_initializerDefineProperty(_this, "toggle", _descriptor3, _assertThisInitialized(_assertThisInitialized(_this)));
_this.inputId = (0, _widgetHelpers.instanceId)(_assertThisInitialized(_assertThisInitialized(_this)), '_input');
_this.listId = (0, _widgetHelpers.instanceId)(_assertThisInitialized(_assertThisInitialized(_this)), '_listbox');
_this.activeId = (0, _widgetHelpers.instanceId)(_assertThisInitialized(_assertThisInitialized(_this)), '_listbox_active_option');
_this.handleScroll = (0, _scrollManager.default)(_assertThisInitialized(_assertThisInitialized(_this)));
_this.focusManager = (0, _focusManager.default)(_assertThisInitialized(_assertThisInitialized(_this)), {
willHandle: _this.handleFocusWillChange,
didHandle: _this.handleFocusChanged
});
_this.state = {
isSuggesting: function isSuggesting() {
return _this.inputRef && _this.inputRef.isSuggesting();
}
};
return _this;
}
var _proto = Combobox.prototype;
_proto.shouldComponentUpdate = function shouldComponentUpdate(nextProps, nextState) {
var isSuggesting = nextState.isSuggesting(),
stateChanged = !(0, _.isShallowEqual)(nextState, this.state),
valueChanged = !(0, _.isShallowEqual)(nextProps, this.props);
return isSuggesting || stateChanged || valueChanged;
};
Combobox.getDerivedStateFromProps = function getDerivedStateFromProps(nextProps, prevState) {
var value = nextProps.value,
data = nextProps.data,
messages = nextProps.messages,
filter = nextProps.filter,
minLength = nextProps.minLength,
caseSensitive = nextProps.caseSensitive;
var focusedItem = prevState.focusedItem;
var accessors = (0, _getAccessors.default)(nextProps);
var valueChanged = value !== prevState.lastValue;
var selectedIndex = accessors.indexOf(data, value);
var dataItem = selectedIndex === -1 ? value : data[selectedIndex];
var searchTerm; // filter only when the value is not an item in the data list
if (selectedIndex === -1 || prevState.isSuggesting()) {
searchTerm = accessors.text(dataItem);
}
data = Filter.filter(data, {
filter: filter,
searchTerm: searchTerm,
minLength: minLength,
caseSensitive: caseSensitive,
textField: accessors.text
});
var list = (0, _reduceToListState.default)(data, prevState.list, {
nextProps: nextProps
}); // index may have changed after filtering
if (selectedIndex !== -1) {
selectedIndex = accessors.indexOf(data, value);
}
var focusedIndex = accessors.indexOf(data, focusedItem);
if (focusedIndex === -1) {
// value isn't a dataItem so find the close match
focusedIndex = Filter.indexOf(data, {
searchTerm: searchTerm,
textField: accessors.text,
filter: filter || true
});
}
var selectedItem = data[selectedIndex];
var nextFocusedItem = null; // If no item is focused, or is no longer in the dataset, default to either the selected item, or to the first item in the list
if (focusedIndex === -1) {
if (selectedItem !== undefined) {
nextFocusedItem = selectedItem;
} else {
nextFocusedItem = data[0];
}
} else {
nextFocusedItem = data[focusedIndex];
}
return {
data: data,
list: list,
accessors: accessors,
lastValue: value,
messages: (0, _messages.getMessages)(messages),
selectedItem: valueChanged ? list.nextEnabled(selectedItem) : prevState.selectedItem,
focusedItem: valueChanged || focusedItem === undefined ? list.nextEnabled(selectedItem !== undefined ? selectedItem : nextFocusedItem) : nextFocusedItem
};
}; // has to be done early since `accept()` re-focuses the input
_proto.renderInput = function renderInput() {
var _this$props = this.props,
suggest = _this$props.suggest,
filter = _this$props.filter,
busy = _this$props.busy,
name = _this$props.name,
data = _this$props.data,
value = _this$props.value,
autoFocus = _this$props.autoFocus,
tabIndex = _this$props.tabIndex,
placeholder = _this$props.placeholder,
inputProps = _this$props.inputProps,
disabled = _this$props.disabled,
readOnly = _this$props.readOnly,
open = _this$props.open;
var accessors = this.state.accessors;
var valueItem = accessors.findOrSelf(data, value);
var completeType = suggest ? filter ? 'both' : 'inline' : filter ? 'list' : '';
return _react.default.createElement(_ComboboxInput.default, _extends({}, inputProps, {
role: "combobox",
name: name,
id: this.inputId,
autoFocus: autoFocus,
tabIndex: tabIndex,
suggest: suggest,
disabled: disabled === true,
readOnly: readOnly === true,
"aria-busy": !!busy,
"aria-owns": this.listId,
"aria-autocomplete": completeType,
"aria-activedescendant": open ? this.activeId : null,
"aria-expanded": open,
"aria-haspopup": true,
placeholder: placeholder,
value: accessors.text(valueItem),
onChange: this.handleInputChange,
onKeyDown: this.handleInputKeyDown,
ref: this.attachInputRef
}));
};
_proto.renderList = function renderList(messages) {
var activeId = this.activeId,
inputId = this.inputId,
listId = this.listId;
var _this$props2 = this.props,
open = _this$props2.open,
data = _this$props2.data,
value = _this$props2.value,
listProps = _this$props2.listProps,
optionComponent = _this$props2.optionComponent,
itemComponent = _this$props2.itemComponent,
groupComponent = _this$props2.groupComponent;
var _this$state = this.state,
list = _this$state.list,
accessors = _this$state.accessors,
focusedItem = _this$state.focusedItem,
selectedItem = _this$state.selectedItem,
filteredData = _this$state.data;
var List = this.props.listComponent;
return _react.default.createElement(List, _extends({}, listProps, {
id: listId,
activeId: activeId,
data: filteredData,
dataState: list.dataState,
isDisabled: list.isDisabled,
textAccessor: accessors.text,
valueAccessor: accessors.value,
itemComponent: itemComponent,
groupComponent: groupComponent,
optionComponent: optionComponent,
selectedItem: selectedItem,
focusedItem: open ? focusedItem : null,
searchTerm: accessors.text(value) || '',
"aria-hidden": !open,
"aria-labelledby": inputId,
"aria-live": open && 'polite',
onSelect: this.handleSelect,
onMove: this.handleScroll,
ref: this.attachListRef,
messages: {
emptyList: data.length ? messages.emptyFilter : messages.emptyList
}
}));
};
_proto.render = function render() {
var _this2 = this;
var _this$props3 = this.props,
isRtl = _this$props3.isRtl,
className = _this$props3.className,
popupTransition = _this$props3.popupTransition,
busy = _this$props3.busy,
dropUp = _this$props3.dropUp,
open = _this$props3.open,
selectIcon = _this$props3.selectIcon,
busySpinner = _this$props3.busySpinner,
containerClassName = _this$props3.containerClassName;
var _this$state2 = this.state,
focused = _this$state2.focused,
messages = _this$state2.messages;
var disabled = this.props.disabled === true,
readOnly = this.props.readOnly === true;
var elementProps = Props.pickElementProps(this);
var shouldRenderPopup = (0, _widgetHelpers.isFirstFocusedRender)(this);
return _react.default.createElement(_Widget.default, _extends({}, elementProps, {
open: open,
isRtl: isRtl,
dropUp: dropUp,
focused: focused,
disabled: disabled,
readOnly: readOnly,
onBlur: this.focusManager.handleBlur,
onFocus: this.focusManager.handleFocus,
onKeyDown: this.handleKeyDown,
className: (0, _classnames.default)(className, 'rw-combobox')
}), _react.default.createElement(_WidgetPicker.default, {
className: containerClassName
}, this.renderInput(), _react.default.createElement(_Select.default, {
bordered: true,
busy: busy,
icon: selectIcon,
spinner: busySpinner,
onClick: this.toggle,
disabled: disabled || readOnly,
label: messages.openCombobox(this.props)
})), shouldRenderPopup && _react.default.createElement(_Popup.default, {
open: open,
dropUp: dropUp,
transition: popupTransition,
onEntering: function onEntering() {
return _this2.listRef.forceUpdate();
}
}, _react.default.createElement("div", null, this.renderList(messages))));
};
_proto.focus = function focus() {
if (this.inputRef) this.inputRef.focus();
};
_proto.change = function change(nextValue, typing, originalEvent) {
var _this$props4 = this.props,
onChange = _this$props4.onChange,
lastValue = _this$props4.value;
this._typedChange = !!typing;
(0, _widgetHelpers.notify)(onChange, [nextValue, {
lastValue: lastValue,
originalEvent: originalEvent
}]);
};
_proto.open = function open() {
if (!this.props.open) (0, _widgetHelpers.notify)(this.props.onToggle, true);
};
_proto.close = function close() {
if (this.props.open) (0, _widgetHelpers.notify)(this.props.onToggle, false);
};
_proto.suggest = function suggest(searchTerm) {
var _this$props5 = this.props,
textField = _this$props5.textField,
suggest = _this$props5.suggest,
minLength = _this$props5.minLength;
var data = this.state.data;
if (!this._deleting) return Filter.suggest(data, {
minLength: minLength,
textField: textField,
searchTerm: searchTerm,
filter: suggest,
caseSensitive: false
});
return searchTerm;
};
return Combobox;
}(_react.default.Component), _class3.propTypes = propTypes, _class3.defaultProps = {
data: [],
value: '',
open: false,
suggest: false,
filter: false,
delay: 500,
selectIcon: _Icon.caretDown,
listComponent: _List.default
}, _temp), (_descriptor = _applyDecoratedDescriptor(_class2.prototype, "handleSelect", [_interaction.widgetEditable], {
enumerable: true,
initializer: function initializer() {
var _this3 = this;
return function (data, originalEvent) {
_this3.close();
(0, _widgetHelpers.notify)(_this3.props.onSelect, [data, {
originalEvent: originalEvent
}]);
_this3.change(data, false, originalEvent);
_this3.inputRef && _this3.inputRef.accept(true);
_this3.focus();
};
}
}), _descriptor2 = _applyDecoratedDescriptor(_class2.prototype, "handleKeyDown", [_interaction.widgetEditable], {
enumerable: true,
initializer: function initializer() {
var _this4 = this;
return function (e) {
var key = e.key,
altKey = e.altKey;
var _this4$props = _this4.props,
open = _this4$props.open,
onKeyDown = _this4$props.onKeyDown;
var _this4$state = _this4.state,
focusedItem = _this4$state.focusedItem,
selectedItem = _this4$state.selectedItem,
list = _this4$state.list;
(0, _widgetHelpers.notify)(onKeyDown, [e]);
if (e.defaultPrevented) return;
var select = function select(item) {
return item != null && _this4.handleSelect(item, e);
};
var focusItem = function focusItem(item) {
return _this4.setState({
focusedItem: item
});
};
if (key === 'End' && open) {
e.preventDefault();
focusItem(list.last());
} else if (key === 'Home' && open) {
e.preventDefault();
focusItem(list.first());
} else if (key === 'Escape' && open) {
e.preventDefault();
_this4.close();
} else if (key === 'Enter' && open) {
e.preventDefault();
select(_this4.state.focusedItem);
} else if (key === 'Tab') {
_this4.inputRef.accept();
} else if (key === 'ArrowDown') {
e.preventDefault();
if (altKey) return _this4.open();
if (open) focusItem(list.next(focusedItem));else select(list.next(selectedItem));
} else if (key === 'ArrowUp') {
e.preventDefault();
if (altKey) return _this4.close();
if (open) focusItem(list.prev(focusedItem));else select(list.prev(selectedItem));
}
};
}
}), _descriptor3 = _applyDecoratedDescriptor(_class2.prototype, "toggle", [_interaction.widgetEditable], {
enumerable: true,
initializer: function initializer() {
var _this5 = this;
return function () {
_this5.focus();
_this5.props.open ? _this5.close() : _this5.open();
};
}
})), _class2)) || _class;
var _default = (0, _uncontrollable.default)(Combobox, {
open: 'onToggle',
value: 'onChange'
}, ['focus']);
exports.default = _default;
module.exports = exports["default"];