@zag-js/pin-input 0.16.0 → 0.18.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.mts +39 -39
- package/dist/index.d.ts +39 -39
- package/dist/index.js +74 -64
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +74 -64
- package/dist/index.mjs.map +1 -1
- package/package.json +9 -9
- package/src/index.ts +1 -1
- package/src/pin-input.connect.ts +3 -3
- package/src/pin-input.dom.ts +3 -3
- package/src/pin-input.machine.ts +76 -60
- package/src/pin-input.types.ts +36 -36
package/src/pin-input.machine.ts
CHANGED
|
@@ -35,8 +35,8 @@ export function machine(userContext: UserDefinedContext) {
|
|
|
35
35
|
},
|
|
36
36
|
|
|
37
37
|
watch: {
|
|
38
|
-
focusedIndex: ["focusInput", "
|
|
39
|
-
value: ["
|
|
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"
|
|
49
|
+
actions: ["setValueAtIndex"],
|
|
50
50
|
},
|
|
51
|
-
{ actions: ["setValue"
|
|
51
|
+
{ actions: ["setValue"] },
|
|
52
52
|
],
|
|
53
53
|
CLEAR_VALUE: [
|
|
54
54
|
{
|
|
55
55
|
guard: "isDisabled",
|
|
56
|
-
actions: ["clearValue"
|
|
56
|
+
actions: ["clearValue"],
|
|
57
57
|
},
|
|
58
58
|
{
|
|
59
|
-
actions: ["clearValue", "
|
|
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", "
|
|
78
|
+
actions: ["setFocusedValue", "syncInputValue"],
|
|
79
79
|
},
|
|
80
80
|
{
|
|
81
81
|
guard: "isValidValue",
|
|
82
|
-
actions: ["setFocusedValue", "
|
|
82
|
+
actions: ["setFocusedValue", "setNextFocusedIndex", "syncInputValue"],
|
|
83
83
|
},
|
|
84
84
|
],
|
|
85
85
|
PASTE: [
|
|
86
86
|
{
|
|
87
87
|
guard: "isValidValue",
|
|
88
|
-
actions: ["setPastedValue", "
|
|
88
|
+
actions: ["setPastedValue", "setLastValueFocusIndex"],
|
|
89
89
|
},
|
|
90
|
-
{ actions: ["
|
|
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"
|
|
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"
|
|
109
|
+
actions: ["clearFocusedValue"],
|
|
110
110
|
},
|
|
111
111
|
{
|
|
112
|
-
actions: ["setPrevFocusedIndex", "clearFocusedValue"
|
|
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
|
|
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
|
|
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
|
|
149
|
+
setupValue(ctx) {
|
|
150
150
|
if (ctx.value.length) return
|
|
151
|
-
const inputs = dom.
|
|
151
|
+
const inputs = dom.getInputEls(ctx)
|
|
152
152
|
const emptyValues = Array.from<string>({ length: inputs.length }).fill("")
|
|
153
|
-
|
|
153
|
+
assignValue(ctx, emptyValues)
|
|
154
154
|
},
|
|
155
|
-
focusInput
|
|
155
|
+
focusInput(ctx) {
|
|
156
156
|
raf(() => {
|
|
157
157
|
if (ctx.focusedIndex === -1) return
|
|
158
158
|
dom.getFocusedInputEl(ctx)?.focus()
|
|
159
159
|
})
|
|
160
160
|
},
|
|
161
|
-
|
|
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
|
|
170
|
+
invokeOnComplete(ctx) {
|
|
171
171
|
if (!ctx.isValueComplete) return
|
|
172
172
|
ctx.onComplete?.({ value: Array.from(ctx.value), valueAsString: ctx.valueAsString })
|
|
173
173
|
},
|
|
174
|
-
|
|
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
|
|
177
|
+
clearFocusedIndex(ctx) {
|
|
185
178
|
ctx.focusedIndex = -1
|
|
186
179
|
},
|
|
187
|
-
|
|
188
|
-
assign(ctx, evt.value)
|
|
189
|
-
},
|
|
190
|
-
setFocusedIndex: (ctx, evt) => {
|
|
180
|
+
setFocusedIndex(ctx, evt) {
|
|
191
181
|
ctx.focusedIndex = evt.index
|
|
192
182
|
},
|
|
193
|
-
|
|
194
|
-
|
|
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
|
|
198
|
-
|
|
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
|
|
203
|
-
|
|
204
|
-
|
|
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
|
-
|
|
208
|
+
set.value(ctx, value)
|
|
212
209
|
})
|
|
213
210
|
},
|
|
214
|
-
setValueAtIndex
|
|
215
|
-
|
|
211
|
+
setValueAtIndex(ctx, evt) {
|
|
212
|
+
const nextValue = getNextValue(ctx.focusedValue, evt.value)
|
|
213
|
+
set.valueAtIndex(ctx, evt.index, nextValue)
|
|
216
214
|
},
|
|
217
|
-
clearValue
|
|
215
|
+
clearValue(ctx) {
|
|
218
216
|
const nextValue = Array.from<string>({ length: ctx.valueLength }).fill("")
|
|
219
|
-
|
|
220
|
-
},
|
|
221
|
-
clearFocusedValue: (ctx) => {
|
|
222
|
-
ctx.value[ctx.focusedIndex] = ""
|
|
217
|
+
set.value(ctx, nextValue)
|
|
223
218
|
},
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
input.value = ctx.focusedValue
|
|
219
|
+
clearFocusedValue(ctx) {
|
|
220
|
+
set.valueAtIndex(ctx, ctx.focusedIndex, "")
|
|
227
221
|
},
|
|
228
|
-
setFocusIndexToFirst
|
|
222
|
+
setFocusIndexToFirst(ctx) {
|
|
229
223
|
ctx.focusedIndex = 0
|
|
230
224
|
},
|
|
231
|
-
setNextFocusedIndex
|
|
225
|
+
setNextFocusedIndex(ctx) {
|
|
232
226
|
ctx.focusedIndex = Math.min(ctx.focusedIndex + 1, ctx.valueLength - 1)
|
|
233
227
|
},
|
|
234
|
-
setPrevFocusedIndex
|
|
228
|
+
setPrevFocusedIndex(ctx) {
|
|
235
229
|
ctx.focusedIndex = Math.max(ctx.focusedIndex - 1, 0)
|
|
236
230
|
},
|
|
237
|
-
setLastValueFocusIndex
|
|
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
|
|
254
|
-
|
|
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
|
|
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
|
+
}
|
package/src/pin-input.types.ts
CHANGED
|
@@ -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
|
+
}
|