UNPKG

wix-style-react

Version:
458 lines (456 loc) 21.2 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); exports.__esModule = true; exports.default = void 0; var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty")); var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends")); var _objectWithoutProperties2 = _interopRequireDefault(require("@babel/runtime/helpers/objectWithoutProperties")); var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator")); var _react = _interopRequireWildcard(require("react")); var _propTypes = _interopRequireDefault(require("prop-types")); var _InputWithOptions = _interopRequireDefault(require("../InputWithOptions")); var _wixUiIconsCommon = require("@wix/wix-ui-icons-common"); var _StringUtils = require("../utils/StringUtils"); var _SearchSt = require("./Search.st.css"); var _Input = _interopRequireDefault(require("../Input/Input")); var _DropdownLayout = require("../DropdownLayout/DropdownLayout"); var _WixStyleReactDefaultsOverrideProvider = require("../WixStyleReactDefaultsOverrideProvider"); var _excluded = ["defaultValue", "dataHook", "expandWidth", "highlight"]; var _jsxFileName = "/home/builduser/work/a9c1ac8876d5057c/packages/wix-style-react/dist/cjs/Search/Search.js"; function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t in e) "default" !== _t && {}.hasOwnProperty.call(e, _t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t)) && (i.get || i.set) ? o(f, _t, i) : f[_t] = e[_t]); return f; })(e, t); } function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } // 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(() => fn.apply(context, args), wait); }; } /** * Search component with suggestions based on input value listed in dropdown */ class Search extends _react.Component { constructor(props) { var _this; super(props); _this = this; this.searchInput = /*#__PURE__*/_react.default.createRef(); /** * Creates an onChange debounced function */ this._createDebouncedOnChange = () => { var { debounceMs, onChange } = this.props; return debounceMs > 0 ? debounce(onChange, debounceMs) : onChange; }; this._getIsControlled = () => 'value' in this.props && 'onChange' in this.props; this._getFilteredOptions = () => { var { options, predicate } = this.props; var searchText = this._currentValue(); if (!searchText || !searchText.length) { return options; } var filterFn = predicate || this._stringFilter; return options.filter(filterFn); }; this._stringFilter = option => { var searchText = this._currentValue(); return _StringUtils.StringUtils.includesCaseInsensitive(option.value, searchText.trim()); }; this._onChange = e => { e.persist(); this.setState({ inputValue: e.target.value }, () => { this._onChangeHandler(e); }); }; this._onClear = event => { var { expandable } = this.props; var { collapsed } = this.state; 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, () => { this._onClearHandler(event); }); }; this._onClearHandler = event => { var { onClear } = this.props; if (onClear) onClear(event); }; this._currentValue = () => this.state.inputValue; this._onFocus = event => { var { onFocus } = this.props; if (this.state.collapsed && this.props.expandable) { this.setState({ collapsed: false }); } onFocus && onFocus(event); }; this._onBlur = /*#__PURE__*/function () { var _ref = (0, _asyncToGenerator2.default)(function* (event) { var { onBlur } = _this.props; onBlur && (yield onBlur(event)); if (!_this.state.collapsed && _this.props.expandable) { var value = _this._currentValue(); if (value === '') { _this.setState({ collapsed: true }); } } }); return function (_x) { return _ref.apply(this, arguments); }; }(); this._onWrapperClick = () => { if (!this.props.expandable || this.props.expandable && this.state.collapsed) { this.searchInput.current && this.searchInput.current.focus(); } }; this._onWrapperMouseDown = 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(); } } }; /** * Sets focus on the input element */ this.focus = () => { this.searchInput.current && this.searchInput.current.focus(); }; /** * Removes focus on the input element */ this.blur = () => { this.searchInput.current && this.searchInput.current.blur(); }; /** * Clears the input. * * @param event */ this.clear = event => { this.searchInput.current && this.searchInput.current.clear(event); }; var initialValue = this._getIsControlled() ? props.value : props.defaultValue || ''; this._onChangeHandler = this._createDebouncedOnChange(); this.state = { inputValue: initialValue, collapsed: props.expandable && !initialValue && !props.autoFocus }; } componentDidUpdate(prevProps) { var { value, expandable } = this.props; if (prevProps.value !== value) { // Collapse the expandable input when the input has no focus & no value in it. var collapsed = expandable && this.searchInput.current && !this.searchInput.current._focused && prevProps.value && !value ? true : this.props.collapsed; this.setState({ inputValue: value, collapsed }); } if (prevProps.debounceMs !== this.props.debounceMs || prevProps.onChange !== this.props.onChange) { this._onChangeHandler = this._createDebouncedOnChange(); } } render() { var { input: inputPropsDefaults } = this.context; var _this$props = this.props, { defaultValue, dataHook, expandWidth, highlight } = _this$props, restProps = (0, _objectWithoutProperties2.default)(_this$props, _excluded); var { expandable, size = inputPropsDefaults.size } = restProps; var { collapsed, inputValue } = this.state; var contentStyle = expandable && !collapsed ? { width: expandWidth } : undefined; return /*#__PURE__*/_react.default.createElement("div", { "data-hook": dataHook, className: (0, _SearchSt.st)(_SearchSt.classes.root, { expandable, expanded: expandable && collapsed, size }), onClick: this._onWrapperClick, onMouseDown: this._onWrapperMouseDown, "data-expandable": expandable || null, "data-collapsed": expandable && collapsed || null, __self: this, __source: { fileName: _jsxFileName, lineNumber: 478, columnNumber: 7 } }, /*#__PURE__*/_react.default.createElement("div", { className: _SearchSt.classes.content, style: contentStyle, __self: this, __source: { fileName: _jsxFileName, lineNumber: 490, columnNumber: 9 } }, /*#__PURE__*/_react.default.createElement(_InputWithOptions.default, (0, _extends2.default)({}, restProps, { value: inputValue, ref: this.searchInput, prefix: /*#__PURE__*/_react.default.createElement(_Input.default.IconAffix, { __self: this, __source: { fileName: _jsxFileName, lineNumber: 496, columnNumber: 15 } }, size === 'small' ? /*#__PURE__*/_react.default.createElement(_wixUiIconsCommon.SearchSmall, { __self: this, __source: { fileName: _jsxFileName, lineNumber: 497, columnNumber: 37 } }) : /*#__PURE__*/_react.default.createElement(_wixUiIconsCommon.Search, { __self: this, __source: { fileName: _jsxFileName, lineNumber: 497, columnNumber: 55 } })), 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, __self: this, __source: { fileName: _jsxFileName, lineNumber: 491, columnNumber: 11 } })))); } } Search.contextType = _WixStyleReactDefaultsOverrideProvider.WixStyleReactDefaultsOverrideContext; Search.displayName = 'Search'; 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, /** Control the border style of input */ border: _propTypes.default.oneOf(['standard', 'round', 'bottomLine', 'none']), /** 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, /** 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 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 lazy loading of the dropdown layout items is enabled */ infiniteScroll: _propTypes.default.bool, /** Defines a callback function which is called on a request to render more list items */ loadMore: _propTypes.default.func, /** 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 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, /** 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]) }; Search.defaultProps = _objectSpread(_objectSpread({}, _InputWithOptions.default.defaultProps), {}, { clearButton: true, placeholder: 'Search', expandable: false, expandWidth: '100%', debounceMs: 0, onChange: () => {}, highlight: true, border: 'round' }); var _default = exports.default = Search; //# sourceMappingURL=Search.js.map