@@ -13,12 +13,15 @@ type FormState = {
1313type FormConfigInput < Data = any > = {
1414 initialValues : Data ,
1515 validationSchema ?: ObjectSchema < any > ,
16- validationClasses ?: {
17- valid ?: string ,
18- invalid ?: string ,
19- showValid ?: boolean ,
20- showInvalid ?: boolean ,
21- } ,
16+ css ?: {
17+ enabled ?: boolean ,
18+ validClass ?: string ,
19+ invalidClass ?: string ,
20+ useValid ?: boolean ,
21+ useInvalid ?: boolean ,
22+ }
23+ validateOnChange ?: boolean ,
24+ validateOnBlur ?: boolean ,
2225} ;
2326
2427function isNullish ( value ) {
@@ -220,17 +223,23 @@ const getFieldState = (name: string, $state: FormState) => {
220223 return obj [ lastSegment ] || null ;
221224}
222225
223- export function createForm < Data > ( { initialValues, validationSchema, validationClasses } : FormConfigInput < Data > ) {
226+ export function createForm < Data > ( { initialValues, validationSchema, css : cssConfig , validateOnChange , validateOnBlur } : FormConfigInput < Data > ) {
224227 const form = writable < Data > ( initialValues ) ;
225228 const state = writable < FormState > ( { } ) ;
226229 const isValid = writable < boolean > ( false ) ;
227230 const isTouched = writable < boolean > ( false ) ;
228- const classes = Object . assign ( {
229- valid : 'is-valid' ,
230- invalid : 'is-invalid' ,
231- showValid : true ,
232- showInvalid : true ,
233- } , validationClasses || { } ) ;
231+ const css = {
232+ ...{
233+ enabled : true ,
234+ validClass : 'is-valid' ,
235+ invalidClass : 'is-invalid' ,
236+ useValid : true ,
237+ useInvalid : true ,
238+ } , ...( cssConfig || { } )
239+ } ;
240+ validateOnChange = typeof validateOnChange !== 'boolean' ? true : validateOnChange ;
241+ validateOnBlur = typeof validateOnBlur !== 'boolean' ? true : validateOnBlur ;
242+ const cssValidator = writable < number > ( 0 ) ;
234243
235244 createState ( initialValues , state , validationSchema ) ;
236245
@@ -241,6 +250,7 @@ export function createForm<Data>({ initialValues, validationSchema, validationCl
241250
242251 const validateForm = ( highlight : 'none' | 'errors' | 'all' = 'none' ) => {
243252 isValid . set ( validate ( form , validationSchema , state , highlight === 'all' , highlight === 'all' || highlight === 'errors' ) ) ;
253+ css . enabled && ! validateOnChange && highlight !== 'none' && cssValidator . update ( val => val + 1 ) ;
244254 }
245255
246256 form . subscribe ( ( ) => {
@@ -258,41 +268,56 @@ export function createForm<Data>({ initialValues, validationSchema, validationCl
258268 }
259269
260270 const formControl = ( node : HTMLInputElement , options : any = { } ) => {
261- const changeListender = ( event : Event ) => {
262- handleChange ( event ) ;
271+ const changeListener = ( event : Event ) => {
272+ if ( validateOnChange ) {
273+ handleChange ( event ) ;
274+ css . enabled && checkValidity ( get ( state ) ) ;
275+ }
276+ }
277+ const blurListener = ( event : Event ) => {
278+ if ( validateOnBlur ) {
279+ handleChange ( event ) ;
280+ css . enabled && checkValidity ( get ( state ) ) ;
281+ }
263282 }
264283 let unsubscribeState : Unsubscriber = null ;
284+ let unsubscribeCssValidator : Unsubscriber = null ;
265285
266286 const checkValidity = async ( $state : FormState ) => {
267- const fieldState = getFieldState ( node . name , $state ) ;
268- const invalid = fieldState ?. _touched && ! ! fieldState ?. _errors ?. length ;
269- const valid = fieldState ?. _touched && ! fieldState ?. _errors ?. length ;
270- node . classList . remove ( classes . valid ) ;
271- node . classList . remove ( classes . invalid ) ;
272- if ( invalid ) {
273- classes . showInvalid && node . classList . add ( classes . invalid ) ;
274- } else if ( valid ) {
275- classes . showValid && node . classList . add ( classes . valid ) ;
287+ if ( node . name ) {
288+ const fieldState = getFieldState ( node . name , $state ) ;
289+ const invalid = fieldState ?. _touched && ! ! fieldState ?. _errors ?. length ;
290+ const valid = fieldState ?. _touched && ! fieldState ?. _errors ?. length ;
291+ node . classList . remove ( css . validClass ) ;
292+ node . classList . remove ( css . invalidClass ) ;
293+ if ( invalid ) {
294+ css . useInvalid && node . classList . add ( css . invalidClass ) ;
295+ } else if ( valid ) {
296+ css . useValid && node . classList . add ( css . validClass ) ;
297+ }
276298 }
277-
278299 }
279300
280301 if ( [ 'input' , 'checkbox' , 'radio' , 'select' , 'textarea' ] . includes ( node . tagName )
281302 || node . contentEditable ) {
282- node . addEventListener ( 'change' , changeListender ) ;
283- node . addEventListener ( 'blur' , changeListender ) ;
303+ node . addEventListener ( 'change' , changeListener ) ;
304+ node . addEventListener ( 'blur' , blurListener ) ;
305+
284306 if ( node . name ) {
285307 unsubscribeState = state . subscribe ( $state => {
286- checkValidity ( $state ) ;
308+ css . enabled && validateOnChange && checkValidity ( $state ) ;
309+ } ) ;
310+ unsubscribeCssValidator = cssValidator . subscribe ( ( ) => {
311+ checkValidity ( get ( state ) ) ;
287312 } ) ;
288313 }
289314 }
290315 return {
291- update ( options : any = { } ) { } ,
292316 destroy ( ) {
293- node . removeEventListener ( 'change' , changeListender ) ;
294- node . removeEventListener ( 'blur' , changeListender ) ;
317+ node . removeEventListener ( 'change' , changeListener ) ;
318+ node . removeEventListener ( 'blur' , blurListener ) ;
295319 unsubscribeState && unsubscribeState ( ) ;
320+ unsubscribeCssValidator && unsubscribeCssValidator ( ) ;
296321 }
297322 }
298323 }
0 commit comments