calcite-react
Version:
Calcite components for React
542 lines (435 loc) • 24.3 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _react = _interopRequireWildcard(require("react"));
var _reactDom = _interopRequireDefault(require("react-dom"));
var _propTypes = _interopRequireDefault(require("prop-types"));
var _downshift = _interopRequireDefault(require("downshift"));
var _reactVirtualized = require("react-virtualized");
var _reactPopper = require("react-popper");
var _matchSorter = _interopRequireDefault(require("match-sorter"));
var _uniqid = _interopRequireDefault(require("uniqid"));
var _helpers = require("../utils/helpers");
var _SearchStyled = require("./Search-styled");
var _SearchMenu = _interopRequireDefault(require("./SearchMenu"));
var _Menu = require("../Menu");
var _Tooltip = _interopRequireDefault(require("../Tooltip"));
var _List = require("../List/List");
var _MagnifyingGlassIcon = _interopRequireDefault(require("calcite-ui-icons-react/MagnifyingGlassIcon"));
var _XCircleIcon = _interopRequireDefault(require("calcite-ui-icons-react/XCircleIcon"));
var _CalciteThemeProvider = require("../CalciteThemeProvider");
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
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 _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
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); }
function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }
function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; var ownKeys = Object.keys(source); if (typeof Object.getOwnPropertySymbols === 'function') { ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) { return Object.getOwnPropertyDescriptor(source, sym).enumerable; })); } ownKeys.forEach(function (key) { _defineProperty(target, key, source[key]); }); } return target; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); }
function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); }
function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }
function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
var Search =
/*#__PURE__*/
function (_Component) {
_inherits(Search, _Component);
function Search(props) {
var _this;
_classCallCheck(this, Search);
_this = _possibleConstructorReturn(this, _getPrototypeOf(Search).call(this, props));
_defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "itemToString", function (item) {
if (!item) {
return;
}
if (typeof item === 'string') {
return item;
}
if (_this.props.dataSourceConfig) {
if (_react.default.isValidElement(item)) {
if (item.props.item) {
return item.props.item[_this.props.dataSourceConfig.label];
}
return item.props[_this.props.dataSourceConfig.label];
}
return item[_this.props.dataSourceConfig.label];
}
return item.name;
});
_defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "itemToValue", function (item) {
if (!item) {
return;
}
if (typeof item === 'string') {
return item;
}
if (_this.props.dataSourceConfig) {
if (_react.default.isValidElement(item)) {
if (item.props.item) {
return item.props.item[_this.props.dataSourceConfig.value];
}
return item.props[_this.props.dataSourceConfig.value];
}
return item[_this.props.dataSourceConfig.value];
}
return item.value;
});
_defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "handleOnUserAction", function (changes, downshiftProps) {
var selectedItemVal = changes.selectedItem || _this.props.selectedItem || '';
var inputValue = _this.props.inputValue || '';
var newItemsToShow;
var isClosingMenu = changes.hasOwnProperty('isOpen') && !changes.isOpen;
if (changes.type === _downshift.default.stateChangeTypes.keyDownEscape && !isClosingMenu) {
selectedItemVal = '';
}
if (changes.hasOwnProperty('inputValue')) {
if (changes.type === _downshift.default.stateChangeTypes.keyDownEscape) {
inputValue = _this.userInputtedValue || '';
} else {
inputValue = changes.inputValue || '';
_this.userInputtedValue = changes.inputValue;
}
} // If the component is just being used as an input filter
if (!_this.props.children && !_this.props.items) {
// This will be handled in onInputValueChange if IME is being used
if (!_this.isComposing) {
_this.props.onUserAction(changes.inputValue === undefined ? downshiftProps.inputValue : changes.inputValue);
}
return;
} // object or string?
if (!_this.props.remote) {
newItemsToShow = _this.userInputtedValue ? (0, _matchSorter.default)(_this.props.children || _this.props.items, _this.userInputtedValue, {
keys: [_this.itemToString]
}) : _this.props.children || _this.props.items;
if (changes.hasOwnProperty('highlightedIndex') && (changes.type === _downshift.default.stateChangeTypes.keyDownArrowUp || changes.type === _downshift.default.stateChangeTypes.keyDownArrowDown)) {
inputValue = _this.itemToString(newItemsToShow[changes.highlightedIndex]) || '';
}
if (isClosingMenu) {
inputValue = _this.itemToString(selectedItemVal) || '';
_this.userInputtedValue = selectedItemVal;
}
_this.setState({
itemsToShow: newItemsToShow
});
}
_this.props.onUserAction(inputValue, selectedItemVal);
});
_defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "handleOnInputValueChange", function (inputValue, downshiftProps) {
// If IME keyboard is being used, then we need to update the value
// before onUserAction gets called. Otherwise, the IME will close.
if (!_this.isComposing) {
return;
}
var value = inputValue !== undefined ? inputValue : _this.props.inputValue || '';
_this.props.onUserAction(value, downshiftProps.selectedItem || _this.props.selectedItem);
});
_defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "getSearchIcon", function (searchIcon) {
if (searchIcon) {
return _react.default.createElement(_SearchStyled.StyledSearchIconContainer, null, searchIcon);
}
return null;
});
_defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "getClearSearchIcon", function () {
if (_this.props.inputValue || _this.props.selectedItem) {
return _react.default.createElement(_SearchStyled.StyledClearIconContainer, {
onClick: _this.props.onRequestClear
}, _this.props.clearIcon);
}
});
_defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "getShortcutIcon", function () {
if (_this.props.shortcutCharacter && !_this.props.inputValue && !_this.props.selectedItem) {
return _react.default.createElement(_SearchStyled.StyledShortcutCharacter, null, _react.default.createElement(_Tooltip.default, {
title: _this.props.shortcutTooltip,
placement: "left"
}, _this.props.shortcutCharacter));
}
});
_defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "getMenuItems", function (itemsToShow, virtualized, params) {
var getItemProps = params.getItemProps,
highlightedIndex = params.highlightedIndex,
selectedItem = params.selectedItem,
menuHeight = params.menuHeight,
virtualizedRowHeight = params.virtualizedRowHeight,
virtualizedMenuWidth = params.virtualizedMenuWidth;
if (virtualized) {
return _react.default.createElement(_reactVirtualized.List, {
width: virtualizedMenuWidth || 9999,
autoWidth: !virtualizedMenuWidth,
scrollToIndex: highlightedIndex || 0,
scrollToAlignment: "auto",
height: itemsToShow.length < 7 ? itemsToShow.length * virtualizedRowHeight : menuHeight,
rowCount: itemsToShow.length,
rowHeight: virtualizedRowHeight,
rowRenderer: function rowRenderer(_ref) {
var index = _ref.index,
rowRenderStyle = _ref.style;
return _this.getMenuItem(itemsToShow[index], {
getItemProps: getItemProps,
highlightedIndex: highlightedIndex,
index: index,
selectedItem: selectedItem,
rowRenderStyle: rowRenderStyle
});
},
style: {
direction: document && document.documentElement.dir || 'ltr'
}
});
}
return itemsToShow.map(function (item, index) {
return _this.getMenuItem(item, {
getItemProps: getItemProps,
highlightedIndex: highlightedIndex,
index: index,
selectedItem: selectedItem
});
});
});
_defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "getMenuItem", function (item, params) {
var getItemProps = params.getItemProps,
highlightedIndex = params.highlightedIndex,
index = params.index,
selectedItem = params.selectedItem,
rowRenderStyle = params.rowRenderStyle;
if (_react.default.isValidElement(item)) {
return _react.default.cloneElement(item, _objectSpread({}, getItemProps({
style: _objectSpread({}, item.props.style, rowRenderStyle),
item: item.props.item || item.props,
index: index,
active: highlightedIndex === index,
selected: _this.itemToValue(selectedItem) === _this.itemToValue(item),
key: _this.itemToValue(item)
})));
}
return _react.default.createElement(_Menu.MenuItem, getItemProps({
style: _objectSpread({}, rowRenderStyle),
item: item,
index: index,
active: highlightedIndex === index,
selected: selectedItem === item,
key: _this.itemToValue(item)
}), _this.itemToString(item));
});
_defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "getFullWidthStyle", function (fullWidth) {
if (fullWidth) {
return {
minWidth: '100%'
};
}
});
_defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "_getPopper", function (popper, appendToBody) {
if (appendToBody) {
return _reactDom.default.createPortal(popper, document.body);
}
return popper;
});
_this.state = {
itemsToShow: _this.props.children || _this.props.items
};
_this.isComposing = false;
_this.userInputtedValue = '';
_this._generatedId = (0, _uniqid.default)();
return _this;
}
_createClass(Search, [{
key: "componentDidMount",
value: function componentDidMount() {
this.props.inputRef && this.props.inputRef(document.getElementById("".concat(this._generatedId, "Reference")));
}
}, {
key: "render",
value: function render() {
var _this2 = this;
var _this$props = this.props,
minimal = _this$props.minimal,
fullWidth = _this$props.fullWidth,
inputValue = _this$props.inputValue,
selectedItem = _this$props.selectedItem,
onChange = _this$props.onChange,
onUserAction = _this$props.onUserAction,
onRequestClear = _this$props.onRequestClear,
placeholder = _this$props.placeholder,
containerStyle = _this$props.containerStyle,
menuStyle = _this$props.menuStyle,
appendToBody = _this$props.appendToBody,
positionFixed = _this$props.positionFixed,
children = _this$props.children,
virtualized = _this$props.virtualized,
virtualizedRowHeight = _this$props.virtualizedRowHeight,
virtualizedMenuWidth = _this$props.virtualizedMenuWidth,
searchIcon = _this$props.searchIcon,
other = _objectWithoutProperties(_this$props, ["minimal", "fullWidth", "inputValue", "selectedItem", "onChange", "onUserAction", "onRequestClear", "placeholder", "containerStyle", "menuStyle", "appendToBody", "positionFixed", "children", "virtualized", "virtualizedRowHeight", "virtualizedMenuWidth", "searchIcon"]);
var menuHeight = menuStyle && parseInt(menuStyle.height, 10) || menuStyle && parseInt(menuStyle.maxHeight, 10) || 300;
var itemsToShow;
if (this.props.remote) itemsToShow = this.props.items || this.props.children;else itemsToShow = this.state.itemsToShow;
return _react.default.createElement(_List.ListContext.Consumer, null, function (listContext) {
return _react.default.createElement(_SearchStyled.StyledSearchContainer, {
style: containerStyle,
fullWidth: fullWidth,
minimal: minimal
}, _this2.getSearchIcon(searchIcon), _react.default.createElement(_reactPopper.Manager, {
style: _SearchStyled.ManagerStyle
}, _react.default.createElement(_downshift.default, {
itemToString: _this2.itemToString,
inputValue: inputValue,
selectedItem: selectedItem,
onChange: onChange,
onUserAction: _this2.handleOnUserAction,
onInputValueChange: _this2.handleOnInputValueChange
}, function (_ref2) {
var getRootProps = _ref2.getRootProps,
getInputProps = _ref2.getInputProps,
getItemProps = _ref2.getItemProps,
highlightedIndex = _ref2.highlightedIndex,
isOpen = _ref2.isOpen;
return _react.default.createElement(_SearchStyled.StyledSearchInputWrapper, getRootProps({}, {
suppressRefError: true
}), _react.default.createElement(_reactPopper.Reference, {
style: {
display: 'inline-block'
}
}, function (_ref3) {
var ref = _ref3.ref;
return _react.default.createElement(_SearchStyled.StyledSearch, _extends({
as: "input"
}, getInputProps(_objectSpread({
id: "".concat(_this2._generatedId, "Reference"),
placeholder: placeholder,
minimal: minimal,
fullWidth: fullWidth,
ref: ref,
selectableListFilter: listContext.selectable,
searchIcon: searchIcon,
type: 'text',
onKeyDown: function onKeyDown(evt) {
// Prevent downshift key events while IME is open
// IME uses key events for selecting IME options
if (evt.nativeEvent.isComposing) {
evt.nativeEvent.preventDownshiftDefault = true;
}
},
onCompositionStart: function onCompositionStart(evt) {
_this2.isComposing = true;
},
onCompositionEnd: function onCompositionEnd(evt) {
_this2.isComposing = false;
}
}, other))));
}), isOpen && itemsToShow ? _this2._getPopper(_react.default.createElement(_reactPopper.Popper, {
positionFixed: positionFixed,
style: _objectSpread({}, _this2.getFullWidthStyle(fullWidth), _SearchStyled.PopperStyle),
placement: (0, _helpers.rtlPlacement)(_this2.props.placement),
modifiers: _objectSpread({
preventOverflow: {
boundariesElement: positionFixed || appendToBody ? 'window' : 'scrollParent'
}
}, _this2.props.popperModifiers)
}, function (_ref4) {
var ref = _ref4.ref,
style = _ref4.style,
placement = _ref4.placement,
scheduleUpdate = _ref4.scheduleUpdate;
return _react.default.createElement(_SearchMenu.default, {
innerRef: ref,
fullWidth: fullWidth,
style: _objectSpread({}, style, menuStyle),
isOpen: isOpen,
"data-placement": placement,
scheduleUpdate: scheduleUpdate
}, _this2.getMenuItems(itemsToShow, virtualized, {
highlightedIndex: highlightedIndex,
menuHeight: menuHeight,
virtualizedRowHeight: virtualizedRowHeight,
virtualizedMenuWidth: virtualizedMenuWidth,
getItemProps: getItemProps,
selectedItem: selectedItem
}));
}), appendToBody) : null);
})), _this2.getShortcutIcon(), _this2.getClearSearchIcon());
});
}
}]);
return Search;
}(_react.Component);
Search.propTypes = {
/** Array of items. */
items: _propTypes.default.array,
/** Text in the input. */
inputValue: _propTypes.default.string,
/** The selected item. */
selectedItem: _propTypes.default.any,
/** Text for the placeholder property on the input. */
placeholder: _propTypes.default.string,
/** An object used to map properties to the name and value of items (only applies if `items` is an array of objects). */
dataSourceConfig: _propTypes.default.object,
/** Function called when an item is selected. */
onChange: _propTypes.default.func,
/** Function called when the input value is changed, items dropdown hides/shows, or when the user hovers over or clicks on an item. */
onUserAction: _propTypes.default.func,
/** Function called when the user clicks the clear button. */
onRequestClear: _propTypes.default.func,
/** Placement of the popover in relation to the target. */
placement: _propTypes.default.oneOf(['top', 'right', 'bottom', 'left', 'top-start', 'right-start', 'bottom-start', 'left-start', 'top-end', 'right-end', 'bottom-end', 'left-end']),
/** Toggle minimal style on the input. */
minimal: _propTypes.default.bool,
/** Whether or not the Search and its menu will fill the container's width. */
fullWidth: _propTypes.default.bool,
/** Style prop applied to the Search container element. */
containerStyle: _propTypes.default.object,
/** Style prop applied to the menu wrapper. */
menuStyle: _propTypes.default.object,
/** Character used to display a shortcut icon on the right side of the Search input. */
shortcutCharacter: _propTypes.default.string,
/** Text used in the shortcut tooltip to describe what the shortcut is. */
shortcutTooltip: _propTypes.default.node,
/** Uses `position: fixed` on the tooltip allowing it to show up outside of containers that have `overflow: hidden`. */
positionFixed: _propTypes.default.bool,
/** You can add Search options as children if you want more control over the item rendering. Search MenuItems can take either an item object that maps to your dataSourceConfig, or you can manually set the label and value props on MenuItems. */
children: _propTypes.default.node,
/** Use react-virtualized to render rows as the user scrolls. */
virtualized: _propTypes.default.bool,
/** (virtualized only) Row height used to calculate how many rows to render in a virtualized menu. */
virtualizedRowHeight: _propTypes.default.oneOfType([_propTypes.default.number, _propTypes.default.func]),
/** (virtualized only) Width of the menu; unloaded rows may be wider than the initial set. */
virtualizedMenuWidth: _propTypes.default.number,
/** SVG icon to be displayed inside the Search input */
searchIcon: _propTypes.default.node,
/** SVG icon to clear the value of the Search input */
clearIcon: _propTypes.default.node,
/** Use a remote API for the data load. This will allow the application to see the exact return from the API with no filtering applied */
remote: _propTypes.default.bool,
/** Modifiers to be passed to the Popper element */
popperModifiers: _propTypes.default.object
};
Search.defaultProps = {
placeholder: 'Search...',
dataSourceConfig: {
label: 'label',
value: 'value'
},
placement: 'bottom-start',
shortcutTooltip: 'Press / to search',
virtualizedRowHeight: 42,
searchIcon: _react.default.createElement(_MagnifyingGlassIcon.default, {
filled: true,
size: 16
}),
clearIcon: _react.default.createElement(_XCircleIcon.default, {
filled: true,
size: 16,
color: _CalciteThemeProvider.CalciteTheme.palette.darkerGray
}),
onUserAction: function onUserAction() {},
onChange: function onChange() {},
onRequestClear: function onRequestClear() {},
remote: false
};
Search.displayName = 'Search';
var _default = Search;
exports.default = _default;