mirror of
https://github.com/mosip/inji-wallet.git
synced 2026-01-08 21:18:14 -05:00
155 lines
3.6 KiB
TypeScript
155 lines
3.6 KiB
TypeScript
import { useRef, createRef, MutableRefObject } from 'react';
|
|
import { useMachine } from '@xstate/react';
|
|
import { EventFrom } from 'xstate';
|
|
import { createModel } from 'xstate/lib/model';
|
|
import { TextInput } from 'react-native';
|
|
|
|
const model = createModel(
|
|
{
|
|
selectedIndex: 0,
|
|
error: '',
|
|
inputRefs: [] as PinInputRef[],
|
|
values: [] as string[],
|
|
},
|
|
{
|
|
events: {
|
|
FOCUS_INPUT: (index: number) => ({ index }),
|
|
UPDATE_INPUT: (value: string, index: number) => ({ value, index }),
|
|
KEY_PRESS: (key: string) => ({ key }),
|
|
},
|
|
}
|
|
);
|
|
|
|
export const pinInputMachine = model.createMachine(
|
|
{
|
|
predictableActionArguments: true,
|
|
preserveActionOrder: true,
|
|
tsTypes: {} as import('./pinInput.typegen').Typegen0,
|
|
schema: {
|
|
context: model.initialContext,
|
|
events: {} as EventFrom<typeof model>,
|
|
},
|
|
id: 'pinInput',
|
|
initial: 'idle',
|
|
states: {
|
|
idle: {
|
|
on: {
|
|
FOCUS_INPUT: {
|
|
actions: ['selectInput'],
|
|
},
|
|
UPDATE_INPUT: [
|
|
{
|
|
cond: 'isBlank',
|
|
actions: ['updateInput'],
|
|
},
|
|
{
|
|
cond: 'hasNextInput',
|
|
target: 'selectingNext',
|
|
actions: ['updateInput'],
|
|
},
|
|
{
|
|
actions: ['updateInput'],
|
|
},
|
|
],
|
|
KEY_PRESS: {
|
|
cond: 'canGoBack',
|
|
target: 'selectingPrev',
|
|
},
|
|
},
|
|
after: {
|
|
// allowance to wait for route transition to end
|
|
INITIAL_FOCUS_DELAY: {
|
|
actions: ['focusSelected'],
|
|
},
|
|
},
|
|
},
|
|
selectingNext: {
|
|
entry: ['selectNextInput', 'focusSelected'],
|
|
always: 'idle',
|
|
},
|
|
selectingPrev: {
|
|
entry: ['selectPrevInput', 'clearInput', 'focusSelected'],
|
|
always: 'idle',
|
|
},
|
|
},
|
|
},
|
|
{
|
|
actions: {
|
|
selectInput: model.assign({
|
|
selectedIndex: (_, event) => event.index,
|
|
}),
|
|
|
|
selectNextInput: model.assign({
|
|
selectedIndex: ({ selectedIndex }) => selectedIndex + 1,
|
|
}),
|
|
|
|
selectPrevInput: model.assign({
|
|
selectedIndex: ({ selectedIndex }) => selectedIndex - 1,
|
|
}),
|
|
|
|
focusSelected: ({ selectedIndex, inputRefs }) => {
|
|
inputRefs[selectedIndex].current.focus();
|
|
},
|
|
|
|
clearInput: model.assign({
|
|
values: ({ values, selectedIndex }) => {
|
|
const newValues = [...values];
|
|
newValues[selectedIndex] = '';
|
|
return newValues;
|
|
},
|
|
}),
|
|
|
|
updateInput: model.assign({
|
|
values: ({ values }, event) => {
|
|
const newValues = [...values];
|
|
newValues[event.index] = event.value;
|
|
return newValues;
|
|
},
|
|
}),
|
|
},
|
|
|
|
guards: {
|
|
hasNextInput: ({ inputRefs, selectedIndex }) => {
|
|
return selectedIndex + 1 < inputRefs.length;
|
|
},
|
|
|
|
isBlank: (_, event) => {
|
|
return !event.value;
|
|
},
|
|
|
|
canGoBack: ({ values, selectedIndex }, event) => {
|
|
return (
|
|
selectedIndex - 1 >= 0 &&
|
|
!values[selectedIndex] &&
|
|
event.key === 'Backspace'
|
|
);
|
|
},
|
|
},
|
|
|
|
delays: {
|
|
INITIAL_FOCUS_DELAY: 100,
|
|
},
|
|
}
|
|
);
|
|
|
|
export function usePinInput(length: number) {
|
|
const machine = useRef(
|
|
pinInputMachine.withContext({
|
|
...pinInputMachine.context,
|
|
inputRefs: Array(length)
|
|
.fill(null)
|
|
.map(() => createRef()),
|
|
})
|
|
);
|
|
|
|
const [state, send] = useMachine(machine.current);
|
|
|
|
return {
|
|
state,
|
|
send,
|
|
events: model.events,
|
|
};
|
|
}
|
|
|
|
export type PinInputRef = MutableRefObject<TextInput>;
|