@thejones/react-common-components
Version:
React component - semantic ui
1,153 lines (940 loc) • 59.7 kB
JavaScript
"use strict";
var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard");
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
var _objectSpread2 = _interopRequireDefault(require("@babel/runtime/helpers/objectSpread"));
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
var _possibleConstructorReturn2 = _interopRequireDefault(require("@babel/runtime/helpers/possibleConstructorReturn"));
var _getPrototypeOf3 = _interopRequireDefault(require("@babel/runtime/helpers/getPrototypeOf"));
var _get3 = _interopRequireDefault(require("@babel/runtime/helpers/get"));
var _inherits2 = _interopRequireDefault(require("@babel/runtime/helpers/inherits"));
var _assertThisInitialized2 = _interopRequireDefault(require("@babel/runtime/helpers/assertThisInitialized"));
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var _compact2 = _interopRequireDefault(require("lodash/compact"));
var _map2 = _interopRequireDefault(require("lodash/map"));
var _every2 = _interopRequireDefault(require("lodash/every"));
var _without2 = _interopRequireDefault(require("lodash/without"));
var _findIndex2 = _interopRequireDefault(require("lodash/findIndex"));
var _find2 = _interopRequireDefault(require("lodash/find"));
var _reduce2 = _interopRequireDefault(require("lodash/reduce"));
var _some2 = _interopRequireDefault(require("lodash/some"));
var _escapeRegExp2 = _interopRequireDefault(require("lodash/escapeRegExp"));
var _deburr2 = _interopRequireDefault(require("lodash/deburr"));
var _isFunction2 = _interopRequireDefault(require("lodash/isFunction"));
var _filter2 = _interopRequireDefault(require("lodash/filter"));
var _pick2 = _interopRequireDefault(require("lodash/pick"));
var _dropRight2 = _interopRequireDefault(require("lodash/dropRight"));
var _isEmpty2 = _interopRequireDefault(require("lodash/isEmpty"));
var _size2 = _interopRequireDefault(require("lodash/size"));
var _difference2 = _interopRequireDefault(require("lodash/difference"));
var _union2 = _interopRequireDefault(require("lodash/union"));
var _get4 = _interopRequireDefault(require("lodash/get"));
var _includes2 = _interopRequireDefault(require("lodash/includes"));
var _isUndefined2 = _interopRequireDefault(require("lodash/isUndefined"));
var _invoke2 = _interopRequireDefault(require("lodash/invoke"));
var _isEqual2 = _interopRequireDefault(require("lodash/isEqual"));
var _has2 = _interopRequireDefault(require("lodash/has"));
var _isNil2 = _interopRequireDefault(require("lodash/isNil"));
var _eventStack = _interopRequireDefault(require("@semantic-ui-react/event-stack"));
var _classnames = _interopRequireDefault(require("classnames"));
var _keyboardKey = _interopRequireDefault(require("keyboard-key"));
var _propTypes = _interopRequireDefault(require("prop-types"));
var _react = _interopRequireWildcard(require("react"));
var _shallowequal = _interopRequireDefault(require("shallowequal"));
var _lib = require("../../lib");
var _Ref = _interopRequireDefault(require("../../addons/Ref"));
var _Icon = _interopRequireDefault(require("../../elements/Icon"));
var _Label = _interopRequireDefault(require("../../elements/Label"));
var _DropdownDivider = _interopRequireDefault(require("./DropdownDivider"));
var _DropdownItem = _interopRequireDefault(require("./DropdownItem"));
var _DropdownHeader = _interopRequireDefault(require("./DropdownHeader"));
var _DropdownMenu = _interopRequireDefault(require("./DropdownMenu"));
var _DropdownSearchInput = _interopRequireDefault(require("./DropdownSearchInput"));
var getKeyOrValue = function getKeyOrValue(key, value) {
return (0, _isNil2.default)(key) ? value : key;
};
/**
* A dropdown allows a user to select a value from a series of options.
* @see Form
* @see Select
* @see Menu
*/
var Dropdown =
/*#__PURE__*/
function (_Component) {
(0, _inherits2.default)(Dropdown, _Component);
function Dropdown() {
var _getPrototypeOf2;
var _this;
(0, _classCallCheck2.default)(this, Dropdown);
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
_this = (0, _possibleConstructorReturn2.default)(this, (_getPrototypeOf2 = (0, _getPrototypeOf3.default)(Dropdown)).call.apply(_getPrototypeOf2, [this].concat(args)));
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)), "searchRef", (0, _react.createRef)());
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)), "sizerRef", (0, _react.createRef)());
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)), "ref", (0, _react.createRef)());
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)), "handleChange", function (e, value) {
(0, _invoke2.default)(_this.props, 'onChange', e, (0, _objectSpread2.default)({}, _this.props, {
value: value
}));
});
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)), "closeOnChange", function (e) {
var _this$props = _this.props,
closeOnChange = _this$props.closeOnChange,
multiple = _this$props.multiple;
var shouldClose = (0, _isUndefined2.default)(closeOnChange) ? !multiple : closeOnChange;
if (shouldClose) _this.close(e);
});
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)), "closeOnEscape", function (e) {
if (_keyboardKey.default.getCode(e) !== _keyboardKey.default.Escape) return;
e.preventDefault();
_this.close();
});
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)), "moveSelectionOnKeyDown", function (e) {
var _moves;
var _this$props2 = _this.props,
multiple = _this$props2.multiple,
selectOnNavigation = _this$props2.selectOnNavigation;
var moves = (_moves = {}, (0, _defineProperty2.default)(_moves, _keyboardKey.default.ArrowDown, 1), (0, _defineProperty2.default)(_moves, _keyboardKey.default.ArrowUp, -1), _moves);
var move = moves[_keyboardKey.default.getCode(e)];
if (move === undefined) return;
e.preventDefault();
_this.moveSelectionBy(move);
if (!multiple && selectOnNavigation) _this.makeSelectedItemActive(e);
});
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)), "openOnSpace", function (e) {
if (_keyboardKey.default.getCode(e) !== _keyboardKey.default.Spacebar) return;
e.preventDefault();
_this.open(e);
});
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)), "openOnArrow", function (e) {
var code = _keyboardKey.default.getCode(e);
if (!(0, _includes2.default)([_keyboardKey.default.ArrowDown, _keyboardKey.default.ArrowUp], code)) return;
if (_this.state.open) return;
e.preventDefault();
_this.open(e);
});
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)), "makeSelectedItemActive", function (e) {
var _this$state = _this.state,
open = _this$state.open,
value = _this$state.value;
var multiple = _this.props.multiple;
var item = _this.getSelectedItem();
var selectedValue = (0, _get4.default)(item, 'value'); // prevent selecting null if there was no selected item value
// prevent selecting duplicate items when the dropdown is closed
if ((0, _isNil2.default)(selectedValue) || !open) return; // state value may be undefined
var newValue = multiple ? (0, _union2.default)(_this.state.value, [selectedValue]) : selectedValue;
var valueHasChanged = multiple ? !!(0, _difference2.default)(newValue, value).length : newValue !== value;
if (valueHasChanged) {
// notify the onChange prop that the user is trying to change value
_this.setValue(newValue);
_this.setSelectedIndex(newValue);
_this.handleChange(e, newValue); // Heads up! This event handler should be called after `onChange`
// Notify the onAddItem prop if this is a new value
if (item['data-additional']) {
(0, _invoke2.default)(_this.props, 'onAddItem', e, (0, _objectSpread2.default)({}, _this.props, {
value: selectedValue
}));
}
}
});
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)), "selectItemOnEnter", function (e) {
var search = _this.props.search;
if (_keyboardKey.default.getCode(e) !== _keyboardKey.default.Enter) return;
e.preventDefault();
var optionSize = (0, _size2.default)(_this.getMenuOptions());
if (search && optionSize === 0) return;
_this.makeSelectedItemActive(e);
_this.closeOnChange(e);
_this.clearSearchQuery();
if (search) (0, _invoke2.default)(_this.searchRef.current, 'focus');
});
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)), "removeItemOnBackspace", function (e) {
var _this$props3 = _this.props,
multiple = _this$props3.multiple,
search = _this$props3.search;
var _this$state2 = _this.state,
searchQuery = _this$state2.searchQuery,
value = _this$state2.value;
if (_keyboardKey.default.getCode(e) !== _keyboardKey.default.Backspace) return;
if (searchQuery || !search || !multiple || (0, _isEmpty2.default)(value)) return;
e.preventDefault(); // remove most recent value
var newValue = (0, _dropRight2.default)(value);
_this.setValue(newValue);
_this.setSelectedIndex(newValue);
_this.handleChange(e, newValue);
});
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)), "closeOnDocumentClick", function (e) {
if (!_this.props.closeOnBlur) return; // If event happened in the dropdown, ignore it
if (_this.ref.current && (0, _lib.doesNodeContainClick)(_this.ref.current, e)) return;
_this.close();
});
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)), "handleMouseDown", function (e) {
_this.isMouseDown = true;
(0, _invoke2.default)(_this.props, 'onMouseDown', e, _this.props);
document.addEventListener('mouseup', _this.handleDocumentMouseUp);
});
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)), "handleDocumentMouseUp", function () {
_this.isMouseDown = false;
document.removeEventListener('mouseup', _this.handleDocumentMouseUp);
});
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)), "handleClick", function (e) {
var _this$props4 = _this.props,
minCharacters = _this$props4.minCharacters,
search = _this$props4.search;
var _this$state3 = _this.state,
open = _this$state3.open,
searchQuery = _this$state3.searchQuery;
(0, _invoke2.default)(_this.props, 'onClick', e, _this.props); // prevent closeOnDocumentClick()
e.stopPropagation();
if (!search) return _this.toggle(e);
if (open) {
(0, _invoke2.default)(_this.searchRef.current, 'focus');
return;
}
if (searchQuery.length >= minCharacters || minCharacters === 1) {
_this.open(e);
return;
}
(0, _invoke2.default)(_this.searchRef.current, 'focus');
});
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)), "handleIconClick", function (e) {
var clearable = _this.props.clearable;
var hasValue = _this.hasValue();
(0, _invoke2.default)(_this.props, 'onClick', e, _this.props); // prevent handleClick()
e.stopPropagation();
if (clearable && hasValue) {
_this.clearValue(e);
} else {
_this.toggle(e);
}
});
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)), "handleItemClick", function (e, item) {
var _this$props5 = _this.props,
multiple = _this$props5.multiple,
search = _this$props5.search;
var currentValue = _this.state.value;
var value = item.value; // prevent toggle() in handleClick()
e.stopPropagation(); // prevent closeOnDocumentClick() if multiple or item is disabled
if (multiple || item.disabled) e.nativeEvent.stopImmediatePropagation();
if (item.disabled) return;
var isAdditionItem = item['data-additional'];
var newValue = multiple ? (0, _union2.default)(_this.state.value, [value]) : value;
var valueHasChanged = multiple ? !!(0, _difference2.default)(newValue, currentValue).length : newValue !== currentValue; // notify the onChange prop that the user is trying to change value
if (valueHasChanged) {
_this.setValue(newValue);
_this.setSelectedIndex(value);
_this.handleChange(e, newValue);
}
_this.clearSearchQuery();
_this.closeOnChange(e); // Heads up! This event handler should be called after `onChange`
// Notify the onAddItem prop if this is a new value
if (isAdditionItem) (0, _invoke2.default)(_this.props, 'onAddItem', e, (0, _objectSpread2.default)({}, _this.props, {
value: value
}));
if (search) (0, _invoke2.default)(_this.searchRef.current, 'focus');
});
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)), "handleFocus", function (e) {
var focus = _this.state.focus;
if (focus) return;
(0, _invoke2.default)(_this.props, 'onFocus', e, _this.props);
_this.setState({
focus: true
});
});
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)), "handleBlur", function (e) {
// Heads up! Don't remove this.
// https://github.com/Semantic-Org/Semantic-UI-React/issues/1315
var currentTarget = (0, _get4.default)(e, 'currentTarget');
if (currentTarget && currentTarget.contains(document.activeElement)) return;
var _this$props6 = _this.props,
closeOnBlur = _this$props6.closeOnBlur,
multiple = _this$props6.multiple,
selectOnBlur = _this$props6.selectOnBlur; // do not "blur" when the mouse is down inside of the Dropdown
if (_this.isMouseDown) return;
(0, _invoke2.default)(_this.props, 'onBlur', e, _this.props);
if (selectOnBlur && !multiple) {
_this.makeSelectedItemActive(e);
if (closeOnBlur) _this.close();
}
_this.setState({
focus: false
});
_this.clearSearchQuery();
});
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)), "handleSearchChange", function (e, _ref) {
var value = _ref.value;
// prevent propagating to this.props.onChange()
e.stopPropagation();
var minCharacters = _this.props.minCharacters;
var open = _this.state.open;
var newQuery = value;
(0, _invoke2.default)(_this.props, 'onSearchChange', e, (0, _objectSpread2.default)({}, _this.props, {
searchQuery: newQuery
}));
_this.trySetState({
searchQuery: newQuery
}, {
selectedIndex: 0
}); // open search dropdown on search query
if (!open && newQuery.length >= minCharacters) {
_this.open();
return;
} // close search dropdown if search query is too small
if (open && minCharacters !== 1 && newQuery.length < minCharacters) _this.close();
});
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)), "getKeyAndValues", function (options) {
return options ? options.map(function (option) {
return (0, _pick2.default)(option, ['key', 'value']);
}) : options;
});
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)), "getMenuOptions", function () {
var value = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : _this.state.value;
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : _this.props.options;
var _this$props7 = _this.props,
additionLabel = _this$props7.additionLabel,
additionPosition = _this$props7.additionPosition,
allowAdditions = _this$props7.allowAdditions,
deburr = _this$props7.deburr,
multiple = _this$props7.multiple,
search = _this$props7.search;
var searchQuery = _this.state.searchQuery;
var filteredOptions = options; // filter out active options
if (multiple) {
filteredOptions = (0, _filter2.default)(filteredOptions, function (opt) {
return !(0, _includes2.default)(value, opt.value);
});
} // filter by search query
if (search && searchQuery) {
if ((0, _isFunction2.default)(search)) {
filteredOptions = search(filteredOptions, searchQuery);
} else {
// remove diacritics on search input and options, if deburr prop is set
var strippedQuery = deburr ? (0, _deburr2.default)(searchQuery) : searchQuery;
var re = new RegExp((0, _escapeRegExp2.default)(strippedQuery), 'i');
filteredOptions = (0, _filter2.default)(filteredOptions, function (opt) {
return re.test(deburr ? (0, _deburr2.default)(opt.text) : opt.text);
});
}
} // insert the "add" item
if (allowAdditions && search && searchQuery && !(0, _some2.default)(filteredOptions, {
text: searchQuery
})) {
var additionLabelElement = _react.default.isValidElement(additionLabel) ? _react.default.cloneElement(additionLabel, {
key: 'addition-label'
}) : additionLabel || '';
var addItem = {
key: 'addition',
// by using an array, we can pass multiple elements, but when doing so
// we must specify a `key` for React to know which one is which
text: [additionLabelElement, _react.default.createElement("b", {
key: "addition-query"
}, searchQuery)],
value: searchQuery,
className: 'addition',
'data-additional': true
};
if (additionPosition === 'top') filteredOptions.unshift(addItem);else filteredOptions.push(addItem);
}
return filteredOptions;
});
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)), "getSelectedItem", function () {
var selectedIndex = _this.state.selectedIndex;
var options = _this.getMenuOptions();
return (0, _get4.default)(options, "[".concat(selectedIndex, "]"));
});
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)), "getEnabledIndices", function (givenOptions) {
var options = givenOptions || _this.getMenuOptions();
return (0, _reduce2.default)(options, function (memo, item, index) {
if (!item.disabled) memo.push(index);
return memo;
}, []);
});
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)), "getItemByValue", function (value) {
var options = _this.props.options;
return (0, _find2.default)(options, {
value: value
});
});
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)), "getMenuItemIndexByValue", function (value, givenOptions) {
var options = givenOptions || _this.getMenuOptions();
return (0, _findIndex2.default)(options, ['value', value]);
});
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)), "getDropdownAriaOptions", function () {
var _this$props8 = _this.props,
loading = _this$props8.loading,
disabled = _this$props8.disabled,
search = _this$props8.search,
multiple = _this$props8.multiple;
var open = _this.state.open;
var ariaOptions = {
role: search ? 'combobox' : 'listbox',
'aria-busy': loading,
'aria-disabled': disabled,
'aria-expanded': !!open
};
if (ariaOptions.role === 'listbox') {
ariaOptions['aria-multiselectable'] = multiple;
}
return ariaOptions;
});
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)), "clearSearchQuery", function () {
_this.trySetState({
searchQuery: ''
});
});
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)), "setValue", function (value) {
_this.trySetState({
value: value
});
});
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)), "setSelectedIndex", function () {
var value = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : _this.state.value;
var optionsProps = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : _this.props.options;
var multiple = _this.props.multiple;
var selectedIndex = _this.state.selectedIndex;
var options = _this.getMenuOptions(value, optionsProps);
var enabledIndicies = _this.getEnabledIndices(options);
var newSelectedIndex; // update the selected index
if (!selectedIndex || selectedIndex < 0) {
var firstIndex = enabledIndicies[0]; // Select the currently active item, if none, use the first item.
// Multiple selects remove active items from the list,
// their initial selected index should be 0.
newSelectedIndex = multiple ? firstIndex : _this.getMenuItemIndexByValue(value, options) || enabledIndicies[0];
} else if (multiple) {
// multiple selects remove options from the menu as they are made active
// keep the selected index within range of the remaining items
if (selectedIndex >= options.length - 1) {
newSelectedIndex = enabledIndicies[enabledIndicies.length - 1];
}
} else {
var activeIndex = _this.getMenuItemIndexByValue(value, options); // regular selects can only have one active item
// set the selected index to the currently active item
newSelectedIndex = (0, _includes2.default)(enabledIndicies, activeIndex) ? activeIndex : undefined;
}
if (!newSelectedIndex || newSelectedIndex < 0) {
newSelectedIndex = enabledIndicies[0];
}
_this.setState({
selectedIndex: newSelectedIndex
});
});
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)), "handleLabelClick", function (e, labelProps) {
// prevent focusing search input on click
e.stopPropagation();
_this.setState({
selectedLabel: labelProps.value
});
(0, _invoke2.default)(_this.props, 'onLabelClick', e, labelProps);
});
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)), "handleLabelRemove", function (e, labelProps) {
// prevent focusing search input on click
e.stopPropagation();
var value = _this.state.value;
var newValue = (0, _without2.default)(value, labelProps.value);
_this.setValue(newValue);
_this.setSelectedIndex(newValue);
_this.handleChange(e, newValue);
});
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)), "moveSelectionBy", function (offset) {
var startIndex = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : _this.state.selectedIndex;
var options = _this.getMenuOptions(); // Prevent infinite loop
// TODO: remove left part of condition after children API will be removed
if (options === undefined || (0, _every2.default)(options, 'disabled')) return;
var lastIndex = options.length - 1;
var wrapSelection = _this.props.wrapSelection; // next is after last, wrap to beginning
// next is before first, wrap to end
var nextIndex = startIndex + offset; // if 'wrapSelection' is set to false and selection is after last or before first, it just does not change
if (!wrapSelection && (nextIndex > lastIndex || nextIndex < 0)) {
nextIndex = startIndex;
} else if (nextIndex > lastIndex) nextIndex = 0;else if (nextIndex < 0) nextIndex = lastIndex;
if (options[nextIndex].disabled) {
_this.moveSelectionBy(offset, nextIndex);
return;
}
_this.setState({
selectedIndex: nextIndex
});
_this.scrollSelectedItemIntoView();
});
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)), "handleIconOverrides", function (predefinedProps) {
var clearable = _this.props.clearable;
var classes = (0, _classnames.default)(clearable && _this.hasValue() && 'clear', predefinedProps.className);
return {
className: classes,
onClick: function onClick(e) {
(0, _invoke2.default)(predefinedProps, 'onClick', e, predefinedProps);
_this.handleIconClick(e);
}
};
});
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)), "clearValue", function (e) {
var multiple = _this.props.multiple;
var newValue = multiple ? [] : '';
_this.setValue(newValue);
_this.setSelectedIndex(newValue);
_this.handleChange(e, newValue);
});
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)), "computeSearchInputTabIndex", function () {
var _this$props9 = _this.props,
disabled = _this$props9.disabled,
tabIndex = _this$props9.tabIndex;
if (!(0, _isNil2.default)(tabIndex)) return tabIndex;
return disabled ? -1 : 0;
});
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)), "computeSearchInputWidth", function () {
var searchQuery = _this.state.searchQuery;
if (_this.sizerRef.current && searchQuery) {
// resize the search input, temporarily show the sizer so we can measure it
_this.sizerRef.current.style.display = 'inline';
_this.sizerRef.current.textContent = searchQuery;
var searchWidth = Math.ceil(_this.sizerRef.current.getBoundingClientRect().width);
_this.sizerRef.current.style.removeProperty('display');
return searchWidth;
}
});
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)), "computeTabIndex", function () {
var _this$props10 = _this.props,
disabled = _this$props10.disabled,
search = _this$props10.search,
tabIndex = _this$props10.tabIndex; // don't set a root node tabIndex as the search input has its own tabIndex
if (search) return undefined;
if (disabled) return -1;
return (0, _isNil2.default)(tabIndex) ? 0 : tabIndex;
});
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)), "handleSearchInputOverrides", function (predefinedProps) {
return {
onChange: function onChange(e, inputProps) {
(0, _invoke2.default)(predefinedProps, 'onChange', e, inputProps);
_this.handleSearchChange(e, inputProps);
}
};
});
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)), "hasValue", function () {
var multiple = _this.props.multiple;
var value = _this.state.value;
return multiple ? !(0, _isEmpty2.default)(value) : !(0, _isNil2.default)(value) && value !== '';
});
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)), "scrollSelectedItemIntoView", function () {
if (!_this.ref.current) return;
var menu = _this.ref.current.querySelector('.menu.visible');
if (!menu) return;
var item = menu.querySelector('.item.selected');
if (!item) return;
var isOutOfUpperView = item.offsetTop < menu.scrollTop;
var isOutOfLowerView = item.offsetTop + item.clientHeight > menu.scrollTop + menu.clientHeight;
if (isOutOfUpperView) {
menu.scrollTop = item.offsetTop;
} else if (isOutOfLowerView) {
// eslint-disable-next-line no-mixed-operators
menu.scrollTop = item.offsetTop + item.clientHeight - menu.clientHeight;
}
});
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)), "setOpenDirection", function () {
if (!_this.ref.current) return;
var menu = _this.ref.current.querySelector('.menu.visible');
if (!menu) return;
var dropdownRect = _this.ref.current.getBoundingClientRect();
var menuHeight = menu.clientHeight;
var spaceAtTheBottom = document.documentElement.clientHeight - dropdownRect.top - dropdownRect.height - menuHeight;
var spaceAtTheTop = dropdownRect.top - menuHeight;
var upward = spaceAtTheBottom < 0 && spaceAtTheTop > spaceAtTheBottom; // set state only if there's a relevant difference
if (!upward !== !_this.state.upward) {
_this.trySetState({
upward: upward
});
}
});
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)), "open", function (e) {
var _this$props11 = _this.props,
disabled = _this$props11.disabled,
open = _this$props11.open,
search = _this$props11.search;
if (disabled) return;
if (search) (0, _invoke2.default)(_this.searchRef.current, 'focus');
(0, _invoke2.default)(_this.props, 'onOpen', e, _this.props);
_this.trySetState({
open: true
});
_this.scrollSelectedItemIntoView();
});
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)), "close", function (e) {
var open = _this.state.open;
if (open) {
(0, _invoke2.default)(_this.props, 'onClose', e, _this.props);
_this.trySetState({
open: false
});
}
});
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)), "handleClose", function () {
var hasSearchFocus = document.activeElement === _this.searchRef.current; // https://github.com/Semantic-Org/Semantic-UI-React/issues/627
// Blur the Dropdown on close so it is blurred after selecting an item.
// This is to prevent it from re-opening when switching tabs after selecting an item.
if (!hasSearchFocus) {
_this.ref.current.blur();
}
var hasDropdownFocus = document.activeElement === _this.ref.current;
var hasFocus = hasSearchFocus || hasDropdownFocus; // We need to keep the virtual model in sync with the browser focus change
// https://github.com/Semantic-Org/Semantic-UI-React/issues/692
_this.setState({
focus: hasFocus
});
});
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)), "toggle", function (e) {
return _this.state.open ? _this.close(e) : _this.open(e);
});
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)), "renderText", function () {
var _this$props12 = _this.props,
multiple = _this$props12.multiple,
placeholder = _this$props12.placeholder,
search = _this$props12.search,
text = _this$props12.text;
var _this$state4 = _this.state,
searchQuery = _this$state4.searchQuery,
value = _this$state4.value,
open = _this$state4.open;
var hasValue = _this.hasValue();
var classes = (0, _classnames.default)(placeholder && !hasValue && 'default', 'text', search && searchQuery && 'filtered');
var _text = placeholder;
if (searchQuery) {
_text = null;
} else if (text) {
_text = text;
} else if (open && !multiple) {
_text = (0, _get4.default)(_this.getSelectedItem(), 'text');
} else if (hasValue) {
_text = (0, _get4.default)(_this.getItemByValue(value), 'text');
}
return _react.default.createElement("div", {
className: classes,
role: "alert",
"aria-live": "polite"
}, _text);
});
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)), "renderSearchInput", function () {
var _this$props13 = _this.props,
search = _this$props13.search,
searchInput = _this$props13.searchInput;
var searchQuery = _this.state.searchQuery;
return search && _react.default.createElement(_Ref.default, {
innerRef: _this.searchRef
}, _DropdownSearchInput.default.create(searchInput, {
defaultProps: {
style: {
width: _this.computeSearchInputWidth()
},
tabIndex: _this.computeSearchInputTabIndex(),
value: searchQuery
},
overrideProps: _this.handleSearchInputOverrides
}));
});
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)), "renderSearchSizer", function () {
var _this$props14 = _this.props,
search = _this$props14.search,
multiple = _this$props14.multiple;
return search && multiple && _react.default.createElement("span", {
className: "sizer",
ref: _this.sizerRef
});
});
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)), "renderLabels", function () {
var _this$props15 = _this.props,
multiple = _this$props15.multiple,
renderLabel = _this$props15.renderLabel;
var _this$state5 = _this.state,
selectedLabel = _this$state5.selectedLabel,
value = _this$state5.value;
if (!multiple || (0, _isEmpty2.default)(value)) {
return;
}
var selectedItems = (0, _map2.default)(value, _this.getItemByValue);
// if no item could be found for a given state value the selected item will be undefined
// compact the selectedItems so we only have actual objects left
return (0, _map2.default)((0, _compact2.default)(selectedItems), function (item, index) {
var defaultProps = {
active: item.value === selectedLabel,
as: 'a',
key: getKeyOrValue(item.key, item.value),
onClick: _this.handleLabelClick,
onRemove: _this.handleLabelRemove,
value: item.value
};
return _Label.default.create(renderLabel(item, index, defaultProps), {
defaultProps: defaultProps
});
});
});
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)), "renderOptions", function () {
var _this$props16 = _this.props,
lazyLoad = _this$props16.lazyLoad,
multiple = _this$props16.multiple,
search = _this$props16.search,
noResultsMessage = _this$props16.noResultsMessage;
var _this$state6 = _this.state,
open = _this$state6.open,
selectedIndex = _this$state6.selectedIndex,
value = _this$state6.value; // lazy load, only render options when open
if (lazyLoad && !open) return null;
var options = _this.getMenuOptions();
if (noResultsMessage !== null && search && (0, _isEmpty2.default)(options)) {
return _react.default.createElement("div", {
className: "message"
}, noResultsMessage);
}
var isActive = multiple ? function (optValue) {
return (0, _includes2.default)(value, optValue);
} : function (optValue) {
return optValue === value;
};
return (0, _map2.default)(options, function (opt, i) {
return _DropdownItem.default.create((0, _objectSpread2.default)({
active: isActive(opt.value),
onClick: _this.handleItemClick,
selected: selectedIndex === i
}, opt, {
key: getKeyOrValue(opt.key, opt.value),
// Needed for handling click events on disabled items
style: (0, _objectSpread2.default)({}, opt.style, {
pointerEvents: 'all'
})
}));
});
});
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)), "renderMenu", function () {
var _this$props17 = _this.props,
children = _this$props17.children,
direction = _this$props17.direction,
header = _this$props17.header;
var open = _this.state.open;
var ariaOptions = _this.getDropdownMenuAriaOptions(); // single menu child
if (!_lib.childrenUtils.isNil(children)) {
var menuChild = _react.Children.only(children);
var className = (0, _classnames.default)(direction, (0, _lib.useKeyOnly)(open, 'visible'), menuChild.props.className);
return (0, _react.cloneElement)(menuChild, (0, _objectSpread2.default)({
className: className
}, ariaOptions));
}
return _react.default.createElement(_DropdownMenu.default, (0, _extends2.default)({}, ariaOptions, {
direction: direction,
open: open
}), _DropdownHeader.default.create(header, {
autoGenerateKey: false
}), _this.renderOptions());
});
return _this;
}
(0, _createClass2.default)(Dropdown, [{
key: "getInitialAutoControlledState",
value: function getInitialAutoControlledState() {
return {
focus: false,
searchQuery: ''
};
}
}, {
key: "componentWillMount",
value: function componentWillMount() {
var _this$state7 = this.state,
open = _this$state7.open,
value = _this$state7.value;
this.setValue(value);
this.setSelectedIndex(value);
if (open) {
this.open();
}
}
}, {
key: "componentWillReceiveProps",
value: function componentWillReceiveProps(nextProps) {
(0, _get3.default)((0, _getPrototypeOf3.default)(Dropdown.prototype), "componentWillReceiveProps", this).call(this, nextProps);
/* eslint-disable no-console */
if (process.env.NODE_ENV !== 'production') {
// in development, validate value type matches dropdown type
var isNextValueArray = Array.isArray(nextProps.value);
var hasValue = (0, _has2.default)(nextProps, 'value');
if (hasValue && nextProps.multiple && !isNextValueArray) {
console.error('Dropdown `value` must be an array when `multiple` is set.' + " Received type: `".concat(Object.prototype.toString.call(nextProps.value), "`."));
} else if (hasValue && !nextProps.multiple && isNextValueArray) {
console.error('Dropdown `value` must not be an array when `multiple` is not set.' + ' Either set `multiple={true}` or use a string or number value.');
}
}
/* eslint-enable no-console */
if (!(0, _shallowequal.default)(nextProps.value, this.props.value)) {
this.setValue(nextProps.value);
this.setSelectedIndex(nextProps.value);
} // The selected index is only dependent on option keys/values.
// We only check those properties to avoid recursive performance impacts.
// https://github.com/Semantic-Org/Semantic-UI-React/issues/3000
if (!(0, _isEqual2.default)(this.getKeyAndValues(nextProps.options), this.getKeyAndValues(this.props.options))) {
this.setSelectedIndex(undefined, nextProps.options);
}
}
}, {
key: "shouldComponentUpdate",
value: function shouldComponentUpdate(nextProps, nextState) {
return !(0, _shallowequal.default)(nextProps, this.props) || !(0, _shallowequal.default)(nextState, this.state);
}
}, {
key: "componentDidUpdate",
value: function componentDidUpdate(prevProps, prevState) {
// eslint-disable-line complexity
var _this$props18 = this.props,
closeOnBlur = _this$props18.closeOnBlur,
minCharacters = _this$props18.minCharacters,
openOnFocus = _this$props18.openOnFocus,
search = _this$props18.search; // focused / blurred
if (!prevState.focus && this.state.focus) {
if (!this.isMouseDown) {
var openable = !search || search && minCharacters === 1 && !this.state.open;
if (openOnFocus && openable) this.open();
}
} else if (prevState.focus && !this.state.focus) {
if (!this.isMouseDown && closeOnBlur) {
this.close();
}
} // opened / closed
if (!prevState.open && this.state.open) {
this.setOpenDirection();
this.scrollSelectedItemIntoView();
} else if (prevState.open && !this.state.open) {
this.handleClose();
}
} // ----------------------------------------
// Document Event Handlers
// ----------------------------------------
// onChange needs to receive a value
// can't rely on props.value if we are controlled
}, {
key: "getDropdownMenuAriaOptions",
value: function getDropdownMenuAriaOptions() {
var _this$props19 = this.props,
search = _this$props19.search,
multiple = _this$props19.multiple;
var ariaOptions = {};
if (search) {
ariaOptions['aria-multiselectable'] = multiple;
ariaOptions.role = 'listbox';
}
return ariaOptions;
} // ----------------------------------------
// Setters
// ----------------------------------------
}, {
key: "render",
value: function render() {
var _this$props20 = this.props,
basic = _this$props20.basic,
button = _this$props20.button,
className = _this$props20.className,
compact = _this$props20.compact,
disabled = _this$props20.disabled,
error = _this$props20.error,
fluid = _this$props20.fluid,
floating = _this$props20.floating,
icon = _this$props20.icon,
inline = _this$props20.inline,
item = _this$props20.item,
labeled = _this$props20.labeled,
loading = _this$props20.loading,
multiple = _this$props20.multiple,
pointing = _this$props20.pointing,
search = _this$props20.search,
selection = _this$props20.selection,
scrolling = _this$props20.scrolling,
simple = _this$props20.simple,
trigger = _this$props20.trigger;
var _this$state8 = this.state,
focus = _this$state8.focus,
open = _this$state8.open,
upward = _this$state8.upward; // Classes
var classes = (0, _classnames.default)('ui', (0, _lib.useKeyOnly)(open, 'active visible'), (0, _lib.useKeyOnly)(disabled, 'disabled'), (0, _lib.useKeyOnly)(error, 'error'), (0, _lib.useKeyOnly)(loading, 'loading'), (0, _lib.useKeyOnly)(basic, 'basic'), (0, _lib.useKeyOnly)(button, 'button'), (0, _lib.useKeyOnly)(compact, 'compact'), (0, _lib.useKeyOnly)(fluid, 'fluid'), (0, _lib.useKeyOnly)(floating, 'floating'), (0, _lib.useKeyOnly)(inline, 'inline'), // TODO: consider augmentation to render Dropdowns as Button/Menu, solves icon/link item issues
// https://github.com/Semantic-Org/Semantic-UI-React/issues/401#issuecomment-240487229
// TODO: the icon class is only required when a dropdown is a button
// useKeyOnly(icon, 'icon'),
(0, _lib.useKeyOnly)(labeled, 'labeled'), (0, _lib.useKeyOnly)(item, 'item'), (0, _lib.useKeyOnly)(multiple, 'multiple'), (0, _lib.useKeyOnly)(search, 'search'), (0, _lib.useKeyOnly)(selection, 'selection'), (0, _lib.useKeyOnly)(simple, 'simple'), (0, _lib.useKeyOnly)(scrolling, 'scrolling'), (0, _lib.useKeyOnly)(upward, 'upward'), (0, _lib.useKeyOrValueAndKey)(pointing, 'pointing'), 'dropdown', className);
var rest = (0, _lib.getUnhandledProps)(Dropdown, this.props);
var ElementType = (0, _lib.getElementType)(Dropdown, this.props);
var ariaOptions = this.getDropdownAriaOptions(ElementType, this.props);
return _react.default.createElement(_Ref.default, {
innerRef: this.ref
}, _react.default.createElement(ElementType, (0, _extends2.default)({}, rest, ariaOptions, {
className: classes,
onBlur: this.handleBlur,
onClick: this.handleClick,
onMouseDown: this.handleMouseDown,
onFocus: this.handleFocus,
onChange: this.handleChange,
tabIndex: this.computeTabIndex()
}), this.renderLabels(), this.renderSearchInput(), this.renderSearchSizer(), trigger || this.renderText(), _Icon.default.create(icon, {
overrideProps: this.handleIconOverrides,
autoGenerateKey: false
}), this.renderMenu(), open && _react.default.createElement(_eventStack.default, {
name: "keydown",
on: this.closeOnEscape
}), open && _react.default.createElement(_eventStack.default, {
name: "keydown",
on: this.moveSelectionOnKeyDown
}), open && _react.default.createElement(_eventStack.default, {
name: "click",
on: this.closeOnDocumentClick
}), open && _react.default.createElement(_eventStack.default, {
name: "keydown",
on: this.selectItemOnEnter
}), focus && _react.default.createElement(_eventStack.default, {
name: "keydown",
on: this.removeItemOnBackspace
}), focus && !open && _react.default.createElement(_eventStack.default, {
name: "keydown",
on: this.openOnArrow
}), focus && !open && _react.default.createElement(_eventStack.default, {
name: "keydown",
on: this.openOnSpace
})));
}
}]);
return Dropdown;
}(_lib.AutoControlledComponent);
exports.default = Dropdown;
(0, _defineProperty2.default)(Dropdown, "defaultProps", {
additionLabel: 'Add ',
additionPosition: 'top',
closeOnBlur: true,
deburr: false,
icon: 'dropdown',
minCharacters: 1,
noResultsMessage: 'No results found.',
openOnFocus: true,
renderLabel: function renderLabel(_ref2) {
var text = _ref2.text;
return text;
},
searchInput: 'text',
selectOnBlur: true,
selectOnNavigation: true,
wrapSelection: true
});
(0, _defineProperty2.default)(Dropdown, "autoControlledProps", ['open', 'searchQuery', 'selectedLabel', 'value', 'upward']);
(0, _defineProperty2.default)(Dropdown, "Divider", _DropdownDivider.default);
(0, _defineProperty2.default)(Dropdown, "Header", _DropdownHeader.default);
(0, _defineProperty2.default)(Dropdown, "Item", _DropdownItem.default);
(0, _defineProperty2.default)(Dropdown, "Menu", _DropdownMenu.default);
(0, _defineProperty2.default)(Dropdown, "SearchInput", _DropdownSearchInput.default);
(0, _defineProperty2.default)(Dropdown, "handledProps", ["additionLabel", "additionPosition", "allowAdditions", "as", "basic", "button", "children", "className", "clearable", "closeOnBlur", "closeOnChange", "compact", "deburr", "defaultOpen", "defaultSearchQuery", "defaultSelectedLabel", "defaultUpward", "defaultValue", "direction", "disabled", "error", "floating", "fluid", "header", "icon", "inline", "item", "labeled", "lazyLoad", "loading", "minCharacters", "multiple", "noResultsMessage", "onAddItem", "onBlur", "onChange", "onClick", "onClose", "onFocus", "onLabelClick", "onMouseDown", "onOpen", "onSearchChange", "open", "openOnFocus", "options", "placeholder", "pointing", "renderLabel", "scrolling", "search", "searchInput", "searchQuery", "selectOnBlur", "selectOnNavigation", "selectedLabel", "selection", "simple", "tabIndex", "text", "trigger", "upward", "value", "wrapSelection"]);
Dropdown.propTypes = process.env.NODE_ENV !== "production" ? {
/** An element type to render as (string or function). */
as: _lib.customPropTypes.as,
/** Label prefixed to an option added by a user. */
additionLabel: _propTypes.default.oneOfType([_propTypes.default.element, _propTypes.default.string]),
/** Position of the `Add: ...` option in the dropdown list ('top' or 'bottom'). */
additionPosition: _propTypes.default.oneOf(['top', 'bottom']),
/**
* Allow user additions to the list of options (boolean).
* Requires the use of `selection`, `options` and `sear