feat: Autocomplete and Select support autoAdjustOverflow

This commit is contained in:
andy 2025-01-16 12:48:49 +11:00 committed by Andy Ho
parent 5199b5c9bf
commit 93fb1eb164
9 changed files with 58 additions and 14 deletions

View File

@ -43,6 +43,7 @@ Common props ref[Common props](/docs/react/common-props)
| Property | Description | Type | Default | Version |
| --- | --- | --- | --- | --- |
| allowClear | Show clear button | boolean \| { clearIcon?: ReactNode } | false | 5.8.0: Support Object type |
| autoAdjustOverflow | Whether to adjust dropdown placement automatically when dropdown is off screen | boolean | true | 5.24.0 |
| autoFocus | If get focus when component mounted | boolean | false | |
| backfill | If backfill selected item the input when using keyboard | boolean | false | |
| children (for customize input element) | Customize input element | HTMLInputElement \| HTMLTextAreaElement \| React.ReactElement<InputProps> | <Input /> | |

View File

@ -5,6 +5,7 @@ import toArray from 'rc-util/lib/Children/toArray';
import omit from 'rc-util/lib/omit';
import { useZIndex } from '../_util/hooks/useZIndex';
import type { AdjustOverflow } from '../_util/placements';
import genPurePanel from '../_util/PurePanel';
import type { InputStatus } from '../_util/statusUtils';
import { devUseWarning } from '../_util/warning';
@ -43,6 +44,7 @@ export interface AutoCompleteProps<
/** @deprecated Please use `popupMatchSelectWidth` instead */
dropdownMatchSelectWidth?: boolean | number;
popupMatchSelectWidth?: boolean | number;
autoAdjustOverflow?: boolean | AdjustOverflow;
}
function isSelectOptionOrSelectOptGroup(child: any): boolean {
@ -60,6 +62,7 @@ const AutoComplete: React.ForwardRefRenderFunction<RefSelectProps, AutoCompleteP
dropdownClassName,
children,
dataSource,
autoAdjustOverflow = true,
} = props;
const childNodes: React.ReactElement[] = toArray(children);
@ -144,6 +147,7 @@ const AutoComplete: React.ForwardRefRenderFunction<RefSelectProps, AutoCompleteP
}}
className={classNames(`${prefixCls}-auto-complete`, className)}
mode={Select.SECRET_COMBOBOX_MODE_DO_NOT_USE as SelectProps['mode']}
autoAdjustOverflow={autoAdjustOverflow}
{...{
// Internal api
getInputElement,

View File

@ -44,6 +44,7 @@ demo:
| 参数 | 说明 | 类型 | 默认值 | 版本 |
| --- | --- | --- | --- | --- |
| allowClear | 支持清除 | boolean \| { clearIcon?: ReactNode } | false | 5.8.0: 支持对象形式 |
| autoAdjustOverflow | 下拉框被遮挡时自动调整位置 | boolean | true | 5.24.0 |
| autoFocus | 自动获取焦点 | boolean | false | |
| backfill | 使用键盘选择选项的时候把选中项回填到输入框中 | boolean | false | |
| children (自动完成的数据源) | 自动完成的数据源,不能和自定义输入框同时配置 | React.ReactElement&lt;OptionProps> \| Array&lt;React.ReactElement&lt;OptionProps>> | - | |

View File

@ -329,7 +329,10 @@ const Cascader = React.forwardRef<CascaderRef, CascaderProps<any>>((props, ref)
disabled={mergedDisabled}
style={{ ...cascader?.style, ...style }}
{...(restProps as any)}
builtinPlacements={mergedBuiltinPlacements(builtinPlacements, popupOverflow)}
builtinPlacements={mergedBuiltinPlacements({
buildInPlacements: builtinPlacements,
popupOverflow,
})}
direction={mergedDirection}
placement={memoPlacement}
notFoundContent={mergedNotFoundContent}

View File

@ -80,6 +80,7 @@ Common props ref[Common props](/docs/react/common-props)
| Property | Description | Type | Default | Version |
| --- | --- | --- | --- | --- |
| allowClear | Customize clear icon | boolean \| { clearIcon?: ReactNode } | false | 5.8.0: Support object type |
| autoAdjustOverflow | Whether to adjust dropdown placement automatically when dropdown is off screen | boolean | true | 5.24.0 |
| autoClearSearchValue | Whether the current search will be cleared on selecting an item. Only applies when `mode` is set to `multiple` or `tags` | boolean | true | |
| autoFocus | Get focus by default | boolean | false | |
| defaultActiveFirstOption | Whether active first option by default | boolean | true | |

View File

@ -10,6 +10,7 @@ import omit from 'rc-util/lib/omit';
import { useZIndex } from '../_util/hooks/useZIndex';
import type { SelectCommonPlacement } from '../_util/motion';
import { getTransitionName } from '../_util/motion';
import type { AdjustOverflow } from '../_util/placements';
import genPurePanel from '../_util/PurePanel';
import type { InputStatus } from '../_util/statusUtils';
import { getMergedStatus, getStatusClassNames } from '../_util/statusUtils';
@ -82,6 +83,7 @@ export interface SelectProps<
/** @deprecated Please use `popupMatchSelectWidth` instead */
dropdownMatchSelectWidth?: boolean | number;
popupMatchSelectWidth?: boolean | number;
autoAdjustOverflow?: boolean | AdjustOverflow;
}
const SECRET_COMBOBOX_MODE_DO_NOT_USE = 'SECRET_COMBOBOX_MODE_DO_NOT_USE';
@ -120,6 +122,7 @@ const InternalSelect = <
tagRender,
maxCount,
prefix,
autoAdjustOverflow = true,
...rest
} = props;
@ -287,7 +290,11 @@ const InternalSelect = <
style={{ ...select?.style, ...style }}
dropdownMatchSelectWidth={mergedPopupMatchSelectWidth}
transitionName={getTransitionName(rootPrefixCls, 'slide-up', transitionName)}
builtinPlacements={mergedBuiltinPlacements(builtinPlacements, popupOverflow)}
builtinPlacements={mergedBuiltinPlacements({
buildInPlacements: builtinPlacements,
popupOverflow,
autoAdjustOverflow,
})}
listHeight={listHeight}
listItemHeight={listItemHeight}
mode={mode}

View File

@ -81,6 +81,7 @@ return (
| 参数 | 说明 | 类型 | 默认值 | 版本 |
| --- | --- | --- | --- | --- |
| allowClear | 自定义清除按钮 | boolean \| { clearIcon?: ReactNode } | false | 5.8.0: 支持对象类型 |
| autoAdjustOverflow | 下拉框被遮挡时自动调整位置 | boolean | true | 5.24.0 |
| autoClearSearchValue | 是否在选中项后清空搜索框,只在 `mode``multiple``tags` 时有效 | boolean | true | |
| autoFocus | 默认获取焦点 | boolean | false | |
| defaultActiveFirstOption | 是否默认高亮第一个选项 | boolean | true | |

View File

@ -1,16 +1,40 @@
import type { AlignType, BuildInPlacements } from '@rc-component/trigger';
import type { PopupOverflow } from '../config-provider/context';
import type { AdjustOverflow } from '../_util/placements';
const getBuiltInPlacements = (popupOverflow?: PopupOverflow): Record<string, AlignType> => {
interface MergedPlacementsConfig {
buildInPlacements?: BuildInPlacements;
popupOverflow?: PopupOverflow;
autoAdjustOverflow?: boolean | AdjustOverflow;
}
const getOverflow = (autoAdjustOverflow?: boolean | AdjustOverflow): AlignType['overflow'] => {
if (autoAdjustOverflow === false) {
return {
adjustX: false,
adjustY: false,
};
}
const overflow =
autoAdjustOverflow && typeof autoAdjustOverflow === 'object' ? autoAdjustOverflow : {};
return {
adjustX: true,
adjustY: true,
shiftY: true,
...overflow,
};
};
const getBuiltInPlacements = (
popupOverflow?: PopupOverflow,
autoAdjustOverflow?: boolean | AdjustOverflow,
): Record<string, AlignType> => {
const htmlRegion: AlignType['htmlRegion'] = popupOverflow === 'scroll' ? 'scroll' : 'visible';
const sharedConfig: AlignType = {
overflow: {
adjustX: true,
adjustY: true,
shiftY: true,
},
overflow: getOverflow(autoAdjustOverflow),
htmlRegion,
dynamicInset: true,
};
@ -39,11 +63,10 @@ const getBuiltInPlacements = (popupOverflow?: PopupOverflow): Record<string, Ali
};
};
function mergedBuiltinPlacements(
buildInPlacements?: BuildInPlacements,
popupOverflow?: PopupOverflow,
) {
return buildInPlacements || getBuiltInPlacements(popupOverflow);
function mergedBuiltinPlacements(config: MergedPlacementsConfig) {
const { buildInPlacements, popupOverflow, autoAdjustOverflow } = config;
return buildInPlacements || getBuiltInPlacements(popupOverflow, autoAdjustOverflow);
}
export default mergedBuiltinPlacements;

View File

@ -296,7 +296,10 @@ const InternalTreeSelect = <ValueType = any, OptionType extends DataNode = DataN
disabled={mergedDisabled}
{...selectProps}
dropdownMatchSelectWidth={mergedPopupMatchSelectWidth}
builtinPlacements={mergedBuiltinPlacements(builtinPlacements, popupOverflow)}
builtinPlacements={mergedBuiltinPlacements({
buildInPlacements: builtinPlacements,
popupOverflow,
})}
ref={ref}
prefixCls={prefixCls}
className={mergedClassName}