auth/wyatt_arch_migration #25
| @ -16,16 +16,16 @@ | ||||
|  * along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||
| --> | ||||
| 
 | ||||
| # Dart - Form BLoC | ||||
| # Flutter - Form BLoC | ||||
| 
 | ||||
| <p align="left"> | ||||
|   <a href="https://git.wyatt-studio.fr/Wyatt-FOSS/wyatt-packages/src/branch/master/packages/wyatt_analysis"> | ||||
|     <img src="https://img.shields.io/badge/Style-Wyatt%20Analysis-blue.svg?style=flat-square" alt="Style: Wyatt Analysis" /> | ||||
|   </a> | ||||
|   <img src="https://img.shields.io/badge/SDK-Dart%20%7C%20Flutter-blue?style=flat-square" alt="SDK: Dart & Flutter" /> | ||||
|   <img src="https://img.shields.io/badge/SDK-Flutter-blue?style=flat-square" alt="SDK: Flutter" /> | ||||
| </p> | ||||
| 
 | ||||
| Form Bloc for Dart & Flutter. | ||||
| Form Bloc for Flutter. | ||||
| 
 | ||||
| ## Features | ||||
| 
 | ||||
|  | ||||
| @ -1,4 +1,4 @@ | ||||
| include: package:wyatt_analysis/analysis_options.yaml | ||||
| include: package:wyatt_analysis/analysis_options.flutter.yaml | ||||
| 
 | ||||
| analyzer: | ||||
|   exclude: "!example/**" | ||||
| @ -21,6 +21,6 @@ | ||||
|   <key>CFBundleVersion</key> | ||||
|   <string>1.0</string> | ||||
|   <key>MinimumOSVersion</key> | ||||
|   <string>9.0</string> | ||||
|   <string>11.0</string> | ||||
| </dict> | ||||
| </plist> | ||||
|  | ||||
| @ -272,7 +272,7 @@ | ||||
| 				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; | ||||
| 				GCC_WARN_UNUSED_FUNCTION = YES; | ||||
| 				GCC_WARN_UNUSED_VARIABLE = YES; | ||||
| 				IPHONEOS_DEPLOYMENT_TARGET = 9.0; | ||||
| 				IPHONEOS_DEPLOYMENT_TARGET = 11.0; | ||||
| 				MTL_ENABLE_DEBUG_INFO = NO; | ||||
| 				SDKROOT = iphoneos; | ||||
| 				SUPPORTED_PLATFORMS = iphoneos; | ||||
| @ -349,7 +349,7 @@ | ||||
| 				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; | ||||
| 				GCC_WARN_UNUSED_FUNCTION = YES; | ||||
| 				GCC_WARN_UNUSED_VARIABLE = YES; | ||||
| 				IPHONEOS_DEPLOYMENT_TARGET = 9.0; | ||||
| 				IPHONEOS_DEPLOYMENT_TARGET = 11.0; | ||||
| 				MTL_ENABLE_DEBUG_INFO = YES; | ||||
| 				ONLY_ACTIVE_ARCH = YES; | ||||
| 				SDKROOT = iphoneos; | ||||
| @ -398,7 +398,7 @@ | ||||
| 				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; | ||||
| 				GCC_WARN_UNUSED_FUNCTION = YES; | ||||
| 				GCC_WARN_UNUSED_VARIABLE = YES; | ||||
| 				IPHONEOS_DEPLOYMENT_TARGET = 9.0; | ||||
| 				IPHONEOS_DEPLOYMENT_TARGET = 11.0; | ||||
| 				MTL_ENABLE_DEBUG_INFO = NO; | ||||
| 				SDKROOT = iphoneos; | ||||
| 				SUPPORTED_PLATFORMS = iphoneos; | ||||
|  | ||||
| @ -43,5 +43,7 @@ | ||||
| 	</array> | ||||
| 	<key>UIViewControllerBasedStatusBarAppearance</key> | ||||
| 	<false/> | ||||
| 	<key>CADisableMinimumFrameDurationOnPhone</key> | ||||
| 	<true/> | ||||
| </dict> | ||||
| </plist> | ||||
|  | ||||
| @ -18,7 +18,7 @@ import 'package:flutter/material.dart'; | ||||
| import 'package:flutter_bloc/flutter_bloc.dart'; | ||||
| import 'package:form_bloc_example/app/metadata.dart'; | ||||
| import 'package:form_bloc_example/constants.dart'; | ||||
| import 'package:form_bloc_example/cubit/custom_form_cubit.dart'; | ||||
| import 'package:form_bloc_example/simple_custom_form_cubit/simple_custom_form_cubit.dart'; | ||||
| import 'package:form_bloc_example/sign_up/sign_up_page.dart'; | ||||
| import 'package:wyatt_form_bloc/wyatt_form_bloc.dart'; | ||||
| 
 | ||||
