@zag-js/pin-input 0.16.0 → 0.17.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.
@@ -35,8 +35,8 @@ export function machine(userContext: UserDefinedContext) {
35
35
  },
36
36
 
37
37
  watch: {
38
- focusedIndex: ["focusInput", "setInputSelection"],
39
- value: ["dispatchInputEvent", "syncInputElements"],
38
+ focusedIndex: ["focusInput", "selectInputIfNeeded"],
39
+ value: ["syncInputElements"],
40
40
  isValueComplete: ["invokeOnComplete", "blurFocusedInputIfNeeded"],
41
41
  },
42
42
 
@@ -46,17 +46,17 @@ export function machine(userContext: UserDefinedContext) {
46
46
  SET_VALUE: [
47
47
  {
48
48
  guard: "hasIndex",
49
- actions: ["setValueAtIndex", "invokeOnChange"],
49
+ actions: ["setValueAtIndex"],
50
50
  },
51
- { actions: ["setValue", "invokeOnChange"] },
51
+ { actions: ["setValue"] },
52
52
  ],
53
53
  CLEAR_VALUE: [
54
54
  {
55
55
  guard: "isDisabled",
56
- actions: ["clearValue", "invokeOnChange"],
56
+ actions: ["clearValue"],
57
57
  },
58
58
  {
59
- actions: ["clearValue", "invokeOnChange", "setFocusIndexToFirst"],
59
+ actions: ["clearValue", "setFocusIndexToFirst"],
60
60
  },
61
61
  ],
62
62
  },
