@zag-js/number-input 0.20.0 → 0.22.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/dist/index.mjs CHANGED
@@ -13,7 +13,7 @@ var parts = anatomy.build();
13
13
 
14
14
  // src/number-input.connect.ts
15
15
  import { getEventPoint, getEventStep, getNativeEvent, isLeftClick } from "@zag-js/dom-event";
16
- import { ariaAttr, dataAttr } from "@zag-js/dom-query";
16
+ import { ariaAttr, dataAttr, isHTMLElement } from "@zag-js/dom-query";
17
17
  import { roundToDevicePixel as roundToDevicePixel2 } from "@zag-js/number-utils";
18
18
 
19
19
  // src/number-input.dom.ts
@@ -114,54 +114,15 @@ var dom = createScope({
114
114
  }
115
115
  });
116
116
 
117
- // src/number-input.utils.ts
118
- import { isModifiedEvent } from "@zag-js/dom-event";
119
- import { clamp, decrement, formatDecimal, increment } from "@zag-js/number-utils";
120
- var utils = {
121
- isValidNumericEvent: (ctx, event) => {
122
- if (event.key == null)
123
- return true;
124
- const isModifier = isModifiedEvent(event);
125
- const isSingleKey = event.key.length === 1;
126
- if (isModifier || !isSingleKey)
127
- return true;
128
- return ctx.validateCharacter?.(event.key) ?? utils.isFloatingPoint(event.key);
129
- },
130
- isFloatingPoint: (v) => /^[0-9+\-.]$/.test(v),
131
- sanitize: (ctx, value) => {
132
- return value.split("").filter(ctx.validateCharacter ?? utils.isFloatingPoint).join("");
133
- },
134
- increment: (ctx, step) => {
135
- const value = increment(ctx.value, step ?? ctx.step);
136
- return formatDecimal(clamp(value, ctx), ctx);
137
- },
138
- decrement: (ctx, step) => {
139
- const value = decrement(ctx.value, step ?? ctx.step);
140
- return formatDecimal(clamp(value, ctx), ctx);
141
- },
142
- clamp: (ctx) => {
143
- return formatDecimal(clamp(ctx.value, ctx), ctx);
144
- },
145
- parse: (ctx, value) => {
146
- return ctx.parse?.(value) ?? value;
147
- },
148
- format: (ctx, value) => {
149
- const _val = value.toString();
150
- return ctx.format?.(_val) ?? _val;
151
- },
152
- round: (ctx) => {
153
- return formatDecimal(ctx.value, ctx);
154
- }
155
- };
156
-
157
117
  // src/number-input.connect.ts
