250 lines
7.0 KiB
JavaScript
250 lines
7.0 KiB
JavaScript
import { defineComponent, inject, ref, computed, watch, unref, createVNode, mergeProps, toRaw } from 'vue';
|
|
import { get } from 'lodash-unified';
|
|
import GroupItem from './group-item.mjs';
|
|
import OptionItem from './option-item.mjs';
|
|
import { useProps } from './useProps.mjs';
|
|
import { selectV2InjectionKey } from './token.mjs';
|
|
import FixedSizeList from '../../virtual-list/src/components/fixed-size-list.mjs';
|
|
import DynamicSizeList from '../../virtual-list/src/components/dynamic-size-list.mjs';
|
|
import { useNamespace } from '../../../hooks/use-namespace/index.mjs';
|
|
import { isUndefined } from '../../../utils/types.mjs';
|
|
import { isIOS } from '@vueuse/core';
|
|
import { getEventCode } from '../../../utils/dom/event.mjs';
|
|
import { EVENT_CODE } from '../../../constants/aria.mjs';
|
|
import { isObject } from '@vue/shared';
|
|
|
|
const props = {
|
|
loading: Boolean,
|
|
data: {
|
|
type: Array,
|
|
required: true
|
|
},
|
|
hoveringIndex: Number,
|
|
width: Number,
|
|
id: String,
|
|
ariaLabel: String
|
|
};
|
|
var ElSelectMenu = defineComponent({
|
|
name: "ElSelectDropdown",
|
|
props,
|
|
setup(props2, {
|
|
slots,
|
|
expose
|
|
}) {
|
|
const select = inject(selectV2InjectionKey);
|
|
const ns = useNamespace("select");
|
|
const {
|
|
getLabel,
|
|
getValue,
|
|
getDisabled
|
|
} = useProps(select.props);
|
|
const cachedHeights = ref([]);
|
|
const listRef = ref();
|
|
const size = computed(() => props2.data.length);
|
|
watch(() => size.value, () => {
|
|
var _a, _b;
|
|
(_b = (_a = select.tooltipRef.value) == null ? void 0 : _a.updatePopper) == null ? void 0 : _b.call(_a);
|
|
});
|
|
const isSized = computed(() => isUndefined(select.props.estimatedOptionHeight));
|
|
const listProps = computed(() => {
|
|
if (isSized.value) {
|
|
return {
|
|
itemSize: select.props.itemHeight
|
|
};
|
|
}
|
|
return {
|
|
estimatedSize: select.props.estimatedOptionHeight,
|
|
itemSize: (idx) => cachedHeights.value[idx]
|
|
};
|
|
});
|
|
const contains = (arr = [], target) => {
|
|
const {
|
|
props: {
|
|
valueKey
|
|
}
|
|
} = select;
|
|
if (!isObject(target)) {
|
|
return arr.includes(target);
|
|
}
|
|
return arr && arr.some((item) => {
|
|
return toRaw(get(item, valueKey)) === get(target, valueKey);
|
|
});
|
|
};
|
|
const isEqual = (selected, target) => {
|
|
if (!isObject(target)) {
|
|
return selected === target;
|
|
} else {
|
|
const {
|
|
valueKey
|
|
} = select.props;
|
|
return get(selected, valueKey) === get(target, valueKey);
|
|
}
|
|
};
|
|
const isItemSelected = (modelValue, target) => {
|
|
if (select.props.multiple) {
|
|
return contains(modelValue, getValue(target));
|
|
}
|
|
return isEqual(modelValue, getValue(target));
|
|
};
|
|
const isItemDisabled = (modelValue, selected) => {
|
|
const {
|
|
disabled,
|
|
multiple,
|
|
multipleLimit
|
|
} = select.props;
|
|
return disabled || !selected && (multiple ? multipleLimit > 0 && modelValue.length >= multipleLimit : false);
|
|
};
|
|
const isItemHovering = (target) => props2.hoveringIndex === target;
|
|
const scrollToItem = (index) => {
|
|
const list = listRef.value;
|
|
if (list) {
|
|
list.scrollToItem(index);
|
|
}
|
|
};
|
|
const resetScrollTop = () => {
|
|
const list = listRef.value;
|
|
if (list) {
|
|
list.resetScrollTop();
|
|
}
|
|
};
|
|
const exposed = {
|
|
listRef,
|
|
isSized,
|
|
isItemDisabled,
|
|
isItemHovering,
|
|
isItemSelected,
|
|
scrollToItem,
|
|
resetScrollTop
|
|
};
|
|
expose(exposed);
|
|
const Item = (itemProps) => {
|
|
const {
|
|
index,
|
|
data,
|
|
style
|
|
} = itemProps;
|
|
const sized = unref(isSized);
|
|
const {
|
|
itemSize,
|
|
estimatedSize
|
|
} = unref(listProps);
|
|
const {
|
|
modelValue
|
|
} = select.props;
|
|
const {
|
|
onSelect,
|
|
onHover
|
|
} = select;
|
|
const item = data[index];
|
|
if (item.type === "Group") {
|
|
return createVNode(GroupItem, {
|
|
"item": item,
|
|
"style": style,
|
|
"height": sized ? itemSize : estimatedSize
|
|
}, null);
|
|
}
|
|
const isSelected = isItemSelected(modelValue, item);
|
|
const isDisabled = isItemDisabled(modelValue, isSelected);
|
|
const isHovering = isItemHovering(index);
|
|
return createVNode(OptionItem, mergeProps(itemProps, {
|
|
"selected": isSelected,
|
|
"disabled": getDisabled(item) || isDisabled,
|
|
"created": !!item.created,
|
|
"hovering": isHovering,
|
|
"item": item,
|
|
"onSelect": onSelect,
|
|
"onHover": onHover
|
|
}), {
|
|
default: (props3) => {
|
|
var _a;
|
|
return ((_a = slots.default) == null ? void 0 : _a.call(slots, props3)) || createVNode("span", null, [getLabel(item)]);
|
|
}
|
|
});
|
|
};
|
|
const {
|
|
onKeyboardNavigate,
|
|
onKeyboardSelect
|
|
} = select;
|
|
const onForward = () => {
|
|
onKeyboardNavigate("forward");
|
|
};
|
|
const onBackward = () => {
|
|
onKeyboardNavigate("backward");
|
|
};
|
|
const onKeydown = (e) => {
|
|
const code = getEventCode(e);
|
|
const {
|
|
tab,
|
|
esc,
|
|
down,
|
|
up,
|
|
enter,
|
|
numpadEnter
|
|
} = EVENT_CODE;
|
|
if ([esc, down, up, enter, numpadEnter].includes(code)) {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
}
|
|
switch (code) {
|
|
case tab:
|
|
case esc:
|
|
break;
|
|
case down:
|
|
onForward();
|
|
break;
|
|
case up:
|
|
onBackward();
|
|
break;
|
|
case enter:
|
|
case numpadEnter:
|
|
onKeyboardSelect();
|
|
break;
|
|
}
|
|
};
|
|
return () => {
|
|
var _a, _b, _c, _d;
|
|
const {
|
|
data,
|
|
width
|
|
} = props2;
|
|
const {
|
|
height,
|
|
multiple,
|
|
scrollbarAlwaysOn
|
|
} = select.props;
|
|
const isScrollbarAlwaysOn = computed(() => {
|
|
return isIOS ? true : scrollbarAlwaysOn;
|
|
});
|
|
const List = unref(isSized) ? FixedSizeList : DynamicSizeList;
|
|
return createVNode("div", {
|
|
"class": [ns.b("dropdown"), ns.is("multiple", multiple)],
|
|
"style": {
|
|
width: `${width}px`
|
|
}
|
|
}, [(_a = slots.header) == null ? void 0 : _a.call(slots), ((_b = slots.loading) == null ? void 0 : _b.call(slots)) || ((_c = slots.empty) == null ? void 0 : _c.call(slots)) || createVNode(List, mergeProps({
|
|
"ref": listRef
|
|
}, unref(listProps), {
|
|
"className": ns.be("dropdown", "list"),
|
|
"scrollbarAlwaysOn": isScrollbarAlwaysOn.value,
|
|
"data": data,
|
|
"height": height,
|
|
"width": width,
|
|
"total": data.length,
|
|
"innerElement": "ul",
|
|
"innerProps": {
|
|
id: props2.id,
|
|
role: "listbox",
|
|
"aria-label": props2.ariaLabel,
|
|
"aria-orientation": "vertical"
|
|
},
|
|
"onKeydown": onKeydown
|
|
}), {
|
|
default: (props3) => createVNode(Item, props3, null)
|
|
}), (_d = slots.footer) == null ? void 0 : _d.call(slots)]);
|
|
};
|
|
}
|
|
});
|
|
|
|
export { ElSelectMenu as default };
|
|
//# sourceMappingURL=select-dropdown.mjs.map
|