@zag-js/number-input 0.82.1 → 1.0.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
@@ -1,8 +1,9 @@
1
1
  import { createAnatomy } from '@zag-js/anatomy';
2
- import { createScope, isSafari, MAX_Z_INDEX, dataAttr, ariaAttr, isComposingEvent, getEventStep, isLeftClick, getEventPoint, getWindow, trackFormControl, observeAttributes, addDomEvent, requestPointerLock, raf, setElementValue, isModifierKey } from '@zag-js/dom-query';
3
- import { roundToDpr, wrap, compact, isValueAtMin, isValueAtMax, isValueWithinRange, callAll, incrementValue, clampValue, decrementValue, isEqual } from '@zag-js/utils';
4
- import { createMachine, choose, ref, guards } from '@zag-js/core';
2
+ import { trackFormControl, observeAttributes, addDomEvent, isSafari, requestPointerLock, raf, setElementValue, MAX_Z_INDEX, dataAttr, ariaAttr, isComposingEvent, getEventStep, isLeftClick, getEventPoint, getWindow, isModifierKey } from '@zag-js/dom-query';
3
+ import { compact, isValueAtMin, isValueAtMax, isValueWithinRange, callAll, incrementValue, clampValue, decrementValue, createSplitProps, roundToDpr, wrap } from '@zag-js/utils';
4
+ import { setup, memo } from '@zag-js/core';
5
5
  import { NumberParser } from '@internationalized/number';
6
+ import { createProps } from '@zag-js/types';
6
7
 
7
8
  // src/number-input.anatomy.ts
