UNPKG

@react-awesome-query-builder/antd

Version:
349 lines (344 loc) 15.3 kB
import _extends from "@babel/runtime/helpers/extends"; import _asyncToGenerator from "@babel/runtime/helpers/asyncToGenerator"; import _regeneratorRuntime from "@babel/runtime/regenerator"; function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t["return"] || t["return"](); } finally { if (u) throw o; } } }; } function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } } function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; } import React, { useMemo, useCallback } from "react"; import { Select, Divider, Tooltip } from "antd"; import { calcTextWidth, SELECT_WIDTH_OFFSET_RIGHT } from "../../utils/domUtils"; import { Hooks, Utils } from "@react-awesome-query-builder/ui"; var fixListValuesGroupOrder = Utils.Autocomplete.fixListValuesGroupOrder; var makeCustomListValue = Utils.ListUtils.makeCustomListValue; var useListValuesAutocomplete = Hooks.useListValuesAutocomplete; // see type ListItem var mapListItemToOptionKeys = { value: "value", title: "label", groupTitle: "grouplabel" }; export default (function (props) { var _config$settings$load, _config$settings$notF; var config = props.config, placeholder = props.placeholder, allowCustomValues = props.allowCustomValues, customProps = props.customProps, value = props.value, readonly = props.readonly, multiple = props.multiple, useAsyncSearch = props.useAsyncSearch; // hook var _useListValuesAutocom = useListValuesAutocomplete(props, { debounceTimeout: 100, multiple: multiple, uif: "antd" }), open = _useListValuesAutocom.open, onDropdownVisibleChange = _useListValuesAutocom.onDropdownVisibleChange, onChange = _useListValuesAutocom.onChange, isSpecialValue = _useListValuesAutocom.isSpecialValue, onSearch = _useListValuesAutocom.onSearch, inputValue = _useListValuesAutocom.inputValue, options = _useListValuesAutocom.options, isInitialLoading = _useListValuesAutocom.isInitialLoading, isLoading = _useListValuesAutocom.isLoading, aPlaceholder = _useListValuesAutocom.aPlaceholder, extendOptions = _useListValuesAutocom.extendOptions, getOptionDisabled = _useListValuesAutocom.getOptionDisabled, getOptionIsCustom = _useListValuesAutocom.getOptionIsCustom, getOptionLabel = _useListValuesAutocom.getOptionLabel, onInputChange = _useListValuesAutocom.onInputChange; var filteredOptions = extendOptions(options); // To colorize custom options if (multiple && allowCustomValues && value !== null && value !== void 0 && value.length) { var _iterator = _createForOfIteratorHelper(value), _step; try { var _loop = function _loop() { var v = _step.value; if (getOptionIsCustom(v) && !options.find(function (_ref) { var value = _ref.value; return value === v; })) { filteredOptions.push(makeCustomListValue(v)); } }; for (_iterator.s(); !(_step = _iterator.n()).done;) { _loop(); } } catch (err) { _iterator.e(err); } finally { _iterator.f(); } } var optionsMaxWidth = useMemo(function () { return filteredOptions.reduce(function (max, option) { return Math.max(max, calcTextWidth(option.title, null)); }, 0); }, [options]); var _config$settings = config.settings, defaultSelectWidth = _config$settings.defaultSelectWidth, defaultSearchWidth = _config$settings.defaultSearchWidth, renderSize = _config$settings.renderSize; var placeholderWidth = calcTextWidth(placeholder); var isEmptyValue = multiple ? !(value !== null && value !== void 0 && value.length) : !value; var aValue = !isEmptyValue ? value : multiple ? [] : undefined; var width = aValue || !placeholderWidth ? null : placeholderWidth + SELECT_WIDTH_OFFSET_RIGHT; var dropdownWidth = optionsMaxWidth && !isNaN(optionsMaxWidth) ? optionsMaxWidth + SELECT_WIDTH_OFFSET_RIGHT : null; var minWidth = width || defaultSelectWidth; var isClearAllClicked = React.useRef(false); var style = { width: multiple ? undefined : minWidth, minWidth: minWidth }; var dropdownStyle = { width: dropdownWidth }; var mode = !multiple ? undefined : (customProps === null || customProps === void 0 ? void 0 : customProps.mode) || (allowCustomValues && customProps !== null && customProps !== void 0 && customProps.tokenSeparators ? "tags" : "multiple"); var dynamicPlaceholder = !readonly ? aPlaceholder : ""; var nestByGroup = function nestByGroup(opts) { var fix = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; var newOpts = opts; if (fix) { newOpts = fixListValuesGroupOrder(newOpts); } var nestedOpts = []; var _iterator2 = _createForOfIteratorHelper(newOpts), _step2; try { for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) { var o = _step2.value; var groupTitle = o.groupTitle; delete o.groupTitle; if (groupTitle) { o.grouplabel = groupTitle; var targetGroup = void 0; var lastO = nestedOpts[nestedOpts.length - 1]; if (lastO !== null && lastO !== void 0 && lastO.options && lastO.label === groupTitle) { targetGroup = lastO; } else { targetGroup = { label: groupTitle, options: [] }; nestedOpts.push(targetGroup); } targetGroup.options.push(o); } else { nestedOpts.push(o); } } } catch (err) { _iterator2.e(err); } finally { _iterator2.f(); } return nestedOpts; }; var renderOptionLabel = function renderOptionLabel(option) { var tooltip = option.tooltip; var label = getOptionLabel(option); if (tooltip) { label = /*#__PURE__*/React.createElement(Tooltip, { title: tooltip }, label); } return label; }; // rendering special 'Load more' option has side effect: on change rc-select will save its title as internal value in own state var optionsToRender = nestByGroup(filteredOptions === null || filteredOptions === void 0 ? void 0 : filteredOptions.filter(function (option) { return !option.specialValue; }).map(function (option) { return { label: getOptionIsCustom(option) ? /*#__PURE__*/React.createElement("span", { className: "customSelectOption" }, getOptionLabel(option)) : renderOptionLabel(option), value: option.value, groupTitle: option.groupTitle, disabled: getOptionDisabled(option) }; })); var onSpecialClick = function onSpecialClick(specialValue) { return function () { var option = filteredOptions.find(function (opt) { return opt.specialValue == specialValue; }); onChange(null, specialValue, option); }; }; var specialOptions = filteredOptions === null || filteredOptions === void 0 ? void 0 : filteredOptions.filter(function (option) { return !!option.specialValue; }).map(function (option) { return /*#__PURE__*/React.createElement("a", { style: { padding: "5px 10px", display: "block", cursor: "pointer" }, key: option.specialValue, disabled: getOptionDisabled(option), onClick: onSpecialClick(option.specialValue) }, getOptionLabel(option)); }); var aOnSelect = /*#__PURE__*/function () { var _ref2 = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime.mark(function _callee(newValue, option) { var isAutoTokenization; return _regeneratorRuntime.wrap(function _callee$(_context) { while (1) switch (_context.prev = _context.next) { case 0: // For both multiple/single `newValue` is string, `option` is {value, children} // ! Custom option is always `{}` isAutoTokenization = multiple && !!option.props && !(option.value !== undefined || option.label !== undefined); if (!isAutoTokenization) { _context.next = 3; break; } return _context.abrupt("return"); case 3: if (!isSpecialValue(option)) { _context.next = 8; break; } _context.next = 6; return onChange(null, newValue, option); case 6: _context.next = 11; break; case 8: if (!multiple) { _context.next = 11; break; } _context.next = 11; return onChange(null, newValue, option); case 11: case "end": return _context.stop(); } }, _callee); })); return function aOnSelect(_x, _x2) { return _ref2.apply(this, arguments); }; }(); var aOnClear = function aOnClear() { if (open) { // tip: if closed, search is hidden, so let's clear tags AND search value isClearAllClicked.current = true; } }; var aOnChange = /*#__PURE__*/function () { var _ref3 = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime.mark(function _callee2(newValue, option) { var isAddFromSearch, shouldIgnore; return _regeneratorRuntime.wrap(function _callee2$(_context2) { while (1) switch (_context2.prev = _context2.next) { case 0: // For multiple `newValue` is array of strings, `option` is array of {value, children} // For single `newValue` is string/undefined, `option` is {value, children}/undefined // ! Custom option is always `{}` // // For multiple called on: // - click (x) at right (clear all) // - tag removal // - option selection (`aOnSelect` is also called after) // - trying to add new tag from search input (for mode "tags" - unwanted!) // - automatic tokenization (like pasting "1,2,3") - issue #1115 // // For single called on: // - click (x) at right (clear all) // - option selection (`aOnSelect` is also called after) // ! NOT when trying to add new tag isAddFromSearch = multiple && newValue.length && newValue.length > aValue.length && newValue[newValue.length - 1] == inputValue; shouldIgnore = isSpecialValue(option) // use onSelect instead || mode === "tags" && isAddFromSearch // obsolete, we don't use tags mode || isClearAllClicked.current && inputValue; isClearAllClicked.current = false; if (shouldIgnore) { _context2.next = 6; break; } _context2.next = 6; return onChange(null, newValue, option); case 6: case "end": return _context2.stop(); } }, _callee2); })); return function aOnChange(_x3, _x4) { return _ref3.apply(this, arguments); }; }(); // to keep compatibility with antD var aOnSearch = /*#__PURE__*/function () { var _ref4 = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime.mark(function _callee3(newInputValue) { return _regeneratorRuntime.wrap(function _callee3$(_context3) { while (1) switch (_context3.prev = _context3.next) { case 0: if (isClearAllClicked.current) { isClearAllClicked.current = false; } // if (newInputValue === "" && !open) { // return; // ? // } _context3.next = 3; return onInputChange(null, newInputValue); case 3: case "end": return _context3.stop(); } }, _callee3); })); return function aOnSearch(_x5) { return _ref4.apply(this, arguments); }; }(); var dropdownRender = useCallback(function (menu) { return /*#__PURE__*/React.createElement("div", null, menu, specialOptions.length > 0 && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Divider, { style: { margin: "0px" } }), /*#__PURE__*/React.createElement("div", { style: { display: "flex", flexDirection: "column" } }, specialOptions))); }, [filteredOptions]); var filterOption = useCallback(function (input, option) { var keysForFilter = config.settings.listKeysForSearch.map(function (k) { return mapListItemToOptionKeys[k]; }); var valueForFilter = keysForFilter.map(function (k) { return typeof option[k] == "string" ? option[k] : ""; }).join("\0"); var matches = valueForFilter.toLowerCase().indexOf(input.toLowerCase()) >= 0; return matches; }, [config]); return /*#__PURE__*/React.createElement(Select, _extends({ filterOption: useAsyncSearch ? false : filterOption, dropdownRender: dropdownRender, allowClear: true, notFoundContent: isLoading ? (_config$settings$load = config.settings.loadingLabel) !== null && _config$settings$load !== void 0 ? _config$settings$load : "Loading..." : (_config$settings$notF = config.settings.notFoundLabel) !== null && _config$settings$notF !== void 0 ? _config$settings$notF : "Not found", disabled: readonly, mode: mode, style: (customProps === null || customProps === void 0 ? void 0 : customProps.style) || style, dropdownStyle: (customProps === null || customProps === void 0 ? void 0 : customProps.dropdownStyle) || dropdownStyle, key: "widget-autocomplete", popupMatchSelectWidth: (customProps === null || customProps === void 0 ? void 0 : customProps.popupMatchSelectWidth) || (customProps === null || customProps === void 0 ? void 0 : customProps.dropdownMatchSelectWidth) || false, placeholder: (customProps === null || customProps === void 0 ? void 0 : customProps.placeholder) || dynamicPlaceholder, onDropdownVisibleChange: onDropdownVisibleChange, onChange: aOnChange, onClear: aOnClear, onSelect: aOnSelect, onSearch: aOnSearch, showSearch: true, size: renderSize, loading: isLoading, value: aValue, searchValue: inputValue, open: open, options: optionsToRender }, customProps)); });