UNPKG

wix-style-react

Version:
564 lines (431 loc) 25.7 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); var _typeof = require("@babel/runtime/helpers/typeof"); Object.defineProperty(exports, "__esModule", { value: true }); exports["default"] = void 0; var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends")); var _objectWithoutProperties2 = _interopRequireDefault(require("@babel/runtime/helpers/objectWithoutProperties")); var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator")); var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator")); var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck")); var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass")); var _assertThisInitialized2 = _interopRequireDefault(require("@babel/runtime/helpers/assertThisInitialized")); var _inherits2 = _interopRequireDefault(require("@babel/runtime/helpers/inherits")); var _possibleConstructorReturn2 = _interopRequireDefault(require("@babel/runtime/helpers/possibleConstructorReturn")); var _getPrototypeOf2 = _interopRequireDefault(require("@babel/runtime/helpers/getPrototypeOf")); var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty")); var _react = _interopRequireWildcard(require("react")); var _propTypes = _interopRequireDefault(require("prop-types")); var _InputWithOptions = _interopRequireDefault(require("../InputWithOptions")); var _Search = _interopRequireDefault(require("wix-ui-icons-common/Search")); var _StringUtils = require("../utils/StringUtils"); var _SearchSt = require("./Search.st.css"); var _Input = _interopRequireDefault(require("../Input/Input")); var _DropdownLayout = require("../DropdownLayout/DropdownLayout"); var _excluded = ["defaultValue", "dataHook", "expandWidth", "highlight"]; function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); } function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || _typeof(obj) !== "object" && typeof obj !== "function") { return { "default": obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj["default"] = obj; if (cache) { cache.set(obj, newObj); } return newObj; } function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) { symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); } keys.push.apply(keys, symbols); } return keys; } function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { (0, _defineProperty2["default"])(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = (0, _getPrototypeOf2["default"])(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = (0, _getPrototypeOf2["default"])(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return (0, _possibleConstructorReturn2["default"])(this, result); }; } function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } // because lodash debounce is not compatible with jest timeout mocks function debounce(fn, wait) { var timeout; return function () { for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { args[_key] = arguments[_key]; } var context = this; clearTimeout(timeout); timeout = setTimeout(function () { return fn.apply(context, args); }, wait); }; } /** * Search component with suggestions based on input value listed in dropdown */ var Search = /*#__PURE__*/function (_Component) { (0, _inherits2["default"])(Search, _Component); var _super = _createSuper(Search); function Search(props) { var _this; (0, _classCallCheck2["default"])(this, Search); _this = _super.call(this, props); (0, _defineProperty2["default"])((0, _assertThisInitialized2["default"])(_this), "searchInput", /*#__PURE__*/_react["default"].createRef()); (0, _defineProperty2["default"])((0, _assertThisInitialized2["default"])(_this), "_createDebouncedOnChange", function () { var _this$props = _this.props, debounceMs = _this$props.debounceMs, onChange = _this$props.onChange; return debounceMs > 0 ? debounce(onChange, debounceMs) : onChange; }); (0, _defineProperty2["default"])((0, _assertThisInitialized2["default"])(_this), "_getIsControlled", function () { return 'value' in _this.props && 'onChange' in _this.props; }); (0, _defineProperty2["default"])((0, _assertThisInitialized2["default"])(_this), "_getFilteredOptions", function () { var _this$props2 = _this.props, options = _this$props2.options, predicate = _this$props2.predicate; var searchText = _this._currentValue(); if (!searchText || !searchText.length) { return options; } var filterFn = predicate || _this._stringFilter; return options.filter(filterFn); }); (0, _defineProperty2["default"])((0, _assertThisInitialized2["default"])(_this), "_stringFilter", function (option) { var searchText = _this._currentValue(); return _StringUtils.StringUtils.includesCaseInsensitive(option.value, searchText.trim()); }); (0, _defineProperty2["default"])((0, _assertThisInitialized2["default"])(_this), "_onChange", function (e) { e.persist(); _this.setState({ inputValue: e.target.value }, function () { _this._onChangeHandler(e); }); }); (0, _defineProperty2["default"])((0, _assertThisInitialized2["default"])(_this), "_onClear", function (event) { var expandable = _this.props.expandable; var collapsed = _this.state.collapsed; var stateChanges = {}; if (!_this._getIsControlled()) { stateChanges.inputValue = ''; } if (expandable && !collapsed && _this._currentValue === '') { stateChanges.collapsed = true; _this.searchInput.current && _this.searchInput.current.blur(); } _this.setState(stateChanges, function () { _this._onClearHandler(event); }); }); (0, _defineProperty2["default"])((0, _assertThisInitialized2["default"])(_this), "_onClearHandler", function (event) { var onClear = _this.props.onClear; if (onClear) onClear(event); }); (0, _defineProperty2["default"])((0, _assertThisInitialized2["default"])(_this), "_currentValue", function () { return _this.state.inputValue; }); (0, _defineProperty2["default"])((0, _assertThisInitialized2["default"])(_this), "_onFocus", function (event) { var onFocus = _this.props.onFocus; if (_this.state.collapsed && _this.props.expandable) { _this.setState({ collapsed: false }); } onFocus && onFocus(event); }); (0, _defineProperty2["default"])((0, _assertThisInitialized2["default"])(_this), "_onBlur", /*#__PURE__*/function () { var _ref = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee(event) { var onBlur, value; return _regenerator["default"].wrap(function _callee$(_context) { while (1) { switch (_context.prev = _context.next) { case 0: onBlur = _this.props.onBlur; _context.t0 = onBlur; if (!_context.t0) { _context.next = 5; break; } _context.next = 5; return onBlur(event); case 5: if (!_this.state.collapsed && _this.props.expandable) { value = _this._currentValue(); if (value === '') { _this.setState({ collapsed: true }); } } case 6: case "end": return _context.stop(); } } }, _callee); })); return function (_x) { return _ref.apply(this, arguments); }; }()); (0, _defineProperty2["default"])((0, _assertThisInitialized2["default"])(_this), "_onWrapperClick", function () { if (!_this.props.expandable || _this.props.expandable && _this.state.collapsed) { _this.searchInput.current && _this.searchInput.current.focus(); } }); (0, _defineProperty2["default"])((0, _assertThisInitialized2["default"])(_this), "_onWrapperMouseDown", function (e) { // We need to capture mouse down and prevent it's event if the input // is already open if (_this.props.expandable && !_this.state.collapsed) { var value = _this._currentValue(); if (value === '') { e.preventDefault(); } } }); var initialValue = _this._getIsControlled() ? props.value : props.defaultValue || ''; _this._onChangeHandler = _this._createDebouncedOnChange(); _this.state = { inputValue: initialValue, collapsed: props.expandable && !initialValue && !props.autoFocus }; return _this; } (0, _createClass2["default"])(Search, [{ key: "componentDidUpdate", value: function componentDidUpdate(prevProps) { if (prevProps.value !== this.props.value) { this.setState({ inputValue: this.props.value }); } if (prevProps.debounceMs !== this.props.debounceMs || prevProps.onChange !== this.props.onChange) { this._onChangeHandler = this._createDebouncedOnChange(); } } /** * Creates an onChange debounced function */ }, { key: "render", value: function render() { var _this$props3 = this.props, defaultValue = _this$props3.defaultValue, dataHook = _this$props3.dataHook, expandWidth = _this$props3.expandWidth, highlight = _this$props3.highlight, restProps = (0, _objectWithoutProperties2["default"])(_this$props3, _excluded); var expandable = restProps.expandable, size = restProps.size; var _this$state = this.state, collapsed = _this$state.collapsed, inputValue = _this$state.inputValue; var contentStyle = expandable && !collapsed ? { width: expandWidth } : undefined; return /*#__PURE__*/_react["default"].createElement("div", { "data-hook": dataHook, className: (0, _SearchSt.st)(_SearchSt.classes.root, { expandable: expandable, expanded: expandable && collapsed, size: size }), onClick: this._onWrapperClick, onMouseDown: this._onWrapperMouseDown, "data-expandable": expandable || null, "data-collapsed": expandable && collapsed || null }, /*#__PURE__*/_react["default"].createElement("div", { className: _SearchSt.classes.content, style: contentStyle }, /*#__PURE__*/_react["default"].createElement(_InputWithOptions["default"], (0, _extends2["default"])({}, restProps, { value: inputValue, ref: this.searchInput, prefix: /*#__PURE__*/_react["default"].createElement(_Input["default"].IconAffix, null, /*#__PURE__*/_react["default"].createElement(_Search["default"], null)), dataHook: "search-inputwithoptions", menuArrow: false, closeOnSelect: true, options: this._getFilteredOptions(), onClear: restProps.clearButton ? this._onClear : undefined, onChange: this._onChange, onFocus: this._onFocus, onBlur: this._onBlur, highlight: highlight })))); } }]); return Search; }(_react.Component); (0, _defineProperty2["default"])(Search, "displayName", 'Search'); (0, _defineProperty2["default"])(Search, "propTypes", { /** Associate a control with the regions that it controls */ ariaControls: _propTypes["default"].string, /** Associate a region with its descriptions. Similar to aria-controls but instead associating descriptions to the region and description identifiers are separated with a space. */ ariaDescribedby: _propTypes["default"].string, /** Define a string that labels the current element in case where a text label is not visible on the screen */ ariaLabel: _propTypes["default"].string, /** Sets the value of native autocomplete attribute (consult the [HTML spec](https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#attr-fe-autocomplete) for possible values) */ autocomplete: _propTypes["default"].string, /** Focus the element on mount (standard React input autoFocus) */ autoFocus: _propTypes["default"].bool, /** Select the entire text of the element on focus (standard React input autoSelect) */ autoSelect: _propTypes["default"].bool, /** Control the border style of input */ border: _propTypes["default"].oneOf(['standard', 'round', 'bottomLine']), /** Specifies a CSS class name to be appended to the component’s root element */ className: _propTypes["default"].string, /** Displays clear button (X) on a non-empty input */ clearButton: _propTypes["default"].bool, /** Closes DropdownLayout when option is selected */ closeOnSelect: _propTypes["default"].bool, /** Render a custom input component instead of the default html input tag */ customInput: _propTypes["default"].node, /** Applies a data-hook HTML attribute that can be used in the tests */ dataHook: _propTypes["default"].string, /** Specifies the `onChange` debounce in milliseconds */ debounceMs: _propTypes["default"].number, /** Defines the initial value of an input for those who want to use this component un-controlled */ defaultValue: _propTypes["default"].string, /** Specifies whether the input should be disabled or not */ disabled: _propTypes["default"].bool, /** Restricts input editing */ disableEditing: _propTypes["default"].bool, /** Sets the offset of the dropdown from the left in pixels */ dropdownOffsetLeft: _propTypes["default"].string, /** Sets the width of the dropdown in pixels */ dropdownWidth: _propTypes["default"].string, /** Specifies whether to collapse input to search icon only. Once clicked, icon will expand to a full search input. */ expandable: _propTypes["default"].bool, /** Specifies the width of an input in an expanded state */ expandWidth: _propTypes["default"].oneOfType([_propTypes["default"].string, _propTypes["default"].number]), /** USED FOR TESTING - forces focus state on the input */ forceFocus: _propTypes["default"].bool, /** USED FOR TESTING - forces hover state on the input */ forceHover: _propTypes["default"].bool, /** Specifies whether there are more items to be loaded */ hasMore: _propTypes["default"].bool, /** Specifies whether the status suffix should be hidden */ hideStatusSuffix: _propTypes["default"].bool, /** Highlight word parts that match search criteria in bold */ highlight: _propTypes["default"].bool, /** Assigns an unique identifier for the root element */ id: _propTypes["default"].string, /** Specifies whether `<DropdownLayout/>` is in a container component. If true, some styles such as shadows, positioning and padding will be added to the component contentContainer. */ inContainer: _propTypes["default"].bool, /** Specifies whether lazy loading of the dropdown layout items is enabled */ infiniteScroll: _propTypes["default"].bool, /** Allows to render a custom input component instead of the default `<Input/>` */ inputElement: _propTypes["default"].element, /** Defines a callback function which is called on a request to render more list items */ loadMore: _propTypes["default"].func, /** Sets a maximum value of an input. Similar to HTML5 max attribute. */ max: _propTypes["default"].number, /** Sets the maximum height of the `dropdownLayout` in pixels */ maxHeightPixels: _propTypes["default"].oneOfType([_propTypes["default"].string, _propTypes["default"].number]), /** Sets the maximum number of characters that can be entered into a field */ maxLength: _propTypes["default"].number, /** Sets a minimum value of an input. Similar to HTML5 min attribute */ min: _propTypes["default"].number, /** Sets the minimum width of dropdownLayout in pixels */ minWidthPixels: _propTypes["default"].oneOfType([_propTypes["default"].string, _propTypes["default"].number]), /** Reference element data when a form is submitted */ name: _propTypes["default"].string, /** Specifies whether input shouldn’t have rounded corners on its left */ noLeftBorderRadius: _propTypes["default"].bool, /** Specifies whether input shouldn’t have rounded corners on its right */ noRightBorderRadius: _propTypes["default"].bool, /** Defines a standard input `onBlur` callback */ onBlur: _propTypes["default"].func, /** Defines a standard input `onChange` callback */ onChange: _propTypes["default"].func, /** Displays clear button (X) on a non-empty input and calls a callback function with no arguments */ onClear: _propTypes["default"].func, /** Defines a callback function which is called whenever the user presses the escape key */ onClose: _propTypes["default"].func, /** Defines a callback function called on `compositionstart`/`compositionend` events */ onCompositionChange: _propTypes["default"].func, /** Defines a callback handler that is called when the presses -enter- */ onEnterPressed: _propTypes["default"].func, /** Defines a callback handler that is called when the user presses -escape- */ onEscapePressed: _propTypes["default"].func, /** Defines a standard input `onFocus` callback */ onFocus: _propTypes["default"].func, /** Defines a standard input `onClick` callback */ onInputClicked: _propTypes["default"].func, /** Defines a standard input `onKeyDown` callback */ onKeyDown: _propTypes["default"].func, /** Defines a standard input `onKeyUp` callback */ onKeyUp: _propTypes["default"].func, /** Defines a callback function which is called when the user performs a submit action. Submit action triggers are: * "Enter", "Tab", [typing any defined delimiters], paste action. * `onManuallyInput(values: Array<string>): void` - the array of strings is the result of splitting the input value by the given delimiters */ onManuallyInput: _propTypes["default"].func, /** Defines a callback function which is called whenever the user enters dropdown layout with the mouse cursor */ onMouseEnter: _propTypes["default"].func, /** Defines a callback function which is called whenever the user exits from dropdown layout with a mouse cursor */ onMouseLeave: _propTypes["default"].func, /** Defines a callback function which is called whenever an option becomes focused (hovered/active). Receives the relevant option object from the original `props.options array`. */ onOptionMarked: _propTypes["default"].func, /** Defines a callback function which is called when options dropdown is hidden */ onOptionsHide: _propTypes["default"].func, /** Defines a callback function which is called when the options dropdown is shown */ onOptionsShow: _propTypes["default"].func, /** Defines a callback handler that is called when user pastes text from a clipboard (using mouse or keyboard shortcut) */ onPaste: _propTypes["default"].func, /** Defines a callback function which is called whenever user selects a different option in the list */ onSelect: _propTypes["default"].func, /** Array of objects: * - `id <string / number>` *required*: the id of the option, should be unique; * - value `<function / string / node>` *required*: can be a string, react element or a builder function; * - disabled `<bool>` *default value- false*: whether this option is disabled or not; * - linkTo `<string>`: when provided the option will be an anchor to the given value; * - title `<bool>` *default value- false* **deprecated**: please use `listItemSectionBuilder` for rendering a title; * - overrideStyle `<bool>` *default value- false* **deprecated**: please use `overrideOptionStyle` for override option styles; * - overrideOptionStyle `<bool>` *default value- false* - when set to `true`, the option will be responsible for its own styles. No styles will be applied from the DropdownLayout itself; * - label `<string>`: the string displayed within an input when the option is selected. This is used when using `<DropdownLayout/>` with an `<Input/>`. */ options: _propTypes["default"].arrayOf(_DropdownLayout.optionValidator), /** Handles container overflow */ overflow: _propTypes["default"].string, /** Sets a pattern that the typed value must match to be valid (regex) */ pattern: _propTypes["default"].string, /** Sets a placeholder message to display */ placeholder: _propTypes["default"].string, /** Allows to pass common popover props. Check `<Popover/>` API for a full list. */ popoverProps: _propTypes["default"].shape({ appendTo: _propTypes["default"].oneOf(['window', 'scrollParent', 'parent', 'viewport']), maxWidth: _propTypes["default"].oneOfType([_propTypes["default"].string, _propTypes["default"].number]), minWidth: _propTypes["default"].oneOfType([_propTypes["default"].string, _propTypes["default"].number]), flip: _propTypes["default"].bool, fixed: _propTypes["default"].bool, placement: _propTypes["default"].oneOf(['auto-start', 'auto', 'auto-end', 'top-start', 'top', 'top-end', 'right-start', 'right', 'right-end', 'bottom-end', 'bottom', 'bottom-start', 'left-end', 'left', 'left-start']), dynamicWidth: _propTypes["default"].bool }), /** Defines a custom function for options filtering */ predicate: _propTypes["default"].func, /** Specifies whether input is read only */ readOnly: _propTypes["default"].bool, /** Specifies that an input must be filled out before submitting the form */ required: _propTypes["default"].bool, /** Flip the component horizontally so it’s more suitable for RTL */ rtl: _propTypes["default"].bool, /** Specifies whether the selected option will be highlighted when the dropdown is reopened */ selectedHighlight: _propTypes["default"].bool, /** Specifies selected option by its id */ selectedId: _propTypes["default"].oneOfType([_propTypes["default"].string, _propTypes["default"].number]), /** Controls whether to show options if input is empty */ showOptionsIfEmptyInput: _propTypes["default"].bool, /** Controls the size of the input */ size: _propTypes["default"].oneOf(['small', 'medium', 'large']), /** Specify the status of a field */ status: _propTypes["default"].oneOf(['error', 'warning', 'loading']), /** Defines the message to display on status icon hover. If not given or empty there will be no tooltip. */ statusMessage: _propTypes["default"].node, /** Indicates that element can be focused and where it participates in sequential keyboard navigation */ tabIndex: _propTypes["default"].number, /** Handles text overflow behavior. It can either `clip` (default) or display `ellipsis`. */ textOverflow: _propTypes["default"].string, /** Controls the placement of a status tooltip */ tooltipPlacement: _propTypes["default"].string, /** Specifies the type of `<input>` element to display. The default type is text. */ type: _propTypes["default"].string, /** Specifies the current value of the element */ value: _propTypes["default"].oneOfType([_propTypes["default"].string, _propTypes["default"].number]) }); (0, _defineProperty2["default"])(Search, "defaultProps", _objectSpread(_objectSpread({}, _InputWithOptions["default"].defaultProps), {}, { clearButton: true, placeholder: 'Search', expandable: false, expandWidth: '100%', debounceMs: 0, onChange: function onChange() {}, highlight: true, border: 'round' })); var _default = Search; exports["default"] = _default;