| @ -29,41 +29,43 @@ class App extends StatelessWidget { | ||||
|   const App({Key? key}) : super(key: key); | ||||
| 
 | ||||
|   static List<FormInput> getNormalEntries() { | ||||
|     return const [ | ||||
|       FormInput(formFieldName, Name.pure(), metadata: FormInputMetadata<Metadata>(extra: blue)), | ||||
|       FormInput(formFieldEmail, Email.pure(), metadata: FormInputMetadata<Metadata>(extra: blue)), | ||||
|       FormInput(formFieldPhone, Phone.pure(), metadata: FormInputMetadata<Metadata>(extra: red)), | ||||
|       FormInput( | ||||
|           formFieldList, ListOption<String>.pure(defaultValue: ['checkbox3'])), | ||||
|       FormInput(formFieldRadio, TextString.pure()), | ||||
|       FormInput(formFieldPro, Boolean.pure(), | ||||
|           metadata: FormInputMetadata<Metadata>(name: 'business')), | ||||
|       FormInput(formFieldHidden, Boolean.pure(), | ||||
|           metadata: FormInputMetadata<Metadata>(export: false, extra: blue)), | ||||
|     return [ | ||||
|       FormInput(formFieldName, const Name.pure(), | ||||
|           metadata: const FormInputMetadata<Metadata>(extra: blue)), | ||||
|       FormInput(formFieldEmail, const Email.pure(), | ||||
|           metadata: const FormInputMetadata<Metadata>(extra: blue)), | ||||
|       FormInput(formFieldList, | ||||
|           const ListOption<String>.pure(defaultValue: 'c3')), | ||||
|       FormInput(formFieldRadio, const TextString.pure()), | ||||
|       FormInput(formFieldPro, const Boolean.pure(), | ||||
|           metadata: const FormInputMetadata<Metadata>(name: 'business')), | ||||
|       FormInput(formFieldHidden, const Boolean.pure(), | ||||
|           metadata: | ||||
|               const FormInputMetadata<Metadata>(export: false, extra: blue)), | ||||
|     ]; | ||||
|   } | ||||
| 
 | ||||
|   static List<FormInput> getBusinessEntries() { | ||||
|     const entries = [ | ||||
|       FormInput(formFieldSiren, Siren.pure()), | ||||
|       FormInput(formFieldIban, Iban.pure()), | ||||
|     final entries = [ | ||||
|       FormInput(formFieldPhone, const Phone.pure(), | ||||
|           metadata: const FormInputMetadata<Metadata>(extra: red)), | ||||
|     ]; | ||||
|     return getNormalEntries() + entries; | ||||
|   } | ||||
| 
 | ||||
|   static FormData getNormalFormData() { | ||||
|     return FormData(getNormalEntries()); | ||||
|   static WyattForm getNormalFormData() { | ||||
|     return WyattFormImpl(getNormalEntries()); | ||||
|   } | ||||
| 
 | ||||
|   static FormData getProFormData() { | ||||
|     return FormData(getBusinessEntries()); | ||||
|   static WyattForm getProFormData() { | ||||
|     return WyattFormImpl(getBusinessEntries()); | ||||
|   } | ||||
| 
 | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     FormDataCubit formCubit = CustomFormCubit(inputs: getNormalFormData()); | ||||
|     SimpleCustomFormCubit formCubit = SimpleCustomFormCubit(getNormalFormData()); | ||||
| 
 | ||||
|     return BlocProvider( | ||||
|     return BlocProvider<SimpleCustomFormCubit>( | ||||
|       create: (context) => formCubit, | ||||
|       child: const WidgetTree(), | ||||
|     ); | ||||
|  | ||||
| @ -28,7 +28,5 @@ class Metadata { | ||||
|   }); | ||||
| 
 | ||||
|   @override | ||||
|   String toString() { | ||||
|     return category.toString(); | ||||
|   } | ||||
|   String toString() => 'Metadata(category: $category)'; | ||||
| } | ||||
|  | ||||
| @ -17,8 +17,6 @@ | ||||
| const String formFieldName = 'name'; | ||||
| const String formFieldPhone = 'phone'; | ||||
| const String formFieldEmail = 'email'; | ||||
| const String formFieldSiren = 'siren'; | ||||
| const String formFieldIban = 'iban'; | ||||
| const String formFieldList = 'list'; | ||||
| const String formFieldRadio = 'radio'; | ||||
| const String formFieldHidden = 'hidden'; | ||||
|  | ||||
| @ -14,39 +14,33 @@ | ||||
| // You should have received a copy of the GNU General Public License | ||||
| // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||
| 
 | ||||
| import 'dart:developer'; | ||||
| 
 | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:flutter_bloc/flutter_bloc.dart'; | ||||
| import 'package:form_bloc_example/app/app.dart'; | ||||
| import 'package:form_bloc_example/app/metadata.dart'; | ||||
| import 'package:form_bloc_example/constants.dart'; | ||||
| import 'package:form_bloc_example/simple_custom_form_cubit/simple_custom_form_cubit.dart'; | ||||
| import 'package:wyatt_form_bloc/wyatt_form_bloc.dart'; | ||||
| 
 | ||||
| class CategoryIndicator extends StatelessWidget { | ||||
|   const CategoryIndicator( | ||||
|       {Key? key, required this.field, required this.builder}) | ||||
|       : super(key: key); | ||||
| 
 | ||||
|   final String field; | ||||
|   final Function(BuildContext context, FormDataState state) builder; | ||||
| 
 | ||||
|   Color computeColor(Metadata meta) { | ||||
|     switch (meta.category) { | ||||
| Color computeColor(Metadata? meta) { | ||||
|   switch (meta?.category) { | ||||
|     case Category.perso: | ||||
|       return Colors.blue; | ||||
|     case Category.business: | ||||
|       return Colors.red; | ||||
|     case null: | ||||
|       return Colors.green; | ||||
|   } | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| class _NameInput extends StatelessWidget { | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     return BlocBuilder<FormDataCubit, FormDataState>( | ||||
|       builder: ((context, state) { | ||||
|         final meta = state.data.metadataOf(field).extra as Metadata; | ||||
|     return InputBuilderMetadata<SimpleCustomFormCubit, Metadata>( | ||||
|       field: formFieldName, | ||||
|       builder: (context, cubit, state, field, inputValid, metadata) { | ||||
|         final meta = state.form.metadataOf<Metadata>(field).extra; | ||||
|         final color = computeColor(meta); | ||||
| 
 | ||||
|         return Row( | ||||
|           children: [ | ||||
|             Container( | ||||
| @ -57,57 +51,23 @@ class CategoryIndicator extends StatelessWidget { | ||||
|                 borderRadius: BorderRadius.circular(8), | ||||
|               ), | ||||
|             ), | ||||
|             const SizedBox(width: 20,), | ||||
|             const SizedBox( | ||||
|               width: 20, | ||||
|             ), | ||||
|             SizedBox( | ||||
|               width: MediaQuery.of(context).size.width - 45, | ||||
|               child: builder(context, state), | ||||
|             ) | ||||
|           ], | ||||
|         ); | ||||
|       }), | ||||
|     ); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| class _NameInput extends StatelessWidget { | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     return CategoryIndicator( | ||||
|         field: formFieldName, | ||||
|         builder: (context, state) { | ||||
|           return TextField( | ||||
|             onChanged: (name) => context | ||||
|                 .read<FormDataCubit>() | ||||
|                 .dataChanged(formFieldName, Name.dirty(name)), | ||||
|               child: TextField( | ||||
|                 onChanged: (value) => | ||||
|                     cubit.dataChanged(field, Name.dirty(value)), | ||||
|                 keyboardType: TextInputType.name, | ||||
|                 decoration: InputDecoration( | ||||
|                   labelText: 'name', | ||||
|                   helperText: '', | ||||
|               errorText: | ||||
|                   state.data.isNotValid(formFieldName) ? 'invalid name' : null, | ||||
|                   errorText: !inputValid ? 'invalid name' : null, | ||||
|                 ), | ||||
|           ); | ||||
|         }); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| class _EmailInput extends StatelessWidget { | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     return CategoryIndicator( | ||||
|         field: formFieldEmail, | ||||
|       builder: (context, state) { | ||||
|         return TextField( | ||||
|           onChanged: (email) => context | ||||
|               .read<FormDataCubit>() | ||||
|               .dataChanged(formFieldEmail, Email.dirty(email)), | ||||
|           keyboardType: TextInputType.emailAddress, | ||||
|           decoration: InputDecoration( | ||||
|             labelText: 'email', | ||||
|             helperText: '', | ||||
|             errorText: | ||||
|                 state.data.isNotValid(formFieldEmail) ? 'invalid email' : null, | ||||
|               ), | ||||
|             ) | ||||
|           ], | ||||
|         ); | ||||
|       }, | ||||
|     ); | ||||
| @ -117,79 +77,70 @@ class _EmailInput extends StatelessWidget { | ||||
| class _PhoneInput extends StatelessWidget { | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     return CategoryIndicator( | ||||
|     return InputBuilderMetadata<SimpleCustomFormCubit, Metadata>( | ||||
|       field: formFieldPhone, | ||||
|       builder: (context, state) { | ||||
|         return TextField( | ||||
|           onChanged: (phone) => context | ||||
|               .read<FormDataCubit>() | ||||
|               .dataChanged(formFieldPhone, Phone.dirty(phone)), | ||||
|       builder: (context, cubit, state, field, inputValid, metadata) { | ||||
|         final meta = state.form.metadataOf<Metadata>(field).extra; | ||||
|         final color = computeColor(meta); | ||||
|         return Row( | ||||
|           children: [ | ||||
|             Container( | ||||
|               height: 8, | ||||
|               width: 8, | ||||
|               decoration: BoxDecoration( | ||||
|                 color: color, | ||||
|                 borderRadius: BorderRadius.circular(8), | ||||
|               ), | ||||
|             ), | ||||
|             const SizedBox( | ||||
|               width: 20, | ||||
|             ), | ||||
|             SizedBox( | ||||
|               width: MediaQuery.of(context).size.width - 45, | ||||
|               child: TextField( | ||||
|                 onChanged: (value) => | ||||
|                     cubit.dataChanged(field, Phone.dirty(value)), | ||||
|                 keyboardType: TextInputType.phone, | ||||
|                 decoration: InputDecoration( | ||||
|                   labelText: 'phone', | ||||
|                   helperText: '', | ||||
|             errorText: | ||||
|                 state.data.isNotValid(formFieldPhone) ? 'invalid phone' : null, | ||||
|                   errorText: !inputValid ? 'invalid phone' : null, | ||||
|                 ), | ||||
|               ), | ||||
|             ) | ||||
|           ], | ||||
|         ); | ||||
|       }, | ||||
|     ); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| class _SirenInput extends StatelessWidget { | ||||
| class _EmailInput extends StatelessWidget { | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     return CategoryIndicator( | ||||
|         field: formFieldName, | ||||
|       builder: (context, state) { | ||||
|         return TextField( | ||||
|           onChanged: (siren) => context | ||||
|               .read<FormDataCubit>() | ||||
|               .dataChanged(formFieldSiren, Siren.dirty(siren)), | ||||
|           keyboardType: TextInputType.number, | ||||
|     return InputBuilder<SimpleCustomFormCubit>( | ||||
|       field: formFieldEmail, | ||||
|       builder: (context, cubit, state, field, inputValid) => TextField( | ||||
|         onChanged: (value) => cubit.dataChanged(field, Email.dirty(value)), | ||||
|         keyboardType: TextInputType.emailAddress, | ||||
|         decoration: InputDecoration( | ||||
|             labelText: 'siren', | ||||
|           labelText: 'email', | ||||
|           helperText: '', | ||||
|             errorText: | ||||
|                 state.data.isNotValid(formFieldSiren) ? 'invalid SIREN' : null, | ||||
|           errorText: !inputValid ? 'invalid email' : null, | ||||
|         ), | ||||
|         ); | ||||
|       }, | ||||
|     ); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| class _IbanInput extends StatelessWidget { | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     return BlocBuilder<FormDataCubit, FormDataState>( | ||||
|       builder: (context, state) { | ||||
|         return TextField( | ||||
|           onChanged: (iban) => context | ||||
|               .read<FormDataCubit>() | ||||
|               .dataChanged(formFieldIban, Iban.dirty(iban)), | ||||
|           keyboardType: TextInputType.text, | ||||
|           decoration: InputDecoration( | ||||
|             labelText: 'iban', | ||||
|             helperText: '', | ||||
|             errorText: | ||||
|                 state.data.isNotValid(formFieldIban) ? 'invalid IBAN' : null, | ||||
|       ), | ||||
|     ); | ||||
|       }, | ||||
|     ); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| class _CheckListInput extends StatelessWidget { | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     return BlocBuilder<FormDataCubit, FormDataState>( | ||||
|       builder: (context, state) { | ||||
|         final input = | ||||
|             state.data.validatorOf<ListOption<String>>(formFieldList); | ||||
|         final options = input.value; | ||||
|     return InputBuilder<SimpleCustomFormCubit>( | ||||
|       field: formFieldList, | ||||
|       builder: (context, cubit, state, field, inputValid) { | ||||
|         final input = state.form.validatorOf<ListOption<String>>(field); | ||||
|         final choices = ['c1', 'c2', 'c3']; | ||||
| 
 | ||||
|         return Column( | ||||
|           mainAxisSize: MainAxisSize.min, | ||||
| @ -197,33 +148,33 @@ class _CheckListInput extends StatelessWidget { | ||||
|             ListTile( | ||||
|               title: const Text('Checkbox1'), | ||||
|               trailing: Checkbox( | ||||
|                   value: options.contains('checkbox1'), | ||||
|                   value: input.value == choices[0], | ||||
|                   onChanged: (_) { | ||||
|                     context.read<FormDataCubit>().dataChanged( | ||||
|                           formFieldList, | ||||
|                           input.select('checkbox1'), | ||||
|                     cubit.dataChanged( | ||||
|                       field, | ||||
|                       ListOption.dirty(choices: choices, value: choices[0]), | ||||
|                     ); | ||||
|                   }), | ||||
|             ), | ||||
|             ListTile( | ||||
|               title: const Text('Checkbox2'), | ||||
|               trailing: Checkbox( | ||||
|                   value: options.contains('checkbox2'), | ||||
|                   value: input.value == choices[1], | ||||
|                   onChanged: (_) { | ||||
|                     context.read<FormDataCubit>().dataChanged( | ||||
|                           formFieldList, | ||||
|                           input.select('checkbox2'), | ||||
|                     cubit.dataChanged( | ||||
|                       field, | ||||
|                       ListOption.dirty(choices: choices, value: choices[1]), | ||||
|                     ); | ||||
|                   }), | ||||
|             ), | ||||
|             ListTile( | ||||
|               title: const Text('Checkbox3 (default)'), | ||||
|               trailing: Checkbox( | ||||
|                   value: options.contains('checkbox3'), | ||||
|                   value: input.value == choices[2], | ||||
|                   onChanged: (_) { | ||||
|                     context.read<FormDataCubit>().dataChanged( | ||||
|                           formFieldList, | ||||
|                           input.select('checkbox3'), | ||||
|                     cubit.dataChanged( | ||||
|                       field, | ||||
|                       ListOption.dirty(choices: choices, value: choices[2]), | ||||
|                     ); | ||||
|                   }), | ||||
|             ), | ||||
| @ -237,9 +188,10 @@ class _CheckListInput extends StatelessWidget { | ||||
| class _RadioListInput extends StatelessWidget { | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     return BlocBuilder<FormDataCubit, FormDataState>( | ||||
|       builder: (context, state) { | ||||
|         final input = state.data.validatorOf<TextString>(formFieldRadio); | ||||
|     return InputBuilder<SimpleCustomFormCubit>( | ||||
|       field: formFieldRadio, | ||||
|       builder: ((context, cubit, state, field, inputValid) { | ||||
|         final input = state.form.validatorOf<TextString>(field); | ||||
| 
 | ||||
|         return Column( | ||||
|           mainAxisSize: MainAxisSize.min, | ||||
| @ -250,8 +202,8 @@ class _RadioListInput extends StatelessWidget { | ||||
|                   groupValue: true, | ||||
|                   value: input.value == 'radio1', | ||||
|                   onChanged: (_) { | ||||
|                     context.read<FormDataCubit>().dataChanged( | ||||
|                           formFieldRadio, | ||||
|                     cubit.dataChanged( | ||||
|                       field, | ||||
|                       const TextString.dirty('radio1'), | ||||
|                     ); | ||||
|                   }), | ||||
| @ -262,8 +214,8 @@ class _RadioListInput extends StatelessWidget { | ||||
|                   groupValue: true, | ||||
|                   value: input.value == 'radio2', | ||||
|                   onChanged: (_) { | ||||
|                     context.read<FormDataCubit>().dataChanged( | ||||
|                           formFieldRadio, | ||||
|                     cubit.dataChanged( | ||||
|                       field, | ||||
|                       const TextString.dirty('radio2'), | ||||
|                     ); | ||||
|                   }), | ||||
| @ -274,15 +226,15 @@ class _RadioListInput extends StatelessWidget { | ||||
|                   groupValue: true, | ||||
|                   value: input.value == 'radio3', | ||||
|                   onChanged: (_) { | ||||
|                     context.read<FormDataCubit>().dataChanged( | ||||
|                           formFieldRadio, | ||||
|                     cubit.dataChanged( | ||||
|                       field, | ||||
|                       const TextString.dirty('radio3'), | ||||
|                     ); | ||||
|                   }), | ||||
|             ), | ||||
|           ], | ||||
|         ); | ||||
|       }, | ||||
|       }), | ||||
|     ); | ||||
|   } | ||||
| } | ||||
| @ -292,15 +244,16 @@ class _CheckHiddenInput extends StatelessWidget { | ||||
|   Widget build(BuildContext context) { | ||||
|     return ListTile( | ||||
|       title: const Text('This input is not exported'), | ||||
|       trailing: BlocBuilder<FormDataCubit, FormDataState>( | ||||
|         builder: (context, state) { | ||||
|       trailing: InputBuilder<SimpleCustomFormCubit>( | ||||
|         field: formFieldHidden, | ||||
|         builder: (context, cubit, state, field, inputValid) { | ||||
|           return Checkbox( | ||||
|               value: state.data.validatorOf<Boolean>(formFieldHidden).value, | ||||
|               value: state.form.validatorOf<Boolean>(field).value, | ||||
|               onChanged: (v) { | ||||
|                 final value = v!; // state is false, so value can't be null | ||||
| 
 | ||||
|                 context.read<FormDataCubit>().dataChanged( | ||||
|                       formFieldHidden, | ||||
|                 cubit.dataChanged( | ||||
|                   field, | ||||
|                   Boolean.dirty(value: value), | ||||
|                 ); | ||||
|               }); | ||||
| @ -315,25 +268,24 @@ class _CheckIsProInput extends StatelessWidget { | ||||
|   Widget build(BuildContext context) { | ||||
|     return ListTile( | ||||
|       title: const Text('Are you a pro?'), | ||||
|       trailing: BlocBuilder<FormDataCubit, FormDataState>( | ||||
|         builder: (context, state) { | ||||
|       trailing: InputBuilder<SimpleCustomFormCubit>( | ||||
|         field: formFieldPro, | ||||
|         builder: (context, cubit, state, field, inputValid) { | ||||
|           return Checkbox( | ||||
|               value: state.data.validatorOf<Boolean>(formFieldPro).value, | ||||
|               onChanged: (isPro) { | ||||
|                 final value = isPro!; // state is false, so value can't be null | ||||
|               value: state.form.validatorOf<Boolean>(field).value, | ||||
|               onChanged: (v) { | ||||
|                 final value = v!; // state is false, so value can't be null | ||||
| 
 | ||||
|                 context.read<FormDataCubit>().dataChanged( | ||||
|                       formFieldPro, | ||||
|                 cubit.dataChanged( | ||||
|                   field, | ||||
|                   Boolean.dirty(value: value), | ||||
|                 ); | ||||
| 
 | ||||
|                 if (value) { | ||||
|                   context.read<FormDataCubit>().updateFormData( | ||||
|                       App.getProFormData(), | ||||
|                   cubit.update(App.getProFormData(), | ||||
|                       operation: SetOperation.union); | ||||
|                 } else { | ||||
|                   context.read<FormDataCubit>().updateFormData( | ||||
|                       App.getNormalFormData(), | ||||
|                   cubit.update(App.getNormalFormData(), | ||||
|                       operation: SetOperation.intersection); | ||||
|                 } | ||||
|               }); | ||||
| @ -346,49 +298,28 @@ class _CheckIsProInput extends StatelessWidget { | ||||
| class _SignUpButton extends StatelessWidget { | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     return BlocBuilder<FormDataCubit, FormDataState>( | ||||
|       buildWhen: (previous, current) => previous.status != current.status, | ||||
|       builder: (context, state) { | ||||
|         return state.status.isSubmissionInProgress | ||||
|     return SubmitBuilder<SimpleCustomFormCubit>( | ||||
|         builder: (context, cubit, status) { | ||||
|       return status.isSubmissionInProgress | ||||
|           ? const CircularProgressIndicator() | ||||
|           : ElevatedButton( | ||||
|                 onPressed: state.status.isValidated | ||||
|                     ? () => context.read<FormDataCubit>().submitForm() | ||||
|                     : null, | ||||
|               onPressed: status.isValidated ? () => cubit.submit() : null, | ||||
|               child: const Text('SIGN UP'), | ||||
|             ); | ||||
|       }, | ||||
|     ); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| class _DebugButton extends StatelessWidget { | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     return BlocBuilder<FormDataCubit, FormDataState>( | ||||
|       builder: (context, state) { | ||||
|         return ElevatedButton( | ||||
|           onPressed: () { | ||||
|             log(state.toString()); | ||||
|           }, | ||||
|           child: const Text('DEBUG'), | ||||
|         ); | ||||
|       }, | ||||
|     ); | ||||
|     }); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| class _ResetButton extends StatelessWidget { | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     return BlocBuilder<FormDataCubit, FormDataState>( | ||||
|       builder: (context, state) { | ||||
|     return SubmitBuilder<SimpleCustomFormCubit>( | ||||
|         builder: (context, cubit, status) { | ||||
|       return ElevatedButton( | ||||
|           onPressed: () => context.read<FormDataCubit>().resetForm(), | ||||
|         onPressed: () => cubit.reset(), | ||||
|         child: const Text('RESET'), | ||||
|       ); | ||||
|       }, | ||||
|     ); | ||||
|     }); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| @ -397,7 +328,7 @@ class SignUpForm extends StatelessWidget { | ||||
| 
 | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     return BlocListener<FormDataCubit, FormDataState>( | ||||
|     return BlocListener<SimpleCustomFormCubit, FormDataState>( | ||||
|       listener: (context, state) { | ||||
|         if (state.status.isSubmissionSuccess) { | ||||
|           Navigator.of(context).pop(); // Never happens here | ||||
| @ -418,8 +349,6 @@ class SignUpForm extends StatelessWidget { | ||||
|             const SizedBox(height: 8), | ||||
|             _EmailInput(), | ||||
|             const SizedBox(height: 8), | ||||
|             _PhoneInput(), | ||||
|             const SizedBox(height: 8), | ||||
|             _CheckListInput(), | ||||
|             const SizedBox(height: 8), | ||||
|             _RadioListInput(), | ||||
| @ -428,13 +357,12 @@ class SignUpForm extends StatelessWidget { | ||||
|             const SizedBox(height: 8), | ||||
|             _CheckIsProInput(), | ||||
|             const SizedBox(height: 8), | ||||
|             BlocBuilder<FormDataCubit, FormDataState>( | ||||
|             BlocBuilder<SimpleCustomFormCubit, FormDataState>( | ||||
|               builder: (context, state) { | ||||
|                 if (state.data.validatorOf<Boolean>(formFieldPro).value) { | ||||
|                 if (state.form.validatorOf<Boolean>(formFieldPro).value ?? | ||||
|                     false) { | ||||
|                   return Column(children: [ | ||||
|                     _SirenInput(), | ||||
|                     const SizedBox(height: 8), | ||||
|                     _IbanInput(), | ||||
|                     _PhoneInput(), | ||||
|                     const SizedBox(height: 8), | ||||
|                   ]); | ||||
|                 } | ||||
| @ -444,8 +372,6 @@ class SignUpForm extends StatelessWidget { | ||||
|             const SizedBox(height: 8), | ||||
|             _SignUpButton(), | ||||
|             const SizedBox(height: 8), | ||||
|             _DebugButton(), | ||||
|             const SizedBox(height: 8), | ||||
|             _ResetButton(), | ||||
|           ], | ||||
|         ), | ||||
|  | ||||
| @ -18,12 +18,13 @@ import 'dart:developer'; | ||||
| 
 | ||||
| import 'package:wyatt_form_bloc/wyatt_form_bloc.dart'; | ||||
| 
 | ||||
| class CustomFormCubit extends FormDataCubit { | ||||
|   CustomFormCubit({required FormData inputs}) : super(inputs: inputs); | ||||
| class SimpleCustomFormCubit extends FormDataCubitImpl { | ||||
|   SimpleCustomFormCubit(super.form) : super(); | ||||
| 
 | ||||
|   @override | ||||
|   Future<void> submitForm() { | ||||
|     log(state.data.toMap().toString()); | ||||
|   Future<void> submit() { | ||||
|     final value = FormJsonEncoder().encode(state.form); | ||||
|     log(value); | ||||
| 
 | ||||
|     return Future.value(); | ||||
|   } | ||||
| @ -14,17 +14,4 @@ | ||||
| // You should have received a copy of the GNU General Public License | ||||
| // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||
| 
 | ||||
| export 'form/every_input_validator.dart'; | ||||
| export 'inputs/base/regex_validator.dart'; | ||||
| export 'inputs/base/text_validator.dart'; | ||||
| export 'inputs/boolean.dart'; | ||||
| export 'inputs/confirmed_password.dart'; | ||||
| export 'inputs/email.dart'; | ||||
| export 'inputs/enum_validator.dart'; | ||||
| export 'inputs/iban.dart'; | ||||
| export 'inputs/list_option.dart'; | ||||
| export 'inputs/name.dart'; | ||||
| export 'inputs/password.dart'; | ||||
| export 'inputs/phone.dart'; | ||||
| export 'inputs/siren.dart'; | ||||
| export 'inputs/text_string.dart'; | ||||
| export 'enums/enums.dart'; | ||||
| @ -0,0 +1,39 @@ | ||||
| // Copyright (C) 2022 WYATT GROUP | ||||
| // Please see the AUTHORS file for details. | ||||
| // | ||||
| // This program is free software: you can redistribute it and/or modify | ||||
| // it under the terms of the GNU General Public License as published by | ||||
| // the Free Software Foundation, either version 3 of the License, or | ||||
| // any later version. | ||||
| // | ||||
| // This program is distributed in the hope that it will be useful, | ||||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
| // GNU General Public License for more details. | ||||
| // | ||||
| // You should have received a copy of the GNU General Public License | ||||
| // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||
| 
 | ||||
| import 'package:wyatt_form_bloc/src/data/form_operations/form_difference.dart'; | ||||
| import 'package:wyatt_form_bloc/src/data/form_operations/form_intersection.dart'; | ||||
| import 'package:wyatt_form_bloc/src/data/form_operations/form_replace.dart'; | ||||
| import 'package:wyatt_form_bloc/src/data/form_operations/form_union.dart'; | ||||
| import 'package:wyatt_form_bloc/src/domain/form_operations/form_operation.dart'; | ||||
| 
 | ||||
| enum SetOperation { | ||||
|   /// Replace entire set with new set. | ||||
|   replace(FormReplace()), | ||||
| 
 | ||||
|   /// Keep common elements between sets. | ||||
|   intersection(FormIntersection()), | ||||
| 
 | ||||
|   /// Remove common elements between sets. | ||||
|   difference(FormDifference()), | ||||
| 
 | ||||
|   /// Add new elements to set. | ||||
|   union(FormUnion()); | ||||
| 
 | ||||
|   final FormOperation operation; | ||||
| 
 | ||||
|   const SetOperation(this.operation); | ||||
| } | ||||
| @ -23,5 +23,6 @@ abstract class ValidationError {} | ||||
| 
 | ||||
| enum ValidationStandardError implements ValidationError { | ||||
|   invalid, | ||||
|   empty | ||||
|   empty, | ||||
|   notEqual, | ||||
| } | ||||
| @ -1,110 +0,0 @@ | ||||
| // Copyright (C) 2022 WYATT GROUP | ||||
| // Please see the AUTHORS file for details. | ||||
| // | ||||
| // This program is free software: you can redistribute it and/or modify | ||||
| // it under the terms of the GNU General Public License as published by | ||||
| // the Free Software Foundation, either version 3 of the License, or | ||||
| // any later version. | ||||
| // | ||||
| // This program is distributed in the hope that it will be useful, | ||||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
| // GNU General Public License for more details. | ||||
| // | ||||
| // You should have received a copy of the GNU General Public License | ||||
| // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||
| 
 | ||||
| import 'dart:async'; | ||||
| 
 | ||||
| import 'package:bloc/bloc.dart'; | ||||
| import 'package:equatable/equatable.dart'; | ||||
| import 'package:wyatt_form_bloc/src/enums/enums.dart'; | ||||
| import 'package:wyatt_form_bloc/src/form/form.dart'; | ||||
| import 'package:wyatt_form_bloc/src/validators/form/every_input_validator.dart'; | ||||
| 
 | ||||
| part 'form_data_state.dart'; | ||||
| 
 | ||||
| abstract class FormDataCubit extends Cubit<FormDataState> { | ||||
|   FormValidator validationStrategy; | ||||
|   FormData formCopy; | ||||
| 
 | ||||
|   FormDataCubit({ | ||||
|     required FormData inputs, | ||||
|     FormValidator validator = const EveryInputValidator(), | ||||
|   })  : formCopy = inputs.clone(), | ||||
|         validationStrategy = validator, | ||||
|         super(FormDataState(data: inputs)); | ||||
| 
 | ||||
|   /// Change value of a field. | ||||
|   /// | ||||
|   /// Inputs: | ||||
|   /// - `field`: The key of the field to change. | ||||
|   /// - `dirtyValue`: The new value of the field. (Wrapped in a dirty validator) | ||||
|   void dataChanged<V>( | ||||
|     String field, | ||||
|     FormInputValidator<V, ValidationError> dirtyValue, | ||||
|   ) { | ||||
|     final form = state.data.clone(); | ||||
| 
 | ||||
|     if (form.contains(field)) { | ||||
|       form.updateValidator(field, dirtyValue); | ||||
|     } else { | ||||
|       throw Exception('Form field $field not found'); | ||||
|     } | ||||
| 
 | ||||
|     emit( | ||||
|       state.copyWith( | ||||
|         data: form, | ||||
|         status: validationStrategy.validate(form), | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   /// Just validate the form manually. (Useful if you just update the strategy) | ||||
|   void validate() { | ||||
|     final form = state.data; | ||||
|     emit(state.copyWith(status: validationStrategy.validate(form))); | ||||
|   } | ||||
| 
 | ||||
|   /// Update entries list. | ||||
|   /// | ||||
|   /// Inputs: | ||||
|   /// - `data`: The new entries list. | ||||
|   /// - `operation`: The operation to perform on the entries set. | ||||
|   void updateFormData( | ||||
|     FormData data, { | ||||
|     SetOperation operation = SetOperation.replace, | ||||
|   }) { | ||||
|     FormData form = data; | ||||
| 
 | ||||
|     switch (operation) { | ||||
|       case SetOperation.replace: | ||||
|         form = data; | ||||
|         break; | ||||
|       case SetOperation.difference: | ||||
|         form = state.data.difference(data); | ||||
|         break; | ||||
|       case SetOperation.intersection: | ||||
|         form = state.data.intersection(data); | ||||
|         break; | ||||
|       case SetOperation.union: | ||||
|         form = state.data.union(data); | ||||
|         break; | ||||
|     } | ||||
| 
 | ||||
|     emit( | ||||
|       state.copyWith( | ||||
|         data: form, | ||||
|         status: validationStrategy.validate(form), | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   /// Reset all form inputs | ||||
|   void resetForm() { | ||||
|     emit(FormDataState(data: formCopy)); | ||||
|   } | ||||
| 
 | ||||
|   /// Submit the form. | ||||
|   Future<void> submitForm(); | ||||
| } | ||||
							
								
								
									
										34
									
								
								packages/wyatt_form_bloc/lib/src/data/data.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								packages/wyatt_form_bloc/lib/src/data/data.dart
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,34 @@ | ||||
| // Copyright (C) 2022 WYATT GROUP | ||||
| // Please see the AUTHORS file for details. | ||||
| //  | ||||
| // This program is free software: you can redistribute it and/or modify | ||||
| // it under the terms of the GNU General Public License as published by | ||||
| // the Free Software Foundation, either version 3 of the License, or | ||||
| // any later version. | ||||
| //  | ||||
| // This program is distributed in the hope that it will be useful, | ||||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
| // GNU General Public License for more details. | ||||
| //  | ||||
| // You should have received a copy of the GNU General Public License | ||||
| // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||
| 
 | ||||
| export 'form/wyatt_form_impl.dart'; | ||||
| export 'form_encoders/form_json_encoder.dart'; | ||||
| export 'form_encoders/form_map_encoder.dart'; | ||||
| export 'form_operations/form_difference.dart'; | ||||
| export 'form_operations/form_intersection.dart'; | ||||
| export 'form_operations/form_replace.dart'; | ||||
| export 'form_operations/form_union.dart'; | ||||
| export 'form_validators/every_input_validator.dart'; | ||||
| export 'form_validators/only_required_input_validator.dart'; | ||||
| export 'input_validators/boolean.dart'; | ||||
| export 'input_validators/confirmed_password.dart'; | ||||
| export 'input_validators/email.dart'; | ||||
| export 'input_validators/enum_option.dart'; | ||||
| export 'input_validators/list_option.dart'; | ||||
| export 'input_validators/name.dart'; | ||||
| export 'input_validators/password.dart'; | ||||
| export 'input_validators/phone.dart'; | ||||
| export 'input_validators/text_string.dart'; | ||||
							
								
								
									
										136
									
								
								packages/wyatt_form_bloc/lib/src/data/form/wyatt_form_impl.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										136
									
								
								packages/wyatt_form_bloc/lib/src/data/form/wyatt_form_impl.dart
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,136 @@ | ||||
| // Copyright (C) 2022 WYATT GROUP | ||||
| // Please see the AUTHORS file for details. | ||||
| // | ||||
| // This program is free software: you can redistribute it and/or modify | ||||
| // it under the terms of the GNU General Public License as published by | ||||
| // the Free Software Foundation, either version 3 of the License, or | ||||
| // any later version. | ||||
| // | ||||
| // This program is distributed in the hope that it will be useful, | ||||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
| // GNU General Public License for more details. | ||||
| // | ||||
| // You should have received a copy of the GNU General Public License | ||||
| // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||
| 
 | ||||
| import 'package:wyatt_form_bloc/src/core/enums/form_status.dart'; | ||||
| import 'package:wyatt_form_bloc/src/core/enums/validation_error.dart'; | ||||
| import 'package:wyatt_form_bloc/src/data/form_validators/every_input_validator.dart'; | ||||
| import 'package:wyatt_form_bloc/src/domain/entities/form_input.dart'; | ||||
| import 'package:wyatt_form_bloc/src/domain/entities/form_input_metadata.dart'; | ||||
| import 'package:wyatt_form_bloc/src/domain/form/wyatt_form.dart'; | ||||
| import 'package:wyatt_form_bloc/src/domain/form_operations/form_operation.dart'; | ||||
| import 'package:wyatt_form_bloc/src/domain/form_validators/form_validator.dart'; | ||||
| import 'package:wyatt_form_bloc/src/domain/input_validators/form_input_validator.dart'; | ||||
| 
 | ||||
| // ignore: must_be_immutable | ||||
| class WyattFormImpl extends WyattForm { | ||||
|   final List< | ||||
|       FormInput<dynamic, FormInputValidator<dynamic, ValidationError>, | ||||
|           dynamic>> _inputs; | ||||
|   final FormValidator _validator; | ||||
| 
 | ||||
|   late List< | ||||
|       FormInput<dynamic, FormInputValidator<dynamic, ValidationError>, | ||||
|           dynamic>> _inputsInitial; | ||||
| 
 | ||||
|   WyattFormImpl( | ||||
|     this._inputs, { | ||||
|     FormValidator validationStrategy = const EveryInputValidator(), | ||||
|   }) : _validator = validationStrategy { | ||||
|     _inputsInitial = _inputs.map((input) => input.clone()).toList(); | ||||
|   } | ||||
| 
 | ||||
|   @override | ||||
|   List< | ||||
|       FormInput<dynamic, FormInputValidator<dynamic, ValidationError>, | ||||
|           dynamic>> get inputs => _inputs; | ||||
| 
 | ||||
|   @override | ||||
|   FormValidator get formValidationStrategy => _validator; | ||||
| 
 | ||||
|   @override | ||||
|   bool containsKey(String key) => inputs.any((input) => input.key == key); | ||||
| 
 | ||||
|   @override | ||||
|   List<FormInputValidator<Value, Error>> | ||||
|       asValidatorList<Value, Error extends ValidationError>() => inputs | ||||
|           .map<FormInputValidator<Value, Error>>( | ||||
|             (input) => input.validator as FormInputValidator<Value, Error>, | ||||
|           ) | ||||
|           .toList(); | ||||
| 
 | ||||
|   @override | ||||
|   FormInput<dynamic, FormInputValidator<dynamic, ValidationError>, dynamic> | ||||
|       inputOf(String key) { | ||||
|     if (containsKey(key)) { | ||||
|       return inputs.firstWhere((input) => input.key == key); | ||||
|     } else { | ||||
|       throw Exception('FormInput with key `$key` does not exist in form'); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   @override | ||||
|   Error? errorOf<Error extends ValidationError>(String key) => | ||||
|       (inputOf(key).validator as FormInputValidator<dynamic, Error>).error; | ||||
| 
 | ||||
|   @override | ||||
|   FormInputMetadata<Extra> metadataOf<Extra>(String key) => | ||||
|       inputOf(key).metadata as FormInputMetadata<Extra>; | ||||
| 
 | ||||
|   @override | ||||
|   Validator validatorOf< | ||||
|           Validator extends FormInputValidator<dynamic, ValidationError>>( | ||||
|     String key, | ||||
|   ) => | ||||
|       inputOf(key).validator as Validator; | ||||
| 
 | ||||
|   @override | ||||
|   Value? valueOf<Value>(String key) => | ||||
|       (inputOf(key).validator as FormInputValidator<Value, dynamic>).value; | ||||
| 
 | ||||
|   @override | ||||
|   FormStatus validate() => formValidationStrategy.validate(this); | ||||
| 
 | ||||
|   @override | ||||
|   WyattForm clone() => WyattFormImpl( | ||||
|         _inputs.map((input) => input.clone()).toList(), | ||||
|         validationStrategy: formValidationStrategy, | ||||
|       ); | ||||
| 
 | ||||
|   @override | ||||
|   WyattForm reset() => WyattFormImpl( | ||||
|         _inputsInitial, | ||||
|         validationStrategy: formValidationStrategy, | ||||
|       ); | ||||
| 
 | ||||
|   @override | ||||
|   bool? get stringify => true; | ||||
| 
 | ||||
|   @override | ||||
|   List<Object?> get props => _inputs; | ||||
| 
 | ||||
|   @override | ||||
|   void updateMetadata<Extra>(String key, FormInputMetadata<Extra> metadata) { | ||||
|     final index = _inputs.indexOf( | ||||
|       inputOf(key), | ||||
|     ); | ||||
|     _inputs[index] = _inputs[index].copyWith(metadata: metadata); | ||||
|   } | ||||
| 
 | ||||
|   @override | ||||
|   void updateValidator<Value>( | ||||
|     String key, | ||||
|     FormInputValidator<Value?, ValidationError> dirtyValue, | ||||
|   ) { | ||||
|     final index = _inputs.indexOf( | ||||
|       inputOf(key), | ||||
|     ); | ||||
|     _inputs[index] = _inputs[index].copyWith(validator: dirtyValue); | ||||
|   } | ||||
| 
 | ||||
|   @override | ||||
|   WyattForm operationWith(FormOperation operation, WyattForm other) => | ||||
|       operation.call(this, other); | ||||
| } | ||||
| @ -0,0 +1,30 @@ | ||||
| // Copyright (C) 2022 WYATT GROUP | ||||
| // Please see the AUTHORS file for details. | ||||
| // | ||||
| // This program is free software: you can redistribute it and/or modify | ||||
| // it under the terms of the GNU General Public License as published by | ||||
| // the Free Software Foundation, either version 3 of the License, or | ||||
| // any later version. | ||||
| // | ||||
| // This program is distributed in the hope that it will be useful, | ||||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
| // GNU General Public License for more details. | ||||
| // | ||||
| // You should have received a copy of the GNU General Public License | ||||
| // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||
| 
 | ||||
| import 'dart:convert'; | ||||
| 
 | ||||
| import 'package:wyatt_form_bloc/src/data/form_encoders/form_map_encoder.dart'; | ||||
| import 'package:wyatt_form_bloc/src/domain/form/wyatt_form.dart'; | ||||
| import 'package:wyatt_form_bloc/src/domain/form_encoders/form_encoder.dart'; | ||||
| 
 | ||||
| class FormJsonEncoder extends FormEncoder<String> { | ||||
|   @override | ||||
|   String encode(WyattForm form) { | ||||
|     final mapEncoder = FormMapEncoder(); | ||||
|     final map = mapEncoder.encode(form); | ||||
|     return jsonEncode(map); | ||||
|   } | ||||
| } | ||||
| @ -0,0 +1,32 @@ | ||||
| // Copyright (C) 2022 WYATT GROUP | ||||
| // Please see the AUTHORS file for details. | ||||
| // | ||||
| // This program is free software: you can redistribute it and/or modify | ||||
| // it under the terms of the GNU General Public License as published by | ||||
| // the Free Software Foundation, either version 3 of the License, or | ||||
| // any later version. | ||||
| // | ||||
| // This program is distributed in the hope that it will be useful, | ||||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
| // GNU General Public License for more details. | ||||
| // | ||||
| // You should have received a copy of the GNU General Public License | ||||
| // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||
| 
 | ||||
| import 'package:wyatt_form_bloc/src/domain/form/wyatt_form.dart'; | ||||
| import 'package:wyatt_form_bloc/src/domain/form_encoders/form_encoder.dart'; | ||||
| 
 | ||||
| class FormMapEncoder extends FormEncoder<Map<String, dynamic>> { | ||||
|   @override | ||||
|   Map<String, dynamic> encode(WyattForm form) { | ||||
|     final inputs = form.inputs; | ||||
|     final map = <String, dynamic>{}; | ||||
|     for (final input in inputs) { | ||||
|       if (input.metadata.export) { | ||||
|         map[input.name] = input.validator.value; | ||||
|       } | ||||
|     } | ||||
|     return map; | ||||
|   } | ||||
| } | ||||
| @ -0,0 +1,50 @@ | ||||
| // Copyright (C) 2022 WYATT GROUP | ||||
| // Please see the AUTHORS file for details. | ||||
| // | ||||
| // This program is free software: you can redistribute it and/or modify | ||||
| // it under the terms of the GNU General Public License as published by | ||||
| // the Free Software Foundation, either version 3 of the License, or | ||||
| // any later version. | ||||
| // | ||||
| // This program is distributed in the hope that it will be useful, | ||||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
| // GNU General Public License for more details. | ||||
| // | ||||
| // You should have received a copy of the GNU General Public License | ||||
| // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||
| 
 | ||||
| import 'package:wyatt_form_bloc/src/core/enums/validation_error.dart'; | ||||
| import 'package:wyatt_form_bloc/src/data/form/wyatt_form_impl.dart'; | ||||
| import 'package:wyatt_form_bloc/src/domain/entities/form_input.dart'; | ||||
| import 'package:wyatt_form_bloc/src/domain/form/wyatt_form.dart'; | ||||
| import 'package:wyatt_form_bloc/src/domain/form_operations/form_operation.dart'; | ||||
| import 'package:wyatt_form_bloc/src/domain/input_validators/form_input_validator.dart'; | ||||
| 
 | ||||
| class FormDifference extends FormOperation { | ||||
|   const FormDifference(); | ||||
| 
 | ||||
|   @override | ||||
|   WyattForm call(WyattForm a, WyattForm b) { | ||||
|     final inputs = < | ||||
|         FormInput<dynamic, FormInputValidator<dynamic, ValidationError>, | ||||
|             dynamic>>[]; | ||||
| 
 | ||||
|     for (final i in b.inputs) { | ||||
|       if (!a.containsKey(i.key)) { | ||||
|         inputs.add(i); | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     for (final i in a.inputs) { | ||||
|       if (!b.containsKey(i.key)) { | ||||
|         inputs.add(i); | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     return WyattFormImpl( | ||||
|       inputs, | ||||
|       validationStrategy: a.formValidationStrategy, | ||||
|     ); | ||||
|   } | ||||
| } | ||||
| @ -0,0 +1,44 @@ | ||||
| // Copyright (C) 2022 WYATT GROUP | ||||
| // Please see the AUTHORS file for details. | ||||
| // | ||||
| // This program is free software: you can redistribute it and/or modify | ||||
| // it under the terms of the GNU General Public License as published by | ||||
| // the Free Software Foundation, either version 3 of the License, or | ||||
| // any later version. | ||||
| // | ||||
| // This program is distributed in the hope that it will be useful, | ||||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
| // GNU General Public License for more details. | ||||
| // | ||||
| // You should have received a copy of the GNU General Public License | ||||
| // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||
| 
 | ||||
| import 'package:wyatt_form_bloc/src/core/enums/validation_error.dart'; | ||||
| import 'package:wyatt_form_bloc/src/data/form/wyatt_form_impl.dart'; | ||||
| import 'package:wyatt_form_bloc/src/domain/entities/form_input.dart'; | ||||
| import 'package:wyatt_form_bloc/src/domain/form/wyatt_form.dart'; | ||||
| import 'package:wyatt_form_bloc/src/domain/form_operations/form_operation.dart'; | ||||
| import 'package:wyatt_form_bloc/src/domain/input_validators/form_input_validator.dart'; | ||||
| 
 | ||||
| class FormIntersection extends FormOperation { | ||||
|   const FormIntersection(); | ||||
| 
 | ||||
|   @override | ||||
|   WyattForm call(WyattForm a, WyattForm b) { | ||||
|     final inputs = < | ||||
|         FormInput<dynamic, FormInputValidator<dynamic, ValidationError>, | ||||
|             dynamic>>[]; | ||||
| 
 | ||||
|     for (final i in a.inputs) { | ||||
|       if (b.containsKey(i.key)) { | ||||
|         inputs.add(i); | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     return WyattFormImpl( | ||||
|       inputs, | ||||
|       validationStrategy: a.formValidationStrategy, | ||||
|     ); | ||||
|   } | ||||
| } | ||||
| @ -14,15 +14,12 @@ | ||||
| // You should have received a copy of the GNU General Public License | ||||
| // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||
| 
 | ||||
| import 'dart:convert'; | ||||
| import 'package:wyatt_form_bloc/src/domain/form/wyatt_form.dart'; | ||||
| import 'package:wyatt_form_bloc/src/domain/form_operations/form_operation.dart'; | ||||
| 
 | ||||
| import 'package:equatable/equatable.dart'; | ||||
| import 'package:wyatt_form_bloc/src/enums/form_input_status.dart'; | ||||
| import 'package:wyatt_form_bloc/src/enums/form_status.dart'; | ||||
| import 'package:wyatt_form_bloc/src/enums/validation_error.dart'; | ||||
| class FormReplace extends FormOperation { | ||||
|   const FormReplace(); | ||||
| 
 | ||||
| part 'form_data.dart'; | ||||
| part 'form_input.dart'; | ||||
| part 'form_input_metadata.dart'; | ||||
| part 'form_input_validator.dart'; | ||||
| part 'form_validator.dart'; | ||||
|   @override | ||||
|   WyattForm call(WyattForm a, WyattForm b) => b; | ||||
| } | ||||
| @ -0,0 +1,48 @@ | ||||
| // Copyright (C) 2022 WYATT GROUP | ||||
| // Please see the AUTHORS file for details. | ||||
| // | ||||
| // This program is free software: you can redistribute it and/or modify | ||||
| // it under the terms of the GNU General Public License as published by | ||||
| // the Free Software Foundation, either version 3 of the License, or | ||||
| // any later version. | ||||
| // | ||||
| // This program is distributed in the hope that it will be useful, | ||||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
| // GNU General Public License for more details. | ||||
| // | ||||
| // You should have received a copy of the GNU General Public License | ||||
| // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||
| 
 | ||||
| import 'package:wyatt_form_bloc/src/core/enums/validation_error.dart'; | ||||
| import 'package:wyatt_form_bloc/src/data/form/wyatt_form_impl.dart'; | ||||
| import 'package:wyatt_form_bloc/src/domain/entities/form_input.dart'; | ||||
| import 'package:wyatt_form_bloc/src/domain/form/wyatt_form.dart'; | ||||
| import 'package:wyatt_form_bloc/src/domain/form_operations/form_operation.dart'; | ||||
| import 'package:wyatt_form_bloc/src/domain/input_validators/form_input_validator.dart'; | ||||
| 
 | ||||
| class FormUnion extends FormOperation { | ||||
|   const FormUnion(); | ||||
| 
 | ||||
|   @override | ||||
|   WyattForm call(WyattForm a, WyattForm b) { | ||||
|     final inputs = < | ||||
|         FormInput<dynamic, FormInputValidator<dynamic, ValidationError>, | ||||
|             dynamic>>[]; | ||||
| 
 | ||||
|     for (final i in a.inputs) { | ||||
|       inputs.add(i); | ||||
|     } | ||||
| 
 | ||||
|     for (final i in b.inputs) { | ||||
|       if (!a.containsKey(i.key)) { | ||||
|         inputs.add(i); | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     return WyattFormImpl( | ||||
|       inputs, | ||||
|       validationStrategy: a.formValidationStrategy, | ||||
|     ); | ||||
|   } | ||||
| } | ||||
| @ -14,7 +14,10 @@ | ||||
| // You should have received a copy of the GNU General Public License | ||||
| // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||
| 
 | ||||
| import 'package:wyatt_form_bloc/wyatt_form_bloc.dart'; | ||||
| import 'package:wyatt_form_bloc/src/core/enums/form_status.dart'; | ||||
| import 'package:wyatt_form_bloc/src/core/enums/validation_error.dart'; | ||||
| import 'package:wyatt_form_bloc/src/domain/form/wyatt_form.dart'; | ||||
| import 'package:wyatt_form_bloc/src/domain/form_validators/form_validator.dart'; | ||||
| 
 | ||||
| /// {@template every} | ||||
| /// Check and validate every input of a form | ||||
| @ -24,18 +27,13 @@ class EveryInputValidator extends FormValidator { | ||||
|   const EveryInputValidator() : super(); | ||||
| 
 | ||||
|   @override | ||||
|   FormStatus validate(FormData form) { | ||||
|   FormStatus validate(WyattForm form) { | ||||
|     if (isPure(form)) { | ||||
|       return FormStatus.pure; | ||||
|     } | ||||
| 
 | ||||
|     return rawValidate(form.validators<dynamic, ValidationError>()); | ||||
|   } | ||||
|     final validators = form.asValidatorList<dynamic, ValidationError>(); | ||||
| 
 | ||||
|   @override | ||||
|   FormStatus rawValidate( | ||||
|     List<FormInputValidator<dynamic, ValidationError>> validators, | ||||
|   ) { | ||||
|     if (validators.any((validator) => validator.valid == false)) { | ||||
|       return FormStatus.invalid; | ||||
|     } | ||||
| @ -0,0 +1,29 @@ | ||||
| // Copyright (C) 2022 WYATT GROUP | ||||
| // Please see the AUTHORS file for details. | ||||
| // | ||||
| // This program is free software: you can redistribute it and/or modify | ||||
| // it under the terms of the GNU General Public License as published by | ||||
| // the Free Software Foundation, either version 3 of the License, or | ||||
| // any later version. | ||||
| // | ||||
| // This program is distributed in the hope that it will be useful, | ||||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
| // GNU General Public License for more details. | ||||
| // | ||||
| // You should have received a copy of the GNU General Public License | ||||
| // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||
| 
 | ||||
| import 'package:wyatt_form_bloc/src/core/enums/form_status.dart'; | ||||
| import 'package:wyatt_form_bloc/src/domain/form/wyatt_form.dart'; | ||||
| import 'package:wyatt_form_bloc/src/domain/form_validators/form_validator.dart'; | ||||
| 
 | ||||
| class OnlyRequiredInputValidator extends FormValidator { | ||||
|   const OnlyRequiredInputValidator() : super(); | ||||
| 
 | ||||
|   @override | ||||
|   FormStatus validate(WyattForm form) { | ||||
|     // TODO(hpcl): use metadata to check if input is required or not. | ||||
|     return FormStatus.valid; | ||||
|   } | ||||
| } | ||||
| @ -14,13 +14,13 @@ | ||||
| // You should have received a copy of the GNU General Public License | ||||
| // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||
| 
 | ||||
| import 'package:wyatt_form_bloc/src/enums/enums.dart'; | ||||
| import 'package:wyatt_form_bloc/src/form/form.dart'; | ||||
| import 'package:wyatt_form_bloc/src/core/enums/validation_error.dart'; | ||||
| import 'package:wyatt_form_bloc/src/domain/input_validators/form_input_validator.dart'; | ||||
| 
 | ||||
| /// {@template boolean} | ||||
| /// Form input for a bool input | ||||
| /// {@endtemplate} | ||||
| class Boolean extends FormInputValidator<bool, ValidationStandardError> { | ||||
| class Boolean extends NullableValidator<bool, ValidationStandardError> { | ||||
|   /// {@macro boolean} | ||||
|   const Boolean.pure({bool? defaultValue = false}) | ||||
|       : super.pure(defaultValue ?? false); | ||||
| @ -28,7 +28,11 @@ class Boolean extends FormInputValidator<bool, ValidationStandardError> { | ||||
|   /// {@macro boolean} | ||||
|   const Boolean.dirty({bool value = false}) : super.dirty(value); | ||||
|    | ||||
|   @override | ||||
|   ValidationStandardError get onNull => ValidationStandardError.invalid; | ||||
| 
 | ||||
|   @override | ||||
|   ValidationStandardError? validator(bool? value) => | ||||
|       value != null ? null : ValidationStandardError.invalid; | ||||
|       value != null ? null : onNull; | ||||
| 
 | ||||
| } | ||||
| @ -14,25 +14,31 @@ | ||||
| // You should have received a copy of the GNU General Public License | ||||
| // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||
| 
 | ||||
| import 'package:wyatt_form_bloc/src/enums/enums.dart'; | ||||
| import 'package:wyatt_form_bloc/src/form/form.dart'; | ||||
| import 'package:wyatt_form_bloc/src/core/enums/validation_error.dart'; | ||||
| import 'package:wyatt_form_bloc/src/domain/input_validators/form_input_validator.dart'; | ||||
| 
 | ||||
| /// {@template confirmed_password} | ||||
| /// Form input for a confirmed password input. | ||||
| /// {@endtemplate} | ||||
| class ConfirmedPassword | ||||
|     extends FormInputValidator<String, ValidationStandardError> { | ||||
|     extends EqualityValidator<String, ValidationStandardError> { | ||||
|   /// {@macro confirmed_password} | ||||
|   const ConfirmedPassword.pure({this.password = ''}) : super.pure(''); | ||||
|   const ConfirmedPassword.pure({this.password = '', String? defaultValue}) | ||||
|       : super.pure(defaultValue); | ||||
| 
 | ||||
|   /// {@macro confirmed_password} | ||||
|   const ConfirmedPassword.dirty({required this.password, String value = ''}) | ||||
|   const ConfirmedPassword.dirty({required this.password, String? value}) | ||||
|       : super.dirty(value); | ||||
| 
 | ||||
|   /// The original password. | ||||
|   final String password; | ||||
| 
 | ||||
|   @override | ||||
|   ValidationStandardError? validator(String? value) => | ||||
|       password == value ? null : ValidationStandardError.invalid; | ||||
|   String get another => password; | ||||
| 
 | ||||
|   @override | ||||
|   ValidationStandardError get onNotEqual => ValidationStandardError.notEqual; | ||||
| 
 | ||||
|   @override | ||||
|   ValidationStandardError get onNull => ValidationStandardError.invalid; | ||||
| } | ||||
| @ -14,15 +14,15 @@ | ||||
| // You should have received a copy of the GNU General Public License | ||||
| // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||
| 
 | ||||
| import 'package:wyatt_form_bloc/src/enums/enums.dart'; | ||||
| import 'package:wyatt_form_bloc/src/validators/inputs/base/regex_validator.dart'; | ||||
| import 'package:wyatt_form_bloc/src/core/enums/validation_error.dart'; | ||||
| import 'package:wyatt_form_bloc/src/domain/input_validators/form_input_validator.dart'; | ||||
| 
 | ||||
| /// {@template email} | ||||
| /// Form input for an email input. | ||||
| /// {@endtemplate} | ||||
| class Email extends RegexValidator<ValidationStandardError> { | ||||
|   /// {@macro email} | ||||
|   const Email.pure() : super.pure(); | ||||
|   const Email.pure([super.value]) : super.pure(); | ||||
| 
 | ||||
|   /// {@macro email} | ||||
|   const Email.dirty([super.value = '']) : super.dirty(); | ||||
| @ -14,24 +14,27 @@ | ||||
| // You should have received a copy of the GNU General Public License | ||||
| // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||
| 
 | ||||
| import 'package:wyatt_form_bloc/src/enums/enums.dart'; | ||||
| import 'package:wyatt_form_bloc/src/validators/inputs/base/regex_validator.dart'; | ||||
| import 'package:wyatt_form_bloc/src/core/enums/validation_error.dart'; | ||||
| import 'package:wyatt_form_bloc/src/domain/input_validators/form_input_validator.dart'; | ||||
| 
 | ||||
| /// {@template siren} | ||||
| /// Form input for a SIREN input. | ||||
| /// {@endtemplate} | ||||
| class Siren extends RegexValidator<ValidationStandardError> { | ||||
|   /// {@macro siren} | ||||
|   const Siren.pure() : super.pure(); | ||||
| class EnumOption<T> extends AnyValidator<T, List<T>, ValidationError> { | ||||
|   const EnumOption.pure({this.enums = const [], T? defaultValue}) | ||||
|       : super.pure(defaultValue); | ||||
| 
 | ||||
|   /// {@macro siren} | ||||
|   const Siren.dirty([super.value = '']) : super.dirty(); | ||||
|   const EnumOption.dirty({required this.enums, T? value}) | ||||
|       : super.dirty(value); | ||||
| 
 | ||||
|   final List<T> enums; | ||||
| 
 | ||||
|   @override | ||||
|   ValidationStandardError get onEmpty => ValidationStandardError.empty; | ||||
|   @override | ||||
|   ValidationStandardError get onError => ValidationStandardError.invalid; | ||||
|   List<T> get allChoices => enums; | ||||
| 
 | ||||
|   @override | ||||
|   RegExp get regex => RegExp(r'(\d{9}|\d{3}[ ]\d{3}[ ]\d{3})$'); | ||||
|   ValidationError get onError => ValidationStandardError.invalid; | ||||
| 
 | ||||
|   @override | ||||
|   ValidationError get onNull => ValidationStandardError.invalid; | ||||
| 
 | ||||
|   @override | ||||
|   bool test(T? element, T value) => element == value; | ||||
| } | ||||
| @ -0,0 +1,40 @@ | ||||
| // Copyright (C) 2022 WYATT GROUP | ||||
| // Please see the AUTHORS file for details. | ||||
| // | ||||
| // This program is free software: you can redistribute it and/or modify | ||||
| // it under the terms of the GNU General Public License as published by | ||||
| // the Free Software Foundation, either version 3 of the License, or | ||||
| // any later version. | ||||
| // | ||||
| // This program is distributed in the hope that it will be useful, | ||||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
| // GNU General Public License for more details. | ||||
| // | ||||
| // You should have received a copy of the GNU General Public License | ||||
| // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||
| 
 | ||||
| import 'package:wyatt_form_bloc/src/core/enums/validation_error.dart'; | ||||
| import 'package:wyatt_form_bloc/src/domain/input_validators/form_input_validator.dart'; | ||||
| 
 | ||||
| class ListOption<T> extends AnyValidator<T, Iterable<T>, ValidationError> { | ||||
|   const ListOption.pure({this.choices = const [], T? defaultValue}) | ||||
|       : super.pure(defaultValue); | ||||
| 
 | ||||
|   const ListOption.dirty({required this.choices, T? value}) | ||||
|       : super.dirty(value); | ||||
| 
 | ||||
|   final Iterable<T> choices; | ||||
| 
 | ||||
|   @override | ||||
|   Iterable<T> get allChoices => choices; | ||||
| 
 | ||||
|   @override | ||||
|   ValidationError get onError => ValidationStandardError.invalid; | ||||
| 
 | ||||
|   @override | ||||
|   ValidationError get onNull => ValidationStandardError.invalid; | ||||
| 
 | ||||
|   @override | ||||
|   bool test(T? element, T value) => element == value; | ||||
| } | ||||
| @ -14,15 +14,15 @@ | ||||
| // You should have received a copy of the GNU General Public License | ||||
| // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||
| 
 | ||||
| import 'package:wyatt_form_bloc/src/enums/enums.dart'; | ||||
| import 'package:wyatt_form_bloc/src/validators/inputs/base/regex_validator.dart'; | ||||
| import 'package:wyatt_form_bloc/src/core/enums/validation_error.dart'; | ||||
| import 'package:wyatt_form_bloc/src/domain/input_validators/form_input_validator.dart'; | ||||
| 
 | ||||
| /// {@template name} | ||||
| /// Form input for a name input. | ||||
| /// {@endtemplate} | ||||
| class Name extends RegexValidator<ValidationStandardError> { | ||||
|   /// {@macro name} | ||||
|   const Name.pure() : super.pure(); | ||||
|   const Name.pure([super.value]) : super.pure(); | ||||
| 
 | ||||
|   /// {@macro name} | ||||
|   const Name.dirty([super.value = '']) : super.dirty(); | ||||
| @ -14,18 +14,18 @@ | ||||
| // You should have received a copy of the GNU General Public License | ||||
| // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||
| 
 | ||||
| import 'package:wyatt_form_bloc/src/enums/enums.dart'; | ||||
| import 'package:wyatt_form_bloc/src/validators/inputs/base/regex_validator.dart'; | ||||
| import 'package:wyatt_form_bloc/src/core/enums/validation_error.dart'; | ||||
| import 'package:wyatt_form_bloc/src/domain/input_validators/form_input_validator.dart'; | ||||
| 
 | ||||
| /// {@template password} | ||||
| /// Form input for a password input. | ||||
| /// {@endtemplate} | ||||
| class Password extends RegexValidator<ValidationStandardError> { | ||||
|   /// {@macro password} | ||||
|   const Password.pure() : super.pure(); | ||||
|   const Password.pure([super.value]) : super.pure(); | ||||
| 
 | ||||
|   /// {@macro password} | ||||
|   const Password.dirty([super.value = '']) : super.dirty(); | ||||
|   const Password.dirty([super.value]) : super.dirty(); | ||||
| 
 | ||||
|   @override | ||||
|   ValidationStandardError get onEmpty => ValidationStandardError.empty; | ||||
| @ -14,15 +14,15 @@ | ||||
| // You should have received a copy of the GNU General Public License | ||||
| // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||
| 
 | ||||
| import 'package:wyatt_form_bloc/src/enums/enums.dart'; | ||||
| import 'package:wyatt_form_bloc/src/validators/inputs/base/regex_validator.dart'; | ||||
| import 'package:wyatt_form_bloc/src/core/enums/validation_error.dart'; | ||||
| import 'package:wyatt_form_bloc/src/domain/input_validators/form_input_validator.dart'; | ||||
| 
 | ||||
| /// {@template phone} | ||||
| /// Form input for a phone input. | ||||
| /// {@endtemplate} | ||||
| class Phone extends RegexValidator<ValidationStandardError> { | ||||
|   /// {@macro phone} | ||||
|   const Phone.pure() : super.pure(); | ||||
|   const Phone.pure([super.value]) : super.pure(); | ||||
| 
 | ||||
|   /// {@macro phone} | ||||
|   const Phone.dirty([super.value = '']) : super.dirty(); | ||||
| @ -14,15 +14,15 @@ | ||||
| // You should have received a copy of the GNU General Public License | ||||
| // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||
| 
 | ||||
| import 'package:wyatt_form_bloc/src/enums/enums.dart'; | ||||
| import 'package:wyatt_form_bloc/src/validators/inputs/base/text_validator.dart'; | ||||
| import 'package:wyatt_form_bloc/src/core/enums/validation_error.dart'; | ||||
| import 'package:wyatt_form_bloc/src/domain/input_validators/form_input_validator.dart'; | ||||
| 
 | ||||
| /// {@template text_string} | ||||
| /// Form input for a text input | ||||
| /// {@endtemplate} | ||||
| class TextString extends TextValidator<ValidationStandardError> { | ||||
|   /// {@macro text_string} | ||||
|   const TextString.pure() : super.pure(); | ||||
|   const TextString.pure([super.value]) : super.pure(); | ||||
| 
 | ||||
|   /// {@macro text_string} | ||||
|   const TextString.dirty([super.value = '']) : super.dirty(); | ||||
							
								
								
									
										23
									
								
								packages/wyatt_form_bloc/lib/src/domain/domain.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								packages/wyatt_form_bloc/lib/src/domain/domain.dart
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,23 @@ | ||||
| // Copyright (C) 2022 WYATT GROUP | ||||
| // Please see the AUTHORS file for details. | ||||
| //  | ||||
| // This program is free software: you can redistribute it and/or modify | ||||
| // it under the terms of the GNU General Public License as published by | ||||
| // the Free Software Foundation, either version 3 of the License, or | ||||
| // any later version. | ||||
| //  | ||||
| // This program is distributed in the hope that it will be useful, | ||||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
| // GNU General Public License for more details. | ||||
| //  | ||||
| // You should have received a copy of the GNU General Public License | ||||
| // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||
| 
 | ||||
| export 'entities/form_input.dart'; | ||||
| export 'entities/form_input_metadata.dart'; | ||||
| export 'form/wyatt_form.dart'; | ||||
| export 'form_encoders/form_encoder.dart'; | ||||
| export 'form_operations/form_operation.dart'; | ||||
| export 'form_validators/form_validator.dart'; | ||||
| export 'input_validators/form_input_validator.dart'; | ||||
| @ -15,27 +15,36 @@ | ||||
| // You should have received a copy of the GNU General Public License | ||||
| // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||
| 
 | ||||
| part of 'form.dart'; | ||||
| import 'package:equatable/equatable.dart'; | ||||
| import 'package:wyatt_architecture/wyatt_architecture.dart'; | ||||
| import 'package:wyatt_form_bloc/src/core/enums/validation_error.dart'; | ||||
| import 'package:wyatt_form_bloc/src/domain/entities/form_input_metadata.dart'; | ||||
| import 'package:wyatt_form_bloc/src/domain/input_validators/form_input_validator.dart'; | ||||
| 
 | ||||
| class FormInput<Validator extends FormInputValidator<dynamic, ValidationError>> | ||||
|     extends Equatable { | ||||
| // ignore: must_be_immutable | ||||
| class FormInput< | ||||
|     Value, | ||||
|     Validator extends FormInputValidator<Value, ValidationError>, | ||||
|     Extra> extends Equatable implements Entity { | ||||
|   final String key; | ||||
|   final Validator validator; | ||||
|   final FormInputMetadata<dynamic> metadata; | ||||
|   late FormInputMetadata<Extra> metadata; | ||||
| 
 | ||||
|   String get name => metadata._name ?? key; | ||||
|   String get name => metadata.name ?? key; | ||||
| 
 | ||||
|   const FormInput( | ||||
|   FormInput( | ||||
|     this.key, | ||||
|     this.validator, { | ||||
|     // ignore: avoid_redundant_argument_values | ||||
|     this.metadata = const FormInputMetadata<void>(export: true), | ||||
|   }); | ||||
|     FormInputMetadata<Extra>? metadata, | ||||
|   }) { | ||||
|     this.metadata = metadata ?? FormInputMetadata<Extra>(); | ||||
|   } | ||||
| 
 | ||||
|   FormInput copyWith({ | ||||
|   FormInput<Value, Validator, Extra> copyWith({ | ||||
|     String? key, | ||||
|     Validator? validator, | ||||
|     FormInputMetadata? metadata, | ||||
|     FormInputMetadata<Extra>? metadata, | ||||
|   }) => | ||||
|       FormInput( | ||||
|         key ?? this.key, | ||||
| @ -43,11 +52,12 @@ class FormInput<Validator extends FormInputValidator<dynamic, ValidationError>> | ||||
|         metadata: metadata ?? this.metadata, | ||||
|       ); | ||||
| 
 | ||||
|   FormInput clone() => copyWith( | ||||
|   FormInput<Value, Validator, Extra> clone() => copyWith( | ||||
|         key: key, | ||||
|         validator: validator, | ||||
|         metadata: metadata, | ||||
|       ); | ||||
| 
 | ||||
|   @override | ||||
|   bool? get stringify => true; | ||||
| 
 | ||||
| @ -15,33 +15,35 @@ | ||||
| // You should have received a copy of the GNU General Public License | ||||
| // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||
| 
 | ||||
| part of 'form.dart'; | ||||
| import 'package:equatable/equatable.dart'; | ||||
| import 'package:wyatt_architecture/wyatt_architecture.dart'; | ||||
| 
 | ||||
| class FormInputMetadata<T extends Object?> extends Equatable { | ||||
| class FormInputMetadata<Extra> extends Equatable | ||||
|     implements Entity { | ||||
|   final bool export; | ||||
|   final String? _name; | ||||
|   final T? extra; | ||||
|   final String? name; | ||||
|   final Extra? extra; | ||||
| 
 | ||||
|   const FormInputMetadata({ | ||||
|     this.export = true, | ||||
|     this.name, | ||||
|     this.extra, | ||||
|     String? name, | ||||
|   }) : _name = name; | ||||
|   }); | ||||
| 
 | ||||
|   FormInputMetadata<T> copyWith({ | ||||
|   FormInputMetadata<Extra> copyWith({ | ||||
|     bool? export, | ||||
|     String? name, | ||||
|     T? extra, | ||||
|     Extra? extra, | ||||
|   }) => | ||||
|       FormInputMetadata<T>( | ||||
|       FormInputMetadata<Extra>( | ||||
|         export: export ?? this.export, | ||||
|         name: name ?? _name, | ||||
|         name: name ?? this.name, | ||||
|         extra: extra ?? this.extra, | ||||
|       ); | ||||
| 
 | ||||
|   FormInputMetadata<T> clone() => copyWith( | ||||
|   FormInputMetadata<Extra> clone() => copyWith( | ||||
|         export: export, | ||||
|         name: _name, | ||||
|         name: name, | ||||
|         extra: extra, | ||||
|       ); | ||||
| 
 | ||||
| @ -49,5 +51,5 @@ class FormInputMetadata<T extends Object?> extends Equatable { | ||||
|   bool? get stringify => true; | ||||
| 
 | ||||
|   @override | ||||
|   List<Object?> get props => [export, _name, extra]; | ||||
|   List<Object?> get props => [export, name, extra]; | ||||
| } | ||||
							
								
								
									
										60
									
								
								packages/wyatt_form_bloc/lib/src/domain/form/wyatt_form.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								packages/wyatt_form_bloc/lib/src/domain/form/wyatt_form.dart
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,60 @@ | ||||
| // Copyright (C) 2022 WYATT GROUP | ||||
| // Please see the AUTHORS file for details. | ||||
| // | ||||
| // This program is free software: you can redistribute it and/or modify | ||||
| // it under the terms of the GNU General Public License as published by | ||||
| // the Free Software Foundation, either version 3 of the License, or | ||||
| // any later version. | ||||
| // | ||||
| // This program is distributed in the hope that it will be useful, | ||||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
| // GNU General Public License for more details. | ||||
| // | ||||
| // You should have received a copy of the GNU General Public License | ||||
| // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||
| 
 | ||||
| // ignore_for_file: strict_raw_type | ||||
| 
 | ||||
| import 'package:equatable/equatable.dart'; | ||||
| import 'package:wyatt_form_bloc/src/core/enums/form_status.dart'; | ||||
| import 'package:wyatt_form_bloc/src/core/enums/validation_error.dart'; | ||||
| import 'package:wyatt_form_bloc/src/domain/entities/form_input.dart'; | ||||
| import 'package:wyatt_form_bloc/src/domain/entities/form_input_metadata.dart'; | ||||
| import 'package:wyatt_form_bloc/src/domain/form_operations/form_operation.dart'; | ||||
| import 'package:wyatt_form_bloc/src/domain/form_validators/form_validator.dart'; | ||||
| import 'package:wyatt_form_bloc/src/domain/input_validators/form_input_validator.dart'; | ||||
| 
 | ||||
| abstract class WyattForm extends Equatable { | ||||
|   List<FormInput> get inputs; | ||||
|   FormValidator get formValidationStrategy; | ||||
| 
 | ||||
|   bool containsKey(String key); | ||||
| 
 | ||||
|   FormInput inputOf(String key); | ||||
|   Validator validatorOf< | ||||
|       Validator extends FormInputValidator<dynamic, ValidationError>>( | ||||
|     String key, | ||||
|   ); | ||||
|   Error? errorOf<Error extends ValidationError>(String key); | ||||
|   Value? valueOf<Value>(String key); | ||||
|   FormInputMetadata<Extra> metadataOf<Extra>(String key); | ||||
| 
 | ||||
|   List<FormInputValidator<Value, Error>> | ||||
|       asValidatorList<Value, Error extends ValidationError>(); | ||||
| 
 | ||||
|   void updateValidator<Value>( | ||||
|     String key, | ||||
|     FormInputValidator<Value?, ValidationError> dirtyValue, | ||||
|   ); | ||||
| 
 | ||||
|   void updateMetadata<Extra>( | ||||
|     String key, | ||||
|     FormInputMetadata<Extra> metadata, | ||||
|   ); | ||||
| 
 | ||||
|   FormStatus validate(); | ||||
|   WyattForm clone(); | ||||
|   WyattForm operationWith(FormOperation operation, WyattForm other); | ||||
|   WyattForm reset(); | ||||
| } | ||||
| @ -14,16 +14,9 @@ | ||||
| // You should have received a copy of the GNU General Public License | ||||
| // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||
| 
 | ||||
| enum SetOperation {  | ||||
|   /// Replace entire set with new set. | ||||
|   replace,  | ||||
| import 'package:wyatt_form_bloc/src/domain/form/wyatt_form.dart'; | ||||
| 
 | ||||
|   /// Keep common elements between sets. | ||||
|   intersection,  | ||||
|    | ||||
|   /// Remove common elements between sets. | ||||
|   difference,  | ||||
|    | ||||
|   /// Add new elements to set. | ||||
|   union | ||||
| // ignore: one_member_abstracts | ||||
| abstract class FormEncoder<Output> { | ||||
|   Output encode(WyattForm form); | ||||
| } | ||||
| @ -0,0 +1,23 @@ | ||||
| // Copyright (C) 2022 WYATT GROUP | ||||
| // Please see the AUTHORS file for details. | ||||
| // | ||||
| // This program is free software: you can redistribute it and/or modify | ||||
| // it under the terms of the GNU General Public License as published by | ||||
| // the Free Software Foundation, either version 3 of the License, or | ||||
| // any later version. | ||||
| // | ||||
| // This program is distributed in the hope that it will be useful, | ||||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
| // GNU General Public License for more details. | ||||
| // | ||||
| // You should have received a copy of the GNU General Public License | ||||
| // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||
| 
 | ||||
| import 'package:wyatt_form_bloc/src/domain/form/wyatt_form.dart'; | ||||
| 
 | ||||
| // ignore: one_member_abstracts | ||||
| abstract class FormOperation { | ||||
|   const FormOperation(); | ||||
|   WyattForm call(WyattForm a, WyattForm b); | ||||
| } | ||||
| @ -14,7 +14,9 @@ | ||||
| // You should have received a copy of the GNU General Public License | ||||
| // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||
| 
 | ||||
| part of 'form.dart'; | ||||
| import 'package:wyatt_form_bloc/src/core/enums/form_status.dart'; | ||||
| import 'package:wyatt_form_bloc/src/core/enums/validation_error.dart'; | ||||
| import 'package:wyatt_form_bloc/src/domain/form/wyatt_form.dart'; | ||||
| 
 | ||||
| /// {@template form_validator} | ||||
| /// A [FormValidator] represents the global validaton state of a Form. | ||||
| @ -23,13 +25,9 @@ abstract class FormValidator { | ||||
|   /// {@macro form_validator} | ||||
|   const FormValidator(); | ||||
| 
 | ||||
|   bool isPure(FormData form) => form | ||||
|       .validators<dynamic, ValidationError>() | ||||
|   bool isPure(WyattForm form) => form | ||||
|       .asValidatorList<dynamic, ValidationError>() | ||||
|       .every((validator) => validator.pure); | ||||
| 
 | ||||
|   FormStatus validate(FormData form); | ||||
| 
 | ||||
|   FormStatus rawValidate( | ||||
|     List<FormInputValidator<dynamic, ValidationError>> validators, | ||||
|   ); | ||||
|   FormStatus validate(WyattForm form); | ||||
| } | ||||
| @ -0,0 +1,45 @@ | ||||
| // Copyright (C) 2022 WYATT GROUP | ||||
| // Please see the AUTHORS file for details. | ||||
| // | ||||
| // This program is free software: you can redistribute it and/or modify | ||||
| // it under the terms of the GNU General Public License as published by | ||||
| // the Free Software Foundation, either version 3 of the License, or | ||||
| // any later version. | ||||
| // | ||||
| // This program is distributed in the hope that it will be useful, | ||||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
| // GNU General Public License for more details. | ||||
| // | ||||
| // You should have received a copy of the GNU General Public License | ||||
| // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||
| 
 | ||||
| part of 'form_input_validator.dart'; | ||||
| 
 | ||||
| abstract class AnyValidator<O, I extends Iterable<O?>, | ||||
|     E extends ValidationError> extends FormInputValidator<O?, E> { | ||||
|   const AnyValidator.pure(super.value) : super.pure(); | ||||
|   const AnyValidator.dirty(super.value) : super.dirty(); | ||||
| 
 | ||||
|   I get allChoices; | ||||
| 
 | ||||
|   bool test(O? element, O value); | ||||
| 
 | ||||
|   E get onError; | ||||
| 
 | ||||
|   /// If value is null. | ||||
|   E get onNull; | ||||
| 
 | ||||
|   @override | ||||
|   E? validator(O? value) { | ||||
|     if (value == null) { | ||||
|       return onNull; | ||||
|     } | ||||
|     if (allChoices.any( | ||||
|       (element) => test(element, value), | ||||
|     )) { | ||||
|       return null; | ||||
|     } | ||||
|     return onError; | ||||
|   } | ||||
| } | ||||
| @ -0,0 +1,45 @@ | ||||
| // Copyright (C) 2022 WYATT GROUP | ||||
| // Please see the AUTHORS file for details. | ||||
| //  | ||||
| // This program is free software: you can redistribute it and/or modify | ||||
| // it under the terms of the GNU General Public License as published by | ||||
| // the Free Software Foundation, either version 3 of the License, or | ||||
| // any later version. | ||||
| //  | ||||
| // This program is distributed in the hope that it will be useful, | ||||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
| // GNU General Public License for more details. | ||||
| //  | ||||
| // You should have received a copy of the GNU General Public License | ||||
| // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||
| 
 | ||||
| part of 'form_input_validator.dart'; | ||||
| 
 | ||||
| /// {@template equality_validator} | ||||
| /// Abstract equality validator validating a value when equals to another. | ||||
| /// {@endtemplate} | ||||
| abstract class EqualityValidator<O extends Object, E extends ValidationError> | ||||
|     extends FormInputValidator<O?, E> { | ||||
|   const EqualityValidator.pure(super.value) : super.pure(); | ||||
|   const EqualityValidator.dirty(super.value) : super.dirty(); | ||||
| 
 | ||||
|   O get another; | ||||
| 
 | ||||
|   /// If values are different. | ||||
|   E get onNotEqual; | ||||
| 
 | ||||
|   /// If value is null. | ||||
|   E get onNull; | ||||
| 
 | ||||
|   @override | ||||
|   E? validator(O? value) { | ||||
|     if (value == null) { | ||||
|       return onNull; | ||||
|     } | ||||
|     if (value != another) { | ||||
|       return onNotEqual; | ||||
|     } | ||||
|     return null; | ||||
|   } | ||||
| } | ||||
| @ -14,7 +14,15 @@ | ||||
| // You should have received a copy of the GNU General Public License | ||||
| // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||
| 
 | ||||
| part of 'form.dart'; | ||||
| import 'package:equatable/equatable.dart'; | ||||
| import 'package:wyatt_form_bloc/src/core/enums/form_input_status.dart'; | ||||
| import 'package:wyatt_form_bloc/src/core/enums/validation_error.dart'; | ||||
| 
 | ||||
| part 'regex_validator.dart'; | ||||
| part 'text_validator.dart'; | ||||
| part 'nullable_validator.dart'; | ||||
| part 'equality_validator.dart'; | ||||
| part 'any_validator.dart'; | ||||
| 
 | ||||
| /// {@template form_input_validator} | ||||
| /// A [FormInputValidator] represents the value of a single form input field. | ||||
| @ -39,21 +47,21 @@ part of 'form.dart'; | ||||
| /// } | ||||
| /// ``` | ||||
| /// {@endtemplate} | ||||
| abstract class FormInputValidator<V, E extends ValidationError> | ||||
| abstract class FormInputValidator<Value, Error extends ValidationError> | ||||
|     extends Equatable { | ||||
|   const FormInputValidator._(this.value, [this.pure = true]); | ||||
| 
 | ||||
|   /// Constructor which create a `pure` [FormInputValidator] with a given value. | ||||
|   const FormInputValidator.pure(V value) : this._(value); | ||||
|   const FormInputValidator.pure(Value value) : this._(value); | ||||
| 
 | ||||
|   /// Constructor which create a `dirty` [FormInputValidator] with a | ||||
|   /// given value. | ||||
|   const FormInputValidator.dirty(V value) : this._(value, false); | ||||
|   const FormInputValidator.dirty(Value value) : this._(value, false); | ||||
| 
 | ||||
|   /// The value of the given [FormInputValidator]. | ||||
|   /// For example, if you have a `FormInputValidator` for `FirstName`, | ||||
|   /// the value could be 'Joe'. | ||||
|   final V value; | ||||
|   final Value? value; | ||||
| 
 | ||||
|   /// If the [FormInputValidator] is pure (has been touched/modified). | ||||
|   /// Typically when the `FormInputValidator` is initially created, | ||||
| @ -80,7 +88,7 @@ abstract class FormInputValidator<V, E extends ValidationError> | ||||
| 
 | ||||
|   /// Returns a validation error if the [FormInputValidator] is invalid. | ||||
|   /// Returns `null` if the [FormInputValidator] is valid. | ||||
|   E? get error => validator(value); | ||||
|   Error? get error => validator(value); | ||||
| 
 | ||||
|   /// Whether the [FormInputValidator] value is valid according to the | ||||
|   /// overridden `validator`. | ||||
| @ -96,7 +104,7 @@ abstract class FormInputValidator<V, E extends ValidationError> | ||||
| 
 | ||||
|   /// A function that must return a validation error if the provided | ||||
|   /// [value] is invalid and `null` otherwise. | ||||
|   E? validator(V value); | ||||
|   Error? validator(Value? value); | ||||
| 
 | ||||
|   @override | ||||
|   bool? get stringify => true; | ||||
| @ -14,19 +14,24 @@ | ||||
| // You should have received a copy of the GNU General Public License | ||||
| // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||
| 
 | ||||
| import 'package:wyatt_form_bloc/src/enums/validation_error.dart'; | ||||
| import 'package:wyatt_form_bloc/src/validators/inputs/base/text_validator.dart'; | ||||
| part of 'form_input_validator.dart'; | ||||
| 
 | ||||
| class EnumValidator<E> extends TextValidator<ValidationStandardError> { | ||||
|   /// {@macro text_string} | ||||
|   const EnumValidator.pure() : super.pure(); | ||||
| /// {@template nullable_validator} | ||||
| /// Abstract nullable validator not validating null values. | ||||
| /// {@endtemplate} | ||||
| abstract class NullableValidator<O extends Object, E extends ValidationError> | ||||
|     extends FormInputValidator<O?, E> { | ||||
|   const NullableValidator.pure(super.value) : super.pure(); | ||||
|   const NullableValidator.dirty(super.value) : super.dirty(); | ||||
| 
 | ||||
|   /// {@macro text_string} | ||||
|   EnumValidator.dirty(E value) : super.dirty(value.toString()); | ||||
|   /// If value is null. | ||||
|   E get onNull; | ||||
| 
 | ||||
|   @override | ||||
|   ValidationStandardError get onEmpty => ValidationStandardError.empty; | ||||
| 
 | ||||
|   @override | ||||
|   ValidationStandardError get onNull => ValidationStandardError.invalid; | ||||
|   E? validator(O? value) { | ||||
|     if (value == null) { | ||||
|       return onNull; | ||||
|     } | ||||
|     return null; | ||||
|   } | ||||
| } | ||||
| @ -14,20 +14,21 @@ | ||||
| // You should have received a copy of the GNU General Public License | ||||
| // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||
| 
 | ||||
| import 'package:wyatt_form_bloc/src/enums/validation_error.dart'; | ||||
| import 'package:wyatt_form_bloc/src/form/form.dart'; | ||||
| part of 'form_input_validator.dart'; | ||||
| 
 | ||||
| /// {@template regex_validator} | ||||
| /// Abstract regex validator for form input. | ||||
| /// {@endtemplate} | ||||
| abstract class RegexValidator<E extends ValidationError> | ||||
|     extends FormInputValidator<String, E> { | ||||
|   const RegexValidator.pure() : super.pure(''); | ||||
|   const RegexValidator.dirty([super.value = '']) : super.dirty(); | ||||
|     extends FormInputValidator<String?, E> { | ||||
|   const RegexValidator.pure(super.value) : super.pure(); | ||||
|   const RegexValidator.dirty(super.value) : super.dirty(); | ||||
| 
 | ||||
|   RegExp get regex; | ||||
| 
 | ||||
|   /// If the value is **not** null, but empty. | ||||
|   E get onEmpty; | ||||
| 
 | ||||
|   /// If value does not conform to regex. | ||||
|   E get onError; | ||||
| 
 | ||||
| @ -14,13 +14,12 @@ | ||||
| // You should have received a copy of the GNU General Public License | ||||
| // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||
| 
 | ||||
| import 'package:wyatt_form_bloc/src/enums/validation_error.dart'; | ||||
| import 'package:wyatt_form_bloc/src/form/form.dart'; | ||||
| part of 'form_input_validator.dart'; | ||||
| 
 | ||||
| abstract class TextValidator<E extends ValidationError> | ||||
|     extends FormInputValidator<String, E> { | ||||
|   const TextValidator.pure() : super.pure(''); | ||||
|   const TextValidator.dirty([super.value = '']) : super.dirty(); | ||||
|     extends FormInputValidator<String?, E> { | ||||
|   const TextValidator.pure(super.value) : super.pure(); | ||||
|   const TextValidator.dirty(super.value) : super.dirty(); | ||||
| 
 | ||||
|   /// If the value is **not** null, but empty. | ||||
|   E get onEmpty; | ||||
| @ -1,184 +0,0 @@ | ||||
| // Copyright (C) 2022 WYATT GROUP | ||||
| // Please see the AUTHORS file for details. | ||||
| // | ||||
| // This program is free software: you can redistribute it and/or modify | ||||
| // it under the terms of the GNU General Public License as published by | ||||
| // the Free Software Foundation, either version 3 of the License, or | ||||
| // any later version. | ||||
| // | ||||
| // This program is distributed in the hope that it will be useful, | ||||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
| // GNU General Public License for more details. | ||||
| // | ||||
| // You should have received a copy of the GNU General Public License | ||||
| // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||
| 
 | ||||
| part of 'form.dart'; | ||||
| 
 | ||||
| class FormData extends Equatable { | ||||
|   final List<FormInput> _inputs; | ||||
| 
 | ||||
|   const FormData(this._inputs); | ||||
|   const FormData.empty() : this(const <FormInput>[]); | ||||
| 
 | ||||
|   /// Returns all inputs as a list | ||||
|   List<FormInput> inputs() => _inputs; | ||||
| 
 | ||||
|   /// Returns the input for the associated key | ||||
|   FormInput inputOf(String key) { | ||||
|     if (contains(key)) { | ||||
|       return _inputs.firstWhere((input) => input.key == key); | ||||
|     } else { | ||||
|       throw Exception('FormInput with key `$key` does not exist in form'); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   /// Updates a input (perform a replace at index). | ||||
|   void updateInput(String key, FormInput input) { | ||||
|     if (contains(key)) { | ||||
|       final index = _inputs.indexOf( | ||||
|         inputOf(key), | ||||
|       ); | ||||
|       _inputs[index] = input; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   /// Returns all associated validators as a list | ||||
|   List<FormInputValidator<V, E>> validators<V, E extends ValidationError>() => | ||||
|       _inputs | ||||
|           .map<FormInputValidator<V, E>>( | ||||
|             (input) => input.validator as FormInputValidator<V, E>, | ||||
|           ) | ||||
|           .toList(); | ||||
| 
 | ||||
|   /// A [FormInputValidator] represents the value of a single form input field. | ||||
|   /// It contains information about the [FormInputStatus], value, as well | ||||
|   /// as validation status. | ||||
|   T validatorOf<T>(String key) => inputOf(key).validator as T; | ||||
| 
 | ||||
|   /// Updates validator of a given input. (perform copyWith) | ||||
|   void updateValidator<V>( | ||||
|     String key, | ||||
|     FormInputValidator<V, ValidationError> dirtyValue, | ||||
|   ) { | ||||
|     if (contains(key)) { | ||||
|       final index = _inputs.indexOf( | ||||
|         inputOf(key), | ||||
|       ); | ||||
|       _inputs[index] = _inputs[index].copyWith(validator: dirtyValue); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   /// Returns a validation error if the [FormInputValidator] is invalid. | ||||
|   /// Returns null if the [FormInputValidator] is valid. | ||||
|   E? errorOf<E extends ValidationError>(String key) => | ||||
|       (inputOf(key).validator as FormInputValidator<dynamic, E>).error; | ||||
| 
 | ||||
|   /// The value of the associated [FormInputValidator]. For example, | ||||
|   /// if you have a FormInputValidator for FirstName, the value could be 'Joe'. | ||||
|   V valueOf<V>(String key) => | ||||
|       (inputOf(key).validator as FormInputValidator<V, dynamic>).value; | ||||
| 
 | ||||
|   /// Returns `true` if the [FormInputValidator] is not valid. | ||||
|   /// Same as `E? errorOf<V, E>(String key) != null` | ||||
|   bool isNotValid(String key) => !inputOf(key).validator.valid; | ||||
| 
 | ||||
|   /// Returns all associated metadata as a list | ||||
|   List<FormInputMetadata<M>> metadata<M>() => _inputs | ||||
|       .map<FormInputMetadata<M>>( | ||||
|         (input) => input.metadata as FormInputMetadata<M>, | ||||
|       ) | ||||
|       .toList(); | ||||
| 
 | ||||
|   /// Returns the metadata associated. With `M` the type of extra data. | ||||
|   FormInputMetadata<M> metadataOf<M>(String key) => | ||||
|       inputOf(key).metadata as FormInputMetadata<M>; | ||||
| 
 | ||||
|   /// Updates metadata of a given input. (perform copyWith) | ||||
|   void updateMetadata<M>(String key, FormInputMetadata<M> meta) { | ||||
|     if (contains(key)) { | ||||
|       final index = _inputs.indexOf( | ||||
|         inputOf(key), | ||||
|       ); | ||||
|       _inputs[index] = _inputs[index].copyWith(metadata: meta); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   /// Check if this contains an input with the given key. | ||||
|   bool contains(String key) => _inputs.any((input) => input.key == key); | ||||
| 
 | ||||
|   /// Makes an intersection set operation and returns newly created [FormData] | ||||
|   FormData intersection(FormData other) { | ||||
|     final List<FormInput> inputs = <FormInput>[]; | ||||
| 
 | ||||
|     for (final FormInput i in _inputs) { | ||||
|       if (other.contains(i.key)) { | ||||
|         inputs.add(i); | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     return FormData(inputs); | ||||
|   } | ||||
| 
 | ||||
|   /// Makes a difference set operation and returns newly created [FormData] | ||||
|   FormData difference(FormData other) { | ||||
|     final List<FormInput> inputs = <FormInput>[]; | ||||
| 
 | ||||
|     for (final FormInput i in other._inputs) { | ||||
|       if (!contains(i.key)) { | ||||
|         inputs.add(i); | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     for (final FormInput i in _inputs) { | ||||
|       if (!other.contains(i.key)) { | ||||
|         inputs.add(i); | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     return FormData(inputs); | ||||
|   } | ||||
| 
 | ||||
|   /// Makes an union set operation and returns newly created [FormData] | ||||
|   FormData union(FormData other) { | ||||
|     final List<FormInput> inputs = <FormInput>[]; | ||||
| 
 | ||||
|     for (final FormInput i in _inputs) { | ||||
|       inputs.add(i); | ||||
|     } | ||||
| 
 | ||||
|     for (final FormInput i in other._inputs) { | ||||
|       if (!contains(i.key)) { | ||||
|         inputs.add(i); | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     return FormData(inputs); | ||||
|   } | ||||
| 
 | ||||
|   /// Deeply copy this. | ||||
|   FormData clone() => FormData( | ||||
|         _inputs.map((input) => input.clone()).toList(), | ||||
|       ); | ||||
| 
 | ||||
|   /// Export this to [Map] format. | ||||
|   Map<String, dynamic> toMap() { | ||||
|     final map = <String, dynamic>{}; | ||||
|     for (final input in _inputs) { | ||||
|       if (input.metadata.export) { | ||||
|         map[input.name] = input.validator.value; | ||||
|       } | ||||
|     } | ||||
|     return map; | ||||
|   } | ||||
| 
 | ||||
|   /// Export this to [String] format. | ||||
|   String toJson() => jsonEncode(toMap()); | ||||
| 
 | ||||
|   @override | ||||
|   bool? get stringify => true; | ||||
| 
 | ||||
|   @override | ||||
|   List<Object?> get props => _inputs; | ||||
| } | ||||
| @ -0,0 +1,47 @@ | ||||
| // Copyright (C) 2022 WYATT GROUP | ||||
| // Please see the AUTHORS file for details. | ||||
| // | ||||
| // This program is free software: you can redistribute it and/or modify | ||||
| // it under the terms of the GNU General Public License as published by | ||||
| // the Free Software Foundation, either version 3 of the License, or | ||||
| // any later version. | ||||
| // | ||||
| // This program is distributed in the hope that it will be useful, | ||||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
| // GNU General Public License for more details. | ||||
| // | ||||
| // You should have received a copy of the GNU General Public License | ||||
| // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||
| 
 | ||||
| import 'dart:async'; | ||||
| 
 | ||||
| import 'package:equatable/equatable.dart'; | ||||
| import 'package:flutter_bloc/flutter_bloc.dart'; | ||||
| import 'package:wyatt_form_bloc/src/core/enums/form_status.dart'; | ||||
| import 'package:wyatt_form_bloc/src/core/enums/set_operations.dart'; | ||||
| import 'package:wyatt_form_bloc/src/core/enums/validation_error.dart'; | ||||
| import 'package:wyatt_form_bloc/src/domain/form/wyatt_form.dart'; | ||||
| import 'package:wyatt_form_bloc/src/domain/input_validators/form_input_validator.dart'; | ||||
| 
 | ||||
| part 'form_data_state.dart'; | ||||
| 
 | ||||
| abstract class FormDataCubit<State extends FormDataState> extends Cubit<State> { | ||||
|   FormDataCubit(super.initialState) : super(); | ||||
| 
 | ||||
|   FutureOr<void> dataChanged<Value>( | ||||
|     String key, | ||||
|     FormInputValidator<Value, ValidationError> dirtyValue, | ||||
|   ); | ||||
| 
 | ||||
|   FutureOr<void> update( | ||||
|     WyattForm form, { | ||||
|     SetOperation operation = SetOperation.replace, | ||||
|   }); | ||||
| 
 | ||||
|   FutureOr<void> validate(); | ||||
| 
 | ||||
|   FutureOr<void> reset(); | ||||
| 
 | ||||
|   FutureOr<void> submit(); | ||||
| } | ||||
| @ -16,35 +16,19 @@ | ||||
| 
 | ||||
| part of 'form_data_cubit.dart'; | ||||
| 
 | ||||
| class FormDataState extends Equatable { | ||||
| abstract class FormDataState extends Equatable { | ||||
|   /// Global status of a form. | ||||
|   final FormStatus status; | ||||
| 
 | ||||
|   /// FormData with all inputs, and associated metadata. | ||||
|   final FormData data; | ||||
|   final WyattForm form; | ||||
| 
 | ||||
|   /// Optional error message. | ||||
|   final String? errorMessage; | ||||
| 
 | ||||
|   const FormDataState({ | ||||
|     required this.data, | ||||
|     required this.form, | ||||
|     this.status = FormStatus.pure, | ||||
|     this.errorMessage, | ||||
|   }); | ||||
| 
 | ||||
|   FormDataState copyWith({ | ||||
|     FormStatus? status, | ||||
|     FormData? data, | ||||
|     String? errorMessage, | ||||
|   }) => FormDataState( | ||||
|       status: status ?? this.status, | ||||
|       data: data ?? this.data, | ||||
|       errorMessage: errorMessage ?? this.errorMessage, | ||||
|     ); | ||||
| 
 | ||||
|   @override | ||||
|   bool? get stringify => true; | ||||
|    | ||||
|   @override | ||||
|   List<Object?> get props => [status, data, errorMessage]; | ||||
| } | ||||
| @ -0,0 +1,86 @@ | ||||
| // Copyright (C) 2022 WYATT GROUP | ||||
| // Please see the AUTHORS file for details. | ||||
| // | ||||
| // This program is free software: you can redistribute it and/or modify | ||||
| // it under the terms of the GNU General Public License as published by | ||||
| // the Free Software Foundation, either version 3 of the License, or | ||||
| // any later version. | ||||
| // | ||||
| // This program is distributed in the hope that it will be useful, | ||||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
| // GNU General Public License for more details. | ||||
| // | ||||
| // You should have received a copy of the GNU General Public License | ||||
| // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||
| 
 | ||||
| import 'dart:async'; | ||||
| 
 | ||||
| import 'package:wyatt_form_bloc/src/core/enums/form_status.dart'; | ||||
| import 'package:wyatt_form_bloc/src/core/enums/set_operations.dart'; | ||||
| import 'package:wyatt_form_bloc/src/core/enums/validation_error.dart'; | ||||
| import 'package:wyatt_form_bloc/src/domain/form/wyatt_form.dart'; | ||||
| import 'package:wyatt_form_bloc/src/domain/input_validators/form_input_validator.dart'; | ||||
| import 'package:wyatt_form_bloc/src/presentation/features/form_data/form_data_cubit.dart'; | ||||
| 
 | ||||
| part 'form_data_state_impl.dart'; | ||||
| 
 | ||||
| abstract class FormDataCubitImpl extends FormDataCubit<FormDataStateImpl> { | ||||
|   FormDataCubitImpl(WyattForm form) : super(FormDataStateImpl(form: form)); | ||||
| 
 | ||||
|   @override | ||||
|   FutureOr<void> dataChanged<Value>( | ||||
|     String key, | ||||
|     FormInputValidator<Value, ValidationError> dirtyValue, | ||||
|   ) { | ||||
|     final form = state.form.clone(); | ||||
| 
 | ||||
|     try { | ||||
|       form.updateValidator(key, dirtyValue); | ||||
|     } catch (e) { | ||||
|       rethrow; | ||||
|     } | ||||
| 
 | ||||
|     emit( | ||||
|       state.copyWith( | ||||
|         form: form, | ||||
|         status: form.validate(), | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   @override | ||||
|   FutureOr<void> update( | ||||
|     WyattForm form, { | ||||
|     SetOperation operation = SetOperation.replace, | ||||
|   }) { | ||||
|     final WyattForm newForm = operation.operation.call(state.form, form); | ||||
| 
 | ||||
|     emit( | ||||
|       state.copyWith( | ||||
|         form: newForm, | ||||
|         status: newForm.validate(), | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   @override | ||||
|   FutureOr<void> reset() { | ||||
|     final form = state.form.reset(); | ||||
|     emit( | ||||
|       state.copyWith( | ||||
|         form: form, | ||||
|         status: form.validate(), | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   @override | ||||
|   FutureOr<void> validate() { | ||||
|     emit( | ||||
|       state.copyWith( | ||||
|         status: state.form.validate(), | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
| } | ||||
| @ -0,0 +1,42 @@ | ||||
| // Copyright (C) 2022 WYATT GROUP | ||||
| // Please see the AUTHORS file for details. | ||||
| // | ||||
| // This program is free software: you can redistribute it and/or modify | ||||
| // it under the terms of the GNU General Public License as published by | ||||
| // the Free Software Foundation, either version 3 of the License, or | ||||
| // any later version. | ||||
| // | ||||
| // This program is distributed in the hope that it will be useful, | ||||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
| // GNU General Public License for more details. | ||||
| // | ||||
| // You should have received a copy of the GNU General Public License | ||||
| // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||
| 
 | ||||
| part of 'form_data_cubit_impl.dart'; | ||||
| 
 | ||||
| class FormDataStateImpl extends FormDataState { | ||||
|   const FormDataStateImpl({ | ||||
|     required super.form, | ||||
|     super.status = FormStatus.pure, | ||||
|     super.errorMessage, | ||||
|   }); | ||||
| 
 | ||||
|   FormDataStateImpl copyWith({ | ||||
|     FormStatus? status, | ||||
|     WyattForm? form, | ||||
|     String? errorMessage, | ||||
|   }) => | ||||
|       FormDataStateImpl( | ||||
|         status: status ?? this.status, | ||||
|         form: form ?? this.form, | ||||
|         errorMessage: errorMessage ?? this.errorMessage, | ||||
|       ); | ||||
| 
 | ||||
|   @override | ||||
|   bool? get stringify => true; | ||||
| 
 | ||||
|   @override | ||||
|   List<Object?> get props => [status, form, errorMessage]; | ||||
| } | ||||
| @ -0,0 +1,43 @@ | ||||
| // Copyright (C) 2022 WYATT GROUP | ||||
| // Please see the AUTHORS file for details. | ||||
| // | ||||
| // This program is free software: you can redistribute it and/or modify | ||||
| // it under the terms of the GNU General Public License as published by | ||||
| // the Free Software Foundation, either version 3 of the License, or | ||||
| // any later version. | ||||
| // | ||||
| // This program is distributed in the hope that it will be useful, | ||||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
| // GNU General Public License for more details. | ||||
| // | ||||
| // You should have received a copy of the GNU General Public License | ||||
| // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||
| 
 | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:flutter_bloc/flutter_bloc.dart'; | ||||
| import 'package:wyatt_form_bloc/wyatt_form_bloc.dart'; | ||||
| 
 | ||||
| class InputBuilder<Cubit extends FormDataCubit> extends StatelessWidget { | ||||
|   const InputBuilder({required this.field, required this.builder, super.key}); | ||||
| 
 | ||||
|   final String field; | ||||
| 
 | ||||
|   final Widget Function( | ||||
|     BuildContext context, | ||||
|     Cubit cubit, | ||||
|     FormDataState state, | ||||
|     String field, | ||||
|     bool inputValid, | ||||
|   ) builder; | ||||
| 
 | ||||
|   @override | ||||
|   Widget build(BuildContext context) => | ||||
|       BlocBuilder<Cubit, FormDataState>( | ||||
|         builder: (context, state) { | ||||
|           final cubit = context.read<Cubit>(); | ||||
|           final inputValid = state.form.validatorOf(field).valid; | ||||
|           return builder.call(context, cubit, state, field, inputValid); | ||||
|         }, | ||||
|       ); | ||||
| } | ||||
| @ -0,0 +1,57 @@ | ||||
| // Copyright (C) 2022 WYATT GROUP | ||||
| // Please see the AUTHORS file for details. | ||||
| // | ||||
| // This program is free software: you can redistribute it and/or modify | ||||
| // it under the terms of the GNU General Public License as published by | ||||
| // the Free Software Foundation, either version 3 of the License, or | ||||
| // any later version. | ||||
| // | ||||
| // This program is distributed in the hope that it will be useful, | ||||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
| // GNU General Public License for more details. | ||||
| // | ||||
| // You should have received a copy of the GNU General Public License | ||||
| // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||
| 
 | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:flutter_bloc/flutter_bloc.dart'; | ||||
| import 'package:wyatt_form_bloc/src/domain/entities/form_input_metadata.dart'; | ||||
| import 'package:wyatt_form_bloc/src/presentation/presentation.dart'; | ||||
| 
 | ||||
| class InputBuilderMetadata<Cubit extends FormDataCubit, Extra> | ||||
|     extends StatelessWidget { | ||||
|   const InputBuilderMetadata({ | ||||
|     required this.field, | ||||
|     required this.builder, | ||||
|     super.key, | ||||
|   }); | ||||
| 
 | ||||
|   final String field; | ||||
| 
 | ||||
|   final Widget Function( | ||||
|     BuildContext context, | ||||
|     Cubit cubit, | ||||
|     FormDataState state, | ||||
|     String field, | ||||
|     bool inputValid, | ||||
|     FormInputMetadata<Extra>? metadata, | ||||
|   ) builder; | ||||
| 
 | ||||
|   @override | ||||
|   Widget build(BuildContext context) => BlocBuilder<Cubit, FormDataState>( | ||||
|         builder: (context, state) { | ||||
|           final cubit = context.read<Cubit>(); | ||||
|           final inputValid = state.form.validatorOf(field).valid; | ||||
|           final metadata = state.form.metadataOf<Extra>(field); | ||||
|           return builder.call( | ||||
|             context, | ||||
|             cubit, | ||||
|             state, | ||||
|             field, | ||||
|             inputValid, | ||||
|             metadata, | ||||
|           ); | ||||
|         }, | ||||
|       ); | ||||
| } | ||||
| @ -0,0 +1,37 @@ | ||||
| // Copyright (C) 2022 WYATT GROUP | ||||
| // Please see the AUTHORS file for details. | ||||
| // | ||||
| // This program is free software: you can redistribute it and/or modify | ||||
| // it under the terms of the GNU General Public License as published by | ||||
| // the Free Software Foundation, either version 3 of the License, or | ||||
| // any later version. | ||||
| // | ||||
| // This program is distributed in the hope that it will be useful, | ||||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
| // GNU General Public License for more details. | ||||
| // | ||||
| // You should have received a copy of the GNU General Public License | ||||
| // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||
| 
 | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:flutter_bloc/flutter_bloc.dart'; | ||||
| import 'package:wyatt_form_bloc/src/core/enums/form_status.dart'; | ||||
| import 'package:wyatt_form_bloc/src/presentation/features/form_data/form_data_cubit.dart'; | ||||
| 
 | ||||
| class SubmitBuilder<Cubit extends FormDataCubit> extends StatelessWidget { | ||||
|   const SubmitBuilder({required this.builder, super.key}); | ||||
| 
 | ||||
|   final Widget Function(BuildContext context, Cubit cubit, FormStatus status) | ||||
|       builder; | ||||
| 
 | ||||
|   @override | ||||
|   Widget build(BuildContext context) => BlocBuilder<Cubit, FormDataState>( | ||||
|         buildWhen: (previous, current) => previous.status != current.status, | ||||
|         builder: (context, state) => builder.call( | ||||
|           context, | ||||
|           context.read<Cubit>(), | ||||
|           state.status, | ||||
|         ), | ||||
|       ); | ||||
| } | ||||
| @ -0,0 +1,21 @@ | ||||
| // Copyright (C) 2022 WYATT GROUP | ||||
| // Please see the AUTHORS file for details. | ||||
| // | ||||
| // This program is free software: you can redistribute it and/or modify | ||||
| // it under the terms of the GNU General Public License as published by | ||||
| // the Free Software Foundation, either version 3 of the License, or | ||||
| // any later version. | ||||
| // | ||||
| // This program is distributed in the hope that it will be useful, | ||||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
| // GNU General Public License for more details. | ||||
| // | ||||
| // You should have received a copy of the GNU General Public License | ||||
| // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||
| 
 | ||||
| export 'features/form_data/form_data_cubit.dart'; | ||||
| export 'features/form_data_impl/form_data_cubit_impl.dart'; | ||||
| export 'features/widgets/input_builder.dart'; | ||||
| export 'features/widgets/input_builder_metadata.dart'; | ||||
| export 'features/widgets/submit_builder.dart'; | ||||
| @ -14,7 +14,7 @@ | ||||
| // You should have received a copy of the GNU General Public License | ||||
| // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||
| 
 | ||||
| export 'cubit/form_data_cubit.dart'; | ||||
| export 'enums/enums.dart'; | ||||
| export 'form/form.dart'; | ||||
| export 'validators/validators.dart'; | ||||
| export 'core/core.dart'; | ||||
| export 'data/data.dart'; | ||||
| export 'domain/domain.dart'; | ||||
| export 'presentation/presentation.dart'; | ||||
|  | ||||
| @ -1,39 +0,0 @@ | ||||
| // Copyright (C) 2022 WYATT GROUP | ||||
| // Please see the AUTHORS file for details. | ||||
| // | ||||
| // This program is free software: you can redistribute it and/or modify | ||||
| // it under the terms of the GNU General Public License as published by | ||||
| // the Free Software Foundation, either version 3 of the License, or | ||||
| // any later version. | ||||
| // | ||||
| // This program is distributed in the hope that it will be useful, | ||||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
| // GNU General Public License for more details. | ||||
| // | ||||
| // You should have received a copy of the GNU General Public License | ||||
| // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||
| 
 | ||||
| import 'package:wyatt_form_bloc/src/enums/enums.dart'; | ||||
| import 'package:wyatt_form_bloc/src/validators/inputs/base/regex_validator.dart'; | ||||
| 
 | ||||
| /// {@template iban} | ||||
| /// Form input for an IBAN input. | ||||
| /// {@endtemplate} | ||||
| class Iban extends RegexValidator<ValidationStandardError> { | ||||
|   /// {@macro iban} | ||||
|   const Iban.pure() : super.pure(); | ||||
| 
 | ||||
|   /// {@macro iban} | ||||
|   const Iban.dirty([super.value = '']) : super.dirty(); | ||||
| 
 | ||||
|   @override | ||||
|   ValidationStandardError get onEmpty => ValidationStandardError.empty; | ||||
|   @override | ||||
|   ValidationStandardError get onError => ValidationStandardError.invalid; | ||||
| 
 | ||||
|   @override | ||||
|   RegExp get regex => RegExp( | ||||
|         r'^(?:((?:IT|SM)\d{2}[A-Z]{1}\d{22})|(NL\d{2}[A-Z]{4}\d{10})|(LV\d{2}[A-Z]{4}\d{13})|((?:BG|GB|IE)\d{2}[A-Z]{4}\d{14})|(GI\d{2}[A-Z]{4}\d{15})|(RO\d{2}[A-Z]{4}\d{16})|(MT\d{2}[A-Z]{4}\d{23})|(NO\d{13})|((?:DK|FI)\d{16})|((?:SI)\d{17})|((?:AT|EE|LU|LT)\d{18})|((?:HR|LI|CH)\d{19})|((?:DE|VA)\d{20})|((?:AD|CZ|ES|MD|SK|SE)\d{22})|(PT\d{23})|((?:IS)\d{24})|((?:BE)\d{14})|((?:FR|MC|GR)\d{25})|((?:PL|HU|CY)\d{26}))$', | ||||
|       ); | ||||
| } | ||||
| @ -1,50 +0,0 @@ | ||||
| // Copyright (C) 2022 WYATT GROUP | ||||
| // Please see the AUTHORS file for details. | ||||
| // | ||||
| // This program is free software: you can redistribute it and/or modify | ||||
| // it under the terms of the GNU General Public License as published by | ||||
| // the Free Software Foundation, either version 3 of the License, or | ||||
| // any later version. | ||||
| // | ||||
| // This program is distributed in the hope that it will be useful, | ||||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
| // GNU General Public License for more details. | ||||
| // | ||||
| // You should have received a copy of the GNU General Public License | ||||
| // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||
| 
 | ||||
| import 'package:wyatt_form_bloc/src/enums/enums.dart'; | ||||
| import 'package:wyatt_form_bloc/src/form/form.dart'; | ||||
| 
 | ||||
| /// {@template list_option} | ||||
| /// Form input for a list input | ||||
| /// {@endtemplate} | ||||
| class ListOption<T> | ||||
|     extends FormInputValidator<List<T>, ValidationStandardError> { | ||||
|   /// {@macro list_option} | ||||
|   const ListOption.pure({List<T>? defaultValue}) | ||||
|       : super.pure(defaultValue ?? const []); | ||||
| 
 | ||||
|   /// {@macro list_option} | ||||
|   const ListOption.dirty({List<T>? value}) : super.dirty(value ?? const []); | ||||
| 
 | ||||
|   ListOption<T> select(T? v) { | ||||
|     if (v == null) { | ||||
|       return this; | ||||
|     } | ||||
|     if (value.contains(v)) { | ||||
|       final List<T> newValue = List.from(value)..remove(v); | ||||
|       return ListOption<T>.dirty(value: newValue); | ||||
|     } else { | ||||
|       final List<T> newValue = List.from(value)..add(v); | ||||
|       return ListOption<T>.dirty(value: newValue); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   @override | ||||
|   ValidationStandardError? validator(List<T>? value) => | ||||
|       value?.isNotEmpty ?? false == true | ||||
|           ? null | ||||
|           : ValidationStandardError.invalid; | ||||
| } | ||||
| @ -3,15 +3,33 @@ description: Manage forms in Dart & Flutter with Bloc | ||||
| repository: https://git.wyatt-studio.fr/Wyatt-FOSS/wyatt-packages/src/branch/master/packages/wyatt_form_bloc | ||||
| version: 0.0.6 | ||||
| 
 | ||||
| publish_to: https://git.wyatt-studio.fr/api/packages/Wyatt-FOSS/pub | ||||
| 
 | ||||
| environment: | ||||
|   sdk: '>=2.17.2 <3.0.0' | ||||
|   sdk: ">=2.17.2 <3.0.0" | ||||
| 
 | ||||
| dependencies: | ||||
|   bloc: ^8.1.0 | ||||
|   flutter: | ||||
|     sdk: flutter | ||||
| 
 | ||||
|   flutter_bloc: ^8.1.0 | ||||
|   equatable: ^2.0.5 | ||||
| 
 | ||||
|   wyatt_architecture: | ||||
|     git: | ||||
|       url: https://git.wyatt-studio.fr/Wyatt-FOSS/wyatt-packages | ||||
|       ref: wyatt_architecture-v0.0.2-dev.0 | ||||
|       path: packages/wyatt_architecture | ||||
| 
 | ||||
|   wyatt_type_utils: | ||||
|     git: | ||||
|       url: https://git.wyatt-studio.fr/Wyatt-FOSS/wyatt-packages | ||||
|       ref: wyatt_type_utils-v0.0.3+1 | ||||
|       path: packages/wyatt_type_utils | ||||
| 
 | ||||
| dev_dependencies: | ||||
|   test: ^1.21.4 | ||||
|   flutter_test: | ||||
|     sdk: flutter | ||||
| 
 | ||||
|   wyatt_analysis: | ||||
|     git: | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user