{"version":3,"file":"FormReducer-D0uD9Rp3.js","sources":["../../Client/shared/Hooks/FormReducer.tsx"],"sourcesContent":["import { Value } from '@webkit/index';\r\nimport * as React from 'react';\r\nimport { FormEvent, useReducer } from 'react';\r\n\r\ntype ExtractValueType<T> = T extends Value<infer U> ? U : never;\r\nexport type Reducer = Record<string, Value<any>>;\r\n\r\nexport type ReducerType<TForm extends Reducer> = {\r\n    // The current state of the form.\r\n    form: TForm;\r\n\r\n    // The list of keys in the form.\r\n    keys: (keyof TForm)[];\r\n\r\n    // Resets a specific form entry to its original state, or the whole form if key is omitted. Calls Value.reset on each underlying field.\r\n    reset: (key?: keyof TForm) => void;\r\n\r\n    // Validates a specific form entry (which calls the underlying Value's validate method), or the whole form if key is omitted.\r\n    validate: (key?: keyof TForm) => void;\r\n\r\n    // Sets a form Value's underlying value and validates it.\r\n    set: <TKey extends keyof TForm, TValue extends ExtractValueType<TForm[TKey]>>(key: TKey, value: TValue) => void;\r\n\r\n    // Sets a form Value's underlying value but performs no validation.\r\n    setWithoutValidation: <TKey extends keyof TForm, TValue extends ExtractValueType<TForm[TKey]>>(\r\n        key: TKey,\r\n        value: TValue\r\n    ) => void;\r\n\r\n    // Indicates whether a specific form entry is valid, or the whole form if no keys are provided.\r\n    valid: (keys?: (keyof TForm)[]) => boolean;\r\n\r\n    // Submits the underlying form, validating all entries and preventing the default action if any entries are invalid.\r\n    submit: (e?: FormEvent<HTMLFormElement>, keys?: (keyof TForm)[]) => void;\r\n\r\n    // Reverts the entire form to the initial state provided at construction.\r\n    revert: () => void;\r\n\r\n    /** Sets the value and validates it explicity without the form reducer to avoid needing to rerender to see if the value is valid. TODO: We should fix this hook, perhaps by getting rid of useReducer, so it does not have any stale state. */\r\n    setAndForceValidate: (key: keyof TForm, value: any) => void;\r\n};\r\n\r\nexport const useFormReducer = <TForm extends Reducer>(initial: TForm): ReducerType<TForm> => {\r\n    const [form, dispatch] = useReducer(reducer, initial) as [TForm, React.Dispatch<FormAction<TForm>>];\r\n\r\n    const actions = {\r\n        form,\r\n        keys: Object.keys(form),\r\n        reset: (key?: keyof TForm) => dispatch({ type: FormActionType.Reset, key }),\r\n        validate: (key?: keyof TForm) => dispatch({ type: FormActionType.Validate, key }),\r\n        getFormKeys: () => Object.keys(form) as (keyof TForm)[],\r\n        validateByKeys: (keys: (keyof TForm)[]) => dispatch({ type: FormActionType.Validate, keys }),\r\n        set: <TKey extends keyof TForm, TValue extends ExtractValueType<TForm[TKey]>>(key: TKey, value: TValue) =>\r\n            dispatch({ type: FormActionType.Set, key, value }),\r\n        setWithoutValidation: <TKey extends keyof TForm, TValue extends ExtractValueType<TForm[TKey]>>(\r\n            key: TKey,\r\n            value: TValue\r\n        ) => dispatch({ type: FormActionType.SetWithoutValidation, key, value }),\r\n        valid,\r\n        submit,\r\n        revert: () => dispatch({ type: FormActionType.Revert }),\r\n        setAndForceValidate,\r\n    };\r\n\r\n    return actions;\r\n\r\n    function reducer(form: TForm, action: FormAction<TForm>): TForm {\r\n        const { type, key, keys } = action;\r\n        switch (type) {\r\n            case FormActionType.Set:\r\n                if (!key) return form;\r\n\r\n                form[key].set(action.value);\r\n                form[key].validate();\r\n                return { ...form };\r\n            case FormActionType.SetWithoutValidation:\r\n                if (!key) return form;\r\n\r\n                form[key].set(action.value);\r\n\r\n                return { ...form };\r\n            case FormActionType.Reset:\r\n                if (key) return { ...form, [key]: form[key].reset() };\r\n\r\n                Object.keys(form).forEach(key => form[key].reset());\r\n                return { ...form };\r\n            case FormActionType.Validate:\r\n                if (key) {\r\n                    form[key].validate();\r\n                } else {\r\n                    (keys || Object.keys(form)).forEach(key => form[key].validate());\r\n                }\r\n                return { ...form };\r\n            case FormActionType.Revert:\r\n                return { ...initial };\r\n            default:\r\n                throw new Error('No handler for form action.');\r\n        }\r\n    }\r\n\r\n    /** TODO: Remove this when the stale state issue is fixed (see comment above). */\r\n    function setAndForceValidate(key: keyof TForm, value: any) {\r\n        form[key].set(value);\r\n        actions.validate(key);\r\n    }\r\n\r\n    function valid(keys?: (keyof TForm)[]) {\r\n        // We're calling the `reducer` function to validate here because we want to ensure that the\r\n        // form we're checking validity for is up to date. This is important because we may have already\r\n        // called `validate` before `valid`, but the underlying state only updates on a rerender, which\r\n        // may or may not have occurred after the call to `validate` but before the call to `valid`.\r\n        // Calling `reducer` manually allows us to get the most up-to-date form state.\r\n        const newForm = reducer(form, { type: FormActionType.Validate, keys });\r\n        return (keys || Object.keys(newForm)).every(key => newForm[key].valid);\r\n    }\r\n\r\n    function submit(e?: FormEvent<HTMLFormElement>, keys?: (keyof TForm)[]) {\r\n        actions.validate();\r\n        const isValid = actions.valid(keys);\r\n        if (!isValid && e) {\r\n            e.preventDefault();\r\n        }\r\n    }\r\n};\r\n\r\nexport enum FormActionType {\r\n    Set,\r\n    SetWithoutValidation,\r\n    Reset,\r\n    Validate,\r\n    Revert,\r\n}\r\n\r\nexport interface FormAction<TForm extends Reducer> {\r\n    type: FormActionType;\r\n    key?: keyof TForm;\r\n    value?: any;\r\n    keys?: (keyof TForm)[];\r\n}\r\n"],"names":["useFormReducer","initial","form","dispatch","useReducer","reducer","actions","key","keys","value","valid","submit","setAndForceValidate","action","type","newForm"],"mappings":"wCA0Ca,MAAAA,EAAyCC,GAAuC,CACzF,KAAM,CAACC,EAAMC,CAAQ,EAAIC,EAAAA,WAAWC,EAASJ,CAAO,EAE9CK,EAAU,CACZ,KAAAJ,EACA,KAAM,OAAO,KAAKA,CAAI,EACtB,MAAQK,GAAsBJ,EAAS,CAAE,KAAM,EAAsB,IAAAI,EAAK,EAC1E,SAAWA,GAAsBJ,EAAS,CAAE,KAAM,EAAyB,IAAAI,EAAK,EAChF,YAAa,IAAM,OAAO,KAAKL,CAAI,EACnC,eAAiBM,GAA0BL,EAAS,CAAE,KAAM,EAAyB,KAAAK,EAAM,EAC3F,IAAK,CAAyED,EAAWE,IACrFN,EAAS,CAAE,KAAM,EAAoB,IAAAI,EAAK,MAAAE,EAAO,EACrD,qBAAsB,CAClBF,EACAE,IACCN,EAAS,CAAE,KAAM,EAAqC,IAAAI,EAAK,MAAAE,EAAO,EACvE,MAAAC,EACA,OAAAC,EACA,OAAQ,IAAMR,EAAS,CAAE,KAAM,EAAuB,EACtD,oBAAAS,CACJ,EAEO,OAAAN,EAEE,SAAAD,EAAQH,EAAaW,EAAkC,CAC5D,KAAM,CAAE,KAAAC,EAAM,IAAAP,EAAK,KAAAC,CAAS,EAAAK,EAC5B,OAAQC,EAAM,CACV,IAAK,GACG,OAACP,GAELL,EAAKK,CAAG,EAAE,IAAIM,EAAO,KAAK,EAC1BX,EAAKK,CAAG,EAAE,SAAS,EACZ,CAAE,GAAGL,CAAK,GAJAA,EAKrB,IAAK,GACG,OAACK,GAELL,EAAKK,CAAG,EAAE,IAAIM,EAAO,KAAK,EAEnB,CAAE,GAAGX,CAAK,GAJAA,EAKrB,IAAK,GACD,OAAIK,EAAY,CAAE,GAAGL,EAAM,CAACK,CAAG,EAAGL,EAAKK,CAAG,EAAE,OAAQ,GAE7C,OAAA,KAAKL,CAAI,EAAE,QAAQK,GAAOL,EAAKK,CAAG,EAAE,OAAO,EAC3C,CAAE,GAAGL,CAAK,GACrB,IAAK,GACD,OAAIK,EACAL,EAAKK,CAAG,EAAE,SAAS,GAElBC,GAAQ,OAAO,KAAKN,CAAI,GAAG,QAAQK,GAAOL,EAAKK,CAAG,EAAE,SAAA,CAAU,EAE5D,CAAE,GAAGL,CAAK,EACrB,IAAK,GACM,MAAA,CAAE,GAAGD,CAAQ,EACxB,QACU,MAAA,IAAI,MAAM,6BAA6B,CAAA,CACrD,CAIK,SAAAW,EAAoBL,EAAkBE,EAAY,CAClDP,EAAAK,CAAG,EAAE,IAAIE,CAAK,EACnBH,EAAQ,SAASC,CAAG,CAAA,CAGxB,SAASG,EAAMF,EAAwB,CAMnC,MAAMO,EAAUV,EAAQH,EAAM,CAAE,KAAM,EAAyB,KAAAM,EAAM,EAC7D,OAAAA,GAAQ,OAAO,KAAKO,CAAO,GAAG,MAAaR,GAAAQ,EAAQR,CAAG,EAAE,KAAK,CAAA,CAGhE,SAAAI,EAAO,EAAgCH,EAAwB,CACpEF,EAAQ,SAAS,EAEb,CADYA,EAAQ,MAAME,CAAI,GAClB,GACZ,EAAE,eAAe,CACrB,CAER"}