UNPKG

@vuesax-alpha/nightly

Version:
753 lines (748 loc) • 22.8 kB
'use strict'; 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