{"version":3,"file":"index.vue.mjs","sources":["../../../../../packages/components/cascade-panel/src/index.vue"],"sourcesContent":["<template>\n  <div\n    :class=\"['el-cascade-panel', border && 'is-bordered']\"\n    @keydown=\"handleKeyDown\"\n  >\n    <el-cascade-menu\n      v-for=\"(menu, index) in menus\"\n      :key=\"index\"\n      :ref=\"item => (menuList[index] = item)\"\n      :index=\"index\"\n      :nodes=\"menu\"\n    />\n  </div>\n</template>\n\n<script lang=\"ts\">\nimport {\n  computed,\n  defineComponent,\n  nextTick,\n  onBeforeUpdate,\n  onMounted,\n  provide,\n  reactive,\n  ref,\n  shallowRef,\n  watch\n} from 'vue'\nimport { isEqual, flattenDeep } from 'lodash-unified'\nimport { isClient } from '@vueuse/core'\nimport {\n  focusNode,\n  getSibling,\n  isEmpty,\n  unique,\n  castArray,\n  scrollIntoView\n} from '@element-ultra/utils'\nimport {\n  EVENT_CODE,\n  UPDATE_MODEL_EVENT,\n  CHANGE_EVENT\n} from '@element-ultra/shared'\n\nimport ElCascadeMenu from './menu.vue'\nimport Store from './store'\nimport Node, { ExpandTrigger } from './node'\nimport { CommonProps, useCascadeConfig } from './config'\nimport { checkNode, getMenuIndex, sortByOriginalOrder } from './utils'\nimport { Cascade_PANEL_INJECTION_KEY } from './types'\n\nimport type { PropType } from 'vue'\nimport type { Nullable } from '@element-ultra/utils'\nimport type {\n  CascadeValue,\n  CascadeNodeValue,\n  CascadeOption,\n  RenderLabel,\n  default as CascadeNode\n} from './node'\n\nimport type { ElCascadePanelContext } from './types'\n\nexport default defineComponent({\n  name: 'ElCascadePanel',\n\n  components: {\n    ElCascadeMenu\n  },\n\n  props: {\n    ...CommonProps,\n    border: {\n      type: Boolean,\n      default: true\n    },\n    renderLabel: Function as PropType<RenderLabel>\n  },\n\n  emits: [UPDATE_MODEL_EVENT, CHANGE_EVENT, 'close', 'expand-change'],\n\n  setup(props, { emit, slots }) {\n    // for interrupt sync check status in lazy mode\n    let manualChecked = false\n\n    const config = useCascadeConfig(props)\n\n    let store: Nullable<Store> = null\n    const initialLoaded = ref(true)\n    const menuList = ref<any[]>([])\n    const checkedValue = shallowRef<Nullable<CascadeValue>>(null)\n    const menus = ref<CascadeNode[][]>([])\n    const expandingNode = shallowRef<Nullable<CascadeNode>>(null)\n    const checkedNodes = shallowRef<CascadeNode[]>([])\n\n    const isHoverMenu = computed(\n      () => config.value.expandTrigger === ExpandTrigger.HOVER\n    )\n    const renderLabelFn = computed(() => props.renderLabel || slots.default)\n\n    const initStore = () => {\n      const { options } = props\n      const cfg = config.value\n\n      manualChecked = false\n      store = new Store(options, cfg)\n      menus.value = [store.getNodes()]\n\n      if (cfg.lazy && isEmpty(props.options)) {\n        initialLoaded.value = false\n        lazyLoad(undefined, list => {\n          if (list) {\n            store = new Store(list, cfg)\n            menus.value = [store.getNodes()]\n          }\n          initialLoaded.value = true\n          syncCheckedValue(false, true)\n        })\n      } else {\n        syncCheckedValue(false, true)\n      }\n    }\n\n    const lazyLoad: ElCascadePanelContext['lazyLoad'] = (node, cb) => {\n      const cfg = config.value\n      node! = node || new Node({}, cfg, undefined, true)\n      node.loading = true\n\n      const resolve = (dataList: CascadeOption[]) => {\n        const _node = node as Node\n        const parent = _node.root ? null : _node\n        dataList && store?.appendNodes(dataList, parent as any)\n        _node.loading = false\n        _node.loaded = true\n        _node.childrenData = _node.childrenData || []\n        cb && cb(dataList)\n      }\n\n      cfg.lazyLoad(node, resolve as any)\n    }\n\n    const expandNode: ElCascadePanelContext['expandNode'] = (node, silent) => {\n      const { level } = node\n      const newMenus = menus.value.slice(0, level)\n      let newExpandingNode: Nullable<CascadeNode>\n\n      if (node.isLeaf) {\n        newExpandingNode = node.pathNodes[level - 2]\n      } else {\n        newExpandingNode = node\n        newMenus.push(node.children)\n      }\n\n      if (expandingNode.value?.uid !== newExpandingNode?.uid) {\n        expandingNode.value = node\n        menus.value = newMenus\n        !silent && emit('expand-change', node?.pathValues || [])\n      }\n    }\n\n    const handleCheckChange: ElCascadePanelContext['handleCheckChange'] = (\n      node,\n      checked,\n      emitClose = true\n    ) => {\n      const { checkStrictly, multiple } = config.value\n      const oldNode = checkedNodes.value[0]\n      manualChecked = true\n      // 单选则取消旧的选中\n      !multiple && oldNode?.doCheck(false)\n\n      node.doCheck(checked)\n      calculateCheckedValue()\n      if (!multiple && !checkStrictly) {\n        emitClose ? emit('close') : expandParentNode(node)\n      }\n    }\n\n    const expandParentNode = node => {\n      if (!node) return\n      node = node.parent\n      expandParentNode(node)\n      node && expandNode(node)\n    }\n\n    const getFlattedNodes = (leafOnly: boolean) => {\n      return store?.getFlattedNodes(leafOnly)\n    }\n\n    const getCheckedNodes = (leafOnly: boolean) => {\n      return getFlattedNodes(leafOnly)?.filter(node => node.checked !== false)\n    }\n\n    const clearCheckedNodes = () => {\n      checkedNodes.value.forEach(node => node.doCheck(false))\n      calculateCheckedValue()\n    }\n\n    const calculateCheckedValue = () => {\n      const { checkStrictly, multiple } = config.value\n      const oldNodes = checkedNodes.value\n      const newNodes = getCheckedNodes(!checkStrictly)!\n\n      // ensure the original order\n      const nodes = sortByOriginalOrder(oldNodes, newNodes)\n\n      const values = nodes.map(node => node.valueByOption)\n      checkedNodes.value = nodes\n      checkedValue.value = multiple ? values : values[0] ?? null\n    }\n\n    const syncCheckedValue = (loaded = false, forced = false) => {\n      const { modelValue } = props\n      const { lazy, multiple, checkStrictly } = config.value\n      const leafOnly = !checkStrictly\n\n      if (\n        !initialLoaded.value ||\n        manualChecked ||\n        (!forced && isEqual(modelValue, checkedValue.value))\n      )\n        return\n\n      if (lazy && !loaded) {\n        const values: CascadeNodeValue[] = unique(\n          flattenDeep(castArray(modelValue))\n        )\n        const nodes = values\n          .map(val => store?.getNodeByValue(val))\n          .filter(node => !!node && !node.loaded && !node.loading) as Node[]\n\n        if (nodes.length) {\n          nodes.forEach(node => {\n            lazyLoad(node, () => syncCheckedValue(false, forced))\n          })\n        } else {\n          syncCheckedValue(true, forced)\n        }\n      } else {\n        const values = multiple ? castArray(modelValue) : [modelValue]\n        const nodes = unique(\n          values.map(val => store?.getNodeByValue(val, leafOnly))\n        ) as Node[]\n        syncMenuState(nodes, false)\n        checkedValue.value = modelValue!\n      }\n    }\n\n    const syncMenuState = (\n      newCheckedNodes: CascadeNode[],\n      reserveExpandingState = true\n    ) => {\n      const { checkStrictly } = config.value\n      const oldNodes = checkedNodes.value\n      const newNodes = newCheckedNodes.filter(\n        node => !!node && (checkStrictly || node.isLeaf)\n      )\n      const oldExpandingNode = store?.getSameNode(expandingNode.value!)\n      const newExpandingNode =\n        (reserveExpandingState && oldExpandingNode) || newNodes[0]\n\n      if (newExpandingNode) {\n        newExpandingNode.pathNodes.forEach(node => expandNode(node, true))\n      } else {\n        expandingNode.value = null\n      }\n\n      oldNodes.forEach(node => node.doCheck(false))\n      // newNodes.forEach(node => node.doCheck(true))\n      if (props.props.multiple) {\n        reactive(newNodes).forEach((node) => node.doCheck(true))\n      } else {\n        newNodes.forEach((node) => node.doCheck(true))\n      }\n      checkedNodes.value = newNodes\n      nextTick(scrollToExpandingNode)\n    }\n\n    const scrollToExpandingNode = () => {\n      if (!isClient) return\n\n      menuList.value.forEach(menu => {\n        const menuElement = menu?.$el\n        if (menuElement) {\n          const container = (menuElement as HTMLElement).querySelector(\n            '.el-scrollbar__wrap'\n          )\n          const activeNode =\n            menuElement.querySelector('.el-cascade-node.is-active') ||\n            menuElement.querySelector('.el-cascade-node.in-active-path')\n          scrollIntoView(container as HTMLElement, activeNode)\n        }\n      })\n    }\n\n    const handleKeyDown = (e: KeyboardEvent) => {\n      const target = e.target as HTMLElement\n      const { code } = e\n\n      switch (code) {\n        case EVENT_CODE.up:\n        case EVENT_CODE.down: {\n          e.preventDefault()\n          const distance = code === EVENT_CODE.up ? -1 : 1\n          focusNode(\n            getSibling(target, distance, '.el-cascade-node[tabindex=\"-1\"]')\n          )\n          break\n        }\n        case EVENT_CODE.left: {\n          e.preventDefault()\n          const preMenu = menuList.value[getMenuIndex(target) - 1]\n          const expandedNode = preMenu?.$el.querySelector(\n            '.el-cascade-node[aria-expanded=\"true\"]'\n          )\n          focusNode(expandedNode)\n          break\n        }\n        case EVENT_CODE.right: {\n          e.preventDefault()\n          const nextMenu = menuList.value[getMenuIndex(target) + 1]\n          const firstNode = nextMenu?.$el.querySelector(\n            '.el-cascade-node[tabindex=\"-1\"]'\n          )\n          focusNode(firstNode)\n          break\n        }\n        case EVENT_CODE.enter:\n          checkNode(target)\n          break\n        case EVENT_CODE.esc:\n        case EVENT_CODE.tab:\n          emit('close')\n          break\n      }\n    }\n\n    provide(\n      Cascade_PANEL_INJECTION_KEY,\n      reactive({\n        config,\n        expandingNode,\n        checkedNodes,\n        isHoverMenu,\n        initialLoaded,\n        renderLabelFn,\n        lazyLoad,\n        expandNode,\n        handleCheckChange\n      })\n    )\n\n    watch([config, () => props.options], initStore, {\n      deep: true,\n      immediate: true\n    })\n\n    watch(\n      () => props.modelValue,\n      () => {\n        manualChecked = false\n        syncCheckedValue()\n      }\n    )\n\n    watch(checkedValue, val => {\n      if (!isEqual(val, props.modelValue)) {\n        emit(UPDATE_MODEL_EVENT, val)\n        const { multiple } = config.value\n        const _checkedNodes = checkedNodes.value\n        if (multiple) {\n          const { label, item } = _checkedNodes.reduce(\n            (acc, cur) => {\n              acc.item.push(cur.nodeByOption)\n              acc.label.push(cur.labelByOption)\n              return acc\n            },\n            { label: [] as any[], item: [] as any[] }\n          )\n          emit(CHANGE_EVENT, val, label, item)\n        } else {\n          const checkedNode = _checkedNodes[0]\n          emit(\n            CHANGE_EVENT,\n            val,\n            checkedNode?.labelByOption,\n            checkedNode?.nodeByOption\n          )\n        }\n      }\n    })\n\n    onBeforeUpdate(() => (menuList.value = []))\n\n    onMounted(() => !isEmpty(props.modelValue) && syncCheckedValue())\n\n    return {\n      menuList,\n      menus,\n      checkedNodes,\n      handleKeyDown,\n      handleCheckChange,\n      getFlattedNodes,\n      getCheckedNodes,\n      clearCheckedNodes,\n      calculateCheckedValue,\n      scrollToExpandingNode\n    }\n  }\n})\n</script>\n"],"names":["_resolveComponent","_openBlock","_createElementBlock","_normalizeClass","_Fragment","_renderList"],"mappings":";;;;;qCACEA,iBAWM,iBAAA,CAAA,CAAA;SATHC,WAAO,EAAAC,kBAAA;AAAA,IAAA,KAAA;AAAA,IAAA;AAAA,MAAA,OAAAC,cAAA,CAAA,CAAA,oBAAA,IAAA,CAAA,MAAA,IAAA,aAAA,CAAA,CAAA;AAAA,yBAER,CAAA,KAME,OAAA,CAAA,CAAA,GAAA,CAAA,GAAA,IAAA,KAAA,IAAA,CAAA,aAAA,IALwB,IAAK,CAAA,aAAA,CAAV,GAAA,IAAA,CAAA,CAAA;AAAA,KAAA;AAAA;iBACb,IAAK,CAAA,EAAAD,kBAAA;AAAA,QAAAE,QAAA;AAAA,QAAA,IAAA;AAAA,QAAAC,UAAA,CAAA,IAAA,CAAA,KAAA,EAAA,CAAA,MAAA,KAAA,KAAA;;YACV,GAAK,EAAA,KAAA;AAAA,YACL,OAAO,EAAA,IAAA;AAAA,YACP,KAAK,CAAM,IAAA,KAAA,IAAA,CAAA,QAAA,CAAA,KAAA,CAAA,GAAA,IAAA;AAAA,YAAA,KAAA;AAAA;;;;;;;;;;;;;;;"}