8
9
  var anatomy = createAnatomy("numberInput").parts(
@@ -16,140 +17,136 @@ var anatomy = createAnatomy("numberInput").parts(
16
17
  "scrubber"
17
18
  );
18
19
  var parts = anatomy.build();
19
- var dom = createScope({
20
- getRootId: (ctx) => ctx.ids?.root ?? `number-input:${ctx.id}`,
21
- getInputId: (ctx) => ctx.ids?.input ?? `number-input:${ctx.id}:input`,
22
- getIncrementTriggerId: (ctx) => ctx.ids?.incrementTrigger ?? `number-input:${ctx.id}:inc`,
23
- getDecrementTriggerId: (ctx) => ctx.ids?.decrementTrigger ?? `number-input:${ctx.id}:dec`,
24
- getScrubberId: (ctx) => ctx.ids?.scrubber ?? `number-input:${ctx.id}:scrubber`,
25
- getCursorId: (ctx) => `number-input:${ctx.id}:cursor`,
26
- getLabelId: (ctx) => ctx.ids?.label ?? `number-input:${ctx.id}:label`,
27
- getInputEl: (ctx) => dom.getById(ctx, dom.getInputId(ctx)),
28
- getIncrementTriggerEl: (ctx) => dom.getById(ctx, dom.getIncrementTriggerId(ctx)),
29
- getDecrementTriggerEl: (ctx) => dom.getById(ctx, dom.getDecrementTriggerId(ctx)),
30
- getScrubberEl: (ctx) => dom.getById(ctx, dom.getScrubberId(ctx)),
31
- getCursorEl: (ctx) => dom.getDoc(ctx).getElementById(dom.getCursorId(ctx)),
32
- getPressedTriggerEl: (ctx, hint = ctx.hint) => {
33
- let btnEl = null;
34
- if (hint === "increment") {
35
- btnEl = dom.getIncrementTriggerEl(ctx);
20
+ var getRootId = (ctx) => ctx.ids?.root ?? `number-input:${ctx.id}`;
21
+ var getInputId = (ctx) => ctx.ids?.input ?? `number-input:${ctx.id}:input`;
22
+ var getIncrementTriggerId = (ctx) => ctx.ids?.incrementTrigger ?? `number-input:${ctx.id}:inc`;
23
+ var getDecrementTriggerId = (ctx) => ctx.ids?.decrementTrigger ?? `number-input:${ctx.id}:dec`;
24
+ var getScrubberId = (ctx) => ctx.ids?.scrubber ?? `number-input:${ctx.id}:scrubber`;
25
+ var getCursorId = (ctx) => `number-input:${ctx.id}:cursor`;
26
+ var getLabelId = (ctx) => ctx.ids?.label ?? `number-input:${ctx.id}:label`;
27
+ var getInputEl = (ctx) => ctx.getById(getInputId(ctx));
28
+ var getIncrementTriggerEl = (ctx) => ctx.getById(getIncrementTriggerId(ctx));
29
+ var getDecrementTriggerEl = (ctx) => ctx.getById(getDecrementTriggerId(ctx));
30
+ var getCursorEl = (ctx) => ctx.getDoc().getElementById(getCursorId(ctx));
31
+ var getPressedTriggerEl = (ctx, hint) => {
32
+ let btnEl = null;
33
+ if (hint === "increment") {
34
+ btnEl = getIncrementTriggerEl(ctx);
35
+ }
36
+ if (hint === "decrement") {
37
+ btnEl = getDecrementTriggerEl(ctx);
38
+ }
39
+ return btnEl;
40
+ };
41
+ var setupVirtualCursor = (ctx, point) => {
42
+ if (isSafari()) return;
43
+ createVirtualCursor(ctx, point);
44
+ return () => {
45
+ getCursorEl(ctx)?.remove();
46
+ };
47
+ };
48
+ var preventTextSelection = (ctx) => {
49
+ const doc = ctx.getDoc();
50
+ const html = doc.documentElement;
51
+ const body = doc.body;
52
+ body.style.pointerEvents = "none";
53
+ html.style.userSelect = "none";
54
+ html.style.cursor = "ew-resize";
55
+ return () => {
56
+ body.style.pointerEvents = "";
57
+ html.style.userSelect = "";
58
+ html.style.cursor = "";
59
+ if (!html.style.length) {
60
+ html.removeAttribute("style");
36
61
  }
37
- if (hint === "decrement") {
38
- btnEl = dom.getDecrementTriggerEl(ctx);
62
+ if (!body.style.length) {
63
+ body.removeAttribute("style");
39
64
  }
40
- return btnEl;
41
- },
42
- setupVirtualCursor(ctx) {
43
- if (isSafari()) return;
44
- dom.createVirtualCursor(ctx);
45
- return () => {
46
- dom.getCursorEl(ctx)?.remove();
47
- };
48
- },
49
- preventTextSelection(ctx) {
50
- const doc = dom.getDoc(ctx);
51
- const html = doc.documentElement;
52
- const body = doc.body;
53
- body.style.pointerEvents = "none";
54
- html.style.userSelect = "none";
55
- html.style.cursor = "ew-resize";
56
- return () => {
57
- body.style.pointerEvents = "";
58
- html.style.userSelect = "";
59
- html.style.cursor = "";
60
- if (!html.style.length) {
61
- html.removeAttribute("style");
62
- }
63
- if (!body.style.length) {
64
- body.removeAttribute("style");
65
- }
66
- };
67
- },
68
- getMousemoveValue(ctx, event) {
69
- const win = dom.getWin(ctx);
70
- const x = roundToDpr(event.movementX, win.devicePixelRatio);
71
- const y = roundToDpr(event.movementY, win.devicePixelRatio);
72
- let hint = x > 0 ? "increment" : x < 0 ? "decrement" : null;
73
- if (ctx.isRtl && hint === "increment") hint = "decrement";
74
- if (ctx.isRtl && hint === "decrement") hint = "increment";
75
- const point = {
76
- x: ctx.scrubberCursorPoint.x + x,
77
- y: ctx.scrubberCursorPoint.y + y
78
- };
79
- const width = win.innerWidth;
80
- const half = roundToDpr(7.5, win.devicePixelRatio);
81
- point.x = wrap(point.x + half, width) - half;
82
- return { hint, point };
83
- },
84
- createVirtualCursor(ctx) {
85
- const doc = dom.getDoc(ctx);
86
- const el = doc.createElement("div");
87
- el.className = "scrubber--cursor";
88
- el.id = dom.getCursorId(ctx);
89
- Object.assign(el.style, {
90
- width: "15px",
91
- height: "15px",
92
- position: "fixed",
93
- pointerEvents: "none",
94
- left: "0px",
95
- top: "0px",
96
- zIndex: MAX_Z_INDEX,
97
- transform: ctx.scrubberCursorPoint ? `translate3d(${ctx.scrubberCursorPoint.x}px, ${ctx.scrubberCursorPoint.y}px, 0px)` : void 0,
98
- willChange: "transform"
99
- });
100
- el.innerHTML = `
101
- <svg width="46" height="15" style="left: -15.5px; position: absolute; top: 0; filter: drop-shadow(rgba(0, 0, 0, 0.4) 0px 1px 1.1px);">
102
- <g transform="translate(2 3)">
103
- <path fill-rule="evenodd" d="M 15 4.5L 15 2L 11.5 5.5L 15 9L 15 6.5L 31 6.5L 31 9L 34.5 5.5L 31 2L 31 4.5Z" style="stroke-width: 2px; stroke: white;"></path>
104
- <path fill-rule="evenodd" d="M 15 4.5L 15 2L 11.5 5.5L 15 9L 15 6.5L 31 6.5L 31 9L 34.5 5.5L 31 2L 31 4.5Z"></path>
105
- </g>
106
- </svg>`;
107
- doc.body.appendChild(el);
108
- }
109
- });
65
+ };
66
+ };
67
+ var getMousemoveValue = (ctx, opts) => {
68
+ const { point, isRtl, event } = opts;
69
+ const win = ctx.getWin();
70
+ const x = roundToDpr(event.movementX, win.devicePixelRatio);
71
+ const y = roundToDpr(event.movementY, win.devicePixelRatio);
72
+ let hint = x > 0 ? "increment" : x < 0 ? "decrement" : null;
73
+ if (isRtl && hint === "increment") hint = "decrement";
74
+ if (isRtl && hint === "decrement") hint = "increment";
75
+ const newPoint = { x: point.x + x, y: point.y + y };
76
+ const width = win.innerWidth;
77
+ const half = roundToDpr(7.5, win.devicePixelRatio);
78
+ newPoint.x = wrap(newPoint.x + half, width) - half;
79
+ return { hint, point: newPoint };
80
+ };
81
+ var createVirtualCursor = (ctx, point) => {
82
+ const doc = ctx.getDoc();
83
+ const el = doc.createElement("div");
84
+ el.className = "scrubber--cursor";
85
+ el.id = getCursorId(ctx);
86
+ Object.assign(el.style, {
87
+ width: "15px",
88
+ height: "15px",
89
+ position: "fixed",
90
+ pointerEvents: "none",
91
+ left: "0px",
92
+ top: "0px",
93
+ zIndex: MAX_Z_INDEX,
94
+ transform: point ? `translate3d(${point.x}px, ${point.y}px, 0px)` : void 0,
95
+ willChange: "transform"
96
+ });
97
+ el.innerHTML = `
98
+ <svg width="46" height="15" style="left: -15.5px; position: absolute; top: 0; filter: drop-shadow(rgba(0, 0, 0, 0.4) 0px 1px 1.1px);">
99
+ <g transform="translate(2 3)">
100
+ <path fill-rule="evenodd" d="M 15 4.5L 15 2L 11.5 5.5L 15 9L 15 6.5L 31 6.5L 31 9L 34.5 5.5L 31 2L 31 4.5Z" style="stroke-width: 2px; stroke: white;"></path>
101
+ <path fill-rule="evenodd" d="M 15 4.5L 15 2L 11.5 5.5L 15 9L 15 6.5L 31 6.5L 31 9L 34.5 5.5L 31 2L 31 4.5Z"></path>
102
+ </g>
103
+ </svg>`;
104
+ doc.body.appendChild(el);
105
+ };
110
106
 
111
107
  // src/number-input.connect.ts
112
- function connect(state, send, normalize) {
108
+ function connect(service, normalize) {
109
+ const { state, send, prop, scope, computed } = service;
113
110
  const focused = state.hasTag("focus");
114
- const disabled = state.context.isDisabled;
115
- const readOnly = state.context.readOnly;
116
- const empty = state.context.isValueEmpty;
117
- const invalid = state.context.isOutOfRange || !!state.context.invalid;
118
- const isIncrementDisabled = disabled || !state.context.canIncrement || readOnly;
119
- const isDecrementDisabled = disabled || !state.context.canDecrement || readOnly;
120
- const translations = state.context.translations;
111
+ const disabled = computed("isDisabled");
112
+ const readOnly = prop("readOnly");
113
+ const empty = computed("isValueEmpty");
114
+ const invalid = computed("isOutOfRange") || !!prop("invalid");
115
+ const isIncrementDisabled = disabled || !computed("canIncrement") || readOnly;
116
+ const isDecrementDisabled = disabled || !computed("canDecrement") || readOnly;
117
+ const translations = prop("translations");
121
118
  return {
122
119
  focused,
123
120
  invalid,
124
121
  empty,
125
- value: state.context.formattedValue,
126
- valueAsNumber: state.context.valueAsNumber,
122
+ value: computed("formattedValue"),
123
+ valueAsNumber: computed("valueAsNumber"),
127
124
  setValue(value) {
128
125
  send({ type: "VALUE.SET", value });
129
126
  },
130
127
  clearValue() {
131
- send("VALUE.CLEAR");
128
+ send({ type: "VALUE.CLEAR" });
132
129
  },
133
130
  increment() {
134
- send("VALUE.INCREMENT");
131
+ send({ type: "VALUE.INCREMENT" });
135
132
  },
136
133
  decrement() {
137
- send("VALUE.DECREMENT");
134
+ send({ type: "VALUE.DECREMENT" });
138
135
  },
139
136
  setToMax() {
140
- send({ type: "VALUE.SET", value: state.context.max });
137
+ send({ type: "VALUE.SET", value: prop("max") });
141
138
  },
142
139
  setToMin() {
143
- send({ type: "VALUE.SET", value: state.context.min });
140
+ send({ type: "VALUE.SET", value: prop("min") });
144
141
  },
145
142
  focus() {
146
- dom.getInputEl(state.context)?.focus();
143
+ getInputEl(scope)?.focus();
147
144
  },
148
145
  getRootProps() {
149
146
  return normalize.element({
150
- id: dom.getRootId(state.context),
147
+ id: getRootId(scope),
151
148
  ...parts.root.attrs,
152
- dir: state.context.dir,
149
+ dir: prop("dir"),
153
150
  "data-disabled": dataAttr(disabled),
154
151
  "data-focus": dataAttr(focused),
155
152
  "data-invalid": dataAttr(invalid)
@@ -158,30 +155,30 @@ function connect(state, send, normalize) {
158
155
  getLabelProps() {
159
156
  return normalize.label({
160
157
  ...parts.label.attrs,
161
- dir: state.context.dir,
158
+ dir: prop("dir"),
162
159
  "data-disabled": dataAttr(disabled),
163
160
  "data-focus": dataAttr(focused),
164
161
  "data-invalid": dataAttr(invalid),
165
- id: dom.getLabelId(state.context),
166
- htmlFor: dom.getInputId(state.context)
162
+ id: getLabelId(scope),
163
+ htmlFor: getInputId(scope)
167
164
  });
168
165
  },
169
166
  getControlProps() {
170
167
  return normalize.element({
171
168
  ...parts.control.attrs,
172
- dir: state.context.dir,
169
+ dir: prop("dir"),
173
170
  role: "group",
174
171
  "aria-disabled": disabled,
175
172
  "data-focus": dataAttr(focused),
176
173
  "data-disabled": dataAttr(disabled),
177
174
  "data-invalid": dataAttr(invalid),
178
- "aria-invalid": ariaAttr(state.context.invalid)
175
+ "aria-invalid": ariaAttr(invalid)
179
176
  });
180
177
  },
181
178
  getValueTextProps() {
182
179
  return normalize.element({
183
180
  ...parts.valueText.attrs,
184
- dir: state.context.dir,
181
+ dir: prop("dir"),
185
182
  "data-disabled": dataAttr(disabled),
186
183
  "data-invalid": dataAttr(invalid),
187
184
  "data-focus": dataAttr(focused)
@@ -190,34 +187,34 @@ function connect(state, send, normalize) {
190
187
  getInputProps() {
191
188
  return normalize.input({
192
189
  ...parts.input.attrs,
193
- dir: state.context.dir,
194
- name: state.context.name,
195
- form: state.context.form,
196
- id: dom.getInputId(state.context),
190
+ dir: prop("dir"),
191
+ name: prop("name"),
192
+ form: prop("form"),
193
+ id: getInputId(scope),
197
194
  role: "spinbutton",
198
- defaultValue: state.context.formattedValue,
199
- pattern: state.context.pattern,
200
- inputMode: state.context.inputMode,
195
+ defaultValue: computed("formattedValue"),
196
+ pattern: prop("pattern"),
197
+ inputMode: prop("inputMode"),
201
198
  "aria-invalid": ariaAttr(invalid),
202
199
  "data-invalid": dataAttr(invalid),
203
200
  disabled,
204
201
  "data-disabled": dataAttr(disabled),
205
- readOnly: state.context.readOnly,
206
- required: state.context.required,
202
+ readOnly,
203
+ required: prop("required"),
207
204
  autoComplete: "off",
208
205
  autoCorrect: "off",
209
206
  spellCheck: "false",
210
207
  type: "text",
211
208
  "aria-roledescription": "numberfield",
212
- "aria-valuemin": state.context.min,
213
- "aria-valuemax": state.context.max,
214
- "aria-valuenow": Number.isNaN(state.context.valueAsNumber) ? void 0 : state.context.valueAsNumber,
215
- "aria-valuetext": state.context.valueText,
209
+ "aria-valuemin": prop("min"),
210
+ "aria-valuemax": prop("max"),
211
+ "aria-valuenow": Number.isNaN(computed("valueAsNumber")) ? void 0 : computed("valueAsNumber"),
212
+ "aria-valuetext": computed("valueText"),
216
213
  onFocus() {
217
- send("INPUT.FOCUS");
214
+ send({ type: "INPUT.FOCUS" });
218
215
  },
219
216
  onBlur() {
220
- send("INPUT.BLUR");
217
+ send({ type: "INPUT.BLUR" });
221
218
  },
222
219
  onInput(event) {
223
220
  send({ type: "INPUT.CHANGE", target: event.currentTarget, hint: "set" });
@@ -226,7 +223,7 @@ function connect(state, send, normalize) {
226
223
  try {
227
224
  const { selectionStart, selectionEnd, value } = event.currentTarget;
228
225
  const nextValue = value.slice(0, selectionStart) + (event.data ?? "") + value.slice(selectionEnd);
229
- const isValid = state.context.parser.isValidPartialNumber(nextValue);
226
+ const isValid = computed("parser").isValidPartialNumber(nextValue);
230
227
  if (!isValid) {
231
228
  event.preventDefault();
232
229
  }
@@ -237,7 +234,7 @@ function connect(state, send, normalize) {
237
234
  if (event.defaultPrevented) return;
238
235
  if (readOnly) return;
239
236
  if (isComposingEvent(event)) return;
240
- const step = getEventStep(event) * state.context.step;
237
+ const step = getEventStep(event) * prop("step");
241
238
  const keyMap = {
242
239
  ArrowUp() {
243
240
  send({ type: "INPUT.ARROW_UP", step });
@@ -249,16 +246,16 @@ function connect(state, send, normalize) {
249
246
  },
250
247
  Home() {
251
248
  if (isModifierKey(event)) return;
252
- send("INPUT.HOME");
249
+ send({ type: "INPUT.HOME" });
253
250
  event.preventDefault();
254
251
  },
255
252
  End() {
256
253
  if (isModifierKey(event)) return;
257
- send("INPUT.END");
254
+ send({ type: "INPUT.END" });
258
255
  event.preventDefault();
259
256
  },
260
257
  Enter() {
261
- send("INPUT.ENTER");
258
+ send({ type: "INPUT.ENTER" });
262
259
  }
263
260
  };
264
261
  const exec = keyMap[event.key];
@@ -269,14 +266,14 @@ function connect(state, send, normalize) {
269
266
  getDecrementTriggerProps() {
270
267
  return normalize.button({
271
268
  ...parts.decrementTrigger.attrs,
272
- dir: state.context.dir,
273
- id: dom.getDecrementTriggerId(state.context),
269
+ dir: prop("dir"),
270
+ id: getDecrementTriggerId(scope),
274
271
  disabled: isDecrementDisabled,
275
272
  "data-disabled": dataAttr(isDecrementDisabled),
276
273
  "aria-label": translations.decrementLabel,
277
274
  type: "button",
278
275
  tabIndex: -1,
279
- "aria-controls": dom.getInputId(state.context),
276
+ "aria-controls": getInputId(scope),
280
277
  onPointerDown(event) {
281
278
  if (isDecrementDisabled || !isLeftClick(event)) return;
282
279
  send({ type: "TRIGGER.PRESS_DOWN", hint: "decrement", pointerType: event.pointerType });
@@ -299,14 +296,14 @@ function connect(state, send, normalize) {
299
296
  getIncrementTriggerProps() {
300
297
  return normalize.button({
301
298
  ...parts.incrementTrigger.attrs,
302
- dir: state.context.dir,
303
- id: dom.getIncrementTriggerId(state.context),
299
+ dir: prop("dir"),
300
+ id: getIncrementTriggerId(scope),
304
301
  disabled: isIncrementDisabled,
305
302
  "data-disabled": dataAttr(isIncrementDisabled),
306
303
  "aria-label": translations.incrementLabel,
307
304
  type: "button",
308
305
  tabIndex: -1,
309
- "aria-controls": dom.getInputId(state.context),
306
+ "aria-controls": getInputId(scope),
310
307
  onPointerDown(event) {
311
308
  if (isIncrementDisabled || !isLeftClick(event)) return;
312
309
  send({ type: "TRIGGER.PRESS_DOWN", hint: "increment", pointerType: event.pointerType });
@@ -328,9 +325,9 @@ function connect(state, send, normalize) {
328
325
  getScrubberProps() {
329
326
  return normalize.element({
330
327
  ...parts.scrubber.attrs,
331
- dir: state.context.dir,
328
+ dir: prop("dir"),
332
329
  "data-disabled": dataAttr(disabled),
333
- id: dom.getScrubberId(state.context),
330
+ id: getScrubberId(scope),
334
331
  role: "presentation",
335
332
  onMouseDown(event) {
336
333
  if (disabled) return;
@@ -352,7 +349,7 @@ function connect(state, send, normalize) {
352
349
 
353
350
  // src/cursor.ts
354
351
  function recordCursor(inputEl) {
355
- if (inputEl.ownerDocument.activeElement !== inputEl) return;
352
+ if (!inputEl || inputEl.ownerDocument.activeElement !== inputEl) return;
356
353
  try {
357
354
  const { selectionStart: start, selectionEnd: end, value } = inputEl;
358
355
  const beforeTxt = value.substring(0, start);
@@ -368,7 +365,7 @@ function recordCursor(inputEl) {
368
365
  }
369
366
  }
370
367
  function restoreCursor(inputEl, selection) {
371
- if (inputEl.ownerDocument.activeElement !== inputEl) return;
368
+ if (!inputEl || inputEl.ownerDocument.activeElement !== inputEl) return;
372
369
  if (!selection) {
373
370
  inputEl.setSelectionRange(inputEl.value.length, inputEl.value.length);
374
371
  return;
@@ -393,430 +390,460 @@ function restoreCursor(inputEl, selection) {
393
390
  }
394
391
  }
395
392
  var createFormatter = (locale, options = {}) => {
396
- return ref(new Intl.NumberFormat(locale, options));
393
+ return new Intl.NumberFormat(locale, options);
397
394
  };
398
395
  var createParser = (locale, options = {}) => {
399
- return ref(new NumberParser(locale, options));
396
+ return new NumberParser(locale, options);
400
397
  };
401
- var parseValue = (ctx, value) => {
402
- if (!ctx.formatOptions) return parseFloat(value);
403
- return ctx.parser.parse(String(value));
398
+ var parseValue = (value, params) => {
399
+ const { prop, computed } = params;
400
+ if (!prop("formatOptions")) return parseFloat(value);
401
+ return computed("parser").parse(String(value));
404
402
  };
405
- var formatValue = (ctx, value) => {
403
+ var formatValue = (value, params) => {
404
+ const { prop, computed } = params;
406
405
  if (Number.isNaN(value)) return "";
407
- if (!ctx.formatOptions) return value.toString();
408
- return ctx.formatter.format(value);
406
+ if (!prop("formatOptions")) return value.toString();
407
+ return computed("formatter").format(value);
409
408
  };
410
409
 
411
410
  // src/number-input.machine.ts
411
+ var { choose, guards, createMachine } = setup();
412
412
  var { not, and } = guards;
413
- function machine(userContext) {
414
- const ctx = compact(userContext);
415
- return createMachine(
416
- {
417
- id: "number-input",
418
- initial: "idle",
419
- context: {
420
- dir: "ltr",
421
- locale: "en-US",
422
- focusInputOnChange: true,
423
- clampValueOnBlur: true,
424
- allowOverflow: false,
425
- inputMode: "decimal",
426
- pattern: "[0-9]*(.[0-9]+)?",
427
- value: "",
428
- step: 1,
429
- min: Number.MIN_SAFE_INTEGER,
430
- max: Number.MAX_SAFE_INTEGER,
431
- invalid: false,
432
- spinOnPress: true,
433
- disabled: false,
434
- readOnly: false,
435
- ...ctx,
436
- hint: null,
437
- scrubberCursorPoint: null,
438
- fieldsetDisabled: false,
439
- formatter: createFormatter(ctx.locale || "en-US", ctx.formatOptions),
440
- parser: createParser(ctx.locale || "en-US", ctx.formatOptions),
441
- translations: {
442
- incrementLabel: "increment value",
443
- decrementLabel: "decrease value",
444
- ...ctx.translations
413
+ var machine = createMachine({
414
+ props({ props: props2 }) {
415
+ return {
416
+ dir: "ltr",
417
+ locale: "en-US",
418
+ focusInputOnChange: true,
419
+ clampValueOnBlur: true,
420
+ allowOverflow: false,
421
+ inputMode: "decimal",
422
+ pattern: "[0-9]*(.[0-9]+)?",
423
+ defaultValue: "",
424
+ step: 1,
425
+ min: Number.MIN_SAFE_INTEGER,
426
+ max: Number.MAX_SAFE_INTEGER,
427
+ spinOnPress: true,
428
+ ...compact(props2),
429
+ translations: {
430
+ incrementLabel: "increment value",
431
+ decrementLabel: "decrease value",
432
+ ...props2.translations
433
+ }
434
+ };
435
+ },
436
+ initialState() {
437
+ return "idle";
438
+ },
439
+ context({ prop, bindable, getComputed }) {
440
+ return {
441
+ value: bindable(() => ({
442
+ defaultValue: prop("defaultValue"),
443
+ prop: prop("value"),
444
+ onChange(value) {
445
+ const computed = getComputed();
446
+ prop("onValueChange")?.({ value, valueAsNumber: computed("valueAsNumber") });
445
447
  }
446
- },
447
- computed: {
448
- isRtl: (ctx2) => ctx2.dir === "rtl",
449
- valueAsNumber: (ctx2) => parseValue(ctx2, ctx2.value),
450
- formattedValue: (ctx2) => formatValue(ctx2, ctx2.valueAsNumber),
451
- isAtMin: (ctx2) => isValueAtMin(ctx2.valueAsNumber, ctx2.min),
452
- isAtMax: (ctx2) => isValueAtMax(ctx2.valueAsNumber, ctx2.max),
453
- isOutOfRange: (ctx2) => !isValueWithinRange(ctx2.valueAsNumber, ctx2.min, ctx2.max),
454
- isValueEmpty: (ctx2) => ctx2.value === "",
455
- isDisabled: (ctx2) => !!ctx2.disabled || ctx2.fieldsetDisabled,
456
- canIncrement: (ctx2) => ctx2.allowOverflow || !ctx2.isAtMax,
457
- canDecrement: (ctx2) => ctx2.allowOverflow || !ctx2.isAtMin,
458
- valueText: (ctx2) => ctx2.translations.valueText?.(ctx2.value)
459
- },
460
- watch: {
461
- formatOptions: ["setFormatterAndParser", "syncInputElement"],
462
- locale: ["setFormatterAndParser", "syncInputElement"],
463
- value: ["syncInputElement"],
464
- isOutOfRange: ["invokeOnInvalid"],
465
- scrubberCursorPoint: ["setVirtualCursorPosition"]
466
- },
467
- activities: ["trackFormControl"],
448
+ })),
449
+ hint: bindable(() => ({ defaultValue: null })),
450
+ scrubberCursorPoint: bindable(() => ({
451
+ defaultValue: null,
452
+ hash(value) {
453
+ return value ? `x:${value.x}, y:${value.y}` : "";
454
+ }
455
+ })),
456
+ fieldsetDisabled: bindable(() => ({ defaultValue: false }))
457
+ };
458
+ },
459
+ computed: {
460
+ isRtl: ({ prop }) => prop("dir") === "rtl",
461
+ valueAsNumber: ({ context, computed, prop }) => parseValue(context.get("value"), { computed, prop }),
462
+ formattedValue: ({ computed, prop }) => formatValue(computed("valueAsNumber"), { computed, prop }),
463
+ isAtMin: ({ computed, prop }) => isValueAtMin(computed("valueAsNumber"), prop("min")),
464
+ isAtMax: ({ computed, prop }) => isValueAtMax(computed("valueAsNumber"), prop("max")),
465
+ isOutOfRange: ({ computed, prop }) => !isValueWithinRange(computed("valueAsNumber"), prop("min"), prop("max")),
466
+ isValueEmpty: ({ context }) => context.get("value") === "",
467
+ isDisabled: ({ prop, context }) => !!prop("disabled") || context.get("fieldsetDisabled"),
468
+ canIncrement: ({ prop, computed }) => prop("allowOverflow") || !computed("isAtMax"),
469
+ canDecrement: ({ prop, computed }) => prop("allowOverflow") || !computed("isAtMin"),
470
+ valueText: ({ prop, context }) => prop("translations").valueText?.(context.get("value")),
471
+ formatter: memo(
472
+ ({ prop }) => [prop("locale"), prop("formatOptions")],
473
+ (locale, formatOptions) => createFormatter(locale, formatOptions)
474
+ ),
475
+ parser: memo(
476
+ ({ prop }) => [prop("locale"), prop("formatOptions")],
477
+ (locale, formatOptions) => createParser(locale, formatOptions)
478
+ )
479
+ },
480
+ watch({ track, action, context, computed, prop }) {
481
+ track([() => context.get("value"), () => prop("locale")], () => {
482
+ action(["syncInputElement"]);
483
+ });
484
+ track([() => computed("isOutOfRange")], () => {
485
+ action(["invokeOnInvalid"]);
486
+ });
487
+ track([() => context.hash("scrubberCursorPoint")], () => {
488
+ action(["setVirtualCursorPosition"]);
489
+ });
490
+ },
491
+ effects: ["trackFormControl"],
492
+ on: {
493
+ "VALUE.SET": {
494
+ actions: ["setRawValue", "setHintToSet"]
495
+ },
496
+ "VALUE.CLEAR": {
497
+ actions: ["clearValue"]
498
+ },
499
+ "VALUE.INCREMENT": {
500
+ actions: ["increment"]
501
+ },
502
+ "VALUE.DECREMENT": {
503
+ actions: ["decrement"]
504
+ }
505
+ },
506
+ states: {
507
+ idle: {
468
508
  on: {
469
- "VALUE.SET": {
470
- actions: ["setRawValue", "setHintToSet"]
471
- },
472
- "VALUE.CLEAR": {
473
- actions: ["clearValue"]
474
- },
475
- "VALUE.INCREMENT": {
509
+ "TRIGGER.PRESS_DOWN": [
510
+ { guard: "isTouchPointer", target: "before:spin", actions: ["setHint"] },
511
+ {
512
+ target: "before:spin",
513
+ actions: ["focusInput", "invokeOnFocus", "setHint"]
514
+ }
515
+ ],
516
+ "SCRUBBER.PRESS_DOWN": {
517
+ target: "scrubbing",
518
+ actions: ["focusInput", "invokeOnFocus", "setHint", "setCursorPoint"]
519
+ },
520
+ "INPUT.FOCUS": {
521
+ target: "focused",
522
+ actions: ["focusInput", "invokeOnFocus"]
523
+ }
524
+ }
525
+ },
526
+ focused: {
527
+ tags: ["focus"],
528
+ effects: ["attachWheelListener"],
529
+ on: {
530
+ "TRIGGER.PRESS_DOWN": [
531
+ { guard: "isTouchPointer", target: "before:spin", actions: ["setHint"] },
532
+ { target: "before:spin", actions: ["focusInput", "setHint"] }
533
+ ],
534
+ "SCRUBBER.PRESS_DOWN": {
535
+ target: "scrubbing",
536
+ actions: ["focusInput", "setHint", "setCursorPoint"]
537
+ },
538
+ "INPUT.ARROW_UP": {
476
539
  actions: ["increment"]
477
540
  },
478
- "VALUE.DECREMENT": {
541
+ "INPUT.ARROW_DOWN": {
479
542
  actions: ["decrement"]
480
- }
481
- },
482
- states: {
483
- idle: {
484
- on: {
485
- "TRIGGER.PRESS_DOWN": [
486
- { guard: "isTouchPointer", target: "before:spin", actions: ["setHint"] },
487
- {
488
- target: "before:spin",
489
- actions: ["focusInput", "invokeOnFocus", "setHint"]
490
- }
491
- ],
492
- "SCRUBBER.PRESS_DOWN": {
493
- target: "scrubbing",
494
- actions: ["focusInput", "invokeOnFocus", "setHint", "setCursorPoint"]
495
- },
496
- "INPUT.FOCUS": {
497
- target: "focused",
498
- actions: ["focusInput", "invokeOnFocus"]
499
- }
500
- }
501
543
  },
502
- focused: {
503
- tags: "focus",
504
- activities: "attachWheelListener",
505
- on: {
506
- "TRIGGER.PRESS_DOWN": [
507
- { guard: "isTouchPointer", target: "before:spin", actions: ["setHint"] },
508
- { target: "before:spin", actions: ["focusInput", "setHint"] }
509
- ],
510
- "SCRUBBER.PRESS_DOWN": {
511
- target: "scrubbing",
512
- actions: ["focusInput", "setHint", "setCursorPoint"]
513
- },
514
- "INPUT.ARROW_UP": {
515
- actions: "increment"
516
- },
517
- "INPUT.ARROW_DOWN": {
518
- actions: "decrement"
519
- },
520
- "INPUT.HOME": {
521
- actions: "decrementToMin"
522
- },
523
- "INPUT.END": {
524
- actions: "incrementToMax"
525
- },
526
- "INPUT.CHANGE": {
527
- actions: ["setValue", "setHint"]
528
- },
529
- "INPUT.BLUR": [
530
- {
531
- guard: and("clampValueOnBlur", not("isInRange")),
532
- target: "idle",
533
- actions: ["setClampedValue", "clearHint", "invokeOnBlur"]
534
- },
535
- {
536
- target: "idle",
537
- actions: ["setFormattedValue", "clearHint", "invokeOnBlur"]
538
- }
539
- ],
540
- "INPUT.ENTER": {
541
- actions: ["setFormattedValue", "clearHint", "invokeOnBlur"]
542
- }
543
- }
544
+ "INPUT.HOME": {
545
+ actions: ["decrementToMin"]
544
546
  },
545
- "before:spin": {
546
- tags: "focus",
547
- activities: "trackButtonDisabled",
548
- entry: choose([
549
- { guard: "isIncrementHint", actions: "increment" },
550
- { guard: "isDecrementHint", actions: "decrement" }
551
- ]),
552
- after: {
553
- CHANGE_DELAY: {
554
- target: "spinning",
555
- guard: and("isInRange", "spinOnPress")
556
- }
557
- },
558
- on: {
559
- "TRIGGER.PRESS_UP": [
560
- { guard: "isTouchPointer", target: "focused", actions: "clearHint" },
561
- { target: "focused", actions: ["focusInput", "clearHint"] }
562
- ]
563
- }
547
+ "INPUT.END": {
548
+ actions: ["incrementToMax"]
564
549
  },
565
- spinning: {
566
- tags: "focus",
567
- activities: "trackButtonDisabled",
568
- every: [
569
- {
570
- delay: "CHANGE_INTERVAL",
571
- guard: and(not("isAtMin"), "isIncrementHint"),
572
- actions: "increment"
573
- },
574
- {
575
- delay: "CHANGE_INTERVAL",
576
- guard: and(not("isAtMax"), "isDecrementHint"),
577
- actions: "decrement"
578
- }
579
- ],
580
- on: {
581
- "TRIGGER.PRESS_UP": {
582
- target: "focused",
583
- actions: ["focusInput", "clearHint"]
584
- }
585
- }
550
+ "INPUT.CHANGE": {
551
+ actions: ["setValue", "setHint"]
586
552
  },
587
- scrubbing: {
588
- tags: "focus",
589
- activities: ["activatePointerLock", "trackMousemove", "setupVirtualCursor", "preventTextSelection"],
590
- on: {
591
- "SCRUBBER.POINTER_UP": {
592
- target: "focused",
593
- actions: ["focusInput", "clearCursorPoint"]
594
- },
595
- "SCRUBBER.POINTER_MOVE": [
596
- {
597
- guard: "isIncrementHint",
598
- actions: ["increment", "setCursorPoint"]
599
- },
600
- {
601
- guard: "isDecrementHint",
602
- actions: ["decrement", "setCursorPoint"]
603
- }
604
- ]
553
+ "INPUT.BLUR": [
554
+ {
555
+ guard: and("clampValueOnBlur", not("isInRange")),
556
+ target: "idle",
557
+ actions: ["setClampedValue", "clearHint", "invokeOnBlur"]
558
+ },
559
+ {
560
+ target: "idle",
561
+ actions: ["setFormattedValue", "clearHint", "invokeOnBlur"]
562
+ }
563
+ ],
564
+ "INPUT.ENTER": {
565
+ actions: ["setFormattedValue", "clearHint", "invokeOnBlur"]
566
+ }
567
+ }
568
+ },
569
+ "before:spin": {
570
+ tags: ["focus"],
571
+ effects: ["trackButtonDisabled", "waitForChangeDelay"],
572
+ entry: choose([
573
+ { guard: "isIncrementHint", actions: ["increment"] },
574
+ { guard: "isDecrementHint", actions: ["decrement"] }
575
+ ]),
576
+ on: {
577
+ CHANGE_DELAY: {
578
+ target: "spinning",
579
+ guard: and("isInRange", "spinOnPress")
580
+ },
581
+ "TRIGGER.PRESS_UP": [
582
+ { guard: "isTouchPointer", target: "focused", actions: ["clearHint"] },
583
+ { target: "focused", actions: ["focusInput", "clearHint"] }
584
+ ]
585
+ }
586
+ },
587
+ spinning: {
588
+ tags: ["focus"],
589
+ effects: ["trackButtonDisabled", "spinValue"],
590
+ on: {
591
+ SPIN: [
592
+ {
593
+ guard: and(not("isAtMin"), "isIncrementHint"),
594
+ actions: ["increment"]
595
+ },
596
+ {
597
+ guard: and(not("isAtMax"), "isDecrementHint"),
598
+ actions: ["decrement"]
605
599
  }
600
+ ],
601
+ "TRIGGER.PRESS_UP": {
602
+ target: "focused",
603
+ actions: ["focusInput", "clearHint"]
606
604
  }
607
605
  }
608
606
  },
609
- {
610
- delays: {
611
- CHANGE_INTERVAL: 50,
612
- CHANGE_DELAY: 300
607
+ scrubbing: {
608
+ tags: ["focus"],
609
+ effects: ["activatePointerLock", "trackMousemove", "setupVirtualCursor", "preventTextSelection"],
610
+ on: {
611
+ "SCRUBBER.POINTER_UP": {
612
+ target: "focused",
613
+ actions: ["focusInput", "clearCursorPoint"]
614
+ },
615
+ "SCRUBBER.POINTER_MOVE": [
616
+ {
617
+ guard: "isIncrementHint",
618
+ actions: ["increment", "setCursorPoint"]
619
+ },
620
+ {
621
+ guard: "isDecrementHint",
622
+ actions: ["decrement", "setCursorPoint"]
623
+ }
624
+ ]
625
+ }
626
+ }
627
+ },
628
+ implementations: {
629
+ guards: {
630
+ clampValueOnBlur: ({ prop }) => prop("clampValueOnBlur"),
631
+ isAtMin: ({ computed }) => computed("isAtMin"),
632
+ spinOnPress: ({ prop }) => !!prop("spinOnPress"),
633
+ isAtMax: ({ computed }) => computed("isAtMax"),
634
+ isInRange: ({ computed }) => !computed("isOutOfRange"),
635
+ isDecrementHint: ({ context, event }) => (event.hint ?? context.get("hint")) === "decrement",
636
+ isIncrementHint: ({ context, event }) => (event.hint ?? context.get("hint")) === "increment",
637
+ isTouchPointer: ({ event }) => event.pointerType === "touch"
638
+ },
639
+ effects: {
640
+ waitForChangeDelay({ send }) {
641
+ const id = setTimeout(() => {
642
+ send({ type: "CHANGE_DELAY" });
643
+ }, 300);
644
+ return () => clearTimeout(id);
613
645
  },
614
- guards: {
615
- clampValueOnBlur: (ctx2) => ctx2.clampValueOnBlur,
616
- isAtMin: (ctx2) => ctx2.isAtMin,
617
- spinOnPress: (ctx2) => !!ctx2.spinOnPress,
618
- isAtMax: (ctx2) => ctx2.isAtMax,
619
- isInRange: (ctx2) => !ctx2.isOutOfRange,
620
- isDecrementHint: (ctx2, evt) => (evt.hint ?? ctx2.hint) === "decrement",
621
- isIncrementHint: (ctx2, evt) => (evt.hint ?? ctx2.hint) === "increment",
622
- isTouchPointer: (_ctx, evt) => evt.pointerType === "touch"
646
+ spinValue({ send }) {
647
+ const id = setInterval(() => {
648
+ send({ type: "SPIN" });
649
+ }, 50);
650
+ return () => clearInterval(id);
623
651
  },
624
- activities: {
625
- trackFormControl(ctx2, _evt, { initialContext }) {
626
- const inputEl = dom.getInputEl(ctx2);
627
- return trackFormControl(inputEl, {
628
- onFieldsetDisabledChange(disabled) {
629
- ctx2.fieldsetDisabled = disabled;
630
- },
631
- onFormReset() {
632
- set.value(ctx2, initialContext.value);
633
- }
634
- });
635
- },
636
- setupVirtualCursor(ctx2) {
637
- return dom.setupVirtualCursor(ctx2);
638
- },
639
- preventTextSelection(ctx2) {
640
- return dom.preventTextSelection(ctx2);
641
- },
642
- trackButtonDisabled(ctx2, _evt, { send }) {
643
- const btn = dom.getPressedTriggerEl(ctx2, ctx2.hint);
644
- return observeAttributes(btn, {
645
- attributes: ["disabled"],
646
- callback() {
647
- send({ type: "TRIGGER.PRESS_UP", src: "attr" });
648
- }
649
- });
650
- },
651
- attachWheelListener(ctx2, _evt, { send }) {
652
- const inputEl = dom.getInputEl(ctx2);
653
- if (!inputEl || !dom.isActiveElement(ctx2, inputEl) || !ctx2.allowMouseWheel) return;
654
- function onWheel(event) {
655
- event.preventDefault();
656
- const dir = Math.sign(event.deltaY) * -1;
657
- if (dir === 1) {
658
- send("VALUE.INCREMENT");
659
- } else if (dir === -1) {
660
- send("VALUE.DECREMENT");
661
- }
652
+ trackFormControl({ context, scope }) {
653
+ const inputEl = getInputEl(scope);
654
+ return trackFormControl(inputEl, {
655
+ onFieldsetDisabledChange(disabled) {
656
+ context.set("fieldsetDisabled", disabled);
657
+ },
658
+ onFormReset() {
659
+ context.set("value", context.initial("value"));
662
660
  }
663
- return addDomEvent(inputEl, "wheel", onWheel, { passive: false });
664
- },
665
- activatePointerLock(ctx2) {
666
- if (isSafari()) return;
667
- return requestPointerLock(dom.getDoc(ctx2));
668
- },
669
- trackMousemove(ctx2, _evt, { send }) {
670
- const doc = dom.getDoc(ctx2);
671
- function onMousemove(event) {
672
- if (!ctx2.scrubberCursorPoint) return;
673
- const value = dom.getMousemoveValue(ctx2, event);
674
- if (!value.hint) return;
675
- send({
676
- type: "SCRUBBER.POINTER_MOVE",
677
- hint: value.hint,
678
- point: value.point
679
- });
661
+ });
662
+ },
663
+ setupVirtualCursor({ context, scope }) {
664
+ const point = context.get("scrubberCursorPoint");
665
+ return setupVirtualCursor(scope, point);
666
+ },
667
+ preventTextSelection({ scope }) {
668
+ return preventTextSelection(scope);
669
+ },
670
+ trackButtonDisabled({ context, scope, send }) {
671
+ const hint = context.get("hint");
672
+ const btn = getPressedTriggerEl(scope, hint);
673
+ return observeAttributes(btn, {
674
+ attributes: ["disabled"],
675
+ callback() {
676
+ send({ type: "TRIGGER.PRESS_UP", src: "attr" });
680
677
  }
681
- function onMouseup() {
682
- send("SCRUBBER.POINTER_UP");
678
+ });
679
+ },
680
+ attachWheelListener({ scope, send, prop }) {
681
+ const inputEl = getInputEl(scope);
682
+ if (!inputEl || !scope.isActiveElement(inputEl) || !prop("allowMouseWheel")) return;
683
+ function onWheel(event) {
684
+ event.preventDefault();
685
+ const dir = Math.sign(event.deltaY) * -1;
686
+ if (dir === 1) {
687
+ send({ type: "VALUE.INCREMENT" });
688
+ } else if (dir === -1) {
689
+ send({ type: "VALUE.DECREMENT" });
683
690
  }
684
- return callAll(
685
- addDomEvent(doc, "mousemove", onMousemove, false),
686
- addDomEvent(doc, "mouseup", onMouseup, false)
687
- );
688
691
  }
692
+ return addDomEvent(inputEl, "wheel", onWheel, { passive: false });
689
693
  },
690
- actions: {
691
- focusInput(ctx2) {
692
- if (!ctx2.focusInputOnChange) return;
693
- const inputEl = dom.getInputEl(ctx2);
694
- if (dom.isActiveElement(ctx2, inputEl)) return;
695
- raf(() => inputEl?.focus({ preventScroll: true }));
696
- },
697
- increment(ctx2, evt) {
698
- const nextValue = incrementValue(ctx2.valueAsNumber, evt.step ?? ctx2.step);
699
- const value = formatValue(ctx2, clampValue(nextValue, ctx2.min, ctx2.max));
700
- set.value(ctx2, value);
701
- },
702
- decrement(ctx2, evt) {
703
- const nextValue = decrementValue(ctx2.valueAsNumber, evt.step ?? ctx2.step);
704
- const value = formatValue(ctx2, clampValue(nextValue, ctx2.min, ctx2.max));
705
- set.value(ctx2, value);
706
- },
707
- setClampedValue(ctx2) {
708
- const nextValue = clampValue(ctx2.valueAsNumber, ctx2.min, ctx2.max);
709
- set.value(ctx2, formatValue(ctx2, nextValue));
710
- },
711
- setRawValue(ctx2, evt) {
712
- const parsedValue = parseValue(ctx2, evt.value);
713
- const value = formatValue(ctx2, clampValue(parsedValue, ctx2.min, ctx2.max));
714
- set.value(ctx2, value);
715
- },
716
- setValue(ctx2, evt) {
717
- const value = evt.target?.value ?? evt.value;
718
- set.value(ctx2, value);
719
- },
720
- clearValue(ctx2) {
721
- set.value(ctx2, "");
722
- },
723
- incrementToMax(ctx2) {
724
- const value = formatValue(ctx2, ctx2.max);
725
- set.value(ctx2, value);
726
- },
727
- decrementToMin(ctx2) {
728
- const value = formatValue(ctx2, ctx2.min);
729
- set.value(ctx2, value);
730
- },
731
- setHint(ctx2, evt) {
732
- ctx2.hint = evt.hint;
733
- },
734
- clearHint(ctx2) {
735
- ctx2.hint = null;
736
- },
737
- setHintToSet(ctx2) {
738
- ctx2.hint = "set";
739
- },
740
- invokeOnFocus(ctx2) {
741
- ctx2.onFocusChange?.({
742
- focused: true,
743
- value: ctx2.formattedValue,
744
- valueAsNumber: ctx2.valueAsNumber
745
- });
746
- },
747
- invokeOnBlur(ctx2) {
748
- ctx2.onFocusChange?.({
749
- focused: false,
750
- value: ctx2.formattedValue,
751
- valueAsNumber: ctx2.valueAsNumber
752
- });
753
- },
754
- invokeOnInvalid(ctx2) {
755
- if (!ctx2.isOutOfRange) return;
756
- const reason = ctx2.valueAsNumber > ctx2.max ? "rangeOverflow" : "rangeUnderflow";
757
- ctx2.onValueInvalid?.({
758
- reason,
759
- value: ctx2.formattedValue,
760
- valueAsNumber: ctx2.valueAsNumber
694
+ activatePointerLock({ scope }) {
695
+ if (isSafari()) return;
696
+ return requestPointerLock(scope.getDoc());
697
+ },
698
+ trackMousemove({ scope, send, context, computed }) {
699
+ const doc = scope.getDoc();
700
+ function onMousemove(event) {
701
+ const point = context.get("scrubberCursorPoint");
702
+ const isRtl = computed("isRtl");
703
+ const value = getMousemoveValue(scope, { point, isRtl, event });
704
+ if (!value.hint) return;
705
+ send({
706
+ type: "SCRUBBER.POINTER_MOVE",
707
+ hint: value.hint,
708
+ point: value.point
761
709
  });
762
- },
763
- syncInputElement(ctx2, evt) {
764
- const value = evt.type.endsWith("CHANGE") ? ctx2.value : ctx2.formattedValue;
765
- sync.input(ctx2, value);
766
- },
767
- setFormattedValue(ctx2) {
768
- set.value(ctx2, ctx2.formattedValue);
769
- },
770
- setCursorPoint(ctx2, evt) {
771
- ctx2.scrubberCursorPoint = evt.point;
772
- },
773
- clearCursorPoint(ctx2) {
774
- ctx2.scrubberCursorPoint = null;
775
- },
776
- setVirtualCursorPosition(ctx2) {
777
- const cursorEl = dom.getCursorEl(ctx2);
778
- if (!cursorEl || !ctx2.scrubberCursorPoint) return;
779
- const { x, y } = ctx2.scrubberCursorPoint;
780
- cursorEl.style.transform = `translate3d(${x}px, ${y}px, 0px)`;
781
- },
782
- setFormatterAndParser(ctx2) {
783
- if (!ctx2.locale) return;
784
- ctx2.formatter = createFormatter(ctx2.locale, ctx2.formatOptions);
785
- ctx2.parser = createParser(ctx2.locale, ctx2.formatOptions);
786
710
  }
711
+ function onMouseup() {
712
+ send({ type: "SCRUBBER.POINTER_UP" });
713
+ }
714
+ return callAll(addDomEvent(doc, "mousemove", onMousemove, false), addDomEvent(doc, "mouseup", onMouseup, false));
715
+ }
716
+ },
717
+ actions: {
718
+ focusInput({ scope, prop }) {
719
+ if (!prop("focusInputOnChange")) return;
720
+ const inputEl = getInputEl(scope);
721
+ if (scope.isActiveElement(inputEl)) return;
722
+ raf(() => inputEl?.focus({ preventScroll: true }));
723
+ },
724
+ increment({ context, event, prop, computed }) {
725
+ const nextValue = incrementValue(computed("valueAsNumber"), event.step ?? prop("step"));
726
+ const value = formatValue(clampValue(nextValue, prop("min"), prop("max")), { computed, prop });
727
+ context.set("value", value);
728
+ },
729
+ decrement({ context, event, prop, computed }) {
730
+ const nextValue = decrementValue(computed("valueAsNumber"), event.step ?? prop("step"));
731
+ const value = formatValue(clampValue(nextValue, prop("min"), prop("max")), { computed, prop });
732
+ context.set("value", value);
733
+ },
734
+ setClampedValue({ context, prop, computed }) {
735
+ const nextValue = clampValue(computed("valueAsNumber"), prop("min"), prop("max"));
736
+ context.set("value", formatValue(nextValue, { computed, prop }));
737
+ },
738
+ setRawValue({ context, event, prop, computed }) {
739
+ const parsedValue = parseValue(event.value, { computed, prop });
740
+ const value = formatValue(clampValue(parsedValue, prop("min"), prop("max")), { computed, prop });
741
+ context.set("value", value);
742
+ },
743
+ setValue({ context, event }) {
744
+ const value = event.target?.value ?? event.value;
745
+ context.set("value", value);
746
+ },
747
+ clearValue({ context }) {
748
+ context.set("value", "");
749
+ },
750
+ incrementToMax({ context, prop, computed }) {
751
+ const value = formatValue(prop("max"), { computed, prop });
752
+ context.set("value", value);
787
753
  },
788
- compareFns: {
789
- formatOptions: (a, b) => isEqual(a, b),
790
- scrubberCursorPoint: (a, b) => isEqual(a, b)
754
+ decrementToMin({ context, prop, computed }) {
755
+ const value = formatValue(prop("min"), { computed, prop });
756
+ context.set("value", value);
757
+ },
758
+ setHint({ context, event }) {
759
+ context.set("hint", event.hint);
760
+ },
761
+ clearHint({ context }) {
762
+ context.set("hint", null);
763
+ },
764
+ setHintToSet({ context }) {
765
+ context.set("hint", "set");
766
+ },
767
+ invokeOnFocus({ computed, prop }) {
768
+ prop("onFocusChange")?.({
769
+ focused: true,
770
+ value: computed("formattedValue"),
771
+ valueAsNumber: computed("valueAsNumber")
772
+ });
773
+ },
774
+ invokeOnBlur({ computed, prop }) {
775
+ prop("onFocusChange")?.({
776
+ focused: false,
777
+ value: computed("formattedValue"),
778
+ valueAsNumber: computed("valueAsNumber")
779
+ });
780
+ },
781
+ invokeOnInvalid({ computed, prop }) {
782
+ if (!computed("isOutOfRange")) return;
783
+ const reason = computed("valueAsNumber") > prop("max") ? "rangeOverflow" : "rangeUnderflow";
784
+ prop("onValueInvalid")?.({
785
+ reason,
786
+ value: computed("formattedValue"),
787
+ valueAsNumber: computed("valueAsNumber")
788
+ });
789
+ },
790
+ syncInputElement({ context, event, computed, scope }) {
791
+ const value = event.type.endsWith("CHANGE") ? context.get("value") : computed("formattedValue");
792
+ const inputEl = getInputEl(scope);
793
+ const sel = recordCursor(inputEl);
794
+ raf(() => {
795
+ setElementValue(inputEl, value);
796
+ restoreCursor(inputEl, sel);
797
+ });
798
+ },
799
+ setFormattedValue({ context, computed }) {
800
+ context.set("value", computed("formattedValue"));
801
+ },
802
+ setCursorPoint({ context, event }) {
803
+ context.set("scrubberCursorPoint", event.point);
804
+ },
805
+ clearCursorPoint({ context }) {
806
+ context.set("scrubberCursorPoint", null);
807
+ },
808
+ setVirtualCursorPosition({ context, scope }) {
809
+ const cursorEl = getCursorEl(scope);
810
+ const point = context.get("scrubberCursorPoint");
811
+ if (!cursorEl || !point) return;
812
+ cursorEl.style.transform = `translate3d(${point.x}px, ${point.y}px, 0px)`;
791
813
  }
792
814
  }
793
- );
794
- }
795
- var sync = {
796
- input(ctx, value) {
797
- const inputEl = dom.getInputEl(ctx);
798
- if (!inputEl) return;
799
- const sel = recordCursor(inputEl);
800
- raf(() => {
801
- setElementValue(inputEl, value);
802
- restoreCursor(inputEl, sel);
803
- });
804
- }
805
- };
806
- var invoke = {
807
- onChange: (ctx) => {
808
- ctx.onValueChange?.({
809
- value: ctx.value,
810
- valueAsNumber: ctx.valueAsNumber
811
- });
812
- }
813
- };
814
- var set = {
815
- value: (ctx, value) => {
816
- if (isEqual(ctx.value, value)) return;
817
- ctx.value = value;
818
- invoke.onChange(ctx);
819
815
  }
820
- };
816
+ });
817
+ var props = createProps()([
818
+ "allowMouseWheel",
819
+ "allowOverflow",
820
+ "clampValueOnBlur",
821
+ "dir",
822
+ "disabled",
823
+ "focusInputOnChange",
824
+ "form",
825
+ "formatOptions",
826
+ "getRootNode",
827
+ "id",
828
+ "ids",
829
+ "inputMode",
830
+ "invalid",
831
+ "locale",
832
+ "max",
833
+ "min",
834
+ "name",
835
+ "onFocusChange",
836
+ "onValueChange",
837
+ "onValueInvalid",
838
+ "pattern",
839
+ "required",
840
+ "readOnly",
841
+ "spinOnPress",
842
+ "step",
843
+ "translations",
844
+ "value",
845
+ "defaultValue"
846
+ ]);
847
+ var splitProps = createSplitProps(props);
821
848
 
822
- export { anatomy, connect, machine };
849
+ export { anatomy, connect, machine, props, splitProps };