element-plus
Version:
A Component Library for Vue 3
1 lines • 13.6 kB
Source Map (JSON)
{"version":3,"file":"index.mjs","sources":["../../../../../../packages/components/cascader-panel/src/index.vue"],"sourcesContent":["<template>\n <div\n :class=\"['el-cascader-panel', border && 'is-bordered']\"\n @keydown=\"handleKeyDown\"\n >\n <el-cascader-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 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-plus/utils'\nimport {\n EVENT_CODE,\n UPDATE_MODEL_EVENT,\n CHANGE_EVENT,\n} from '@element-plus/constants'\n\nimport ElCascaderMenu from './menu.vue'\nimport Store from './store'\nimport Node, { ExpandTrigger } from './node'\nimport { CommonProps, useCascaderConfig } from './config'\nimport { checkNode, getMenuIndex, sortByOriginalOrder } from './utils'\nimport { CASCADER_PANEL_INJECTION_KEY } from './types'\n\nimport type { PropType } from 'vue'\nimport type { Nullable } from '@element-plus/utils'\nimport type {\n CascaderValue,\n CascaderNodeValue,\n CascaderOption,\n RenderLabel,\n default as CascaderNode,\n} from './node'\n\nimport type { ElCascaderPanelContext } from './types'\n\nexport default defineComponent({\n name: 'ElCascaderPanel',\n\n components: {\n ElCascaderMenu,\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 = useCascaderConfig(props)\n\n let store: Nullable<Store> = null\n const initialLoaded = ref(true)\n const menuList = ref<any[]>([])\n const checkedValue = ref<Nullable<CascaderValue>>(null)\n const menus = ref<CascaderNode[][]>([])\n const expandingNode = ref<Nullable<CascaderNode>>(null)\n const checkedNodes = ref<CascaderNode[]>([])\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: ElCascaderPanelContext['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: CascaderOption[]) => {\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: ElCascaderPanelContext['expandNode'] = (node, silent) => {\n const { level } = node\n const newMenus = menus.value.slice(0, level)\n let newExpandingNode: Nullable<CascaderNode>\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: ElCascaderPanelContext['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 node.doCheck(checked)\n calculateCheckedValue()\n emitClose && !multiple && !checkStrictly && emit('close')\n !emitClose && !multiple && !checkStrictly && expandParentNode(node)\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 // ensure the original order\n const nodes = sortByOriginalOrder(oldNodes, newNodes)\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: CascaderNodeValue[] = 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: CascaderNode[],\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\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-cascader-node.is-active') ||\n menuElement.querySelector('.el-cascader-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-cascader-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-cascader-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-cascader-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 CASCADER_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 emit(CHANGE_EVENT, val)\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":["_openBlock"],"mappings":"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"}