{"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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"}