diff --git a/src/packages/inputnumber/demo.scss b/src/packages/inputnumber/demo.scss new file mode 100644 index 0000000000..870bd6de47 --- /dev/null +++ b/src/packages/inputnumber/demo.scss @@ -0,0 +1,6 @@ +.format-width { + input, + .nut-number-input { + --nutui-inputnumber-input-width: 60px; + } +} diff --git a/src/packages/inputnumber/demo.tsx b/src/packages/inputnumber/demo.tsx index 5cfac27a54..4f21636843 100644 --- a/src/packages/inputnumber/demo.tsx +++ b/src/packages/inputnumber/demo.tsx @@ -4,6 +4,7 @@ import { InputNumber } from './inputnumber' import ConfigProvider from '@/packages/configprovider' import Cell from '@/packages/cell' import Toast from '@/packages/toast' +import './demo.scss' interface ValState { val1: number | string @@ -29,6 +30,7 @@ interface T { '3a42134b': string '65bafb1d': string '7e2394ae': string + '7e2394be': string } const customTheme = { @@ -63,6 +65,7 @@ const InputNumberDemo = () => { '3a42134b': '支持小数点', '65bafb1d': '支持异步修改', '7e2394ae': '自定义按钮大小', + '7e2394be': '支持formatter', }, 'zh-TW': { '6333c786': '超出限制事件觸發', @@ -77,6 +80,7 @@ const InputNumberDemo = () => { '3a42134b': '支持小數點', '65bafb1d': '支持異步修改', '7e2394ae': '自定義按鈕大小', + '7e2394be': '支持formatter', }, 'en-US': { '6333c786': 'Exceeded limit event triggered', @@ -91,6 +95,7 @@ const InputNumberDemo = () => { '3a42134b': 'support decimal point', '65bafb1d': 'Support for asynchronous modification', '7e2394ae': 'custom button size', + '7e2394be': 'support formatter', }, }) @@ -181,6 +186,28 @@ const InputNumberDemo = () => { inputWidth="50" /> + +

支持formatter

+ + + `$ ${value}`.replace(/\B(?=(\d{3})+(?!\d))/g, ',') + } + /> + + + `${value}%`} + /> + ) diff --git a/src/packages/inputnumber/doc.en-US.md b/src/packages/inputnumber/doc.en-US.md index 6a4af959ec..4d4c056ce4 100644 --- a/src/packages/inputnumber/doc.en-US.md +++ b/src/packages/inputnumber/doc.en-US.md @@ -204,6 +204,39 @@ export default App; ``` ::: +### support formatter + +:::demo +```tsx +import React from "react"; +import { InputNumber } from '@nutui/nutui-react'; + +const App = () => { + return ( + <> + + `$ ${value}`.replace(/\B(?=(\d{3})+(?!\d))/g, ',') + } + /> + `${value}%`} + /> + + ) +} +export default App; +``` +::: + ## API ### Props @@ -220,6 +253,7 @@ export default App; | disabled | Disable all features | boolean | `false` | | readonly | Read only status disables input box operation behavior | boolean | `false` | | isAsync | Support for asynchronous modification | boolean | `false` | +| formatter`v1.4.14` | Specifies the format of the value displayed in the input box | function(value: number | string): string | - | ### Events diff --git a/src/packages/inputnumber/doc.md b/src/packages/inputnumber/doc.md index 6f25f5c051..4f8f456eba 100644 --- a/src/packages/inputnumber/doc.md +++ b/src/packages/inputnumber/doc.md @@ -249,6 +249,39 @@ export default App; ``` ::: +### 支持formatter + +:::demo +```tsx +import React from "react"; +import { InputNumber } from '@nutui/nutui-react'; + +const App = () => { + return ( + <> + + `$ ${value}`.replace(/\B(?=(\d{3})+(?!\d))/g, ',') + } + /> + `${value}%`} + /> + + ) +} +export default App; +``` +::: + ## API ### Props @@ -265,6 +298,7 @@ export default App; | disabled | 禁用所有功能 | boolean | `false` | | readonly | 只读状态禁用输入框操作行为 | boolean | `false` | | isAsync | 支持异步修改 | boolean | `false` | +| formatter`v1.4.14` | 指定输入框展示值的格式 | function(value: number | string): string | - | ### Events diff --git a/src/packages/inputnumber/inputnumber.tsx b/src/packages/inputnumber/inputnumber.tsx index 6ac1f9f4f8..b034247495 100644 --- a/src/packages/inputnumber/inputnumber.tsx +++ b/src/packages/inputnumber/inputnumber.tsx @@ -1,6 +1,7 @@ import React, { useState, useEffect, + useRef, FunctionComponent, ChangeEvent, FocusEvent, @@ -24,6 +25,7 @@ export interface InputNumberProps extends BasicComponent { isAsync: boolean className: string style: React.CSSProperties + formatter?: (displayValue: string | number) => string add: (e: MouseEvent) => void reduce: (e: MouseEvent) => void overlimit: (e: MouseEvent) => void @@ -77,6 +79,7 @@ export const InputNumber: FunctionComponent< isAsync, className, style, + formatter, add, reduce, change, @@ -97,9 +100,15 @@ export const InputNumber: FunctionComponent< ...props, } const [inputValue, setInputValue] = useState(modelValue) + const inputRef = useRef('') useEffect(() => { - setInputValue(modelValue) - }, [modelValue]) + if (formatter) { + inputRef.current = formatter(modelValue) + setInputValue(formatter(modelValue)) + } else { + setInputValue(modelValue) + } + }, [modelValue, formatter]) const b = bem('inputnumber') const classes = classNames( @@ -114,6 +123,10 @@ export const InputNumber: FunctionComponent< ...style, } const addAllow = (value = inputValue) => { + if (formatter) { + const numValue = String(value).replace(/[^0-9|\.]/gi, '') + return Number(numValue) < Number(max) && !disabled + } if (value || typeof value === 'number') { return value < Number(max) && !disabled } @@ -121,6 +134,10 @@ export const InputNumber: FunctionComponent< } const reduceAllow = (value = inputValue) => { + if (formatter) { + const numValue = String(value).replace(/[^0-9|\.]/gi, '') + return Number(numValue) > Number(min) && !disabled + } if (value || typeof value === 'number') { return value > Number(min) && !disabled } @@ -148,11 +165,17 @@ export const InputNumber: FunctionComponent< change && change(outputValue, e) if (!isAsync) { if (Number(outputValue) < Number(min)) { - setInputValue(Number(min)) + formatter + ? setInputValue(formatter(Number(min))) + : setInputValue(Number(min)) } else if (Number(outputValue) > Number(max)) { - setInputValue(Number(max)) + formatter + ? setInputValue(formatter(Number(max))) + : setInputValue(Number(max)) } else { - setInputValue(outputValue) + formatter + ? setInputValue(formatter(outputValue)) + : setInputValue(outputValue) } } } @@ -161,8 +184,15 @@ export const InputNumber: FunctionComponent< onReduce && onReduce(e) reduce && reduce(e) if (reduceAllow()) { - const outputValue = Number(inputValue) - Number(step) - emitChange(outputValue, e) + if (formatter) { + const numValue = String(inputValue).replace(/[^0-9|\.]/gi, '') + const outputValue = Number(numValue) - Number(step) + inputRef.current = formatter(outputValue) + emitChange(outputValue, e) + } else { + const outputValue = Number(inputValue) - Number(step) + emitChange(outputValue, e) + } } else { onOverlimit && onOverlimit(e) overlimit && overlimit(e) @@ -173,8 +203,15 @@ export const InputNumber: FunctionComponent< onAdd && onAdd(e) add && add(e) if (addAllow()) { - const outputValue = Number(inputValue) + Number(step) - emitChange(outputValue, e) + if (formatter) { + const numValue = String(inputValue).replace(/[^0-9|\.]/gi, '') + const outputValue = Number(numValue) + Number(step) + inputRef.current = formatter(outputValue) + emitChange(outputValue, e) + } else { + const outputValue = Number(inputValue) + Number(step) + emitChange(outputValue, e) + } } else { onOverlimit && onOverlimit(e) overlimit && overlimit(e) @@ -194,6 +231,48 @@ export const InputNumber: FunctionComponent< } } + const changeFormatValue = (e: ChangeEvent) => { + const input = e.target.value + + const numReg = new RegExp('^[0-9]*$') + const numValue = input.replace(/[^0-9|\.]/gi, '') + + if (formatter) { + if (!numReg.test(input[0]) && numValue) { + setInputValue(formatter(numValue)) + } else if (!numReg.test(input[0]) && !numValue) { + setInputValue(input) + } else if (numReg.test(input[0])) { + console.log('inputRef.current', inputRef.current) + console.log('formatter(numValue)', formatter(numValue)) + + // 针对于100%这种尾字符例子,直接删除会进行匹配 + if (formatter(numValue) === inputRef.current) { + setInputValue(numValue) + } else { + setInputValue(formatter(numValue)) + inputRef.current = formatter(numValue) + } + } + } + } + + const burFormatValue = (e: ChangeEvent) => { + const input = e.target.value + + const numReg = new RegExp('^[0-9]*$') + const numValue = input.replace(/[^0-9|\.]/gi, '') + if (formatter) { + if (formatter(numValue) === input) { + emitChange(numValue, e) + return + } + if (!numReg.test(input) || !input) { + setInputValue(formatter('')) + } + } + } + const focusValue = (e: FocusEvent) => { if (disabled) return if (readonly) return @@ -227,18 +306,35 @@ export const InputNumber: FunctionComponent< onClick={reduceNumber} /> - + <> + {formatter ? ( + + ) : ( + + )} +