775 lines
26 KiB
JavaScript
775 lines
26 KiB
JavaScript
|
|
'use strict';
|
||
|
|
|
||
|
|
Object.defineProperty(exports, '__esModule', { value: true });
|
||
|
|
|
||
|
|
var vue = require('vue');
|
||
|
|
var lodashUnified = require('lodash-unified');
|
||
|
|
var core = require('@vueuse/core');
|
||
|
|
var useAllowCreate = require('./useAllowCreate.js');
|
||
|
|
var useProps = require('./useProps.js');
|
||
|
|
var index = require('../../../hooks/use-locale/index.js');
|
||
|
|
var index$1 = require('../../../hooks/use-namespace/index.js');
|
||
|
|
var useFormItem = require('../../form/src/hooks/use-form-item.js');
|
||
|
|
var index$2 = require('../../../hooks/use-empty-values/index.js');
|
||
|
|
var index$3 = require('../../../hooks/use-composition/index.js');
|
||
|
|
var index$4 = require('../../../hooks/use-focus-controller/index.js');
|
||
|
|
var error = require('../../../utils/error.js');
|
||
|
|
var shared = require('@vue/shared');
|
||
|
|
var icon = require('../../../utils/vue/icon.js');
|
||
|
|
var strings = require('../../../utils/strings.js');
|
||
|
|
var useFormCommonProps = require('../../form/src/hooks/use-form-common-props.js');
|
||
|
|
var form = require('../../../constants/form.js');
|
||
|
|
var aria = require('../../../constants/aria.js');
|
||
|
|
var types = require('../../../utils/types.js');
|
||
|
|
var event = require('../../../constants/event.js');
|
||
|
|
|
||
|
|
const useSelect = (props, emit) => {
|
||
|
|
const { t } = index.useLocale();
|
||
|
|
const nsSelect = index$1.useNamespace("select");
|
||
|
|
const nsInput = index$1.useNamespace("input");
|
||
|
|
const { form: elForm, formItem: elFormItem } = useFormItem.useFormItem();
|
||
|
|
const { inputId } = useFormItem.useFormItemInputId(props, {
|
||
|
|
formItemContext: elFormItem
|
||
|
|
});
|
||
|
|
const { aliasProps, getLabel, getValue, getDisabled, getOptions } = useProps.useProps(props);
|
||
|
|
const { valueOnClear, isEmptyValue } = index$2.useEmptyValues(props);
|
||
|
|
const states = vue.reactive({
|
||
|
|
inputValue: "",
|
||
|
|
cachedOptions: [],
|
||
|
|
createdOptions: [],
|
||
|
|
hoveringIndex: -1,
|
||
|
|
inputHovering: false,
|
||
|
|
selectionWidth: 0,
|
||
|
|
collapseItemWidth: 0,
|
||
|
|
previousQuery: null,
|
||
|
|
previousValue: void 0,
|
||
|
|
selectedLabel: "",
|
||
|
|
menuVisibleOnFocus: false,
|
||
|
|
isBeforeHide: false
|
||
|
|
});
|
||
|
|
const popperSize = vue.ref(-1);
|
||
|
|
const selectRef = vue.ref();
|
||
|
|
const selectionRef = vue.ref();
|
||
|
|
const tooltipRef = vue.ref();
|
||
|
|
const tagTooltipRef = vue.ref();
|
||
|
|
const inputRef = vue.ref();
|
||
|
|
const prefixRef = vue.ref();
|
||
|
|
const suffixRef = vue.ref();
|
||
|
|
const menuRef = vue.ref();
|
||
|
|
const tagMenuRef = vue.ref();
|
||
|
|
const collapseItemRef = vue.ref();
|
||
|
|
const {
|
||
|
|
isComposing,
|
||
|
|
handleCompositionStart,
|
||
|
|
handleCompositionEnd,
|
||
|
|
handleCompositionUpdate
|
||
|
|
} = index$3.useComposition({
|
||
|
|
afterComposition: (e) => onInput(e)
|
||
|
|
});
|
||
|
|
const selectDisabled = vue.computed(() => props.disabled || !!(elForm == null ? void 0 : elForm.disabled));
|
||
|
|
const { wrapperRef, isFocused, handleBlur } = index$4.useFocusController(inputRef, {
|
||
|
|
disabled: selectDisabled,
|
||
|
|
afterFocus() {
|
||
|
|
if (props.automaticDropdown && !expanded.value) {
|
||
|
|
expanded.value = true;
|
||
|
|
states.menuVisibleOnFocus = true;
|
||
|
|
}
|
||
|
|
},
|
||
|
|
beforeBlur(event) {
|
||
|
|
var _a, _b;
|
||
|
|
return ((_a = tooltipRef.value) == null ? void 0 : _a.isFocusInsideContent(event)) || ((_b = tagTooltipRef.value) == null ? void 0 : _b.isFocusInsideContent(event));
|
||
|
|
},
|
||
|
|
afterBlur() {
|
||
|
|
var _a;
|
||
|
|
expanded.value = false;
|
||
|
|
states.menuVisibleOnFocus = false;
|
||
|
|
if (props.validateEvent) {
|
||
|
|
(_a = elFormItem == null ? void 0 : elFormItem.validate) == null ? void 0 : _a.call(elFormItem, "blur").catch((err) => error.debugWarn());
|
||
|
|
}
|
||
|
|
}
|
||
|
|
});
|
||
|
|
const allOptions = vue.computed(() => filterOptions(""));
|
||
|
|
const hasOptions = vue.computed(() => {
|
||
|
|
if (props.loading)
|
||
|
|
return false;
|
||
|
|
return props.options.length > 0 || states.createdOptions.length > 0;
|
||
|
|
});
|
||
|
|
const filteredOptions = vue.ref([]);
|
||
|
|
const expanded = vue.ref(false);
|
||
|
|
const needStatusIcon = vue.computed(() => {
|
||
|
|
var _a;
|
||
|
|
return (_a = elForm == null ? void 0 : elForm.statusIcon) != null ? _a : false;
|
||
|
|
});
|
||
|
|
const popupHeight = vue.computed(() => {
|
||
|
|
const totalHeight = filteredOptions.value.length * props.itemHeight;
|
||
|
|
return totalHeight > props.height ? props.height : totalHeight;
|
||
|
|
});
|
||
|
|
const hasModelValue = vue.computed(() => {
|
||
|
|
return props.multiple ? shared.isArray(props.modelValue) && props.modelValue.length > 0 : !isEmptyValue(props.modelValue);
|
||
|
|
});
|
||
|
|
const showClearBtn = vue.computed(() => {
|
||
|
|
return props.clearable && !selectDisabled.value && hasModelValue.value && (isFocused.value || states.inputHovering);
|
||
|
|
});
|
||
|
|
const iconComponent = vue.computed(() => props.remote && props.filterable ? "" : props.suffixIcon);
|
||
|
|
const iconReverse = vue.computed(() => iconComponent.value && nsSelect.is("reverse", expanded.value));
|
||
|
|
const validateState = vue.computed(() => (elFormItem == null ? void 0 : elFormItem.validateState) || "");
|
||
|
|
const validateIcon = vue.computed(() => {
|
||
|
|
if (!validateState.value)
|
||
|
|
return;
|
||
|
|
return icon.ValidateComponentsMap[validateState.value];
|
||
|
|
});
|
||
|
|
const debounce = vue.computed(() => props.remote ? 300 : 0);
|
||
|
|
const emptyText = vue.computed(() => {
|
||
|
|
if (props.loading) {
|
||
|
|
return props.loadingText || t("el.select.loading");
|
||
|
|
} else {
|
||
|
|
if (props.remote && !states.inputValue && !hasOptions.value)
|
||
|
|
return false;
|
||
|
|
if (props.filterable && states.inputValue && hasOptions.value && filteredOptions.value.length === 0) {
|
||
|
|
return props.noMatchText || t("el.select.noMatch");
|
||
|
|
}
|
||
|
|
if (!hasOptions.value) {
|
||
|
|
return props.noDataText || t("el.select.noData");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return null;
|
||
|
|
});
|
||
|
|
const isFilterMethodValid = vue.computed(() => props.filterable && shared.isFunction(props.filterMethod));
|
||
|
|
const isRemoteMethodValid = vue.computed(() => props.filterable && props.remote && shared.isFunction(props.remoteMethod));
|
||
|
|
const filterOptions = (query) => {
|
||
|
|
const regexp = new RegExp(strings.escapeStringRegexp(query), "i");
|
||
|
|
const isValidOption = (o) => {
|
||
|
|
if (isFilterMethodValid.value || isRemoteMethodValid.value)
|
||
|
|
return true;
|
||
|
|
return query ? regexp.test(getLabel(o) || "") : true;
|
||
|
|
};
|
||
|
|
if (props.loading) {
|
||
|
|
return [];
|
||
|
|
}
|
||
|
|
return [...states.createdOptions, ...props.options].reduce((all, item) => {
|
||
|
|
const options = getOptions(item);
|
||
|
|
if (shared.isArray(options)) {
|
||
|
|
const filtered = options.filter(isValidOption);
|
||
|
|
if (filtered.length > 0) {
|
||
|
|
all.push({
|
||
|
|
label: getLabel(item),
|
||
|
|
type: "Group"
|
||
|
|
}, ...filtered);
|
||
|
|
}
|
||
|
|
} else if (props.remote || isValidOption(item)) {
|
||
|
|
all.push(item);
|
||
|
|
}
|
||
|
|
return all;
|
||
|
|
}, []);
|
||
|
|
};
|
||
|
|
const updateOptions = () => {
|
||
|
|
filteredOptions.value = filterOptions(states.inputValue);
|
||
|
|
};
|
||
|
|
const allOptionsValueMap = vue.computed(() => {
|
||
|
|
const valueMap = /* @__PURE__ */ new Map();
|
||
|
|
allOptions.value.forEach((option, index) => {
|
||
|
|
valueMap.set(getValueKey(getValue(option)), { option, index });
|
||
|
|
});
|
||
|
|
return valueMap;
|
||
|
|
});
|
||
|
|
const filteredOptionsValueMap = vue.computed(() => {
|
||
|
|
const valueMap = /* @__PURE__ */ new Map();
|
||
|
|
filteredOptions.value.forEach((option, index) => {
|
||
|
|
valueMap.set(getValueKey(getValue(option)), { option, index });
|
||
|
|
});
|
||
|
|
return valueMap;
|
||
|
|
});
|
||
|
|
const optionsAllDisabled = vue.computed(() => filteredOptions.value.every((option) => getDisabled(option)));
|
||
|
|
const selectSize = useFormCommonProps.useFormSize();
|
||
|
|
const collapseTagSize = vue.computed(() => selectSize.value === "small" ? "small" : "default");
|
||
|
|
const calculatePopperSize = () => {
|
||
|
|
var _a;
|
||
|
|
if (types.isNumber(props.fitInputWidth)) {
|
||
|
|
popperSize.value = props.fitInputWidth;
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
const width = ((_a = selectRef.value) == null ? void 0 : _a.offsetWidth) || 200;
|
||
|
|
if (!props.fitInputWidth && hasOptions.value) {
|
||
|
|
vue.nextTick(() => {
|
||
|
|
popperSize.value = Math.max(width, calculateLabelMaxWidth());
|
||
|
|
});
|
||
|
|
} else {
|
||
|
|
popperSize.value = width;
|
||
|
|
}
|
||
|
|
};
|
||
|
|
const calculateLabelMaxWidth = () => {
|
||
|
|
var _a, _b;
|
||
|
|
const canvas = document.createElement("canvas");
|
||
|
|
const ctx = canvas.getContext("2d");
|
||
|
|
const selector = nsSelect.be("dropdown", "item");
|
||
|
|
const dom = ((_b = (_a = menuRef.value) == null ? void 0 : _a.listRef) == null ? void 0 : _b.innerRef) || document;
|
||
|
|
const dropdownItemEl = dom.querySelector(`.${selector}`);
|
||
|
|
if (dropdownItemEl === null || ctx === null)
|
||
|
|
return 0;
|
||
|
|
const style = getComputedStyle(dropdownItemEl);
|
||
|
|
const padding = Number.parseFloat(style.paddingLeft) + Number.parseFloat(style.paddingRight);
|
||
|
|
ctx.font = `bold ${style.font.replace(new RegExp(`\\b${style.fontWeight}\\b`), "")}`;
|
||
|
|
const maxWidth = filteredOptions.value.reduce((max, option) => {
|
||
|
|
const metrics = ctx.measureText(getLabel(option));
|
||
|
|
return Math.max(metrics.width, max);
|
||
|
|
}, 0);
|
||
|
|
return maxWidth + padding;
|
||
|
|
};
|
||
|
|
const getGapWidth = () => {
|
||
|
|
if (!selectionRef.value)
|
||
|
|
return 0;
|
||
|
|
const style = window.getComputedStyle(selectionRef.value);
|
||
|
|
return Number.parseFloat(style.gap || "6px");
|
||
|
|
};
|
||
|
|
const tagStyle = vue.computed(() => {
|
||
|
|
const gapWidth = getGapWidth();
|
||
|
|
const inputSlotWidth = props.filterable ? gapWidth + form.MINIMUM_INPUT_WIDTH : 0;
|
||
|
|
const maxWidth = collapseItemRef.value && props.maxCollapseTags === 1 ? states.selectionWidth - states.collapseItemWidth - gapWidth - inputSlotWidth : states.selectionWidth - inputSlotWidth;
|
||
|
|
return { maxWidth: `${maxWidth}px` };
|
||
|
|
});
|
||
|
|
const collapseTagStyle = vue.computed(() => {
|
||
|
|
return { maxWidth: `${states.selectionWidth}px` };
|
||
|
|
});
|
||
|
|
const shouldShowPlaceholder = vue.computed(() => {
|
||
|
|
if (shared.isArray(props.modelValue)) {
|
||
|
|
return props.modelValue.length === 0 && !states.inputValue;
|
||
|
|
}
|
||
|
|
return props.filterable ? !states.inputValue : true;
|
||
|
|
});
|
||
|
|
const currentPlaceholder = vue.computed(() => {
|
||
|
|
var _a;
|
||
|
|
const _placeholder = (_a = props.placeholder) != null ? _a : t("el.select.placeholder");
|
||
|
|
return props.multiple || !hasModelValue.value ? _placeholder : states.selectedLabel;
|
||
|
|
});
|
||
|
|
const popperRef = vue.computed(() => {
|
||
|
|
var _a, _b;
|
||
|
|
return (_b = (_a = tooltipRef.value) == null ? void 0 : _a.popperRef) == null ? void 0 : _b.contentRef;
|
||
|
|
});
|
||
|
|
const indexRef = vue.computed(() => {
|
||
|
|
if (props.multiple) {
|
||
|
|
const len = props.modelValue.length;
|
||
|
|
if (props.modelValue.length > 0 && filteredOptionsValueMap.value.has(props.modelValue[len - 1])) {
|
||
|
|
const { index } = filteredOptionsValueMap.value.get(props.modelValue[len - 1]);
|
||
|
|
return index;
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
if (!isEmptyValue(props.modelValue) && filteredOptionsValueMap.value.has(props.modelValue)) {
|
||
|
|
const { index } = filteredOptionsValueMap.value.get(props.modelValue);
|
||
|
|
return index;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return -1;
|
||
|
|
});
|
||
|
|
const dropdownMenuVisible = vue.computed({
|
||
|
|
get() {
|
||
|
|
return expanded.value && emptyText.value !== false;
|
||
|
|
},
|
||
|
|
set(val) {
|
||
|
|
expanded.value = val;
|
||
|
|
}
|
||
|
|
});
|
||
|
|
const showTagList = vue.computed(() => {
|
||
|
|
if (!props.multiple) {
|
||
|
|
return [];
|
||
|
|
}
|
||
|
|
return props.collapseTags ? states.cachedOptions.slice(0, props.maxCollapseTags) : states.cachedOptions;
|
||
|
|
});
|
||
|
|
const collapseTagList = vue.computed(() => {
|
||
|
|
if (!props.multiple) {
|
||
|
|
return [];
|
||
|
|
}
|
||
|
|
return props.collapseTags ? states.cachedOptions.slice(props.maxCollapseTags) : [];
|
||
|
|
});
|
||
|
|
const {
|
||
|
|
createNewOption,
|
||
|
|
removeNewOption,
|
||
|
|
selectNewOption,
|
||
|
|
clearAllNewOption
|
||
|
|
} = useAllowCreate.useAllowCreate(props, states);
|
||
|
|
const toggleMenu = () => {
|
||
|
|
if (selectDisabled.value)
|
||
|
|
return;
|
||
|
|
if (states.menuVisibleOnFocus) {
|
||
|
|
states.menuVisibleOnFocus = false;
|
||
|
|
} else {
|
||
|
|
expanded.value = !expanded.value;
|
||
|
|
}
|
||
|
|
};
|
||
|
|
const onInputChange = () => {
|
||
|
|
if (states.inputValue.length > 0 && !expanded.value) {
|
||
|
|
expanded.value = true;
|
||
|
|
}
|
||
|
|
createNewOption(states.inputValue);
|
||
|
|
vue.nextTick(() => {
|
||
|
|
handleQueryChange(states.inputValue);
|
||
|
|
});
|
||
|
|
};
|
||
|
|
const debouncedOnInputChange = lodashUnified.debounce(onInputChange, debounce.value);
|
||
|
|
const handleQueryChange = (val) => {
|
||
|
|
if (states.previousQuery === val || isComposing.value) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
states.previousQuery = val;
|
||
|
|
if (props.filterable && shared.isFunction(props.filterMethod)) {
|
||
|
|
props.filterMethod(val);
|
||
|
|
} else if (props.filterable && props.remote && shared.isFunction(props.remoteMethod)) {
|
||
|
|
props.remoteMethod(val);
|
||
|
|
}
|
||
|
|
if (props.defaultFirstOption && (props.filterable || props.remote) && filteredOptions.value.length) {
|
||
|
|
vue.nextTick(checkDefaultFirstOption);
|
||
|
|
} else {
|
||
|
|
vue.nextTick(updateHoveringIndex);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
const checkDefaultFirstOption = () => {
|
||
|
|
const optionsInDropdown = filteredOptions.value.filter((n) => !n.disabled && n.type !== "Group");
|
||
|
|
const userCreatedOption = optionsInDropdown.find((n) => n.created);
|
||
|
|
const firstOriginOption = optionsInDropdown[0];
|
||
|
|
states.hoveringIndex = getValueIndex(filteredOptions.value, userCreatedOption || firstOriginOption);
|
||
|
|
};
|
||
|
|
const emitChange = (val) => {
|
||
|
|
if (!lodashUnified.isEqual(props.modelValue, val)) {
|
||
|
|
emit(event.CHANGE_EVENT, val);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
const update = (val) => {
|
||
|
|
emit(event.UPDATE_MODEL_EVENT, val);
|
||
|
|
emitChange(val);
|
||
|
|
states.previousValue = props.multiple ? String(val) : val;
|
||
|
|
vue.nextTick(() => {
|
||
|
|
if (props.multiple && shared.isArray(props.modelValue)) {
|
||
|
|
const cachedOptions = states.cachedOptions.slice();
|
||
|
|
const selectedOptions = props.modelValue.map((value) => getOption(value, cachedOptions));
|
||
|
|
if (!lodashUnified.isEqual(states.cachedOptions, selectedOptions)) {
|
||
|
|
states.cachedOptions = selectedOptions;
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
initStates(true);
|
||
|
|
}
|
||
|
|
});
|
||
|
|
};
|
||
|
|
const getValueIndex = (arr = [], value) => {
|
||
|
|
if (!shared.isObject(value)) {
|
||
|
|
return arr.indexOf(value);
|
||
|
|
}
|
||
|
|
const valueKey = props.valueKey;
|
||
|
|
let index = -1;
|
||
|
|
arr.some((item, i) => {
|
||
|
|
if (lodashUnified.get(item, valueKey) === lodashUnified.get(value, valueKey)) {
|
||
|
|
index = i;
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
return false;
|
||
|
|
});
|
||
|
|
return index;
|
||
|
|
};
|
||
|
|
const getValueKey = (item) => {
|
||
|
|
return shared.isObject(item) ? lodashUnified.get(item, props.valueKey) : item;
|
||
|
|
};
|
||
|
|
const handleResize = () => {
|
||
|
|
calculatePopperSize();
|
||
|
|
};
|
||
|
|
const resetSelectionWidth = () => {
|
||
|
|
states.selectionWidth = Number.parseFloat(window.getComputedStyle(selectionRef.value).width);
|
||
|
|
};
|
||
|
|
const resetCollapseItemWidth = () => {
|
||
|
|
states.collapseItemWidth = collapseItemRef.value.getBoundingClientRect().width;
|
||
|
|
};
|
||
|
|
const updateTooltip = () => {
|
||
|
|
var _a, _b;
|
||
|
|
(_b = (_a = tooltipRef.value) == null ? void 0 : _a.updatePopper) == null ? void 0 : _b.call(_a);
|
||
|
|
};
|
||
|
|
const updateTagTooltip = () => {
|
||
|
|
var _a, _b;
|
||
|
|
(_b = (_a = tagTooltipRef.value) == null ? void 0 : _a.updatePopper) == null ? void 0 : _b.call(_a);
|
||
|
|
};
|
||
|
|
const onSelect = (option) => {
|
||
|
|
const optionValue = getValue(option);
|
||
|
|
if (props.multiple) {
|
||
|
|
let selectedOptions = props.modelValue.slice();
|
||
|
|
const index = getValueIndex(selectedOptions, optionValue);
|
||
|
|
if (index > -1) {
|
||
|
|
selectedOptions = [
|
||
|
|
...selectedOptions.slice(0, index),
|
||
|
|
...selectedOptions.slice(index + 1)
|
||
|
|
];
|
||
|
|
states.cachedOptions.splice(index, 1);
|
||
|
|
removeNewOption(option);
|
||
|
|
} else if (props.multipleLimit <= 0 || selectedOptions.length < props.multipleLimit) {
|
||
|
|
selectedOptions = [...selectedOptions, optionValue];
|
||
|
|
states.cachedOptions.push(option);
|
||
|
|
selectNewOption(option);
|
||
|
|
}
|
||
|
|
update(selectedOptions);
|
||
|
|
if (option.created) {
|
||
|
|
handleQueryChange("");
|
||
|
|
}
|
||
|
|
if (props.filterable && !props.reserveKeyword) {
|
||
|
|
states.inputValue = "";
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
states.selectedLabel = getLabel(option);
|
||
|
|
!lodashUnified.isEqual(props.modelValue, optionValue) && update(optionValue);
|
||
|
|
expanded.value = false;
|
||
|
|
selectNewOption(option);
|
||
|
|
if (!option.created) {
|
||
|
|
clearAllNewOption();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
focus();
|
||
|
|
};
|
||
|
|
const deleteTag = (event, option) => {
|
||
|
|
let selectedOptions = props.modelValue.slice();
|
||
|
|
const index = getValueIndex(selectedOptions, getValue(option));
|
||
|
|
if (index > -1 && !selectDisabled.value) {
|
||
|
|
selectedOptions = [
|
||
|
|
...props.modelValue.slice(0, index),
|
||
|
|
...props.modelValue.slice(index + 1)
|
||
|
|
];
|
||
|
|
states.cachedOptions.splice(index, 1);
|
||
|
|
update(selectedOptions);
|
||
|
|
emit("remove-tag", getValue(option));
|
||
|
|
removeNewOption(option);
|
||
|
|
}
|
||
|
|
event.stopPropagation();
|
||
|
|
focus();
|
||
|
|
};
|
||
|
|
const focus = () => {
|
||
|
|
var _a;
|
||
|
|
(_a = inputRef.value) == null ? void 0 : _a.focus();
|
||
|
|
};
|
||
|
|
const blur = () => {
|
||
|
|
var _a;
|
||
|
|
if (expanded.value) {
|
||
|
|
expanded.value = false;
|
||
|
|
vue.nextTick(() => {
|
||
|
|
var _a2;
|
||
|
|
return (_a2 = inputRef.value) == null ? void 0 : _a2.blur();
|
||
|
|
});
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
(_a = inputRef.value) == null ? void 0 : _a.blur();
|
||
|
|
};
|
||
|
|
const handleEsc = () => {
|
||
|
|
if (states.inputValue.length > 0) {
|
||
|
|
states.inputValue = "";
|
||
|
|
} else {
|
||
|
|
expanded.value = false;
|
||
|
|
}
|
||
|
|
};
|
||
|
|
const getLastNotDisabledIndex = (value) => lodashUnified.findLastIndex(value, (it) => !states.cachedOptions.some((option) => getValue(option) === it && getDisabled(option)));
|
||
|
|
const handleDel = (e) => {
|
||
|
|
if (!props.multiple)
|
||
|
|
return;
|
||
|
|
if (e.code === aria.EVENT_CODE.delete)
|
||
|
|
return;
|
||
|
|
if (states.inputValue.length === 0) {
|
||
|
|
e.preventDefault();
|
||
|
|
const selected = props.modelValue.slice();
|
||
|
|
const lastNotDisabledIndex = getLastNotDisabledIndex(selected);
|
||
|
|
if (lastNotDisabledIndex < 0)
|
||
|
|
return;
|
||
|
|
const removeTagValue = selected[lastNotDisabledIndex];
|
||
|
|
selected.splice(lastNotDisabledIndex, 1);
|
||
|
|
const option = states.cachedOptions[lastNotDisabledIndex];
|
||
|
|
states.cachedOptions.splice(lastNotDisabledIndex, 1);
|
||
|
|
removeNewOption(option);
|
||
|
|
update(selected);
|
||
|
|
emit("remove-tag", removeTagValue);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
const handleClear = () => {
|
||
|
|
let emptyValue;
|
||
|
|
if (shared.isArray(props.modelValue)) {
|
||
|
|
emptyValue = [];
|
||
|
|
} else {
|
||
|
|
emptyValue = valueOnClear.value;
|
||
|
|
}
|
||
|
|
states.selectedLabel = "";
|
||
|
|
expanded.value = false;
|
||
|
|
update(emptyValue);
|
||
|
|
emit("clear");
|
||
|
|
clearAllNewOption();
|
||
|
|
focus();
|
||
|
|
};
|
||
|
|
const onKeyboardNavigate = (direction, hoveringIndex = void 0) => {
|
||
|
|
const options = filteredOptions.value;
|
||
|
|
if (!["forward", "backward"].includes(direction) || selectDisabled.value || options.length <= 0 || optionsAllDisabled.value || isComposing.value) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
if (!expanded.value) {
|
||
|
|
return toggleMenu();
|
||
|
|
}
|
||
|
|
if (types.isUndefined(hoveringIndex)) {
|
||
|
|
hoveringIndex = states.hoveringIndex;
|
||
|
|
}
|
||
|
|
let newIndex = -1;
|
||
|
|
if (direction === "forward") {
|
||
|
|
newIndex = hoveringIndex + 1;
|
||
|
|
if (newIndex >= options.length) {
|
||
|
|
newIndex = 0;
|
||
|
|
}
|
||
|
|
} else if (direction === "backward") {
|
||
|
|
newIndex = hoveringIndex - 1;
|
||
|
|
if (newIndex < 0 || newIndex >= options.length) {
|
||
|
|
newIndex = options.length - 1;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
const option = options[newIndex];
|
||
|
|
if (getDisabled(option) || option.type === "Group") {
|
||
|
|
return onKeyboardNavigate(direction, newIndex);
|
||
|
|
} else {
|
||
|
|
states.hoveringIndex = newIndex;
|
||
|
|
scrollToItem(newIndex);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
const onKeyboardSelect = () => {
|
||
|
|
if (!expanded.value) {
|
||
|
|
return toggleMenu();
|
||
|
|
} else if (~states.hoveringIndex && filteredOptions.value[states.hoveringIndex]) {
|
||
|
|
onSelect(filteredOptions.value[states.hoveringIndex]);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
const onHoverOption = (idx) => {
|
||
|
|
states.hoveringIndex = idx != null ? idx : -1;
|
||
|
|
};
|
||
|
|
const updateHoveringIndex = () => {
|
||
|
|
if (!props.multiple) {
|
||
|
|
states.hoveringIndex = filteredOptions.value.findIndex((item) => {
|
||
|
|
return getValueKey(getValue(item)) === getValueKey(props.modelValue);
|
||
|
|
});
|
||
|
|
} else {
|
||
|
|
states.hoveringIndex = filteredOptions.value.findIndex((item) => props.modelValue.some((modelValue) => getValueKey(modelValue) === getValueKey(getValue(item))));
|
||
|
|
}
|
||
|
|
};
|
||
|
|
const onInput = (event) => {
|
||
|
|
states.inputValue = event.target.value;
|
||
|
|
if (props.remote) {
|
||
|
|
debouncedOnInputChange();
|
||
|
|
} else {
|
||
|
|
return onInputChange();
|
||
|
|
}
|
||
|
|
};
|
||
|
|
const handleClickOutside = (event) => {
|
||
|
|
expanded.value = false;
|
||
|
|
if (isFocused.value) {
|
||
|
|
const _event = new FocusEvent("blur", event);
|
||
|
|
handleBlur(_event);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
const handleMenuEnter = () => {
|
||
|
|
states.isBeforeHide = false;
|
||
|
|
return vue.nextTick(() => {
|
||
|
|
if (~indexRef.value) {
|
||
|
|
scrollToItem(states.hoveringIndex);
|
||
|
|
}
|
||
|
|
});
|
||
|
|
};
|
||
|
|
const scrollToItem = (index) => {
|
||
|
|
menuRef.value.scrollToItem(index);
|
||
|
|
};
|
||
|
|
const getOption = (value, cachedOptions) => {
|
||
|
|
const selectValue = getValueKey(value);
|
||
|
|
if (allOptionsValueMap.value.has(selectValue)) {
|
||
|
|
const { option } = allOptionsValueMap.value.get(selectValue);
|
||
|
|
return option;
|
||
|
|
}
|
||
|
|
if (cachedOptions && cachedOptions.length) {
|
||
|
|
const option = cachedOptions.find((option2) => getValueKey(getValue(option2)) === selectValue);
|
||
|
|
if (option) {
|
||
|
|
return option;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return {
|
||
|
|
[aliasProps.value.value]: value,
|
||
|
|
[aliasProps.value.label]: value
|
||
|
|
};
|
||
|
|
};
|
||
|
|
const getIndex = (option) => {
|
||
|
|
var _a, _b;
|
||
|
|
return (_b = (_a = allOptionsValueMap.value.get(getValue(option))) == null ? void 0 : _a.index) != null ? _b : -1;
|
||
|
|
};
|
||
|
|
const initStates = (needUpdateSelectedLabel = false) => {
|
||
|
|
if (props.multiple) {
|
||
|
|
if (props.modelValue.length > 0) {
|
||
|
|
const cachedOptions = states.cachedOptions.slice();
|
||
|
|
states.cachedOptions.length = 0;
|
||
|
|
states.previousValue = props.modelValue.toString();
|
||
|
|
for (const value of props.modelValue) {
|
||
|
|
const option = getOption(value, cachedOptions);
|
||
|
|
states.cachedOptions.push(option);
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
states.cachedOptions = [];
|
||
|
|
states.previousValue = void 0;
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
if (hasModelValue.value) {
|
||
|
|
states.previousValue = props.modelValue;
|
||
|
|
const options = filteredOptions.value;
|
||
|
|
const selectedItemIndex = options.findIndex((option) => getValueKey(getValue(option)) === getValueKey(props.modelValue));
|
||
|
|
if (~selectedItemIndex) {
|
||
|
|
states.selectedLabel = getLabel(options[selectedItemIndex]);
|
||
|
|
} else {
|
||
|
|
if (!states.selectedLabel || needUpdateSelectedLabel) {
|
||
|
|
states.selectedLabel = getValueKey(props.modelValue);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
states.selectedLabel = "";
|
||
|
|
states.previousValue = void 0;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
clearAllNewOption();
|
||
|
|
calculatePopperSize();
|
||
|
|
};
|
||
|
|
vue.watch(() => props.fitInputWidth, () => {
|
||
|
|
calculatePopperSize();
|
||
|
|
});
|
||
|
|
vue.watch(expanded, (val) => {
|
||
|
|
if (val) {
|
||
|
|
if (!props.persistent) {
|
||
|
|
calculatePopperSize();
|
||
|
|
}
|
||
|
|
handleQueryChange("");
|
||
|
|
} else {
|
||
|
|
states.inputValue = "";
|
||
|
|
states.previousQuery = null;
|
||
|
|
states.isBeforeHide = true;
|
||
|
|
createNewOption("");
|
||
|
|
}
|
||
|
|
emit("visible-change", val);
|
||
|
|
});
|
||
|
|
vue.watch(() => props.modelValue, (val, oldVal) => {
|
||
|
|
var _a;
|
||
|
|
const isValEmpty = !val || shared.isArray(val) && val.length === 0;
|
||
|
|
if (isValEmpty || props.multiple && !lodashUnified.isEqual(val.toString(), states.previousValue) || !props.multiple && getValueKey(val) !== getValueKey(states.previousValue)) {
|
||
|
|
initStates(true);
|
||
|
|
}
|
||
|
|
if (!lodashUnified.isEqual(val, oldVal) && props.validateEvent) {
|
||
|
|
(_a = elFormItem == null ? void 0 : elFormItem.validate) == null ? void 0 : _a.call(elFormItem, "change").catch((err) => error.debugWarn());
|
||
|
|
}
|
||
|
|
}, {
|
||
|
|
deep: true
|
||
|
|
});
|
||
|
|
vue.watch(() => props.options, () => {
|
||
|
|
const input = inputRef.value;
|
||
|
|
if (!input || input && document.activeElement !== input) {
|
||
|
|
initStates();
|
||
|
|
}
|
||
|
|
}, {
|
||
|
|
deep: true,
|
||
|
|
flush: "post"
|
||
|
|
});
|
||
|
|
vue.watch(() => filteredOptions.value, () => {
|
||
|
|
calculatePopperSize();
|
||
|
|
return menuRef.value && vue.nextTick(menuRef.value.resetScrollTop);
|
||
|
|
});
|
||
|
|
vue.watchEffect(() => {
|
||
|
|
if (states.isBeforeHide)
|
||
|
|
return;
|
||
|
|
updateOptions();
|
||
|
|
});
|
||
|
|
vue.watchEffect(() => {
|
||
|
|
const { valueKey, options } = props;
|
||
|
|
const duplicateValue = /* @__PURE__ */ new Map();
|
||
|
|
for (const item of options) {
|
||
|
|
const optionValue = getValue(item);
|
||
|
|
let v = optionValue;
|
||
|
|
if (shared.isObject(v)) {
|
||
|
|
v = lodashUnified.get(optionValue, valueKey);
|
||
|
|
}
|
||
|
|
if (duplicateValue.get(v)) {
|
||
|
|
break;
|
||
|
|
} else {
|
||
|
|
duplicateValue.set(v, true);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
});
|
||
|
|
vue.onMounted(() => {
|
||
|
|
initStates();
|
||
|
|
});
|
||
|
|
core.useResizeObserver(selectRef, handleResize);
|
||
|
|
core.useResizeObserver(selectionRef, resetSelectionWidth);
|
||
|
|
core.useResizeObserver(menuRef, updateTooltip);
|
||
|
|
core.useResizeObserver(wrapperRef, updateTooltip);
|
||
|
|
core.useResizeObserver(tagMenuRef, updateTagTooltip);
|
||
|
|
core.useResizeObserver(collapseItemRef, resetCollapseItemWidth);
|
||
|
|
return {
|
||
|
|
inputId,
|
||
|
|
collapseTagSize,
|
||
|
|
currentPlaceholder,
|
||
|
|
expanded,
|
||
|
|
emptyText,
|
||
|
|
popupHeight,
|
||
|
|
debounce,
|
||
|
|
allOptions,
|
||
|
|
allOptionsValueMap,
|
||
|
|
filteredOptions,
|
||
|
|
iconComponent,
|
||
|
|
iconReverse,
|
||
|
|
tagStyle,
|
||
|
|
collapseTagStyle,
|
||
|
|
popperSize,
|
||
|
|
dropdownMenuVisible,
|
||
|
|
hasModelValue,
|
||
|
|
shouldShowPlaceholder,
|
||
|
|
selectDisabled,
|
||
|
|
selectSize,
|
||
|
|
needStatusIcon,
|
||
|
|
showClearBtn,
|
||
|
|
states,
|
||
|
|
isFocused,
|
||
|
|
nsSelect,
|
||
|
|
nsInput,
|
||
|
|
inputRef,
|
||
|
|
menuRef,
|
||
|
|
tagMenuRef,
|
||
|
|
tooltipRef,
|
||
|
|
tagTooltipRef,
|
||
|
|
selectRef,
|
||
|
|
wrapperRef,
|
||
|
|
selectionRef,
|
||
|
|
prefixRef,
|
||
|
|
suffixRef,
|
||
|
|
collapseItemRef,
|
||
|
|
popperRef,
|
||
|
|
validateState,
|
||
|
|
validateIcon,
|
||
|
|
showTagList,
|
||
|
|
collapseTagList,
|
||
|
|
debouncedOnInputChange,
|
||
|
|
deleteTag,
|
||
|
|
getLabel,
|
||
|
|
getValue,
|
||
|
|
getDisabled,
|
||
|
|
getValueKey,
|
||
|
|
getIndex,
|
||
|
|
handleClear,
|
||
|
|
handleClickOutside,
|
||
|
|
handleDel,
|
||
|
|
handleEsc,
|
||
|
|
focus,
|
||
|
|
blur,
|
||
|
|
handleMenuEnter,
|
||
|
|
handleResize,
|
||
|
|
resetSelectionWidth,
|
||
|
|
updateTooltip,
|
||
|
|
updateTagTooltip,
|
||
|
|
updateOptions,
|
||
|
|
toggleMenu,
|
||
|
|
scrollTo: scrollToItem,
|
||
|
|
onInput,
|
||
|
|
onKeyboardNavigate,
|
||
|
|
onKeyboardSelect,
|
||
|
|
onSelect,
|
||
|
|
onHover: onHoverOption,
|
||
|
|
handleCompositionStart,
|
||
|
|
handleCompositionEnd,
|
||
|
|
handleCompositionUpdate
|
||
|
|
};
|
||
|
|
};
|
||
|
|
|
||
|
|
exports["default"] = useSelect;
|
||
|
|
//# sourceMappingURL=useSelect.js.map
|