init
This commit is contained in:
commit
55ae41cb0c
1902 changed files with 513237 additions and 0 deletions
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -0,0 +1,179 @@
|
|||
import { useCallback, useReducer } from 'react'
|
||||
|
||||
export type AnswerValue = string
|
||||
|
||||
export type QuestionState = {
|
||||
selectedValue?: string | string[]
|
||||
textInputValue: string
|
||||
}
|
||||
|
||||
type State = {
|
||||
currentQuestionIndex: number
|
||||
answers: Record<string, AnswerValue>
|
||||
questionStates: Record<string, QuestionState>
|
||||
isInTextInput: boolean
|
||||
}
|
||||
|
||||
type Action =
|
||||
| { type: 'next-question' }
|
||||
| { type: 'prev-question' }
|
||||
| {
|
||||
type: 'update-question-state'
|
||||
questionText: string
|
||||
updates: Partial<QuestionState>
|
||||
isMultiSelect: boolean
|
||||
}
|
||||
| {
|
||||
type: 'set-answer'
|
||||
questionText: string
|
||||
answer: string
|
||||
shouldAdvance: boolean
|
||||
}
|
||||
| { type: 'set-text-input-mode'; isInInput: boolean }
|
||||
|
||||
function reducer(state: State, action: Action): State {
|
||||
switch (action.type) {
|
||||
case 'next-question':
|
||||
return {
|
||||
...state,
|
||||
currentQuestionIndex: state.currentQuestionIndex + 1,
|
||||
isInTextInput: false,
|
||||
}
|
||||
|
||||
case 'prev-question':
|
||||
return {
|
||||
...state,
|
||||
currentQuestionIndex: Math.max(0, state.currentQuestionIndex - 1),
|
||||
isInTextInput: false,
|
||||
}
|
||||
|
||||
case 'update-question-state': {
|
||||
const existing = state.questionStates[action.questionText]
|
||||
const newState: QuestionState = {
|
||||
selectedValue:
|
||||
action.updates.selectedValue ??
|
||||
existing?.selectedValue ??
|
||||
(action.isMultiSelect ? [] : undefined),
|
||||
textInputValue:
|
||||
action.updates.textInputValue ?? existing?.textInputValue ?? '',
|
||||
}
|
||||
|
||||
return {
|
||||
...state,
|
||||
questionStates: {
|
||||
...state.questionStates,
|
||||
[action.questionText]: newState,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
case 'set-answer': {
|
||||
const newState = {
|
||||
...state,
|
||||
answers: {
|
||||
...state.answers,
|
||||
[action.questionText]: action.answer,
|
||||
},
|
||||
}
|
||||
|
||||
if (action.shouldAdvance) {
|
||||
return {
|
||||
...newState,
|
||||
currentQuestionIndex: newState.currentQuestionIndex + 1,
|
||||
isInTextInput: false,
|
||||
}
|
||||
}
|
||||
|
||||
return newState
|
||||
}
|
||||
|
||||
case 'set-text-input-mode':
|
||||
return {
|
||||
...state,
|
||||
isInTextInput: action.isInInput,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const INITIAL_STATE: State = {
|
||||
currentQuestionIndex: 0,
|
||||
answers: {},
|
||||
questionStates: {},
|
||||
isInTextInput: false,
|
||||
}
|
||||
|
||||
export type MultipleChoiceState = {
|
||||
currentQuestionIndex: number
|
||||
answers: Record<string, AnswerValue>
|
||||
questionStates: Record<string, QuestionState>
|
||||
isInTextInput: boolean
|
||||
nextQuestion: () => void
|
||||
prevQuestion: () => void
|
||||
updateQuestionState: (
|
||||
questionText: string,
|
||||
updates: Partial<QuestionState>,
|
||||
isMultiSelect: boolean,
|
||||
) => void
|
||||
setAnswer: (
|
||||
questionText: string,
|
||||
answer: string,
|
||||
shouldAdvance?: boolean,
|
||||
) => void
|
||||
setTextInputMode: (isInInput: boolean) => void
|
||||
}
|
||||
|
||||
export function useMultipleChoiceState(): MultipleChoiceState {
|
||||
const [state, dispatch] = useReducer(reducer, INITIAL_STATE)
|
||||
|
||||
const nextQuestion = useCallback(() => {
|
||||
dispatch({ type: 'next-question' })
|
||||
}, [])
|
||||
|
||||
const prevQuestion = useCallback(() => {
|
||||
dispatch({ type: 'prev-question' })
|
||||
}, [])
|
||||
|
||||
const updateQuestionState = useCallback(
|
||||
(
|
||||
questionText: string,
|
||||
updates: Partial<QuestionState>,
|
||||
isMultiSelect: boolean,
|
||||
) => {
|
||||
dispatch({
|
||||
type: 'update-question-state',
|
||||
questionText,
|
||||
updates,
|
||||
isMultiSelect,
|
||||
})
|
||||
},
|
||||
[],
|
||||
)
|
||||
|
||||
const setAnswer = useCallback(
|
||||
(questionText: string, answer: string, shouldAdvance: boolean = true) => {
|
||||
dispatch({
|
||||
type: 'set-answer',
|
||||
questionText,
|
||||
answer,
|
||||
shouldAdvance,
|
||||
})
|
||||
},
|
||||
[],
|
||||
)
|
||||
|
||||
const setTextInputMode = useCallback((isInInput: boolean) => {
|
||||
dispatch({ type: 'set-text-input-mode', isInInput })
|
||||
}, [])
|
||||
|
||||
return {
|
||||
currentQuestionIndex: state.currentQuestionIndex,
|
||||
answers: state.answers,
|
||||
questionStates: state.questionStates,
|
||||
isInTextInput: state.isInTextInput,
|
||||
nextQuestion,
|
||||
prevQuestion,
|
||||
updateQuestionState,
|
||||
setAnswer,
|
||||
setTextInputMode,
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue