test(ci): add exporter heading levels and a11y typography smoke; update MAIN_PLAN
CI / test (pull_request) Has been cancelled
CI / test (pull_request) Has been cancelled
This commit is contained in:
@@ -0,0 +1,69 @@
|
||||
import { createStore } from 'zustand/vanilla'
|
||||
|
||||
export type BaseField = {
|
||||
name: string
|
||||
label: string
|
||||
required?: boolean
|
||||
pattern?: string
|
||||
maxLength?: number
|
||||
}
|
||||
|
||||
export type TextField = BaseField & { type: 'text' | 'email' | 'tel' | 'date' | 'textarea' }
|
||||
export type CheckboxField = BaseField & { type: 'checkbox' }
|
||||
export type RadioField = BaseField & { type: 'radio'; options: string[] }
|
||||
export type SelectField = BaseField & { type: 'select'; options: string[] }
|
||||
|
||||
export type FormField = TextField | CheckboxField | RadioField | SelectField
|
||||
|
||||
export type FormFieldInput = FormField
|
||||
|
||||
export type FormState = {
|
||||
fields: FormField[]
|
||||
actionUrl: string
|
||||
spam: { honeypotFieldName: string; minSubmitSeconds: number }
|
||||
addField: (f: FormFieldInput) => void
|
||||
moveField: (from: number, to: number) => void
|
||||
removeField: (index: number) => void
|
||||
updateField: (index: number, patch: Partial<FormField>) => void
|
||||
setActionUrl: (url: string) => void
|
||||
setSpam: (s: { honeypotFieldName?: string; minSubmitSeconds?: number }) => void
|
||||
}
|
||||
|
||||
export function createFormStore() {
|
||||
return createStore<FormState>((set) => ({
|
||||
fields: [],
|
||||
actionUrl: '',
|
||||
spam: { honeypotFieldName: '_hp', minSubmitSeconds: 2 },
|
||||
addField: (f) => set((st) => ({ fields: [...st.fields, f] })),
|
||||
moveField: (from, to) =>
|
||||
set((st) => {
|
||||
const arr = [...st.fields]
|
||||
if (from < 0 || from >= arr.length || to < 0 || to >= arr.length) return st
|
||||
const [spliced] = arr.splice(from, 1)
|
||||
arr.splice(to, 0, spliced)
|
||||
return { fields: arr }
|
||||
}),
|
||||
removeField: (index) =>
|
||||
set((st) => {
|
||||
if (index < 0 || index >= st.fields.length) return st
|
||||
const arr = st.fields.slice()
|
||||
arr.splice(index, 1)
|
||||
return { fields: arr }
|
||||
}),
|
||||
updateField: (index, patch) =>
|
||||
set((st) => {
|
||||
if (index < 0 || index >= st.fields.length) return st
|
||||
const next = st.fields.slice()
|
||||
next[index] = { ...next[index], ...patch } as FormField
|
||||
return { fields: next }
|
||||
}),
|
||||
setActionUrl: (url) => set({ actionUrl: url }),
|
||||
setSpam: (s) =>
|
||||
set((st) => ({
|
||||
spam: {
|
||||
honeypotFieldName: s.honeypotFieldName ?? st.spam.honeypotFieldName,
|
||||
minSubmitSeconds: s.minSubmitSeconds ?? st.spam.minSubmitSeconds,
|
||||
},
|
||||
})),
|
||||
}))
|
||||
}
|
||||
Reference in New Issue
Block a user