mirror of
https://gitee.com/ant-design/ant-design.git
synced 2024-12-02 03:59:01 +08:00
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:
parent
f73825ba74
commit
6380fc7a95
@ -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"
|
||||
|
@ -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"
|
||||
|
7
components/input-number/demo/focus.md
Normal file
7
components/input-number/demo/focus.md
Normal file
@ -0,0 +1,7 @@
|
||||
## zh-CN
|
||||
|
||||
聚焦额外配置属性。
|
||||
|
||||
## en-US
|
||||
|
||||
Focus with additional option.
|
53
components/input-number/demo/focus.tsx
Normal file
53
components/input-number/demo/focus.tsx
Normal 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;
|
@ -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
|
||||
|
||||
|
@ -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';
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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<
|
||||
|
@ -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",
|
||||
|
Loading…
Reference in New Issue
Block a user