feat(input-number): add suffix prop & change controls animation (#49674)

* feat: add suffix prop & change controls animation

* feat: add opacity

* refactor: calc

* refactor: change file

* test: change text

---------

Co-authored-by: afc163 <afc163@gmail.com>
This commit is contained in:
ice 2024-07-10 14:43:39 +08:00 committed by GitHub
parent 09e6ed7278
commit 10e46c107e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 230 additions and 23 deletions

View File

@ -3203,7 +3203,7 @@ exports[`renders components/input-number/demo/out-of-range.tsx extend context co
exports[`renders components/input-number/demo/out-of-range.tsx extend context correctly 2`] = `[]`; exports[`renders components/input-number/demo/out-of-range.tsx extend context correctly 2`] = `[]`;
exports[`renders components/input-number/demo/prefix.tsx extend context correctly 1`] = ` exports[`renders components/input-number/demo/presuffix.tsx extend context correctly 1`] = `
Array [ Array [
<div <div
class="ant-input-number-affix-wrapper ant-input-number-outlined" class="ant-input-number-affix-wrapper ant-input-number-outlined"
@ -3490,10 +3490,95 @@ Array [
</div> </div>
</div> </div>
</div>, </div>,
<br />,
<br />,
<div
class="ant-input-number-affix-wrapper ant-input-number-outlined"
style="width: 100%;"
>
<div
class="ant-input-number"
>
<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
autocomplete="off"
class="ant-input-number-input"
role="spinbutton"
step="1"
value=""
/>
</div>
</div>
<span
class="ant-input-number-suffix"
>
RMB
</span>
</div>,
] ]
`; `;
exports[`renders components/input-number/demo/prefix.tsx extend context correctly 2`] = `[]`; exports[`renders components/input-number/demo/presuffix.tsx extend context correctly 2`] = `[]`;
exports[`renders components/input-number/demo/render-panel.tsx extend context correctly 1`] = ` exports[`renders components/input-number/demo/render-panel.tsx extend context correctly 1`] = `
<div <div

View File

@ -2911,7 +2911,7 @@ exports[`renders components/input-number/demo/out-of-range.tsx correctly 1`] = `
</div> </div>
`; `;
exports[`renders components/input-number/demo/prefix.tsx correctly 1`] = ` exports[`renders components/input-number/demo/presuffix.tsx correctly 1`] = `
Array [ Array [
<div <div
class="ant-input-number-affix-wrapper ant-input-number-outlined" class="ant-input-number-affix-wrapper ant-input-number-outlined"
@ -3198,6 +3198,91 @@ Array [
</div> </div>
</div> </div>
</div>, </div>,
<br />,
<br />,
<div
class="ant-input-number-affix-wrapper ant-input-number-outlined"
style="width:100%"
>
<div
class="ant-input-number"
>
<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
autocomplete="off"
class="ant-input-number-input"
role="spinbutton"
step="1"
value=""
/>
</div>
</div>
<span
class="ant-input-number-suffix"
>
RMB
</span>
</div>,
] ]
`; `;

View File

@ -0,0 +1,19 @@
import React from 'react';
import InputNumber from '..';
import { fireEvent, render } from '../../../tests/utils';
describe('suffix', () => {
it('should support suffix prop', () => {
const { container } = render(<InputNumber suffix={<i>hello</i>} />);
expect(container.querySelector('.ant-input-number-suffix')).toBeInTheDocument();
});
it('should trigger focus when suffix is clicked', () => {
const { container } = render(<InputNumber suffix={<i>antd</i>} />);
const mockFocus = jest.spyOn(container.querySelector('input')!, 'focus');
fireEvent.click(container.querySelector('i')!);
expect(mockFocus).toHaveBeenCalled();
});
});

View File

@ -1,7 +0,0 @@
## zh-CN
在输入框上添加前缀图标。
## en-US
Add a prefix inside input.

View File

@ -0,0 +1,7 @@
## zh-CN
在输入框上添加前缀或后缀图标。
## en-US
Add a prefix or suffix inside input.

View File

@ -11,6 +11,9 @@ const App: React.FC = () => (
<br /> <br />
<br /> <br />
<InputNumber prefix="¥" disabled style={{ width: '100%' }} /> <InputNumber prefix="¥" disabled style={{ width: '100%' }} />
<br />
<br />
<InputNumber suffix="RMB" style={{ width: '100%' }} />
</> </>
); );

View File

@ -27,7 +27,7 @@ When a numeric value needs to be provided.
<code src="./demo/variant.tsx" version="5.13.0">Variants</code> <code src="./demo/variant.tsx" version="5.13.0">Variants</code>
<code src="./demo/filled-debug.tsx" debug>Filled Debug</code> <code src="./demo/filled-debug.tsx" debug>Filled Debug</code>
<code src="./demo/out-of-range.tsx">Out of range</code> <code src="./demo/out-of-range.tsx">Out of range</code>
<code src="./demo/prefix.tsx">Prefix</code> <code src="./demo/presuffix.tsx">Prefix / Suffix</code>
<code src="./demo/status.tsx">Status</code> <code src="./demo/status.tsx">Status</code>
<code src="./demo/controls.tsx" debug>Icon</code> <code src="./demo/controls.tsx" debug>Icon</code>
<code src="./demo/render-panel.tsx" debug>_InternalPanelDoNotUseOrYouWillBeFired</code> <code src="./demo/render-panel.tsx" debug>_InternalPanelDoNotUseOrYouWillBeFired</code>
@ -58,6 +58,7 @@ Common props ref[Common props](/docs/react/common-props)
| readOnly | If readonly the input | boolean | false | - | | readOnly | If readonly the input | boolean | false | - |
| status | Set validation status | 'error' \| 'warning' | - | 4.19.0 | | status | Set validation status | 'error' \| 'warning' | - | 4.19.0 |
| prefix | The prefix icon for the Input | ReactNode | - | 4.17.0 | | prefix | The prefix icon for the Input | ReactNode | - | 4.17.0 |
| suffix | The suffix icon for the Input | ReactNode | - | 5.20.0 |
| size | The height of input box | `large` \| `middle` \| `small` | - | - | | size | The height of input box | `large` \| `middle` \| `small` | - | - |
| step | The number to which the current value is increased or decreased. It can be an integer or decimal | number \| string | 1 | - | | step | The number to which the current value is increased or decreased. It can be an integer or decimal | number \| string | 1 | - |
| stringMode | Set value as string to support high precision decimals. Will return string value by `onChange` | boolean | false | 4.13.0 | | stringMode | Set value as string to support high precision decimals. Will return string value by `onChange` | boolean | false | 4.13.0 |

View File

@ -27,6 +27,7 @@ export interface InputNumberProps<T extends ValueType = ValueType>
addonBefore?: React.ReactNode; addonBefore?: React.ReactNode;
addonAfter?: React.ReactNode; addonAfter?: React.ReactNode;
prefix?: React.ReactNode; prefix?: React.ReactNode;
suffix?: React.ReactNode;
size?: SizeType; size?: SizeType;
disabled?: boolean; disabled?: boolean;
/** @deprecated Use `variant` instead. */ /** @deprecated Use `variant` instead. */
@ -66,6 +67,7 @@ const InputNumber = React.forwardRef<HTMLInputElement, InputNumberProps>((props,
addonBefore, addonBefore,
addonAfter, addonAfter,
prefix, prefix,
suffix,
bordered, bordered,
readOnly, readOnly,
status: customStatus, status: customStatus,
@ -141,7 +143,7 @@ const InputNumber = React.forwardRef<HTMLInputElement, InputNumberProps>((props,
readOnly={readOnly} readOnly={readOnly}
controls={controlsTemp} controls={controlsTemp}
prefix={prefix} prefix={prefix}
suffix={suffixNode} suffix={suffixNode || suffix}
addonBefore={ addonBefore={
addonBefore && ( addonBefore && (
<ContextIsolator form space> <ContextIsolator form space>

View File

@ -28,7 +28,7 @@ demo:
<code src="./demo/variant.tsx" version="5.13.0">形态变体</code> <code src="./demo/variant.tsx" version="5.13.0">形态变体</code>
<code src="./demo/filled-debug.tsx" debug>Filled Debug</code> <code src="./demo/filled-debug.tsx" debug>Filled Debug</code>
<code src="./demo/out-of-range.tsx">超出边界</code> <code src="./demo/out-of-range.tsx">超出边界</code>
<code src="./demo/prefix.tsx">前缀</code> <code src="./demo/presuffix.tsx">缀/后</code>
<code src="./demo/status.tsx">自定义状态</code> <code src="./demo/status.tsx">自定义状态</code>
<code src="./demo/controls.tsx" debug>图标按钮</code> <code src="./demo/controls.tsx" debug>图标按钮</code>
<code src="./demo/render-panel.tsx" debug>_InternalPanelDoNotUseOrYouWillBeFired</code> <code src="./demo/render-panel.tsx" debug>_InternalPanelDoNotUseOrYouWillBeFired</code>
@ -59,6 +59,7 @@ demo:
| readOnly | 只读 | boolean | false | - | | readOnly | 只读 | boolean | false | - |
| status | 设置校验状态 | 'error' \| 'warning' | - | 4.19.0 | | status | 设置校验状态 | 'error' \| 'warning' | - | 4.19.0 |
| prefix | 带有前缀图标的 input | ReactNode | - | 4.17.0 | | prefix | 带有前缀图标的 input | ReactNode | - | 4.17.0 |
| suffix | 带有后缀图标的 input | ReactNode | - | 5.20.0 |
| size | 输入框大小 | `large` \| `middle` \| `small` | - | - | | size | 输入框大小 | `large` \| `middle` \| `small` | - | - |
| step | 每次改变步数,可以为小数 | number \| string | 1 | - | | step | 每次改变步数,可以为小数 | number \| string | 1 | - |
| stringMode | 字符值模式,开启后支持高精度小数。同时 `onChange` 将返回 string 类型 | boolean | false | 4.13.0 | | stringMode | 字符值模式,开启后支持高精度小数。同时 `onChange` 将返回 string 类型 | boolean | false | 4.13.0 |

View File

@ -61,6 +61,7 @@ const genInputNumberStyles: GenerateStyle<InputNumberToken> = (token: InputNumbe
colorTextDescription, colorTextDescription,
motionDurationMid, motionDurationMid,
handleHoverColor, handleHoverColor,
handleOpacity,
paddingInline, paddingInline,
paddingBlock, paddingBlock,
handleBg, handleBg,
@ -69,7 +70,6 @@ const genInputNumberStyles: GenerateStyle<InputNumberToken> = (token: InputNumbe
borderRadiusSM, borderRadiusSM,
borderRadiusLG, borderRadiusLG,
controlWidth, controlWidth,
handleOpacity,
handleBorderColor, handleBorderColor,
filledHandleBg, filledHandleBg,
lineHeightLG, lineHeightLG,
@ -236,31 +236,32 @@ const genInputNumberStyles: GenerateStyle<InputNumberToken> = (token: InputNumbe
}, },
}, },
}, },
[`&:hover ${componentCls}-handler-wrap, &-focused ${componentCls}-handler-wrap`]: {
width: token.handleWidth,
opacity: 1,
},
}, },
}, },
// Handler // Handler
{ {
[componentCls]: { [componentCls]: {
[`&:hover ${componentCls}-handler-wrap, &-focused ${componentCls}-handler-wrap`]: {
opacity: 1,
},
[`${componentCls}-handler-wrap`]: { [`${componentCls}-handler-wrap`]: {
position: 'absolute', position: 'absolute',
insetBlockStart: 0, insetBlockStart: 0,
insetInlineEnd: 0, insetInlineEnd: 0,
width: token.handleWidth, width: 0,
opacity: handleOpacity,
height: '100%', height: '100%',
borderStartStartRadius: 0, borderStartStartRadius: 0,
borderStartEndRadius: borderRadius, borderStartEndRadius: borderRadius,
borderEndEndRadius: borderRadius, borderEndEndRadius: borderRadius,
borderEndStartRadius: 0, borderEndStartRadius: 0,
opacity: handleOpacity,
display: 'flex', display: 'flex',
flexDirection: 'column', flexDirection: 'column',
alignItems: 'stretch', alignItems: 'stretch',
transition: `opacity ${motionDurationMid} linear ${motionDurationMid}`, transition: `all ${motionDurationMid}`,
overflow: 'hidden',
// Fix input number inside Menu makes icon too large // Fix input number inside Menu makes icon too large
// We arise the selector priority by nest selector here // We arise the selector priority by nest selector here
@ -370,6 +371,7 @@ const genAffixWrapperStyles: GenerateStyle<InputNumberToken> = (token: InputNumb
paddingInlineSM, paddingInlineSM,
paddingBlockLG, paddingBlockLG,
paddingBlockSM, paddingBlockSM,
motionDurationMid,
} = token; } = token;
return { return {
@ -382,6 +384,7 @@ const genAffixWrapperStyles: GenerateStyle<InputNumberToken> = (token: InputNumb
// or number handler will cover form status // or number handler will cover form status
position: 'relative', position: 'relative',
display: 'inline-flex', display: 'inline-flex',
alignItems: 'center',
width: controlWidth, width: controlWidth,
padding: 0, padding: 0,
paddingInlineStart: paddingInline, paddingInlineStart: paddingInline,
@ -438,6 +441,7 @@ const genAffixWrapperStyles: GenerateStyle<InputNumberToken> = (token: InputNumb
}, },
[componentCls]: { [componentCls]: {
position: 'static',
color: 'inherit', color: 'inherit',
'&-prefix, &-suffix': { '&-prefix, &-suffix': {
@ -452,15 +456,22 @@ const genAffixWrapperStyles: GenerateStyle<InputNumberToken> = (token: InputNumb
}, },
'&-suffix': { '&-suffix': {
position: 'absolute',
insetBlockStart: 0, insetBlockStart: 0,
insetInlineEnd: 0, insetInlineEnd: 0,
zIndex: 1,
height: '100%', height: '100%',
marginInlineEnd: paddingInline, marginInlineEnd: paddingInline,
marginInlineStart: inputAffixPadding, marginInlineStart: inputAffixPadding,
transition: `margin ${motionDurationMid}`,
}, },
}, },
[`&:hover ${componentCls}-handler-wrap, &-focused ${componentCls}-handler-wrap`]: {
width: token.handleWidth,
opacity: 1,
},
[`&:hover ${componentCls}-suffix`]: {
marginInlineEnd: token.calc(token.handleWidth).add(paddingInline).equal(),
},
}, },
}; };
}; };