import { useCallback , useState , useMemo } from 'react' ; import { useControllableProp } from '../../hooks' ; import { StringOrNumber } from '../../types' ; import { toNumber , toPrecision , isNull } from '../../utils' ; import { NumberStore } from '../../stores' ; export interface UseCounterProps { /**
     * 非受控初值
     */ defaultValue ? : StringOrNumber ; /**
     * 受控值
     */ value ? : StringOrNumber ; /**
     * 值变化时的回调
     */ onChange ? : ( val : number ) => void ; /**
     * 精度（小数点保留位数）
     */ precision ? : number ; /**
     * 步长
     */ step ? : number ; /**
     * 最小值
     */ min ? : number ; /**
     * 最大值
     */ max ? : number ; /**
     * 格式化选项，参考 Intl.NumberFormat
     */ formatOptions ? : Intl . NumberFormatOptions ; } export function useCounter ( props ) { const { onChange , defaultValue , value : valueProp , precision , step : stepProp = 1 , min = Number . MIN_SAFE_INTEGER , max = Number . MAX_SAFE_INTEGER , formatOptions , } = props ; const [ valueState , setValue ] = useState < number > ( ( ) => { if ( isNull ( defaultValue ) ) { return 0 ; } return toNumber ( defaultValue ) ; } ) ; const [ isControlled , value ] = useControllableProp ( valueProp , valueState ) ; const numberStore = useMemo ( ( ) => { return new NumberStore ( 'zh-CN' , formatOptions ) ; } , [ formatOptions ] ) ; const correct = useCallback ( ( value ) => { const nextValue = correctValue ( value , min , max ) ; return formatValue ( nextValue , precision ) ; } , [ min , max , precision ] ) ; const update = useCallback ( ( next ) => { if ( ! isControlled ) { setValue ( next ) ; } onChange ?. ( next ) ; } , [ onChange , isControlled ] ) ; const increment = useCallback ( ( step = stepProp ) => { let next = toNumber ( value ) + step ; next = correct ( next ) ; update ( next ) ; return next ; } , [ update , value , stepProp , correct ] ) ; const decrement = useCallback ( ( step = stepProp ) => { let next = toNumber ( value ) - step ; next = correct ( next ) ; update ( next ) ; return next ; } , [ update , value , stepProp , correct ] ) ; return { isAtMax : value === max , isAtMin : value === min , value , update , increment , decrement , correct , numberStore , } ; } param value param precision function formatValue ( value , precision ) { return toPrecision ( toNumber ( value ) , precision ) ; } param value param min param max function correctValue ( value , min , max ) { return Math . min ( Math . max ( value , min ) , max ) ; }