UNPKG

hongluan-ui

Version:
1 lines 13.7 kB
{"version":3,"file":"index.mjs","sources":["../../../../../../packages/components/cascader-panel/src/index.vue"],"sourcesContent":["<template>\n <div\n :class=\"[\n namespace,\n border && 'is-bordered'\n ]\"\n @keydown=\"handleKeyDown\"\n >\n <hl-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, defineComponent, nextTick,\n onBeforeUpdate, onMounted,\n provide, reactive,\n ref, watch,\n} from 'vue'\nimport { isEqual, flattenDeep, cloneDeep } from 'lodash-unified'\nimport { focusNode, getSibling, scrollIntoView, castArray, unique, isClient, isEmpty } from '@hongluan-ui/utils'\nimport { EVENT_CODE, UPDATE_MODEL_EVENT, CHANGE_EVENT } from '@hongluan-ui/constants'\nimport { useNamespace } from '@hongluan-ui/hooks'\nimport HlCascaderMenu from './menu.vue'\nimport Store from './store'\nimport Node 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 '@hongluan-ui/utils'\nimport type {\n CascaderValue,\n CascaderNode,\n CascaderNodeValue,\n CascaderOption,\n RenderLabel,\n CascaderPanelContext,\n} from './types'\n\nexport default defineComponent({\n name: 'CascaderPanel',\n\n components: {\n HlCascaderMenu,\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: [\n UPDATE_MODEL_EVENT,\n CHANGE_EVENT,\n 'close',\n 'expand-change',\n ],\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(() => config.value.expandTrigger === 'hover')\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: CascaderPanelContext['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: CascaderPanelContext['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: CascaderPanelContext['handleCheckChange'] = (node, checked, emitClose = true) => {\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\n const clearCheckedNodes = () => {\n checkedNodes.value.forEach(node => node.doCheck(false))\n calculateCheckedValue()\n menus.value = menus.value.slice(0, 1)\n expandingNode.value = null\n emit('expand-change', [])\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 ) return\n\n if (lazy && !loaded) {\n const values: CascaderNodeValue[] = unique(flattenDeep(castArray(modelValue)))\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(values.map((val: CascaderNodeValue) => store?.getNodeByValue(val, leafOnly))) as Node[]\n syncMenuState(nodes, forced)\n checkedValue.value = cloneDeep(modelValue)\n }\n }\n\n const syncMenuState = (newCheckedNodes: CascaderNode[], reserveExpandingState = true) => {\n const { checkStrictly } = config.value\n const oldNodes = checkedNodes.value\n const newNodes = newCheckedNodes.filter(node => !!node && (checkStrictly || node.isLeaf))\n const oldExpandingNode = store?.getSameNode(expandingNode.value)\n const newExpandingNode = 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 reactive(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.querySelector('.scrollbar-wrap')\n const activeNode = menuElement.querySelector('.cascader-node.is-active') ||\n menuElement.querySelector('.cascader-node.in-active-path')\n scrollIntoView(container, 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 const distance = code === EVENT_CODE.up ? -1 : 1\n focusNode(getSibling(target, distance, '.cascader-node[tabindex=\"-1\"]') as HTMLElement)\n break\n case EVENT_CODE.left:\n const preMenu = menuList.value[getMenuIndex(target) - 1]\n const expandedNode = preMenu?.$el.querySelector('.cascader-node[aria-expanded=\"true\"]')\n focusNode(expandedNode)\n break\n case EVENT_CODE.right:\n const nextMenu = menuList.value[getMenuIndex(target) + 1]\n const firstNode = nextMenu?.$el.querySelector('.cascader-node[tabindex=\"-1\"]')\n focusNode(firstNode)\n break\n case EVENT_CODE.enter:\n checkNode(target)\n break\n }\n }\n\n provide(CASCADER_PANEL_INJECTION_KEY, reactive({\n config,\n expandingNode,\n checkedNodes,\n isHoverMenu,\n initialLoaded,\n renderLabelFn,\n lazyLoad,\n expandNode,\n handleCheckChange,\n }))\n\n watch([config, () => props.options], initStore, {\n deep: true,\n immediate: true,\n })\n\n watch(() => props.modelValue, () => {\n manualChecked = false\n syncCheckedValue()\n }, {\n deep: true,\n })\n\n watch(\n () => checkedValue.value,\n val => {\n if (!isEqual(val, props.modelValue)) {\n emit(UPDATE_MODEL_EVENT, val)\n emit(CHANGE_EVENT, val)\n }\n },\n )\n\n onBeforeUpdate(() => menuList.value = [])\n\n onMounted(() => !isEmpty(props.modelValue) && syncCheckedValue())\n const { namespace } = useNamespace('cascader-panel')\n return {\n namespace,\n menuList,\n menus,\n checkedNodes,\n handleKeyDown,\n handleCheckChange,\n getFlattedNodes,\n /**\n * @description get an array of currently selected node,(leafOnly) whether only return the leaf checked nodes, default is `false`\n */\n getCheckedNodes,\n /**\n * @description clear checked nodes\n */\n clearCheckedNodes,\n calculateCheckedValue,\n scrollToExpandingNode,\n }\n },\n})\n</script>\n"],"names":["_openBlock"],"mappings":"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"}