@zag-js/number-input 0.16.0 → 0.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zag-js/number-input",
3
- "version": "0.16.0",
3
+ "version": "0.18.0",
4
4
  "description": "Core logic for the number-input widget implemented as a state machine",
5
5
  "keywords": [
6
6
  "js",
@@ -27,15 +27,14 @@
27
27
  "url": "https://github.com/chakra-ui/zag/issues"
28
28
  },
29
29
  "dependencies": {
30
- "@zag-js/anatomy": "0.16.0",
31
- "@zag-js/core": "0.16.0",
32
- "@zag-js/dom-query": "0.16.0",
33
- "@zag-js/dom-event": "0.16.0",
34
- "@zag-js/form-utils": "0.16.0",
35
- "@zag-js/mutation-observer": "0.16.0",
36
- "@zag-js/number-utils": "0.16.0",
37
- "@zag-js/utils": "0.16.0",
38
- "@zag-js/types": "0.16.0"
30
+ "@zag-js/anatomy": "0.18.0",
31
+ "@zag-js/core": "0.18.0",
32
+ "@zag-js/dom-query": "0.18.0",
33
+ "@zag-js/dom-event": "0.18.0",
34
+ "@zag-js/mutation-observer": "0.18.0",
35
+ "@zag-js/number-utils": "0.18.0",
36
+ "@zag-js/utils": "0.18.0",
37
+ "@zag-js/types": "0.18.0"
39
38
  },
