@zag-js/number-input 0.10.4 → 0.11.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.
@@ -1,386 +0,0 @@
1
- import { createMachine, choose, guards } from '@zag-js/core';
2
- import { addDomEvent, requestPointerLock } from '@zag-js/dom-event';
3
- import { isSafari, raf } from '@zag-js/dom-query';
4
- import { dispatchInputValueEvent } from '@zag-js/form-utils';
5
- import { observeAttributes } from '@zag-js/mutation-observer';
6
- import { valueOf, isAtMin, isAtMax, isWithinRange } from '@zag-js/number-utils';
7
- import { compact, callAll } from '@zag-js/utils';
8
- import { dom } from './number-input.dom.mjs';
9
- import { utils } from './number-input.utils.mjs';
10
-
11
- const { not, and } = guards;
12
- function machine(userContext) {
13
- const ctx = compact(userContext);
14
- return createMachine(
15
- {
16
- id: "number-input",
17
- initial: "idle",
18
- context: {
19
- dir: "ltr",
20
- focusInputOnChange: true,
21
- clampValueOnBlur: true,
22
- allowOverflow: false,
23
- inputMode: "decimal",
24
- pattern: "[0-9]*(.[0-9]+)?",
25
- hint: null,
26
- value: "",
27
- step: 1,
28
- min: Number.MIN_SAFE_INTEGER,
29
- max: Number.MAX_SAFE_INTEGER,
30
- scrubberCursorPoint: null,
31
- invalid: false,
32
- spinOnPress: true,
33
- ...ctx,
34
- translations: {
35
- incrementLabel: "increment value",
36
- decrementLabel: "decrease value",
37
- ...ctx.translations
38
- }
39
- },
40
- computed: {
41
- isRtl: (ctx2) => ctx2.dir === "rtl",
42
- valueAsNumber: (ctx2) => valueOf(ctx2.value),
43
- isAtMin: (ctx2) => isAtMin(ctx2.value, ctx2),
44
- isAtMax: (ctx2) => isAtMax(ctx2.value, ctx2),
45
- isOutOfRange: (ctx2) => !isWithinRange(ctx2.value, ctx2),
46
- isValueEmpty: (ctx2) => ctx2.value === "",
47
- canIncrement: (ctx2) => ctx2.allowOverflow || !ctx2.isAtMax,
48
- canDecrement: (ctx2) => ctx2.allowOverflow || !ctx2.isAtMin,
49
- valueText: (ctx2) => ctx2.translations.valueText?.(ctx2.value),
50
- formattedValue: (ctx2) => ctx2.format?.(ctx2.value).toString() ?? ctx2.value
51
- },
52
- watch: {
53
- value: ["invokeOnChange", "dispatchChangeEvent"],
54
- isOutOfRange: ["invokeOnInvalid"],
55
- scrubberCursorPoint: ["setVirtualCursorPosition"]
56
- },
57
- entry: ["syncInputValue"],
58
- on: {
59
- SET_VALUE: [
60
- {
61
- guard: "clampOnBlur",
62
- actions: ["setValue", "clampValue", "setHintToSet"]
63
- },
64
- {
65
- actions: ["setValue", "setHintToSet"]
66
- }
67
- ],
68
- CLEAR_VALUE: {
69
- actions: ["clearValue"]
70
- },
71
- INCREMENT: {
72
- actions: ["increment"]
73
- },
74
- DECREMENT: {
75
- actions: ["decrement"]
76
- }
77
- },
78
- states: {
79
- idle: {
80
- exit: "invokeOnFocus",
81
- on: {
82
- PRESS_DOWN: {
83
- target: "before:spin",
84
- actions: ["focusInput", "setHint"]
85
- },
86
- PRESS_DOWN_SCRUBBER: {
87
- target: "scrubbing",
88
- actions: ["focusInput", "setHint", "setCursorPoint"]
89
- },
90
- FOCUS: "focused"
91
- }
92
- },
93
- focused: {
94
- tags: "focus",
95
- entry: "focusInput",
96
- activities: "attachWheelListener",
97
- on: {
98
- PRESS_DOWN: {
99
- target: "before:spin",
100
- actions: ["focusInput", "setHint"]
101
- },
102
- PRESS_DOWN_SCRUBBER: {
103
- target: "scrubbing",
104
- actions: ["focusInput", "setHint", "setCursorPoint"]
105
- },
106
- ARROW_UP: {
107
- actions: "increment"
108
- },
109
- ARROW_DOWN: {
110
- actions: "decrement"
111
- },
112
- HOME: {
113
- actions: "setToMin"
114
- },
115
- END: {
116
- actions: "setToMax"
117
- },
118
- CHANGE: {
119
- actions: ["setValue", "setHint"]
120
- },
121
- BLUR: [
122
- {
123
- guard: "isInvalidExponential",
124
- target: "idle",
125
- actions: ["clearValue", "clearHint", "invokeOnBlur"]
126
- },
127
- {
128
- guard: and("clampOnBlur", not("isInRange"), not("isEmptyValue")),
129
- target: "idle",
130
- actions: ["clampValue", "clearHint", "invokeOnBlur"]
131
- },
132
- {
133
- target: "idle",
134
- actions: ["roundValue", "invokeOnBlur"]
135
- }
136
- ]
137
- }
138
- },
139
- "before:spin": {
140
- tags: "focus",
141
- activities: "trackButtonDisabled",
142
- entry: choose([
143
- { guard: "isIncrementHint", actions: "increment" },
144
- { guard: "isDecrementHint", actions: "decrement" }
145
- ]),
146
- after: {
147
- CHANGE_DELAY: {
148
- target: "spinning",
149
- guard: and("isInRange", "spinOnPress")
150
- }
151
- },
152
- on: {
153
- PRESS_UP: {
154
- target: "focused",
155
- actions: "clearHint"
156
- }
157
- }
158
- },
159
- spinning: {
160
- tags: "focus",
161
- activities: "trackButtonDisabled",
162
- every: [
163
- {
164
- delay: "CHANGE_INTERVAL",
165
- guard: and(not("isAtMin"), "isIncrementHint"),
166
- actions: "increment"
167
- },
168
- {
169
- delay: "CHANGE_INTERVAL",
170
- guard: and(not("isAtMax"), "isDecrementHint"),
171
- actions: "decrement"
172
- }
173
- ],
174
- on: {
175
- PRESS_UP: {
176
- target: "focused",
177
- actions: "clearHint"
178
- }
179
- }
180
- },
181
- scrubbing: {
182
- tags: "focus",
183
- exit: "clearCursorPoint",
184
- activities: ["activatePointerLock", "trackMousemove", "setupVirtualCursor", "preventTextSelection"],
185
- on: {
186
- POINTER_UP_SCRUBBER: "focused",
187
- POINTER_MOVE_SCRUBBER: [
188
- {
189
- guard: "isIncrementHint",
190
- actions: ["increment", "setCursorPoint"]
191
- },
192
- {
193
- guard: "isDecrementHint",
194
- actions: ["decrement", "setCursorPoint"]
195
- }
196
- ]
197
- }
198
- }
199
- }
200
- },
201
- {
202
- delays: {
203
- CHANGE_INTERVAL: 50,
204
- CHANGE_DELAY: 300
205
- },
206
- guards: {
207
- clampOnBlur: (ctx2) => !!ctx2.clampValueOnBlur,
208
- isAtMin: (ctx2) => ctx2.isAtMin,
209
- spinOnPress: (ctx2) => !!ctx2.spinOnPress,
210
- isAtMax: (ctx2) => ctx2.isAtMax,
211
- isInRange: (ctx2) => !ctx2.isOutOfRange,
212
- isDecrementHint: (ctx2, evt) => (evt.hint ?? ctx2.hint) === "decrement",
213
- isEmptyValue: (ctx2) => ctx2.isValueEmpty,
214
- isIncrementHint: (ctx2, evt) => (evt.hint ?? ctx2.hint) === "increment",
215
- isInvalidExponential: (ctx2) => ctx2.value.toString().startsWith("e")
216
- },
217
- activities: {
218
- setupVirtualCursor(ctx2) {
219
- return dom.setupVirtualCursor(ctx2);
220
- },
221
- preventTextSelection(ctx2) {
222
- return dom.preventTextSelection(ctx2);
223
- },
224
- trackButtonDisabled(ctx2, _evt, { send }) {
225
- const btn = dom.getPressedTriggerEl(ctx2, ctx2.hint);
226
- return observeAttributes(btn, ["disabled"], () => {
227
- send("PRESS_UP");
228
- });
229
- },
230
- attachWheelListener(ctx2, _evt, { send }) {
231
- const input = dom.getInputEl(ctx2);
232
- if (!input)
233
- return;
234
- function onWheel(event) {
235
- const isInputFocused = dom.getDoc(ctx2).activeElement === input;
236
- if (!ctx2.allowMouseWheel || !isInputFocused)
237
- return;
238
- event.preventDefault();
239
- const dir = Math.sign(event.deltaY) * -1;
240
- if (dir === 1) {
241
- send("INCREMENT");
242
- } else if (dir === -1) {
243
- send("DECREMENT");
244
- }
245
- }
246
- return addDomEvent(input, "wheel", onWheel, { passive: false });
247
- },
248
- activatePointerLock(ctx2) {
249
- if (isSafari())
250
- return;
251
- return requestPointerLock(dom.getDoc(ctx2));
252
- },
253
- trackMousemove(ctx2, _evt, { send }) {
254
- const doc = dom.getDoc(ctx2);
255
- function onMousemove(event) {
256
- if (!ctx2.scrubberCursorPoint)
257
- return;
258
- const value = dom.getMousementValue(ctx2, event);
259
- if (!value.hint)
260
- return;
261
- send({
262
- type: "POINTER_MOVE_SCRUBBER",
263
- hint: value.hint,
264
- point: value.point
265
- });
266
- }
267
- function onMouseup() {
268
- send("POINTER_UP_SCRUBBER");
269
- }
270
- return callAll(
271
- addDomEvent(doc, "mousemove", onMousemove, false),
272
- addDomEvent(doc, "mouseup", onMouseup, false)
273
- );
274
- }
275
- },
276
- actions: {
277
- focusInput(ctx2) {
278
- if (!ctx2.focusInputOnChange)
279
- return;
280
- const input = dom.getInputEl(ctx2);
281
- raf(() => input?.focus());
282
- },
283
- increment(ctx2, evt) {
284
- ctx2.value = utils.increment(ctx2, evt.step);
285
- },
286
- decrement(ctx2, evt) {
287
- ctx2.value = utils.decrement(ctx2, evt.step);
288
- },
289
- clampValue(ctx2) {
290
- ctx2.value = utils.clamp(ctx2);
291
- },
292
- roundValue(ctx2) {
293
- if (ctx2.value !== "") {
294
- ctx2.value = utils.round(ctx2);
295
- }
296
- },
297
- setValue(ctx2, evt) {
298
- const value = evt.target?.value ?? evt.value;
299
- ctx2.value = utils.sanitize(ctx2, utils.parse(ctx2, value.toString()));
300
- },
301
- clearValue(ctx2) {
302
- ctx2.value = "";
303
- },
304
- setToMax(ctx2) {
305
- ctx2.value = ctx2.max.toString();
306
- },
307
- setToMin(ctx2) {
308
- ctx2.value = ctx2.min.toString();
309
- },
310
- setHint(ctx2, evt) {
311
- ctx2.hint = evt.hint;
312
- },
313
- clearHint(ctx2) {
314
- ctx2.hint = null;
315
- },
316
- setHintToSet(ctx2) {
317
- ctx2.hint = "set";
318
- },
319
- invokeOnChange(ctx2) {
320
- ctx2.onChange?.({
321
- value: ctx2.value,
322
- valueAsNumber: ctx2.valueAsNumber
323
- });
324
- },
325
- invokeOnFocus(ctx2, evt) {
326
- let srcElement = null;
327
- if (evt.type === "PRESS_DOWN") {
328
- srcElement = dom.getPressedTriggerEl(ctx2, evt.hint);
329
- } else if (evt.type === "FOCUS") {
330
- srcElement = dom.getInputEl(ctx2);
331
- } else if (evt.type === "PRESS_DOWN_SCRUBBER") {
332
- srcElement = dom.getScrubberEl(ctx2);
333
- }
334
- ctx2.onFocus?.({
335
- value: ctx2.value,
336
- valueAsNumber: ctx2.valueAsNumber,
337
- srcElement
338
- });
339
- },
340
- invokeOnBlur(ctx2) {
341
- ctx2.onBlur?.({
342
- value: ctx2.value,
343
- valueAsNumber: ctx2.valueAsNumber
344
- });
345
- },
346
- invokeOnInvalid(ctx2) {
347
- if (!ctx2.isOutOfRange)
348
- return;
349
- const reason = ctx2.valueAsNumber > ctx2.max ? "rangeOverflow" : "rangeUnderflow";
350
- ctx2.onInvalid?.({
351
- reason,
352
- value: ctx2.formattedValue,
353
- valueAsNumber: ctx2.valueAsNumber
354
- });
355
- },
356
- // sync input value, in event it was set from form libraries via `ref`, `bind:this`, etc.
357
- syncInputValue(ctx2) {
358
- const input = dom.getInputEl(ctx2);
359
- if (!input || input.value == ctx2.value)
360
- return;
361
- const value = utils.parse(ctx2, input.value);
362
- ctx2.value = utils.sanitize(ctx2, value);
363
- },
364
- setCursorPoint(ctx2, evt) {
365
- ctx2.scrubberCursorPoint = evt.point;
366
- },
367
- clearCursorPoint(ctx2) {
368
- ctx2.scrubberCursorPoint = null;
369
- },
370
- setVirtualCursorPosition(ctx2) {
371
- const cursor = dom.getCursorEl(ctx2);
372
- if (!cursor || !ctx2.scrubberCursorPoint)
373
- return;
374
- const { x, y } = ctx2.scrubberCursorPoint;
375
- cursor.style.transform = `translate3d(${x}px, ${y}px, 0px)`;
376
- },
377
- dispatchChangeEvent(ctx2) {
378
- const inputEl = dom.getInputEl(ctx2);
379
- dispatchInputValueEvent(inputEl, { value: ctx2.formattedValue });
380
- }
381
- }
382
- }
383
- );
384
- }
385
-
386
- export { machine };
@@ -1,13 +0,0 @@
1
- import type { JSX } from "@zag-js/types";
2
- import type { MachineContext as Ctx } from "./number-input.types";
3
- export declare const utils: {
4
- isValidNumericEvent: (ctx: Ctx, event: JSX.KeyboardEvent) => boolean;
5
- isFloatingPoint: (v: string) => boolean;
6
- sanitize: (ctx: Ctx, value: string) => string;
7
- increment: (ctx: Ctx, step?: number) => string;
8
- decrement: (ctx: Ctx, step?: number) => string;
9
- clamp: (ctx: Ctx) => string;
10
- parse: (ctx: Ctx, value: string) => string;
11
- format: (ctx: Ctx, value: string | number) => string | number;
12
- round: (ctx: Ctx) => string;
13
- };
@@ -1,45 +0,0 @@
1
- 'use strict';
2
-
3
- Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
4
-
5
- const domEvent = require('@zag-js/dom-event');
6
- const numberUtils = require('@zag-js/number-utils');
7
-
8
- const utils = {
9
- isValidNumericEvent: (ctx, event) => {
10
- if (event.key == null)
11
- return true;
12
- const isModifier = domEvent.isModifiedEvent(event);
13
- const isSingleKey = event.key.length === 1;
14
- if (isModifier || !isSingleKey)
15
- return true;
16
- return ctx.validateCharacter?.(event.key) ?? utils.isFloatingPoint(event.key);
17
- },
18
- isFloatingPoint: (v) => /^[0-9+\-.]$/.test(v),
19
- sanitize: (ctx, value) => {
20
- return value.split("").filter(ctx.validateCharacter ?? utils.isFloatingPoint).join("");
21
- },
22
- increment: (ctx, step) => {
23
- const value = numberUtils.increment(ctx.value, step ?? ctx.step);
24
- return numberUtils.formatDecimal(numberUtils.clamp(value, ctx), ctx);
25
- },
26
- decrement: (ctx, step) => {
27
- const value = numberUtils.decrement(ctx.value, step ?? ctx.step);
28
- return numberUtils.formatDecimal(numberUtils.clamp(value, ctx), ctx);
29
- },
30
- clamp: (ctx) => {
31
- return numberUtils.formatDecimal(numberUtils.clamp(ctx.value, ctx), ctx);
32
- },
33
- parse: (ctx, value) => {
34
- return ctx.parse?.(value) ?? value;
35
- },
36
- format: (ctx, value) => {
37
- const _val = value.toString();
38
- return ctx.format?.(_val) ?? _val;
39
- },
40
- round: (ctx) => {
41
- return numberUtils.formatDecimal(ctx.value, ctx);
42
- }
43
- };
44
-
45
- exports.utils = utils;
@@ -1,41 +0,0 @@
1
- import { isModifiedEvent } from '@zag-js/dom-event';
2
- import { increment, formatDecimal, clamp, decrement } from '@zag-js/number-utils';
3
-
4
- const utils = {
5
- isValidNumericEvent: (ctx, event) => {
6
- if (event.key == null)
7
- return true;
8
- const isModifier = isModifiedEvent(event);
9
- const isSingleKey = event.key.length === 1;
10
- if (isModifier || !isSingleKey)
11
- return true;
12
- return ctx.validateCharacter?.(event.key) ?? utils.isFloatingPoint(event.key);
13
- },
14
- isFloatingPoint: (v) => /^[0-9+\-.]$/.test(v),
15
- sanitize: (ctx, value) => {
16
- return value.split("").filter(ctx.validateCharacter ?? utils.isFloatingPoint).join("");
17
- },
18
- increment: (ctx, step) => {
19
- const value = increment(ctx.value, step ?? ctx.step);
20
- return formatDecimal(clamp(value, ctx), ctx);
21
- },
22
- decrement: (ctx, step) => {
23
- const value = decrement(ctx.value, step ?? ctx.step);
24
- return formatDecimal(clamp(value, ctx), ctx);
25
- },
26
- clamp: (ctx) => {
27
- return formatDecimal(clamp(ctx.value, ctx), ctx);
28
- },
29
- parse: (ctx, value) => {
30
- return ctx.parse?.(value) ?? value;
31
- },
32
- format: (ctx, value) => {
33
- const _val = value.toString();
34
- return ctx.format?.(_val) ?? _val;
35
- },
36
- round: (ctx) => {
37
- return formatDecimal(ctx.value, ctx);
38
- }
39
- };
40
-
41
- export { utils };