song-ui-u
Version:
vue3 + js的PC前端组件库
285 lines (282 loc) • 9.11 kB
JavaScript
import { defineComponent, ref, computed, watch, provide, createVNode, createTextVNode, nextTick } from 'vue';
import { useNamespace } from '../../../hook/use-namespace/index.mjs';
import { X, ChevronDown } from 'song-ui-pro-icon';
import '../../../hook/use-zindex/index.mjs';
import { SELECT_KEY } from './constant.mjs';
import '../../button/index.mjs';
import '../../buttonGroup/index.mjs';
import { XIcon } from '../../icon/index.mjs';
import '../../input/index.mjs';
import '../../textarea/index.mjs';
import '../../row/index.mjs';
import '../../col/index.mjs';
import '../../container/index.mjs';
import '../../checkbox/index.mjs';
import '../../switch/index.mjs';
import '../../form/index.mjs';
import '../../message/index.mjs';
import '../../mask/src/mask.mjs';
import '../../modal/index.mjs';
import '../../messageBox/index.mjs';
import '../../drawer/index.mjs';
import '../../badge/index.mjs';
import '../../space/index.mjs';
import '../../image/index.mjs';
import '../../radio/index.mjs';
import '../../divider/index.mjs';
import '../../chat/index.mjs';
import '../../progress/index.mjs';
import '../../upload/index.mjs';
import '../../vTree/index.mjs';
import '../../table/index.mjs';
import '../../tabs/index.mjs';
import '../../menu/index.mjs';
import '../../steps/index.mjs';
import '../../header/index.mjs';
import '../../breadcrumble/index.mjs';
import '../../datePicker/index.mjs';
import '../../tooltip/index.mjs';
import '../../popover/index.mjs';
import '../../timePicker/index.mjs';
import '../index.mjs';
import '../../collapse/index.mjs';
import '../../card/index.mjs';
import '../../timeline/index.mjs';
import '../../tag/index.mjs';
import '../../result/index.mjs';
import '../../sender/index.mjs';
var Select = /* @__PURE__ */ defineComponent({
name: "x-select",
props: {
modelValue: {
type: [String, Number, Array],
default: ""
},
multiple: {
type: Boolean,
default: false
},
disabled: {
type: Boolean,
default: false
},
clearable: {
type: Boolean,
default: false
},
placeholder: {
type: String,
default: "\u8BF7\u9009\u62E9"
},
filterable: {
type: Boolean,
default: false
},
size: {
type: String,
default: "default"
},
width: {
type: String,
default: "100%"
}
},
emits: ["update:modelValue", "change", "clear", "blur", "focus"],
setup(props, {
slots,
emit
}) {
const ns = useNamespace("select");
const visible = ref(false);
const selectedLabel = ref("");
const selectedLabels = ref([]);
const inputValue = ref("");
const options = ref([]);
const selectRef = ref(null);
const inputRef = ref(null);
const dropdownRef = ref(null);
const isFocus = ref(false);
const isHover = ref(false);
const styleWidth = computed(() => ({
width: props.width
}));
const controlSize = computed(() => props.size);
const registerOption = (option) => {
options.value.push(option);
};
const unregisterOption = (id) => {
const index = options.value.findIndex((option) => option.id === id);
if (index !== -1) {
options.value.splice(index, 1);
}
};
const selectOption = (value, label) => {
if (props.multiple) {
const values = Array.isArray(props.modelValue) ? [...props.modelValue] : [];
const index = values.indexOf(value);
if (index === -1) {
values.push(value);
selectedLabels.value.push(label);
} else {
values.splice(index, 1);
selectedLabels.value.splice(index, 1);
}
emit("update:modelValue", values);
emit("change", values);
} else {
if (selectedLabel.value == label) {
selectedLabel.value = "";
} else {
selectedLabel.value = label;
}
emit("update:modelValue", value);
emit("change", value);
closeDropdown();
}
};
const clearSelection = (e) => {
e.stopPropagation();
console.log("clearSelection");
if (props.multiple) {
emit("update:modelValue", []);
selectedLabels.value = [];
} else {
emit("update:modelValue", "");
selectedLabel.value = "";
}
emit("clear");
};
const toggleDropdown = () => {
if (props.disabled) return;
visible.value = !visible.value;
if (visible.value && props.filterable) {
nextTick(() => {
inputRef.value?.focus();
});
}
};
const closeDropdown = () => {
visible.value = false;
if (props.filterable) {
inputValue.value = "";
}
};
const handleInput = (e) => {
inputValue.value = e.target.value;
};
const handleFocus = () => {
isFocus.value = true;
emit("focus");
};
const handleBlur = () => {
isFocus.value = false;
emit("blur");
};
const handleMouseEnter = () => {
isHover.value = true;
};
const handleMouseLeave = () => {
isHover.value = false;
};
const isOptionSelected = (value) => {
if (props.multiple) {
return Array.isArray(props.modelValue) && props.modelValue.includes(value);
}
return props.modelValue === value && selectedLabel.value;
};
watch(() => props.modelValue, (newVal) => {
if (props.multiple) ; else {
const selectedOption = options.value.find((option) => option.value === newVal);
selectedLabel.value = selectedOption ? selectedOption.label : "";
}
}, {
immediate: true
});
provide(SELECT_KEY, {
props,
registerOption,
unregisterOption,
selectOption,
isOptionSelected,
inputValue
});
const handleClickOutside = (e) => {
if (selectRef.value && !selectRef.value.contains(e.target)) {
closeDropdown();
}
};
watch(visible, (newVal) => {
if (newVal) {
document.addEventListener("click", handleClickOutside);
} else {
document.removeEventListener("click", handleClickOutside);
}
});
return () => createVNode("div", {
"ref": selectRef,
"style": styleWidth.value,
"class": [ns.b(), ns.is("focus", isFocus.value), ns.is("disabled", props.disabled), ns.m("size", controlSize.value), ns.is("clearable", props.clearable && !props.disabled && (props.multiple ? selectedLabels.value.length > 0 : selectedLabel.value)), ns.is("multiple", props.multiple), ns.is("filterable", props.filterable)],
"onClick": toggleDropdown,
"onMouseenter": handleMouseEnter,
"onMouseleave": handleMouseLeave
}, [createVNode("div", {
"class": [ns.e("wrapper"), ns.is("hover", isHover.value && !props.disabled)]
}, [props.multiple ? createVNode("div", {
"class": [ns.e("tags")]
}, [selectedLabels.value.length > 0 ? selectedLabels.value.map((label, index) => createVNode("span", {
"class": [ns.e("tag")],
"key": index
}, [label, createVNode(XIcon, {
"onClick": (e) => {
e.stopPropagation();
selectOption(props.modelValue[index], label);
}
}, {
default: () => [createVNode(X, null, null)]
})])) : createVNode("span", {
"class": [ns.e("placeholder")]
}, null), props.filterable && createVNode("input", {
"ref": inputRef,
"type": "text",
"class": [ns.e("filter-input")],
"value": inputValue.value,
"onInput": handleInput,
"disabled": props.disabled,
"onFocus": handleFocus,
"onBlur": handleBlur,
"placeholder": selectedLabel.value || props.placeholder
}, null)]) : (
/* 单选模式 */
createVNode("div", {
"class": [ns.e("selected")]
}, [props.filterable && visible.value ? createVNode("input", {
"ref": inputRef,
"type": "text",
"class": [ns.e("filter-input")],
"value": inputValue.value,
"placeholder": selectedLabel.value || props.placeholder,
"onInput": handleInput,
"onFocus": handleFocus,
"onBlur": handleBlur
}, null) : createVNode("span", {
"class": [selectedLabel.value ? ns.e("label") : ns.e("placeholder")]
}, [selectedLabel.value || props.placeholder])])
), props.clearable && !props.disabled && (props.multiple ? selectedLabels.value.length > 0 : selectedLabel.value) && isHover.value && createVNode(XIcon, {
"class": [ns.e("clear")],
"onClick": clearSelection
}, {
default: () => [createVNode(X, null, null)]
}), createVNode(XIcon, {
"class": [ns.e("arrow"), visible.value ? ns.is("reverse", true) : ""]
}, {
default: () => [createVNode(ChevronDown, null, null)]
})]), visible.value && createVNode("div", {
"ref": dropdownRef,
"class": [ns.e("dropdown")]
}, [options.value.length === 0 && createVNode("div", {
"class": [ns.e("empty")]
}, [createTextVNode("\u65E0\u6570\u636E")]), slots.default?.()])]);
}
});
export { Select as default };
//# sourceMappingURL=select.mjs.map