@@ -75,19 +75,19 @@ export function machine(userContext: UserDefinedContext) {
75
75
  INPUT: [
76
76
  {
77
77
  guard: and("isFinalValue", "isValidValue"),
78
- actions: ["setFocusedValue", "invokeOnChange", "syncInputValue"],
78
+ actions: ["setFocusedValue", "syncInputValue"],
79
79
  },
80
80
  {
81
81
  guard: "isValidValue",
82
- actions: ["setFocusedValue", "invokeOnChange", "setNextFocusedIndex", "syncInputValue"],
82
+ actions: ["setFocusedValue", "setNextFocusedIndex", "syncInputValue"],
83
83
  },
84
84
  ],
85
85
  PASTE: [
86
86
  {
87
87
  guard: "isValidValue",
88
- actions: ["setPastedValue", "invokeOnChange", "setLastValueFocusIndex"],
88
+ actions: ["setPastedValue", "setLastValueFocusIndex"],
89
89
  },
90
- { actions: ["resetFocusedValue", "invokeOnChange"] },
90
+ { actions: ["revertInputValue"] },
91
91
  ],
92
92
  BLUR: {
93
93
  target: "idle",
@@ -95,7 +95,7 @@ export function machine(userContext: UserDefinedContext) {
95
95
  },
96
96
  DELETE: {
97
97
  guard: "hasValue",
98
- actions: ["clearFocusedValue", "invokeOnChange"],
98
+ actions: ["clearFocusedValue"],
99
99
  },
100
100
  ARROW_LEFT: {
101
101
  actions: "setPrevFocusedIndex",
@@ -106,10 +106,10 @@ export function machine(userContext: UserDefinedContext) {
106
106
  BACKSPACE: [
107
107
  {
108
108
  guard: "hasValue",
109
- actions: ["clearFocusedValue", "invokeOnChange"],
109
+ actions: ["clearFocusedValue"],
110
110
  },
111
111
  {
112
- actions: ["setPrevFocusedIndex", "clearFocusedValue", "invokeOnChange"],
112
+ actions: ["setPrevFocusedIndex", "clearFocusedValue"],
113
113
  },
114
114
  ],
115
115
  ENTER: {
@@ -130,12 +130,12 @@ export function machine(userContext: UserDefinedContext) {
130
130
  isValueEmpty: (_ctx, evt) => evt.value === "",
131
131
  hasValue: (ctx) => ctx.value[ctx.focusedIndex] !== "",
132
132
  isValueComplete: (ctx) => ctx.isValueComplete,
133
- isValidValue: (ctx, evt) => {
133
+ isValidValue(ctx, evt) {
134
134
  if (!ctx.pattern) return isValidType(evt.value, ctx.type)
135
135
  const regex = new RegExp(ctx.pattern, "g")
136
136
  return regex.test(evt.value)
137
137
  },
138
- isFinalValue: (ctx) => {
138
+ isFinalValue(ctx) {
139
139
  return (
140
140
  ctx.filledValueLength + 1 === ctx.valueLength &&
141
141
  ctx.value.findIndex((v) => v.trim() === "") === ctx.focusedIndex
@@ -146,19 +146,19 @@ export function machine(userContext: UserDefinedContext) {
146
146
  isDisabled: (ctx) => !!ctx.disabled,
147
147
  },
148
148
  actions: {
149
- setupValue: (ctx) => {
149
+ setupValue(ctx) {
150
150
  if (ctx.value.length) return
151
- const inputs = dom.getElements(ctx)
151
+ const inputs = dom.getInputEls(ctx)
152
152
  const emptyValues = Array.from<string>({ length: inputs.length }).fill("")
153
- assign(ctx, emptyValues)
153
+ assignValue(ctx, emptyValues)
154
154
  },
155
- focusInput: (ctx) => {
155
+ focusInput(ctx) {
156
156
  raf(() => {
157
157
  if (ctx.focusedIndex === -1) return
158
158
  dom.getFocusedInputEl(ctx)?.focus()
159
159
  })
160
160
  },
161
- setInputSelection: (ctx) => {
161
+ selectInputIfNeeded(ctx) {
162
162
  raf(() => {
163
163
  if (ctx.focusedIndex === -1) return
164
164
  const input = dom.getFocusedInputEl(ctx)
@@ -167,74 +167,68 @@ export function machine(userContext: UserDefinedContext) {
167
167
  input.selectionEnd = length
168
168
  })
169
169
  },
170
- invokeOnComplete: (ctx) => {
170
+ invokeOnComplete(ctx) {
171
171
  if (!ctx.isValueComplete) return
172
172
  ctx.onComplete?.({ value: Array.from(ctx.value), valueAsString: ctx.valueAsString })
173
173
  },
174
- invokeOnChange: (ctx) => {
175
- ctx.onChange?.({ value: Array.from(ctx.value) })
176
- },
177
- dispatchInputEvent: (ctx) => {
178
- const inputEl = dom.getHiddenInputEl(ctx)
179
- dispatchInputValueEvent(inputEl, { value: ctx.valueAsString })
180
- },
181
- invokeOnInvalid: (ctx, evt) => {
174
+ invokeOnInvalid(ctx, evt) {
182
175
  ctx.onInvalid?.({ value: evt.value, index: ctx.focusedIndex })
183
176
  },
184
- clearFocusedIndex: (ctx) => {
177
+ clearFocusedIndex(ctx) {
185
178
  ctx.focusedIndex = -1
186
179
  },
187
- setValue: (ctx, evt) => {
188
- assign(ctx, evt.value)
189
- },
190
- setFocusedIndex: (ctx, evt) => {
180
+ setFocusedIndex(ctx, evt) {
191
181
  ctx.focusedIndex = evt.index
192
182
  },
193
- setFocusedValue: (ctx, evt) => {
194
- ctx.value[ctx.focusedIndex] = getNextValue(ctx.focusedValue, evt.value)
183
+ setValue(ctx, evt) {
184
+ set.value(ctx, evt.value)
185
+ },
186
+ setFocusedValue(ctx, evt) {
187
+ const nextValue = getNextValue(ctx.focusedValue, evt.value)
188
+ set.valueAtIndex(ctx, ctx.focusedIndex, nextValue)
189
+ },
190
+ revertInputValue(ctx) {
191
+ const inputEl = dom.getFocusedInputEl(ctx)
192
+ dom.setValue(inputEl, ctx.focusedValue)
195
193
  },
196
194
  syncInputValue(ctx, evt) {
197
- const input = dom.getInputEl(ctx, evt.index.toString())
198
- if (!input) return
199
- input.value = ctx.value[evt.index]
195
+ const inputEl = dom.getInputEl(ctx, evt.index.toString())
196
+ dom.setValue(inputEl, ctx.value[evt.index])
200
197
  },
201
198
  syncInputElements(ctx) {
202
- const inputs = dom.getElements(ctx)
203
- inputs.forEach((input, index) => {
204
- input.value = ctx.value[index]
199
+ const inputEls = dom.getInputEls(ctx)
200
+ inputEls.forEach((inputEl, index) => {
201
+ dom.setValue(inputEl, ctx.value[index])
205
202
  })
206
203
  },
207
204
  setPastedValue(ctx, evt) {
208
205
  raf(() => {
209
206
  const startIndex = ctx.focusedValue ? 1 : 0
210
207
  const value = evt.value.substring(startIndex, startIndex + ctx.valueLength)
211
- assign(ctx, value)
208
+ set.value(ctx, value)
212
209
  })
213
210
  },
214
- setValueAtIndex: (ctx, evt) => {
215
- ctx.value[evt.index] = getNextValue(ctx.focusedValue, evt.value)
211
+ setValueAtIndex(ctx, evt) {
212
+ const nextValue = getNextValue(ctx.focusedValue, evt.value)
213
+ set.valueAtIndex(ctx, evt.index, nextValue)
216
214
  },
217
- clearValue: (ctx) => {
215
+ clearValue(ctx) {
218
216
  const nextValue = Array.from<string>({ length: ctx.valueLength }).fill("")
219
- assign(ctx, nextValue)
220
- },
221
- clearFocusedValue: (ctx) => {
222
- ctx.value[ctx.focusedIndex] = ""
217
+ set.value(ctx, nextValue)
223
218
  },
224
- resetFocusedValue: (ctx) => {
225
- const input = dom.getFocusedInputEl(ctx)
226
- input.value = ctx.focusedValue
219
+ clearFocusedValue(ctx) {
220
+ set.valueAtIndex(ctx, ctx.focusedIndex, "")
227
221
  },
228
- setFocusIndexToFirst: (ctx) => {
222
+ setFocusIndexToFirst(ctx) {
229
223
  ctx.focusedIndex = 0
230
224
  },
231
- setNextFocusedIndex: (ctx) => {
225
+ setNextFocusedIndex(ctx) {
232
226
  ctx.focusedIndex = Math.min(ctx.focusedIndex + 1, ctx.valueLength - 1)
233
227
  },
234
- setPrevFocusedIndex: (ctx) => {
228
+ setPrevFocusedIndex(ctx) {
235
229
  ctx.focusedIndex = Math.max(ctx.focusedIndex - 1, 0)
236
230
  },
237
- setLastValueFocusIndex: (ctx) => {
231
+ setLastValueFocusIndex(ctx) {
238
232
  raf(() => {
239
233
  ctx.focusedIndex = Math.min(ctx.filledValueLength, ctx.valueLength - 1)
240
234
  })
@@ -250,8 +244,8 @@ export function machine(userContext: UserDefinedContext) {
250
244
  },
251
245
  requestFormSubmit(ctx) {
252
246
  if (!ctx.name || !ctx.isValueComplete) return
253
- const input = dom.getHiddenInputEl(ctx)
254
- input?.form?.requestSubmit()
247
+ const inputEl = dom.getHiddenInputEl(ctx)
248
+ inputEl?.form?.requestSubmit()
255
249
  },
256
250
  },
257
251
  },
@@ -269,7 +263,7 @@ function isValidType(value: string, type: MachineContext["type"]) {
269
263
  return !!REGEX[type]?.test(value)
270
264
  }
271
265
 
272
- function assign(ctx: MachineContext, value: string | string[]) {
266
+ function assignValue(ctx: MachineContext, value: string | string[]) {
273
267
  const arr = Array.isArray(value) ? value : value.split("").filter(Boolean)
274
268
  arr.forEach((value, index) => {
275
269
  ctx.value[index] = value
@@ -282,3 +276,25 @@ function getNextValue(current: string, next: string) {
282
276
  else if (current[0] === next[1]) nextValue = next[0]
283
277
  return nextValue
284
278
  }
279
+
280
+ const invoke = {
281
+ change(ctx: MachineContext) {
282
+ // callback
283
+ ctx.onChange?.({ value: Array.from(ctx.value) })
284
+
285
+ // form event
286
+ const inputEl = dom.getHiddenInputEl(ctx)
287
+ dispatchInputValueEvent(inputEl, { value: ctx.valueAsString })
288
+ },
289
+ }
290
+
291
+ const set = {
292
+ value(ctx: MachineContext, values: string[]) {
293
+ assignValue(ctx, values)
294
+ invoke.change(ctx)
295
+ },
296
+ valueAtIndex(ctx: MachineContext, index: number, value: string) {
297
+ ctx.value[index] = value
298
+ invoke.change(ctx)
299
+ },
300
+ }
@@ -90,42 +90,6 @@ type PublicContext = DirectionProperty &
90
90
  translations: IntlTranslations
91
91
  }
92
92
 
93
- export type PublicApi<T extends PropTypes = PropTypes> = {
94
- /**
95
- * The value of the input as an array of strings.
96
- */
97
- value: string[]
98
- /**
99
- * The value of the input as a string.
100
- */
101
- valueAsString: string
102
- /**
103
- * Whether all inputs are filled.
104
- */
105
- isValueComplete: boolean
106
- /**
107
- * Function to set the value of the inputs.
108
- */
109
- setValue(value: string[]): void
110
- /**
111
- * Function to clear the value of the inputs.
112
- */
113
- clearValue(): void
114
- /**
115
- * Function to set the value of the input at a specific index.
116
- */
117
- setValueAtIndex(index: number, value: string): void
118
- /**
119
- * Function to focus the pin-input. This will focus the first input.
120
- */
121
- focus: () => void
122
- rootProps: T["element"]
123
- labelProps: T["label"]
124
- hiddenInputProps: T["input"]
125
- controlProps: T["element"]
126
- getInputProps({ index }: { index: number }): T["input"]
127
- }
128
-
129
93
  export type UserDefinedContext = RequiredBy<PublicContext, "id">
130
94
 
131
95
  type ComputedContext = Readonly<{
@@ -173,3 +137,39 @@ export type MachineState = {
173
137
  export type State = S.State<MachineContext, MachineState>
174
138
 
175
139
  export type Send = S.Send<S.AnyEventObject>
140
+
141
+ export type MachineApi<T extends PropTypes = PropTypes> = {
142
+ /**
143
+ * The value of the input as an array of strings.
144
+ */
145
+ value: string[]
146
+ /**
147
+ * The value of the input as a string.
148
+ */
149
+ valueAsString: string
150
+ /**
151
+ * Whether all inputs are filled.
152
+ */
153
+ isValueComplete: boolean
154
+ /**
155
+ * Function to set the value of the inputs.
156
+ */
157
+ setValue(value: string[]): void
158
+ /**
159
+ * Function to clear the value of the inputs.
160
+ */
161
+ clearValue(): void
162
+ /**
163
+ * Function to set the value of the input at a specific index.
164
+ */
165
+ setValueAtIndex(index: number, value: string): void
166
+ /**
167
+ * Function to focus the pin-input. This will focus the first input.
168
+ */
169
+ focus: () => void
170
+ rootProps: T["element"]
171
+ labelProps: T["label"]
172
+ hiddenInputProps: T["input"]
173
+ controlProps: T["element"]
174
+ getInputProps({ index }: { index: number }): T["input"]
175
+ }