@zag-js/pin-input 0.1.11 → 0.1.14

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.d.ts CHANGED
@@ -8,6 +8,7 @@ declare type IntlMessages = {
8
8
  declare type ElementIds = Partial<{
9
9
  root: string;
10
10
  hiddenInput: string;
11
+ label: string;
11
12
  input(id: string): string;
12
13
  }>;
13
14
  declare type PublicContext = DirectionProperty & CommonProperties & {
@@ -123,8 +124,9 @@ declare function connect<T extends PropTypes>(state: State, send: Send, normaliz
123
124
  setValue(value: string[]): void;
124
125
  clearValue(): void;
125
126
  setValueAtIndex(index: number, value: string): void;
126
- focus(): void;
127
+ focus: () => void;
127
128
  rootProps: T["element"];
129
+ labelProps: T["label"];
128
130
  hiddenInputProps: T["input"];
129
131
  getInputProps({ index }: {
130
132
  index: number;
package/dist/index.js CHANGED
@@ -45,9 +45,6 @@ function getDocument(el) {
45
45
  return el;
46
46
  return (el == null ? void 0 : el.ownerDocument) ?? document;
47
47
  }
48
- function getWindow(el) {
49
- return (el == null ? void 0 : el.ownerDocument.defaultView) ?? window;
50
- }
51
48
  function defineDomHelpers(helpers) {
52
49
  const dom2 = {
53
50
  getRootNode: (ctx) => {
@@ -68,28 +65,9 @@ function getNativeEvent(e) {
68
65
  return e.nativeEvent ?? e;
69
66
  }
70
67
  var isModifiedEvent = (v) => v.ctrlKey || v.altKey || v.metaKey;
71
- function getDescriptor(el, options) {
72
- const { type, property } = options;
73
- const proto = getWindow(el)[type].prototype;
74
- return Object.getOwnPropertyDescriptor(proto, property) ?? {};
75
- }
76
- function dispatchInputValueEvent(el, value) {
77
- var _a;
78
- if (!el)
79
- return;
80
- const win = getWindow(el);
81
- if (!(el instanceof win.HTMLInputElement))
82
- return;
83
- const desc = getDescriptor(el, { type: "HTMLInputElement", property: "value" });
84
- (_a = desc.set) == null ? void 0 : _a.call(el, value);
85
- const event = new win.Event("input", { bubbles: true });
86
- el.dispatchEvent(event);
87
- }
88
68
  var rtlKeyMap = {
89
69
  ArrowLeft: "ArrowRight",
90
- ArrowRight: "ArrowLeft",
91
- Home: "End",
92
- End: "Home"
70
+ ArrowRight: "ArrowLeft"
93
71
  };
94
72
  var sameKeyMap = {
95
73
  Up: "ArrowUp",
@@ -155,6 +133,10 @@ var dom = defineDomHelpers({
155
133
  var _a;
156
134
  return ((_a = ctx.ids) == null ? void 0 : _a.hiddenInput) ?? `pin-input:${ctx.id}:hidden`;
157
135
  },
136
+ getLabelId: (ctx) => {
137
+ var _a;
138
+ return ((_a = ctx.ids) == null ? void 0 : _a.label) ?? `pin-input:${ctx.id}:label`;
139
+ },
158
140
  getRootEl: (ctx) => dom.getById(ctx, dom.getRootId(ctx)),
159
141
  getElements: (ctx) => {
160
142
  const ownerId = CSS.escape(dom.getRootId(ctx));
@@ -172,6 +154,10 @@ function connect(state, send, normalize) {
172
154
  const isInvalid = state.context.invalid;
173
155
  const focusedIndex = state.context.focusedIndex;
174
156
  const messages = state.context.messages;
157
+ function focus() {
158
+ var _a;
159
+ (_a = dom.getFirstInputEl(state.context)) == null ? void 0 : _a.focus();
160
+ }
175
161
  return {
176
162
  value: state.context.value,
177
163
  valueAsString: state.context.valueAsString,
@@ -188,10 +174,7 @@ function connect(state, send, normalize) {
188
174
  setValueAtIndex(index, value) {
189
175
  send({ type: "SET_VALUE", value, index });
190
176
  },
191
- focus() {
192
- var _a;
193
- (_a = dom.getFirstInputEl(state.context)) == null ? void 0 : _a.focus();
194
- },
177
+ focus,
195
178
  rootProps: normalize.element({
196
179
  dir: state.context.dir,
197
180
  "data-part": "root",
@@ -200,6 +183,18 @@ function connect(state, send, normalize) {
200
183
  "data-disabled": dataAttr(state.context.disabled),
201
184
  "data-complete": dataAttr(isValueComplete)
202
185
  }),
186
+ labelProps: normalize.label({
187
+ "data-part": "label",
188
+ htmlFor: dom.getHiddenInputId(state.context),
189
+ id: dom.getLabelId(state.context),
190
+ "data-invalid": dataAttr(isInvalid),
191
+ "data-disabled": dataAttr(state.context.disabled),
192
+ "data-complete": dataAttr(isValueComplete),
193
+ onClick: (event) => {
194
+ event.preventDefault();
195
+ focus();
196
+ }
197
+ }),
203
198
  hiddenInputProps: normalize.input({
204
199
  "aria-hidden": true,
205
200
  type: "text",
@@ -269,6 +264,8 @@ function connect(state, send, normalize) {
269
264
  exec(event);
270
265
  event.preventDefault();
271
266
  } else {
267
+ if (key === "Tab")
268
+ return;
272
269
  send({ type: "KEY_DOWN", value: key, preventDefault: () => event.preventDefault() });
273
270
  }
274
271
  },
@@ -285,237 +282,264 @@ function connect(state, send, normalize) {
285
282
 
286
283
  // src/pin-input.machine.ts
287
284
  var import_core = require("@zag-js/core");
285
+
286
+ // ../../utilities/form-utils/dist/index.mjs
287
+ function getWindow(el) {
288
+ return (el == null ? void 0 : el.ownerDocument.defaultView) ?? window;
289
+ }
290
+ function getDescriptor(el, options) {
291
+ const { type, property } = options;
292
+ const proto = getWindow(el)[type].prototype;
293
+ return Object.getOwnPropertyDescriptor(proto, property) ?? {};
294
+ }
295
+ function dispatchInputValueEvent(el, value) {
296
+ var _a;
297
+ if (!el)
298
+ return;
299
+ const win = getWindow(el);
300
+ if (!(el instanceof win.HTMLInputElement))
301
+ return;
302
+ const desc = getDescriptor(el, { type: "HTMLInputElement", property: "value" });
303
+ (_a = desc.set) == null ? void 0 : _a.call(el, value);
304
+ const event = new win.Event("input", { bubbles: true });
305
+ el.dispatchEvent(event);
306
+ }
307
+
308
+ // src/pin-input.machine.ts
288
309
  var { and, not } = import_core.guards;
289
310
  function machine(ctx) {
290
- return (0, import_core.createMachine)({
291
- id: "pin-input",
292
- initial: "unknown",
293
- context: {
294
- value: [],
295
- focusedIndex: -1,
296
- placeholder: "\u25CB",
297
- otp: false,
298
- type: "numeric",
299
- ...ctx,
300
- messages: {
301
- inputLabel: (index, length) => `pin code ${index + 1} of ${length}`,
302
- ...ctx.messages
303
- }
304
- },
305
- computed: {
306
- valueLength: (ctx2) => ctx2.value.length,
307
- filledValueLength: (ctx2) => ctx2.value.filter((v) => (v == null ? void 0 : v.trim()) !== "").length,
308
- isValueComplete: (ctx2) => ctx2.valueLength === ctx2.filledValueLength,
309
- valueAsString: (ctx2) => ctx2.value.join("")
310
- },
311
- watch: {
312
- focusedIndex: "focusInput",
313
- value: "invokeOnChange",
314
- isValueComplete: ["invokeOnComplete", "blurFocusedInputIfNeeded"]
315
- },
316
- on: {
317
- SET_VALUE: [
318
- {
319
- guard: "hasIndex",
320
- actions: "setValueAtIndex"
321
- },
322
- { actions: "setValue" }
323
- ],
324
- CLEAR_VALUE: [
325
- {
326
- guard: "isDisabled",
327
- actions: "clearValue"
328
- },
329
- {
330
- actions: ["clearValue", "setFocusIndexToFirst"]
331
- }
332
- ]
333
- },
334
- states: {
335
- unknown: {
336
- on: {
337
- SETUP: [
338
- {
339
- guard: "autoFocus",
340
- target: "focused",
341
- actions: ["setupValue", "setFocusIndexToFirst"]
342
- },
343
- {
344
- target: "idle",
345
- actions: "setupValue"
346
- }
347
- ]
311
+ return (0, import_core.createMachine)(
312
+ {
313
+ id: "pin-input",
314
+ initial: "unknown",
315
+ context: {
316
+ value: [],
317
+ focusedIndex: -1,
318
+ placeholder: "\u25CB",
319
+ otp: false,
320
+ type: "numeric",
321
+ ...ctx,
322
+ messages: {
323
+ inputLabel: (index, length) => `pin code ${index + 1} of ${length}`,
324
+ ...ctx.messages
348
325
  }
349
326
  },
350
- idle: {
351
- on: {
352
- FOCUS: {
353
- target: "focused",
354
- actions: "setFocusedIndex"
355
- }
356
- }
327
+ computed: {
328
+ valueLength: (ctx2) => ctx2.value.length,
329
+ filledValueLength: (ctx2) => ctx2.value.filter((v) => (v == null ? void 0 : v.trim()) !== "").length,
330
+ isValueComplete: (ctx2) => ctx2.valueLength === ctx2.filledValueLength,
331
+ valueAsString: (ctx2) => ctx2.value.join("")
357
332
  },
358
- focused: {
359
- on: {
360
- INPUT: [
361
- {
362
- guard: and("isFinalValue", "isValidValue"),
363
- actions: "setFocusedValue"
364
- },
365
- {
366
- guard: "isValidValue",
367
- actions: ["setFocusedValue", "setNextFocusedIndex"]
368
- }
369
- ],
370
- PASTE: {
371
- guard: "isValidValue",
372
- actions: ["setPastedValue", "setLastValueFocusIndex"]
373
- },
374
- BLUR: {
375
- target: "idle",
376
- actions: "clearFocusedIndex"
377
- },
378
- DELETE: {
379
- guard: "hasValue",
380
- actions: "clearFocusedValue"
381
- },
382
- ARROW_LEFT: {
383
- actions: "setPrevFocusedIndex"
333
+ watch: {
334
+ focusedIndex: "focusInput",
335
+ value: "invokeOnChange",
336
+ isValueComplete: ["invokeOnComplete", "blurFocusedInputIfNeeded"]
337
+ },
338
+ on: {
339
+ SET_VALUE: [
340
+ {
341
+ guard: "hasIndex",
342
+ actions: "setValueAtIndex"
384
343
  },
385
- ARROW_RIGHT: {
386
- actions: "setNextFocusedIndex"
344
+ { actions: "setValue" }
345
+ ],
346
+ CLEAR_VALUE: [
347
+ {
348
+ guard: "isDisabled",
349
+ actions: "clearValue"
387
350
  },
388
- BACKSPACE: [
389
- {
351
+ {
352
+ actions: ["clearValue", "setFocusIndexToFirst"]
353
+ }
354
+ ]
355
+ },
356
+ states: {
357
+ unknown: {
358
+ on: {
359
+ SETUP: [
360
+ {
361
+ guard: "autoFocus",
362
+ target: "focused",
363
+ actions: ["setupValue", "setFocusIndexToFirst"]
364
+ },
365
+ {
366
+ target: "idle",
367
+ actions: "setupValue"
368
+ }
369
+ ]
370
+ }
371
+ },
372
+ idle: {
373
+ on: {
374
+ FOCUS: {
375
+ target: "focused",
376
+ actions: "setFocusedIndex"
377
+ }
378
+ }
379
+ },
380
+ focused: {
381
+ on: {
382
+ INPUT: [
383
+ {
384
+ guard: and("isFinalValue", "isValidValue"),
385
+ actions: "setFocusedValue"
386
+ },
387
+ {
388
+ guard: "isValidValue",
389
+ actions: ["setFocusedValue", "setNextFocusedIndex"]
390
+ }
391
+ ],
392
+ PASTE: {
393
+ guard: "isValidValue",
394
+ actions: ["setPastedValue", "setLastValueFocusIndex"]
395
+ },
396
+ BLUR: {
397
+ target: "idle",
398
+ actions: "clearFocusedIndex"
399
+ },
400
+ DELETE: {
390
401
  guard: "hasValue",
391
402
  actions: "clearFocusedValue"
392
403
  },
393
- {
394
- actions: ["setPrevFocusedIndex", "clearFocusedValue"]
404
+ ARROW_LEFT: {
405
+ actions: "setPrevFocusedIndex"
406
+ },
407
+ ARROW_RIGHT: {
408
+ actions: "setNextFocusedIndex"
409
+ },
410
+ BACKSPACE: [
411
+ {
412
+ guard: "hasValue",
413
+ actions: "clearFocusedValue"
414
+ },
415
+ {
416
+ actions: ["setPrevFocusedIndex", "clearFocusedValue"]
417
+ }
418
+ ],
419
+ ENTER: {
420
+ guard: "isValueComplete",
421
+ actions: "requestFormSubmit"
422
+ },
423
+ KEY_DOWN: {
424
+ guard: not("isValidValue"),
425
+ actions: ["preventDefault", "invokeOnInvalid"]
395
426
  }
396
- ],
397
- ENTER: {
398
- guard: "isValueComplete",
399
- actions: "requestFormSubmit"
400
- },
401
- KEY_DOWN: {
402
- guard: not("isValidValue"),
403
- actions: ["preventDefault", "invokeOnInvalid"]
404
427
  }
405
428
  }
406
429
  }
407
- }
408
- }, {
409
- guards: {
410
- autoFocus: (ctx2) => !!ctx2.autoFocus,
411
- isValueEmpty: (_ctx, evt) => evt.value === "",
412
- hasValue: (ctx2) => ctx2.value[ctx2.focusedIndex] !== "",
413
- isValueComplete: (ctx2) => ctx2.isValueComplete,
414
- isValidValue: (ctx2, evt) => {
415
- if (!ctx2.pattern)
416
- return isValidType(evt.value, ctx2.type);
417
- const regex = new RegExp(ctx2.pattern, "g");
418
- return regex.test(evt.value);
419
- },
420
- isFinalValue: (ctx2) => {
421
- return ctx2.filledValueLength + 1 === ctx2.valueLength && ctx2.value.findIndex((v) => v.trim() === "") === ctx2.focusedIndex;
422
- },
423
- isLastInputFocused: (ctx2) => ctx2.focusedIndex === ctx2.valueLength - 1,
424
- hasIndex: (_ctx, evt) => evt.index !== void 0,
425
- isDisabled: (ctx2) => !!ctx2.disabled
426
430
  },
427
- actions: {
428
- setupValue: (ctx2) => {
429
- const inputs = dom.getElements(ctx2);
430
- const empty = Array.from({ length: inputs.length }).map(() => "");
431
- ctx2.value = Object.assign(empty, ctx2.value);
431
+ {
432
+ guards: {
433
+ autoFocus: (ctx2) => !!ctx2.autoFocus,
434
+ isValueEmpty: (_ctx, evt) => evt.value === "",
435
+ hasValue: (ctx2) => ctx2.value[ctx2.focusedIndex] !== "",
436
+ isValueComplete: (ctx2) => ctx2.isValueComplete,
437
+ isValidValue: (ctx2, evt) => {
438
+ if (!ctx2.pattern)
439
+ return isValidType(evt.value, ctx2.type);
440
+ const regex = new RegExp(ctx2.pattern, "g");
441
+ return regex.test(evt.value);
442
+ },
443
+ isFinalValue: (ctx2) => {
444
+ return ctx2.filledValueLength + 1 === ctx2.valueLength && ctx2.value.findIndex((v) => v.trim() === "") === ctx2.focusedIndex;
445
+ },
446
+ isLastInputFocused: (ctx2) => ctx2.focusedIndex === ctx2.valueLength - 1,
447
+ hasIndex: (_ctx, evt) => evt.index !== void 0,
448
+ isDisabled: (ctx2) => !!ctx2.disabled
432
449
  },
433
- focusInput: (ctx2) => {
434
- raf(() => {
450
+ actions: {
451
+ setupValue: (ctx2) => {
452
+ const inputs = dom.getElements(ctx2);
453
+ const empty = Array.from({ length: inputs.length }).map(() => "");
454
+ ctx2.value = Object.assign(empty, ctx2.value);
455
+ },
456
+ focusInput: (ctx2) => {
457
+ raf(() => {
458
+ var _a;
459
+ if (ctx2.focusedIndex === -1)
460
+ return;
461
+ (_a = dom.getFocusedEl(ctx2)) == null ? void 0 : _a.focus();
462
+ });
463
+ },
464
+ invokeOnComplete: (ctx2) => {
435
465
  var _a;
436
- if (ctx2.focusedIndex === -1)
466
+ if (!ctx2.isValueComplete)
437
467
  return;
438
- (_a = dom.getFocusedEl(ctx2)) == null ? void 0 : _a.focus();
439
- });
440
- },
441
- invokeOnComplete: (ctx2) => {
442
- var _a;
443
- if (!ctx2.isValueComplete)
444
- return;
445
- (_a = ctx2.onComplete) == null ? void 0 : _a.call(ctx2, { value: Array.from(ctx2.value), valueAsString: ctx2.valueAsString });
446
- },
447
- invokeOnChange: (ctx2, evt) => {
448
- var _a;
449
- if (evt.type === "SETUP")
450
- return;
451
- (_a = ctx2.onChange) == null ? void 0 : _a.call(ctx2, { value: Array.from(ctx2.value) });
452
- dispatchInputValueEvent(dom.getHiddenInputEl(ctx2), ctx2.valueAsString);
453
- },
454
- invokeOnInvalid: (ctx2, evt) => {
455
- var _a;
456
- (_a = ctx2.onInvalid) == null ? void 0 : _a.call(ctx2, { value: evt.value, index: ctx2.focusedIndex });
457
- },
458
- clearFocusedIndex: (ctx2) => {
459
- ctx2.focusedIndex = -1;
460
- },
461
- setValue: (ctx2, evt) => {
462
- assign(ctx2, evt.value);
463
- },
464
- setFocusedIndex: (ctx2, evt) => {
465
- ctx2.focusedIndex = evt.index;
466
- },
467
- setFocusedValue: (ctx2, evt) => {
468
- ctx2.value[ctx2.focusedIndex] = lastChar(evt.value);
469
- },
470
- setPastedValue(ctx2, evt) {
471
- raf(() => {
472
- const value = evt.value.substring(0, ctx2.valueLength);
473
- assign(ctx2, value.split("").filter(Boolean));
474
- });
475
- },
476
- setValueAtIndex: (ctx2, evt) => {
477
- ctx2.value[evt.index] = lastChar(evt.value);
478
- },
479
- clearValue: (ctx2) => {
480
- assign(ctx2, "");
481
- },
482
- clearFocusedValue: (ctx2) => {
483
- ctx2.value[ctx2.focusedIndex] = "";
484
- },
485
- setFocusIndexToFirst: (ctx2) => {
486
- ctx2.focusedIndex = 0;
487
- },
488
- setNextFocusedIndex: (ctx2) => {
489
- ctx2.focusedIndex = Math.min(ctx2.focusedIndex + 1, ctx2.valueLength - 1);
490
- },
491
- setPrevFocusedIndex: (ctx2) => {
492
- ctx2.focusedIndex = Math.max(ctx2.focusedIndex - 1, 0);
493
- },
494
- setLastValueFocusIndex: (ctx2) => {
495
- raf(() => {
496
- ctx2.focusedIndex = Math.min(ctx2.filledValueLength, ctx2.valueLength - 1);
497
- });
498
- },
499
- preventDefault(_, evt) {
500
- evt.preventDefault();
501
- },
502
- blurFocusedInputIfNeeded(ctx2) {
503
- if (!ctx2.blurOnComplete)
504
- return;
505
- raf(() => {
468
+ (_a = ctx2.onComplete) == null ? void 0 : _a.call(ctx2, { value: Array.from(ctx2.value), valueAsString: ctx2.valueAsString });
469
+ },
470
+ invokeOnChange: (ctx2, evt) => {
506
471
  var _a;
507
- (_a = dom.getFocusedEl(ctx2)) == null ? void 0 : _a.blur();
508
- });
509
- },
510
- requestFormSubmit(ctx2) {
511
- var _a;
512
- if (!ctx2.name)
513
- return;
514
- const input = dom.getHiddenInputEl(ctx2);
515
- (_a = input == null ? void 0 : input.form) == null ? void 0 : _a.requestSubmit();
472
+ if (evt.type === "SETUP")
473
+ return;
474
+ (_a = ctx2.onChange) == null ? void 0 : _a.call(ctx2, { value: Array.from(ctx2.value) });
475
+ dispatchInputValueEvent(dom.getHiddenInputEl(ctx2), ctx2.valueAsString);
476
+ },
477
+ invokeOnInvalid: (ctx2, evt) => {
478
+ var _a;
479
+ (_a = ctx2.onInvalid) == null ? void 0 : _a.call(ctx2, { value: evt.value, index: ctx2.focusedIndex });
480
+ },
481
+ clearFocusedIndex: (ctx2) => {
482
+ ctx2.focusedIndex = -1;
483
+ },
484
+ setValue: (ctx2, evt) => {
485
+ assign(ctx2, evt.value);
486
+ },
487
+ setFocusedIndex: (ctx2, evt) => {
488
+ ctx2.focusedIndex = evt.index;
489
+ },
490
+ setFocusedValue: (ctx2, evt) => {
491
+ ctx2.value[ctx2.focusedIndex] = lastChar(evt.value);
492
+ },
493
+ setPastedValue(ctx2, evt) {
494
+ raf(() => {
495
+ const value = evt.value.substring(0, ctx2.valueLength);
496
+ assign(ctx2, value.split("").filter(Boolean));
497
+ });
498
+ },
499
+ setValueAtIndex: (ctx2, evt) => {
500
+ ctx2.value[evt.index] = lastChar(evt.value);
501
+ },
502
+ clearValue: (ctx2) => {
503
+ assign(ctx2, "");
504
+ },
505
+ clearFocusedValue: (ctx2) => {
506
+ ctx2.value[ctx2.focusedIndex] = "";
507
+ },
508
+ setFocusIndexToFirst: (ctx2) => {
509
+ ctx2.focusedIndex = 0;
510
+ },
511
+ setNextFocusedIndex: (ctx2) => {
512
+ ctx2.focusedIndex = Math.min(ctx2.focusedIndex + 1, ctx2.valueLength - 1);
513
+ },
514
+ setPrevFocusedIndex: (ctx2) => {
515
+ ctx2.focusedIndex = Math.max(ctx2.focusedIndex - 1, 0);
516
+ },
517
+ setLastValueFocusIndex: (ctx2) => {
518
+ raf(() => {
519
+ ctx2.focusedIndex = Math.min(ctx2.filledValueLength, ctx2.valueLength - 1);
520
+ });
521
+ },
522
+ preventDefault(_, evt) {
523
+ evt.preventDefault();
524
+ },
525
+ blurFocusedInputIfNeeded(ctx2) {
526
+ if (!ctx2.blurOnComplete)
527
+ return;
528
+ raf(() => {
529
+ var _a;
530
+ (_a = dom.getFocusedEl(ctx2)) == null ? void 0 : _a.blur();
531
+ });
532
+ },
533
+ requestFormSubmit(ctx2) {
534
+ var _a;
535
+ if (!ctx2.name)
536
+ return;
537
+ const input = dom.getHiddenInputEl(ctx2);
538
+ (_a = input == null ? void 0 : input.form) == null ? void 0 : _a.requestSubmit();
539
+ }
516
540
  }
517
541
  }
518
- });
542
+ );
519
543
  }
520
544
  var REGEX = {
521
545
  numeric: /^[0-9]+$/,
package/dist/index.mjs CHANGED
@@ -18,9 +18,6 @@ function getDocument(el) {
18
18
  return el;
19
19
  return (el == null ? void 0 : el.ownerDocument) ?? document;
20
20
  }
21
- function getWindow(el) {
22
- return (el == null ? void 0 : el.ownerDocument.defaultView) ?? window;
23
- }
24
21
  function defineDomHelpers(helpers) {
25
22
  const dom2 = {
26
23
  getRootNode: (ctx) => {
@@ -41,28 +38,9 @@ function getNativeEvent(e) {
41
38
  return e.nativeEvent ?? e;
42
39
  }
43
40
  var isModifiedEvent = (v) => v.ctrlKey || v.altKey || v.metaKey;
44
- function getDescriptor(el, options) {
45
- const { type, property } = options;
46
- const proto = getWindow(el)[type].prototype;
47
- return Object.getOwnPropertyDescriptor(proto, property) ?? {};
48
- }
49
- function dispatchInputValueEvent(el, value) {
50
- var _a;
51
- if (!el)
52
- return;
53
- const win = getWindow(el);
54
- if (!(el instanceof win.HTMLInputElement))
55
- return;
56
- const desc = getDescriptor(el, { type: "HTMLInputElement", property: "value" });
57
- (_a = desc.set) == null ? void 0 : _a.call(el, value);
58
- const event = new win.Event("input", { bubbles: true });
59
- el.dispatchEvent(event);
60
- }
61
41
  var rtlKeyMap = {
62
42
  ArrowLeft: "ArrowRight",
63
- ArrowRight: "ArrowLeft",
64
- Home: "End",
65
- End: "Home"
43
+ ArrowRight: "ArrowLeft"
66
44
  };
67
45
  var sameKeyMap = {
68
46
  Up: "ArrowUp",
@@ -128,6 +106,10 @@ var dom = defineDomHelpers({
128
106
  var _a;
129
107
  return ((_a = ctx.ids) == null ? void 0 : _a.hiddenInput) ?? `pin-input:${ctx.id}:hidden`;
130
108
  },
109
+ getLabelId: (ctx) => {
110
+ var _a;
111
+ return ((_a = ctx.ids) == null ? void 0 : _a.label) ?? `pin-input:${ctx.id}:label`;
112
+ },
131
113
  getRootEl: (ctx) => dom.getById(ctx, dom.getRootId(ctx)),
132
114
  getElements: (ctx) => {
133
115
  const ownerId = CSS.escape(dom.getRootId(ctx));
@@ -145,6 +127,10 @@ function connect(state, send, normalize) {
145
127
  const isInvalid = state.context.invalid;
146
128
  const focusedIndex = state.context.focusedIndex;
147
129
  const messages = state.context.messages;
130
+ function focus() {
131
+ var _a;
132
+ (_a = dom.getFirstInputEl(state.context)) == null ? void 0 : _a.focus();
133
+ }
148
134
  return {
149
135
  value: state.context.value,
150
136
  valueAsString: state.context.valueAsString,
@@ -161,10 +147,7 @@ function connect(state, send, normalize) {
161
147
  setValueAtIndex(index, value) {
162
148
  send({ type: "SET_VALUE", value, index });
163
149
  },
164
- focus() {
165
- var _a;
166
- (_a = dom.getFirstInputEl(state.context)) == null ? void 0 : _a.focus();
167
- },
150
+ focus,
168
151
  rootProps: normalize.element({
169
152
  dir: state.context.dir,
170
153
  "data-part": "root",
@@ -173,6 +156,18 @@ function connect(state, send, normalize) {
173
156
  "data-disabled": dataAttr(state.context.disabled),
174
157
  "data-complete": dataAttr(isValueComplete)
175
158
  }),
159
+ labelProps: normalize.label({
160
+ "data-part": "label",
161
+ htmlFor: dom.getHiddenInputId(state.context),
162
+ id: dom.getLabelId(state.context),
163
+ "data-invalid": dataAttr(isInvalid),
164
+ "data-disabled": dataAttr(state.context.disabled),
165
+ "data-complete": dataAttr(isValueComplete),
166
+ onClick: (event) => {
167
+ event.preventDefault();
168
+ focus();
169
+ }
170
+ }),
176
171
  hiddenInputProps: normalize.input({
177
172
  "aria-hidden": true,
178
173
  type: "text",
@@ -242,6 +237,8 @@ function connect(state, send, normalize) {
242
237
  exec(event);
243
238
  event.preventDefault();
244
239
  } else {
240
+ if (key === "Tab")
241
+ return;
245
242
  send({ type: "KEY_DOWN", value: key, preventDefault: () => event.preventDefault() });
246
243
  }
247
244
  },
@@ -258,237 +255,264 @@ function connect(state, send, normalize) {
258
255
 
259
256
  // src/pin-input.machine.ts
260
257
  import { createMachine, guards } from "@zag-js/core";
258
+
259
+ // ../../utilities/form-utils/dist/index.mjs
260
+ function getWindow(el) {
261
+ return (el == null ? void 0 : el.ownerDocument.defaultView) ?? window;
262
+ }
263
+ function getDescriptor(el, options) {
264
+ const { type, property } = options;
265
+ const proto = getWindow(el)[type].prototype;
266
+ return Object.getOwnPropertyDescriptor(proto, property) ?? {};
267
+ }
268
+ function dispatchInputValueEvent(el, value) {
269
+ var _a;
270
+ if (!el)
271
+ return;
272
+ const win = getWindow(el);
273
+ if (!(el instanceof win.HTMLInputElement))
274
+ return;
275
+ const desc = getDescriptor(el, { type: "HTMLInputElement", property: "value" });
276
+ (_a = desc.set) == null ? void 0 : _a.call(el, value);
277
+ const event = new win.Event("input", { bubbles: true });
278
+ el.dispatchEvent(event);
279
+ }
280
+
281
+ // src/pin-input.machine.ts
261
282
  var { and, not } = guards;
262
283
  function machine(ctx) {
263
- return createMachine({
264
- id: "pin-input",
265
- initial: "unknown",
266
- context: {
267
- value: [],
268
- focusedIndex: -1,
269
- placeholder: "\u25CB",
270
- otp: false,
271
- type: "numeric",
272
- ...ctx,
273
- messages: {
274
- inputLabel: (index, length) => `pin code ${index + 1} of ${length}`,
275
- ...ctx.messages
276
- }
277
- },
278
- computed: {
279
- valueLength: (ctx2) => ctx2.value.length,
280
- filledValueLength: (ctx2) => ctx2.value.filter((v) => (v == null ? void 0 : v.trim()) !== "").length,
281
- isValueComplete: (ctx2) => ctx2.valueLength === ctx2.filledValueLength,
282
- valueAsString: (ctx2) => ctx2.value.join("")
283
- },
284
- watch: {
285
- focusedIndex: "focusInput",
286
- value: "invokeOnChange",
287
- isValueComplete: ["invokeOnComplete", "blurFocusedInputIfNeeded"]
288
- },
289
- on: {
290
- SET_VALUE: [
291
- {
292
- guard: "hasIndex",
293
- actions: "setValueAtIndex"
294
- },
295
- { actions: "setValue" }
296
- ],
297
- CLEAR_VALUE: [
298
- {
299
- guard: "isDisabled",
300
- actions: "clearValue"
301
- },
302
- {
303
- actions: ["clearValue", "setFocusIndexToFirst"]
304
- }
305
- ]
306
- },
307
- states: {
308
- unknown: {
309
- on: {
310
- SETUP: [
311
- {
312
- guard: "autoFocus",
313
- target: "focused",
314
- actions: ["setupValue", "setFocusIndexToFirst"]
315
- },
316
- {
317
- target: "idle",
318
- actions: "setupValue"
319
- }
320
- ]
284
+ return createMachine(
285
+ {
286
+ id: "pin-input",
287
+ initial: "unknown",
288
+ context: {
289
+ value: [],
290
+ focusedIndex: -1,
291
+ placeholder: "\u25CB",
292
+ otp: false,
293
+ type: "numeric",
294
+ ...ctx,
295
+ messages: {
296
+ inputLabel: (index, length) => `pin code ${index + 1} of ${length}`,
297
+ ...ctx.messages
321
298
  }
322
299
  },
323
- idle: {
324
- on: {
325
- FOCUS: {
326
- target: "focused",
327
- actions: "setFocusedIndex"
328
- }
329
- }
300
+ computed: {
301
+ valueLength: (ctx2) => ctx2.value.length,
302
+ filledValueLength: (ctx2) => ctx2.value.filter((v) => (v == null ? void 0 : v.trim()) !== "").length,
303
+ isValueComplete: (ctx2) => ctx2.valueLength === ctx2.filledValueLength,
304
+ valueAsString: (ctx2) => ctx2.value.join("")
330
305
  },
331
- focused: {
332
- on: {
333
- INPUT: [
334
- {
335
- guard: and("isFinalValue", "isValidValue"),
336
- actions: "setFocusedValue"
337
- },
338
- {
339
- guard: "isValidValue",
340
- actions: ["setFocusedValue", "setNextFocusedIndex"]
341
- }
342
- ],
343
- PASTE: {
344
- guard: "isValidValue",
345
- actions: ["setPastedValue", "setLastValueFocusIndex"]
346
- },
347
- BLUR: {
348
- target: "idle",
349
- actions: "clearFocusedIndex"
350
- },
351
- DELETE: {
352
- guard: "hasValue",
353
- actions: "clearFocusedValue"
354
- },
355
- ARROW_LEFT: {
356
- actions: "setPrevFocusedIndex"
306
+ watch: {
307
+ focusedIndex: "focusInput",
308
+ value: "invokeOnChange",
309
+ isValueComplete: ["invokeOnComplete", "blurFocusedInputIfNeeded"]
310
+ },
311
+ on: {
312
+ SET_VALUE: [
313
+ {
314
+ guard: "hasIndex",
315
+ actions: "setValueAtIndex"
357
316
  },
358
- ARROW_RIGHT: {
359
- actions: "setNextFocusedIndex"
317
+ { actions: "setValue" }
318
+ ],
319
+ CLEAR_VALUE: [
320
+ {
321
+ guard: "isDisabled",
322
+ actions: "clearValue"
360
323
  },
361
- BACKSPACE: [
362
- {
324
+ {
325
+ actions: ["clearValue", "setFocusIndexToFirst"]
326
+ }
327
+ ]
328
+ },
329
+ states: {
330
+ unknown: {
331
+ on: {
332
+ SETUP: [
333
+ {
334
+ guard: "autoFocus",
335
+ target: "focused",
336
+ actions: ["setupValue", "setFocusIndexToFirst"]
337
+ },
338
+ {
339
+ target: "idle",
340
+ actions: "setupValue"
341
+ }
342
+ ]
343
+ }
344
+ },
345
+ idle: {
346
+ on: {
347
+ FOCUS: {
348
+ target: "focused",
349
+ actions: "setFocusedIndex"
350
+ }
351
+ }
352
+ },
353
+ focused: {
354
+ on: {
355
+ INPUT: [
356
+ {
357
+ guard: and("isFinalValue", "isValidValue"),
358
+ actions: "setFocusedValue"
359
+ },
360
+ {
361
+ guard: "isValidValue",
362
+ actions: ["setFocusedValue", "setNextFocusedIndex"]
363
+ }
364
+ ],
365
+ PASTE: {
366
+ guard: "isValidValue",
367
+ actions: ["setPastedValue", "setLastValueFocusIndex"]
368
+ },
369
+ BLUR: {
370
+ target: "idle",
371
+ actions: "clearFocusedIndex"
372
+ },
373
+ DELETE: {
363
374
  guard: "hasValue",
364
375
  actions: "clearFocusedValue"
365
376
  },
366
- {
367
- actions: ["setPrevFocusedIndex", "clearFocusedValue"]
377
+ ARROW_LEFT: {
378
+ actions: "setPrevFocusedIndex"
379
+ },
380
+ ARROW_RIGHT: {
381
+ actions: "setNextFocusedIndex"
382
+ },
383
+ BACKSPACE: [
384
+ {
385
+ guard: "hasValue",
386
+ actions: "clearFocusedValue"
387
+ },
388
+ {
389
+ actions: ["setPrevFocusedIndex", "clearFocusedValue"]
390
+ }
391
+ ],
392
+ ENTER: {
393
+ guard: "isValueComplete",
394
+ actions: "requestFormSubmit"
395
+ },
396
+ KEY_DOWN: {
397
+ guard: not("isValidValue"),
398
+ actions: ["preventDefault", "invokeOnInvalid"]
368
399
  }
369
- ],
370
- ENTER: {
371
- guard: "isValueComplete",
372
- actions: "requestFormSubmit"
373
- },
374
- KEY_DOWN: {
375
- guard: not("isValidValue"),
376
- actions: ["preventDefault", "invokeOnInvalid"]
377
400
  }
378
401
  }
379
402
  }
380
- }
381
- }, {
382
- guards: {
383
- autoFocus: (ctx2) => !!ctx2.autoFocus,
384
- isValueEmpty: (_ctx, evt) => evt.value === "",
385
- hasValue: (ctx2) => ctx2.value[ctx2.focusedIndex] !== "",
386
- isValueComplete: (ctx2) => ctx2.isValueComplete,
387
- isValidValue: (ctx2, evt) => {
388
- if (!ctx2.pattern)
389
- return isValidType(evt.value, ctx2.type);
390
- const regex = new RegExp(ctx2.pattern, "g");
391
- return regex.test(evt.value);
392
- },
393
- isFinalValue: (ctx2) => {
394
- return ctx2.filledValueLength + 1 === ctx2.valueLength && ctx2.value.findIndex((v) => v.trim() === "") === ctx2.focusedIndex;
395
- },
396
- isLastInputFocused: (ctx2) => ctx2.focusedIndex === ctx2.valueLength - 1,
397
- hasIndex: (_ctx, evt) => evt.index !== void 0,
398
- isDisabled: (ctx2) => !!ctx2.disabled
399
403
  },
400
- actions: {
401
- setupValue: (ctx2) => {
402
- const inputs = dom.getElements(ctx2);
403
- const empty = Array.from({ length: inputs.length }).map(() => "");
404
- ctx2.value = Object.assign(empty, ctx2.value);
404
+ {
405
+ guards: {
406
+ autoFocus: (ctx2) => !!ctx2.autoFocus,
407
+ isValueEmpty: (_ctx, evt) => evt.value === "",
408
+ hasValue: (ctx2) => ctx2.value[ctx2.focusedIndex] !== "",
409
+ isValueComplete: (ctx2) => ctx2.isValueComplete,
410
+ isValidValue: (ctx2, evt) => {
411
+ if (!ctx2.pattern)
412
+ return isValidType(evt.value, ctx2.type);
413
+ const regex = new RegExp(ctx2.pattern, "g");
414
+ return regex.test(evt.value);
415
+ },
416
+ isFinalValue: (ctx2) => {
417
+ return ctx2.filledValueLength + 1 === ctx2.valueLength && ctx2.value.findIndex((v) => v.trim() === "") === ctx2.focusedIndex;
418
+ },
419
+ isLastInputFocused: (ctx2) => ctx2.focusedIndex === ctx2.valueLength - 1,
420
+ hasIndex: (_ctx, evt) => evt.index !== void 0,
421
+ isDisabled: (ctx2) => !!ctx2.disabled
405
422
  },
406
- focusInput: (ctx2) => {
407
- raf(() => {
423
+ actions: {
424
+ setupValue: (ctx2) => {
425
+ const inputs = dom.getElements(ctx2);
426
+ const empty = Array.from({ length: inputs.length }).map(() => "");
427
+ ctx2.value = Object.assign(empty, ctx2.value);
428
+ },
429
+ focusInput: (ctx2) => {
430
+ raf(() => {
431
+ var _a;
432
+ if (ctx2.focusedIndex === -1)
433
+ return;
434
+ (_a = dom.getFocusedEl(ctx2)) == null ? void 0 : _a.focus();
435
+ });
436
+ },
437
+ invokeOnComplete: (ctx2) => {
408
438
  var _a;
409
- if (ctx2.focusedIndex === -1)
439
+ if (!ctx2.isValueComplete)
410
440
  return;
411
- (_a = dom.getFocusedEl(ctx2)) == null ? void 0 : _a.focus();
412
- });
413
- },
414
- invokeOnComplete: (ctx2) => {
415
- var _a;
416
- if (!ctx2.isValueComplete)
417
- return;
418
- (_a = ctx2.onComplete) == null ? void 0 : _a.call(ctx2, { value: Array.from(ctx2.value), valueAsString: ctx2.valueAsString });
419
- },
420
- invokeOnChange: (ctx2, evt) => {
421
- var _a;
422
- if (evt.type === "SETUP")
423
- return;
424
- (_a = ctx2.onChange) == null ? void 0 : _a.call(ctx2, { value: Array.from(ctx2.value) });
425
- dispatchInputValueEvent(dom.getHiddenInputEl(ctx2), ctx2.valueAsString);
426
- },
427
- invokeOnInvalid: (ctx2, evt) => {
428
- var _a;
429
- (_a = ctx2.onInvalid) == null ? void 0 : _a.call(ctx2, { value: evt.value, index: ctx2.focusedIndex });
430
- },
431
- clearFocusedIndex: (ctx2) => {
432
- ctx2.focusedIndex = -1;
433
- },
434
- setValue: (ctx2, evt) => {
435
- assign(ctx2, evt.value);
436
- },
437
- setFocusedIndex: (ctx2, evt) => {
438
- ctx2.focusedIndex = evt.index;
439
- },
440
- setFocusedValue: (ctx2, evt) => {
441
- ctx2.value[ctx2.focusedIndex] = lastChar(evt.value);
442
- },
443
- setPastedValue(ctx2, evt) {
444
- raf(() => {
445
- const value = evt.value.substring(0, ctx2.valueLength);
446
- assign(ctx2, value.split("").filter(Boolean));
447
- });
448
- },
449
- setValueAtIndex: (ctx2, evt) => {
450
- ctx2.value[evt.index] = lastChar(evt.value);
451
- },
452
- clearValue: (ctx2) => {
453
- assign(ctx2, "");
454
- },
455
- clearFocusedValue: (ctx2) => {
456
- ctx2.value[ctx2.focusedIndex] = "";
457
- },
458
- setFocusIndexToFirst: (ctx2) => {
459
- ctx2.focusedIndex = 0;
460
- },
461
- setNextFocusedIndex: (ctx2) => {
462
- ctx2.focusedIndex = Math.min(ctx2.focusedIndex + 1, ctx2.valueLength - 1);
463
- },
464
- setPrevFocusedIndex: (ctx2) => {
465
- ctx2.focusedIndex = Math.max(ctx2.focusedIndex - 1, 0);
466
- },
467
- setLastValueFocusIndex: (ctx2) => {
468
- raf(() => {
469
- ctx2.focusedIndex = Math.min(ctx2.filledValueLength, ctx2.valueLength - 1);
470
- });
471
- },
472
- preventDefault(_, evt) {
473
- evt.preventDefault();
474
- },
475
- blurFocusedInputIfNeeded(ctx2) {
476
- if (!ctx2.blurOnComplete)
477
- return;
478
- raf(() => {
441
+ (_a = ctx2.onComplete) == null ? void 0 : _a.call(ctx2, { value: Array.from(ctx2.value), valueAsString: ctx2.valueAsString });
442
+ },
443
+ invokeOnChange: (ctx2, evt) => {
479
444
  var _a;
480
- (_a = dom.getFocusedEl(ctx2)) == null ? void 0 : _a.blur();
481
- });
482
- },
483
- requestFormSubmit(ctx2) {
484
- var _a;
485
- if (!ctx2.name)
486
- return;
487
- const input = dom.getHiddenInputEl(ctx2);
488
- (_a = input == null ? void 0 : input.form) == null ? void 0 : _a.requestSubmit();
445
+ if (evt.type === "SETUP")
446
+ return;
447
+ (_a = ctx2.onChange) == null ? void 0 : _a.call(ctx2, { value: Array.from(ctx2.value) });
448
+ dispatchInputValueEvent(dom.getHiddenInputEl(ctx2), ctx2.valueAsString);
449
+ },
450
+ invokeOnInvalid: (ctx2, evt) => {
451
+ var _a;
452
+ (_a = ctx2.onInvalid) == null ? void 0 : _a.call(ctx2, { value: evt.value, index: ctx2.focusedIndex });
453
+ },
454
+ clearFocusedIndex: (ctx2) => {
455
+ ctx2.focusedIndex = -1;
456
+ },
457
+ setValue: (ctx2, evt) => {
458
+ assign(ctx2, evt.value);
459
+ },
460
+ setFocusedIndex: (ctx2, evt) => {
461
+ ctx2.focusedIndex = evt.index;
462
+ },
463
+ setFocusedValue: (ctx2, evt) => {
464
+ ctx2.value[ctx2.focusedIndex] = lastChar(evt.value);
465
+ },
466
+ setPastedValue(ctx2, evt) {
467
+ raf(() => {
468
+ const value = evt.value.substring(0, ctx2.valueLength);
469
+ assign(ctx2, value.split("").filter(Boolean));
470
+ });
471
+ },
472
+ setValueAtIndex: (ctx2, evt) => {
473
+ ctx2.value[evt.index] = lastChar(evt.value);
474
+ },
475
+ clearValue: (ctx2) => {
476
+ assign(ctx2, "");
477
+ },
478
+ clearFocusedValue: (ctx2) => {
479
+ ctx2.value[ctx2.focusedIndex] = "";
480
+ },
481
+ setFocusIndexToFirst: (ctx2) => {
482
+ ctx2.focusedIndex = 0;
483
+ },
484
+ setNextFocusedIndex: (ctx2) => {
485
+ ctx2.focusedIndex = Math.min(ctx2.focusedIndex + 1, ctx2.valueLength - 1);
486
+ },
487
+ setPrevFocusedIndex: (ctx2) => {
488
+ ctx2.focusedIndex = Math.max(ctx2.focusedIndex - 1, 0);
489
+ },
490
+ setLastValueFocusIndex: (ctx2) => {
491
+ raf(() => {
492
+ ctx2.focusedIndex = Math.min(ctx2.filledValueLength, ctx2.valueLength - 1);
493
+ });
494
+ },
495
+ preventDefault(_, evt) {
496
+ evt.preventDefault();
497
+ },
498
+ blurFocusedInputIfNeeded(ctx2) {
499
+ if (!ctx2.blurOnComplete)
500
+ return;
501
+ raf(() => {
502
+ var _a;
503
+ (_a = dom.getFocusedEl(ctx2)) == null ? void 0 : _a.blur();
504
+ });
505
+ },
506
+ requestFormSubmit(ctx2) {
507
+ var _a;
508
+ if (!ctx2.name)
509
+ return;
510
+ const input = dom.getHiddenInputEl(ctx2);
511
+ (_a = input == null ? void 0 : input.form) == null ? void 0 : _a.requestSubmit();
512
+ }
489
513
  }
490
514
  }
491
- });
515
+ );
492
516
  }
493
517
  var REGEX = {
494
518
  numeric: /^[0-9]+$/,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zag-js/pin-input",
3
- "version": "0.1.11",
3
+ "version": "0.1.14",
4
4
  "description": "Core logic for the pin-input widget implemented as a state machine",
5
5
  "keywords": [
6
6
  "js",
@@ -29,12 +29,13 @@
29
29
  "url": "https://github.com/chakra-ui/zag/issues"
30
30
  },
31
31
  "dependencies": {
32
- "@zag-js/core": "0.1.9",
33
- "@zag-js/types": "0.2.3"
32
+ "@zag-js/core": "0.1.10",
33
+ "@zag-js/types": "0.2.5"
34
34
  },
35
35
  "devDependencies": {
36
- "@zag-js/dom-utils": "0.1.8",
37
- "@zag-js/utils": "0.1.3"
36
+ "@zag-js/dom-utils": "0.1.11",
37
+ "@zag-js/form-utils": "0.1.1",
38
+ "@zag-js/utils": "0.1.4"
38
39
  },
39
40
  "scripts": {
40
41
  "build-fast": "tsup src/index.ts --format=esm,cjs",