element-plus
Version:
A Component Library for Vue 3
653 lines (648 loc) • 22.1 kB
JavaScript
Object.defineProperty(exports, '__esModule', { value: true });
var vue = require('vue');
var shared = require('@vue/shared');
var lodashUnified = require('lodash-unified');
var core = require('@vueuse/core');
require('../../../constants/index.js');
require('../../../utils/index.js');
require('../../../hooks/index.js');
require('../../form/index.js');
var useInput = require('../../select-v2/src/useInput.js');
var index = require('../../../hooks/use-locale/index.js');
var index$1 = require('../../../hooks/use-id/index.js');
var index$2 = require('../../../hooks/use-namespace/index.js');
var index$3 = require('../../../hooks/use-focus-controller/index.js');
var useFormItem = require('../../form/src/hooks/use-form-item.js');
var icon = require('../../../utils/vue/icon.js');
var useFormCommonProps = require('../../form/src/hooks/use-form-common-props.js');
var error = require('../../../utils/error.js');
var types = require('../../../utils/types.js');
var event = require('../../../constants/event.js');
var aria = require('../../../constants/aria.js');
var scroll = require('../../../utils/dom/scroll.js');
const MINIMUM_INPUT_WIDTH = 11;
const useSelect = (props, emit) => {
const { t } = index.useLocale();
const contentId = index$1.useId();
const nsSelect = index$2.useNamespace("select");
const nsInput = index$2.useNamespace("input");
const states = vue.reactive({
inputValue: "",
options: /* @__PURE__ */ new Map(),
cachedOptions: /* @__PURE__ */ new Map(),
disabledOptions: /* @__PURE__ */ new Map(),
optionValues: [],
selected: props.multiple ? [] : {},
selectionWidth: 0,
calculatorWidth: 0,
collapseItemWidth: 0,
selectedLabel: "",
hoveringIndex: -1,
previousQuery: null,
inputHovering: false,
menuVisibleOnFocus: false,
isBeforeHide: false
});
const selectRef = vue.ref(null);
const selectionRef = vue.ref(null);
const tooltipRef = vue.ref(null);
const tagTooltipRef = vue.ref(null);
const inputRef = vue.ref(null);
const calculatorRef = vue.ref(null);
const prefixRef = vue.ref(null);
const suffixRef = vue.ref(null);
const menuRef = vue.ref(null);
const tagMenuRef = vue.ref(null);
const collapseItemRef = vue.ref(null);
const scrollbarRef = vue.ref(null);
const { wrapperRef, isFocused, handleFocus, handleBlur } = index$3.useFocusController(inputRef, {
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() {
expanded.value = false;
states.menuVisibleOnFocus = false;
}
});
const expanded = vue.ref(false);
const hoverOption = vue.ref();
const { form, formItem } = useFormItem.useFormItem();
const { inputId } = useFormItem.useFormItemInputId(props, {
formItemContext: formItem
});
const selectDisabled = vue.computed(() => props.disabled || (form == null ? void 0 : form.disabled));
const hasEmptyStringOption = vue.computed(() => optionsArray.value.some((option) => option.value === ""));
const hasModelValue = vue.computed(() => {
return props.multiple ? shared.isArray(props.modelValue) && props.modelValue.length > 0 : !lodashUnified.isNil(props.modelValue) && (props.modelValue !== "" || hasEmptyStringOption.value);
});
const showClose = vue.computed(() => {
const criteria = props.clearable && !selectDisabled.value && states.inputHovering && hasModelValue.value;
return criteria;
});
const iconComponent = vue.computed(() => props.remote && props.filterable && !props.remoteShowSuffix ? "" : props.suffixIcon);
const iconReverse = vue.computed(() => nsSelect.is("reverse", iconComponent.value && expanded.value));
const validateState = vue.computed(() => (formItem == null ? void 0 : formItem.validateState) || "");
const validateIcon = vue.computed(() => 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 && states.options.size === 0)
return false;
if (props.filterable && states.inputValue && states.options.size > 0 && filteredOptionsCount.value === 0) {
return props.noMatchText || t("el.select.noMatch");
}
if (states.options.size === 0) {
return props.noDataText || t("el.select.noData");
}
}
return null;
});
const filteredOptionsCount = vue.computed(() => optionsArray.value.filter((option) => option.visible).length);
const optionsArray = vue.computed(() => {
const list = Array.from(states.options.values());
const newList = [];
states.optionValues.forEach((item) => {
const index = list.findIndex((i) => i.value === item);
if (index > -1) {
newList.push(list[index]);
}
});
return newList.length >= list.length ? newList : list;
});
const cachedOptionsArray = vue.computed(() => Array.from(states.cachedOptions.values()));
const showNewOption = vue.computed(() => {
const hasExistingOption = optionsArray.value.filter((option) => {
return !option.created;
}).some((option) => {
return option.currentLabel === states.inputValue;
});
return props.filterable && props.allowCreate && states.inputValue !== "" && !hasExistingOption;
});
const updateOptions = () => {
if (props.filterable && shared.isFunction(props.filterMethod))
return;
if (props.filterable && props.remote && shared.isFunction(props.remoteMethod))
return;
optionsArray.value.forEach((option) => {
var _a;
(_a = option.updateOption) == null ? void 0 : _a.call(option, states.inputValue);
});
};
const selectSize = useFormCommonProps.useFormSize();
const collapseTagSize = vue.computed(() => ["small"].includes(selectSize.value) ? "small" : "default");
const dropdownMenuVisible = vue.computed({
get() {
return expanded.value && emptyText.value !== false;
},
set(val) {
expanded.value = val;
}
});
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;
});
vue.watch(() => props.modelValue, (val, oldVal) => {
if (props.multiple) {
if (props.filterable && !props.reserveKeyword) {
states.inputValue = "";
handleQueryChange("");
}
}
setSelected();
if (!lodashUnified.isEqual(val, oldVal) && props.validateEvent) {
formItem == null ? void 0 : formItem.validate("change").catch((err) => error.debugWarn(err));
}
}, {
flush: "post",
deep: true
});
vue.watch(() => expanded.value, (val) => {
if (val) {
handleQueryChange(states.inputValue);
} else {
states.inputValue = "";
states.previousQuery = null;
states.isBeforeHide = true;
}
emit("visible-change", val);
});
vue.watch(() => states.options.entries(), () => {
var _a;
if (!core.isClient)
return;
const inputs = ((_a = selectRef.value) == null ? void 0 : _a.querySelectorAll("input")) || [];
if (!props.filterable && !props.defaultFirstOption && !types.isUndefined(props.modelValue) || !Array.from(inputs).includes(document.activeElement)) {
setSelected();
}
if (props.defaultFirstOption && (props.filterable || props.remote) && filteredOptionsCount.value) {
checkDefaultFirstOption();
}
}, {
flush: "post"
});
vue.watch(() => states.hoveringIndex, (val) => {
if (types.isNumber(val) && val > -1) {
hoverOption.value = optionsArray.value[val] || {};
} else {
hoverOption.value = {};
}
optionsArray.value.forEach((option) => {
option.hover = hoverOption.value === option;
});
});
vue.watchEffect(() => {
if (states.isBeforeHide)
return;
updateOptions();
});
const handleQueryChange = (val) => {
if (states.previousQuery === val) {
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) && filteredOptionsCount.value) {
vue.nextTick(checkDefaultFirstOption);
} else {
vue.nextTick(updateHoveringIndex);
}
};
const checkDefaultFirstOption = () => {
const optionsInDropdown = optionsArray.value.filter((n) => n.visible && !n.disabled && !n.states.groupDisabled);
const userCreatedOption = optionsInDropdown.find((n) => n.created);
const firstOriginOption = optionsInDropdown[0];
states.hoveringIndex = getValueIndex(optionsArray.value, userCreatedOption || firstOriginOption);
};
const setSelected = () => {
if (!props.multiple) {
const option = getOption(props.modelValue);
states.selectedLabel = option.currentLabel;
states.selected = option;
return;
} else {
states.selectedLabel = "";
}
const result = [];
if (shared.isArray(props.modelValue)) {
props.modelValue.forEach((value) => {
result.push(getOption(value));
});
}
states.selected = result;
};
const getOption = (value) => {
let option;
const isObjectValue = shared.toRawType(value).toLowerCase() === "object";
const isNull = shared.toRawType(value).toLowerCase() === "null";
const isUndefined2 = shared.toRawType(value).toLowerCase() === "undefined";
for (let i = states.cachedOptions.size - 1; i >= 0; i--) {
const cachedOption = cachedOptionsArray.value[i];
const isEqualValue = isObjectValue ? lodashUnified.get(cachedOption.value, props.valueKey) === lodashUnified.get(value, props.valueKey) : cachedOption.value === value;
if (isEqualValue) {
option = {
value,
currentLabel: cachedOption.currentLabel,
isDisabled: cachedOption.isDisabled
};
break;
}
}
if (option)
return option;
const label = isObjectValue ? value.label : !isNull && !isUndefined2 ? value : "";
const newOption = {
value,
currentLabel: label
};
return newOption;
};
const updateHoveringIndex = () => {
if (!props.multiple) {
states.hoveringIndex = optionsArray.value.findIndex((item) => {
return getValueKey(item) === getValueKey(states.selected);
});
} else {
states.hoveringIndex = optionsArray.value.findIndex((item) => states.selected.some((selected) => getValueKey(selected) === getValueKey(item)));
}
};
const resetSelectionWidth = () => {
states.selectionWidth = selectionRef.value.getBoundingClientRect().width;
};
const resetCalculatorWidth = () => {
states.calculatorWidth = calculatorRef.value.getBoundingClientRect().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 onInputChange = () => {
if (states.inputValue.length > 0 && !expanded.value) {
expanded.value = true;
}
handleQueryChange(states.inputValue);
};
const onInput = (event) => {
states.inputValue = event.target.value;
if (props.remote) {
debouncedOnInputChange();
} else {
return onInputChange();
}
};
const debouncedOnInputChange = lodashUnified.debounce(() => {
onInputChange();
}, debounce.value);
const emitChange = (val) => {
if (!lodashUnified.isEqual(props.modelValue, val)) {
emit(event.CHANGE_EVENT, val);
}
};
const getLastNotDisabledIndex = (value) => lodashUnified.findLastIndex(value, (it) => !states.disabledOptions.has(it));
const deletePrevTag = (e) => {
if (!props.multiple)
return;
if (e.code === aria.EVENT_CODE.delete)
return;
if (e.target.value.length <= 0) {
const value = props.modelValue.slice();
const lastNotDisabledIndex = getLastNotDisabledIndex(value);
if (lastNotDisabledIndex < 0)
return;
value.splice(lastNotDisabledIndex, 1);
emit(event.UPDATE_MODEL_EVENT, value);
emitChange(value);
}
};
const deleteTag = (event$1, tag) => {
const index = states.selected.indexOf(tag);
if (index > -1 && !selectDisabled.value) {
const value = props.modelValue.slice();
value.splice(index, 1);
emit(event.UPDATE_MODEL_EVENT, value);
emitChange(value);
emit("remove-tag", tag.value);
}
event$1.stopPropagation();
focus();
};
const deleteSelected = (event$1) => {
event$1.stopPropagation();
const value = props.multiple ? [] : void 0;
if (props.multiple) {
for (const item of states.selected) {
if (item.isDisabled)
value.push(item.value);
}
}
emit(event.UPDATE_MODEL_EVENT, value);
emitChange(value);
states.hoveringIndex = -1;
expanded.value = false;
emit("clear");
focus();
};
const handleOptionSelect = (option) => {
if (props.multiple) {
const value = (props.modelValue || []).slice();
const optionIndex = getValueIndex(value, option.value);
if (optionIndex > -1) {
value.splice(optionIndex, 1);
} else if (props.multipleLimit <= 0 || value.length < props.multipleLimit) {
value.push(option.value);
}
emit(event.UPDATE_MODEL_EVENT, value);
emitChange(value);
if (option.created) {
handleQueryChange("");
}
if (props.filterable && !props.reserveKeyword) {
states.inputValue = "";
}
} else {
emit(event.UPDATE_MODEL_EVENT, option.value);
emitChange(option.value);
expanded.value = false;
}
focus();
if (expanded.value)
return;
vue.nextTick(() => {
scrollToOption(option);
});
};
const getValueIndex = (arr = [], value) => {
if (!shared.isObject(value))
return arr.indexOf(value);
const valueKey = props.valueKey;
let index = -1;
arr.some((item, i) => {
if (vue.toRaw(lodashUnified.get(item, valueKey)) === lodashUnified.get(value, valueKey)) {
index = i;
return true;
}
return false;
});
return index;
};
const scrollToOption = (option) => {
var _a, _b, _c, _d, _e;
const targetOption = shared.isArray(option) ? option[0] : option;
let target = null;
if (targetOption == null ? void 0 : targetOption.value) {
const options = optionsArray.value.filter((item) => item.value === targetOption.value);
if (options.length > 0) {
target = options[0].$el;
}
}
if (tooltipRef.value && target) {
const menu = (_d = (_c = (_b = (_a = tooltipRef.value) == null ? void 0 : _a.popperRef) == null ? void 0 : _b.contentRef) == null ? void 0 : _c.querySelector) == null ? void 0 : _d.call(_c, `.${nsSelect.be("dropdown", "wrap")}`);
if (menu) {
scroll.scrollIntoView(menu, target);
}
}
(_e = scrollbarRef.value) == null ? void 0 : _e.handleScroll();
};
const onOptionCreate = (vm) => {
states.options.set(vm.value, vm);
states.cachedOptions.set(vm.value, vm);
vm.disabled && states.disabledOptions.set(vm.value, vm);
};
const onOptionDestroy = (key, vm) => {
if (states.options.get(key) === vm) {
states.options.delete(key);
}
};
const {
handleCompositionStart,
handleCompositionUpdate,
handleCompositionEnd
} = useInput.useInput((e) => onInput(e));
const popperRef = vue.computed(() => {
var _a, _b;
return (_b = (_a = tooltipRef.value) == null ? void 0 : _a.popperRef) == null ? void 0 : _b.contentRef;
});
const handleMenuEnter = () => {
vue.nextTick(() => scrollToOption(states.selected));
};
const focus = () => {
var _a;
(_a = inputRef.value) == null ? void 0 : _a.focus();
};
const blur = () => {
handleClickOutside();
};
const handleClearClick = (event) => {
deleteSelected(event);
};
const handleClickOutside = (event) => {
expanded.value = false;
if (isFocused.value) {
const _event = new FocusEvent("focus", event);
vue.nextTick(() => handleBlur(_event));
}
};
const handleEsc = () => {
if (states.inputValue.length > 0) {
states.inputValue = "";
} else {
expanded.value = false;
}
};
const toggleMenu = () => {
if (selectDisabled.value)
return;
if (states.menuVisibleOnFocus) {
states.menuVisibleOnFocus = false;
} else {
expanded.value = !expanded.value;
}
};
const selectOption = () => {
if (!expanded.value) {
toggleMenu();
} else {
if (optionsArray.value[states.hoveringIndex]) {
handleOptionSelect(optionsArray.value[states.hoveringIndex]);
}
}
};
const getValueKey = (item) => {
return shared.isObject(item.value) ? lodashUnified.get(item.value, props.valueKey) : item.value;
};
const optionsAllDisabled = vue.computed(() => optionsArray.value.filter((option) => option.visible).every((option) => option.disabled));
const showTagList = vue.computed(() => {
if (!props.multiple) {
return [];
}
return props.collapseTags ? states.selected.slice(0, props.maxCollapseTags) : states.selected;
});
const collapseTagList = vue.computed(() => {
if (!props.multiple) {
return [];
}
return props.collapseTags ? states.selected.slice(props.maxCollapseTags) : [];
});
const navigateOptions = (direction) => {
if (!expanded.value) {
expanded.value = true;
return;
}
if (states.options.size === 0 || filteredOptionsCount.value === 0)
return;
if (!optionsAllDisabled.value) {
if (direction === "next") {
states.hoveringIndex++;
if (states.hoveringIndex === states.options.size) {
states.hoveringIndex = 0;
}
} else if (direction === "prev") {
states.hoveringIndex--;
if (states.hoveringIndex < 0) {
states.hoveringIndex = states.options.size - 1;
}
}
const option = optionsArray.value[states.hoveringIndex];
if (option.disabled === true || option.states.groupDisabled === true || !option.visible) {
navigateOptions(direction);
}
vue.nextTick(() => scrollToOption(hoverOption.value));
}
};
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 maxWidth = collapseItemRef.value && props.maxCollapseTags === 1 ? states.selectionWidth - states.collapseItemWidth - gapWidth : states.selectionWidth;
return { maxWidth: `${maxWidth}px` };
});
const collapseTagStyle = vue.computed(() => {
return { maxWidth: `${states.selectionWidth}px` };
});
const inputStyle = vue.computed(() => ({
width: `${Math.max(states.calculatorWidth, MINIMUM_INPUT_WIDTH)}px`
}));
if (props.multiple && !shared.isArray(props.modelValue)) {
emit(event.UPDATE_MODEL_EVENT, []);
}
if (!props.multiple && shared.isArray(props.modelValue)) {
emit(event.UPDATE_MODEL_EVENT, "");
}
core.useResizeObserver(selectionRef, resetSelectionWidth);
core.useResizeObserver(calculatorRef, resetCalculatorWidth);
core.useResizeObserver(menuRef, updateTooltip);
core.useResizeObserver(wrapperRef, updateTooltip);
core.useResizeObserver(tagMenuRef, updateTagTooltip);
core.useResizeObserver(collapseItemRef, resetCollapseItemWidth);
vue.onMounted(() => {
setSelected();
});
return {
inputId,
contentId,
nsSelect,
nsInput,
states,
isFocused,
expanded,
optionsArray,
hoverOption,
selectSize,
filteredOptionsCount,
resetCalculatorWidth,
updateTooltip,
updateTagTooltip,
debouncedOnInputChange,
onInput,
deletePrevTag,
deleteTag,
deleteSelected,
handleOptionSelect,
scrollToOption,
hasModelValue,
shouldShowPlaceholder,
currentPlaceholder,
showClose,
iconComponent,
iconReverse,
validateState,
validateIcon,
showNewOption,
updateOptions,
collapseTagSize,
setSelected,
selectDisabled,
emptyText,
handleCompositionStart,
handleCompositionUpdate,
handleCompositionEnd,
onOptionCreate,
onOptionDestroy,
handleMenuEnter,
handleFocus,
focus,
blur,
handleBlur,
handleClearClick,
handleClickOutside,
handleEsc,
toggleMenu,
selectOption,
getValueKey,
navigateOptions,
dropdownMenuVisible,
showTagList,
collapseTagList,
tagStyle,
collapseTagStyle,
inputStyle,
popperRef,
inputRef,
tooltipRef,
tagTooltipRef,
calculatorRef,
prefixRef,
suffixRef,
selectRef,
wrapperRef,
selectionRef,
scrollbarRef,
menuRef,
tagMenuRef,
collapseItemRef
};
};
exports.useSelect = useSelect;
//# sourceMappingURL=useSelect.js.map
;