@zag-js/pin-input 0.1.8 → 0.1.11

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2021 Chakra UI
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/dist/index.d.ts CHANGED
@@ -1,3 +1,136 @@
1
- export { connect } from "./pin-input.connect";
2
- export { machine } from "./pin-input.machine";
3
- export type { UserDefinedContext as Context } from "./pin-input.types";
1
+ import { RequiredBy, DirectionProperty, CommonProperties, Context, PropTypes, NormalizeProps } from '@zag-js/types';
2
+ import * as _zag_js_core from '@zag-js/core';
3
+ import { StateMachine } from '@zag-js/core';
4
+
5
+ declare type IntlMessages = {
6
+ inputLabel: (index: number, length: number) => string;
7
+ };
8
+ declare type ElementIds = Partial<{
9
+ root: string;
10
+ hiddenInput: string;
11
+ input(id: string): string;
12
+ }>;
13
+ declare type PublicContext = DirectionProperty & CommonProperties & {
14
+ /**
15
+ * The name of the input element. Useful for form submission.
16
+ */
17
+ name?: string;
18
+ /**
19
+ * The regular expression that the user-entered input value is checked against.
20
+ */
21
+ pattern?: string;
22
+ /**
23
+ * The ids of the elements in the pin input. Useful for composition.
24
+ */
25
+ ids?: ElementIds;
26
+ /**
27
+ * Whether the inputs are disabled
28
+ */
29
+ disabled?: boolean;
30
+ /**
31
+ * The placeholder text for the input
32
+ */
33
+ placeholder?: string;
34
+ /**
35
+ * Whether to auto-focus the first input.
36
+ */
37
+ autoFocus?: boolean;
38
+ /**
39
+ * Whether the pin input is in the invalid state
40
+ */
41
+ invalid?: boolean;
42
+ /**
43
+ * If `true`, the pin input component signals to its fields that they should
44
+ * use `autocomplete="one-time-code"`.
45
+ */
46
+ otp?: boolean;
47
+ /**
48
+ * The value of the the pin input.
49
+ */
50
+ value: string[];
51
+ /**
52
+ * The type of value the pin-input should allow
53
+ */
54
+ type?: "alphanumeric" | "numeric" | "alphabetic";
55
+ /**
56
+ * Function called when all inputs have valid values
57
+ */
58
+ onComplete?: (details: {
59
+ value: string[];
60
+ valueAsString: string;
61
+ }) => void;
62
+ /**
63
+ * Function called on input change
64
+ */
65
+ onChange?: (details: {
66
+ value: string[];
67
+ }) => void;
68
+ /**
69
+ * Function called when an invalid value is entered
70
+ */
71
+ onInvalid?: (details: {
72
+ value: string;
73
+ index: number;
74
+ }) => void;
75
+ /**
76
+ * If `true`, the input's value will be masked just like `type=password`
77
+ */
78
+ mask?: boolean;
79
+ /**
80
+ * Whether to blur the input when the value is complete
81
+ */
82
+ blurOnComplete?: boolean;
83
+ /**
84
+ * Specifies the localized strings that identifies the accessibility elements and their states
85
+ */
86
+ messages: IntlMessages;
87
+ };
88
+ declare type UserDefinedContext = RequiredBy<PublicContext, "id">;
89
+ declare type ComputedContext = Readonly<{
90
+ /**
91
+ * @computed
92
+ * The number of inputs
93
+ */
94
+ valueLength: number;
95
+ /**
96
+ * @computed
97
+ * The number of inputs that are not empty
98
+ */
99
+ filledValueLength: number;
100
+ /**
101
+ * @computed
102
+ * Whether all input values are valid
103
+ */
104
+ isValueComplete: boolean;
105
+ /**
106
+ * @computed
107
+ * The string representation of the input values
108
+ */
109
+ valueAsString: string;
110
+ }>;
111
+ declare type PrivateContext = Context<{}>;
112
+ declare type MachineContext = PublicContext & PrivateContext & ComputedContext;
113
+ declare type MachineState = {
114
+ value: "unknown" | "idle" | "focused";
115
+ };
116
+ declare type State = StateMachine.State<MachineContext, MachineState>;
117
+ declare type Send = StateMachine.Send<StateMachine.AnyEventObject>;
118
+
119
+ declare function connect<T extends PropTypes>(state: State, send: Send, normalize: NormalizeProps<T>): {
120
+ value: string[];
121
+ valueAsString: string;
122
+ isValueComplete: boolean;
123
+ setValue(value: string[]): void;
124
+ clearValue(): void;
125
+ setValueAtIndex(index: number, value: string): void;
126
+ focus(): void;
127
+ rootProps: T["element"];
128
+ hiddenInputProps: T["input"];
129
+ getInputProps({ index }: {
130
+ index: number;
131
+ }): T["input"];
132
+ };
133
+
134
+ declare function machine(ctx: UserDefinedContext): _zag_js_core.Machine<MachineContext, MachineState, _zag_js_core.StateMachine.AnyEventObject>;
135
+
136
+ export { UserDefinedContext as Context, connect, machine };
package/dist/index.js CHANGED
@@ -1,25 +1,8 @@
1
1
  "use strict";
2
2
  var __defProp = Object.defineProperty;
3
- var __defProps = Object.defineProperties;
4
3
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
- var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
6
4
  var __getOwnPropNames = Object.getOwnPropertyNames;
7
- var __getOwnPropSymbols = Object.getOwnPropertySymbols;
8
5
  var __hasOwnProp = Object.prototype.hasOwnProperty;