40
39
  "devDependencies": {
41
40
  "clean-package": "2.2.0"
package/src/index.ts CHANGED
@@ -1,4 +1,4 @@
1
1
  export { anatomy } from "./number-input.anatomy"
2
2
  export { connect } from "./number-input.connect"
3
3
  export { machine } from "./number-input.machine"
4
- export type { UserDefinedContext as Context, PublicApi } from "./number-input.types"
4
+ export type { UserDefinedContext as Context, MachineApi as Api } from "./number-input.types"
@@ -4,10 +4,10 @@ import { roundToDevicePixel } from "@zag-js/number-utils"
4
4
  import type { NormalizeProps, PropTypes } from "@zag-js/types"
5
5
  import { parts } from "./number-input.anatomy"
6
6
  import { dom } from "./number-input.dom"
7
- import type { PublicApi, Send, State } from "./number-input.types"
7
+ import type { MachineApi, Send, State } from "./number-input.types"
8
8
  import { utils } from "./number-input.utils"
9
9
 
10
- export function connect<T extends PropTypes>(state: State, send: Send, normalize: NormalizeProps<T>): PublicApi<T> {
10
+ export function connect<T extends PropTypes>(state: State, send: Send, normalize: NormalizeProps<T>): MachineApi<T> {
11
11
  const isFocused = state.hasTag("focus")
12
12
  const isInvalid = state.context.isOutOfRange || !!state.context.invalid
13
13
 
@@ -1,7 +1,6 @@
1
1
  import { choose, createMachine, guards } from "@zag-js/core"
2
2
  import { addDomEvent, requestPointerLock } from "@zag-js/dom-event"
3
3
  import { isSafari, raf } from "@zag-js/dom-query"
4
- import { dispatchInputValueEvent } from "@zag-js/form-utils"
5
4
  import { observeAttributes } from "@zag-js/mutation-observer"
6
5
  import { isAtMax, isAtMin, isWithinRange, valueOf } from "@zag-js/number-utils"
7
6
  import { callAll, compact } from "@zag-js/utils"
@@ -54,7 +53,7 @@ export function machine(userContext: UserDefinedContext) {
54
53
  },
55
54
 
56
55
  watch: {
57
- value: ["invokeOnChange", "dispatchChangeEvent"],
56
+ value: ["syncInputElement"],
58
57
  isOutOfRange: ["invokeOnInvalid"],
59
58
  scrubberCursorPoint: ["setVirtualCursorPosition"],
60
59
  },
@@ -62,15 +61,9 @@ export function machine(userContext: UserDefinedContext) {
62
61
  entry: ["syncInputValue"],
63
62
 
64
63
  on: {
65
- SET_VALUE: [
66
- {
67
- guard: "clampOnBlur",
68
- actions: ["setValue", "clampValue", "setHintToSet"],
69
- },
70
- {
71
- actions: ["setValue", "setHintToSet"],
72
- },
73
- ],
64
+ SET_VALUE: {
65
+ actions: ["setValue", "clampValue", "setHintToSet"],
66
+ },
74
67
  CLEAR_VALUE: {
75
68
  actions: ["clearValue"],
76
69
  },
@@ -84,17 +77,19 @@ export function machine(userContext: UserDefinedContext) {
84
77
 
85
78
  states: {
86
79
  idle: {
87
- exit: "invokeOnFocus",
88
80
  on: {
89
81
  PRESS_DOWN: {
90
82
  target: "before:spin",
91
- actions: ["focusInput", "setHint"],
83
+ actions: ["focusInput", "invokeOnFocus", "setHint"],
92
84
  },
93
85
  PRESS_DOWN_SCRUBBER: {
94
86
  target: "scrubbing",
95
- actions: ["focusInput", "setHint", "setCursorPoint"],
87
+ actions: ["focusInput", "invokeOnFocus", "setHint", "setCursorPoint"],
88
+ },
89
+ FOCUS: {
90
+ target: "focused",
91
+ actions: ["focusInput", "invokeOnFocus"],
96
92
  },
97
- FOCUS: "focused",
98
93
  },
99
94
  },
100
95
 
@@ -290,35 +285,37 @@ export function machine(userContext: UserDefinedContext) {
290
285
  actions: {
291
286
  focusInput(ctx) {
292
287
  if (!ctx.focusInputOnChange) return
293
- const input = dom.getInputEl(ctx)
294
- raf(() => input?.focus())
288
+ const inputEl = dom.getInputEl(ctx)
289
+ raf(() => {
290
+ inputEl?.focus()
291
+ })
295
292
  },
296
293
  increment(ctx, evt) {
297
- ctx.value = utils.increment(ctx, evt.step)
294
+ set.value(ctx, utils.increment(ctx, evt.step))
298
295
  },
299
296
  decrement(ctx, evt) {
300
- ctx.value = utils.decrement(ctx, evt.step)
297
+ set.value(ctx, utils.decrement(ctx, evt.step))
301
298
  },
302
299
  clampValue(ctx) {
303
- ctx.value = utils.clamp(ctx)
300
+ set.value(ctx, utils.clamp(ctx))
304
301
  },
305
302
  roundValue(ctx) {
306
- if (ctx.value !== "") {
307
- ctx.value = utils.round(ctx)
308
- }
303
+ if (ctx.value === "") return
304
+ set.value(ctx, utils.round(ctx))
309
305
  },
310
306
  setValue(ctx, evt) {
311
- const value = evt.target?.value ?? evt.value
312
- ctx.value = utils.sanitize(ctx, utils.parse(ctx, value.toString()))
307
+ let value = evt.target?.value ?? evt.value
308
+ value = utils.sanitize(ctx, utils.parse(ctx, value.toString()))
309
+ set.value(ctx, value)
313
310
  },
314
311
  clearValue(ctx) {
315
- ctx.value = ""
312
+ set.value(ctx, "")
316
313
  },
317
314
  setToMax(ctx) {
318
- ctx.value = ctx.max.toString()
315
+ set.value(ctx, ctx.max.toString())
319
316
  },
320
317
  setToMin(ctx) {
321
- ctx.value = ctx.min.toString()
318
+ set.value(ctx, ctx.min.toString())
322
319
  },
323
320
  setHint(ctx, evt) {
324
321
  ctx.hint = evt.hint
@@ -329,12 +326,6 @@ export function machine(userContext: UserDefinedContext) {
329
326
  setHintToSet(ctx) {
330
327
  ctx.hint = "set"
331
328
  },
332
- invokeOnChange(ctx) {
333
- ctx.onChange?.({
334
- value: ctx.value,
335
- valueAsNumber: ctx.valueAsNumber,
336
- })
337
- },
338
329
  invokeOnFocus(ctx, evt) {
339
330
  let srcElement: HTMLElement | null = null
340
331
 
@@ -353,10 +344,7 @@ export function machine(userContext: UserDefinedContext) {
353
344
  })
354
345
  },
355
346
  invokeOnBlur(ctx) {
356
- ctx.onBlur?.({
357
- value: ctx.value,
358
- valueAsNumber: ctx.valueAsNumber,
359
- })
347
+ ctx.onBlur?.({ value: ctx.value, valueAsNumber: ctx.valueAsNumber })
360
348
  },
361
349
  invokeOnInvalid(ctx) {
362
350
  if (!ctx.isOutOfRange) return
@@ -367,12 +355,16 @@ export function machine(userContext: UserDefinedContext) {
367
355
  valueAsNumber: ctx.valueAsNumber,
368
356
  })
369
357
  },
370
- // sync input value, in event it was set from form libraries via `ref`, `bind:this`, etc.
358
+ syncInputElement(ctx) {
359
+ const inputEl = dom.getInputEl(ctx)
360
+ dom.setValue(inputEl, ctx.formattedValue)
361
+ },
371
362
  syncInputValue(ctx) {
372
- const input = dom.getInputEl(ctx)
373
- if (!input || input.value == ctx.value) return
374
- const value = utils.parse(ctx, input.value)
375
- ctx.value = utils.sanitize(ctx, value)
363
+ const inputEl = dom.getInputEl(ctx)
364
+ if (!inputEl || inputEl.value == ctx.value) return
365
+
366
+ const value = utils.parse(ctx, inputEl.value)
367
+ set.value(ctx, utils.sanitize(ctx, value))
376
368
  },
377
369
  setCursorPoint(ctx, evt) {
378
370
  ctx.scrubberCursorPoint = evt.point
@@ -386,11 +378,20 @@ export function machine(userContext: UserDefinedContext) {
386
378
  const { x, y } = ctx.scrubberCursorPoint
387
379
  cursor.style.transform = `translate3d(${x}px, ${y}px, 0px)`
388
380
  },
389
- dispatchChangeEvent(ctx) {
390
- const inputEl = dom.getInputEl(ctx)
391
- dispatchInputValueEvent(inputEl, { value: ctx.formattedValue })
392
- },
393
381
  },
394
382
  },
395
383
  )
396
384
  }
385
+
386
+ const invoke = {
387
+ onChange: (ctx: MachineContext) => {
388
+ ctx.onChange?.({ value: ctx.value, valueAsNumber: ctx.valueAsNumber })
389
+ },
390
+ }
391
+
392
+ const set = {
393
+ value: (ctx: MachineContext, value: string) => {
394
+ ctx.value = value
395
+ invoke.onChange(ctx)
396
+ },
397
+ }
@@ -154,68 +154,6 @@ type PublicContext = DirectionProperty &
154
154
  spinOnPress?: boolean
155
155
  }
156
156
 
157
- export type PublicApi<T extends PropTypes = PropTypes> = {
158
- /**
159
- * Whether the input is focused.
160
- */
161
- isFocused: boolean
162
- /**
163
- * Whether the input is invalid.
164
- */
165
- isInvalid: boolean
166
- /**
167
- * Whether the input value is empty.
168
- */
169
- isValueEmpty: boolean
170
- /**
171
- * The formatted value of the input.
172
- */
173
- value: string
174
- /**
175
- * The value of the input as a number.
176
- */
177
- valueAsNumber: number
178
- /**
179
- * Function to set the value of the input.
180
- */
181
- setValue(value: string | number): void
182
- /**
183
- * Function to clear the value of the input.
184
- */
185
- clearValue(): void
186
- /**
187
- * Function to increment the value of the input by the step.
188
- */
189
- increment(): void
190
- /**
191
- * Function to decrement the value of the input by the step.
192
- */
193
- decrement(): void
194
- /**
195
- * Function to set the value of the input to the max.
196
- */
197
- setToMax(): void
198
- /**
199
- * Function to set the value of the input to the min.
200
- */
201
- setToMin(): void
202
- /**
203
- * Function to focus the input.
204
- */
205
- focus(): void
206
- /**
207
- * Function to blur the input.
208
- */
209
- blur(): void
210
- rootProps: T["element"]
211
- labelProps: T["label"]
212
- controlProps: T["element"]
213
- inputProps: T["input"]
214
- decrementTriggerProps: T["button"]
215
- incrementTriggerProps: T["button"]
216
- scrubberProps: T["element"]
217
- }
218
-
219
157
  export type UserDefinedContext = RequiredBy<PublicContext, "id">
220
158
 
221
159
  type ComputedContext = Readonly<{
@@ -294,3 +232,65 @@ export type MachineState = {
294
232
  export type State = S.State<MachineContext, MachineState>
295
233
 
296
234
  export type Send = S.Send<S.AnyEventObject>
235
+
236
+ export type MachineApi<T extends PropTypes = PropTypes> = {
237
+ /**
238
+ * Whether the input is focused.
239
+ */
240
+ isFocused: boolean
241
+ /**
242
+ * Whether the input is invalid.
243
+ */
244
+ isInvalid: boolean
245
+ /**
246
+ * Whether the input value is empty.
247
+ */
248
+ isValueEmpty: boolean
249
+ /**
250
+ * The formatted value of the input.
251
+ */
252
+ value: string
253
+ /**
254
+ * The value of the input as a number.
255
+ */
256
+ valueAsNumber: number
257
+ /**
258
+ * Function to set the value of the input.
259
+ */
260
+ setValue(value: string | number): void
261
+ /**
262
+ * Function to clear the value of the input.
263
+ */
264
+ clearValue(): void
265
+ /**
266
+ * Function to increment the value of the input by the step.
267
+ */
268
+ increment(): void
269
+ /**
270
+ * Function to decrement the value of the input by the step.
271
+ */
272
+ decrement(): void
273
+ /**
274
+ * Function to set the value of the input to the max.
275
+ */
276
+ setToMax(): void
277
+ /**
278
+ * Function to set the value of the input to the min.
279
+ */
280
+ setToMin(): void
281
+ /**
282
+ * Function to focus the input.
283
+ */
284
+ focus(): void
285
+ /**
286
+ * Function to blur the input.
287
+ */
288
+ blur(): void
289
+ rootProps: T["element"]
290
+ labelProps: T["label"]
291
+ controlProps: T["element"]
292
+ inputProps: T["input"]
293
+ decrementTriggerProps: T["button"]
294
+ incrementTriggerProps: T["button"]
295
+ scrubberProps: T["element"]
296
+ }