Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
"@babel/runtime": "^7.10.1",
"@rc-component/mini-decimal": "^1.0.1",
"classnames": "^2.2.5",
"rc-input": "~1.1.0",
"rc-util": "^5.28.0"
},
"devDependencies": {
Expand Down
100 changes: 90 additions & 10 deletions src/InputNumber.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,16 @@ import getMiniDecimal, {
validateNumber,
ValueType,
} from '@rc-component/mini-decimal';
import classNames from 'classnames';
import clsx from 'classnames';
import { BaseInput } from 'rc-input';
import { useLayoutUpdateEffect } from 'rc-util/lib/hooks/useLayoutEffect';
import { composeRef } from 'rc-util/lib/ref';
import * as React from 'react';
import useCursor from './hooks/useCursor';
import StepHandler from './StepHandler';
import { getDecupleSteps } from './utils/numberUtil';

import { InputFocusOptions, triggerFocus } from 'rc-input/lib/utils/commonUtils';
import useFrame from './hooks/useFrame';

/**
Expand Down Expand Up @@ -43,7 +45,7 @@ const getDecimalIfValidate = (value: ValueType) => {
export interface InputNumberProps<T extends ValueType = ValueType>
extends Omit<
React.InputHTMLAttributes<HTMLInputElement>,
'value' | 'defaultValue' | 'onInput' | 'onChange'
'value' | 'defaultValue' | 'onInput' | 'onChange' | 'prefix' | 'suffix'
> {
/** value will show as string */
stringMode?: boolean;
Expand All @@ -59,6 +61,21 @@ export interface InputNumberProps<T extends ValueType = ValueType>
step?: ValueType;
tabIndex?: number;
controls?: boolean;
prefix?: React.ReactNode;
suffix?: React.ReactNode;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
suffix?: React.ReactNode;

暂时不支持 suffix

Copy link
Contributor Author

@MuxinFeng MuxinFeng Jun 6, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这里是准备放 feedbackIcon 的,antd 的 InputNumber 并没有暴露 suffix 。

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

很细,我都忘记了

addonBefore?: React.ReactNode;
addonAfter?: React.ReactNode;
classes?: {
affixWrapper?: string;
group?: string;
wrapper?: string;
};
classNames?: {
affixWrapper?: string;
group?: string;
wrapper?: string;
input?: string;
};

// Customize handler node
upHandler?: React.ReactNode;
Expand Down Expand Up @@ -86,7 +103,7 @@ export interface InputNumberProps<T extends ValueType = ValueType>
// size?: ISize;
}