9
- var __propIsEnum = Object.prototype.propertyIsEnumerable;
10
- var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
11
- var __spreadValues = (a, b) => {
12
- for (var prop in b || (b = {}))
13
- if (__hasOwnProp.call(b, prop))
14
- __defNormalProp(a, prop, b[prop]);
15
- if (__getOwnPropSymbols)
16
- for (var prop of __getOwnPropSymbols(b)) {
17
- if (__propIsEnum.call(b, prop))
18
- __defNormalProp(a, prop, b[prop]);
19
- }
20
- return a;
21
- };
22
- var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
23
6
  var __export = (target, all) => {
24
7
  for (var name in all)
25
8
  __defProp(target, name, { get: all[name], enumerable: true });
@@ -49,28 +32,58 @@ var dataAttr = (guard) => {
49
32
  var ariaAttr = (guard) => {
50
33
  return guard ? "true" : void 0;
51
34
  };
52
- function nextTick(fn) {
53
- const set = /* @__PURE__ */ new Set();
54
- function raf2(fn2) {
55
- const id = globalThis.requestAnimationFrame(fn2);
56
- set.add(() => globalThis.cancelAnimationFrame(id));
57
- }
58
- raf2(() => raf2(fn));
59
- return function cleanup() {
60
- set.forEach(function(fn2) {
61
- fn2();
62
- });
63
- };
35
+ function isDocument(el) {
36
+ return el.nodeType === Node.DOCUMENT_NODE;
64
37
  }
65
- function raf(fn) {
66
- const id = globalThis.requestAnimationFrame(fn);
67
- return function cleanup() {
68
- globalThis.cancelAnimationFrame(id);
38
+ function isWindow(value) {
39
+ return (value == null ? void 0 : value.toString()) === "[object Window]";
40
+ }
41
+ function getDocument(el) {
42
+ if (isWindow(el))
43
+ return el.document;
44
+ if (isDocument(el))
45
+ return el;
46
+ return (el == null ? void 0 : el.ownerDocument) ?? document;
47
+ }
48
+ function getWindow(el) {
49
+ return (el == null ? void 0 : el.ownerDocument.defaultView) ?? window;
50
+ }
51
+ function defineDomHelpers(helpers) {
52
+ const dom2 = {
53
+ getRootNode: (ctx) => {
54
+ var _a;
55
+ return ((_a = ctx.getRootNode) == null ? void 0 : _a.call(ctx)) ?? document;
56
+ },
57
+ getDoc: (ctx) => getDocument(dom2.getRootNode(ctx)),
58
+ getWin: (ctx) => dom2.getDoc(ctx).defaultView ?? window,
59
+ getActiveElement: (ctx) => dom2.getDoc(ctx).activeElement,
60
+ getById: (ctx, id) => dom2.getRootNode(ctx).getElementById(id)
61
+ };
62
+ return {
63
+ ...dom2,
64
+ ...helpers
69
65
  };
70
66
  }
71
67
  function getNativeEvent(e) {
68
+ return e.nativeEvent ?? e;
69
+ }
70
+ 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) {
72
77
  var _a;
73
- return (_a = e.nativeEvent) != null ? _a : e;
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);
74
87
  }
75
88
  var rtlKeyMap = {
76
89
  ArrowLeft: "ArrowRight",
@@ -88,72 +101,73 @@ var sameKeyMap = {
88
101
  Right: "ArrowRight"
89
102
  };
90
103
  function getEventKey(event, options = {}) {
91
- var _a;
92
104
  const { dir = "ltr", orientation = "horizontal" } = options;
93
105
  let { key } = event;
94
- key = (_a = sameKeyMap[key]) != null ? _a : key;
106
+ key = sameKeyMap[key] ?? key;
95
107
  const isRtl = dir === "rtl" && orientation === "horizontal";
96
108
  if (isRtl && key in rtlKeyMap) {
97
109
  key = rtlKeyMap[key];
98
110
  }
99
111
  return key;
100
112
  }
101
- function queryAll(root, selector) {
102
- var _a;
103
- return Array.from((_a = root == null ? void 0 : root.querySelectorAll(selector)) != null ? _a : []);
113
+ function raf(fn) {
114
+ const id = globalThis.requestAnimationFrame(fn);
115
+ return function cleanup() {
116
+ globalThis.cancelAnimationFrame(id);
117
+ };
104
118
  }
105
-
106
- // ../../types/dist/index.mjs
107
- function createNormalizer(fn) {
108
- return new Proxy({}, {
109
- get() {
110
- return fn;
111
- }
112
- });
119
+ function queryAll(root, selector) {
120
+ return Array.from((root == null ? void 0 : root.querySelectorAll(selector)) ?? []);
113
121
  }
114
- var normalizeProp = createNormalizer((v) => v);
122
+ var visuallyHiddenStyle = {
123
+ border: "0",
124
+ clip: "rect(0 0 0 0)",
125
+ height: "1px",
126
+ margin: "-1px",
127
+ overflow: "hidden",
128
+ padding: "0",
129
+ position: "absolute",
130
+ width: "1px",
131
+ whiteSpace: "nowrap",
132
+ wordWrap: "normal"
133
+ };
115
134
 
116
135
  // ../../utilities/core/dist/index.mjs
117
- var fromLength = (length) => Array.from(Array(length).keys());
118
- var isModifiedEvent = (v) => v.ctrlKey || v.altKey || v.metaKey;
119
136
  function invariant(...a) {
120
137
  const m = a.length === 1 ? a[0] : a[1];
121
138
  const c = a.length === 2 ? a[0] : true;
122
- if (c && void 0 !== "production") {
139
+ if (c && process.env.NODE_ENV !== "production") {
123
140
  throw new Error(m);
124
141
  }
125
142
  }
126
143
 
127
144
  // src/pin-input.dom.ts
128
- var dom = {
129
- getDoc: (ctx) => {
130
- var _a;
131
- return (_a = ctx.doc) != null ? _a : document;
132
- },
133
- getRootNode: (ctx) => {
145
+ var dom = defineDomHelpers({
146
+ getRootId: (ctx) => {
134
147
  var _a;
135
- return (_a = ctx.rootNode) != null ? _a : dom.getDoc(ctx);
148
+ return ((_a = ctx.ids) == null ? void 0 : _a.root) ?? `pin-input:${ctx.id}`;
136
149
  },
137
- getRootId: (ctx) => {
150
+ getInputId: (ctx, id) => {
138
151
  var _a, _b;
139
- return (_b = (_a = ctx.ids) == null ? void 0 : _a.root) != null ? _b : `pin-input:${ctx.uid}`;
152
+ return ((_b = (_a = ctx.ids) == null ? void 0 : _a.input) == null ? void 0 : _b.call(_a, id)) ?? `pin-input:${ctx.id}:${id}`;
140
153
  },
141
- getInputId: (ctx, id) => {
142
- var _a, _b, _c;
143
- return (_c = (_b = (_a = ctx.ids) == null ? void 0 : _a.input) == null ? void 0 : _b.call(_a, id)) != null ? _c : `pin-input:${ctx.uid}:${id}`;
154
+ getHiddenInputId: (ctx) => {
155
+ var _a;
156
+ return ((_a = ctx.ids) == null ? void 0 : _a.hiddenInput) ?? `pin-input:${ctx.id}:hidden`;
144
157
  },
145
- getRootEl: (ctx) => dom.getRootNode(ctx).getElementById(dom.getRootId(ctx)),
158
+ getRootEl: (ctx) => dom.getById(ctx, dom.getRootId(ctx)),
146
159
  getElements: (ctx) => {
147
160
  const ownerId = CSS.escape(dom.getRootId(ctx));
148
161
  const selector = `input[data-ownedby=${ownerId}]`;
149
162
  return queryAll(dom.getRootEl(ctx), selector);
150
163
  },
151
164
  getFocusedEl: (ctx) => dom.getElements(ctx)[ctx.focusedIndex],
152
- getFirstInputEl: (ctx) => dom.getElements(ctx)[0]
153
- };
165
+ getFirstInputEl: (ctx) => dom.getElements(ctx)[0],
166
+ getHiddenInputEl: (ctx) => dom.getById(ctx, dom.getHiddenInputId(ctx))
167
+ });
154
168
 
155
169
  // src/pin-input.connect.ts
156
- function connect(state, send, normalize = normalizeProp) {
170
+ function connect(state, send, normalize) {
157
171
  const isValueComplete = state.context.isValueComplete;
158
172
  const isInvalid = state.context.invalid;
159
173
  const focusedIndex = state.context.focusedIndex;
@@ -186,6 +200,16 @@ function connect(state, send, normalize = normalizeProp) {
186
200
  "data-disabled": dataAttr(state.context.disabled),
187
201
  "data-complete": dataAttr(isValueComplete)
188
202
  }),
203
+ hiddenInputProps: normalize.input({
204
+ "aria-hidden": true,
205
+ type: "text",
206
+ tabIndex: -1,
207
+ id: dom.getHiddenInputId(state.context),
208
+ name: state.context.name,
209
+ style: visuallyHiddenStyle,
210
+ maxLength: state.context.valueLength,
211
+ defaultValue: state.context.valueAsString
212
+ }),
189
213
  getInputProps({ index }) {
190
214
  const inputType = state.context.type === "numeric" ? "tel" : "text";
191
215
  return normalize.input({
@@ -234,6 +258,9 @@ function connect(state, send, normalize = normalizeProp) {
234
258
  },
235
259
  ArrowRight() {
236
260
  send("ARROW_RIGHT");
261
+ },
262
+ Enter() {
263
+ send("ENTER");
237
264
  }
238
265
  };
239
266
  const key = getEventKey(event, { dir: state.context.dir });
@@ -259,22 +286,22 @@ function connect(state, send, normalize = normalizeProp) {
259
286
  // src/pin-input.machine.ts
260
287
  var import_core = require("@zag-js/core");
261
288
  var { and, not } = import_core.guards;
262
- function machine(ctx = {}) {
289
+ function machine(ctx) {
263
290
  return (0, import_core.createMachine)({
264
291
  id: "pin-input",
265
292
  initial: "unknown",
266
- context: __spreadProps(__spreadValues({
267
- uid: "pin-input",
293
+ context: {
268
294
  value: [],
269
295
  focusedIndex: -1,
270
296
  placeholder: "\u25CB",
271
297
  otp: false,
272
- type: "numeric"
273
- }, ctx), {
274
- messages: __spreadValues({
275
- inputLabel: (index, length) => `pin code ${index + 1} of ${length}`
276
- }, ctx.messages)
277
- }),
298
+ type: "numeric",
299
+ ...ctx,
300
+ messages: {
301
+ inputLabel: (index, length) => `pin code ${index + 1} of ${length}`,
302
+ ...ctx.messages
303
+ }
304
+ },
278
305
  computed: {
279
306
  valueLength: (ctx2) => ctx2.value.length,
280
307
  filledValueLength: (ctx2) => ctx2.value.filter((v) => (v == null ? void 0 : v.trim()) !== "").length,
@@ -284,7 +311,7 @@ function machine(ctx = {}) {
284
311
  watch: {
285
312
  focusedIndex: "focusInput",
286
313
  value: "invokeOnChange",
287
- isValueComplete: ["invokeComplete", "blurFocusedInputIfNeeded"]
314
+ isValueComplete: ["invokeOnComplete", "blurFocusedInputIfNeeded"]
288
315
  },
289
316
  on: {
290
317
  SET_VALUE: [
@@ -311,11 +338,11 @@ function machine(ctx = {}) {
311
338
  {
312
339
  guard: "autoFocus",
313
340
  target: "focused",
314
- actions: ["setupDocument", "setupValue", "setFocusIndexToFirst"]
341
+ actions: ["setupValue", "setFocusIndexToFirst"]
315
342
  },
316
343
  {
317
344
  target: "idle",
318
- actions: ["setupDocument", "setupValue"]
345
+ actions: "setupValue"
319
346
  }
320
347
  ]
321
348
  }
@@ -367,6 +394,10 @@ function machine(ctx = {}) {
367
394
  actions: ["setPrevFocusedIndex", "clearFocusedValue"]
368
395
  }
369
396
  ],
397
+ ENTER: {
398
+ guard: "isValueComplete",
399
+ actions: "requestFormSubmit"
400
+ },
370
401
  KEY_DOWN: {
371
402
  guard: not("isValidValue"),
372
403
  actions: ["preventDefault", "invokeOnInvalid"]
@@ -380,7 +411,12 @@ function machine(ctx = {}) {
380
411
  isValueEmpty: (_ctx, evt) => evt.value === "",
381
412
  hasValue: (ctx2) => ctx2.value[ctx2.focusedIndex] !== "",
382
413
  isValueComplete: (ctx2) => ctx2.isValueComplete,
383
- isValidValue: (ctx2, evt) => isValidType(evt.value, ctx2.type),
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
+ },
384
420
  isFinalValue: (ctx2) => {
385
421
  return ctx2.filledValueLength + 1 === ctx2.valueLength && ctx2.value.findIndex((v) => v.trim() === "") === ctx2.focusedIndex;
386
422
  },
@@ -389,19 +425,10 @@ function machine(ctx = {}) {
389
425
  isDisabled: (ctx2) => !!ctx2.disabled
390
426
  },
391
427
  actions: {
392
- setupDocument: (ctx2, evt) => {
393
- if (evt.doc)
394
- ctx2.doc = (0, import_core.ref)(evt.doc);
395
- if (evt.root)
396
- ctx2.rootNode = (0, import_core.ref)(evt.root);
397
- ctx2.uid = evt.id;
398
- },
399
428
  setupValue: (ctx2) => {
400
- nextTick(() => {
401
- const inputs = dom.getElements(ctx2);
402
- const empty = fromLength(inputs.length).map(() => "");
403
- ctx2.value = Object.assign(empty, ctx2.value);
404
- });
429
+ const inputs = dom.getElements(ctx2);
430
+ const empty = Array.from({ length: inputs.length }).map(() => "");
431
+ ctx2.value = Object.assign(empty, ctx2.value);
405
432
  },
406
433
  focusInput: (ctx2) => {
407
434
  raf(() => {
@@ -411,17 +438,18 @@ function machine(ctx = {}) {
411
438
  (_a = dom.getFocusedEl(ctx2)) == null ? void 0 : _a.focus();
412
439
  });
413
440
  },
414
- invokeComplete: (ctx2) => {
441
+ invokeOnComplete: (ctx2) => {
415
442
  var _a;
416
- if (ctx2.isValueComplete) {
417
- (_a = ctx2.onComplete) == null ? void 0 : _a.call(ctx2, { value: Array.from(ctx2.value), valueAsString: ctx2.valueAsString });
418
- }
443
+ if (!ctx2.isValueComplete)
444
+ return;
445
+ (_a = ctx2.onComplete) == null ? void 0 : _a.call(ctx2, { value: Array.from(ctx2.value), valueAsString: ctx2.valueAsString });
419
446
  },
420
447
  invokeOnChange: (ctx2, evt) => {
421
448
  var _a;
422
- if (evt.type !== "SETUP") {
423
- (_a = ctx2.onChange) == null ? void 0 : _a.call(ctx2, { value: Array.from(ctx2.value) });
424
- }
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);
425
453
  },
426
454
  invokeOnInvalid: (ctx2, evt) => {
427
455
  var _a;
@@ -478,6 +506,13 @@ function machine(ctx = {}) {
478
506
  var _a;
479
507
  (_a = dom.getFocusedEl(ctx2)) == null ? void 0 : _a.blur();
480
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();
481
516
  }
482
517
  }
483
518
  });
@@ -508,3 +543,8 @@ function assign(ctx, value) {
508
543
  function lastChar(value) {
509
544
  return value.charAt(value.length - 1);
510
545
  }
546
+ // Annotate the CommonJS export names for ESM import in node:
547
+ 0 && (module.exports = {
548
+ connect,
549
+ machine
550
+ });
package/dist/index.mjs CHANGED
@@ -1,24 +1,3 @@
1
- "use strict";
2
- var __defProp = Object.defineProperty;
3
- var __defProps = Object.defineProperties;
4
- var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
5
- var __getOwnPropSymbols = Object.getOwnPropertySymbols;
6
- var __hasOwnProp = Object.prototype.hasOwnProperty;
7
- var __propIsEnum = Object.prototype.propertyIsEnumerable;
8
- var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
9
- var __spreadValues = (a, b) => {
10
- for (var prop in b || (b = {}))
11
- if (__hasOwnProp.call(b, prop))
12
- __defNormalProp(a, prop, b[prop]);
13
- if (__getOwnPropSymbols)
14
- for (var prop of __getOwnPropSymbols(b)) {
15
- if (__propIsEnum.call(b, prop))
16
- __defNormalProp(a, prop, b[prop]);
17
- }
18
- return a;
19
- };
20
- var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
21
-
22
1
  // ../../utilities/dom/dist/index.mjs
23
2
  var dataAttr = (guard) => {
24
3
  return guard ? "" : void 0;
@@ -26,28 +5,58 @@ var dataAttr = (guard) => {
26
5
  var ariaAttr = (guard) => {
27
6
  return guard ? "true" : void 0;
28
7
  };
29
- function nextTick(fn) {
30
- const set = /* @__PURE__ */ new Set();
31
- function raf2(fn2) {
32
- const id = globalThis.requestAnimationFrame(fn2);
33
- set.add(() => globalThis.cancelAnimationFrame(id));
34
- }
35
- raf2(() => raf2(fn));
36
- return function cleanup() {
37
- set.forEach(function(fn2) {
38
- fn2();
39
- });
40
- };
8
+ function isDocument(el) {
9
+ return el.nodeType === Node.DOCUMENT_NODE;
41
10
  }
42
- function raf(fn) {
43
- const id = globalThis.requestAnimationFrame(fn);
44
- return function cleanup() {
45
- globalThis.cancelAnimationFrame(id);
11
+ function isWindow(value) {
12
+ return (value == null ? void 0 : value.toString()) === "[object Window]";
13
+ }
14
+ function getDocument(el) {
15
+ if (isWindow(el))
16
+ return el.document;
17
+ if (isDocument(el))
18
+ return el;
19
+ return (el == null ? void 0 : el.ownerDocument) ?? document;
20
+ }
21
+ function getWindow(el) {
22
+ return (el == null ? void 0 : el.ownerDocument.defaultView) ?? window;
23
+ }
24
+ function defineDomHelpers(helpers) {
25
+ const dom2 = {
26
+ getRootNode: (ctx) => {
27
+ var _a;
28
+ return ((_a = ctx.getRootNode) == null ? void 0 : _a.call(ctx)) ?? document;
29
+ },
30
+ getDoc: (ctx) => getDocument(dom2.getRootNode(ctx)),
31
+ getWin: (ctx) => dom2.getDoc(ctx).defaultView ?? window,
32
+ getActiveElement: (ctx) => dom2.getDoc(ctx).activeElement,
33
+ getById: (ctx, id) => dom2.getRootNode(ctx).getElementById(id)
34
+ };
35
+ return {
36
+ ...dom2,
37
+ ...helpers
46
38
  };
47
39
  }
48
40
  function getNativeEvent(e) {
41
+ return e.nativeEvent ?? e;
42
+ }
43
+ 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) {
49
50
  var _a;
50
- return (_a = e.nativeEvent) != null ? _a : e;
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);
51
60
  }
52
61
  var rtlKeyMap = {
53
62
  ArrowLeft: "ArrowRight",
@@ -65,72 +74,73 @@ var sameKeyMap = {
65
74
  Right: "ArrowRight"
66
75
  };
67
76
  function getEventKey(event, options = {}) {
68
- var _a;
69
77
  const { dir = "ltr", orientation = "horizontal" } = options;
70
78
  let { key } = event;
71
- key = (_a = sameKeyMap[key]) != null ? _a : key;
79
+ key = sameKeyMap[key] ?? key;
72
80
  const isRtl = dir === "rtl" && orientation === "horizontal";
73
81
  if (isRtl && key in rtlKeyMap) {
74
82
  key = rtlKeyMap[key];
75
83
  }
76
84
  return key;
77
85
  }
78
- function queryAll(root, selector) {
79
- var _a;
80
- return Array.from((_a = root == null ? void 0 : root.querySelectorAll(selector)) != null ? _a : []);
86
+ function raf(fn) {
87
+ const id = globalThis.requestAnimationFrame(fn);
88
+ return function cleanup() {
89
+ globalThis.cancelAnimationFrame(id);
90
+ };
81
91
  }
82
-
83
- // ../../types/dist/index.mjs
84
- function createNormalizer(fn) {
85
- return new Proxy({}, {
86
- get() {
87
- return fn;
88
- }
89
- });
92
+ function queryAll(root, selector) {
93
+ return Array.from((root == null ? void 0 : root.querySelectorAll(selector)) ?? []);
90
94
  }
91
- var normalizeProp = createNormalizer((v) => v);
95
+ var visuallyHiddenStyle = {
96
+ border: "0",
97
+ clip: "rect(0 0 0 0)",
98
+ height: "1px",
99
+ margin: "-1px",
100
+ overflow: "hidden",
101
+ padding: "0",
102
+ position: "absolute",
103
+ width: "1px",
104
+ whiteSpace: "nowrap",
105
+ wordWrap: "normal"
106
+ };
92
107
 
93
108
  // ../../utilities/core/dist/index.mjs
94
- var fromLength = (length) => Array.from(Array(length).keys());
95
- var isModifiedEvent = (v) => v.ctrlKey || v.altKey || v.metaKey;
96
109
  function invariant(...a) {
97
110
  const m = a.length === 1 ? a[0] : a[1];
98
111
  const c = a.length === 2 ? a[0] : true;
99
- if (c && void 0 !== "production") {
112
+ if (c && process.env.NODE_ENV !== "production") {
100
113
  throw new Error(m);
101
114
  }
102
115
  }
103
116
 
104
117
  // src/pin-input.dom.ts
105
- var dom = {
106
- getDoc: (ctx) => {
107
- var _a;
108
- return (_a = ctx.doc) != null ? _a : document;
109
- },
110
- getRootNode: (ctx) => {
118
+ var dom = defineDomHelpers({
119
+ getRootId: (ctx) => {
111
120
  var _a;
112
- return (_a = ctx.rootNode) != null ? _a : dom.getDoc(ctx);
121
+ return ((_a = ctx.ids) == null ? void 0 : _a.root) ?? `pin-input:${ctx.id}`;
113
122
  },
114
- getRootId: (ctx) => {
123
+ getInputId: (ctx, id) => {
115
124
  var _a, _b;
116
- return (_b = (_a = ctx.ids) == null ? void 0 : _a.root) != null ? _b : `pin-input:${ctx.uid}`;
125
+ return ((_b = (_a = ctx.ids) == null ? void 0 : _a.input) == null ? void 0 : _b.call(_a, id)) ?? `pin-input:${ctx.id}:${id}`;
117
126
  },
118
- getInputId: (ctx, id) => {
119
- var _a, _b, _c;
120
- return (_c = (_b = (_a = ctx.ids) == null ? void 0 : _a.input) == null ? void 0 : _b.call(_a, id)) != null ? _c : `pin-input:${ctx.uid}:${id}`;
127
+ getHiddenInputId: (ctx) => {
128
+ var _a;
129
+ return ((_a = ctx.ids) == null ? void 0 : _a.hiddenInput) ?? `pin-input:${ctx.id}:hidden`;
121
130
  },
122
- getRootEl: (ctx) => dom.getRootNode(ctx).getElementById(dom.getRootId(ctx)),
131
+ getRootEl: (ctx) => dom.getById(ctx, dom.getRootId(ctx)),
123
132
  getElements: (ctx) => {
124
133
  const ownerId = CSS.escape(dom.getRootId(ctx));
125
134
  const selector = `input[data-ownedby=${ownerId}]`;
126
135
  return queryAll(dom.getRootEl(ctx), selector);
127
136
  },
128
137
  getFocusedEl: (ctx) => dom.getElements(ctx)[ctx.focusedIndex],
129
- getFirstInputEl: (ctx) => dom.getElements(ctx)[0]
130
- };
138
+ getFirstInputEl: (ctx) => dom.getElements(ctx)[0],
139
+ getHiddenInputEl: (ctx) => dom.getById(ctx, dom.getHiddenInputId(ctx))
140
+ });
131
141
 
132
142
  // src/pin-input.connect.ts
133
- function connect(state, send, normalize = normalizeProp) {
143
+ function connect(state, send, normalize) {
134
144
  const isValueComplete = state.context.isValueComplete;
135
145
  const isInvalid = state.context.invalid;
136
146
  const focusedIndex = state.context.focusedIndex;
@@ -163,6 +173,16 @@ function connect(state, send, normalize = normalizeProp) {
163
173
  "data-disabled": dataAttr(state.context.disabled),
164
174
  "data-complete": dataAttr(isValueComplete)
165
175
  }),
176
+ hiddenInputProps: normalize.input({
177
+ "aria-hidden": true,
178
+ type: "text",
179
+ tabIndex: -1,
180
+ id: dom.getHiddenInputId(state.context),
181
+ name: state.context.name,
182
+ style: visuallyHiddenStyle,
183
+ maxLength: state.context.valueLength,
184
+ defaultValue: state.context.valueAsString
185
+ }),
166
186
  getInputProps({ index }) {
167
187
  const inputType = state.context.type === "numeric" ? "tel" : "text";
168
188
  return normalize.input({
@@ -211,6 +231,9 @@ function connect(state, send, normalize = normalizeProp) {
211
231
  },
212
232
  ArrowRight() {
213
233
  send("ARROW_RIGHT");
234
+ },
235
+ Enter() {
236
+ send("ENTER");
214
237
  }
215
238
  };
216
239
  const key = getEventKey(event, { dir: state.context.dir });
@@ -234,24 +257,24 @@ function connect(state, send, normalize = normalizeProp) {
234
257
  }
235
258
 
236
259
  // src/pin-input.machine.ts
237
- import { createMachine, guards, ref } from "@zag-js/core";
260
+ import { createMachine, guards } from "@zag-js/core";
238
261
  var { and, not } = guards;
239
- function machine(ctx = {}) {
262
+ function machine(ctx) {
240
263
  return createMachine({
241
264
  id: "pin-input",
242
265
  initial: "unknown",
243
- context: __spreadProps(__spreadValues({
244
- uid: "pin-input",
266
+ context: {
245
267
  value: [],
246
268
  focusedIndex: -1,
247
269
  placeholder: "\u25CB",
248
270
  otp: false,
249
- type: "numeric"
250
- }, ctx), {
251
- messages: __spreadValues({
252
- inputLabel: (index, length) => `pin code ${index + 1} of ${length}`
253
- }, ctx.messages)
254
- }),
271
+ type: "numeric",
272
+ ...ctx,
273
+ messages: {
274
+ inputLabel: (index, length) => `pin code ${index + 1} of ${length}`,
275
+ ...ctx.messages
276
+ }
277
+ },
255
278
  computed: {
256
279
  valueLength: (ctx2) => ctx2.value.length,
257
280
  filledValueLength: (ctx2) => ctx2.value.filter((v) => (v == null ? void 0 : v.trim()) !== "").length,
@@ -261,7 +284,7 @@ function machine(ctx = {}) {
261
284
  watch: {
262
285
  focusedIndex: "focusInput",
263
286
  value: "invokeOnChange",
264
- isValueComplete: ["invokeComplete", "blurFocusedInputIfNeeded"]
287
+ isValueComplete: ["invokeOnComplete", "blurFocusedInputIfNeeded"]
265
288
  },
266
289
  on: {
267
290
  SET_VALUE: [
@@ -288,11 +311,11 @@ function machine(ctx = {}) {
288
311
  {
289
312
  guard: "autoFocus",
290
313
  target: "focused",
291
- actions: ["setupDocument", "setupValue", "setFocusIndexToFirst"]
314
+ actions: ["setupValue", "setFocusIndexToFirst"]
292
315
  },
293
316
  {
294
317
  target: "idle",
295
- actions: ["setupDocument", "setupValue"]
318
+ actions: "setupValue"
296
319
  }
297
320
  ]
298
321
  }
@@ -344,6 +367,10 @@ function machine(ctx = {}) {
344
367
  actions: ["setPrevFocusedIndex", "clearFocusedValue"]
345
368
  }
346
369
  ],
370
+ ENTER: {
371
+ guard: "isValueComplete",
372
+ actions: "requestFormSubmit"
373
+ },
347
374
  KEY_DOWN: {
348
375
  guard: not("isValidValue"),
349
376
  actions: ["preventDefault", "invokeOnInvalid"]
@@ -357,7 +384,12 @@ function machine(ctx = {}) {
357
384
  isValueEmpty: (_ctx, evt) => evt.value === "",
358
385
  hasValue: (ctx2) => ctx2.value[ctx2.focusedIndex] !== "",
359
386
  isValueComplete: (ctx2) => ctx2.isValueComplete,
360
- isValidValue: (ctx2, evt) => isValidType(evt.value, ctx2.type),
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
+ },
361
393
  isFinalValue: (ctx2) => {
362
394
  return ctx2.filledValueLength + 1 === ctx2.valueLength && ctx2.value.findIndex((v) => v.trim() === "") === ctx2.focusedIndex;
363
395
  },
@@ -366,19 +398,10 @@ function machine(ctx = {}) {
366
398
  isDisabled: (ctx2) => !!ctx2.disabled
367
399
  },
368
400
  actions: {
369
- setupDocument: (ctx2, evt) => {
370
- if (evt.doc)
371
- ctx2.doc = ref(evt.doc);
372
- if (evt.root)
373
- ctx2.rootNode = ref(evt.root);
374
- ctx2.uid = evt.id;
375
- },
376
401
  setupValue: (ctx2) => {
377
- nextTick(() => {
378
- const inputs = dom.getElements(ctx2);
379
- const empty = fromLength(inputs.length).map(() => "");
380
- ctx2.value = Object.assign(empty, ctx2.value);
381
- });
402
+ const inputs = dom.getElements(ctx2);
403
+ const empty = Array.from({ length: inputs.length }).map(() => "");
404
+ ctx2.value = Object.assign(empty, ctx2.value);
382
405
  },
383
406
  focusInput: (ctx2) => {
384
407
  raf(() => {
@@ -388,17 +411,18 @@ function machine(ctx = {}) {
388
411
  (_a = dom.getFocusedEl(ctx2)) == null ? void 0 : _a.focus();
389
412
  });
390
413
  },
391
- invokeComplete: (ctx2) => {
414
+ invokeOnComplete: (ctx2) => {
392
415
  var _a;
393
- if (ctx2.isValueComplete) {
394
- (_a = ctx2.onComplete) == null ? void 0 : _a.call(ctx2, { value: Array.from(ctx2.value), valueAsString: ctx2.valueAsString });
395
- }
416
+ if (!ctx2.isValueComplete)
417
+ return;
418
+ (_a = ctx2.onComplete) == null ? void 0 : _a.call(ctx2, { value: Array.from(ctx2.value), valueAsString: ctx2.valueAsString });
396
419
  },
397
420
  invokeOnChange: (ctx2, evt) => {
398
421
  var _a;
399
- if (evt.type !== "SETUP") {
400
- (_a = ctx2.onChange) == null ? void 0 : _a.call(ctx2, { value: Array.from(ctx2.value) });
401
- }
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);
402
426
  },
403
427
  invokeOnInvalid: (ctx2, evt) => {
404
428
  var _a;
@@ -455,6 +479,13 @@ function machine(ctx = {}) {
455
479
  var _a;
456
480
  (_a = dom.getFocusedEl(ctx2)) == null ? void 0 : _a.blur();
457
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();
458
489
  }
459
490
  }
460
491
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zag-js/pin-input",
3
- "version": "0.1.8",
3
+ "version": "0.1.11",
4
4
  "description": "Core logic for the pin-input widget implemented as a state machine",
5
5
  "keywords": [
6
6
  "js",
@@ -29,18 +29,21 @@
29
29
  "url": "https://github.com/chakra-ui/zag/issues"
30
30
  },
31
31
  "dependencies": {
32
- "@zag-js/core": "0.1.6",
33
- "@zag-js/dom-utils": "0.1.5",
34
- "@zag-js/types": "0.2.0",
35
- "@zag-js/utils": "0.1.2"
32
+ "@zag-js/core": "0.1.9",
33
+ "@zag-js/types": "0.2.3"
34
+ },
35
+ "devDependencies": {
36
+ "@zag-js/dom-utils": "0.1.8",
37
+ "@zag-js/utils": "0.1.3"
36
38
  },
37
39
  "scripts": {
38
- "build:fast": "zag build",
39
- "start": "zag build --watch",
40
- "build": "zag build --prod",
40
+ "build-fast": "tsup src/index.ts --format=esm,cjs",
41
+ "start": "pnpm build --watch",
42
+ "build": "tsup src/index.ts --format=esm,cjs --dts",
41
43
  "test": "jest --config ../../../jest.config.js --rootDir . --passWithNoTests",
42
44
  "lint": "eslint src --ext .ts,.tsx",
43
- "test:ci": "yarn test --ci --runInBand",
44
- "test:watch": "yarn test --watch --updateSnapshot"
45
+ "test-ci": "pnpm test --ci --runInBand",
46
+ "test-watch": "pnpm test --watch -u",
47
+ "typecheck": "tsc --noEmit"
45
48
  }
46
- }
49
+ }
@@ -1,15 +0,0 @@
1
- import { PropTypes, ReactPropTypes } from "@zag-js/types";
2
- import { Send, State } from "./pin-input.types";
3
- export declare function connect<T extends PropTypes = ReactPropTypes>(state: State, send: Send, normalize?: import("@zag-js/types").NormalizeProps): {
4
- value: string[];
5
- valueAsString: string;
6
- isValueComplete: boolean;
7
- setValue(value: string[]): void;
8
- clearValue(): void;
9
- setValueAtIndex(index: number, value: string): void;
10
- focus(): void;
11
- rootProps: T["element"];
12
- getInputProps({ index }: {
13
- index: number;
14
- }): T["input"];
15
- };
@@ -1,11 +0,0 @@
1
- import { MachineContext as Ctx } from "./pin-input.types";
2
- export declare const dom: {
3
- getDoc: (ctx: Ctx) => Document;
4
- getRootNode: (ctx: Ctx) => Document | ShadowRoot;
5
- getRootId: (ctx: Ctx) => string;
6
- getInputId: (ctx: Ctx, id: string) => string;
7
- getRootEl: (ctx: Ctx) => HTMLElement;
8
- getElements: (ctx: Ctx) => HTMLInputElement[];
9
- getFocusedEl: (ctx: Ctx) => HTMLInputElement;
10
- getFirstInputEl: (ctx: Ctx) => HTMLInputElement;
11
- };
@@ -1,2 +0,0 @@
1
- import { MachineContext, MachineState, UserDefinedContext } from "./pin-input.types";
2
- export declare function machine(ctx?: UserDefinedContext): import("@zag-js/core").Machine<MachineContext, MachineState, import("@zag-js/core").StateMachine.AnyEventObject>;
@@ -1,113 +0,0 @@
1
- import type { StateMachine as S } from "@zag-js/core";
2
- import type { Context, DirectionProperty } from "@zag-js/types";
3
- declare type IntlMessages = {
4
- inputLabel: (index: number, length: number) => string;
5
- };
6
- declare type ElementIds = Partial<{
7
- root: string;
8
- input(id: string): string;
9
- }>;
10
- declare type PublicContext = DirectionProperty & {
11
- /**
12
- * The ids of the elements in the pin input. Useful for composition.
13
- */
14
- ids?: ElementIds;
15
- /**
16
- * Whether the inputs are disabled
17
- */
18
- disabled?: boolean;
19
- /**
20
- * The placeholder text for the input
21
- */
22
- placeholder?: string;
23
- /**
24
- * Whether to auto-focus the first input.
25
- */
26
- autoFocus?: boolean;
27
- /**
28
- * Whether the pin input is in the invalid state
29
- */
30
- invalid?: boolean;
31
- /**
32
- * If `true`, the pin input component signals to its fields that they should
33
- * use `autocomplete="one-time-code"`.
34
- */
35
- otp?: boolean;
36
- /**
37
- * The value of the the pin input.
38
- */
39
- value: string[];
40
- /**
41
- * The type of value the pin-input should allow
42
- */
43
- type?: "alphanumeric" | "numeric" | "alphabetic";
44
- /**
45
- * Function called when all inputs have valid values
46
- */
47
- onComplete?: (details: {
48
- value: string[];
49
- valueAsString: string;
50
- }) => void;
51
- /**
52
- * Function called on input change
53
- */
54
- onChange?: (details: {
55
- value: string[];
56
- }) => void;
57
- /**
58
- * Function called when an invalid value is entered
59
- */
60
- onInvalid?: (details: {
61
- value: string;
62
- index: number;
63
- }) => void;
64
- /**
65
- * If `true`, the input's value will be masked just like `type=password`
66
- */
67
- mask?: boolean;
68
- /**
69
- * Whether to blur the input when the value is complete
70
- */
71
- blurOnComplete?: boolean;
72
- /**
73
- * Specifies the localized strings that identifies the accessibility elements and their states
74
- */
75
- messages: IntlMessages;
76
- };
77
- export declare type UserDefinedContext = Partial<PublicContext>;
78
- declare type ComputedContext = Readonly<{
79
- /**
80
- * @computed
81
- * The number of inputs
82
- */
83
- valueLength: number;
84
- /**
85
- * @computed
86
- * The number of inputs that are not empty
87
- */
88
- filledValueLength: number;
89
- /**
90
- * @computed
91
- * Whether all input values are valid
92
- */
93
- isValueComplete: boolean;
94
- /**
95
- * @computed
96
- * The string representation of the input values
97
- */
98
- valueAsString: string;
99
- }>;
100
- declare type PrivateContext = Context<{
101
- /**
102
- * @internal
103
- * The index of the input field that has focus
104
- */
105
- focusedIndex: number;
106
- }>;
107
- export declare type MachineContext = PublicContext & PrivateContext & ComputedContext;
108
- export declare type MachineState = {
109
- value: "unknown" | "idle" | "focused";
110
- };
111
- export declare type State = S.State<MachineContext, MachineState>;
112
- export declare type Send = S.Send<S.AnyEventObject>;
113
- export {};