mldong-flow-designer-plus
Version:
本项目包含了作者为B站课堂视频[《工作流设计器开发最佳实践》](https://www.bilibili.com/cheese/play/ss24484)的过程源码。教程中开发的组件也可用于实际生产环境中。以下是和使用文档和课程章节说明。 ## 实战项目 [演示地址](https://flow-pro.mldong.com/)
1,545 lines • 74.9 kB
JavaScript
import { defineComponent, inject, ref, computed, onBeforeUnmount, toRef, openBlock, createBlock, Transition, unref, withCtx, withDirectives, createElementVNode, normalizeClass, normalizeStyle, vShow, createElementBlock, Fragment, createVNode, watch, nextTick, provide, reactive, onActivated, onMounted, onUpdated, resolveDynamicComponent, renderSlot, createCommentVNode, withModifiers, getCurrentInstance, toRaw, toRefs, toDisplayString, watchEffect, shallowRef, resolveComponent, resolveDirective, toHandlerKey, renderList, createTextVNode, withKeys, vModelText } from "vue";
import { u as useTooltipContentProps, E as Ee, a as ElTooltip } from "./index-D7UNCCqf.js";
import { a as useEventListener, u as useResizeObserver, b as useMutationObserver } from "./index-B5MEhWj_.js";
import { b as buildProps, _ as _export_sfc, u as useNamespace, t as throwError, h as isClient, d as definePropType, j as isNumber, k as isObject, f as debugWarn, w as withInstall, i as isArray, l as isUndefined, m as isIOS, g as isFunction, n as isPlainObject, c as isString, o as isElement, a as withNoopInstall } from "./install-La2G4dPY.js";
import { u as useAriaProps } from "./index-Ds80FYRq.js";
import { a as addUnit, c as componentSizes, u as useSizeProp } from "./index-CLW2rQKZ.js";
import { E as ElIcon, c as close_default, V as ValidateComponentsMap, i as iconPropType, a as circle_close_default, b as arrow_down_default } from "./icon-DV3tCSN7.js";
import { a as useFormSize, u as useFormItem, e as useFormItemInputId } from "./use-form-common-props-BwON8RcZ.js";
import { b as castArray, k as get, i as isEqual, d as debounce, l as findLastIndex } from "./index-BLhyZWKY.js";
import { u as useId } from "./index-CRwh6fag.js";
import { u as useLocale, a as useEmptyValues, b as useEmptyValuesProps } from "./index-Ii41w6Nd.js";
import { a as useComposition, u as useFocusController } from "./index-BVZ8Smzo.js";
import { E as EVENT_CODE } from "./aria-DJRDfGoO.js";
import { U as UPDATE_MODEL_EVENT, C as CHANGE_EVENT } from "./event-DSz0kuqc.js";
import { s as scrollIntoView } from "./scroll-DwUi3pAD.js";
const GAP = 4;
const BAR_MAP = {
vertical: {
offset: "offsetHeight",
scroll: "scrollTop",
scrollSize: "scrollHeight",
size: "height",
key: "vertical",
axis: "Y",
client: "clientY",
direction: "top"
},
horizontal: {
offset: "offsetWidth",
scroll: "scrollLeft",
scrollSize: "scrollWidth",
size: "width",
key: "horizontal",
axis: "X",
client: "clientX",
direction: "left"
}
};
const renderThumbStyle = ({
move,
size,
bar
}) => ({
[bar.size]: size,
transform: `translate${bar.axis}(${move}%)`
});
const scrollbarContextKey = Symbol("scrollbarContextKey");
const thumbProps = buildProps({
vertical: Boolean,
size: String,
move: Number,
ratio: {
type: Number,
required: true
},
always: Boolean
});
const COMPONENT_NAME$2 = "Thumb";
const _sfc_main$7 = /* @__PURE__ */ defineComponent({
__name: "thumb",
props: thumbProps,
setup(__props) {
const props = __props;
const scrollbar = inject(scrollbarContextKey);
const ns = useNamespace("scrollbar");
if (!scrollbar)
throwError(COMPONENT_NAME$2, "can not inject scrollbar context");
const instance = ref();
const thumb = ref();
const thumbState = ref({});
const visible = ref(false);
let cursorDown = false;
let cursorLeave = false;
let originalOnSelectStart = isClient ? document.onselectstart : null;
const bar = computed(() => BAR_MAP[props.vertical ? "vertical" : "horizontal"]);
const thumbStyle = computed(() => renderThumbStyle({
size: props.size,
move: props.move,
bar: bar.value
}));
const offsetRatio = computed(() => instance.value[bar.value.offset] ** 2 / scrollbar.wrapElement[bar.value.scrollSize] / props.ratio / thumb.value[bar.value.offset]);
const clickThumbHandler = (e) => {
var _a;
e.stopPropagation();
if (e.ctrlKey || [1, 2].includes(e.button))
return;
(_a = window.getSelection()) == null ? void 0 : _a.removeAllRanges();
startDrag(e);
const el = e.currentTarget;
if (!el)
return;
thumbState.value[bar.value.axis] = el[bar.value.offset] - (e[bar.value.client] - el.getBoundingClientRect()[bar.value.direction]);
};
const clickTrackHandler = (e) => {
if (!thumb.value || !instance.value || !scrollbar.wrapElement)
return;
const offset = Math.abs(e.target.getBoundingClientRect()[bar.value.direction] - e[bar.value.client]);
const thumbHalf = thumb.value[bar.value.offset] / 2;
const thumbPositionPercentage = (offset - thumbHalf) * 100 * offsetRatio.value / instance.value[bar.value.offset];
scrollbar.wrapElement[bar.value.scroll] = thumbPositionPercentage * scrollbar.wrapElement[bar.value.scrollSize] / 100;
};
const startDrag = (e) => {
e.stopImmediatePropagation();
cursorDown = true;
document.addEventListener("mousemove", mouseMoveDocumentHandler);
document.addEventListener("mouseup", mouseUpDocumentHandler);
originalOnSelectStart = document.onselectstart;
document.onselectstart = () => false;
};
const mouseMoveDocumentHandler = (e) => {
if (!instance.value || !thumb.value)
return;
if (cursorDown === false)
return;
const prevPage = thumbState.value[bar.value.axis];
if (!prevPage)
return;
const offset = (instance.value.getBoundingClientRect()[bar.value.direction] - e[bar.value.client]) * -1;
const thumbClickPosition = thumb.value[bar.value.offset] - prevPage;
const thumbPositionPercentage = (offset - thumbClickPosition) * 100 * offsetRatio.value / instance.value[bar.value.offset];
scrollbar.wrapElement[bar.value.scroll] = thumbPositionPercentage * scrollbar.wrapElement[bar.value.scrollSize] / 100;
};
const mouseUpDocumentHandler = () => {
cursorDown = false;
thumbState.value[bar.value.axis] = 0;
document.removeEventListener("mousemove", mouseMoveDocumentHandler);
document.removeEventListener("mouseup", mouseUpDocumentHandler);
restoreOnselectstart();
if (cursorLeave)
visible.value = false;
};
const mouseMoveScrollbarHandler = () => {
cursorLeave = false;
visible.value = !!props.size;
};
const mouseLeaveScrollbarHandler = () => {
cursorLeave = true;
visible.value = cursorDown;
};
onBeforeUnmount(() => {
restoreOnselectstart();
document.removeEventListener("mouseup", mouseUpDocumentHandler);
});
const restoreOnselectstart = () => {
if (document.onselectstart !== originalOnSelectStart)
document.onselectstart = originalOnSelectStart;
};
useEventListener(toRef(scrollbar, "scrollbarElement"), "mousemove", mouseMoveScrollbarHandler);
useEventListener(toRef(scrollbar, "scrollbarElement"), "mouseleave", mouseLeaveScrollbarHandler);
return (_ctx, _cache) => {
return openBlock(), createBlock(Transition, {
name: unref(ns).b("fade"),
persisted: ""
}, {
default: withCtx(() => [
withDirectives(createElementVNode("div", {
ref_key: "instance",
ref: instance,
class: normalizeClass([unref(ns).e("bar"), unref(ns).is(unref(bar).key)]),
onMousedown: clickTrackHandler
}, [
createElementVNode("div", {
ref_key: "thumb",
ref: thumb,
class: normalizeClass(unref(ns).e("thumb")),
style: normalizeStyle(unref(thumbStyle)),
onMousedown: clickThumbHandler
}, null, 38)
], 34), [
[vShow, _ctx.always || visible.value]
])
]),
_: 1
}, 8, ["name"]);
};
}
});
var Thumb = /* @__PURE__ */ _export_sfc(_sfc_main$7, [["__file", "thumb.vue"]]);
const barProps = buildProps({
always: {
type: Boolean,
default: true
},
minSize: {
type: Number,
required: true
}
});
const _sfc_main$6 = /* @__PURE__ */ defineComponent({
__name: "bar",
props: barProps,
setup(__props, { expose }) {
const props = __props;
const scrollbar = inject(scrollbarContextKey);
const moveX = ref(0);
const moveY = ref(0);
const sizeWidth = ref("");
const sizeHeight = ref("");
const ratioY = ref(1);
const ratioX = ref(1);
const handleScroll = (wrap) => {
if (wrap) {
const offsetHeight = wrap.offsetHeight - GAP;
const offsetWidth = wrap.offsetWidth - GAP;
moveY.value = wrap.scrollTop * 100 / offsetHeight * ratioY.value;
moveX.value = wrap.scrollLeft * 100 / offsetWidth * ratioX.value;
}
};
const update = () => {
const wrap = scrollbar == null ? void 0 : scrollbar.wrapElement;
if (!wrap)
return;
const offsetHeight = wrap.offsetHeight - GAP;
const offsetWidth = wrap.offsetWidth - GAP;
const originalHeight = offsetHeight ** 2 / wrap.scrollHeight;
const originalWidth = offsetWidth ** 2 / wrap.scrollWidth;
const height = Math.max(originalHeight, props.minSize);
const width = Math.max(originalWidth, props.minSize);
ratioY.value = originalHeight / (offsetHeight - originalHeight) / (height / (offsetHeight - height));
ratioX.value = originalWidth / (offsetWidth - originalWidth) / (width / (offsetWidth - width));
sizeHeight.value = height + GAP < offsetHeight ? `${height}px` : "";
sizeWidth.value = width + GAP < offsetWidth ? `${width}px` : "";
};
expose({
handleScroll,
update
});
return (_ctx, _cache) => {
return openBlock(), createElementBlock(Fragment, null, [
createVNode(Thumb, {
move: moveX.value,
ratio: ratioX.value,
size: sizeWidth.value,
always: _ctx.always
}, null, 8, ["move", "ratio", "size", "always"]),
createVNode(Thumb, {
move: moveY.value,
ratio: ratioY.value,
size: sizeHeight.value,
vertical: "",
always: _ctx.always
}, null, 8, ["move", "ratio", "size", "always"])
], 64);
};
}
});
var Bar = /* @__PURE__ */ _export_sfc(_sfc_main$6, [["__file", "bar.vue"]]);
const scrollbarProps = buildProps({
height: {
type: [String, Number],
default: ""
},
maxHeight: {
type: [String, Number],
default: ""
},
native: {
type: Boolean,
default: false
},
wrapStyle: {
type: definePropType([String, Object, Array]),
default: ""
},
wrapClass: {
type: [String, Array],
default: ""
},
viewClass: {
type: [String, Array],
default: ""
},
viewStyle: {
type: [String, Array, Object],
default: ""
},
noresize: Boolean,
tag: {
type: String,
default: "div"
},
always: Boolean,
minSize: {
type: Number,
default: 20
},
tabindex: {
type: [String, Number],
default: void 0
},
id: String,
role: String,
...useAriaProps(["ariaLabel", "ariaOrientation"])
});
const scrollbarEmits = {
scroll: ({
scrollTop,
scrollLeft
}) => [scrollTop, scrollLeft].every(isNumber)
};
const COMPONENT_NAME$1 = "ElScrollbar";
const __default__$1 = defineComponent({
name: COMPONENT_NAME$1
});
const _sfc_main$5 = /* @__PURE__ */ defineComponent({
...__default__$1,
props: scrollbarProps,
emits: scrollbarEmits,
setup(__props, { expose, emit }) {
const props = __props;
const ns = useNamespace("scrollbar");
let stopResizeObserver = void 0;
let stopResizeListener = void 0;
let wrapScrollTop = 0;
let wrapScrollLeft = 0;
const scrollbarRef = ref();
const wrapRef = ref();
const resizeRef = ref();
const barRef = ref();
const wrapStyle = computed(() => {
const style = {};
if (props.height)
style.height = addUnit(props.height);
if (props.maxHeight)
style.maxHeight = addUnit(props.maxHeight);
return [props.wrapStyle, style];
});
const wrapKls = computed(() => {
return [
props.wrapClass,
ns.e("wrap"),
{ [ns.em("wrap", "hidden-default")]: !props.native }
];
});
const resizeKls = computed(() => {
return [ns.e("view"), props.viewClass];
});
const handleScroll = () => {
var _a;
if (wrapRef.value) {
(_a = barRef.value) == null ? void 0 : _a.handleScroll(wrapRef.value);
wrapScrollTop = wrapRef.value.scrollTop;
wrapScrollLeft = wrapRef.value.scrollLeft;
emit("scroll", {
scrollTop: wrapRef.value.scrollTop,
scrollLeft: wrapRef.value.scrollLeft
});
}
};
function scrollTo(arg1, arg2) {
if (isObject(arg1)) {
wrapRef.value.scrollTo(arg1);
} else if (isNumber(arg1) && isNumber(arg2)) {
wrapRef.value.scrollTo(arg1, arg2);
}
}
const setScrollTop = (value) => {
if (!isNumber(value)) {
debugWarn(COMPONENT_NAME$1, "value must be a number");
return;
}
wrapRef.value.scrollTop = value;
};
const setScrollLeft = (value) => {
if (!isNumber(value)) {
debugWarn(COMPONENT_NAME$1, "value must be a number");
return;
}
wrapRef.value.scrollLeft = value;
};
const update = () => {
var _a;
(_a = barRef.value) == null ? void 0 : _a.update();
};
watch(() => props.noresize, (noresize) => {
if (noresize) {
stopResizeObserver == null ? void 0 : stopResizeObserver();
stopResizeListener == null ? void 0 : stopResizeListener();
} else {
({ stop: stopResizeObserver } = useResizeObserver(resizeRef, update));
stopResizeListener = useEventListener("resize", update);
}
}, { immediate: true });
watch(() => [props.maxHeight, props.height], () => {
if (!props.native)
nextTick(() => {
var _a;
update();
if (wrapRef.value) {
(_a = barRef.value) == null ? void 0 : _a.handleScroll(wrapRef.value);
}
});
});
provide(scrollbarContextKey, reactive({
scrollbarElement: scrollbarRef,
wrapElement: wrapRef
}));
onActivated(() => {
if (wrapRef.value) {
wrapRef.value.scrollTop = wrapScrollTop;
wrapRef.value.scrollLeft = wrapScrollLeft;
}
});
onMounted(() => {
if (!props.native)
nextTick(() => {
update();
});
});
onUpdated(() => update());
expose({
wrapRef,
update,
scrollTo,
setScrollTop,
setScrollLeft,
handleScroll
});
return (_ctx, _cache) => {
return openBlock(), createElementBlock("div", {
ref_key: "scrollbarRef",
ref: scrollbarRef,
class: normalizeClass(unref(ns).b())
}, [
createElementVNode("div", {
ref_key: "wrapRef",
ref: wrapRef,
class: normalizeClass(unref(wrapKls)),
style: normalizeStyle(unref(wrapStyle)),
tabindex: _ctx.tabindex,
onScroll: handleScroll
}, [
(openBlock(), createBlock(resolveDynamicComponent(_ctx.tag), {
id: _ctx.id,
ref_key: "resizeRef",
ref: resizeRef,
class: normalizeClass(unref(resizeKls)),
style: normalizeStyle(_ctx.viewStyle),
role: _ctx.role,
"aria-label": _ctx.ariaLabel,
"aria-orientation": _ctx.ariaOrientation
}, {
default: withCtx(() => [
renderSlot(_ctx.$slots, "default")
]),
_: 3
}, 8, ["id", "class", "style", "role", "aria-label", "aria-orientation"]))
], 46, ["tabindex"]),
!_ctx.native ? (openBlock(), createBlock(Bar, {
key: 0,
ref_key: "barRef",
ref: barRef,
always: _ctx.always,
"min-size": _ctx.minSize
}, null, 8, ["always", "min-size"])) : createCommentVNode("v-if", true)
], 2);
};
}
});
var Scrollbar = /* @__PURE__ */ _export_sfc(_sfc_main$5, [["__file", "scrollbar.vue"]]);
const ElScrollbar = withInstall(Scrollbar);
const tagProps = buildProps({
type: {
type: String,
values: ["primary", "success", "info", "warning", "danger"],
default: "primary"
},
closable: Boolean,
disableTransitions: Boolean,
hit: Boolean,
color: String,
size: {
type: String,
values: componentSizes
},
effect: {
type: String,
values: ["dark", "light", "plain"],
default: "light"
},
round: Boolean
});
const tagEmits = {
close: (evt) => evt instanceof MouseEvent,
click: (evt) => evt instanceof MouseEvent
};
const __default__ = defineComponent({
name: "ElTag"
});
const _sfc_main$4 = /* @__PURE__ */ defineComponent({
...__default__,
props: tagProps,
emits: tagEmits,
setup(__props, { emit }) {
const props = __props;
const tagSize = useFormSize();
const ns = useNamespace("tag");
const containerKls = computed(() => {
const { type, hit, effect, closable, round } = props;
return [
ns.b(),
ns.is("closable", closable),
ns.m(type || "primary"),
ns.m(tagSize.value),
ns.m(effect),
ns.is("hit", hit),
ns.is("round", round)
];
});
const handleClose = (event) => {
emit("close", event);
};
const handleClick = (event) => {
emit("click", event);
};
const handleVNodeMounted = (vnode) => {
var _a, _b, _c;
if ((_c = (_b = (_a = vnode == null ? void 0 : vnode.component) == null ? void 0 : _a.subTree) == null ? void 0 : _b.component) == null ? void 0 : _c.bum) {
vnode.component.subTree.component.bum = null;
}
};
return (_ctx, _cache) => {
return _ctx.disableTransitions ? (openBlock(), createElementBlock("span", {
key: 0,
class: normalizeClass(unref(containerKls)),
style: normalizeStyle({ backgroundColor: _ctx.color }),
onClick: handleClick
}, [
createElementVNode("span", {
class: normalizeClass(unref(ns).e("content"))
}, [
renderSlot(_ctx.$slots, "default")
], 2),
_ctx.closable ? (openBlock(), createBlock(unref(ElIcon), {
key: 0,
class: normalizeClass(unref(ns).e("close")),
onClick: withModifiers(handleClose, ["stop"])
}, {
default: withCtx(() => [
createVNode(unref(close_default))
]),
_: 1
}, 8, ["class", "onClick"])) : createCommentVNode("v-if", true)
], 6)) : (openBlock(), createBlock(Transition, {
key: 1,
name: `${unref(ns).namespace.value}-zoom-in-center`,
appear: "",
onVnodeMounted: handleVNodeMounted
}, {
default: withCtx(() => [
createElementVNode("span", {
class: normalizeClass(unref(containerKls)),
style: normalizeStyle({ backgroundColor: _ctx.color }),
onClick: handleClick
}, [
createElementVNode("span", {
class: normalizeClass(unref(ns).e("content"))
}, [
renderSlot(_ctx.$slots, "default")
], 2),
_ctx.closable ? (openBlock(), createBlock(unref(ElIcon), {
key: 0,
class: normalizeClass(unref(ns).e("close")),
onClick: withModifiers(handleClose, ["stop"])
}, {
default: withCtx(() => [
createVNode(unref(close_default))
]),
_: 1
}, 8, ["class", "onClick"])) : createCommentVNode("v-if", true)
], 6)
]),
_: 3
}, 8, ["name"]));
};
}
});
var Tag = /* @__PURE__ */ _export_sfc(_sfc_main$4, [["__file", "tag.vue"]]);
const ElTag = withInstall(Tag);
const selectGroupKey = Symbol("ElSelectGroup");
const selectKey = Symbol("ElSelect");
const escapeStringRegexp = (string = "") => string.replace(/[|\\{}()[\]^$+*?.]/g, "\\$&").replace(/-/g, "\\x2d");
function useOption(props, states) {
const select = inject(selectKey);
const selectGroup = inject(selectGroupKey, { disabled: false });
const itemSelected = computed(() => {
return contains(castArray(select.props.modelValue), props.value);
});
const limitReached = computed(() => {
var _a;
if (select.props.multiple) {
const modelValue = castArray((_a = select.props.modelValue) != null ? _a : []);
return !itemSelected.value && modelValue.length >= select.props.multipleLimit && select.props.multipleLimit > 0;
} else {
return false;
}
});
const currentLabel = computed(() => {
return props.label || (isObject(props.value) ? "" : props.value);
});
const currentValue = computed(() => {
return props.value || props.label || "";
});
const isDisabled = computed(() => {
return props.disabled || states.groupDisabled || limitReached.value;
});
const instance = getCurrentInstance();
const contains = (arr = [], target) => {
if (!isObject(props.value)) {
return arr && arr.includes(target);
} else {
const valueKey = select.props.valueKey;
return arr && arr.some((item) => {
return toRaw(get(item, valueKey)) === get(target, valueKey);
});
}
};
const hoverItem = () => {
if (!props.disabled && !selectGroup.disabled) {
select.states.hoveringIndex = select.optionsArray.indexOf(instance.proxy);
}
};
const updateOption = (query) => {
const regexp = new RegExp(escapeStringRegexp(query), "i");
states.visible = regexp.test(currentLabel.value) || props.created;
};
watch(() => currentLabel.value, () => {
if (!props.created && !select.props.remote)
select.setSelected();
});
watch(() => props.value, (val, oldVal) => {
const { remote, valueKey } = select.props;
if (val !== oldVal) {
select.onOptionDestroy(oldVal, instance.proxy);
select.onOptionCreate(instance.proxy);
}
if (!props.created && !remote) {
if (valueKey && isObject(val) && isObject(oldVal) && val[valueKey] === oldVal[valueKey]) {
return;
}
select.setSelected();
}
});
watch(() => selectGroup.disabled, () => {
states.groupDisabled = selectGroup.disabled;
}, { immediate: true });
return {
select,
currentLabel,
currentValue,
itemSelected,
isDisabled,
hoverItem,
updateOption
};
}
const _sfc_main$3 = defineComponent({
name: "ElOption",
componentName: "ElOption",
props: {
value: {
required: true,
type: [String, Number, Boolean, Object]
},
label: [String, Number],
created: Boolean,
disabled: Boolean
},
setup(props) {
const ns = useNamespace("select");
const id = useId();
const containerKls = computed(() => [
ns.be("dropdown", "item"),
ns.is("disabled", unref(isDisabled)),
ns.is("selected", unref(itemSelected)),
ns.is("hovering", unref(hover))
]);
const states = reactive({
index: -1,
groupDisabled: false,
visible: true,
hover: false
});
const {
currentLabel,
itemSelected,
isDisabled,
select,
hoverItem,
updateOption
} = useOption(props, states);
const { visible, hover } = toRefs(states);
const vm = getCurrentInstance().proxy;
select.onOptionCreate(vm);
onBeforeUnmount(() => {
const key = vm.value;
const { selected: selectedOptions } = select.states;
const doesSelected = selectedOptions.some((item) => {
return item.value === vm.value;
});
nextTick(() => {
if (select.states.cachedOptions.get(key) === vm && !doesSelected) {
select.states.cachedOptions.delete(key);
}
});
select.onOptionDestroy(key, vm);
});
function selectOptionClick() {
if (!isDisabled.value) {
select.handleOptionSelect(vm);
}
}
return {
ns,
id,
containerKls,
currentLabel,
itemSelected,
isDisabled,
select,
hoverItem,
updateOption,
visible,
hover,
selectOptionClick,
states
};
}
});
function _sfc_render$3(_ctx, _cache, $props, $setup, $data, $options) {
return withDirectives((openBlock(), createElementBlock("li", {
id: _ctx.id,
class: normalizeClass(_ctx.containerKls),
role: "option",
"aria-disabled": _ctx.isDisabled || void 0,
"aria-selected": _ctx.itemSelected,
onMousemove: _ctx.hoverItem,
onClick: withModifiers(_ctx.selectOptionClick, ["stop"])
}, [
renderSlot(_ctx.$slots, "default", {}, () => [
createElementVNode("span", null, toDisplayString(_ctx.currentLabel), 1)
])
], 42, ["id", "aria-disabled", "aria-selected", "onMousemove", "onClick"])), [
[vShow, _ctx.visible]
]);
}
var Option = /* @__PURE__ */ _export_sfc(_sfc_main$3, [["render", _sfc_render$3], ["__file", "option.vue"]]);
const _sfc_main$2 = defineComponent({
name: "ElSelectDropdown",
componentName: "ElSelectDropdown",
setup() {
const select = inject(selectKey);
const ns = useNamespace("select");
const popperClass = computed(() => select.props.popperClass);
const isMultiple = computed(() => select.props.multiple);
const isFitInputWidth = computed(() => select.props.fitInputWidth);
const minWidth = ref("");
function updateMinWidth() {
var _a;
minWidth.value = `${(_a = select.selectRef) == null ? void 0 : _a.offsetWidth}px`;
}
onMounted(() => {
updateMinWidth();
useResizeObserver(select.selectRef, updateMinWidth);
});
return {
ns,
minWidth,
popperClass,
isMultiple,
isFitInputWidth
};
}
});
function _sfc_render$2(_ctx, _cache, $props, $setup, $data, $options) {
return openBlock(), createElementBlock("div", {
class: normalizeClass([_ctx.ns.b("dropdown"), _ctx.ns.is("multiple", _ctx.isMultiple), _ctx.popperClass]),
style: normalizeStyle({ [_ctx.isFitInputWidth ? "width" : "minWidth"]: _ctx.minWidth })
}, [
_ctx.$slots.header ? (openBlock(), createElementBlock("div", {
key: 0,
class: normalizeClass(_ctx.ns.be("dropdown", "header"))
}, [
renderSlot(_ctx.$slots, "header")
], 2)) : createCommentVNode("v-if", true),
renderSlot(_ctx.$slots, "default"),
_ctx.$slots.footer ? (openBlock(), createElementBlock("div", {
key: 1,
class: normalizeClass(_ctx.ns.be("dropdown", "footer"))
}, [
renderSlot(_ctx.$slots, "footer")
], 2)) : createCommentVNode("v-if", true)
], 6);
}
var ElSelectMenu = /* @__PURE__ */ _export_sfc(_sfc_main$2, [["render", _sfc_render$2], ["__file", "select-dropdown.vue"]]);
const useSelect = (props, emit) => {
const { t } = useLocale();
const contentId = useId();
const nsSelect = useNamespace("select");
const nsInput = useNamespace("input");
const states = reactive({
inputValue: "",
options: /* @__PURE__ */ new Map(),
cachedOptions: /* @__PURE__ */ new Map(),
optionValues: [],
selected: [],
selectionWidth: 0,
collapseItemWidth: 0,
selectedLabel: "",
hoveringIndex: -1,
previousQuery: null,
inputHovering: false,
menuVisibleOnFocus: false,
isBeforeHide: false
});
const selectRef = ref(null);
const selectionRef = ref(null);
const tooltipRef = ref(null);
const tagTooltipRef = ref(null);
const inputRef = ref(null);
const prefixRef = ref(null);
const suffixRef = ref(null);
const menuRef = ref(null);
const tagMenuRef = ref(null);
const collapseItemRef = ref(null);
const scrollbarRef = ref(null);
const {
isComposing,
handleCompositionStart,
handleCompositionUpdate,
handleCompositionEnd
} = useComposition({
afterComposition: (e) => onInput(e)
});
const { wrapperRef, isFocused, handleBlur } = useFocusController(inputRef, {
beforeFocus() {
return selectDisabled.value;
},
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 = ref(false);
const hoverOption = ref();
const { form, formItem } = useFormItem();
const { inputId } = useFormItemInputId(props, {
formItemContext: formItem
});
const { valueOnClear, isEmptyValue } = useEmptyValues(props);
const selectDisabled = computed(() => props.disabled || (form == null ? void 0 : form.disabled));
const hasModelValue = computed(() => {
return isArray(props.modelValue) ? props.modelValue.length > 0 : !isEmptyValue(props.modelValue);
});
const needStatusIcon = computed(() => {
var _a;
return (_a = form == null ? void 0 : form.statusIcon) != null ? _a : false;
});
const showClose = computed(() => {
return props.clearable && !selectDisabled.value && states.inputHovering && hasModelValue.value;
});
const iconComponent = computed(() => props.remote && props.filterable && !props.remoteShowSuffix ? "" : props.suffixIcon);
const iconReverse = computed(() => nsSelect.is("reverse", iconComponent.value && expanded.value));
const validateState = computed(() => (formItem == null ? void 0 : formItem.validateState) || "");
const validateIcon = computed(() => ValidateComponentsMap[validateState.value]);
const debounce$1 = computed(() => props.remote ? 300 : 0);
const isRemoteSearchEmpty = computed(() => props.remote && !states.inputValue && states.options.size === 0);
const emptyText = computed(() => {
if (props.loading) {
return props.loadingText || t("el.select.loading");
} else {
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 = computed(() => optionsArray.value.filter((option) => option.visible).length);
const optionsArray = 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 = computed(() => Array.from(states.cachedOptions.values()));
const showNewOption = 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 && isFunction(props.filterMethod))
return;
if (props.filterable && props.remote && isFunction(props.remoteMethod))
return;
optionsArray.value.forEach((option) => {
var _a;
(_a = option.updateOption) == null ? void 0 : _a.call(option, states.inputValue);
});
};
const selectSize = useFormSize();
const collapseTagSize = computed(() => ["small"].includes(selectSize.value) ? "small" : "default");
const dropdownMenuVisible = computed({
get() {
return expanded.value && !isRemoteSearchEmpty.value;
},
set(val) {
expanded.value = val;
}
});
const shouldShowPlaceholder = computed(() => {
if (props.multiple && !isUndefined(props.modelValue)) {
return castArray(props.modelValue).length === 0 && !states.inputValue;
}
const value = isArray(props.modelValue) ? props.modelValue[0] : props.modelValue;
return props.filterable || isUndefined(value) ? !states.inputValue : true;
});
const currentPlaceholder = computed(() => {
var _a;
const _placeholder = (_a = props.placeholder) != null ? _a : t("el.select.placeholder");
return props.multiple || !hasModelValue.value ? _placeholder : states.selectedLabel;
});
const mouseEnterEventName = computed(() => isIOS ? null : "mouseenter");
watch(() => props.modelValue, (val, oldVal) => {
if (props.multiple) {
if (props.filterable && !props.reserveKeyword) {
states.inputValue = "";
handleQueryChange("");
}
}
setSelected();
if (!isEqual(val, oldVal) && props.validateEvent) {
formItem == null ? void 0 : formItem.validate("change").catch((err) => debugWarn(err));
}
}, {
flush: "post",
deep: true
});
watch(() => expanded.value, (val) => {
if (val) {
handleQueryChange(states.inputValue);
} else {
states.inputValue = "";
states.previousQuery = null;
states.isBeforeHide = true;
}
emit("visible-change", val);
});
watch(() => states.options.entries(), () => {
if (!isClient)
return;
setSelected();
if (props.defaultFirstOption && (props.filterable || props.remote) && filteredOptionsCount.value) {
checkDefaultFirstOption();
}
}, {
flush: "post"
});
watch(() => states.hoveringIndex, (val) => {
if (isNumber(val) && val > -1) {
hoverOption.value = optionsArray.value[val] || {};
} else {
hoverOption.value = {};
}
optionsArray.value.forEach((option) => {
option.hover = hoverOption.value === option;
});
});
watchEffect(() => {
if (states.isBeforeHide)
return;
updateOptions();
});
const handleQueryChange = (val) => {
if (states.previousQuery === val || isComposing.value) {
return;
}
states.previousQuery = val;
if (props.filterable && isFunction(props.filterMethod)) {
props.filterMethod(val);
} else if (props.filterable && props.remote && isFunction(props.remoteMethod)) {
props.remoteMethod(val);
}
if (props.defaultFirstOption && (props.filterable || props.remote) && filteredOptionsCount.value) {
nextTick(checkDefaultFirstOption);
} else {
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];
const valueList = optionsArray.value.map((item) => item.value);
states.hoveringIndex = getValueIndex(valueList, userCreatedOption || firstOriginOption);
};
const setSelected = () => {
if (!props.multiple) {
const value = isArray(props.modelValue) ? props.modelValue[0] : props.modelValue;
const option = getOption(value);
states.selectedLabel = option.currentLabel;
states.selected = [option];
return;
} else {
states.selectedLabel = "";
}
const result = [];
if (!isUndefined(props.modelValue)) {
castArray(props.modelValue).forEach((value) => {
result.push(getOption(value));
});
}
states.selected = result;
};
const getOption = (value) => {
let option;
const isObjectValue = isPlainObject(value);
for (let i = states.cachedOptions.size - 1; i >= 0; i--) {
const cachedOption = cachedOptionsArray.value[i];
const isEqualValue = isObjectValue ? get(cachedOption.value, props.valueKey) === get(value, props.valueKey) : cachedOption.value === value;
if (isEqualValue) {
option = {
value,
currentLabel: cachedOption.currentLabel,
get isDisabled() {
return cachedOption.isDisabled;
}
};
break;
}
}
if (option)
return option;
const label = isObjectValue ? value.label : value != null ? value : "";
const newOption = {
value,
currentLabel: label
};
return newOption;
};
const updateHoveringIndex = () => {
states.hoveringIndex = optionsArray.value.findIndex((item) => states.selected.some((selected) => getValueKey(selected) === getValueKey(item)));
};
const resetSelectionWidth = () => {
states.selectionWidth = selectionRef.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 = debounce(() => {
onInputChange();
}, debounce$1.value);
const emitChange = (val) => {
if (!isEqual(props.modelValue, val)) {
emit(CHANGE_EVENT, val);
}
};
const getLastNotDisabledIndex = (value) => findLastIndex(value, (it) => {
const option = states.cachedOptions.get(it);
return option && !option.disabled && !option.states.groupDisabled;
});
const deletePrevTag = (e) => {
if (!props.multiple)
return;
if (e.code === EVENT_CODE.delete)
return;
if (e.target.value.length <= 0) {
const value = castArray(props.modelValue).slice();
const lastNotDisabledIndex = getLastNotDisabledIndex(value);
if (lastNotDisabledIndex < 0)
return;
const removeTagValue = value[lastNotDisabledIndex];
value.splice(lastNotDisabledIndex, 1);
emit(UPDATE_MODEL_EVENT, value);
emitChange(value);
emit("remove-tag", removeTagValue);
}
};
const deleteTag = (event, tag) => {
const index = states.selected.indexOf(tag);
if (index > -1 && !selectDisabled.value) {
const value = castArray(props.modelValue).slice();
value.splice(index, 1);
emit(UPDATE_MODEL_EVENT, value);
emitChange(value);
emit("remove-tag", tag.value);
}
event.stopPropagation();
focus();
};
const deleteSelected = (event) => {
event.stopPropagation();
const value = props.multiple ? [] : valueOnClear.value;
if (props.multiple) {
for (const item of states.selected) {
if (item.isDisabled)
value.push(item.value);
}
}
emit(UPDATE_MODEL_EVENT, value);
emitChange(value);
states.hoveringIndex = -1;
expanded.value = false;
emit("clear");
focus();
};
const handleOptionSelect = (option) => {
var _a;
if (props.multiple) {
const value = castArray((_a = props.modelValue) != null ? _a : []).slice();
const optionIndex = getValueIndex(value, option);
if (optionIndex > -1) {
value.splice(optionIndex, 1);
} else if (props.multipleLimit <= 0 || value.length < props.multipleLimit) {
value.push(option.value);
}
emit(UPDATE_MODEL_EVENT, value);
emitChange(value);
if (option.created) {
handleQueryChange("");
}
if (props.filterable && !props.reserveKeyword) {
states.inputValue = "";
}
} else {
emit(UPDATE_MODEL_EVENT, option.value);
emitChange(option.value);
expanded.value = false;
}
focus();
if (expanded.value)
return;
nextTick(() => {
scrollToOption(option);
});
};
const getValueIndex = (arr = [], option) => {
if (isUndefined(option))
return -1;
if (!isObject(option.value))
return arr.indexOf(option.value);
return arr.findIndex((item) => {
return isEqual(get(item, props.valueKey), getValueKey(option));
});
};
const scrollToOption = (option) => {
var _a, _b, _c, _d, _e;
const targetOption = 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) {
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);
};
const onOptionDestroy = (key, vm) => {
if (states.options.get(key) === vm) {
states.options.delete(key);
}
};
const popperRef = computed(() => {
var _a, _b;
return (_b = (_a = tooltipRef.value) == null ? void 0 : _a.popperRef) == null ? void 0 : _b.contentRef;
});
const handleMenuEnter = () => {
states.isBeforeHide = false;
nextTick(() => scrollToOption(states.selected));
};
const focus = () => {
var _a;
(_a = inputRef.value) == null ? void 0 : _a.focus();
};
const blur = () => {
var _a;
if (expanded.value) {
expanded.value = false;
nextTick(() => {
var _a2;
return (_a2 = inputRef.value) == null ? void 0 : _a2.blur();
});
return;
}
(_a = inputRef.value) == null ? void 0 : _a.blur();
};
const handleClearClick = (event) => {
deleteSelected(event);
};
const handleClickOutside = (event) => {
expanded.value = false;
if (isFocused.value) {
const _event = new FocusEvent("focus", event);
nextTick(() => handleBlur(_event));
}
};
const handleEsc = () => {
if (states.inputValue.length > 0) {
states.inputValue = "";
} else {
expanded.value = false;
}
};
const toggleMenu = () => {
if (selectDisabled.value)
return;
if (isIOS)
states.inputHovering = true;
if (states.menuVisibleOnFocus) {
states.menuVisibleOnFocus = false;
} else {
expanded.value = !expanded.value;
}
};
const selectOption = () => {
if (!expanded.value) {
toggleMenu();
} else {
const option = optionsArray.value[states.hoveringIndex];
if (option && !option.isDisabled) {
handleOptionSelect(option);
}
}
};
const getValueKey = (item) => {
return isObject(item.value) ? get(item.value, props.valueKey) : item.value;
};
const optionsAllDisabled = computed(() => optionsArray.value.filter((option) => option.visible).every((option) => option.isDisabled));
const showTagList = computed(() => {
if (!props.multiple) {
return [];
}
return props.collapseTags ? states.selected.slice(0, props.maxCollapseTags) : states.selected;
});
const collapseTagList = 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 || isComposing.value)
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.isDisabled || !option.visible) {
navigateOptions(direction);
}
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 = computed(() => {
const gapWidth = getGapWidth();
const maxWidth = collapseItemRef.value && props.maxCollapseTags === 1 ? states.selectionWidth - states.collapseItemWidth - gapWidth : states.selectionWidth;
return { maxWidth: `${maxWidth}px` };
});
const collapseTagStyle = computed(() => {
return { maxWidth: `${states.selectionWidth}px` };
});
useResizeObserver(selectionRef, resetSelectionWidth);
useResizeObserver(menuRef, updateTooltip);
useResizeObserver(wrapperRef, updateTooltip);
useResizeObserver(tagMenuRef, updateTagTooltip);
useResizeObserver(collapseItemRef, resetCollapseItemWidth);
onMounted(() => {
setSelected();
});
return {
inputId,
contentId,
nsSelect,
nsInput,
states,
isFocused,
expanded,
optionsArray,
hoverOption,
selectSize,
filteredOptionsCount,
updateTooltip,
updateTagTooltip,
debouncedOnInputChange,
onInput,
deletePrevTag,
deleteTag,
deleteSelected,
handleOptionSelect,
scrollToOption,
hasModelValue,
shouldShowPlaceholder,
currentPlaceholder,
mouseEnterEventName,
needStatusIcon,
showClose,
iconComponent,
iconReverse,
validateState,
validateIcon,
showNewOption,
updateOptions,
collapseTagSize,
setSelected,
selectDisabled,
emptyText,
handleCompositionStart,
handleCompositionUpdate,
handleCompositionEnd,
onOptionCreate,
onOptionDestroy,
handleMenuEnter,
focus,
blur,
handleClearClick,
handleClickOutside,
handleEsc,
toggleMenu,
selectOption,
getValueKey,
navigateOptions,
dropdownMenuVisible,
showTagList,
collapseTagList,
tagStyle,
collapseTagStyle,
popperRef,
inputRef,
tooltipRef,
tagTooltipRef,
prefixRef,
suffixRef,
selectRef,
wrapperRef,
selectionRef,
scrollbarRef,
menuRef,
tagMenuRef,
collapseItemRef
};
};
var ElOptions = defineComponent({
name: "ElOptions",
setup(_, { slots }) {
const select = inject(selectKey);
let cachedValueList = [];
return () => {
var _a, _b;
const children = (_a = slots.default) == null ? void 0 : _a.call(slots);
const valueList = [];
function filterOptions(children2) {
if (!isArray(children2))
return;
children2.forEach((item) => {
var _a2, _b2, _c, _d;
const name = (_a2 = (item == null ? void 0 : item.type) || {}) == null ? void 0 : _a2.name;
if (name === "ElOptionGroup") {
filterOptions(!isString(item.children) && !isArray(item.children) && isFunction((_b2 = item.children) == null ? void 0 : _b2.default) ? (_c = item.children) == null ? void 0 : _c.default() : item.children);
} else if (name === "ElOption") {
valueList.push((_d = item.props) == null ? void 0 : _d.value);
} else if (isArray(item.children)) {
filterOptions(item.children);
}
});
}
if (children.length) {
filterOptions((_b = children[0]) == null ? void 0 : _b.children);
}
if (!isEqual(valueList, cachedValueList)) {
cachedValueList = valueList;
if (select) {
select.states.optionValues = valueList;
}
}
return children;
};
}
});
const SelectProps = buildProps({
name: String,
id: String,
modelValue: {
type: [Array, String, Number, Boolean, Object],
default: void 0
},
autocomplete: {
type: String,
default: "off"
},
automaticDropdown: Boolean,
size: useSizeProp,
effect: {
type: definePropType(String),
default: "light"
},
disabled: Boolean,
clearable: Boolean,
filterable: Boolean,
allowCreate: Boolean,
loading: Boolean,
popperClass: {
type: String,
default: ""
},
popperOptions: {
type: definePropType(Object),
default: () => ({})
},
remote: Boolean,
loadingText: String,
noMatchText: String,
noDataText: String,
remoteMethod: Function,
filterMethod: Function,
multiple: Boolean,
multipleLimit: {
type: Number,
default: 0
},
placeholder: {
type: String
},
defaultFirstOption: Boolean,
reserveKeyword: {
type: Boolean,
default: true
},
valueKey: {
type: String,
default: "value"
},
collapseTags: Boolean,
collapseTagsTooltip: Boolean,
maxCollapseTags: {
type: Number,
default: 1
},
teleported: useTooltipContentProps.teleported,
persistent: {
type: Boolean,
default: true
},
clearIcon: {
type: iconPropType,
default: circle_close_default
},
fitInputWidth: Boolean,
suffixIcon: {
type: iconPropType,
default: arrow_down_default
},
tagType: { ...tagProps.type, default: "info" },
tagEffect: { ...tagProps.effect, default: "light" },
validateEvent: {
type: Boolean,
default: true
},
remoteShowSuffix: Boolean,
showArrow: {
type: Boolean,
default: true
},
offset: {
type: Number,
default: 12
},
placement: {
type: definePropType(String),
values: Ee,
default: "bottom-start"
},
fallbackPlacements: {
type: