element-plus
Version:
A Component Library for Vue 3
727 lines (722 loc) • 24.3 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');
var index = require('../../../hooks/use-locale/index.js');
var index$1 = require('../../../hooks/use-namespace/index.js');
var index$2 = require('../../../hooks/use-deprecated/index.js');
var index$3 = require('../../../hooks/use-form-item/index.js');
var index$4 = require('../../../hooks/use-common-props/index.js');
var error = require('../../../utils/error.js');
var size = require('../../../utils/vue/size.js');
var event = require('../../../constants/event.js');
var scroll = require('../../../utils/dom/scroll.js');
var aria = require('../../../constants/aria.js');
var i18n = require('../../../utils/i18n.js');
function useSelectStates(props) {
const { t } = index.useLocale();
return vue.reactive({
options: /* @__PURE__ */ new Map(),
cachedOptions: /* @__PURE__ */ new Map(),
createdLabel: null,
createdSelected: false,
selected: props.multiple ? [] : {},
inputLength: 20,
inputWidth: 0,
optionsCount: 0,
filteredOptionsCount: 0,
visible: false,
softFocus: false,
selectedLabel: "",
hoverIndex: -1,
query: "",
previousQuery: null,
inputHovering: false,
cachedPlaceHolder: "",
currentPlaceholder: t("el.select.placeholder"),
menuVisibleOnFocus: false,
isOnComposition: false,
isSilentBlur: false,
prefixWidth: 11,
tagInMultiLine: false,
mouseEnter: false
});
}
const useSelect = (props, states, ctx) => {
const { t } = index.useLocale();
const ns = index$1.useNamespace("select");
index$2.useDeprecated({
from: "suffixTransition",
replacement: "override style scheme",
version: "2.3.0",
scope: "props",
ref: "https://element-plus.org/en-US/component/select.html#select-attributes"
}, vue.computed(() => props.suffixTransition === false));
const reference = vue.ref(null);
const input = vue.ref(null);
const tooltipRef = vue.ref(null);
const tags = vue.ref(null);
const selectWrapper = vue.ref(null);
const scrollbar = vue.ref(null);
const hoverOption = vue.ref(-1);
const queryChange = vue.shallowRef({ query: "" });
const groupQueryChange = vue.shallowRef("");
const { form, formItem } = index$3.useFormItem();
const readonly = vue.computed(() => !props.filterable || props.multiple || !states.visible);
const selectDisabled = vue.computed(() => props.disabled || (form == null ? void 0 : form.disabled));
const showClose = vue.computed(() => {
const hasValue = props.multiple ? Array.isArray(props.modelValue) && props.modelValue.length > 0 : props.modelValue !== void 0 && props.modelValue !== null && props.modelValue !== "";
const criteria = props.clearable && !selectDisabled.value && states.inputHovering && hasValue;
return criteria;
});
const iconComponent = vue.computed(() => props.remote && props.filterable && !props.remoteShowSuffix ? "" : props.suffixIcon);
const iconReverse = vue.computed(() => ns.is("reverse", iconComponent.value && states.visible && props.suffixTransition));
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.query === "" && states.options.size === 0)
return false;
if (props.filterable && states.query && states.options.size > 0 && states.filteredOptionsCount === 0) {
return props.noMatchText || t("el.select.noMatch");
}
if (states.options.size === 0) {
return props.noDataText || t("el.select.noData");
}
}
return null;
});
const optionsArray = vue.computed(() => Array.from(states.options.values()));
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.query;
});
return props.filterable && props.allowCreate && states.query !== "" && !hasExistingOption;
});
const selectSize = index$4.useSize();
const collapseTagSize = vue.computed(() => ["small"].includes(selectSize.value) ? "small" : "default");
const dropMenuVisible = vue.computed({
get() {
return states.visible && emptyText.value !== false;
},
set(val) {
states.visible = val;
}
});
vue.watch([() => selectDisabled.value, () => selectSize.value, () => form == null ? void 0 : form.size], () => {
vue.nextTick(() => {
resetInputHeight();
});
});
vue.watch(() => props.placeholder, (val) => {
states.cachedPlaceHolder = states.currentPlaceholder = val;
});
vue.watch(() => props.modelValue, (val, oldVal) => {
if (props.multiple) {
resetInputHeight();
if (val && val.length > 0 || input.value && states.query !== "") {
states.currentPlaceholder = "";
} else {
states.currentPlaceholder = states.cachedPlaceHolder;
}
if (props.filterable && !props.reserveKeyword) {
states.query = "";
handleQueryChange(states.query);
}
}
setSelected();
if (props.filterable && !props.multiple) {
states.inputLength = 20;
}
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(() => states.visible, (val) => {
var _a, _b, _c;
if (!val) {
if (props.filterable) {
if (shared.isFunction(props.filterMethod)) {
props.filterMethod();
}
if (shared.isFunction(props.remoteMethod)) {
props.remoteMethod();
}
}
input.value && input.value.blur();
states.query = "";
states.previousQuery = null;
states.selectedLabel = "";
states.inputLength = 20;
states.menuVisibleOnFocus = false;
resetHoverIndex();
vue.nextTick(() => {
if (input.value && input.value.value === "" && states.selected.length === 0) {
states.currentPlaceholder = states.cachedPlaceHolder;
}
});
if (!props.multiple) {
if (states.selected) {
if (props.filterable && props.allowCreate && states.createdSelected && states.createdLabel) {
states.selectedLabel = states.createdLabel;
} else {
states.selectedLabel = states.selected.currentLabel;
}
if (props.filterable)
states.query = states.selectedLabel;
}
if (props.filterable) {
states.currentPlaceholder = states.cachedPlaceHolder;
}
}
} else {
(_b = (_a = tooltipRef.value) == null ? void 0 : _a.updatePopper) == null ? void 0 : _b.call(_a);
if (props.filterable) {
states.filteredOptionsCount = states.optionsCount;
states.query = props.remote ? "" : states.selectedLabel;
if (props.multiple) {
(_c = input.value) == null ? void 0 : _c.focus();
} else {
if (states.selectedLabel) {
states.currentPlaceholder = `${states.selectedLabel}`;
states.selectedLabel = "";
}
}
handleQueryChange(states.query);
if (!props.multiple && !props.remote) {
queryChange.value.query = "";
vue.triggerRef(queryChange);
vue.triggerRef(groupQueryChange);
}
}
}
ctx.emit("visible-change", val);
});
vue.watch(() => states.options.entries(), () => {
var _a, _b, _c;
if (!core.isClient)
return;
(_b = (_a = tooltipRef.value) == null ? void 0 : _a.updatePopper) == null ? void 0 : _b.call(_a);
if (props.multiple) {
resetInputHeight();
}
const inputs = ((_c = selectWrapper.value) == null ? void 0 : _c.querySelectorAll("input")) || [];
if (!Array.from(inputs).includes(document.activeElement)) {
setSelected();
}
if (props.defaultFirstOption && (props.filterable || props.remote) && states.filteredOptionsCount) {
checkDefaultFirstOption();
}
}, {
flush: "post"
});
vue.watch(() => states.hoverIndex, (val) => {
if (typeof val === "number" && val > -1) {
hoverOption.value = optionsArray.value[val] || {};
} else {
hoverOption.value = {};
}
optionsArray.value.forEach((option) => {
option.hover = hoverOption.value === option;
});
});
const resetInputHeight = () => {
if (props.collapseTags && !props.filterable)
return;
vue.nextTick(() => {
var _a, _b;
if (!reference.value)
return;
const input2 = reference.value.$el.querySelector("input");
const _tags = tags.value;
const sizeInMap = size.getComponentSize(selectSize.value || (form == null ? void 0 : form.size));
input2.style.height = `${(states.selected.length === 0 ? sizeInMap : Math.max(_tags ? _tags.clientHeight + (_tags.clientHeight > sizeInMap ? 6 : 0) : 0, sizeInMap)) - 2}px`;
states.tagInMultiLine = Number.parseFloat(input2.style.height) >= sizeInMap;
if (states.visible && emptyText.value !== false) {
(_b = (_a = tooltipRef.value) == null ? void 0 : _a.updatePopper) == null ? void 0 : _b.call(_a);
}
});
};
const handleQueryChange = async (val) => {
if (states.previousQuery === val || states.isOnComposition)
return;
if (states.previousQuery === null && (typeof props.filterMethod === "function" || typeof props.remoteMethod === "function")) {
states.previousQuery = val;
return;
}
states.previousQuery = val;
vue.nextTick(() => {
var _a, _b;
if (states.visible)
(_b = (_a = tooltipRef.value) == null ? void 0 : _a.updatePopper) == null ? void 0 : _b.call(_a);
});
states.hoverIndex = -1;
if (props.multiple && props.filterable) {
vue.nextTick(() => {
const length = input.value.value.length * 15 + 20;
states.inputLength = props.collapseTags ? Math.min(50, length) : length;
managePlaceholder();
resetInputHeight();
});
}
if (props.remote && typeof props.remoteMethod === "function") {
states.hoverIndex = -1;
props.remoteMethod(val);
} else if (typeof props.filterMethod === "function") {
props.filterMethod(val);
vue.triggerRef(groupQueryChange);
} else {
states.filteredOptionsCount = states.optionsCount;
queryChange.value.query = val;
vue.triggerRef(queryChange);
vue.triggerRef(groupQueryChange);
}
if (props.defaultFirstOption && (props.filterable || props.remote) && states.filteredOptionsCount) {
await vue.nextTick();
checkDefaultFirstOption();
}
};
const managePlaceholder = () => {
if (states.currentPlaceholder !== "") {
states.currentPlaceholder = input.value.value ? "" : states.cachedPlaceHolder;
}
};
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.hoverIndex = getValueIndex(optionsArray.value, userCreatedOption || firstOriginOption);
};
const setSelected = () => {
var _a;
if (!props.multiple) {
const option = getOption(props.modelValue);
if ((_a = option.props) == null ? void 0 : _a.created) {
states.createdLabel = option.props.value;
states.createdSelected = true;
} else {
states.createdSelected = false;
}
states.selectedLabel = option.currentLabel;
states.selected = option;
if (props.filterable)
states.query = states.selectedLabel;
return;
} else {
states.selectedLabel = "";
}
const result = [];
if (Array.isArray(props.modelValue)) {
props.modelValue.forEach((value) => {
result.push(getOption(value));
});
}
states.selected = result;
vue.nextTick(() => {
resetInputHeight();
});
};
const getOption = (value) => {
let option;
const isObjectValue = shared.toRawType(value).toLowerCase() === "object";
const isNull = shared.toRawType(value).toLowerCase() === "null";
const isUndefined = 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 && !isUndefined ? value : "";
const newOption = {
value,
currentLabel: label
};
if (props.multiple) {
;
newOption.hitState = false;
}
return newOption;
};
const resetHoverIndex = () => {
setTimeout(() => {
const valueKey = props.valueKey;
if (!props.multiple) {
states.hoverIndex = optionsArray.value.findIndex((item) => {
return getValueKey(item) === getValueKey(states.selected);
});
} else {
if (states.selected.length > 0) {
states.hoverIndex = Math.min.apply(null, states.selected.map((selected) => {
return optionsArray.value.findIndex((item) => {
return lodashUnified.get(item, valueKey) === lodashUnified.get(selected, valueKey);
});
}));
} else {
states.hoverIndex = -1;
}
}
}, 300);
};
const handleResize = () => {
var _a, _b;
resetInputWidth();
(_b = (_a = tooltipRef.value) == null ? void 0 : _a.updatePopper) == null ? void 0 : _b.call(_a);
if (props.multiple && !props.filterable)
resetInputHeight();
};
const resetInputWidth = () => {
var _a;
states.inputWidth = (_a = reference.value) == null ? void 0 : _a.$el.getBoundingClientRect().width;
};
const onInputChange = () => {
if (props.filterable && states.query !== states.selectedLabel) {
states.query = states.selectedLabel;
handleQueryChange(states.query);
}
};
const debouncedOnInputChange = lodashUnified.debounce(() => {
onInputChange();
}, debounce.value);
const debouncedQueryChange = lodashUnified.debounce((e) => {
handleQueryChange(e.target.value);
}, debounce.value);
const emitChange = (val) => {
if (!lodashUnified.isEqual(props.modelValue, val)) {
ctx.emit(event.CHANGE_EVENT, val);
}
};
const deletePrevTag = (e) => {
if (e.target.value.length <= 0 && !toggleLastOptionHitState()) {
const value = props.modelValue.slice();
value.pop();
ctx.emit(event.UPDATE_MODEL_EVENT, value);
emitChange(value);
}
if (e.target.value.length === 1 && props.modelValue.length === 0) {
states.currentPlaceholder = states.cachedPlaceHolder;
}
};
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);
ctx.emit(event.UPDATE_MODEL_EVENT, value);
emitChange(value);
ctx.emit("remove-tag", tag.value);
}
event$1.stopPropagation();
};
const deleteSelected = (event$1) => {
event$1.stopPropagation();
const value = props.multiple ? [] : "";
if (typeof value !== "string") {
for (const item of states.selected) {
if (item.isDisabled)
value.push(item.value);
}
}
ctx.emit(event.UPDATE_MODEL_EVENT, value);
emitChange(value);
states.hoverIndex = -1;
states.visible = false;
ctx.emit("clear");
};
const handleOptionSelect = (option, byClick) => {
var _a;
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);
}
ctx.emit(event.UPDATE_MODEL_EVENT, value);
emitChange(value);
if (option.created) {
states.query = "";
handleQueryChange("");
states.inputLength = 20;
}
if (props.filterable)
(_a = input.value) == null ? void 0 : _a.focus();
} else {
ctx.emit(event.UPDATE_MODEL_EVENT, option.value);
emitChange(option.value);
states.visible = false;
}
states.isSilentBlur = byClick;
setSoftFocus();
if (states.visible)
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 setSoftFocus = () => {
states.softFocus = true;
const _input = input.value || reference.value;
if (_input) {
_input == null ? void 0 : _input.focus();
}
};
const scrollToOption = (option) => {
var _a, _b, _c, _d, _e;
const targetOption = Array.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, `.${ns.be("dropdown", "wrap")}`);
if (menu) {
scroll.scrollIntoView(menu, target);
}
}
(_e = scrollbar.value) == null ? void 0 : _e.handleScroll();
};
const onOptionCreate = (vm) => {
states.optionsCount++;
states.filteredOptionsCount++;
states.options.set(vm.value, vm);
states.cachedOptions.set(vm.value, vm);
};
const onOptionDestroy = (key, vm) => {
if (states.options.get(key) === vm) {
states.optionsCount--;
states.filteredOptionsCount--;
states.options.delete(key);
}
};
const resetInputState = (e) => {
if (e.code !== aria.EVENT_CODE.backspace)
toggleLastOptionHitState(false);
states.inputLength = input.value.value.length * 15 + 20;
resetInputHeight();
};
const toggleLastOptionHitState = (hit) => {
if (!Array.isArray(states.selected))
return;
const option = states.selected[states.selected.length - 1];
if (!option)
return;
if (hit === true || hit === false) {
option.hitState = hit;
return hit;
}
option.hitState = !option.hitState;
return option.hitState;
};
const handleComposition = (event) => {
const text = event.target.value;
if (event.type === "compositionend") {
states.isOnComposition = false;
vue.nextTick(() => handleQueryChange(text));
} else {
const lastCharacter = text[text.length - 1] || "";
states.isOnComposition = !i18n.isKorean(lastCharacter);
}
};
const handleMenuEnter = () => {
vue.nextTick(() => scrollToOption(states.selected));
};
const handleFocus = (event) => {
if (!states.softFocus) {
if (props.automaticDropdown || props.filterable) {
if (props.filterable && !states.visible) {
states.menuVisibleOnFocus = true;
}
states.visible = true;
}
ctx.emit("focus", event);
} else {
states.softFocus = false;
}
};
const blur = () => {
var _a;
states.visible = false;
(_a = reference.value) == null ? void 0 : _a.blur();
};
const handleBlur = (event) => {
vue.nextTick(() => {
if (states.isSilentBlur) {
states.isSilentBlur = false;
} else {
ctx.emit("blur", event);
}
});
states.softFocus = false;
};
const handleClearClick = (event) => {
deleteSelected(event);
};
const handleClose = () => {
states.visible = false;
};
const handleKeydownEscape = (event) => {
if (states.visible) {
event.preventDefault();
event.stopPropagation();
states.visible = false;
}
};
const toggleMenu = (e) => {
var _a;
if (e && !states.mouseEnter) {
return;
}
if (!selectDisabled.value) {
if (states.menuVisibleOnFocus) {
states.menuVisibleOnFocus = false;
} else {
if (!tooltipRef.value || !tooltipRef.value.isFocusInsideContent()) {
states.visible = !states.visible;
}
}
if (states.visible) {
;
(_a = input.value || reference.value) == null ? void 0 : _a.focus();
}
}
};
const selectOption = () => {
if (!states.visible) {
toggleMenu();
} else {
if (optionsArray.value[states.hoverIndex]) {
handleOptionSelect(optionsArray.value[states.hoverIndex], void 0);
}
}
};
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 navigateOptions = (direction) => {
if (!states.visible) {
states.visible = true;
return;
}
if (states.options.size === 0 || states.filteredOptionsCount === 0)
return;
if (states.isOnComposition)
return;
if (!optionsAllDisabled.value) {
if (direction === "next") {
states.hoverIndex++;
if (states.hoverIndex === states.options.size) {
states.hoverIndex = 0;
}
} else if (direction === "prev") {
states.hoverIndex--;
if (states.hoverIndex < 0) {
states.hoverIndex = states.options.size - 1;
}
}
const option = optionsArray.value[states.hoverIndex];
if (option.disabled === true || option.states.groupDisabled === true || !option.visible) {
navigateOptions(direction);
}
vue.nextTick(() => scrollToOption(hoverOption.value));
}
};
const handleMouseEnter = () => {
states.mouseEnter = true;
};
const handleMouseLeave = () => {
states.mouseEnter = false;
};
return {
optionsArray,
selectSize,
handleResize,
debouncedOnInputChange,
debouncedQueryChange,
deletePrevTag,
deleteTag,
deleteSelected,
handleOptionSelect,
scrollToOption,
readonly,
resetInputHeight,
showClose,
iconComponent,
iconReverse,
showNewOption,
collapseTagSize,
setSelected,
managePlaceholder,
selectDisabled,
emptyText,
toggleLastOptionHitState,
resetInputState,
handleComposition,
onOptionCreate,
onOptionDestroy,
handleMenuEnter,
handleFocus,
blur,
handleBlur,
handleClearClick,
handleClose,
handleKeydownEscape,
toggleMenu,
selectOption,
getValueKey,
navigateOptions,
dropMenuVisible,
queryChange,
groupQueryChange,
reference,
input,
tooltipRef,
tags,
selectWrapper,
scrollbar,
handleMouseEnter,
handleMouseLeave
};
};
exports.useSelect = useSelect;
exports.useSelectStates = useSelectStates;
//# sourceMappingURL=useSelect.js.map
;