amis-rpc-design/node_modules/rc-select/es/BaseSelect.js
2023-10-07 19:42:30 +08:00

580 lines
24 KiB
JavaScript

import _typeof from "@babel/runtime/helpers/esm/typeof";
import _extends from "@babel/runtime/helpers/esm/extends";
import _defineProperty from "@babel/runtime/helpers/esm/defineProperty";
import _toConsumableArray from "@babel/runtime/helpers/esm/toConsumableArray";
import _slicedToArray from "@babel/runtime/helpers/esm/slicedToArray";
import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
import _objectWithoutProperties from "@babel/runtime/helpers/esm/objectWithoutProperties";
var _excluded = ["id", "prefixCls", "className", "showSearch", "tagRender", "direction", "omitDomProps", "displayValues", "onDisplayValuesChange", "emptyOptions", "notFoundContent", "onClear", "mode", "disabled", "loading", "getInputElement", "getRawInputElement", "open", "defaultOpen", "onDropdownVisibleChange", "activeValue", "onActiveValueChange", "activeDescendantId", "searchValue", "autoClearSearchValue", "onSearch", "onSearchSplit", "tokenSeparators", "allowClear", "suffixIcon", "clearIcon", "OptionList", "animation", "transitionName", "dropdownStyle", "dropdownClassName", "dropdownMatchSelectWidth", "dropdownRender", "dropdownAlign", "placement", "builtinPlacements", "getPopupContainer", "showAction", "onFocus", "onBlur", "onKeyUp", "onKeyDown", "onMouseDown"];
import classNames from 'classnames';
import useLayoutEffect from "rc-util/es/hooks/useLayoutEffect";
import useMergedState from "rc-util/es/hooks/useMergedState";
import isMobile from "rc-util/es/isMobile";
import KeyCode from "rc-util/es/KeyCode";
import { useComposeRef } from "rc-util/es/ref";
import * as React from 'react';
import { useAllowClear } from "./hooks/useAllowClear";
import { BaseSelectContext } from "./hooks/useBaseProps";
import useDelayReset from "./hooks/useDelayReset";
import useLock from "./hooks/useLock";
import useSelectTriggerControl from "./hooks/useSelectTriggerControl";
import Selector from "./Selector";
import SelectTrigger from "./SelectTrigger";
import TransBtn from "./TransBtn";
import { getSeparatedContent } from "./utils/valueUtil";
var DEFAULT_OMIT_PROPS = ['value', 'onChange', 'removeIcon', 'placeholder', 'autoFocus', 'maxTagCount', 'maxTagTextLength', 'maxTagPlaceholder', 'choiceTransitionName', 'onInputKeyDown', 'onPopupScroll', 'tabIndex'];
export function isMultiple(mode) {
return mode === 'tags' || mode === 'multiple';
}
var BaseSelect = /*#__PURE__*/React.forwardRef(function (props, ref) {
var _customizeRawInputEle, _classNames2;
var id = props.id,
prefixCls = props.prefixCls,
className = props.className,
showSearch = props.showSearch,
tagRender = props.tagRender,
direction = props.direction,
omitDomProps = props.omitDomProps,
displayValues = props.displayValues,
onDisplayValuesChange = props.onDisplayValuesChange,
emptyOptions = props.emptyOptions,
_props$notFoundConten = props.notFoundContent,
notFoundContent = _props$notFoundConten === void 0 ? 'Not Found' : _props$notFoundConten,
onClear = props.onClear,
mode = props.mode,
disabled = props.disabled,
loading = props.loading,
getInputElement = props.getInputElement,
getRawInputElement = props.getRawInputElement,
open = props.open,
defaultOpen = props.defaultOpen,
onDropdownVisibleChange = props.onDropdownVisibleChange,
activeValue = props.activeValue,
onActiveValueChange = props.onActiveValueChange,
activeDescendantId = props.activeDescendantId,
searchValue = props.searchValue,
autoClearSearchValue = props.autoClearSearchValue,
onSearch = props.onSearch,
onSearchSplit = props.onSearchSplit,
tokenSeparators = props.tokenSeparators,
allowClear = props.allowClear,
suffixIcon = props.suffixIcon,
clearIcon = props.clearIcon,
OptionList = props.OptionList,
animation = props.animation,
transitionName = props.transitionName,
dropdownStyle = props.dropdownStyle,
dropdownClassName = props.dropdownClassName,
dropdownMatchSelectWidth = props.dropdownMatchSelectWidth,
dropdownRender = props.dropdownRender,
dropdownAlign = props.dropdownAlign,
placement = props.placement,
builtinPlacements = props.builtinPlacements,
getPopupContainer = props.getPopupContainer,
_props$showAction = props.showAction,
showAction = _props$showAction === void 0 ? [] : _props$showAction,
onFocus = props.onFocus,
onBlur = props.onBlur,
onKeyUp = props.onKeyUp,
onKeyDown = props.onKeyDown,
onMouseDown = props.onMouseDown,
restProps = _objectWithoutProperties(props, _excluded);
// ============================== MISC ==============================
var multiple = isMultiple(mode);
var mergedShowSearch = (showSearch !== undefined ? showSearch : multiple) || mode === 'combobox';
var domProps = _objectSpread({}, restProps);
DEFAULT_OMIT_PROPS.forEach(function (propName) {
delete domProps[propName];
});
omitDomProps === null || omitDomProps === void 0 ? void 0 : omitDomProps.forEach(function (propName) {
delete domProps[propName];
});
// ============================= Mobile =============================
var _React$useState = React.useState(false),
_React$useState2 = _slicedToArray(_React$useState, 2),
mobile = _React$useState2[0],
setMobile = _React$useState2[1];
React.useEffect(function () {
// Only update on the client side
setMobile(isMobile());
}, []);
// ============================== Refs ==============================
var containerRef = React.useRef(null);
var selectorDomRef = React.useRef(null);
var triggerRef = React.useRef(null);
var selectorRef = React.useRef(null);
var listRef = React.useRef(null);
var blurRef = React.useRef(false);
/** Used for component focused management */
var _useDelayReset = useDelayReset(),
_useDelayReset2 = _slicedToArray(_useDelayReset, 3),
mockFocused = _useDelayReset2[0],
setMockFocused = _useDelayReset2[1],
cancelSetMockFocused = _useDelayReset2[2];
// =========================== Imperative ===========================
React.useImperativeHandle(ref, function () {
var _selectorRef$current, _selectorRef$current2;
return {
focus: (_selectorRef$current = selectorRef.current) === null || _selectorRef$current === void 0 ? void 0 : _selectorRef$current.focus,
blur: (_selectorRef$current2 = selectorRef.current) === null || _selectorRef$current2 === void 0 ? void 0 : _selectorRef$current2.blur,
scrollTo: function scrollTo(arg) {
var _listRef$current;
return (_listRef$current = listRef.current) === null || _listRef$current === void 0 ? void 0 : _listRef$current.scrollTo(arg);
}
};
});
// ========================== Search Value ==========================
var mergedSearchValue = React.useMemo(function () {
var _displayValues$;
if (mode !== 'combobox') {
return searchValue;
}
var val = (_displayValues$ = displayValues[0]) === null || _displayValues$ === void 0 ? void 0 : _displayValues$.value;
return typeof val === 'string' || typeof val === 'number' ? String(val) : '';
}, [searchValue, mode, displayValues]);
// ========================== Custom Input ==========================
// Only works in `combobox`
var customizeInputElement = mode === 'combobox' && typeof getInputElement === 'function' && getInputElement() || null;
// Used for customize replacement for `rc-cascader`
var customizeRawInputElement = typeof getRawInputElement === 'function' && getRawInputElement();
var customizeRawInputRef = useComposeRef(selectorDomRef, customizeRawInputElement === null || customizeRawInputElement === void 0 ? void 0 : (_customizeRawInputEle = customizeRawInputElement.props) === null || _customizeRawInputEle === void 0 ? void 0 : _customizeRawInputEle.ref);
// ============================== Open ==============================
// SSR not support Portal which means we need delay `open` for the first time render
var _React$useState3 = React.useState(false),
_React$useState4 = _slicedToArray(_React$useState3, 2),
rendered = _React$useState4[0],
setRendered = _React$useState4[1];
useLayoutEffect(function () {
setRendered(true);
}, []);
var _useMergedState = useMergedState(false, {
defaultValue: defaultOpen,
value: open
}),
_useMergedState2 = _slicedToArray(_useMergedState, 2),
innerOpen = _useMergedState2[0],
setInnerOpen = _useMergedState2[1];
var mergedOpen = rendered ? innerOpen : false;
// Not trigger `open` in `combobox` when `notFoundContent` is empty
var emptyListContent = !notFoundContent && emptyOptions;
if (disabled || emptyListContent && mergedOpen && mode === 'combobox') {
mergedOpen = false;
}
var triggerOpen = emptyListContent ? false : mergedOpen;
var onToggleOpen = React.useCallback(function (newOpen) {
var nextOpen = newOpen !== undefined ? newOpen : !mergedOpen;
if (!disabled) {
setInnerOpen(nextOpen);
if (mergedOpen !== nextOpen) {
onDropdownVisibleChange === null || onDropdownVisibleChange === void 0 ? void 0 : onDropdownVisibleChange(nextOpen);
}
}
}, [disabled, mergedOpen, setInnerOpen, onDropdownVisibleChange]);
// ============================= Search =============================
var tokenWithEnter = React.useMemo(function () {
return (tokenSeparators || []).some(function (tokenSeparator) {
return ['\n', '\r\n'].includes(tokenSeparator);
});
}, [tokenSeparators]);
var onInternalSearch = function onInternalSearch(searchText, fromTyping, isCompositing) {
var ret = true;
var newSearchText = searchText;
onActiveValueChange === null || onActiveValueChange === void 0 ? void 0 : onActiveValueChange(null);
// Check if match the `tokenSeparators`
var patchLabels = isCompositing ? null : getSeparatedContent(searchText, tokenSeparators);
// Ignore combobox since it's not split-able
if (mode !== 'combobox' && patchLabels) {
newSearchText = '';
onSearchSplit === null || onSearchSplit === void 0 ? void 0 : onSearchSplit(patchLabels);
// Should close when paste finish
onToggleOpen(false);
// Tell Selector that break next actions
ret = false;
}
if (onSearch && mergedSearchValue !== newSearchText) {
onSearch(newSearchText, {
source: fromTyping ? 'typing' : 'effect'
});
}
return ret;
};
// Only triggered when menu is closed & mode is tags
// If menu is open, OptionList will take charge
// If mode isn't tags, press enter is not meaningful when you can't see any option
var onInternalSearchSubmit = function onInternalSearchSubmit(searchText) {
// prevent empty tags from appearing when you click the Enter button
if (!searchText || !searchText.trim()) {
return;
}
onSearch(searchText, {
source: 'submit'
});
};
// Close will clean up single mode search text
React.useEffect(function () {
if (!mergedOpen && !multiple && mode !== 'combobox') {
onInternalSearch('', false, false);
}
}, [mergedOpen]);
// ============================ Disabled ============================
// Close dropdown & remove focus state when disabled change
React.useEffect(function () {
if (innerOpen && disabled) {
setInnerOpen(false);
}
// After onBlur is triggered, the focused does not need to be reset
if (disabled && !blurRef.current) {
setMockFocused(false);
}
}, [disabled]);
// ============================ Keyboard ============================
/**
* We record input value here to check if can press to clean up by backspace
* - null: Key is not down, this is reset by key up
* - true: Search text is empty when first time backspace down
* - false: Search text is not empty when first time backspace down
*/
var _useLock = useLock(),
_useLock2 = _slicedToArray(_useLock, 2),
getClearLock = _useLock2[0],
setClearLock = _useLock2[1];
// KeyDown
var onInternalKeyDown = function onInternalKeyDown(event) {
var clearLock = getClearLock();
var which = event.which;
if (which === KeyCode.ENTER) {
// Do not submit form when type in the input
if (mode !== 'combobox') {
event.preventDefault();
}
// We only manage open state here, close logic should handle by list component
if (!mergedOpen) {
onToggleOpen(true);
}
}
setClearLock(!!mergedSearchValue);
// Remove value by `backspace`
if (which === KeyCode.BACKSPACE && !clearLock && multiple && !mergedSearchValue && displayValues.length) {
var cloneDisplayValues = _toConsumableArray(displayValues);
var removedDisplayValue = null;
for (var i = cloneDisplayValues.length - 1; i >= 0; i -= 1) {
var current = cloneDisplayValues[i];
if (!current.disabled) {
cloneDisplayValues.splice(i, 1);
removedDisplayValue = current;
break;
}
}
if (removedDisplayValue) {
onDisplayValuesChange(cloneDisplayValues, {
type: 'remove',
values: [removedDisplayValue]
});
}
}
for (var _len = arguments.length, rest = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
rest[_key - 1] = arguments[_key];
}
if (mergedOpen && listRef.current) {
var _listRef$current2;
(_listRef$current2 = listRef.current).onKeyDown.apply(_listRef$current2, [event].concat(rest));
}
onKeyDown === null || onKeyDown === void 0 ? void 0 : onKeyDown.apply(void 0, [event].concat(rest));
};
// KeyUp
var onInternalKeyUp = function onInternalKeyUp(event) {
for (var _len2 = arguments.length, rest = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {
rest[_key2 - 1] = arguments[_key2];
}
if (mergedOpen && listRef.current) {
var _listRef$current3;
(_listRef$current3 = listRef.current).onKeyUp.apply(_listRef$current3, [event].concat(rest));
}
onKeyUp === null || onKeyUp === void 0 ? void 0 : onKeyUp.apply(void 0, [event].concat(rest));
};
// ============================ Selector ============================
var onSelectorRemove = function onSelectorRemove(val) {
var newValues = displayValues.filter(function (i) {
return i !== val;
});
onDisplayValuesChange(newValues, {
type: 'remove',
values: [val]
});
};
// ========================== Focus / Blur ==========================
/** Record real focus status */
var focusRef = React.useRef(false);
var onContainerFocus = function onContainerFocus() {
setMockFocused(true);
if (!disabled) {
if (onFocus && !focusRef.current) {
onFocus.apply(void 0, arguments);
}
// `showAction` should handle `focus` if set
if (showAction.includes('focus')) {
onToggleOpen(true);
}
}
focusRef.current = true;
};
var onContainerBlur = function onContainerBlur() {
blurRef.current = true;
setMockFocused(false, function () {
focusRef.current = false;
blurRef.current = false;
onToggleOpen(false);
});
if (disabled) {
return;
}
if (mergedSearchValue) {
// `tags` mode should move `searchValue` into values
if (mode === 'tags') {
onSearch(mergedSearchValue, {
source: 'submit'
});
} else if (mode === 'multiple') {
// `multiple` mode only clean the search value but not trigger event
onSearch('', {
source: 'blur'
});
}
}
if (onBlur) {
onBlur.apply(void 0, arguments);
}
};
// Give focus back of Select
var activeTimeoutIds = [];
React.useEffect(function () {
return function () {
activeTimeoutIds.forEach(function (timeoutId) {
return clearTimeout(timeoutId);
});
activeTimeoutIds.splice(0, activeTimeoutIds.length);
};
}, []);
var onInternalMouseDown = function onInternalMouseDown(event) {
var _triggerRef$current;
var target = event.target;
var popupElement = (_triggerRef$current = triggerRef.current) === null || _triggerRef$current === void 0 ? void 0 : _triggerRef$current.getPopupElement();
// We should give focus back to selector if clicked item is not focusable
if (popupElement && popupElement.contains(target)) {
var timeoutId = setTimeout(function () {
var index = activeTimeoutIds.indexOf(timeoutId);
if (index !== -1) {
activeTimeoutIds.splice(index, 1);
}
cancelSetMockFocused();
if (!mobile && !popupElement.contains(document.activeElement)) {
var _selectorRef$current3;
(_selectorRef$current3 = selectorRef.current) === null || _selectorRef$current3 === void 0 ? void 0 : _selectorRef$current3.focus();
}
});
activeTimeoutIds.push(timeoutId);
}
for (var _len3 = arguments.length, restArgs = new Array(_len3 > 1 ? _len3 - 1 : 0), _key3 = 1; _key3 < _len3; _key3++) {
restArgs[_key3 - 1] = arguments[_key3];
}
onMouseDown === null || onMouseDown === void 0 ? void 0 : onMouseDown.apply(void 0, [event].concat(restArgs));
};
// ============================ Dropdown ============================
var _React$useState5 = React.useState({}),
_React$useState6 = _slicedToArray(_React$useState5, 2),
forceUpdate = _React$useState6[1];
// We need force update here since popup dom is render async
function onPopupMouseEnter() {
forceUpdate({});
}
// Used for raw custom input trigger
var onTriggerVisibleChange;
if (customizeRawInputElement) {
onTriggerVisibleChange = function onTriggerVisibleChange(newOpen) {
onToggleOpen(newOpen);
};
}
// Close when click on non-select element
useSelectTriggerControl(function () {
var _triggerRef$current2;
return [containerRef.current, (_triggerRef$current2 = triggerRef.current) === null || _triggerRef$current2 === void 0 ? void 0 : _triggerRef$current2.getPopupElement()];
}, triggerOpen, onToggleOpen, !!customizeRawInputElement);
// ============================ Context =============================
var baseSelectContext = React.useMemo(function () {
return _objectSpread(_objectSpread({}, props), {}, {
notFoundContent: notFoundContent,
open: mergedOpen,
triggerOpen: triggerOpen,
id: id,
showSearch: mergedShowSearch,
multiple: multiple,
toggleOpen: onToggleOpen
});
}, [props, notFoundContent, triggerOpen, mergedOpen, id, mergedShowSearch, multiple, onToggleOpen]);
// ==================================================================
// == Render ==
// ==================================================================
// ============================= Arrow ==============================
var showSuffixIcon = !!suffixIcon || loading;
var arrowNode;
if (showSuffixIcon) {
arrowNode = /*#__PURE__*/React.createElement(TransBtn, {
className: classNames("".concat(prefixCls, "-arrow"), _defineProperty({}, "".concat(prefixCls, "-arrow-loading"), loading)),
customizeIcon: suffixIcon,
customizeIconProps: {
loading: loading,
searchValue: mergedSearchValue,
open: mergedOpen,
focused: mockFocused,
showSearch: mergedShowSearch
}
});
}
// ============================= Clear ==============================
var onClearMouseDown = function onClearMouseDown() {
var _selectorRef$current4;
onClear === null || onClear === void 0 ? void 0 : onClear();
(_selectorRef$current4 = selectorRef.current) === null || _selectorRef$current4 === void 0 ? void 0 : _selectorRef$current4.focus();
onDisplayValuesChange([], {
type: 'clear',
values: displayValues
});
onInternalSearch('', false, false);
};
var _useAllowClear = useAllowClear(prefixCls, onClearMouseDown, displayValues, allowClear, clearIcon, disabled, mergedSearchValue, mode),
mergedAllowClear = _useAllowClear.allowClear,
clearNode = _useAllowClear.clearIcon;
// =========================== OptionList ===========================
var optionList = /*#__PURE__*/React.createElement(OptionList, {
ref: listRef
});
// ============================= Select =============================
var mergedClassName = classNames(prefixCls, className, (_classNames2 = {}, _defineProperty(_classNames2, "".concat(prefixCls, "-focused"), mockFocused), _defineProperty(_classNames2, "".concat(prefixCls, "-multiple"), multiple), _defineProperty(_classNames2, "".concat(prefixCls, "-single"), !multiple), _defineProperty(_classNames2, "".concat(prefixCls, "-allow-clear"), allowClear), _defineProperty(_classNames2, "".concat(prefixCls, "-show-arrow"), showSuffixIcon), _defineProperty(_classNames2, "".concat(prefixCls, "-disabled"), disabled), _defineProperty(_classNames2, "".concat(prefixCls, "-loading"), loading), _defineProperty(_classNames2, "".concat(prefixCls, "-open"), mergedOpen), _defineProperty(_classNames2, "".concat(prefixCls, "-customize-input"), customizeInputElement), _defineProperty(_classNames2, "".concat(prefixCls, "-show-search"), mergedShowSearch), _classNames2));
// >>> Selector
var selectorNode = /*#__PURE__*/React.createElement(SelectTrigger, {
ref: triggerRef,
disabled: disabled,
prefixCls: prefixCls,
visible: triggerOpen,
popupElement: optionList,
animation: animation,
transitionName: transitionName,
dropdownStyle: dropdownStyle,
dropdownClassName: dropdownClassName,
direction: direction,
dropdownMatchSelectWidth: dropdownMatchSelectWidth,
dropdownRender: dropdownRender,
dropdownAlign: dropdownAlign,
placement: placement,
builtinPlacements: builtinPlacements,
getPopupContainer: getPopupContainer,
empty: emptyOptions,
getTriggerDOMNode: function getTriggerDOMNode() {
return selectorDomRef.current;
},
onPopupVisibleChange: onTriggerVisibleChange,
onPopupMouseEnter: onPopupMouseEnter
}, customizeRawInputElement ? /*#__PURE__*/React.cloneElement(customizeRawInputElement, {
ref: customizeRawInputRef
}) : /*#__PURE__*/React.createElement(Selector, _extends({}, props, {
domRef: selectorDomRef,
prefixCls: prefixCls,
inputElement: customizeInputElement,
ref: selectorRef,
id: id,
showSearch: mergedShowSearch,
autoClearSearchValue: autoClearSearchValue,
mode: mode,
activeDescendantId: activeDescendantId,
tagRender: tagRender,
values: displayValues,
open: mergedOpen,
onToggleOpen: onToggleOpen,
activeValue: activeValue,
searchValue: mergedSearchValue,
onSearch: onInternalSearch,
onSearchSubmit: onInternalSearchSubmit,
onRemove: onSelectorRemove,
tokenWithEnter: tokenWithEnter
})));
// >>> Render
var renderNode;
// Render raw
if (customizeRawInputElement) {
renderNode = selectorNode;
} else {
renderNode = /*#__PURE__*/React.createElement("div", _extends({
className: mergedClassName
}, domProps, {
ref: containerRef,
onMouseDown: onInternalMouseDown,
onKeyDown: onInternalKeyDown,
onKeyUp: onInternalKeyUp,
onFocus: onContainerFocus,
onBlur: onContainerBlur
}), mockFocused && !mergedOpen && /*#__PURE__*/React.createElement("span", {
style: {
width: 0,
height: 0,
position: 'absolute',
overflow: 'hidden',
opacity: 0
},
"aria-live": "polite"
}, "".concat(displayValues.map(function (_ref) {
var label = _ref.label,
value = _ref.value;
return ['number', 'string'].includes(_typeof(label)) ? label : value;
}).join(', '))), selectorNode, arrowNode, mergedAllowClear && clearNode);
}
return /*#__PURE__*/React.createElement(BaseSelectContext.Provider, {
value: baseSelectContext
}, renderNode);
});
// Set display name for dev
if (process.env.NODE_ENV !== 'production') {
BaseSelect.displayName = 'BaseSelect';
}
export default BaseSelect;