2
2
import type { PrimitiveProps } from ' @/Primitive'
3
3
import { useVModel } from ' @vueuse/core'
4
4
import { clamp , createContext , snapValueToStep , useFormControl , useLocale } from ' @/shared'
5
- import { type HTMLAttributes , type Ref , computed , ref , toRefs } from ' vue'
5
+ import { type HTMLAttributes , type Ref , computed , ref , toRefs , watch } from ' vue'
6
6
import type { FormFieldProps } from ' @/shared/types'
7
7
8
8
export interface NumberFieldRootProps extends PrimitiveProps , FormFieldProps {
@@ -40,7 +40,7 @@ interface NumberFieldRootContext {
40
40
inputMode: Ref <HTMLAttributes [' inputmode' ]>
41
41
textValue: Ref <string >
42
42
validate: (val : string ) => boolean
43
- applyInputValue: (val : string ) => void
43
+ applyInputValue: (val : string , format ? : boolean ) => void
44
44
disabled: Ref <boolean >
45
45
max: Ref <number | undefined >
46
46
min: Ref <number | undefined >
@@ -68,7 +68,7 @@ const props = withDefaults(defineProps<NumberFieldRootProps>(), {
68
68
stepSnapping: true ,
69
69
})
70
70
const emits = defineEmits <NumberFieldRootEmits >()
71
- const { disabled, min, max, step, stepSnapping, formatOptions, id, locale : propLocale } = toRefs (props )
71
+ const { disabled, defaultValue, min, max, step, stepSnapping, formatOptions, id, locale : propLocale } = toRefs (props )
72
72
73
73
const modelValue = useVModel (props , ' modelValue' , emits , {
74
74
defaultValue: props .defaultValue ,
@@ -80,6 +80,8 @@ const { primitiveElement, currentElement } = usePrimitiveElement()
80
80
const locale = useLocale (propLocale )
81
81
const isFormControl = useFormControl (currentElement )
82
82
const inputEl = ref <HTMLInputElement >()
83
+ const textValue = ref <string >(' ' )
84
+ let skipFormatNextUpdate: boolean = false
83
85
84
86
const isDecreaseDisabled = computed (() => (
85
87
clampInputValue (modelValue .value ) === min .value
@@ -136,7 +138,18 @@ const inputMode = computed<HTMLAttributes['inputmode']>(() => {
136
138
// Replace negative textValue formatted using currencySign: 'accounting'
137
139
// with a textValue that can be announced using a minus sign.
138
140
const textValueFormatter = useNumberFormatter (locale , formatOptions )
139
- const textValue = computed (() => isNaN (modelValue .value ) ? ' ' : textValueFormatter .format (modelValue .value ))
141
+
142
+ updateTextValue (modelValue .value )
143
+
144
+ watch ([modelValue , defaultValue , formatOptions , propLocale ], () => {
145
+ if (! skipFormatNextUpdate )
146
+ updateTextValue (modelValue .value )
147
+ skipFormatNextUpdate = false
148
+ })
149
+
150
+ function updateTextValue(value : number | null ) {
151
+ textValue .value = (value === null || isNaN (value )) ? ' ' : textValueFormatter .format (value )
152
+ }
140
153
141
154
function validate(val : string ) {
142
155
return numberParser .isValidPartialNumber (val , min .value , max .value )
@@ -159,10 +172,16 @@ function clampInputValue(val: number) {
159
172
return clampedValue
160
173
}
161
174
162
- function applyInputValue(val : string ) {
175
+ function applyInputValue(val : string , format : boolean = true ) {
163
176
const parsedValue = numberParser .parse (val )
164
177
178
+ // if formatting not requested, set flag to skip formatting in the watcher before updating the value
179
+ if (! format )
180
+ skipFormatNextUpdate = true
165
181
modelValue .value = clampInputValue (parsedValue )
182
+ // if formatting is requested, force a text value update here in case the current value is the same as previous
183
+ if (format )
184
+ updateTextValue (modelValue .value )
166
185
// Set to empty state if input value is empty
167
186
if (! val .length )
168
187
return setInputValue (val )
0 commit comments