|
1 | 1 | import React, { useMemo } from 'react'; |
2 | 2 | import classNames from 'classnames'; |
3 | | -import { pick, omit } from 'lodash-es'; |
4 | | -import Panel from './components/Panel'; |
5 | | -import SelectInput from '../select-input'; |
| 3 | +import { omit, pick } from 'lodash-es'; |
| 4 | +import parseTNode, { parseContentTNode } from '../_util/parseTNode'; |
6 | 5 | import FakeArrow from '../common/FakeArrow'; |
7 | | -import useConfig from '../hooks/useConfig'; |
8 | 6 | import useCommonClassName from '../hooks/useCommonClassName'; |
| 7 | +import useConfig from '../hooks/useConfig'; |
| 8 | +import useDefaultProps from '../hooks/useDefaultProps'; |
9 | 9 | import { useLocaleReceiver } from '../locale/LocalReceiver'; |
10 | | -import { TagInputValue } from '../tag-input'; |
11 | | -import { TdCascaderProps } from './interface'; |
12 | | -import { closeIconClickEffect, handleRemoveTagEffect } from './core/effect'; |
13 | | -import { getPanels, getSingleContent, getMultipleContent } from './core/helper'; |
| 10 | +import SelectInput from '../select-input'; |
| 11 | +import Panel from './components/Panel'; |
14 | 12 | import { getFakeArrowIconClass } from './core/className'; |
15 | | -import { useCascaderContext } from './hooks'; |
| 13 | +import { closeIconClickEffect, handleRemoveTagEffect } from './core/effect'; |
| 14 | +import { getMultipleContent, getPanels, getSingleContent } from './core/helper'; |
16 | 15 | import { cascaderDefaultProps } from './defaultProps'; |
17 | | -import { StyledProps } from '../common'; |
18 | | -import useDefaultProps from '../hooks/useDefaultProps'; |
19 | | -import parseTNode, { parseContentTNode } from '../_util/parseTNode'; |
| 16 | +import { useCascaderContext } from './hooks'; |
| 17 | + |
| 18 | +import type { StyledProps } from '../common'; |
| 19 | +import type { TagInputValue } from '../tag-input'; |
| 20 | +import type { TdCascaderProps } from './interface'; |
20 | 21 |
|
21 | 22 | export interface CascaderProps extends TdCascaderProps, StyledProps {} |
22 | 23 |
|
@@ -106,19 +107,40 @@ const Cascader: React.FC<CascaderProps> = (originalProps) => { |
106 | 107 | cascaderMenuList.forEach((menu: HTMLDivElement) => { |
107 | 108 | const firstSelectedNode: HTMLDivElement = |
108 | 109 | menu?.querySelector(`.${classPrefix}-is-selected`) || menu?.querySelector(`.${classPrefix}-is-expanded`); |
| 110 | + |
| 111 | + // 只取第一个选中的节点进行滚动对齐 |
109 | 112 | if (!firstSelectedNode || !menu) return; |
110 | 113 |
|
111 | | - const { paddingBottom } = getComputedStyle(firstSelectedNode); |
112 | | - const { marginBottom } = getComputedStyle(menu); |
113 | | - const elementBottomHeight = parseInt(paddingBottom, 10) + parseInt(marginBottom, 10); |
| 114 | + // 计算节点在菜单中的相对位置 |
| 115 | + const nodeTop = firstSelectedNode.offsetTop; |
| 116 | + const nodeHeight = firstSelectedNode.offsetHeight; |
| 117 | + const menuHeight = menu.clientHeight; |
| 118 | + const currentScrollTop = menu.scrollTop; |
| 119 | + |
| 120 | + // 计算节点在可视区域中的位置 |
| 121 | + const nodeVisibleTop = nodeTop - currentScrollTop; |
| 122 | + const nodeVisibleBottom = nodeVisibleTop + nodeHeight; |
| 123 | + |
| 124 | + const isNodeFullyVisible = nodeVisibleTop >= 0 && nodeVisibleBottom <= menuHeight; |
| 125 | + // 如果节点已经完全可见,则不需要滚动 |
| 126 | + if (isNodeFullyVisible) return; |
| 127 | + |
| 128 | + let targetScrollTop = currentScrollTop; |
| 129 | + |
| 130 | + if (nodeVisibleTop < 0) { |
| 131 | + // 如果节点在可视区域上方,滚动到节点顶部 |
| 132 | + targetScrollTop = nodeTop; |
| 133 | + } else if (nodeVisibleBottom > menuHeight) { |
| 134 | + // 如果节点在可视区域下方,滚动到节点底部对齐菜单底部 |
| 135 | + targetScrollTop = nodeTop - menuHeight + nodeHeight; |
| 136 | + } |
| 137 | + |
| 138 | + // 确保滚动位置不会超出边界 |
| 139 | + const maxScrollTop = menu.scrollHeight - menuHeight; |
| 140 | + targetScrollTop = Math.max(0, Math.min(targetScrollTop, maxScrollTop)); |
114 | 141 |
|
115 | | - const updateValue = |
116 | | - firstSelectedNode.offsetTop - |
117 | | - menu.offsetTop - |
118 | | - (menu.clientHeight - firstSelectedNode.clientHeight) + |
119 | | - elementBottomHeight; |
120 | 142 | // eslint-disable-next-line no-param-reassign |
121 | | - menu.scrollTop = updateValue; |
| 143 | + menu.scrollTop = targetScrollTop; |
122 | 144 | }); |
123 | 145 | }); |
124 | 146 | }; |
|
0 commit comments