@zag-js/pin-input 1.1.0 → 1.2.1

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.mts CHANGED
@@ -112,6 +112,11 @@ interface PinInputProps extends DirectionProperty, CommonProperties {
112
112
  * Specifies the localized strings that identifies the accessibility elements and their states
113
113
  */
114
114
  translations?: IntlTranslations | undefined;
115
+ /**
116
+ * The number of inputs to render to improve SSR aria attributes.
117
+ * This will be required in next major version.
118
+ */
119
+ count?: number | undefined;
115
120
  }
116
121
  type PropsWithDefault = "placeholder" | "otp" | "type" | "defaultValue";
117
122
  interface PinInputSchema {
@@ -120,6 +125,7 @@ interface PinInputSchema {
120
125
  context: {
121
126
  value: string[];
122
127
  focusedIndex: number;
128
+ count: number;
123
129
  };
124
130
  computed: {
125
131
  _value: string[];
@@ -129,9 +135,6 @@ interface PinInputSchema {
129
135
  valueAsString: string;
130
136
  focusedValue: string;
131
137
  };
132
- refs: {
133
- count: number;
134
- };
135
138
  event: EventObject;
136
139
  action: string;
137
140
  effect: string;
@@ -155,6 +158,14 @@ interface PinInputApi<T extends PropTypes = PropTypes> {
155
158
  * Whether all inputs are filled.
156
159
  */
157
160
  complete: boolean;
161
+ /**
162
+ * The number of inputs to render
163
+ */
164
+ count: number;
165
+ /**
166
+ * The array of input values.
167
+ */
168
+ items: number[];
158
169
  /**
159
170
  * Function to set the value of the inputs.
160
171
  */
package/dist/index.d.ts CHANGED
@@ -112,6 +112,11 @@ interface PinInputProps extends DirectionProperty, CommonProperties {
112
112
  * Specifies the localized strings that identifies the accessibility elements and their states
113
113
  */
114
114
  translations?: IntlTranslations | undefined;
115
+ /**
116
+ * The number of inputs to render to improve SSR aria attributes.
117
+ * This will be required in next major version.
118
+ */
119
+ count?: number | undefined;
115
120
  }
116
121
  type PropsWithDefault = "placeholder" | "otp" | "type" | "defaultValue";
117
122
  interface PinInputSchema {
@@ -120,6 +125,7 @@ interface PinInputSchema {
120
125
  context: {
121
126
  value: string[];
122
127
  focusedIndex: number;
128
+ count: number;
123
129
  };
124
130
  computed: {
125
131
  _value: string[];
@@ -129,9 +135,6 @@ interface PinInputSchema {
129
135
  valueAsString: string;
130
136
  focusedValue: string;
131
137
  };
132
- refs: {
133
- count: number;
134
- };
135
138
  event: EventObject;
136
139
  action: string;
137
140
  effect: string;
@@ -155,6 +158,14 @@ interface PinInputApi<T extends PropTypes = PropTypes> {
155
158
  * Whether all inputs are filled.
156
159
  */
157
160
  complete: boolean;
161
+ /**
162
+ * The number of inputs to render
163
+ */
164
+ count: number;
165
+ /**
166
+ * The array of input values.
167
+ */
168
+ items: number[];
158
169
  /**
159
170
  * Function to set the value of the inputs.
160
171
  */
package/dist/index.js CHANGED
@@ -23,6 +23,10 @@ var getInputEls = (ctx) => {
23
23
  var getInputElAtIndex = (ctx, index) => getInputEls(ctx)[index];
24
24
  var getFirstInputEl = (ctx) => getInputEls(ctx)[0];
25
25
  var getHiddenInputEl = (ctx) => ctx.getById(getHiddenInputId(ctx));
26
+ var setInputValue = (inputEl, value) => {
27
+ inputEl.value = value;
28
+ inputEl.setAttribute("value", value);
29
+ };
26
30
 
27
31
  // src/pin-input.utils.ts
28
32
  var REGEX = {
@@ -52,6 +56,8 @@ function connect(service, normalize) {
52
56
  }
53
57
  return {
54
58
  focus,
59
+ count: context.get("count"),
60
+ items: Array.from({ length: context.get("count") }).map((_, i) => i),
55
61
  value: context.get("value"),
56
62
  valueAsString: computed("valueAsString"),
57
63
  complete,
@@ -198,11 +204,11 @@ function connect(service, normalize) {
198
204
  }
199
205
  },
200
206
  onFocus() {
201
- queueMicrotask(() => {
202
- send({ type: "INPUT.FOCUS", index });
203
- });
207
+ send({ type: "INPUT.FOCUS", index });
204
208
  },
205
- onBlur() {
209
+ onBlur(event) {
210
+ const target = event.relatedTarget;
211
+ if (domQuery.isHTMLElement(target) && target.dataset.ownedby === getRootId(scope)) return;
206
212
  send({ type: "INPUT.BLUR", index });
207
213
  }
208
214
  });
@@ -216,7 +222,7 @@ var machine = createMachine({
216
222
  placeholder: "\u25CB",
217
223
  otp: false,
218
224
  type: "numeric",
219
- defaultValue: [],
225
+ defaultValue: props2.count ? fill([], props2.count) : [],
220
226
  ...props2,
221
227
  translations: {
222
228
  inputLabel: (index, length) => `pin code ${index + 1} of ${length}`,
@@ -230,7 +236,6 @@ var machine = createMachine({
230
236
  context({ prop, bindable }) {
231
237
  return {
232
238
  value: bindable(() => ({
233
- sync: true,
234
239
  value: prop("value"),
235
240
  defaultValue: prop("defaultValue"),
236
241
  onChange(value) {
@@ -240,16 +245,15 @@ var machine = createMachine({
240
245
  focusedIndex: bindable(() => ({
241
246
  sync: true,
242
247
  defaultValue: -1
248
+ })),
249
+ // TODO: Move this to `props` in next major version
250
+ count: bindable(() => ({
251
+ defaultValue: prop("count")
243
252
  }))
244
253
  };
245
254
  },
246
- refs() {
247
- return {
248
- count: 0
249
- };
250
- },
251
255
  computed: {
252
- _value: ({ context, refs }) => fill(context.get("value"), refs.get("count")),
256
+ _value: ({ context }) => fill(context.get("value"), context.get("count")),
253
257
  valueLength: ({ computed }) => computed("_value").length,
254
258
  filledValueLength: ({ computed }) => computed("_value").filter((v) => v?.trim() !== "").length,
255
259
  isValueComplete: ({ computed }) => computed("valueLength") === computed("filledValueLength"),
@@ -351,9 +355,10 @@ var machine = createMachine({
351
355
  const inputEl = getHiddenInputEl(scope);
352
356
  domQuery.dispatchInputValueEvent(inputEl, { value: computed("valueAsString") });
353
357
  },
354
- setInputCount({ scope, refs }) {
358
+ setInputCount({ scope, context, prop }) {
359
+ if (prop("count")) return;
355
360
  const inputEls = getInputEls(scope);
356
- refs.set("count", inputEls.length);
361
+ context.set("count", inputEls.length);
357
362
  },
358
363
  focusInput({ context, scope }) {
359
364
  const focusedIndex = context.get("focusedIndex");
@@ -386,8 +391,8 @@ var machine = createMachine({
386
391
  setFocusedIndex({ context, event }) {
387
392
  context.set("focusedIndex", event.index);
388
393
  },
389
- setValue({ context, event, refs }) {
390
- const value = fill(event.value, refs.get("count"));
394
+ setValue({ context, event }) {
395
+ const value = fill(event.value, context.get("count"));
391
396
  context.set("value", value);
392
397
  },
393
398
  setFocusedValue({ context, event, computed }) {
@@ -398,38 +403,41 @@ var machine = createMachine({
398
403
  },
399
404
  revertInputValue({ context, computed, scope }) {
400
405
  const inputEl = getInputElAtIndex(scope, context.get("focusedIndex"));
401
- inputEl.value = computed("focusedValue");
406
+ setInputValue(inputEl, computed("focusedValue"));
402
407
  },
403
408
  syncInputValue({ context, event, scope }) {
404
409
  const value = context.get("value");
405
410
  const inputEl = getInputElAtIndex(scope, event.index);
406
- inputEl.value = value[event.index];
411
+ setInputValue(inputEl, value[event.index]);
407
412
  },
408
413
  syncInputElements({ context, scope }) {
409
414
  const inputEls = getInputEls(scope);
410
415
  const value = context.get("value");
411
416
  inputEls.forEach((inputEl, index) => {
412
- inputEl.value = value[index];
417
+ setInputValue(inputEl, value[index]);
413
418
  });
414
419
  },
415
- setPastedValue({ context, event, computed }) {
420
+ setPastedValue({ context, event, computed, flush }) {
416
421
  domQuery.raf(() => {
417
422
  const valueAsString = computed("valueAsString");
418
423
  const focusedIndex = context.get("focusedIndex");
424
+ const valueLength = computed("valueLength");
419
425
  const filledValueLength = computed("filledValueLength");
420
426
  const startIndex = Math.min(focusedIndex, filledValueLength);
421
427
  const left = startIndex > 0 ? valueAsString.substring(0, focusedIndex) : "";
422
- const right = event.value.substring(0, computed("valueLength") - startIndex);
423
- const value = fill(`${left}${right}`.split(""), computed("valueLength"));
424
- context.set("value", value);
428
+ const right = event.value.substring(0, valueLength - startIndex);
429
+ const value = fill(`${left}${right}`.split(""), valueLength);
430
+ flush(() => {
431
+ context.set("value", value);
432
+ });
425
433
  });
426
434
  },
427
435
  setValueAtIndex({ context, event, computed }) {
428
436
  const nextValue = getNextValue(computed("focusedValue"), event.value);
429
437
  context.set("value", utils.setValueAtIndex(computed("_value"), event.index, nextValue));
430
438
  },
431
- clearValue({ context, refs }) {
432
- const nextValue = Array.from({ length: refs.get("count") }).fill("");
439
+ clearValue({ context }) {
440
+ const nextValue = Array.from({ length: context.get("count") }).fill("");
433
441
  context.set("value", nextValue);
434
442
  },
435
443
  clearFocusedValue({ context, computed }) {
@@ -477,6 +485,8 @@ function fill(value, count) {
477
485
  var props = types.createProps()([
478
486
  "autoFocus",
479
487
  "blurOnComplete",
488
+ "count",
489
+ "defaultValue",
480
490
  "dir",
481
491
  "disabled",
482
492
  "form",
@@ -490,15 +500,14 @@ var props = types.createProps()([
490
500
  "onValueComplete",
491
501
  "onValueInvalid",
492
502
  "otp",
493
- "readOnly",
494
503
  "pattern",
495
504
  "placeholder",
505
+ "readOnly",
496
506
  "required",
497
507
  "selectOnFocus",
498
508
  "translations",
499
509
  "type",
500
- "value",
501
- "defaultValue"
510
+ "value"
502
511
  ]);
503
512
  var splitProps = utils.createSplitProps(props);
504
513
 
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import { createAnatomy } from '@zag-js/anatomy';
2
- import { dispatchInputValueEvent, raf, queryAll, dataAttr, visuallyHiddenStyle, ariaAttr, getBeforeInputValue, getNativeEvent, isComposingEvent, isModifierKey, getEventKey } from '@zag-js/dom-query';
2
+ import { dispatchInputValueEvent, raf, queryAll, dataAttr, visuallyHiddenStyle, ariaAttr, getBeforeInputValue, getNativeEvent, isComposingEvent, isModifierKey, getEventKey, isHTMLElement } from '@zag-js/dom-query';
3
3
  import { setValueAtIndex, createSplitProps, invariant } from '@zag-js/utils';
4
4
  import { setup } from '@zag-js/core';
5
5
  import { createProps } from '@zag-js/types';
@@ -21,6 +21,10 @@ var getInputEls = (ctx) => {
21
21
  var getInputElAtIndex = (ctx, index) => getInputEls(ctx)[index];
22
22
  var getFirstInputEl = (ctx) => getInputEls(ctx)[0];
23
23
  var getHiddenInputEl = (ctx) => ctx.getById(getHiddenInputId(ctx));
24
+ var setInputValue = (inputEl, value) => {
25
+ inputEl.value = value;
26
+ inputEl.setAttribute("value", value);
27
+ };
24
28
 
25
29
  // src/pin-input.utils.ts
26
30
  var REGEX = {
@@ -50,6 +54,8 @@ function connect(service, normalize) {
50
54
  }
51
55
  return {
52
56
  focus,
57
+ count: context.get("count"),
58
+ items: Array.from({ length: context.get("count") }).map((_, i) => i),
53
59
  value: context.get("value"),
54
60
  valueAsString: computed("valueAsString"),
55
61
  complete,
@@ -196,11 +202,11 @@ function connect(service, normalize) {
196
202
  }
197
203
  },
198
204
  onFocus() {
199
- queueMicrotask(() => {
200
- send({ type: "INPUT.FOCUS", index });
201
- });
205
+ send({ type: "INPUT.FOCUS", index });
202
206
  },
203
- onBlur() {
207
+ onBlur(event) {
208
+ const target = event.relatedTarget;
209
+ if (isHTMLElement(target) && target.dataset.ownedby === getRootId(scope)) return;
204
210
  send({ type: "INPUT.BLUR", index });
205
211
  }
206
212
  });
@@ -214,7 +220,7 @@ var machine = createMachine({
214
220
  placeholder: "\u25CB",
215
221
  otp: false,
216
222
  type: "numeric",
217
- defaultValue: [],
223
+ defaultValue: props2.count ? fill([], props2.count) : [],
218
224
  ...props2,
219
225
  translations: {
220
226
  inputLabel: (index, length) => `pin code ${index + 1} of ${length}`,
@@ -228,7 +234,6 @@ var machine = createMachine({
228
234
  context({ prop, bindable }) {
229
235
  return {
230
236
  value: bindable(() => ({
231
- sync: true,
232
237
  value: prop("value"),
233
238
  defaultValue: prop("defaultValue"),
234
239
  onChange(value) {
@@ -238,16 +243,15 @@ var machine = createMachine({
238
243
  focusedIndex: bindable(() => ({
239
244
  sync: true,
240
245
  defaultValue: -1
246
+ })),
247
+ // TODO: Move this to `props` in next major version
248
+ count: bindable(() => ({
249
+ defaultValue: prop("count")
241
250
  }))
242
251
  };
243
252
  },
244
- refs() {
245
- return {
246
- count: 0
247
- };
248
- },
249
253
  computed: {
250
- _value: ({ context, refs }) => fill(context.get("value"), refs.get("count")),
254
+ _value: ({ context }) => fill(context.get("value"), context.get("count")),
251
255
  valueLength: ({ computed }) => computed("_value").length,
252
256
  filledValueLength: ({ computed }) => computed("_value").filter((v) => v?.trim() !== "").length,
253
257
  isValueComplete: ({ computed }) => computed("valueLength") === computed("filledValueLength"),
@@ -349,9 +353,10 @@ var machine = createMachine({
349
353
  const inputEl = getHiddenInputEl(scope);
350
354
  dispatchInputValueEvent(inputEl, { value: computed("valueAsString") });
351
355
  },
352
- setInputCount({ scope, refs }) {
356
+ setInputCount({ scope, context, prop }) {
357
+ if (prop("count")) return;
353
358
  const inputEls = getInputEls(scope);
354
- refs.set("count", inputEls.length);
359
+ context.set("count", inputEls.length);
355
360
  },
356
361
  focusInput({ context, scope }) {
357
362
  const focusedIndex = context.get("focusedIndex");
@@ -384,8 +389,8 @@ var machine = createMachine({
384
389
  setFocusedIndex({ context, event }) {
385
390
  context.set("focusedIndex", event.index);
386
391
  },
387
- setValue({ context, event, refs }) {
388
- const value = fill(event.value, refs.get("count"));
392
+ setValue({ context, event }) {
393
+ const value = fill(event.value, context.get("count"));
389
394
  context.set("value", value);
390
395
  },
391
396
  setFocusedValue({ context, event, computed }) {
@@ -396,38 +401,41 @@ var machine = createMachine({
396
401
  },
397
402
  revertInputValue({ context, computed, scope }) {
398
403
  const inputEl = getInputElAtIndex(scope, context.get("focusedIndex"));
399
- inputEl.value = computed("focusedValue");
404
+ setInputValue(inputEl, computed("focusedValue"));
400
405
  },
401
406
  syncInputValue({ context, event, scope }) {
402
407
  const value = context.get("value");
403
408
  const inputEl = getInputElAtIndex(scope, event.index);
404
- inputEl.value = value[event.index];
409
+ setInputValue(inputEl, value[event.index]);
405
410
  },
406
411
  syncInputElements({ context, scope }) {
407
412
  const inputEls = getInputEls(scope);
408
413
  const value = context.get("value");
409
414
  inputEls.forEach((inputEl, index) => {
410
- inputEl.value = value[index];
415
+ setInputValue(inputEl, value[index]);
411
416
  });
412
417
  },
413
- setPastedValue({ context, event, computed }) {
418
+ setPastedValue({ context, event, computed, flush }) {
414
419
  raf(() => {
415
420
  const valueAsString = computed("valueAsString");
416
421
  const focusedIndex = context.get("focusedIndex");
422
+ const valueLength = computed("valueLength");
417
423
  const filledValueLength = computed("filledValueLength");
418
424
  const startIndex = Math.min(focusedIndex, filledValueLength);
419
425
  const left = startIndex > 0 ? valueAsString.substring(0, focusedIndex) : "";
420
- const right = event.value.substring(0, computed("valueLength") - startIndex);
421
- const value = fill(`${left}${right}`.split(""), computed("valueLength"));
422
- context.set("value", value);
426
+ const right = event.value.substring(0, valueLength - startIndex);
427
+ const value = fill(`${left}${right}`.split(""), valueLength);
428
+ flush(() => {
429
+ context.set("value", value);
430
+ });
423
431
  });
424
432
  },
425
433
  setValueAtIndex({ context, event, computed }) {
426
434
  const nextValue = getNextValue(computed("focusedValue"), event.value);
427
435
  context.set("value", setValueAtIndex(computed("_value"), event.index, nextValue));
428
436
  },
429
- clearValue({ context, refs }) {
430
- const nextValue = Array.from({ length: refs.get("count") }).fill("");
437
+ clearValue({ context }) {
438
+ const nextValue = Array.from({ length: context.get("count") }).fill("");
431
439
  context.set("value", nextValue);
432
440
  },
433
441
  clearFocusedValue({ context, computed }) {
@@ -475,6 +483,8 @@ function fill(value, count) {
475
483
  var props = createProps()([
476
484
  "autoFocus",
477
485
  "blurOnComplete",
486
+ "count",
487
+ "defaultValue",
478
488
  "dir",
479
489
  "disabled",
480
490
  "form",
@@ -488,15 +498,14 @@ var props = createProps()([
488
498
  "onValueComplete",
489
499
  "onValueInvalid",
490
500
  "otp",
491
- "readOnly",
492
501
  "pattern",
493
502
  "placeholder",
503
+ "readOnly",
494
504
  "required",
495
505
  "selectOnFocus",
496
506
  "translations",
497
507
  "type",
498
- "value",
499
- "defaultValue"
508
+ "value"
500
509
  ]);
501
510
  var splitProps = createSplitProps(props);
502
511
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zag-js/pin-input",
3
- "version": "1.1.0",
3
+ "version": "1.2.1",
4
4
  "description": "Core logic for the pin-input widget implemented as a state machine",
5
5
  "keywords": [
6
6
  "js",
@@ -26,11 +26,11 @@
26
26
  "url": "https://github.com/chakra-ui/zag/issues"
27
27
  },
28
28
  "dependencies": {
29
- "@zag-js/anatomy": "1.1.0",
30
- "@zag-js/utils": "1.1.0",
31
- "@zag-js/dom-query": "1.1.0",
32
- "@zag-js/core": "1.1.0",
33
- "@zag-js/types": "1.1.0"
29
+ "@zag-js/anatomy": "1.2.1",
30
+ "@zag-js/dom-query": "1.2.1",
31
+ "@zag-js/utils": "1.2.1",
32
+ "@zag-js/core": "1.2.1",
33
+ "@zag-js/types": "1.2.1"
34
34
  },
35
35
  "devDependencies": {
36
36
  "clean-package": "2.2.0"