1+ /* eslint-disable max-lines */
12import { get } from 'webcoreui'
23
4+ export type ValidationFactory = ( formValues : Record < string , string > ) => Record < string , boolean >
5+
36export type FormActions = {
47 validationRules : Record < string , boolean >
8+ validationFactory : ValidationFactory | null
59 isPreventDefault : boolean
610 onErrorCallback : ( ( invalidFields : string [ ] ) => void ) | null
711 preventDefault : ( ) => FormActions
@@ -11,10 +15,11 @@ export type FormActions = {
1115 update : ( field : string , value : string | boolean ) => FormActions
1216 reset : ( ) => FormActions
1317 clear : ( ) => FormActions
14- setValidations : ( validationRules : Record < string , boolean > ) => FormActions
15- isValidForm : ( ) => boolean ,
18+ destroy : ( ) => void
19+ validate : ( callback : ValidationFactory ) => FormActions
20+ isValid : ( ) => boolean ,
1621 useSmartFill : ( options ?: { patterns ?: Record < string , RegExp > , inputs ?: Record < string , string > } ) => FormActions
17- onChange : ( callback : ( formValues : Record < string , string > ) => void ) => FormActions
22+ onChange : ( callback ? : ( formValues : Record < string , string > ) => void ) => FormActions
1823 onSubmit : ( callback : ( formValues : Record < string , string > ) => void ) => FormActions
1924 onError : ( callback : ( invalidFields : string [ ] ) => void ) => FormActions
2025}
@@ -43,8 +48,23 @@ export const useForm = (selector: string | HTMLFormElement | null | undefined):
4348 ] . join ( ' ' ) )
4449 }
4550
51+ const controller = new AbortController ( )
52+ const { signal } = controller
53+
54+ const getSubmitButton = ( ) : HTMLButtonElement | null =>
55+ form . querySelector ( 'button[type="submit"], button:not([type])' )
56+
57+ const updateSubmitButton = ( actions : FormActions ) => {
58+ const button = getSubmitButton ( )
59+
60+ if ( button && button . hasAttribute ( 'data-form-managed' ) ) {
61+ button . disabled = ! actions . isValid ( )
62+ }
63+ }
64+
4665 return {
4766 validationRules : { } ,
67+ validationFactory : null ,
4868 isPreventDefault : false ,
4969 onErrorCallback : null ,
5070 preventDefault ( ) {
@@ -56,7 +76,9 @@ export const useForm = (selector: string | HTMLFormElement | null | undefined):
5676 return form . querySelector ( `[name=${ field } ]` )
5777 } ,
5878 getInputValue ( field ) {
59- return String ( new FormData ( form ) . get ( field ) )
79+ const value = new FormData ( form ) . get ( field )
80+
81+ return value !== null ? String ( value ) : null
6082 } ,
6183 getInputValues ( ) {
6284 const formData = new FormData ( form )
@@ -71,6 +93,13 @@ export const useForm = (selector: string | HTMLFormElement | null | undefined):
7193 update ( field , value ) {
7294 const input = form . querySelector ( `[name=${ field } ]` ) as HTMLInputElement
7395
96+ if ( ! input ) {
97+ // eslint-disable-next-line no-console
98+ console . warn ( `useForm: no field found with name "${ field } "` )
99+
100+ return this
101+ }
102+
74103 if ( typeof value === 'boolean' ) {
75104 input . checked = value
76105 }
@@ -84,6 +113,12 @@ export const useForm = (selector: string | HTMLFormElement | null | undefined):
84113 reset ( ) {
85114 form . reset ( )
86115
116+ if ( this . validationFactory ) {
117+ this . validationRules = this . validationFactory ( this . getInputValues ( ) )
118+ }
119+
120+ updateSubmitButton ( this )
121+
87122 return this
88123 } ,
89124 clear ( ) {
@@ -101,15 +136,35 @@ export const useForm = (selector: string | HTMLFormElement | null | undefined):
101136 }
102137 } )
103138
139+ if ( this . validationFactory ) {
140+ this . validationRules = this . validationFactory ( this . getInputValues ( ) )
141+ }
142+
143+ updateSubmitButton ( this )
144+
104145 return this
105146 } ,
106- setValidations ( validationRules ) {
107- this . validationRules = validationRules
147+ validate ( validationFactory : ValidationFactory ) {
148+ const submitButton = getSubmitButton ( )
149+
150+ if ( submitButton && ! submitButton . hasAttribute ( 'disabled' ) ) {
151+ submitButton . setAttribute ( 'data-form-managed' , '' )
152+ }
153+
154+ this . validationFactory = validationFactory
155+ this . validationRules = validationFactory ( this . getInputValues ( ) )
156+
157+ updateSubmitButton ( this )
108158
109159 return this
110160 } ,
111- isValidForm ( ) {
112- return Object . values ( this . validationRules ) . every ( key => key )
161+ isValid ( ) {
162+ if ( ! this . validationFactory ) {
163+ return true
164+ }
165+
166+ return Object . keys ( this . validationRules ) . length > 0
167+ && Object . values ( this . validationRules ) . every ( key => key )
113168 } ,
114169 useSmartFill ( { patterns, inputs } = { } ) {
115170 const textPatterns = {
@@ -214,14 +269,21 @@ export const useForm = (selector: string | HTMLFormElement | null | undefined):
214269 if ( autoFilled ) {
215270 event . preventDefault ( )
216271 }
217- } )
272+ } , { signal } )
218273
219274 return this
220275 } ,
221- onChange ( callback ) {
276+ onChange ( callback ? ) {
222277 form . addEventListener ( 'input' , ( ) => {
223- callback ?.( this . getInputValues ( ) )
224- } )
278+ const formValues = this . getInputValues ( )
279+
280+ if ( this . validationFactory ) {
281+ this . validationRules = this . validationFactory ( formValues )
282+ }
283+
284+ updateSubmitButton ( this )
285+ callback ?.( formValues )
286+ } , { signal } )
225287
226288 return this
227289 } ,
@@ -231,25 +293,30 @@ export const useForm = (selector: string | HTMLFormElement | null | undefined):
231293 event . preventDefault ( )
232294 }
233295
234- if ( this . isValidForm ( ) ) {
296+ const valid = this . isValid ( )
297+
298+ if ( valid ) {
235299 callback ?.( this . getInputValues ( ) )
236300 }
237301
238- if ( ! this . isValidForm ( ) && this . onErrorCallback ) {
302+ if ( ! valid && this . onErrorCallback ) {
239303 const invalidFields = Object
240304 . keys ( this . validationRules )
241305 . filter ( key => ! this . validationRules [ key ] )
242306
243307 this . onErrorCallback ( invalidFields )
244308 }
245- } )
309+ } , { signal } )
246310
247311 return this
248312 } ,
249313 onError ( callback ) {
250314 this . onErrorCallback = callback
251315
252316 return this
317+ } ,
318+ destroy ( ) {
319+ controller . abort ( )
253320 }
254321 }
255322}
0 commit comments