@arco-design/web-vue
Version:
Arco Design Vue 2.0: A Vue.js 3 UI Library
613 lines (612 loc) • 19 kB
JavaScript
;
var vue = require("vue");
var globalConfig = require("../_utils/global-config.js");
var is = require("../_utils/is.js");
var utils = require("./utils.js");
var index = require("../trigger/index.js");
var selectView = require("../_components/select-view/select-view.js");
var selectDropdown = require("./select-dropdown.js");
var option = require("./option.js");
var optgroup = require("./optgroup.js");
var virtualList = require("../_components/virtual-list-v2/virtual-list.js");
var useSelect = require("./hooks/use-select.js");
var useTrigger = require("../_hooks/use-trigger.js");
var useFormItem = require("../_hooks/use-form-item.js");
var debounce = require("../_utils/debounce.js");
function _isSlot(s) {
return typeof s === "function" || Object.prototype.toString.call(s) === "[object Object]" && !vue.isVNode(s);
}
const DEFAULT_FIELD_NAMES = {
value: "value",
label: "label",
disabled: "disabled",
tagProps: "tagProps",
render: "render"
};
var _Select = vue.defineComponent({
name: "Select",
components: {
Trigger: index,
SelectView: selectView
},
inheritAttrs: false,
props: {
multiple: {
type: Boolean,
default: false
},
modelValue: {
type: [String, Number, Boolean, Object, Array],
default: void 0
},
defaultValue: {
type: [String, Number, Boolean, Object, Array],
default: (props) => is.isUndefined(props.multiple) ? "" : []
},
inputValue: {
type: String
},
defaultInputValue: {
type: String,
default: ""
},
size: {
type: String
},
placeholder: String,
loading: {
type: Boolean,
default: false
},
disabled: {
type: Boolean,
default: false
},
error: {
type: Boolean,
default: false
},
allowClear: {
type: Boolean,
default: false
},
allowSearch: {
type: [Boolean, Object],
default: (props) => Boolean(props.multiple)
},
allowCreate: {
type: Boolean,
default: false
},
maxTagCount: {
type: Number,
default: 0
},
popupContainer: {
type: [String, Object]
},
bordered: {
type: Boolean,
default: true
},
defaultActiveFirstOption: {
type: Boolean,
default: true
},
popupVisible: {
type: Boolean,
default: void 0
},
defaultPopupVisible: {
type: Boolean,
default: false
},
unmountOnClose: {
type: Boolean,
default: false
},
filterOption: {
type: [Boolean, Function],
default: true
},
options: {
type: Array,
default: () => []
},
virtualListProps: {
type: Object
},
triggerProps: {
type: Object
},
formatLabel: {
type: Function
},
fallbackOption: {
type: [Boolean, Function],
default: true
},
showExtraOptions: {
type: Boolean,
default: true
},
valueKey: {
type: String,
default: "value"
},
searchDelay: {
type: Number,
default: 500
},
limit: {
type: Number,
default: 0
},
fieldNames: {
type: Object
},
scrollbar: {
type: [Boolean, Object],
default: true
},
showHeaderOnEmpty: {
type: Boolean,
default: false
},
showFooterOnEmpty: {
type: Boolean,
default: false
},
tagNowrap: {
type: Boolean,
default: false
}
},
emits: {
"update:modelValue": (value) => true,
"update:inputValue": (inputValue) => true,
"update:popupVisible": (visible) => true,
"change": (value) => true,
"inputValueChange": (inputValue) => true,
"popupVisibleChange": (visible) => true,
"clear": (ev) => true,
"remove": (removed) => true,
"search": (inputValue) => true,
"dropdownScroll": (ev) => true,
"dropdownReachBottom": (ev) => true,
"exceedLimit": (value, ev) => true
},
setup(props, {
slots,
emit,
attrs
}) {
const {
size,
disabled,
error,
options,
filterOption,
valueKey,
multiple,
popupVisible,
defaultPopupVisible,
showExtraOptions,
modelValue,
fieldNames,
loading,
defaultActiveFirstOption
} = vue.toRefs(props);
const prefixCls = globalConfig.getPrefixCls("select");
const {
mergedSize,
mergedDisabled,
mergedError,
eventHandlers
} = useFormItem.useFormItem({
size,
disabled,
error
});
const component = vue.computed(() => props.virtualListProps ? "div" : "li");
const retainInputValue = vue.computed(() => is.isObject(props.allowSearch) && Boolean(props.allowSearch.retainInputValue));
vue.computed(() => {
if (is.isFunction(props.formatLabel)) {
return (data) => {
const optionInfo = optionInfoMap.get(data.value);
return props.formatLabel(optionInfo);
};
}
return void 0;
});
const dropdownRef = vue.ref();
const optionRefs = vue.ref({});
const virtualListRef = vue.ref();
const {
computedPopupVisible,
handlePopupVisibleChange
} = useTrigger.useTrigger({
popupVisible,
defaultPopupVisible,
emit
});
const _value = vue.ref(props.defaultValue);
const computedValueObjects = vue.computed(() => {
var _a;
const mergedValue = (_a = props.modelValue) != null ? _a : _value.value;
const valueArray = is.isArray(mergedValue) ? mergedValue : mergedValue || is.isNumber(mergedValue) || is.isString(mergedValue) || is.isBoolean(mergedValue) ? [mergedValue] : [];
return valueArray.map((value) => ({
value,
key: utils.getKeyFromValue(value, props.valueKey)
}));
});
vue.watch(modelValue, (value) => {
if (is.isUndefined(value) || is.isNull(value)) {
_value.value = multiple.value ? [] : value;
}
});
const computedValueKeys = vue.computed(() => computedValueObjects.value.map((obj) => obj.key));
const mergedFieldNames = vue.computed(() => ({
...DEFAULT_FIELD_NAMES,
...fieldNames == null ? void 0 : fieldNames.value
}));
const _selectedOption = vue.ref();
const getRawOptionFromValueKeys = (valueKeys) => {
const optionMap = {};
valueKeys.forEach((key) => {
optionMap[key] = optionInfoMap.get(key);
});
return optionMap;
};
const updateSelectedOption = (valueKeys) => {
_selectedOption.value = getRawOptionFromValueKeys(valueKeys);
};
const getFallBackOption = (value) => {
if (is.isFunction(props.fallbackOption)) {
return props.fallbackOption(value);
}
return {
[mergedFieldNames.value.value]: value,
[mergedFieldNames.value.label]: String(is.isObject(value) ? value[valueKey == null ? void 0 : valueKey.value] : value)
};
};
const getExtraValueData = () => {
const valueArray = [];
const keyArray = [];
if (props.allowCreate || props.fallbackOption) {
for (const item of computedValueObjects.value) {
if (!keyArray.includes(item.key) && item.value !== "") {
const optionInfo = optionInfoMap.get(item.key);
if (!optionInfo || optionInfo.origin === "extraOptions") {
valueArray.push(item);
keyArray.push(item.key);
}
}
}
}
if (props.allowCreate && computedInputValue.value) {
const key = utils.getKeyFromValue(computedInputValue.value);
if (!keyArray.includes(key)) {
const optionInfo = optionInfoMap.get(key);
if (!optionInfo || optionInfo.origin === "extraOptions") {
valueArray.push({
value: computedInputValue.value,
key
});
}
}
}
return valueArray;
};
const extraValueObjects = vue.ref([]);
const extraOptions = vue.computed(() => extraValueObjects.value.map((obj) => {
var _a;
let optionInfo = getFallBackOption(obj.value);
const extraOptionRawInfo = (_a = _selectedOption.value) == null ? void 0 : _a[obj.key];
if (!is.isUndefined(extraOptionRawInfo) && !is.isEmptyObject(extraOptionRawInfo)) {
optionInfo = {
...optionInfo,
...extraOptionRawInfo
};
}
return optionInfo;
}));
vue.nextTick(() => {
vue.watchEffect(() => {
var _a;
const valueData = getExtraValueData();
if (valueData.length !== extraValueObjects.value.length) {
extraValueObjects.value = valueData;
} else if (valueData.length > 0) {
for (let i = 0; i < valueData.length; i++) {
if (valueData[i].key !== ((_a = extraValueObjects.value[i]) == null ? void 0 : _a.key)) {
extraValueObjects.value = valueData;
break;
}
}
}
});
});
const _inputValue = vue.ref("");
const computedInputValue = vue.computed(() => {
var _a;
return (_a = props.inputValue) != null ? _a : _inputValue.value;
});
vue.watch(computedPopupVisible, (visible) => {
if (!visible && !retainInputValue.value && computedInputValue.value) {
updateInputValue("");
}
});
const getValueFromValueKeys = (valueKeys) => {
var _a, _b;
if (!props.multiple) {
return (_b = (_a = optionInfoMap.get(valueKeys[0])) == null ? void 0 : _a.value) != null ? _b : utils.hasEmptyStringKey(optionInfoMap) ? void 0 : "";
}
return valueKeys.map((key) => {
var _a2, _b2;
return (_b2 = (_a2 = optionInfoMap.get(key)) == null ? void 0 : _a2.value) != null ? _b2 : "";
});
};
const updateValue = (valueKeys) => {
var _a, _b;
const value = getValueFromValueKeys(valueKeys);
_value.value = value;
emit("update:modelValue", value);
emit("change", value);
(_b = (_a = eventHandlers.value) == null ? void 0 : _a.onChange) == null ? void 0 : _b.call(_a);
updateSelectedOption(valueKeys);
};
const updateInputValue = (inputValue) => {
_inputValue.value = inputValue;
emit("update:inputValue", inputValue);
emit("inputValueChange", inputValue);
};
const handleSelect = (key, ev) => {
if (props.multiple) {
if (!computedValueKeys.value.includes(key)) {
if (enabledOptionKeys.value.includes(key)) {
if (props.limit > 0 && computedValueKeys.value.length >= props.limit) {
const info = optionInfoMap.get(key);
emit("exceedLimit", info == null ? void 0 : info.value, ev);
} else {
const valueKeys = computedValueKeys.value.concat(key);
updateValue(valueKeys);
}
}
} else {
const valueKeys = computedValueKeys.value.filter((_key) => _key !== key);
updateValue(valueKeys);
}
if (!retainInputValue.value) {
updateInputValue("");
}
} else {
if (key !== computedValueKeys.value[0]) {
updateValue([key]);
}
if (retainInputValue.value) {
const optionInfo = optionInfoMap.get(key);
if (optionInfo) {
updateInputValue(optionInfo.label);
}
}
handlePopupVisibleChange(false);
}
};
const handleSearch = debounce.debounce((value) => {
emit("search", value);
}, props.searchDelay);
const handleInputValueChange = (inputValue) => {
if (inputValue !== computedInputValue.value) {
if (!computedPopupVisible.value) {
handlePopupVisibleChange(true);
}
updateInputValue(inputValue);
if (props.allowSearch) {
handleSearch(inputValue);
}
}
};
const handleRemove = (key) => {
const optionInfo = optionInfoMap.get(key);
const newKeys = computedValueKeys.value.filter((_key) => _key !== key);
updateValue(newKeys);
emit("remove", optionInfo == null ? void 0 : optionInfo.value);
};
const handleClear = (e) => {
e == null ? void 0 : e.stopPropagation();
const newKeys = computedValueKeys.value.filter((key) => {
var _a;
return (_a = optionInfoMap.get(key)) == null ? void 0 : _a.disabled;
});
updateValue(newKeys);
updateInputValue("");
emit("clear", e);
};
const handleDropdownScroll = (e) => {
emit("dropdownScroll", e);
};
const handleDropdownReachBottom = (e) => {
emit("dropdownReachBottom", e);
};
const {
validOptions,
optionInfoMap,
validOptionInfos,
enabledOptionKeys,
handleKeyDown
} = useSelect.useSelect({
multiple,
options,
extraOptions,
inputValue: computedInputValue,
filterOption,
showExtraOptions,
component,
valueKey,
fieldNames,
loading,
popupVisible: computedPopupVisible,
valueKeys: computedValueKeys,
dropdownRef,
optionRefs,
virtualListRef,
defaultActiveFirstOption,
onSelect: handleSelect,
onPopupVisibleChange: handlePopupVisibleChange
});
const selectViewValue = vue.computed(() => {
var _a;
const result = [];
for (const item of computedValueObjects.value) {
const optionInfo = optionInfoMap.get(item.key);
if (optionInfo) {
result.push({
...optionInfo,
value: item.key,
label: (_a = optionInfo == null ? void 0 : optionInfo.label) != null ? _a : String(is.isObject(item.value) ? item.value[valueKey == null ? void 0 : valueKey.value] : item.value),
closable: !(optionInfo == null ? void 0 : optionInfo.disabled),
tagProps: optionInfo == null ? void 0 : optionInfo.tagProps
});
}
}
return result;
});
const getOptionContentFunc = (optionInfo) => {
if (is.isFunction(slots.option)) {
const optionSlot = slots.option;
return () => optionSlot({
data: optionInfo.raw
});
}
if (is.isFunction(optionInfo.render)) {
return optionInfo.render;
}
return () => optionInfo.label;
};
const renderOption = (optionInfo) => {
if (utils.isGroupOptionInfo(optionInfo)) {
let _slot;
return vue.createVNode(optgroup, {
"key": optionInfo.key,
"label": optionInfo.label
}, _isSlot(_slot = optionInfo.options.map((child) => renderOption(child))) ? _slot : {
default: () => [_slot]
});
}
if (!utils.isValidOption(optionInfo, {
inputValue: computedInputValue.value,
filterOption: filterOption == null ? void 0 : filterOption.value
})) {
return null;
}
return vue.createVNode(option, {
"ref": (ref) => {
if (ref == null ? void 0 : ref.$el) {
optionRefs.value[optionInfo.key] = ref.$el;
}
},
"key": optionInfo.key,
"value": optionInfo.value,
"label": optionInfo.label,
"disabled": optionInfo.disabled,
"internal": true
}, {
default: getOptionContentFunc(optionInfo)
});
};
const renderDropDown = () => {
return vue.createVNode(selectDropdown, {
"ref": dropdownRef,
"loading": props.loading,
"empty": validOptionInfos.value.length === 0,
"virtualList": Boolean(props.virtualListProps),
"scrollbar": props.scrollbar,
"showHeaderOnEmpty": props.showHeaderOnEmpty,
"showFooterOnEmpty": props.showFooterOnEmpty,
"onScroll": handleDropdownScroll,
"onReachBottom": handleDropdownReachBottom
}, {
"default": () => {
var _a, _b;
return [...(_b = (_a = slots.default) == null ? void 0 : _a.call(slots)) != null ? _b : [], ...validOptions.value.map(renderOption)];
},
"virtual-list": () => vue.createVNode(virtualList, vue.mergeProps(props.virtualListProps, {
"ref": virtualListRef,
"data": validOptions.value
}), {
item: ({
item
}) => renderOption(item)
}),
"empty": slots.empty,
"header": slots.header,
"footer": slots.footer
});
};
const renderLabel = ({
data
}) => {
var _a, _b, _c, _d;
if ((slots.label || is.isFunction(props.formatLabel)) && data) {
const optionInfo = optionInfoMap.get(data.value);
if (optionInfo == null ? void 0 : optionInfo.raw) {
return (_c = (_a = slots.label) == null ? void 0 : _a.call(slots, {
data: optionInfo.raw
})) != null ? _c : (_b = props.formatLabel) == null ? void 0 : _b.call(props, optionInfo.raw);
}
}
return (_d = data == null ? void 0 : data.label) != null ? _d : "";
};
return () => vue.createVNode(index, vue.mergeProps({
"trigger": "click",
"position": "bl",
"popupOffset": 4,
"animationName": "slide-dynamic-origin",
"hideEmpty": true,
"preventFocus": true,
"autoFitPopupWidth": true,
"autoFitTransformOrigin": true,
"disabled": mergedDisabled.value,
"popupVisible": computedPopupVisible.value,
"unmountOnClose": props.unmountOnClose,
"clickToClose": !(props.allowSearch || props.allowCreate),
"popupContainer": props.popupContainer,
"onPopupVisibleChange": handlePopupVisibleChange
}, props.triggerProps), {
default: () => {
var _a, _b;
return [(_b = (_a = slots.trigger) == null ? void 0 : _a.call(slots)) != null ? _b : vue.createVNode(selectView, vue.mergeProps({
"class": prefixCls,
"modelValue": selectViewValue.value,
"inputValue": computedInputValue.value,
"multiple": props.multiple,
"disabled": mergedDisabled.value,
"error": mergedError.value,
"loading": props.loading,
"allowClear": props.allowClear,
"allowCreate": props.allowCreate,
"allowSearch": Boolean(props.allowSearch),
"opened": computedPopupVisible.value,
"maxTagCount": props.maxTagCount,
"placeholder": props.placeholder,
"bordered": props.bordered,
"size": mergedSize.value,
"tagNowrap": props.tagNowrap,
"onInputValueChange": handleInputValueChange,
"onRemove": handleRemove,
"onClear": handleClear,
"onKeydown": handleKeyDown
}, attrs), {
"label": renderLabel,
"prefix": slots.prefix,
"arrow-icon": slots["arrow-icon"],
"loading-icon": slots["loading-icon"],
"search-icon": slots["search-icon"]
})];
},
content: renderDropDown
});
}
});
module.exports = _Select;