@vuesax-alpha/nightly
Version:
A Component Library for Vue 3
753 lines (748 loc) • 22.8 kB
JavaScript
;
Object.defineProperty(exports, '__esModule', { value: true });
var vue = require('vue');
var lodashUnified = require('lodash-unified');
require('../../../constants/index.js');
require('../../../utils/index.js');
require('../../../hooks/index.js');
var index = require('../../../hooks/use-namespace/index.js');
var index$1 = require('../../../hooks/use-locale/index.js');
var index$2 = require('../../../hooks/use-id/index.js');
var core = require('@vueuse/core');
var shared = require('@vue/shared');
var aria = require('../../../constants/aria.js');
var event = require('../../../constants/event.js');
var style = require('../../../utils/dom/style.js');
var scroll = require('../../../utils/dom/scroll.js');
var types = require('../../../utils/types.js');
var i18n = require('../../../utils/i18n.js');
function useSelectStates(props) {
return vue.reactive({
options: /* @__PURE__ */ new Map(),
cachedOptions: /* @__PURE__ */ new Map(),
selected: /* @__PURE__ */ new Map(),
disabledOptions: /* @__PURE__ */ new Map(),
createdLabel: null,
targetOnElement: null,
createdSelected: false,
optionsCount: 0,
filteredOptionsCount: 0,
visible: false,
softFocus: false,
selectedLabel: "",
hoverIndex: -1,
query: "",
previousQuery: null,
cachedPlaceHolder: "",
currentPlaceholder: props.placeholder,
menuVisibleOnFocus: false,
isOnComposition: false,
isSilentBlur: false,
mouseEnter: false
});
}
const useSelect = (props, states, emit) => {
const ns = index.useNamespace("select");
const { t } = index$1.useLocale();
const reference = vue.ref();
const input = vue.ref();
const popperRef = vue.ref();
const chips = vue.ref();
const selectWrapper = vue.ref();
const scrollbar = vue.ref();
const hoverOption = vue.ref();
const inputId = index$2.useId(props.id);
const queryChange = vue.shallowRef("");
const debounce = vue.ref(0);
const readonly = vue.computed(
() => !props.filter || props.multiple || !states.visible
);
const selectDisabled = vue.computed(() => props.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.mouseEnter && hasValue;
return criteria;
});
const optionsArray = vue.computed(() => Array.from(states.options.values()));
const cachedOptionsArray = vue.computed(
() => Array.from(states.cachedOptions.values())
);
const selectedArray = vue.computed(() => Array.from(states.selected.values()));
const showNewOption = vue.computed(() => {
const hasExistingOption = optionsArray.value.filter((option) => {
return !option.created;
}).some((option) => {
return option.currentLabel === states.query;
});
return props.filter && props.allowCreate && states.query !== "" && !hasExistingOption;
});
const dropMenuVisible = vue.computed({
get() {
return states.visible;
},
set(val) {
states.visible = val;
}
});
const emptyText = vue.computed(() => {
if (props.loading) {
return props.loadingText || t("vs.select.loading");
}
if (props.filter && states.query && states.options.size > 0 && states.filteredOptionsCount === 0) {
return props.noMatchText || t("vs.select.noMatch");
}
if (states.options.size === 0) {
return props.noDataText || t("vs.select.noData");
}
return null;
});
vue.watch(
() => props.placeholder,
(val) => {
states.cachedPlaceHolder = states.currentPlaceholder = val;
const hasValue = props.multiple && Array.isArray(props.modelValue) && props.modelValue.length > 0;
if (hasValue) {
states.currentPlaceholder = "";
}
}
);
vue.watch(
() => props.modelValue,
(val) => {
if (props.multiple) {
if (val && val.length > 0 || input.value && states.query !== "") {
states.currentPlaceholder = "";
} else {
states.currentPlaceholder = states.cachedPlaceHolder;
}
if (props.filter) {
states.query = "";
handleQueryChange(states.query);
}
vue.nextTick(() => {
if (reference.value && chips.value) {
reference.value.style.height = `${chips.value.scrollHeight - 1}px`;
}
});
}
setSelected();
vue.nextTick(() => {
var _a;
(_a = popperRef.value) == null ? void 0 : _a.updatePopper();
});
},
{
flush: "post",
deep: true
}
);
vue.watch(
() => states.visible,
(val) => {
var _a, _b;
if (!val) {
input.value && input.value.blur();
handleQueryChange("");
states.query = "";
states.previousQuery = null;
states.selectedLabel = "";
states.menuVisibleOnFocus = false;
resetHoverIndex();
vue.nextTick(() => {
if (input.value && input.value.value === "" && selectedArray.value.length === 0) {
states.currentPlaceholder = states.cachedPlaceHolder;
}
});
if (!props.multiple) {
if (selectedArray.value.length) {
if (props.filter && props.allowCreate && states.createdSelected && states.createdLabel) {
states.selectedLabel = states.createdLabel;
} else {
states.selectedLabel = selectedArray.value[0].currentLabel;
}
if (props.filter)
states.query = states.selectedLabel;
}
if (props.filter) {
states.currentPlaceholder = states.cachedPlaceHolder;
}
}
} else {
(_a = popperRef.value) == null ? void 0 : _a.updatePopper();
if (props.filter) {
states.filteredOptionsCount = states.optionsCount;
states.query = states.selectedLabel;
if (props.multiple) {
(_b = input.value) == null ? void 0 : _b.focus();
} else {
if (states.selectedLabel) {
states.currentPlaceholder = `${states.selectedLabel}`;
states.selectedLabel = "";
}
}
states.query && handleQueryChange(states.query);
if (!props.multiple) {
queryChange.value = "";
vue.triggerRef(queryChange);
}
}
}
emit("visible-change", val);
}
);
vue.watch(
() => states.options.entries(),
() => {
var _a, _b;
if (!core.isClient)
return;
(_a = popperRef.value) == null ? void 0 : _a.updatePopper();
const inputs = ((_b = selectWrapper.value) == null ? void 0 : _b.querySelectorAll("input")) || [];
if (!Array.from(inputs).includes(document.activeElement)) {
setSelected();
}
if (props.defaultFirstOption && props.filter && states.filteredOptionsCount) {
checkDefaultFirstOption();
}
},
{
flush: "post"
}
);
vue.watch(
() => states.hoverIndex,
(val) => {
if (val > -1) {
hoverOption.value = optionsArray.value[val];
} else {
hoverOption.value = void 0;
}
optionsArray.value.forEach((option) => {
option.hover = lodashUnified.isEqual(hoverOption.value, option);
});
}
);
const showTagList = vue.computed(() => {
if (!props.multiple) {
return [];
}
return props.collapseChips ? selectedArray.value.slice(0, props.maxCollapseChips) : selectedArray.value;
});
const collapseTagList = vue.computed(() => {
if (!props.multiple) {
return [];
}
return props.collapseChips ? selectedArray.value.slice(props.maxCollapseChips) : [];
});
const handleQueryChange = (val) => {
if (states.previousQuery === val || states.isOnComposition)
return;
if (states.previousQuery === null && shared.isFunction(props.filterMethod)) {
states.previousQuery = val;
return;
}
states.previousQuery = val;
vue.nextTick(() => {
var _a;
if (states.visible)
(_a = popperRef.value) == null ? void 0 : _a.updatePopper();
});
states.hoverIndex = -1;
if (props.multiple && props.filter) {
vue.nextTick(() => {
managePlaceholder();
});
}
if (shared.isFunction(props.filterMethod)) {
props.filterMethod(val);
} else {
states.filteredOptionsCount = states.optionsCount;
queryChange.value = val;
vue.triggerRef(queryChange);
}
if (props.defaultFirstOption && props.filter && states.filteredOptionsCount) {
vue.nextTick(() => {
checkDefaultFirstOption();
});
}
};
const managePlaceholder = () => {
var _a;
if (states.currentPlaceholder !== "") {
states.currentPlaceholder = ((_a = input.value) == null ? void 0 : _a.value) ? "" : states.cachedPlaceHolder;
}
};
const checkDefaultFirstOption = () => {
const optionsInDropdown = optionsArray.value.filter(
(n) => n.visible && !n.isDisabled && !n.groupDisabled
);
const userCreatedOption = optionsInDropdown.find((n) => n.created);
const firstOriginOption = optionsInDropdown[0];
states.hoverIndex = getValueIndex(
optionsArray.value,
firstOriginOption || userCreatedOption
);
};
const setSelected = () => {
states.selected.clear();
if (!props.multiple) {
const option = getOption(props.modelValue);
if (option.created) {
states.createdLabel = `${option.value}`;
states.createdSelected = true;
} else {
states.createdSelected = false;
}
states.selectedLabel = option.currentLabel;
states.selected.set(option.value, option);
if (props.filter)
states.query = states.selectedLabel;
return;
}
states.selectedLabel = "";
if (Array.isArray(props.modelValue)) {
props.modelValue.forEach((value) => {
const option = getOption(value);
states.selected.set(option.value, option);
});
}
};
const getOption = (value) => {
let option = null;
for (let i = states.cachedOptions.size - 1; i >= 0; i--) {
const cachedOption = cachedOptionsArray.value[i];
const isEqualValue = shared.isObject(value) ? lodashUnified.isEqual(cachedOption.value, value) : cachedOption.value === value;
if (isEqualValue) {
option = cachedOption;
break;
}
}
if (option)
return option;
const label = shared.isObject(value) ? "" : !lodashUnified.isNil(value) ? String(value) : "";
const newOption = {
value,
currentLabel: label,
label,
hit: true
};
if (props.multiple) {
newOption.hit = false;
}
return newOption;
};
const resetHoverIndex = () => {
setTimeout(() => {
if (!props.multiple) {
states.hoverIndex = optionsArray.value.findIndex((item) => {
return lodashUnified.isEqual(item, selectedArray.value[0]);
});
return;
}
if (selectedArray.value.length > 0) {
states.hoverIndex = Math.min.apply(
null,
selectedArray.value.map((selected) => {
return optionsArray.value.findIndex((item) => {
return lodashUnified.isEqual(item.value, selected.value);
});
})
);
return;
}
states.hoverIndex = -1;
}, 300);
};
const handleResize = () => {
var _a;
if (reference.value && chips.value) {
reference.value.style.height = `${chips.value.scrollHeight}px`;
}
(_a = popperRef.value) == null ? void 0 : _a.updatePopper();
};
const onInputChange = () => {
if (props.filter && states.query !== states.selectedLabel) {
states.query = states.selectedLabel;
handleQueryChange(states.selectedLabel || "");
}
};
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)) {
emit("change", 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;
const value = e.target.value;
if (value.length <= 0 && !toggleLastOptionHitState()) {
const value2 = props.modelValue.slice();
const lastNotDisabledIndex = getLastNotDisabledIndex(value2);
if (lastNotDisabledIndex < 0)
return;
value2.splice(lastNotDisabledIndex, 1);
emit(event.UPDATE_MODEL_EVENT, value2);
emitChange(value2);
}
if (value.length === 1 && props.modelValue.length === 0) {
states.currentPlaceholder = states.cachedPlaceHolder;
}
};
const deleteTag = (tag) => {
const hasTag = states.cachedOptions.get(tag);
if (!hasTag)
return;
const index = getValueIndex(selectedArray.value, hasTag);
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);
}
focus();
};
const deleteSelected = () => {
const value = props.multiple ? [] : props.notValue;
if (lodashUnified.isArray(value)) {
for (const item of selectedArray.value) {
if (item.isDisabled)
value.push(item.value);
}
}
emit(event.UPDATE_MODEL_EVENT, value);
emitChange(value);
states.hoverIndex = -1;
states.visible = false;
emit("clear");
focus();
};
const handleOptionSelect = (option, byClick) => {
var _a;
if (props.multiple) {
let modelValue = props.modelValue;
if (!lodashUnified.isArray(props.modelValue)) {
if (states.options.has(modelValue)) {
modelValue = [modelValue];
} else {
modelValue = [];
}
}
const value = modelValue.slice();
const optionIndex = getValueIndex(selectedArray.value, option);
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) {
states.query = "";
handleQueryChange("");
}
if (props.filter)
(_a = input.value) == null ? void 0 : _a.focus();
} else {
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, option) => {
let index = -1;
arr.some((item, i) => {
if (lodashUnified.isEqual(item.value, option.value)) {
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;
let target;
if (option == null ? void 0 : option.value) {
const options = optionsArray.value.filter(
(item) => item.value === option.value
);
if (options.length > 0) {
target = options[0].el;
}
}
if (popperRef.value && target) {
const menu = (_c = (_b = (_a = popperRef.value) == null ? void 0 : _a.contentRef) == null ? void 0 : _b.querySelector) == null ? void 0 : _c.call(_b, `.${ns.e("options")}`);
if (menu) {
style.setStyle(menu, "scroll-behavior", "smooth");
scroll.scrollIntoView(menu, target);
style.removeStyle(menu, "scroll-behavior");
}
}
(_d = scrollbar.value) == null ? void 0 : _d.handleScroll();
};
const onOptionCreate = (value, option) => {
states.optionsCount++;
states.filteredOptionsCount++;
states.options.set(value, option);
states.cachedOptions.set(value, option);
option.isDisabled && states.disabledOptions.set(value, option);
};
const onOptionDestroy = (value, option) => {
if (states.options.get(value) === option) {
states.optionsCount--;
states.filteredOptionsCount--;
states.options.delete(value);
}
};
const resetInputState = (e) => {
if (e.code !== aria.EVENT_CODE.backspace)
toggleLastOptionHitState(false);
};
const toggleLastOptionHitState = (hit) => {
if (!selectedArray.value.length)
return;
const lastNotDisabledIndex = getLastNotDisabledIndex(
selectedArray.value.map((it) => it.value)
);
const option = selectedArray.value[lastNotDisabledIndex];
if (!option)
return;
if (types.isBoolean(hit)) {
option.hit = hit;
return hit;
}
option.hit = !option.hit;
return option.hit;
};
const handleComposition = (event) => {
var _a;
const text = (_a = event.target) == null ? void 0 : _a.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 handleTarget = (target, condition = true) => {
if (condition)
states.targetOnElement = target;
};
const handleMenuEnter = () => {
vue.nextTick(() => scrollToOption(selectedArray.value[0]));
};
const focus = () => {
var _a;
states.visible = true;
(_a = reference.value) == null ? void 0 : _a.focus();
};
const handleFocus = (event) => {
if (!states.softFocus) {
if (props.filter) {
if (!states.visible) {
states.menuVisibleOnFocus = true;
}
}
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 {
emit("blur", event);
}
});
states.softFocus = false;
};
const handleClearClick = () => {
deleteSelected();
};
const showClearable = vue.computed(() => {
if (!props.clearable || props.disabled || props.loading)
return false;
if (optionsArray.value.length === 0)
return false;
const ignoreDisabledOptions = optionsArray.value.filter(
(e) => e.isDisabled === false
);
if (ignoreDisabledOptions.length === 0)
return false;
return states.mouseEnter;
});
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 (!popperRef.value || !popperRef.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], false);
}
}
};
const optionsAllDisabled = vue.computed(
() => optionsArray.value.filter((option) => option.visible).every((option) => option.isDisabled)
);
const navigateOptions = (direction = "next") => {
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.isDisabled === true || option.groupDisabled === true || !option.visible) {
navigateOptions(direction);
}
vue.nextTick(
() => !lodashUnified.isNil(hoverOption.value) && scrollToOption(hoverOption.value)
);
}
};
const handleMouseEnter = () => {
states.mouseEnter = true;
};
const handleMouseLeave = () => {
states.mouseEnter = false;
};
const processBeforeOpen = () => true;
const processBeforeClose = () => {
if (states.targetOnElement == null)
return true;
return !["chip-close", "input-filter"].includes(states.targetOnElement);
};
return {
showNewOption,
inputId,
optionsArray,
cachedOptionsArray,
selectedArray,
handleResize,
debouncedOnInputChange,
debouncedQueryChange,
deletePrevTag,
deleteTag,
deleteSelected,
handleOptionSelect,
scrollToOption,
readonly,
showClose,
handleTarget,
setSelected,
managePlaceholder,
selectDisabled,
toggleLastOptionHitState,
resetInputState,
handleComposition,
onOptionCreate,
onOptionDestroy,
handleMenuEnter,
focus,
handleFocus,
blur,
handleBlur,
handleClearClick,
showClearable,
handleClose,
handleKeydownEscape,
toggleMenu,
selectOption,
navigateOptions,
dropMenuVisible,
queryChange,
showTagList,
collapseTagList,
reference,
input,
popperRef,
chips,
selectWrapper,
scrollbar,
handleMouseEnter,
handleMouseLeave,
processBeforeOpen,
processBeforeClose,
emptyText,
resetHoverIndex
};
};
exports.useSelect = useSelect;
exports.useSelectStates = useSelectStates;
//# sourceMappingURL=useSelect.js.map