158
118
  function connect(state, send, normalize) {
159
119
  const isFocused = state.hasTag("focus");
160
120
  const isInvalid = state.context.isOutOfRange || !!state.context.invalid;
161
- const isDisabled = !!state.context.disabled;
162
121
  const isValueEmpty = state.context.isValueEmpty;
163
- const isIncrementDisabled = isDisabled || !state.context.canIncrement;
164
- const isDecrementDisabled = isDisabled || !state.context.canDecrement;
122
+ const isDisabled = state.context.isDisabled;
123
+ const isReadOnly = state.context.readOnly;
124
+ const isIncrementDisabled = isDisabled || !state.context.canIncrement || isReadOnly;
125
+ const isDecrementDisabled = isDisabled || !state.context.canDecrement || isReadOnly;
165
126
  const translations = state.context.translations;
166
127
  return {
167
128
  isFocused,
@@ -170,38 +131,37 @@ function connect(state, send, normalize) {
170
131
  value: state.context.formattedValue,
171
132
  valueAsNumber: state.context.valueAsNumber,
172
133
  setValue(value) {
173
- send({ type: "SET_VALUE", value: value.toString() });
134
+ send({ type: "VALUE.SET", value: value.toString() });
174
135
  },
175
136
  clearValue() {
176
- send("CLEAR_VALUE");
137
+ send("VALUE.CLEAR");
177
138
  },
178
139
  increment() {
179
- send("INCREMENT");
140
+ send("VALUE.INCREMENT");
180
141
  },
181
142
  decrement() {
182
- send("DECREMENT");
143
+ send("VALUE.DECREMENT");
183
144
  },
184
145
  setToMax() {
185
- send({ type: "SET_VALUE", value: state.context.max });
146
+ send({ type: "VALUE.SET", value: state.context.max });
186
147
  },
187
148
  setToMin() {
188
- send({ type: "SET_VALUE", value: state.context.min });
149
+ send({ type: "VALUE.SET", value: state.context.min });
189
150
  },
190
151
  focus() {
191
152
  dom.getInputEl(state.context)?.focus();
192
153
  },
193
- blur() {
194
- dom.getInputEl(state.context)?.blur();
195
- },
196
154
  rootProps: normalize.element({
197
155
  id: dom.getRootId(state.context),
198
156
  ...parts.root.attrs,
157
+ dir: state.context.dir,
199
158
  "data-disabled": dataAttr(isDisabled),
200
159
  "data-focus": dataAttr(isFocused),
201
160
  "data-invalid": dataAttr(isInvalid)
202
161
  }),
203
162
  labelProps: normalize.label({
204
163
  ...parts.label.attrs,
164
+ dir: state.context.dir,
205
165
  "data-disabled": dataAttr(isDisabled),
206
166
  "data-focus": dataAttr(isFocused),
207
167
  "data-invalid": dataAttr(isInvalid),
@@ -210,6 +170,7 @@ function connect(state, send, normalize) {
210
170
  }),
211
171
  controlProps: normalize.element({
212
172
  ...parts.control.attrs,
173
+ dir: state.context.dir,
213
174
  role: "group",
214
175
  "aria-disabled": isDisabled,
215
176
  "data-focus": dataAttr(isFocused),
@@ -219,6 +180,7 @@ function connect(state, send, normalize) {
219
180
  }),
220
181
  inputProps: normalize.input({
221
182
  ...parts.input.attrs,
183
+ dir: state.context.dir,
222
184
  name: state.context.name,
223
185
  form: state.context.form,
224
186
  id: dom.getInputId(state.context),
@@ -238,48 +200,68 @@ function connect(state, send, normalize) {
238
200
  "aria-roledescription": "numberfield",
239
201
  "aria-valuemin": state.context.min,
240
202
  "aria-valuemax": state.context.max,
241
- "aria-valuenow": isNaN(state.context.valueAsNumber) ? void 0 : state.context.valueAsNumber,
203
+ "aria-valuenow": Number.isNaN(state.context.valueAsNumber) ? void 0 : state.context.valueAsNumber,
242
204
  "aria-valuetext": state.context.valueText,
205
+ onCompositionStart() {
206
+ send("INPUT.COMPOSITION_START");
207
+ },
208
+ onCompositionEnd() {
209
+ send("INPUT.COMPOSITION_END");
210
+ },
243
211
  onFocus() {
244
- send("FOCUS");
212
+ send("INPUT.FOCUS");
245
213
  },
246
214
  onBlur() {
247
- send("BLUR");
215
+ send({ type: "INPUT.COMMIT", src: "blur" });
248
216
  },
249
217
  onChange(event) {
250
- send({ type: "CHANGE", target: event.currentTarget, hint: "set" });
218
+ send({ type: "INPUT.CHANGE", target: event.currentTarget, hint: "set" });
219
+ },
220
+ onBeforeInput(event) {
221
+ try {
222
+ const { selectionStart, selectionEnd, value } = event.currentTarget;
223
+ const nextValue = value.slice(0, selectionStart) + event.data + value.slice(selectionEnd);
224
+ const isValid = state.context.parser.isValidPartialNumber(nextValue);
225
+ if (!isValid) {
226
+ event.preventDefault();
227
+ }
228
+ } catch {
229
+ }
251
230
  },
252
231
  onKeyDown(event) {
253
- const evt = getNativeEvent(event);
254
- if (evt.isComposing)
232
+ if (state.context.readOnly)
255
233
  return;
256
- if (!utils.isValidNumericEvent(state.context, event)) {
257
- event.preventDefault();
258
- }
259
234
  const step = getEventStep(event) * state.context.step;
260
235
  const keyMap = {
261
236
  ArrowUp() {
262
- send({ type: "ARROW_UP", step });
237
+ send({ type: "INPUT.ARROW_UP", step });
238
+ event.preventDefault();
263
239
  },
264
240
  ArrowDown() {
265
- send({ type: "ARROW_DOWN", step });
241
+ send({ type: "INPUT.ARROW_DOWN", step });
242
+ event.preventDefault();
266
243
  },
267
244
  Home() {
268
- send("HOME");
245
+ send("INPUT.HOME");
246
+ event.preventDefault();
269
247
  },
270
248
  End() {
271
- send("END");
249
+ send("INPUT.END");
250
+ event.preventDefault();
251
+ },
252
+ Enter() {
253
+ if (state.context.composing)
254
+ return;
255
+ send({ type: "INPUT.COMMIT", src: "enter" });
272
256
  }
273
257
  };
274
258
  const exec = keyMap[event.key];
275
- if (exec) {
276
- exec(event);
277
- event.preventDefault();
278
- }
259
+ exec?.(event);
279
260
  }
280
261
  }),
281
262
  decrementTriggerProps: normalize.button({
282
263
  ...parts.decrementTrigger.attrs,
264
+ dir: state.context.dir,
283
265
  id: dom.getDecrementTriggerId(state.context),
284
266
  disabled: isDecrementDisabled,
285
267
  "data-disabled": dataAttr(isDecrementDisabled),
@@ -288,22 +270,28 @@ function connect(state, send, normalize) {
288
270
  tabIndex: -1,
289
271
  "aria-controls": dom.getInputId(state.context),
290
272
  onPointerDown(event) {
291
- if (isDecrementDisabled)
273
+ if (isDecrementDisabled || !isLeftClick(event))
292
274
  return;
293
- send(isLeftClick(event) ? { type: "PRESS_DOWN", hint: "decrement" } : { type: "FOCUS" });
294
- event.preventDefault();
275
+ if (event.pointerType === "mouse") {
276
+ send({ type: "TRIGGER.PRESS_DOWN", hint: "decrement" });
277
+ return event.preventDefault();
278
+ }
279
+ if (isHTMLElement(event.target)) {
280
+ event.target?.focus();
281
+ }
295
282
  },
296
283
  onPointerUp() {
297
- send({ type: "PRESS_UP", hint: "decrement" });
284
+ send({ type: "TRIGGER.PRESS_UP", hint: "decrement" });
298
285
  },
299
286
  onPointerLeave() {
300
287
  if (isDecrementDisabled)
301
288
  return;
302
- send({ type: "PRESS_UP", hint: "decrement" });
289
+ send({ type: "TRIGGER.PRESS_UP", hint: "decrement" });
303
290
  }
304
291
  }),
305
292
  incrementTriggerProps: normalize.button({
306
293
  ...parts.incrementTrigger.attrs,
294
+ dir: state.context.dir,
307
295
  id: dom.getIncrementTriggerId(state.context),
308
296
  disabled: isIncrementDisabled,
309
297
  "data-disabled": dataAttr(isIncrementDisabled),
@@ -312,16 +300,21 @@ function connect(state, send, normalize) {
312
300
  tabIndex: -1,
313
301
  "aria-controls": dom.getInputId(state.context),
314
302
  onPointerDown(event) {
315
- if (isIncrementDisabled)
303
+ if (isIncrementDisabled || !isLeftClick(event))
316
304
  return;
317
- send(isLeftClick(event) ? { type: "PRESS_DOWN", hint: "increment" } : { type: "FOCUS" });
318
- event.preventDefault();
305
+ if (event.pointerType === "mouse") {
306
+ send({ type: "TRIGGER.PRESS_DOWN", hint: "increment" });
307
+ return event.preventDefault();
308
+ }
309
+ if (isHTMLElement(event.target)) {
310
+ event.target?.focus();
311
+ }
319
312
  },
320
313
  onPointerUp() {
321
- send({ type: "PRESS_UP", hint: "increment" });
314
+ send({ type: "TRIGGER.PRESS_UP", hint: "increment" });
322
315
  },
323
316
  onPointerLeave() {
324
- send({ type: "PRESS_UP", hint: "increment" });
317
+ send({ type: "TRIGGER.PRESS_UP", hint: "increment" });
325
318
  }
326
319
  }),
327
320
  scrubberProps: normalize.element({
@@ -336,7 +329,7 @@ function connect(state, send, normalize) {
336
329
  const point = getEventPoint(evt);
337
330
  point.x = point.x - roundToDevicePixel2(7.5);
338
331
  point.y = point.y - roundToDevicePixel2(7.5);
339
- send({ type: "PRESS_DOWN_SCRUBBER", point });
332
+ send({ type: "SCRUBBER.PRESS_DOWN", point });
340
333
  event.preventDefault();
341
334
  },
342
335
  style: {
@@ -347,12 +340,57 @@ function connect(state, send, normalize) {
347
340
  }
348
341
 
349
342
  // src/number-input.machine.ts
350
- import { choose, createMachine, guards } from "@zag-js/core";
343
+ import { NumberParser } from "@internationalized/number";
344
+ import { choose, createMachine, guards, ref } from "@zag-js/core";
351
345
  import { addDomEvent, requestPointerLock } from "@zag-js/dom-event";
352
346
  import { isSafari as isSafari2, raf } from "@zag-js/dom-query";
347
+ import { trackFormControl } from "@zag-js/form-utils";
353
348
  import { observeAttributes } from "@zag-js/mutation-observer";
354
- import { isAtMax, isAtMin, isWithinRange, valueOf } from "@zag-js/number-utils";
355
- import { callAll, compact, isEqual, match } from "@zag-js/utils";
349
+ import { clamp, decrement, increment, isAtMax, isAtMin, isWithinRange } from "@zag-js/number-utils";
350
+ import { callAll, compact, isEqual } from "@zag-js/utils";
351
+
352
+ // src/cursor.ts
353
+ function recordCursor(inputEl) {
354
+ if (inputEl.ownerDocument.activeElement !== inputEl)
355
+ return;
356
+ try {
357
+ const { selectionStart: start, selectionEnd: end, value } = inputEl;
358
+ const beforeTxt = value.substring(0, start);
359
+ const afterTxt = value.substring(end);
360
+ return {
361
+ start,
362
+ end,
363
+ value,
364
+ beforeTxt,
365
+ afterTxt
366
+ };
367
+ } catch {
368
+ }
369
+ }
370
+ function restoreCursor(inputEl, selection) {
371
+ if (inputEl.ownerDocument.activeElement !== inputEl || !selection)
372
+ return;
373
+ try {
374
+ const { value } = inputEl;
375
+ const { beforeTxt = "", afterTxt = "", start } = selection;
376
+ let startPos = value.length;
377
+ if (value.endsWith(afterTxt)) {
378
+ startPos = value.length - afterTxt.length;
379
+ } else if (value.startsWith(beforeTxt)) {
380
+ startPos = beforeTxt.length;
381
+ } else if (start != null) {
382
+ const beforeLastChar = beforeTxt[start - 1];
383
+ const newIndex = value.indexOf(beforeLastChar, start - 1);
384
+ if (newIndex !== -1) {
385
+ startPos = newIndex + 1;
386
+ }
387
+ }
388
+ inputEl.setSelectionRange(startPos, startPos);
389
+ } catch {
390
+ }
391
+ }
392
+
393
+ // src/number-input.machine.ts
356
394
  var { not, and } = guards;
357
395
  function machine(userContext) {
358
396
  const ctx = compact(userContext);
@@ -362,20 +400,25 @@ function machine(userContext) {
362
400
  initial: "idle",
363
401
  context: {
364
402
  dir: "ltr",
403
+ locale: "en-US",
365
404
  focusInputOnChange: true,
366
405
  clampValueOnBlur: true,
367
406
  allowOverflow: false,
368
407
  inputMode: "decimal",
369
408
  pattern: "[0-9]*(.[0-9]+)?",
370
- hint: null,
371
409
  value: "",
372
410
  step: 1,
373
411
  min: Number.MIN_SAFE_INTEGER,
374
412
  max: Number.MAX_SAFE_INTEGER,
375
- scrubberCursorPoint: null,
376
413
  invalid: false,
377
414
  spinOnPress: true,
378
415
  ...ctx,
416
+ hint: null,
417
+ scrubberCursorPoint: null,
418
+ composing: false,
419
+ fieldsetDisabled: false,
420
+ formatter: createFormatter(ctx.locale || "en-US", ctx.formatOptions),
421
+ parser: createParser(ctx.locale || "en-US", ctx.formatOptions),
379
422
  translations: {
380
423
  incrementLabel: "increment value",
381
424
  decrementLabel: "decrease value",
@@ -384,48 +427,56 @@ function machine(userContext) {
384
427
  },
385
428
  computed: {
386
429
  isRtl: (ctx2) => ctx2.dir === "rtl",
387
- valueAsNumber: (ctx2) => valueOf(ctx2.value),
430
+ valueAsNumber: (ctx2) => ctx2.parser.parse(String(ctx2.value)) ?? 0,
388
431
  isAtMin: (ctx2) => isAtMin(ctx2.value, ctx2),
389
432
  isAtMax: (ctx2) => isAtMax(ctx2.value, ctx2),
390
433
  isOutOfRange: (ctx2) => !isWithinRange(ctx2.value, ctx2),
391
434
  isValueEmpty: (ctx2) => ctx2.value === "",
435
+ isDisabled: (ctx2) => !!ctx2.disabled || ctx2.fieldsetDisabled,
392
436
  canIncrement: (ctx2) => ctx2.allowOverflow || !ctx2.isAtMax,
393
437
  canDecrement: (ctx2) => ctx2.allowOverflow || !ctx2.isAtMin,
394
438
  valueText: (ctx2) => ctx2.translations.valueText?.(ctx2.value),
395
- formattedValue: (ctx2) => ctx2.format?.(ctx2.value).toString() ?? ctx2.value
439
+ formattedValue: (ctx2) => {
440
+ if (Number.isNaN(ctx2.valueAsNumber))
441
+ return ctx2.value;
442
+ return ctx2.formatter.format(ctx2.valueAsNumber);
443
+ }
396
444
  },
397
445
  watch: {
446
+ formatOptions: ["setFormatterAndParser", "syncInputElement"],
447
+ locale: ["setFormatterAndParser", "syncInputElement"],
398
448
  value: ["syncInputElement"],
399
449
  isOutOfRange: ["invokeOnInvalid"],
400
450
  scrubberCursorPoint: ["setVirtualCursorPosition"]
401
451
  },
402
452
  entry: ["syncInputValue"],
453
+ activities: ["trackFormControl"],
403
454
  on: {
404
- SET_VALUE: {
455
+ "VALUE.SET": {
405
456
  actions: ["setValue", "clampValue", "setHintToSet"]
406
457
  },
407
- CLEAR_VALUE: {
458
+ "VALUE.CLEAR": {
408
459
  actions: ["clearValue"]
409
460
  },
410
- INCREMENT: {
461
+ "VALUE.INCREMENT": {
411
462
  actions: ["increment"]
412
463
  },
413
- DECREMENT: {
464
+ "VALUE.DECREMENT": {
414
465
  actions: ["decrement"]
415
466
  }
416
467
  },
417
468
  states: {
418
469
  idle: {
419
470
  on: {
420
- PRESS_DOWN: {
471
+ "TRIGGER.PRESS_DOWN": {
421
472
  target: "before:spin",
422
473
  actions: ["focusInput", "invokeOnFocus", "setHint"]
423
474
  },
424
- PRESS_DOWN_SCRUBBER: {
475
+ "SCRUBBER.PRESS_DOWN": {
425
476
  target: "scrubbing",
426
477
  actions: ["focusInput", "invokeOnFocus", "setHint", "setCursorPoint"]
427
478
  },
428
- FOCUS: {
479
+ "INPUT.FOCUS": {
429
480
  target: "focused",
430
481
  actions: ["focusInput", "invokeOnFocus"]
431
482
  }
@@ -436,45 +487,46 @@ function machine(userContext) {
436
487
  entry: "focusInput",
437
488
  activities: "attachWheelListener",
438
489
  on: {
439
- PRESS_DOWN: {
490
+ "TRIGGER.PRESS_DOWN": {
440
491
  target: "before:spin",
441
492
  actions: ["focusInput", "setHint"]
442
493
  },
443
- PRESS_DOWN_SCRUBBER: {
494
+ "SCRUBBER.PRESS_DOWN": {
444
495
  target: "scrubbing",
445
496
  actions: ["focusInput", "setHint", "setCursorPoint"]
446
497
  },
447
- ARROW_UP: {
498
+ "INPUT.ARROW_UP": {
448
499
  actions: "increment"
449
500
  },
450
- ARROW_DOWN: {
501
+ "INPUT.ARROW_DOWN": {
451
502
  actions: "decrement"
452
503
  },
453
- HOME: {
454
- actions: "setToMin"
504
+ "INPUT.HOME": {
505
+ actions: "decrementToMin"
455
506
  },
456
- END: {
457
- actions: "setToMax"
507
+ "INPUT.END": {
508
+ actions: "incrementToMax"
458
509
  },
459
- CHANGE: {
510
+ "INPUT.CHANGE": {
460
511
  actions: ["setValue", "setHint"]
461
512
  },
462
- BLUR: [
463
- {
464
- guard: "isInvalidExponential",
465
- target: "idle",
466
- actions: ["clearValue", "clearHint", "invokeOnBlur"]
467
- },
513
+ "INPUT.COMMIT": [
468
514
  {
469
- guard: and("clampOnBlur", not("isInRange"), not("isEmptyValue")),
515
+ guard: and("clampValueOnBlur", not("isInRange")),
470
516
  target: "idle",
471
- actions: ["clampValue", "clearHint", "invokeOnBlur"]
517
+ actions: ["clampValue", "syncInputElement", "clearHint", "invokeOnBlur"]
472
518
  },
473
519
  {
474
520
  target: "idle",
475
- actions: ["roundValue", "invokeOnBlur"]
521
+ actions: ["syncInputElement", "invokeOnBlur"]
476
522
  }
477
- ]
523
+ ],
524
+ "INPUT.COMPOSITION_START": {
525
+ actions: "setComposing"
526
+ },
527
+ "INPUT.COMPOSITION_END": {
528
+ actions: "clearComposing"
529
+ }
478
530
  }
479
531
  },
480
532
  "before:spin": {
@@ -491,7 +543,7 @@ function machine(userContext) {
491
543
  }
492
544
  },
493
545
  on: {
494
- PRESS_UP: {
546
+ "TRIGGER.PRESS_UP": {
495
547
  target: "focused",
496
548
  actions: "clearHint"
497
549
  }
@@ -513,7 +565,7 @@ function machine(userContext) {
513
565
  }
514
566
  ],
515
567
  on: {
516
- PRESS_UP: {
568
+ "TRIGGER.PRESS_UP": {
517
569
  target: "focused",
518
570
  actions: "clearHint"
519
571
  }
@@ -524,8 +576,8 @@ function machine(userContext) {
524
576
  exit: "clearCursorPoint",
525
577
  activities: ["activatePointerLock", "trackMousemove", "setupVirtualCursor", "preventTextSelection"],
526
578
  on: {
527
- POINTER_UP_SCRUBBER: "focused",
528
- POINTER_MOVE_SCRUBBER: [
579
+ "SCRUBBER.POINTER_UP": "focused",
580
+ "SCRUBBER.POINTER_MOVE": [
529
581
  {
530
582
  guard: "isIncrementHint",
531
583
  actions: ["increment", "setCursorPoint"]
@@ -545,17 +597,26 @@ function machine(userContext) {
545
597
  CHANGE_DELAY: 300
546
598
  },
547
599
  guards: {
548
- clampOnBlur: (ctx2) => !!ctx2.clampValueOnBlur,
600
+ clampValueOnBlur: (ctx2) => ctx2.clampValueOnBlur,
549
601
  isAtMin: (ctx2) => ctx2.isAtMin,
550
602
  spinOnPress: (ctx2) => !!ctx2.spinOnPress,
551
603
  isAtMax: (ctx2) => ctx2.isAtMax,
552
604
  isInRange: (ctx2) => !ctx2.isOutOfRange,
553
605
  isDecrementHint: (ctx2, evt) => (evt.hint ?? ctx2.hint) === "decrement",
554
- isEmptyValue: (ctx2) => ctx2.isValueEmpty,
555
- isIncrementHint: (ctx2, evt) => (evt.hint ?? ctx2.hint) === "increment",
556
- isInvalidExponential: (ctx2) => ctx2.value.toString().startsWith("e")
606
+ isIncrementHint: (ctx2, evt) => (evt.hint ?? ctx2.hint) === "increment"
557
607
  },
558
608
  activities: {
609
+ trackFormControl(ctx2, _evt, { initialContext }) {
610
+ const inputEl = dom.getInputEl(ctx2);
611
+ return trackFormControl(inputEl, {
612
+ onFieldsetDisabledChange(disabled) {
613
+ ctx2.fieldsetDisabled = disabled;
614
+ },
615
+ onFormReset() {
616
+ set.value(ctx2, initialContext.value);
617
+ }
618
+ });
619
+ },
559
620
  setupVirtualCursor(ctx2) {
560
621
  return dom.setupVirtualCursor(ctx2);
561
622
  },
@@ -565,26 +626,23 @@ function machine(userContext) {
565
626
  trackButtonDisabled(ctx2, _evt, { send }) {
566
627
  const btn = dom.getPressedTriggerEl(ctx2, ctx2.hint);
567
628
  return observeAttributes(btn, ["disabled"], () => {
568
- send("PRESS_UP");
629
+ send({ type: "TRIGGER.PRESS_UP", src: "attr" });
569
630
  });
570
631
  },
571
632
  attachWheelListener(ctx2, _evt, { send }) {
572
- const input = dom.getInputEl(ctx2);
573
- if (!input)
633
+ const inputEl = dom.getInputEl(ctx2);
634
+ if (!inputEl || !dom.isActiveElement(ctx2, inputEl) || !ctx2.allowMouseWheel)
574
635
  return;
575
636
  function onWheel(event) {
576
- const isInputFocused = dom.getDoc(ctx2).activeElement === input;
577
- if (!ctx2.allowMouseWheel || !isInputFocused)
578
- return;
579
637
  event.preventDefault();
580
638
  const dir = Math.sign(event.deltaY) * -1;
581
639
  if (dir === 1) {
582
- send("INCREMENT");
640
+ send("VALUE.INCREMENT");
583
641
  } else if (dir === -1) {
584
- send("DECREMENT");
642
+ send("VALUE.DECREMENT");
585
643
  }
586
644
  }
587
- return addDomEvent(input, "wheel", onWheel, { passive: false });
645
+ return addDomEvent(inputEl, "wheel", onWheel, { passive: false });
588
646
  },
589
647
  activatePointerLock(ctx2) {
590
648
  if (isSafari2())
@@ -600,13 +658,13 @@ function machine(userContext) {
600
658
  if (!value.hint)
601
659
  return;
602
660
  send({
603
- type: "POINTER_MOVE_SCRUBBER",
661
+ type: "SCRUBBER.POINTER_MOVE",
604
662
  hint: value.hint,
605
663
  point: value.point
606
664
  });
607
665
  }
608
666
  function onMouseup() {
609
- send("POINTER_UP_SCRUBBER");
667
+ send("SCRUBBER.POINTER_UP");
610
668
  }
611
669
  return callAll(
612
670
  addDomEvent(doc, "mousemove", onMousemove, false),
@@ -619,37 +677,34 @@ function machine(userContext) {
619
677
  if (!ctx2.focusInputOnChange)
620
678
  return;
621
679
  const inputEl = dom.getInputEl(ctx2);
622
- raf(() => {
623
- inputEl?.focus();
624
- });
680
+ if (dom.isActiveElement(ctx2, inputEl))
681
+ return;
682
+ raf(() => inputEl?.focus({ preventScroll: true }));
625
683
  },
626
684
  increment(ctx2, evt) {
627
- set.value(ctx2, utils.increment(ctx2, evt.step));
685
+ const nextValue = increment(ctx2.value, evt.step ?? ctx2.step);
686
+ set.value(ctx2, String(clamp(nextValue, ctx2)));
628
687
  },
629
688
  decrement(ctx2, evt) {
630
- set.value(ctx2, utils.decrement(ctx2, evt.step));
689
+ const nextValue = decrement(ctx2.value, evt.step ?? ctx2.step);
690
+ set.value(ctx2, String(clamp(nextValue, ctx2)));
631
691
  },
632
692
  clampValue(ctx2) {
633
- set.value(ctx2, utils.clamp(ctx2));
634
- },
635
- roundValue(ctx2) {
636
- if (ctx2.value === "")
637
- return;
638
- set.value(ctx2, utils.round(ctx2));
693
+ const nextValue = clamp(ctx2.value, ctx2);
694
+ set.value(ctx2, String(nextValue));
639
695
  },
640
696
  setValue(ctx2, evt) {
641
- let value = evt.target?.value ?? evt.value;
642
- value = utils.sanitize(ctx2, utils.parse(ctx2, value.toString()));
697
+ const value = evt.target?.value ?? evt.value;
643
698
  set.value(ctx2, value);
644
699
  },
645
700
  clearValue(ctx2) {
646
701
  set.value(ctx2, "");
647
702
  },
648
- setToMax(ctx2) {
649
- set.value(ctx2, ctx2.max.toString());
703
+ incrementToMax(ctx2) {
704
+ set.value(ctx2, String(ctx2.max));
650
705
  },
651
- setToMin(ctx2) {
652
- set.value(ctx2, ctx2.min.toString());
706
+ decrementToMin(ctx2) {
707
+ set.value(ctx2, String(ctx2.min));
653
708
  },
654
709
  setHint(ctx2, evt) {
655
710
  ctx2.hint = evt.hint;
@@ -660,15 +715,9 @@ function machine(userContext) {
660
715
  setHintToSet(ctx2) {
661
716
  ctx2.hint = "set";
662
717
  },
663
- invokeOnFocus(ctx2, evt) {
664
- const srcElement = match(evt.type, {
665
- PRESS_DOWN: dom.getPressedTriggerEl(ctx2, evt.hint),
666
- FOCUS: dom.getInputEl(ctx2),
667
- PRESS_DOWN_SCRUBBER: dom.getScrubberEl(ctx2)
668
- });
718
+ invokeOnFocus(ctx2) {
669
719
  ctx2.onFocusChange?.({
670
720
  focused: true,
671
- srcElement,
672
721
  value: ctx2.formattedValue,
673
722
  valueAsNumber: ctx2.valueAsNumber
674
723
  });
@@ -690,16 +739,20 @@ function machine(userContext) {
690
739
  valueAsNumber: ctx2.valueAsNumber
691
740
  });
692
741
  },
693
- syncInputElement(ctx2) {
742
+ syncInputElement(ctx2, evt) {
694
743
  const inputEl = dom.getInputEl(ctx2);
695
- dom.setValue(inputEl, ctx2.formattedValue);
744
+ const inputValue = evt.type.endsWith("CHANGE") ? ctx2.value : ctx2.formattedValue;
745
+ const sel = recordCursor(inputEl);
746
+ dom.setValue(inputEl, inputValue);
747
+ raf(() => {
748
+ restoreCursor(inputEl, sel);
749
+ });
696
750
  },
697
751
  syncInputValue(ctx2) {
698
752
  const inputEl = dom.getInputEl(ctx2);
699
753
  if (!inputEl || inputEl.value == ctx2.value)
700
754
  return;
701
- const value = utils.parse(ctx2, inputEl.value);
702
- set.value(ctx2, utils.sanitize(ctx2, value));
755
+ set.value(ctx2, ctx2.formattedValue);
703
756
  },
704
757
  setCursorPoint(ctx2, evt) {
705
758
  ctx2.scrubberCursorPoint = evt.point;
@@ -708,19 +761,50 @@ function machine(userContext) {
708
761
  ctx2.scrubberCursorPoint = null;
709
762
  },
710
763
  setVirtualCursorPosition(ctx2) {
711
- const cursor = dom.getCursorEl(ctx2);
712
- if (!cursor || !ctx2.scrubberCursorPoint)
764
+ const cursorEl = dom.getCursorEl(ctx2);
765
+ if (!cursorEl || !ctx2.scrubberCursorPoint)
713
766
  return;
714
767
  const { x, y } = ctx2.scrubberCursorPoint;
715
- cursor.style.transform = `translate3d(${x}px, ${y}px, 0px)`;
768
+ cursorEl.style.transform = `translate3d(${x}px, ${y}px, 0px)`;
769
+ },
770
+ setComposing(ctx2) {
771
+ ctx2.composing = true;
772
+ },
773
+ clearComposing(ctx2) {
774
+ ctx2.composing = false;
775
+ },
776
+ setFormatterAndParser(ctx2) {
777
+ if (!ctx2.locale)
778
+ return;
779
+ ctx2.formatter = createFormatter(ctx2.locale, ctx2.formatOptions);
780
+ ctx2.parser = createParser(ctx2.locale, ctx2.formatOptions);
716
781
  }
782
+ },
783
+ compareFns: {
784
+ formatOptions: (a, b) => isEqual(a, b)
717
785
  }
718
786
  }
719
787
  );
720
788
  }
789
+ var defaultFormatOptions = {
790
+ style: "decimal",
791
+ minimumFractionDigits: 0,
792
+ maximumFractionDigits: 20
793
+ };
794
+ var createFormatter = (locale, options = {}) => {
795
+ const formatOptions = Object.assign({}, defaultFormatOptions, options);
796
+ return ref(new Intl.NumberFormat(locale, formatOptions));
797
+ };
798
+ var createParser = (locale, options = {}) => {
799
+ const formatOptions = Object.assign({}, defaultFormatOptions, options);
800
+ return ref(new NumberParser(locale, formatOptions));
801
+ };
721
802
  var invoke = {
722
803
  onChange: (ctx) => {
723
- ctx.onValueChange?.({ value: ctx.value, valueAsNumber: ctx.valueAsNumber });
804
+ ctx.onValueChange?.({
805
+ value: ctx.value,
806
+ valueAsNumber: ctx.valueAsNumber
807
+ });
724
808
  }
725
809
  };
726
810
  var set = {