const InputNumber = React.forwardRef(
const InternalInputNumber = React.forwardRef(
(props: InputNumberProps, ref: React.Ref<HTMLInputElement>) => {
const {
prefixCls = 'rc-input-number',
Expand All @@ -104,6 +121,7 @@ const InputNumber = React.forwardRef(
keyboard,
controls = true,

classNames,
stringMode,

parser,
Expand Down Expand Up @@ -527,13 +545,18 @@ const InputNumber = React.forwardRef(
// ============================ Render ============================
return (
<div
className={classNames(prefixCls, className, {
[`${prefixCls}-focused`]: focus,
[`${prefixCls}-disabled`]: disabled,
[`${prefixCls}-readonly`]: readOnly,
[`${prefixCls}-not-a-number`]: decimalValue.isNaN(),
[`${prefixCls}-out-of-range`]: !decimalValue.isInvalidate() && !isInRange(decimalValue),
})}
className={clsx(
prefixCls,
className,
{
[`${prefixCls}-focused`]: focus,
[`${prefixCls}-disabled`]: disabled,
[`${prefixCls}-readonly`]: readOnly,
[`${prefixCls}-not-a-number`]: decimalValue.isNaN(),
[`${prefixCls}-out-of-range`]: !decimalValue.isInvalidate() && !isInRange(decimalValue),
},
classNames?.input,
)}
style={style}
onFocus={() => {
setFocus(true);
Expand Down Expand Up @@ -575,6 +598,63 @@ const InputNumber = React.forwardRef(
</div>
);
},
);

const InputNumber = React.forwardRef(
(props: InputNumberProps, ref: React.Ref<HTMLInputElement>) => {
const {
disabled,
style,
prefixCls,
value,
prefix,
addonBefore,
addonAfter,
classes,
className,
classNames,
...rest
} = props;

const inputFocusRef = React.useRef<HTMLInputElement>(null);

const focus = (option?: InputFocusOptions) => {
if (inputFocusRef.current) {
triggerFocus(inputFocusRef.current, option);
}
};

return (
<BaseInput
inputElement={
<InternalInputNumber
prefixCls={prefixCls}
disabled={disabled}
classNames={classNames}
ref={composeRef(inputFocusRef, ref)}
{...rest}
/>
}
className={className}
triggerFocus={focus}
prefixCls={prefixCls}
value={value}
disabled={disabled}
style={style}
prefix={prefix}
addonAfter={addonAfter}
addonBefore={addonBefore}
classes={classes}
classNames={classNames}
components={{
affixWrapper: 'div',
groupWrapper: 'div',
wrapper: 'div',
groupAddon: 'div',
}}
/>
);
},
) as (<T extends ValueType = ValueType>(
props: React.PropsWithChildren<InputNumberProps<T>> & {
ref?: React.Ref<HTMLInputElement>;
Expand Down
185 changes: 185 additions & 0 deletions tests/__snapshots__/baseInput.test.tsx.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`baseInput addon should render properly 1`] = `
<div>
<div>
<div
class="rc-input-group-wrapper"
>
<div
class="rc-input-wrapper rc-input-group"
>
<div
class="rc-input-group-addon"
>
<span>
Addon Before
</span>
</div>
<div
class="rc-input"
>
<div
class="rc-input-handler-wrap"
>
<span
aria-disabled="false"
aria-label="Increase Value"
class="rc-input-handler rc-input-handler-up"
role="button"
unselectable="on"
>
<span
class="rc-input-handler-up-inner"
unselectable="on"
/>
</span>
<span
aria-disabled="false"
aria-label="Decrease Value"
class="rc-input-handler rc-input-handler-down"
role="button"
unselectable="on"
>
<span
class="rc-input-handler-down-inner"
unselectable="on"
/>
</span>
</div>
<div
class="rc-input-input-wrap"
>
<input
autocomplete="off"
class="rc-input-input"
role="spinbutton"
step="1"
value=""
/>
</div>
</div>
</div>
</div>
<br />
<br />
<div
class="rc-input-group-wrapper"
>
<div
class="rc-input-wrapper rc-input-group"
>
<div
class="rc-input"
>
<div
class="rc-input-handler-wrap"
>
<span
aria-disabled="false"
aria-label="Increase Value"
class="rc-input-handler rc-input-handler-up"
role="button"
unselectable="on"
>
<span
class="rc-input-handler-up-inner"
unselectable="on"
/>
</span>
<span
aria-disabled="false"
aria-label="Decrease Value"
class="rc-input-handler rc-input-handler-down"
role="button"
unselectable="on"
>
<span
class="rc-input-handler-down-inner"
unselectable="on"
/>
</span>
</div>
<div
class="rc-input-input-wrap"
>
<input
autocomplete="off"
class="rc-input-input"
role="spinbutton"
step="1"
value=""
/>
</div>
</div>
<div
class="rc-input-group-addon"
>
<span>
Addon After
</span>
</div>
</div>
</div>
</div>
</div>
`;

exports[`baseInput prefix should render properly 1`] = `
<div>
<div
class="rc-input-affix-wrapper"
>
<span
class="rc-input-prefix"
>
<span>
Prefix
</span>
</span>
<div
class="rc-input"
>
<div
class="rc-input-handler-wrap"
>
<span
aria-disabled="false"
aria-label="Increase Value"
class="rc-input-handler rc-input-handler-up"
role="button"
unselectable="on"
>
<span
class="rc-input-handler-up-inner"
unselectable="on"
/>
</span>
<span
aria-disabled="false"
aria-label="Decrease Value"
class="rc-input-handler rc-input-handler-down"
role="button"
unselectable="on"
>
<span
class="rc-input-handler-down-inner"
unselectable="on"
/>
</span>
</div>
<div
class="rc-input-input-wrap"
>
<input
autocomplete="off"
class="rc-input-input"
role="spinbutton"
step="1"
value=""
/>
</div>
</div>
</div>
</div>
`;
26 changes: 26 additions & 0 deletions tests/baseInput.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { render } from '@testing-library/react';
import InputNumber from '../src';

describe('baseInput', () => {
it('prefix should render properly', () => {
const prefix = <span>Prefix</span>;

const { container } = render(<InputNumber prefixCls="rc-input" prefix={prefix} />);
expect(container).toMatchSnapshot();
});

it('addon should render properly', () => {
const addonBefore = <span>Addon Before</span>;
const addonAfter = <span>Addon After</span>;

const { container } = render(
<div>
<InputNumber prefixCls="rc-input" addonBefore={addonBefore} />
<br />
<br />
<InputNumber prefixCls="rc-input" addonAfter={addonAfter} />
</div>,
);
expect(container).toMatchSnapshot();
});
});