@zag-js/pin-input 0.1.16 → 0.2.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.d.ts CHANGED
@@ -2,7 +2,7 @@ import { RequiredBy, DirectionProperty, CommonProperties, Context, PropTypes, No
2
2
  import * as _zag_js_core from '@zag-js/core';
3
3
  import { StateMachine } from '@zag-js/core';
4
4
 
5
- declare type IntlMessages = {
5
+ declare type IntlTranslations = {
6
6
  inputLabel: (index: number, length: number) => string;
7
7
  };
8
8
  declare type ElementIds = Partial<{
@@ -81,10 +81,14 @@ declare type PublicContext = DirectionProperty & CommonProperties & {
81
81
  * Whether to blur the input when the value is complete
82
82
  */
83
83
  blurOnComplete?: boolean;
84
+ /**
85
+ * Whether to select input value when input is focused
86
+ */
87
+ selectOnFocus?: boolean;
84
88
  /**
85
89
  * Specifies the localized strings that identifies the accessibility elements and their states
86
90
  */
87
- messages: IntlMessages;
91
+ translations: IntlTranslations;
88
92
  };
89
93
  declare type UserDefinedContext = RequiredBy<PublicContext, "id">;
90
94
  declare type ComputedContext = Readonly<{
@@ -108,6 +112,11 @@ declare type ComputedContext = Readonly<{
108
112
  * The string representation of the input values
109
113
  */
110
114
  valueAsString: string;
115
+ /**
116
+ * @computed
117
+ * The value at focused index
118
+ */
119
+ focusedValue: string;
111
120
  }>;
112
121
  declare type PrivateContext = Context<{}>;
113
122
  declare type MachineContext = PublicContext & PrivateContext & ComputedContext;
package/dist/index.js CHANGED
@@ -1,4 +1,31 @@
1
- // ../../utilities/dom/dist/index.js
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var src_exports = {};
22
+ __export(src_exports, {
23
+ connect: () => connect,
24
+ machine: () => machine
25
+ });
26
+ module.exports = __toCommonJS(src_exports);
27
+
28
+ // ../../utilities/dom/dist/index.mjs
2
29
  var dataAttr = (guard) => {
3
30
  return guard ? "" : void 0;
4
31
  };
@@ -12,22 +39,44 @@ function isWindow(value) {
12
39
  return (value == null ? void 0 : value.toString()) === "[object Window]";
13
40
  }
14
41
  function getDocument(el) {
42
+ var _a;
15
43
  if (isWindow(el))
16
44
  return el.document;
17
45
  if (isDocument(el))
18
46
  return el;
19
- return (el == null ? void 0 : el.ownerDocument) ?? document;
47
+ return (_a = el == null ? void 0 : el.ownerDocument) != null ? _a : document;
20
48
  }
21
49
  function defineDomHelpers(helpers) {
22
50
  const dom2 = {
23
51
  getRootNode: (ctx) => {
24
- var _a;
25
- return ((_a = ctx.getRootNode) == null ? void 0 : _a.call(ctx)) ?? document;
52
+ var _a, _b;
53
+ return (_b = (_a = ctx.getRootNode) == null ? void 0 : _a.call(ctx)) != null ? _b : document;
26
54
  },
27
55
  getDoc: (ctx) => getDocument(dom2.getRootNode(ctx)),
28
- getWin: (ctx) => dom2.getDoc(ctx).defaultView ?? window,
56
+ getWin: (ctx) => {
57
+ var _a;
58
+ return (_a = dom2.getDoc(ctx).defaultView) != null ? _a : window;
59
+ },
29
60
  getActiveElement: (ctx) => dom2.getDoc(ctx).activeElement,
30
- getById: (ctx, id) => dom2.getRootNode(ctx).getElementById(id)
61
+ getById: (ctx, id) => dom2.getRootNode(ctx).getElementById(id),
62
+ createEmitter: (ctx, target) => {
63
+ const win = dom2.getWin(ctx);
64
+ return function emit(evt, detail, options) {
65
+ const { bubbles = true, cancelable, composed = true } = options != null ? options : {};
66
+ const eventName = `zag:${evt}`;
67
+ const init = { bubbles, cancelable, composed, detail };
68
+ const event = new win.CustomEvent(eventName, init);
69
+ target.dispatchEvent(event);
70
+ };
71
+ },
72
+ createListener: (target) => {
73
+ return function listen(evt, handler) {
74
+ const eventName = `zag:${evt}`;
75
+ const listener = (e) => handler(e);
76
+ target.addEventListener(eventName, listener);
77
+ return () => target.removeEventListener(eventName, listener);
78
+ };
79
+ }
31
80
  };
32
81
  return {
33
82
  ...dom2,
@@ -35,7 +84,8 @@ function defineDomHelpers(helpers) {
35
84
  };
36
85
  }
37
86
  function getNativeEvent(e) {
38
- return e.nativeEvent ?? e;
87
+ var _a;
88
+ return (_a = e.nativeEvent) != null ? _a : e;
39
89
  }
40
90
  var isModifiedEvent = (v) => v.ctrlKey || v.altKey || v.metaKey;
41
91
  var rtlKeyMap = {
@@ -52,9 +102,10 @@ var sameKeyMap = {
52
102
  Right: "ArrowRight"
53
103
  };
54
104
  function getEventKey(event, options = {}) {
105
+ var _a;
55
106
  const { dir = "ltr", orientation = "horizontal" } = options;
56
107
  let { key } = event;
57
- key = sameKeyMap[key] ?? key;
108
+ key = (_a = sameKeyMap[key]) != null ? _a : key;
58
109
  const isRtl = dir === "rtl" && orientation === "horizontal";
59
110
  if (isRtl && key in rtlKeyMap) {
60
111
  key = rtlKeyMap[key];
@@ -68,7 +119,8 @@ function raf(fn) {
68
119
  };
69
120
  }
70
121
  function queryAll(root, selector) {
71
- return Array.from((root == null ? void 0 : root.querySelectorAll(selector)) ?? []);
122
+ var _a;
123
+ return Array.from((_a = root == null ? void 0 : root.querySelectorAll(selector)) != null ? _a : []);
72
124
  }
73
125
  var visuallyHiddenStyle = {
74
126
  border: "0",
@@ -83,7 +135,7 @@ var visuallyHiddenStyle = {
83
135
  wordWrap: "normal"
84
136
  };
85
137
 
86
- // ../../utilities/core/dist/index.js
138
+ // ../../utilities/core/dist/index.mjs
87
139
  function invariant(...a) {
88
140
  const m = a.length === 1 ? a[0] : a[1];
89
141
  const c = a.length === 2 ? a[0] : true;
@@ -95,20 +147,20 @@ function invariant(...a) {
95
147
  // src/pin-input.dom.ts
96
148
  var dom = defineDomHelpers({
97
149
  getRootId: (ctx) => {
98
- var _a;
99
- return ((_a = ctx.ids) == null ? void 0 : _a.root) ?? `pin-input:${ctx.id}`;
150
+ var _a, _b;
151
+ return (_b = (_a = ctx.ids) == null ? void 0 : _a.root) != null ? _b : `pin-input:${ctx.id}`;
100
152
  },
101
153
  getInputId: (ctx, id) => {
102
- var _a, _b;
103
- return ((_b = (_a = ctx.ids) == null ? void 0 : _a.input) == null ? void 0 : _b.call(_a, id)) ?? `pin-input:${ctx.id}:${id}`;
154
+ var _a, _b, _c;
155
+ return (_c = (_b = (_a = ctx.ids) == null ? void 0 : _a.input) == null ? void 0 : _b.call(_a, id)) != null ? _c : `pin-input:${ctx.id}:${id}`;
104
156
  },
105
157
  getHiddenInputId: (ctx) => {
106
- var _a;
107
- return ((_a = ctx.ids) == null ? void 0 : _a.hiddenInput) ?? `pin-input:${ctx.id}:hidden`;
158
+ var _a, _b;
159
+ return (_b = (_a = ctx.ids) == null ? void 0 : _a.hiddenInput) != null ? _b : `pin-input:${ctx.id}:hidden`;
108
160
  },
109
161
  getLabelId: (ctx) => {
110
- var _a;
111
- return ((_a = ctx.ids) == null ? void 0 : _a.label) ?? `pin-input:${ctx.id}:label`;
162
+ var _a, _b;
163
+ return (_b = (_a = ctx.ids) == null ? void 0 : _a.label) != null ? _b : `pin-input:${ctx.id}:label`;
112
164
  },
113
165
  getRootEl: (ctx) => dom.getById(ctx, dom.getRootId(ctx)),
114
166
  getElements: (ctx) => {
@@ -126,7 +178,7 @@ function connect(state, send, normalize) {
126
178
  const isValueComplete = state.context.isValueComplete;
127
179
  const isInvalid = state.context.invalid;
128
180
  const focusedIndex = state.context.focusedIndex;
129
- const messages = state.context.messages;
181
+ const translations = state.context.translations;
130
182
  function focus() {
131
183
  var _a;
132
184
  (_a = dom.getFirstInputEl(state.context)) == null ? void 0 : _a.focus();
@@ -187,12 +239,12 @@ function connect(state, send, normalize) {
187
239
  "data-complete": dataAttr(isValueComplete),
188
240
  id: dom.getInputId(state.context, index.toString()),
189
241
  "data-ownedby": dom.getRootId(state.context),
190
- "aria-label": messages.inputLabel(index, state.context.valueLength),
242
+ "aria-label": translations.inputLabel(index, state.context.valueLength),
191
243
  inputMode: state.context.otp || state.context.type === "numeric" ? "numeric" : "text",
192
244
  "aria-invalid": ariaAttr(isInvalid),
193
245
  "data-invalid": dataAttr(isInvalid),
194
246
  type: state.context.mask ? "password" : inputType,
195
- value: state.context.value[index] || "",
247
+ defaultValue: state.context.value[index] || "",
196
248
  autoCapitalize: "none",
197
249
  autoComplete: state.context.otp ? "one-time-code" : "off",
198
250
  placeholder: focusedIndex === index ? "" : state.context.placeholder,
@@ -254,16 +306,18 @@ function connect(state, send, normalize) {
254
306
  }
255
307
 
256
308
  // src/pin-input.machine.ts
257
- import { createMachine, guards } from "@zag-js/core";
309
+ var import_core = require("@zag-js/core");
258
310
 
259
- // ../../utilities/form-utils/dist/index.js
311
+ // ../../utilities/form-utils/dist/index.mjs
260
312
  function getWindow(el) {
261
- return (el == null ? void 0 : el.ownerDocument.defaultView) ?? window;
313
+ var _a;
314
+ return (_a = el == null ? void 0 : el.ownerDocument.defaultView) != null ? _a : window;
262
315
  }
263
316
  function getDescriptor(el, options) {
317
+ var _a;
264
318
  const { type, property } = options;
265
319
  const proto = getWindow(el)[type].prototype;
266
- return Object.getOwnPropertyDescriptor(proto, property) ?? {};
320
+ return (_a = Object.getOwnPropertyDescriptor(proto, property)) != null ? _a : {};
267
321
  }
268
322
  function dispatchInputValueEvent(el, value) {
269
323
  var _a;
@@ -279,9 +333,9 @@ function dispatchInputValueEvent(el, value) {
279
333
  }
280
334
 
281
335
  // src/pin-input.machine.ts
282
- var { and, not } = guards;
336
+ var { and, not } = import_core.guards;
283
337
  function machine(ctx) {
284
- return createMachine(
338
+ return (0, import_core.createMachine)(
285
339
  {
286
340
  id: "pin-input",
287
341
  initial: "unknown",
@@ -292,20 +346,21 @@ function machine(ctx) {
292
346
  otp: false,
293
347
  type: "numeric",
294
348
  ...ctx,
295
- messages: {
349
+ translations: {
296
350
  inputLabel: (index, length) => `pin code ${index + 1} of ${length}`,
297
- ...ctx.messages
351
+ ...ctx.translations
298
352
  }
299
353
  },
300
354
  computed: {
301
355
  valueLength: (ctx2) => ctx2.value.length,
302
356
  filledValueLength: (ctx2) => ctx2.value.filter((v) => (v == null ? void 0 : v.trim()) !== "").length,
303
357
  isValueComplete: (ctx2) => ctx2.valueLength === ctx2.filledValueLength,
304
- valueAsString: (ctx2) => ctx2.value.join("")
358
+ valueAsString: (ctx2) => ctx2.value.join(""),
359
+ focusedValue: (ctx2) => ctx2.value[ctx2.focusedIndex]
305
360
  },
306
361
  watch: {
307
- focusedIndex: "focusInput",
308
- value: "invokeOnChange",
362
+ focusedIndex: ["focusInput", "setInputSelection"],
363
+ value: ["invokeOnChange", "dispatchInputEvent"],
309
364
  isValueComplete: ["invokeOnComplete", "blurFocusedInputIfNeeded"]
310
365
  },
311
366
  on: {
@@ -355,17 +410,20 @@ function machine(ctx) {
355
410
  INPUT: [
356
411
  {
357
412
  guard: and("isFinalValue", "isValidValue"),
358
- actions: "setFocusedValue"
413
+ actions: ["setFocusedValue", "dispatchInputEventIfNeeded"]
359
414
  },
360
415
  {
361
416
  guard: "isValidValue",
362
- actions: ["setFocusedValue", "setNextFocusedIndex"]
417
+ actions: ["setFocusedValue", "setNextFocusedIndex", "dispatchInputEventIfNeeded"]
363
418
  }
364
419
  ],
365
- PASTE: {
366
- guard: "isValidValue",
367
- actions: ["setPastedValue", "setLastValueFocusIndex"]
368
- },
420
+ PASTE: [
421
+ {
422
+ guard: "isValidValue",
423
+ actions: ["setPastedValue", "setLastValueFocusIndex"]
424
+ },
425
+ { actions: "resetFocusedValue" }
426
+ ],
369
427
  BLUR: {
370
428
  target: "idle",
371
429
  actions: "clearFocusedIndex"
@@ -434,6 +492,16 @@ function machine(ctx) {
434
492
  (_a = dom.getFocusedEl(ctx2)) == null ? void 0 : _a.focus();
435
493
  });
436
494
  },
495
+ setInputSelection: (ctx2) => {
496
+ raf(() => {
497
+ if (ctx2.focusedIndex === -1)
498
+ return;
499
+ const input = dom.getFocusedEl(ctx2);
500
+ const length = input.value.length;
501
+ input.selectionStart = ctx2.selectOnFocus ? 0 : length;
502
+ input.selectionEnd = length;
503
+ });
504
+ },
437
505
  invokeOnComplete: (ctx2) => {
438
506
  var _a;
439
507
  if (!ctx2.isValueComplete)
@@ -445,7 +513,16 @@ function machine(ctx) {
445
513
  if (evt.type === "SETUP")
446
514
  return;
447
515
  (_a = ctx2.onChange) == null ? void 0 : _a.call(ctx2, { value: Array.from(ctx2.value) });
516
+ },
517
+ dispatchInputEvent: (ctx2, evt) => {
518
+ if (evt.type === "SETUP")
519
+ return;
448
520
  dispatchInputValueEvent(dom.getHiddenInputEl(ctx2), ctx2.valueAsString);
521
+ const inputs = dom.getElements(ctx2);
522
+ ctx2.value.forEach((val, index) => {
523
+ const input = inputs[index];
524
+ input.value = val || "";
525
+ });
449
526
  },
450
527
  invokeOnInvalid: (ctx2, evt) => {
451
528
  var _a;
@@ -463,21 +540,34 @@ function machine(ctx) {
463
540
  setFocusedValue: (ctx2, evt) => {
464
541
  ctx2.value[ctx2.focusedIndex] = lastChar(evt.value);
465
542
  },
543
+ dispatchInputEventIfNeeded: (ctx2, evt) => {
544
+ const valueIsChanged = lastChar(evt.value) !== ctx2.focusedValue;
545
+ if (evt.value.length <= 1 || valueIsChanged)
546
+ return;
547
+ const inputs = dom.getElements(ctx2);
548
+ const input = inputs[ctx2.focusedIndex];
549
+ input.value = lastChar(evt.value);
550
+ },
466
551
  setPastedValue(ctx2, evt) {
467
552
  raf(() => {
468
- const value = evt.value.substring(0, ctx2.valueLength);
469
- assign(ctx2, value.split("").filter(Boolean));
553
+ const startIndex = ctx2.focusedValue ? 1 : 0;
554
+ const value = evt.value.substring(startIndex, ctx2.valueLength);
555
+ assign(ctx2, value);
470
556
  });
471
557
  },
472
558
  setValueAtIndex: (ctx2, evt) => {
473
559
  ctx2.value[evt.index] = lastChar(evt.value);
474
560
  },
475
561
  clearValue: (ctx2) => {
476
- assign(ctx2, "");
562
+ ctx2.value = ctx2.value.map(() => "");
477
563
  },
478
564
  clearFocusedValue: (ctx2) => {
479
565
  ctx2.value[ctx2.focusedIndex] = "";
480
566
  },
567
+ resetFocusedValue: (ctx2) => {
568
+ const input = dom.getFocusedEl(ctx2);
569
+ input.value = ctx2.focusedValue;
570
+ },
481
571
  setFocusIndexToFirst: (ctx2) => {
482
572
  ctx2.focusedIndex = 0;
483
573
  },
@@ -526,21 +616,16 @@ function isValidType(value, type) {
526
616
  return !!((_a = REGEX[type]) == null ? void 0 : _a.test(value));
527
617
  }
528
618
  function assign(ctx, value) {
529
- const len = ctx.value.length;
530
- for (let i = 0; i < len; i++) {
531
- if (Array.isArray(value)) {
532
- if (!value[i])
533
- continue;
534
- ctx.value[i] = value[i];
535
- } else {
536
- ctx.value[i] = value;
537
- }
538
- }
619
+ const valueArr = value.split("").filter(Boolean);
620
+ const valueObj = Object.assign({}, ctx.value, valueArr);
621
+ console.log("valueObj :>> ", valueObj);
622
+ ctx.value = Object.values(valueObj);
539
623
  }
540
624
  function lastChar(value) {
541
625
  return value.charAt(value.length - 1);
542
626
  }
543
- export {
627
+ // Annotate the CommonJS export names for ESM import in node:
628
+ 0 && (module.exports = {
544
629
  connect,
545
630
  machine
546
- };
631
+ });
package/dist/index.mjs ADDED
@@ -0,0 +1,603 @@
1
+ // ../../utilities/dom/dist/index.mjs
2
+ var dataAttr = (guard) => {
3
+ return guard ? "" : void 0;
4
+ };
5
+ var ariaAttr = (guard) => {
6
+ return guard ? "true" : void 0;
7
+ };
8
+ function isDocument(el) {
9
+ return el.nodeType === Node.DOCUMENT_NODE;
10
+ }
11
+ function isWindow(value) {
12
+ return (value == null ? void 0 : value.toString()) === "[object Window]";
13
+ }
14
+ function getDocument(el) {
15
+ var _a;
16
+ if (isWindow(el))
17
+ return el.document;
18
+ if (isDocument(el))
19
+ return el;
20
+ return (_a = el == null ? void 0 : el.ownerDocument) != null ? _a : document;
21
+ }
22
+ function defineDomHelpers(helpers) {
23
+ const dom2 = {
24
+ getRootNode: (ctx) => {
25
+ var _a, _b;
26
+ return (_b = (_a = ctx.getRootNode) == null ? void 0 : _a.call(ctx)) != null ? _b : document;
27
+ },
28
+ getDoc: (ctx) => getDocument(dom2.getRootNode(ctx)),
29
+ getWin: (ctx) => {
30
+ var _a;
31
+ return (_a = dom2.getDoc(ctx).defaultView) != null ? _a : window;
32
+ },
33
+ getActiveElement: (ctx) => dom2.getDoc(ctx).activeElement,
34
+ getById: (ctx, id) => dom2.getRootNode(ctx).getElementById(id),
35
+ createEmitter: (ctx, target) => {
36
+ const win = dom2.getWin(ctx);
37
+ return function emit(evt, detail, options) {
38
+ const { bubbles = true, cancelable, composed = true } = options != null ? options : {};
39
+ const eventName = `zag:${evt}`;
40
+ const init = { bubbles, cancelable, composed, detail };
41
+ const event = new win.CustomEvent(eventName, init);
42
+ target.dispatchEvent(event);
43
+ };
44
+ },
45
+ createListener: (target) => {
46
+ return function listen(evt, handler) {
47
+ const eventName = `zag:${evt}`;
48
+ const listener = (e) => handler(e);
49
+ target.addEventListener(eventName, listener);
50
+ return () => target.removeEventListener(eventName, listener);
51
+ };
52
+ }
53
+ };
54
+ return {
55
+ ...dom2,
56
+ ...helpers
57
+ };
58
+ }
59
+ function getNativeEvent(e) {
60
+ var _a;
61
+ return (_a = e.nativeEvent) != null ? _a : e;
62
+ }
63
+ var isModifiedEvent = (v) => v.ctrlKey || v.altKey || v.metaKey;
64
+ var rtlKeyMap = {
65
+ ArrowLeft: "ArrowRight",
66
+ ArrowRight: "ArrowLeft"
67
+ };
68
+ var sameKeyMap = {
69
+ Up: "ArrowUp",
70
+ Down: "ArrowDown",
71
+ Esc: "Escape",
72
+ " ": "Space",
73
+ ",": "Comma",
74
+ Left: "ArrowLeft",
75
+ Right: "ArrowRight"
76
+ };
77
+ function getEventKey(event, options = {}) {
78
+ var _a;
79
+ const { dir = "ltr", orientation = "horizontal" } = options;
80
+ let { key } = event;
81
+ key = (_a = sameKeyMap[key]) != null ? _a : key;
82
+ const isRtl = dir === "rtl" && orientation === "horizontal";
83
+ if (isRtl && key in rtlKeyMap) {
84
+ key = rtlKeyMap[key];
85
+ }
86
+ return key;
87
+ }
88
+ function raf(fn) {
89
+ const id = globalThis.requestAnimationFrame(fn);
90
+ return function cleanup() {
91
+ globalThis.cancelAnimationFrame(id);
92
+ };
93
+ }
94
+ function queryAll(root, selector) {
95
+ var _a;
96
+ return Array.from((_a = root == null ? void 0 : root.querySelectorAll(selector)) != null ? _a : []);
97
+ }
98
+ var visuallyHiddenStyle = {
99
+ border: "0",
100
+ clip: "rect(0 0 0 0)",
101
+ height: "1px",
102
+ margin: "-1px",
103
+ overflow: "hidden",
104
+ padding: "0",
105
+ position: "absolute",
106
+ width: "1px",
107
+ whiteSpace: "nowrap",
108
+ wordWrap: "normal"
109
+ };
110
+
111
+ // ../../utilities/core/dist/index.mjs
112
+ function invariant(...a) {
113
+ const m = a.length === 1 ? a[0] : a[1];
114
+ const c = a.length === 2 ? a[0] : true;
115
+ if (c && process.env.NODE_ENV !== "production") {
116
+ throw new Error(m);
117
+ }
118
+ }
119
+
120
+ // src/pin-input.dom.ts
121
+ var dom = defineDomHelpers({
122
+ getRootId: (ctx) => {
123
+ var _a, _b;
124
+ return (_b = (_a = ctx.ids) == null ? void 0 : _a.root) != null ? _b : `pin-input:${ctx.id}`;
125
+ },
126
+ getInputId: (ctx, id) => {
127
+ var _a, _b, _c;
128
+ return (_c = (_b = (_a = ctx.ids) == null ? void 0 : _a.input) == null ? void 0 : _b.call(_a, id)) != null ? _c : `pin-input:${ctx.id}:${id}`;
129
+ },
130
+ getHiddenInputId: (ctx) => {
131
+ var _a, _b;
132
+ return (_b = (_a = ctx.ids) == null ? void 0 : _a.hiddenInput) != null ? _b : `pin-input:${ctx.id}:hidden`;
133
+ },
134
+ getLabelId: (ctx) => {
135
+ var _a, _b;
136
+ return (_b = (_a = ctx.ids) == null ? void 0 : _a.label) != null ? _b : `pin-input:${ctx.id}:label`;
137
+ },
138
+ getRootEl: (ctx) => dom.getById(ctx, dom.getRootId(ctx)),
139
+ getElements: (ctx) => {
140
+ const ownerId = CSS.escape(dom.getRootId(ctx));
141
+ const selector = `input[data-ownedby=${ownerId}]`;
142
+ return queryAll(dom.getRootEl(ctx), selector);
143
+ },
144
+ getFocusedEl: (ctx) => dom.getElements(ctx)[ctx.focusedIndex],
145
+ getFirstInputEl: (ctx) => dom.getElements(ctx)[0],
146
+ getHiddenInputEl: (ctx) => dom.getById(ctx, dom.getHiddenInputId(ctx))
147
+ });
148
+
149
+ // src/pin-input.connect.ts
150
+ function connect(state, send, normalize) {
151
+ const isValueComplete = state.context.isValueComplete;
152
+ const isInvalid = state.context.invalid;
153
+ const focusedIndex = state.context.focusedIndex;
154
+ const translations = state.context.translations;
155
+ function focus() {
156
+ var _a;
157
+ (_a = dom.getFirstInputEl(state.context)) == null ? void 0 : _a.focus();
158
+ }
159
+ return {
160
+ value: state.context.value,
161
+ valueAsString: state.context.valueAsString,
162
+ isValueComplete,
163
+ setValue(value) {
164
+ if (!Array.isArray(value)) {
165
+ invariant("[pin-input/setValue] value must be an array");
166
+ }
167
+ send({ type: "SET_VALUE", value });
168
+ },
169
+ clearValue() {
170
+ send({ type: "CLEAR_VALUE" });
171
+ },
172
+ setValueAtIndex(index, value) {
173
+ send({ type: "SET_VALUE", value, index });
174
+ },
175
+ focus,
176
+ rootProps: normalize.element({
177
+ dir: state.context.dir,
178
+ "data-part": "root",
179
+ id: dom.getRootId(state.context),
180
+ "data-invalid": dataAttr(isInvalid),
181
+ "data-disabled": dataAttr(state.context.disabled),
182
+ "data-complete": dataAttr(isValueComplete)
183
+ }),
184
+ labelProps: normalize.label({
185
+ "data-part": "label",
186
+ htmlFor: dom.getHiddenInputId(state.context),
187
+ id: dom.getLabelId(state.context),
188
+ "data-invalid": dataAttr(isInvalid),
189
+ "data-disabled": dataAttr(state.context.disabled),
190
+ "data-complete": dataAttr(isValueComplete),
191
+ onClick: (event) => {
192
+ event.preventDefault();
193
+ focus();
194
+ }
195
+ }),
196
+ hiddenInputProps: normalize.input({
197
+ "aria-hidden": true,
198
+ type: "text",
199
+ tabIndex: -1,
200
+ id: dom.getHiddenInputId(state.context),
201
+ name: state.context.name,
202
+ style: visuallyHiddenStyle,
203
+ maxLength: state.context.valueLength,
204
+ defaultValue: state.context.valueAsString
205
+ }),
206
+ getInputProps({ index }) {
207
+ const inputType = state.context.type === "numeric" ? "tel" : "text";
208
+ return normalize.input({
209
+ "data-part": "input",
210
+ disabled: state.context.disabled,
211
+ "data-disabled": dataAttr(state.context.disabled),
212
+ "data-complete": dataAttr(isValueComplete),
213
+ id: dom.getInputId(state.context, index.toString()),
214
+ "data-ownedby": dom.getRootId(state.context),
215
+ "aria-label": translations.inputLabel(index, state.context.valueLength),
216
+ inputMode: state.context.otp || state.context.type === "numeric" ? "numeric" : "text",
217
+ "aria-invalid": ariaAttr(isInvalid),
218
+ "data-invalid": dataAttr(isInvalid),
219
+ type: state.context.mask ? "password" : inputType,
220
+ defaultValue: state.context.value[index] || "",
221
+ autoCapitalize: "none",
222
+ autoComplete: state.context.otp ? "one-time-code" : "off",
223
+ placeholder: focusedIndex === index ? "" : state.context.placeholder,
224
+ onChange(event) {
225
+ const evt = getNativeEvent(event);
226
+ const { value } = event.currentTarget;
227
+ if (evt.inputType === "insertFromPaste" || value.length > 2) {
228
+ send({ type: "PASTE", value });
229
+ event.preventDefault();
230
+ return;
231
+ }
232
+ if (evt.inputType === "deleteContentBackward") {
233
+ send("BACKSPACE");
234
+ return;
235
+ }
236
+ send({ type: "INPUT", value });
237
+ },
238
+ onKeyDown(event) {
239
+ const evt = getNativeEvent(event);
240
+ if (evt.isComposing || isModifiedEvent(evt))
241
+ return;
242
+ const keyMap = {
243
+ Backspace() {
244
+ send("BACKSPACE");
245
+ },
246
+ Delete() {
247
+ send("DELETE");
248
+ },
249
+ ArrowLeft() {
250
+ send("ARROW_LEFT");
251
+ },
252
+ ArrowRight() {
253
+ send("ARROW_RIGHT");
254
+ },
255
+ Enter() {
256
+ send("ENTER");
257
+ }
258
+ };
259
+ const key = getEventKey(event, { dir: state.context.dir });
260
+ const exec = keyMap[key];
261
+ if (exec) {
262
+ exec(event);
263
+ event.preventDefault();
264
+ } else {
265
+ if (key === "Tab")
266
+ return;
267
+ send({ type: "KEY_DOWN", value: key, preventDefault: () => event.preventDefault() });
268
+ }
269
+ },
270
+ onFocus() {
271
+ send({ type: "FOCUS", index });
272
+ },
273
+ onBlur() {
274
+ send({ type: "BLUR", index });
275
+ }
276
+ });
277
+ }
278
+ };
279
+ }
280
+
281
+ // src/pin-input.machine.ts
282
+ import { createMachine, guards } from "@zag-js/core";
283
+
284
+ // ../../utilities/form-utils/dist/index.mjs
285
+ function getWindow(el) {
286
+ var _a;
287
+ return (_a = el == null ? void 0 : el.ownerDocument.defaultView) != null ? _a : window;
288
+ }
289
+ function getDescriptor(el, options) {
290
+ var _a;
291
+ const { type, property } = options;
292
+ const proto = getWindow(el)[type].prototype;
293
+ return (_a = Object.getOwnPropertyDescriptor(proto, property)) != null ? _a : {};
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
309
+ var { and, not } = guards;
310
+ function machine(ctx) {
311
+ return 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
+ translations: {
323
+ inputLabel: (index, length) => `pin code ${index + 1} of ${length}`,
324
+ ...ctx.translations
325
+ }
326
+ },
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(""),
332
+ focusedValue: (ctx2) => ctx2.value[ctx2.focusedIndex]
333
+ },
334
+ watch: {
335
+ focusedIndex: ["focusInput", "setInputSelection"],
336
+ value: ["invokeOnChange", "dispatchInputEvent"],
337
+ isValueComplete: ["invokeOnComplete", "blurFocusedInputIfNeeded"]
338
+ },
339
+ on: {
340
+ SET_VALUE: [
341
+ {
342
+ guard: "hasIndex",
343
+ actions: "setValueAtIndex"
344
+ },
345
+ { actions: "setValue" }
346
+ ],
347
+ CLEAR_VALUE: [
348
+ {
349
+ guard: "isDisabled",
350
+ actions: "clearValue"
351
+ },
352
+ {
353
+ actions: ["clearValue", "setFocusIndexToFirst"]
354
+ }
355
+ ]
356
+ },
357
+ states: {
358
+ unknown: {
359
+ on: {
360
+ SETUP: [
361
+ {
362
+ guard: "autoFocus",
363
+ target: "focused",
364
+ actions: ["setupValue", "setFocusIndexToFirst"]
365
+ },
366
+ {
367
+ target: "idle",
368
+ actions: "setupValue"
369
+ }
370
+ ]
371
+ }
372
+ },
373
+ idle: {
374
+ on: {
375
+ FOCUS: {
376
+ target: "focused",
377
+ actions: "setFocusedIndex"
378
+ }
379
+ }
380
+ },
381
+ focused: {
382
+ on: {
383
+ INPUT: [
384
+ {
385
+ guard: and("isFinalValue", "isValidValue"),
386
+ actions: ["setFocusedValue", "dispatchInputEventIfNeeded"]
387
+ },
388
+ {
389
+ guard: "isValidValue",
390
+ actions: ["setFocusedValue", "setNextFocusedIndex", "dispatchInputEventIfNeeded"]
391
+ }
392
+ ],
393
+ PASTE: [
394
+ {
395
+ guard: "isValidValue",
396
+ actions: ["setPastedValue", "setLastValueFocusIndex"]
397
+ },
398
+ { actions: "resetFocusedValue" }
399
+ ],
400
+ BLUR: {
401
+ target: "idle",
402
+ actions: "clearFocusedIndex"
403
+ },
404
+ DELETE: {
405
+ guard: "hasValue",
406
+ actions: "clearFocusedValue"
407
+ },
408
+ ARROW_LEFT: {
409
+ actions: "setPrevFocusedIndex"
410
+ },
411
+ ARROW_RIGHT: {
412
+ actions: "setNextFocusedIndex"
413
+ },
414
+ BACKSPACE: [
415
+ {
416
+ guard: "hasValue",
417
+ actions: "clearFocusedValue"
418
+ },
419
+ {
420
+ actions: ["setPrevFocusedIndex", "clearFocusedValue"]
421
+ }
422
+ ],
423
+ ENTER: {
424
+ guard: "isValueComplete",
425
+ actions: "requestFormSubmit"
426
+ },
427
+ KEY_DOWN: {
428
+ guard: not("isValidValue"),
429
+ actions: ["preventDefault", "invokeOnInvalid"]
430
+ }
431
+ }
432
+ }
433
+ }
434
+ },
435
+ {
436
+ guards: {
437
+ autoFocus: (ctx2) => !!ctx2.autoFocus,
438
+ isValueEmpty: (_ctx, evt) => evt.value === "",
439
+ hasValue: (ctx2) => ctx2.value[ctx2.focusedIndex] !== "",
440
+ isValueComplete: (ctx2) => ctx2.isValueComplete,
441
+ isValidValue: (ctx2, evt) => {
442
+ if (!ctx2.pattern)
443
+ return isValidType(evt.value, ctx2.type);
444
+ const regex = new RegExp(ctx2.pattern, "g");
445
+ return regex.test(evt.value);
446
+ },
447
+ isFinalValue: (ctx2) => {
448
+ return ctx2.filledValueLength + 1 === ctx2.valueLength && ctx2.value.findIndex((v) => v.trim() === "") === ctx2.focusedIndex;
449
+ },
450
+ isLastInputFocused: (ctx2) => ctx2.focusedIndex === ctx2.valueLength - 1,
451
+ hasIndex: (_ctx, evt) => evt.index !== void 0,
452
+ isDisabled: (ctx2) => !!ctx2.disabled
453
+ },
454
+ actions: {
455
+ setupValue: (ctx2) => {
456
+ const inputs = dom.getElements(ctx2);
457
+ const empty = Array.from({ length: inputs.length }).map(() => "");
458
+ ctx2.value = Object.assign(empty, ctx2.value);
459
+ },
460
+ focusInput: (ctx2) => {
461
+ raf(() => {
462
+ var _a;
463
+ if (ctx2.focusedIndex === -1)
464
+ return;
465
+ (_a = dom.getFocusedEl(ctx2)) == null ? void 0 : _a.focus();
466
+ });
467
+ },
468
+ setInputSelection: (ctx2) => {
469
+ raf(() => {
470
+ if (ctx2.focusedIndex === -1)
471
+ return;
472
+ const input = dom.getFocusedEl(ctx2);
473
+ const length = input.value.length;
474
+ input.selectionStart = ctx2.selectOnFocus ? 0 : length;
475
+ input.selectionEnd = length;
476
+ });
477
+ },
478
+ invokeOnComplete: (ctx2) => {
479
+ var _a;
480
+ if (!ctx2.isValueComplete)
481
+ return;
482
+ (_a = ctx2.onComplete) == null ? void 0 : _a.call(ctx2, { value: Array.from(ctx2.value), valueAsString: ctx2.valueAsString });
483
+ },
484
+ invokeOnChange: (ctx2, evt) => {
485
+ var _a;
486
+ if (evt.type === "SETUP")
487
+ return;
488
+ (_a = ctx2.onChange) == null ? void 0 : _a.call(ctx2, { value: Array.from(ctx2.value) });
489
+ },
490
+ dispatchInputEvent: (ctx2, evt) => {
491
+ if (evt.type === "SETUP")
492
+ return;
493
+ dispatchInputValueEvent(dom.getHiddenInputEl(ctx2), ctx2.valueAsString);
494
+ const inputs = dom.getElements(ctx2);
495
+ ctx2.value.forEach((val, index) => {
496
+ const input = inputs[index];
497
+ input.value = val || "";
498
+ });
499
+ },
500
+ invokeOnInvalid: (ctx2, evt) => {
501
+ var _a;
502
+ (_a = ctx2.onInvalid) == null ? void 0 : _a.call(ctx2, { value: evt.value, index: ctx2.focusedIndex });
503
+ },
504
+ clearFocusedIndex: (ctx2) => {
505
+ ctx2.focusedIndex = -1;
506
+ },
507
+ setValue: (ctx2, evt) => {
508
+ assign(ctx2, evt.value);
509
+ },
510
+ setFocusedIndex: (ctx2, evt) => {
511
+ ctx2.focusedIndex = evt.index;
512
+ },
513
+ setFocusedValue: (ctx2, evt) => {
514
+ ctx2.value[ctx2.focusedIndex] = lastChar(evt.value);
515
+ },
516
+ dispatchInputEventIfNeeded: (ctx2, evt) => {
517
+ const valueIsChanged = lastChar(evt.value) !== ctx2.focusedValue;
518
+ if (evt.value.length <= 1 || valueIsChanged)
519
+ return;
520
+ const inputs = dom.getElements(ctx2);
521
+ const input = inputs[ctx2.focusedIndex];
522
+ input.value = lastChar(evt.value);
523
+ },
524
+ setPastedValue(ctx2, evt) {
525
+ raf(() => {
526
+ const startIndex = ctx2.focusedValue ? 1 : 0;
527
+ const value = evt.value.substring(startIndex, ctx2.valueLength);
528
+ assign(ctx2, value);
529
+ });
530
+ },
531
+ setValueAtIndex: (ctx2, evt) => {
532
+ ctx2.value[evt.index] = lastChar(evt.value);
533
+ },
534
+ clearValue: (ctx2) => {
535
+ ctx2.value = ctx2.value.map(() => "");
536
+ },
537
+ clearFocusedValue: (ctx2) => {
538
+ ctx2.value[ctx2.focusedIndex] = "";
539
+ },
540
+ resetFocusedValue: (ctx2) => {
541
+ const input = dom.getFocusedEl(ctx2);
542
+ input.value = ctx2.focusedValue;
543
+ },
544
+ setFocusIndexToFirst: (ctx2) => {
545
+ ctx2.focusedIndex = 0;
546
+ },
547
+ setNextFocusedIndex: (ctx2) => {
548
+ ctx2.focusedIndex = Math.min(ctx2.focusedIndex + 1, ctx2.valueLength - 1);
549
+ },
550
+ setPrevFocusedIndex: (ctx2) => {
551
+ ctx2.focusedIndex = Math.max(ctx2.focusedIndex - 1, 0);
552
+ },
553
+ setLastValueFocusIndex: (ctx2) => {
554
+ raf(() => {
555
+ ctx2.focusedIndex = Math.min(ctx2.filledValueLength, ctx2.valueLength - 1);
556
+ });
557
+ },
558
+ preventDefault(_, evt) {
559
+ evt.preventDefault();
560
+ },
561
+ blurFocusedInputIfNeeded(ctx2) {
562
+ if (!ctx2.blurOnComplete)
563
+ return;
564
+ raf(() => {
565
+ var _a;
566
+ (_a = dom.getFocusedEl(ctx2)) == null ? void 0 : _a.blur();
567
+ });
568
+ },
569
+ requestFormSubmit(ctx2) {
570
+ var _a;
571
+ if (!ctx2.name)
572
+ return;
573
+ const input = dom.getHiddenInputEl(ctx2);
574
+ (_a = input == null ? void 0 : input.form) == null ? void 0 : _a.requestSubmit();
575
+ }
576
+ }
577
+ }
578
+ );
579
+ }
580
+ var REGEX = {
581
+ numeric: /^[0-9]+$/,
582
+ alphabetic: /^[A-Za-z]+$/,
583
+ alphanumeric: /^[a-zA-Z0-9]+$/i
584
+ };
585
+ function isValidType(value, type) {
586
+ var _a;
587
+ if (!type)
588
+ return true;
589
+ return !!((_a = REGEX[type]) == null ? void 0 : _a.test(value));
590
+ }
591
+ function assign(ctx, value) {
592
+ const valueArr = value.split("").filter(Boolean);
593
+ const valueObj = Object.assign({}, ctx.value, valueArr);
594
+ console.log("valueObj :>> ", valueObj);
595
+ ctx.value = Object.values(valueObj);
596
+ }
597
+ function lastChar(value) {
598
+ return value.charAt(value.length - 1);
599
+ }
600
+ export {
601
+ connect,
602
+ machine
603
+ };
package/package.json CHANGED
@@ -1,8 +1,10 @@
1
1
  {
2
- "type": "module",
3
2
  "name": "@zag-js/pin-input",
4
- "version": "0.1.16",
3
+ "version": "0.2.0",
5
4
  "description": "Core logic for the pin-input widget implemented as a state machine",
5
+ "main": "dist/index.js",
6
+ "module": "dist/index.mjs",
7
+ "types": "dist/index.d.ts",
6
8
  "keywords": [
7
9
  "js",
8
10
  "machine",
@@ -15,8 +17,6 @@
15
17
  "author": "Segun Adebayo <sage@adebayosegun.com>",
16
18
  "homepage": "https://github.com/chakra-ui/zag#readme",
17
19
  "license": "MIT",
18
- "main": "dist/index.js",
19
- "types": "dist/index.d.ts",
20
20
  "repository": "https://github.com/chakra-ui/zag/tree/main/packages/pin-input",
21
21
  "sideEffects": false,
22
22
  "files": [
@@ -29,18 +29,18 @@
29
29
  "url": "https://github.com/chakra-ui/zag/issues"
30
30
  },
31
31
  "dependencies": {
32
- "@zag-js/core": "0.1.11",
33
- "@zag-js/types": "0.2.6"
32
+ "@zag-js/core": "0.2.0",
33
+ "@zag-js/types": "0.3.0"
34
34
  },
35
35
  "devDependencies": {
36
- "@zag-js/dom-utils": "0.1.12",
37
- "@zag-js/form-utils": "0.1.2",
38
- "@zag-js/utils": "0.1.5"
36
+ "@zag-js/dom-utils": "0.2.0",
37
+ "@zag-js/form-utils": "0.2.0",
38
+ "@zag-js/utils": "0.2.0"
39
39
  },
40
40
  "scripts": {
41
- "build-fast": "tsup src/index.ts --format=esm",
41
+ "build-fast": "tsup src/index.ts --format=esm,cjs",
42
42
  "start": "pnpm build --watch",
43
- "build": "tsup src/index.ts --format=esm --dts",
43
+ "build": "tsup src/index.ts --format=esm,cjs --dts",
44
44
  "test": "jest --config ../../../jest.config.js --rootDir . --passWithNoTests",
45
45
  "lint": "eslint src --ext .ts,.tsx",
46
46
  "test-ci": "pnpm test --ci --runInBand",