UNPKG

@extclp/vexip-ui

Version:

A Vue 3 UI library, Highly customizability, full TypeScript, performance pretty good

1 lines 14.2 kB
{"version":3,"file":"cascader-panel.vue2.mjs","sources":["../../../components/cascader/cascader-panel.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { Checkbox } from '@/components/checkbox'\nimport { Icon } from '@/components/icon'\nimport { Option } from '@/components/option'\nimport { VirtualList } from '@/components/virtual-list'\n\nimport { onBeforeUnmount, onMounted, ref, watch } from 'vue'\n\nimport { useIcons, useNameHelper } from '@vexip-ui/config'\nimport { useModifier, useRtl } from '@vexip-ui/hooks'\nimport { boundRange, decide } from '@vexip-ui/utils'\n\nimport type { PropType } from 'vue'\nimport type { VirtualListExposed } from '@/components/virtual-list'\nimport type { CascaderOptionState, CascaderPanelSlots } from './symbol'\n\ndefineOptions({ name: 'CascaderPanel' })\n\nconst props = defineProps({\n options: {\n type: Array as PropType<CascaderOptionState[]>,\n default: () => []\n },\n openedId: {\n type: Number,\n default: null\n },\n values: {\n type: Array as PropType<string[]>,\n default: () => []\n },\n ready: {\n type: Boolean,\n default: false\n },\n multiple: {\n type: Boolean,\n default: false\n },\n checkIcon: {\n type: Object,\n default: null\n },\n isAsync: {\n type: Boolean,\n default: false\n },\n merged: {\n type: Boolean,\n default: false\n },\n noCascaded: {\n type: Boolean,\n default: false\n },\n visible: {\n type: Boolean,\n default: false\n },\n labeledBy: {\n type: String,\n default: undefined\n }\n})\n\nconst emit = defineEmits(['select', 'check', 'hover', 'open', 'back', 'close'])\n\ndefineSlots<CascaderPanelSlots>()\n\nconst nh = useNameHelper('cascader')\nconst icons = useIcons()\nconst { isRtl } = useRtl()\nconst currentHitting = ref(-1)\n\nconst list = ref<VirtualListExposed>()\n\nconst { target: wrapper } = useModifier({\n passive: false,\n onKeyDown: (event, modifier) => {\n if (modifier.escape) {\n emit('close')\n return\n }\n\n decide(\n [\n [\n () => modifier.up || modifier.down,\n () => {\n if (currentHitting.value < 0) {\n currentHitting.value = props.options.findIndex(isSelected)\n\n if (currentHitting.value < 0) {\n currentHitting.value = 0\n }\n\n return\n }\n\n currentHitting.value = boundRange(\n findEnabledIndex(currentHitting.value + (modifier.up ? -1 : 1), modifier.up ? -1 : 1),\n 0,\n props.options.length - 1\n )\n ensureOptionInView(currentHitting.value, modifier.up ? 'top' : 'bottom')\n }\n ],\n [\n () => modifier.left || modifier.right,\n () => {\n if (modifier.right) {\n const option = props.options[currentHitting.value]\n\n if (option && hasChildren(option)) {\n emit('open', option)\n }\n } else {\n emit('back')\n }\n }\n ],\n [\n () => modifier.enter || modifier.space,\n () => {\n event.stopPropagation()\n\n const option = props.options[currentHitting.value]\n\n if (option) {\n if (props.multiple) {\n handleToggleCheck(option)\n } else {\n handleSelect(option, currentHitting.value)\n }\n }\n }\n ]\n ],\n {\n beforeMatchAny: () => event.preventDefault(),\n afterMatchAny: modifier.resetAll\n }\n )\n }\n})\n\nlet listHeight = 0\nlet hoverTimer: ReturnType<typeof setTimeout>\n\nwatch([() => props.ready, () => props.options], () => {\n requestAnimationFrame(computeListHeight)\n\n if (props.ready) {\n list.value?.refresh()\n currentHitting.value = props.options.findIndex(isSelected)\n } else {\n currentHitting.value = -1\n }\n})\n\nonMounted(() => {\n requestAnimationFrame(computeListHeight)\n})\n\nonBeforeUnmount(handleMouseLeave)\n\ndefineExpose({ currentHitting })\n\nfunction hasChildren(option: CascaderOptionState) {\n return !!(option.hasChild || option.children?.length)\n}\n\nfunction isSelected(option: CascaderOptionState) {\n return (\n (hasChildren(option) && option.id === props.openedId) || props.values.includes(option.fullValue)\n )\n}\n\nfunction isCheckboxDisabled(option: CascaderOptionState) {\n return (\n option.disabled ||\n (!props.merged &&\n props.multiple &&\n props.isAsync &&\n hasChildren(option) &&\n !option.childrenLoaded)\n )\n}\n\nfunction handleSelect(option: CascaderOptionState, index: number) {\n if (option.disabled) return\n\n currentHitting.value = index\n\n if (props.multiple || props.noCascaded) {\n hasChildren(option) ? emit('select', option) : handleToggleCheck(option)\n } else {\n emit('select', option)\n }\n}\n\nfunction handleToggleCheck(option: CascaderOptionState) {\n !isCheckboxDisabled(option) && emit('check', option)\n}\n\nfunction handleMouseEnter(option: CascaderOptionState) {\n clearTimeout(hoverTimer)\n\n hoverTimer = setTimeout(() => {\n !option.disabled && emit('hover', option)\n }, 100)\n}\n\nfunction handleMouseLeave() {\n clearTimeout(hoverTimer)\n}\n\nfunction computeListHeight() {\n const el = list.value?.wrapper\n\n if (el) {\n const style = getComputedStyle(el)\n const paddingTop = parseInt(style.paddingTop)\n const paddingBottom = parseInt(style.paddingBottom)\n\n listHeight = el.offsetHeight - paddingTop - paddingBottom\n }\n}\n\nfunction queryEnabledIndex(index: number, step: number) {\n const options = props.options\n step = step / Math.abs(step)\n\n while (options[index]?.disabled) {\n index += step\n\n if (index < 0 || index >= options.length) break\n }\n\n return index\n}\n\nfunction findEnabledIndex(index: number, sign: 1 | -1 = 1) {\n const options = props.options\n\n if (options[index]?.disabled) {\n index = queryEnabledIndex(index, sign)\n\n if (sign > 0 ? index >= options.length : index < 0) {\n index = queryEnabledIndex(index, -sign)\n\n // 全禁用\n if (sign > 0 ? index < 0 : index >= options.length) index = -1\n }\n }\n\n return index\n}\n\nfunction ensureOptionInView(index: number, direction: 'top' | 'bottom') {\n const option = props.options[index]\n const optionHeight = 32\n\n if (!option || !list.value) return\n\n if (direction === 'bottom') {\n const target = (index + 1) * optionHeight\n\n if (list.value.scrollOffset + listHeight < target) {\n list.value.scrollTo(target - listHeight)\n }\n } else {\n const target = index * optionHeight\n\n if (list.value.scrollOffset > target) {\n list.value.scrollTo(target)\n }\n }\n}\n</script>\n\n<template>\n <div\n ref=\"wrapper\"\n :class=\"nh.be('panel')\"\n tabindex=\"-1\"\n :aria-labelledby=\"labeledBy\"\n @mouseleave=\"handleMouseLeave\"\n >\n <VirtualList\n ref=\"list\"\n inherit\n :items=\"options\"\n :item-size=\"32\"\n height=\"100%\"\n id-key=\"id\"\n :items-attrs=\"{\n class: [\n nh.be('options'),\n multiple ? nh.bem('options', 'multiple') : null,\n noCascaded ? nh.bem('options', 'no-cascaded') : null\n ],\n role: 'listbox',\n ariaMultiselectable: multiple\n }\"\n @resize=\"computeListHeight\"\n >\n <template #default=\"{ item, index }\">\n <Option\n :class=\"{\n [nh.ns('option--error')]: item.error\n }\"\n :value=\"item.value\"\n :label=\"item.label\"\n :disabled=\"item.disabled\"\n :selected=\"isSelected(item)\"\n :hitting=\"index === currentHitting\"\n @select=\"handleSelect(item, index)\"\n @mouseenter=\"handleMouseEnter(item)\"\n >\n <slot\n :option=\"item\"\n :index=\"index\"\n :selected=\"isSelected(item)\"\n :can-check=\"isCheckboxDisabled(item)\"\n :has-child=\"hasChildren(item)\"\n >\n <Checkbox\n v-if=\"multiple || noCascaded\"\n inherit\n :class=\"nh.be('checkbox')\"\n :checked=\"item.checked\"\n :control=\"hasChildren(item)\"\n :partial=\"item.partial\"\n :disabled=\"isCheckboxDisabled(item)\"\n size=\"small\"\n @click.prevent.stop=\"handleToggleCheck(item)\"\n ></Checkbox>\n <span :class=\"nh.be('label')\">\n <slot\n name=\"label\"\n :option=\"item\"\n :index=\"index\"\n :selected=\"isSelected(item)\"\n :can-check=\"isCheckboxDisabled(item)\"\n :has-child=\"hasChildren(item)\"\n :handle-select=\"() => handleSelect(item, index)\"\n >\n {{ item.label }}\n </slot>\n </span>\n <div :class=\"nh.be('icon')\">\n <Icon v-if=\"item.loading\" v-bind=\"icons.loading\"></Icon>\n <Icon v-else-if=\"item.error\" v-bind=\"icons.refresh\"></Icon>\n <template v-else-if=\"hasChildren(item)\">\n <Icon v-if=\"isRtl\" v-bind=\"icons.angleLeft\"></Icon>\n <Icon v-else v-bind=\"icons.angleRight\"></Icon>\n </template>\n <Icon\n v-else-if=\"!multiple && !noCascaded && checkIcon && values.includes(item.fullValue)\"\n v-bind=\"icons.check\"\n :icon=\"checkIcon || icons.check.icon\"\n ></Icon>\n </div>\n </slot>\n </Option>\n </template>\n </VirtualList>\n </div>\n</template>\n"],"names":["props","__props","emit","__emit","nh","useNameHelper","icons","useIcons","isRtl","useRtl","currentHitting","ref","list","wrapper","useModifier","event","modifier","decide","isSelected","boundRange","findEnabledIndex","ensureOptionInView","option","hasChildren","handleToggleCheck","handleSelect","listHeight","hoverTimer","watch","computeListHeight","_a","onMounted","onBeforeUnmount","handleMouseLeave","__expose","isCheckboxDisabled","index","handleMouseEnter","el","style","paddingTop","paddingBottom","queryEnabledIndex","step","options","sign","direction","optionHeight","target"],"mappings":"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"}