feat: improve focus behavior (#51444)

* feat: improve focus behavior

* test: update snapshot

* chore(deps): bump rc-input-number version

* chore: remove useless code

* test: update snapshot
This commit is contained in:
Jony J 2024-11-01 17:21:02 +08:00 committed by GitHub
parent f73825ba74
commit 6380fc7a95
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 368 additions and 45 deletions

View File

@ -2835,6 +2835,152 @@ exports[`renders components/input-number/demo/filled-debug.tsx extend context co
exports[`renders components/input-number/demo/filled-debug.tsx extend context correctly 2`] = `[]`;
exports[`renders components/input-number/demo/focus.tsx extend context correctly 1`] = `
<div
class="ant-space ant-space-vertical ant-space-gap-row-small ant-space-gap-col-small"
style="width: 100%;"
>
<div
class="ant-space-item"
>
<div
class="ant-space ant-space-horizontal ant-space-align-center ant-space-gap-row-small ant-space-gap-col-small"
style="flex-wrap: wrap;"
>
<div
class="ant-space-item"
>
<button
class="ant-btn ant-btn-default ant-btn-color-default ant-btn-variant-outlined"
type="button"
>
<span>
Focus at first
</span>
</button>
</div>
<div
class="ant-space-item"
>
<button
class="ant-btn ant-btn-default ant-btn-color-default ant-btn-variant-outlined"
type="button"
>
<span>
Focus at last
</span>
</button>
</div>
<div
class="ant-space-item"
>
<button
class="ant-btn ant-btn-default ant-btn-color-default ant-btn-variant-outlined"
type="button"
>
<span>
Focus to select all
</span>
</button>
</div>
<div
class="ant-space-item"
>
<button
class="ant-btn ant-btn-default ant-btn-color-default ant-btn-variant-outlined"
type="button"
>
<span>
Focus prevent scroll
</span>
</button>
</div>
</div>
</div>
<div
class="ant-space-item"
>
<div
class="ant-input-number ant-input-number-outlined"
style="width: 100%;"
>
<div
class="ant-input-number-handler-wrap"
>
<span
aria-disabled="false"
aria-label="Increase Value"
class="ant-input-number-handler ant-input-number-handler-up"
role="button"
unselectable="on"
>
<span
aria-label="up"
class="anticon anticon-up ant-input-number-handler-up-inner"
role="img"
>
<svg
aria-hidden="true"
data-icon="up"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M890.5 755.3L537.9 269.2c-12.8-17.6-39-17.6-51.7 0L133.5 755.3A8 8 0 00140 768h75c5.1 0 9.9-2.5 12.9-6.6L512 369.8l284.1 391.6c3 4.1 7.8 6.6 12.9 6.6h75c6.5 0 10.3-7.4 6.5-12.7z"
/>
</svg>
</span>
</span>
<span
aria-disabled="false"
aria-label="Decrease Value"
class="ant-input-number-handler ant-input-number-handler-down"
role="button"
unselectable="on"
>
<span
aria-label="down"
class="anticon anticon-down ant-input-number-handler-down-inner"
role="img"
>
<svg
aria-hidden="true"
data-icon="down"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"
/>
</svg>
</span>
</span>
</div>
<div
class="ant-input-number-input-wrap"
>
<input
aria-valuenow="999"
autocomplete="off"
class="ant-input-number-input"
role="spinbutton"
step="1"
value="999"
/>
</div>
</div>
</div>
</div>
`;
exports[`renders components/input-number/demo/focus.tsx extend context correctly 2`] = `[]`;
exports[`renders components/input-number/demo/formatter.tsx extend context correctly 1`] = `
<div
class="ant-space ant-space-horizontal ant-space-align-center ant-space-gap-row-small ant-space-gap-col-small"

View File

@ -2549,6 +2549,150 @@ exports[`renders components/input-number/demo/filled-debug.tsx correctly 1`] = `
</div>
`;
exports[`renders components/input-number/demo/focus.tsx correctly 1`] = `
<div
class="ant-space ant-space-vertical ant-space-gap-row-small ant-space-gap-col-small"
style="width:100%"
>
<div
class="ant-space-item"
>
<div
class="ant-space ant-space-horizontal ant-space-align-center ant-space-gap-row-small ant-space-gap-col-small"
style="flex-wrap:wrap"
>
<div
class="ant-space-item"
>
<button
class="ant-btn ant-btn-default ant-btn-color-default ant-btn-variant-outlined"
type="button"
>
<span>
Focus at first
</span>
</button>
</div>
<div
class="ant-space-item"
>
<button
class="ant-btn ant-btn-default ant-btn-color-default ant-btn-variant-outlined"
type="button"
>
<span>
Focus at last
</span>
</button>
</div>
<div
class="ant-space-item"
>
<button
class="ant-btn ant-btn-default ant-btn-color-default ant-btn-variant-outlined"
type="button"
>
<span>
Focus to select all
</span>
</button>
</div>
<div
class="ant-space-item"
>
<button
class="ant-btn ant-btn-default ant-btn-color-default ant-btn-variant-outlined"
type="button"
>
<span>
Focus prevent scroll
</span>
</button>
</div>
</div>
</div>
<div
class="ant-space-item"
>
<div
class="ant-input-number ant-input-number-outlined"
style="width:100%"
>
<div
class="ant-input-number-handler-wrap"
>
<span
aria-disabled="false"
aria-label="Increase Value"
class="ant-input-number-handler ant-input-number-handler-up"
role="button"
unselectable="on"
>
<span
aria-label="up"
class="anticon anticon-up ant-input-number-handler-up-inner"
role="img"
>
<svg
aria-hidden="true"
data-icon="up"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M890.5 755.3L537.9 269.2c-12.8-17.6-39-17.6-51.7 0L133.5 755.3A8 8 0 00140 768h75c5.1 0 9.9-2.5 12.9-6.6L512 369.8l284.1 391.6c3 4.1 7.8 6.6 12.9 6.6h75c6.5 0 10.3-7.4 6.5-12.7z"
/>
</svg>
</span>
</span>
<span
aria-disabled="false"
aria-label="Decrease Value"
class="ant-input-number-handler ant-input-number-handler-down"
role="button"
unselectable="on"
>
<span
aria-label="down"
class="anticon anticon-down ant-input-number-handler-down-inner"
role="img"
>
<svg
aria-hidden="true"
data-icon="down"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"
/>
</svg>
</span>
</span>
</div>
<div
class="ant-input-number-input-wrap"
>
<input
aria-valuenow="999"
autocomplete="off"
class="ant-input-number-input"
role="spinbutton"
step="1"
value="999"
/>
</div>
</div>
</div>
</div>
`;
exports[`renders components/input-number/demo/formatter.tsx correctly 1`] = `
<div
class="ant-space ant-space-horizontal ant-space-align-center ant-space-gap-row-small ant-space-gap-col-small"

View File

@ -0,0 +1,7 @@
## zh-CN
聚焦额外配置属性。
## en-US
Focus with additional option.

View File

@ -0,0 +1,53 @@
import React, { useRef } from 'react';
import { Button, InputNumber, Space } from 'antd';
import type { InputNumberRef } from 'rc-input-number';
const App: React.FC = () => {
const inputRef = useRef<InputNumberRef>(null);
return (
<Space direction="vertical" style={{ width: '100%' }}>
<Space wrap>
<Button
onClick={() => {
inputRef.current!.focus({
cursor: 'start',
});
}}
>
Focus at first
</Button>
<Button
onClick={() => {
inputRef.current!.focus({
cursor: 'end',
});
}}
>
Focus at last
</Button>
<Button
onClick={() => {
inputRef.current!.focus({
cursor: 'all',
});
}}
>
Focus to select all
</Button>
<Button
onClick={() => {
inputRef.current!.focus({
preventScroll: true,
});
}}
>
Focus prevent scroll
</Button>
</Space>
<InputNumber style={{ width: '100%' }} defaultValue={999} ref={inputRef} />
</Space>
);
};
export default App;

View File

@ -29,6 +29,7 @@ When a numeric value needs to be provided.
<code src="./demo/out-of-range.tsx">Out of range</code>
<code src="./demo/presuffix.tsx">Prefix / Suffix</code>
<code src="./demo/status.tsx">Status</code>
<code src="./demo/focus.tsx">Focus</code>
<code src="./demo/controls.tsx" debug>Icon</code>
<code src="./demo/render-panel.tsx" debug>_InternalPanelDoNotUseOrYouWillBeFired</code>
<code src="./demo/debug-token.tsx" debug>Override Component Style</code>
@ -70,11 +71,11 @@ Common props ref[Common props](/docs/react/common-props)
## Ref
| Name | Description | Version |
| ------------- | ---------------------- | ------- |
| blur() | Remove focus | |
| focus() | Get focus | |
| nativeElement | The native DOM element | 5.17.3 |
| Name | Description | Type | Version |
| --- | --- | --- | --- |
| blur() | Remove focus | - | |
| focus() | Get focus | (option?: { preventScroll?: boolean, cursor?: 'start' \| 'end' \| 'all' }) | cursor - 5.23.0 |
| nativeElement | The native DOM element | - | 5.17.3 |
## Design Token

View File

@ -10,12 +10,12 @@ import type { InputStatus } from '../_util/statusUtils';
import { getMergedStatus, getStatusClassNames } from '../_util/statusUtils';
import { devUseWarning } from '../_util/warning';
import ConfigProvider, { ConfigContext } from '../config-provider';
import type { Variant } from '../config-provider';
import DisabledContext from '../config-provider/DisabledContext';
import useCSSVarCls from '../config-provider/hooks/useCSSVarCls';
import useSize from '../config-provider/hooks/useSize';
import type { SizeType } from '../config-provider/SizeContext';
import { FormItemInputContext } from '../form/context';
import type { Variant } from '../config-provider';
import useVariant from '../form/hooks/useVariants';
import { useCompactItemContext } from '../space/Compact';
import useStyle from './style';

View File

@ -30,6 +30,7 @@ demo:
<code src="./demo/out-of-range.tsx">超出边界</code>
<code src="./demo/presuffix.tsx">前缀/后缀</code>
<code src="./demo/status.tsx">自定义状态</code>
<code src="./demo/focus.tsx">聚焦</code>
<code src="./demo/controls.tsx" debug>图标按钮</code>
<code src="./demo/render-panel.tsx" debug>_InternalPanelDoNotUseOrYouWillBeFired</code>
<code src="./demo/debug-token.tsx" debug>覆盖组件样式</code>
@ -71,11 +72,11 @@ demo:
## Ref
| 名称 | 描述 | 版本 |
| ------------- | ----------------- | ------ |
| blur() | 移除焦点 | |
| focus() | 获取焦点 | |
| nativeElement | 获取原生 DOM 元素 | 5.17.3 |
| 名称 | 说明 | 参数 | 版本 |
| --- | --- | --- | --- |
| blur() | 移除焦点 | - | |
| focus() | 获取焦点 | (option?: { preventScroll?: boolean, cursor?: 'start' \| 'end' \| 'all' }) | cursor - 5.23.0 |
| nativeElement | 获取原生 DOM 元素 | - | 5.17.3 |
## 主题变量Design Token

View File

@ -2,6 +2,7 @@ import React, { forwardRef, useContext, useEffect, useRef } from 'react';
import classNames from 'classnames';
import type { InputRef, InputProps as RcInputProps } from 'rc-input';
import RcInput from 'rc-input';
import { InputFocusOptions, triggerFocus } from 'rc-input/lib/utils/commonUtils';
import { composeRef } from 'rc-util/lib/ref';
import ContextIsolator from '../_util/ContextIsolator';
@ -10,51 +11,21 @@ import type { InputStatus } from '../_util/statusUtils';
import { getMergedStatus, getStatusClassNames } from '../_util/statusUtils';
import { devUseWarning } from '../_util/warning';
import { ConfigContext } from '../config-provider';
import type { Variant } from '../config-provider';
import DisabledContext from '../config-provider/DisabledContext';
import useCSSVarCls from '../config-provider/hooks/useCSSVarCls';
import useSize from '../config-provider/hooks/useSize';
import type { SizeType } from '../config-provider/SizeContext';
import { FormItemInputContext } from '../form/context';
import type { Variant } from '../config-provider';
import useVariant from '../form/hooks/useVariants';
import { useCompactItemContext } from '../space/Compact';
import useRemovePasswordTimeout from './hooks/useRemovePasswordTimeout';
import useStyle from './style';
import { hasPrefixSuffix } from './utils';
export interface InputFocusOptions extends FocusOptions {
cursor?: 'start' | 'end' | 'all';
}
export type { InputFocusOptions };
export type { InputRef };
export function triggerFocus(
element?: HTMLInputElement | HTMLTextAreaElement,
option?: InputFocusOptions,
) {
if (!element) {
return;
}
element.focus(option);
// Selection content
const { cursor } = option || {};
if (cursor) {
const len = element.value.length;
switch (cursor) {
case 'start':
element.setSelectionRange(0, 0);
break;
case 'end':
element.setSelectionRange(len, len);
break;
default:
element.setSelectionRange(0, len);
}
}
}
export { triggerFocus };
export interface InputProps
extends Omit<

View File

@ -129,7 +129,7 @@
"rc-field-form": "~2.5.0",
"rc-image": "~7.11.0",
"rc-input": "~1.6.3",
"rc-input-number": "~9.2.0",
"rc-input-number": "~9.3.0",
"rc-mentions": "~2.16.1",
"rc-menu": "~9.15.1",
"rc-motion": "^2.9.3",