From b89ef3de8a068bd097d90b621ce51e9cdad3e944 Mon Sep 17 00:00:00 2001 From: Hugo Pointcheval Date: Wed, 9 Nov 2022 22:35:41 -0500 Subject: [PATCH] feat!(form): migrate to wyatt architecture --- packages/wyatt_form_bloc/README.md | 6 +- .../wyatt_form_bloc/analysis_options.yaml | 2 +- .../ios/Flutter/AppFrameworkInfo.plist | 2 +- .../ios/Runner.xcodeproj/project.pbxproj | 6 +- .../example/ios/Runner/Info.plist | 2 + .../wyatt_form_bloc/example/lib/app/app.dart | 44 ++- .../example/lib/app/metadata.dart | 4 +- .../example/lib/constants.dart | 2 - .../lib/sign_up/widgets/sign_up_form.dart | 374 +++++++----------- .../simple_custom_form_cubit.dart} | 9 +- .../validators.dart => core/core.dart} | 15 +- .../lib/src/{ => core}/enums/enums.dart | 0 .../{ => core}/enums/form_input_status.dart | 0 .../lib/src/{ => core}/enums/form_status.dart | 0 .../lib/src/core/enums/set_operations.dart | 39 ++ .../{ => core}/enums/validation_error.dart | 3 +- .../lib/src/cubit/form_data_cubit.dart | 110 ------ .../wyatt_form_bloc/lib/src/data/data.dart | 34 ++ .../lib/src/data/form/wyatt_form_impl.dart | 136 +++++++ .../data/form_encoders/form_json_encoder.dart | 30 ++ .../data/form_encoders/form_map_encoder.dart | 32 ++ .../data/form_operations/form_difference.dart | 50 +++ .../form_operations/form_intersection.dart | 44 +++ .../form_operations/form_replace.dart} | 17 +- .../src/data/form_operations/form_union.dart | 48 +++ .../every_input_validator.dart | 14 +- .../only_required_input_validator.dart | 29 ++ .../input_validators}/boolean.dart | 12 +- .../input_validators}/confirmed_password.dart | 20 +- .../input_validators}/email.dart | 6 +- .../input_validators/enum_option.dart} | 31 +- .../data/input_validators/list_option.dart | 40 ++ .../input_validators}/name.dart | 6 +- .../input_validators}/password.dart | 8 +- .../input_validators}/phone.dart | 6 +- .../input_validators}/text_string.dart | 6 +- .../lib/src/domain/domain.dart | 23 ++ .../{form => domain/entities}/form_input.dart | 32 +- .../entities}/form_input_metadata.dart | 28 +- .../lib/src/domain/form/wyatt_form.dart | 60 +++ .../form_encoders/form_encoder.dart} | 17 +- .../form_operations/form_operation.dart | 23 ++ .../form_validators}/form_validator.dart | 14 +- .../input_validators/any_validator.dart | 45 +++ .../input_validators/equality_validator.dart | 45 +++ .../form_input_validator.dart | 22 +- .../input_validators/nullable_validator.dart} | 27 +- .../input_validators}/regex_validator.dart | 11 +- .../input_validators}/text_validator.dart | 9 +- .../lib/src/form/form_data.dart | 184 --------- .../features/form_data/form_data_cubit.dart | 47 +++ .../features/form_data}/form_data_state.dart | 24 +- .../form_data_impl/form_data_cubit_impl.dart | 86 ++++ .../form_data_impl/form_data_state_impl.dart | 42 ++ .../features/widgets/input_builder.dart | 43 ++ .../widgets/input_builder_metadata.dart | 57 +++ .../features/widgets/submit_builder.dart | 37 ++ .../lib/src/presentation/presentation.dart | 21 + packages/wyatt_form_bloc/lib/src/src.dart | 8 +- .../lib/src/validators/inputs/iban.dart | 39 -- .../src/validators/inputs/list_option.dart | 50 --- packages/wyatt_form_bloc/pubspec.yaml | 24 +- 62 files changed, 1387 insertions(+), 818 deletions(-) rename packages/wyatt_form_bloc/example/lib/{cubit/custom_form_cubit.dart => simple_custom_form_cubit/simple_custom_form_cubit.dart} (80%) rename packages/wyatt_form_bloc/lib/src/{validators/validators.dart => core/core.dart} (60%) rename packages/wyatt_form_bloc/lib/src/{ => core}/enums/enums.dart (100%) rename packages/wyatt_form_bloc/lib/src/{ => core}/enums/form_input_status.dart (100%) rename packages/wyatt_form_bloc/lib/src/{ => core}/enums/form_status.dart (100%) create mode 100644 packages/wyatt_form_bloc/lib/src/core/enums/set_operations.dart rename packages/wyatt_form_bloc/lib/src/{ => core}/enums/validation_error.dart (97%) delete mode 100644 packages/wyatt_form_bloc/lib/src/cubit/form_data_cubit.dart create mode 100644 packages/wyatt_form_bloc/lib/src/data/data.dart create mode 100644 packages/wyatt_form_bloc/lib/src/data/form/wyatt_form_impl.dart create mode 100644 packages/wyatt_form_bloc/lib/src/data/form_encoders/form_json_encoder.dart create mode 100644 packages/wyatt_form_bloc/lib/src/data/form_encoders/form_map_encoder.dart create mode 100644 packages/wyatt_form_bloc/lib/src/data/form_operations/form_difference.dart create mode 100644 packages/wyatt_form_bloc/lib/src/data/form_operations/form_intersection.dart rename packages/wyatt_form_bloc/lib/src/{form/form.dart => data/form_operations/form_replace.dart} (64%) create mode 100644 packages/wyatt_form_bloc/lib/src/data/form_operations/form_union.dart rename packages/wyatt_form_bloc/lib/src/{validators/form => data/form_validators}/every_input_validator.dart (73%) create mode 100644 packages/wyatt_form_bloc/lib/src/data/form_validators/only_required_input_validator.dart rename packages/wyatt_form_bloc/lib/src/{validators/inputs => data/input_validators}/boolean.dart (74%) rename packages/wyatt_form_bloc/lib/src/{validators/inputs => data/input_validators}/confirmed_password.dart (61%) rename packages/wyatt_form_bloc/lib/src/{validators/inputs => data/input_validators}/email.dart (85%) rename packages/wyatt_form_bloc/lib/src/{validators/inputs/siren.dart => data/input_validators/enum_option.dart} (52%) create mode 100644 packages/wyatt_form_bloc/lib/src/data/input_validators/list_option.dart rename packages/wyatt_form_bloc/lib/src/{validators/inputs => data/input_validators}/name.dart (85%) rename packages/wyatt_form_bloc/lib/src/{validators/inputs => data/input_validators}/password.dart (81%) rename packages/wyatt_form_bloc/lib/src/{validators/inputs => data/input_validators}/phone.dart (85%) rename packages/wyatt_form_bloc/lib/src/{validators/inputs => data/input_validators}/text_string.dart (84%) create mode 100644 packages/wyatt_form_bloc/lib/src/domain/domain.dart rename packages/wyatt_form_bloc/lib/src/{form => domain/entities}/form_input.dart (59%) rename packages/wyatt_form_bloc/lib/src/{form => domain/entities}/form_input_metadata.dart (69%) create mode 100644 packages/wyatt_form_bloc/lib/src/domain/form/wyatt_form.dart rename packages/wyatt_form_bloc/lib/src/{enums/set_operations.dart => domain/form_encoders/form_encoder.dart} (75%) create mode 100644 packages/wyatt_form_bloc/lib/src/domain/form_operations/form_operation.dart rename packages/wyatt_form_bloc/lib/src/{form => domain/form_validators}/form_validator.dart (74%) create mode 100644 packages/wyatt_form_bloc/lib/src/domain/input_validators/any_validator.dart create mode 100644 packages/wyatt_form_bloc/lib/src/domain/input_validators/equality_validator.dart rename packages/wyatt_form_bloc/lib/src/{form => domain/input_validators}/form_input_validator.dart (85%) rename packages/wyatt_form_bloc/lib/src/{validators/inputs/enum_validator.dart => domain/input_validators/nullable_validator.dart} (57%) rename packages/wyatt_form_bloc/lib/src/{validators/inputs/base => domain/input_validators}/regex_validator.dart (80%) rename packages/wyatt_form_bloc/lib/src/{validators/inputs/base => domain/input_validators}/text_validator.dart (78%) delete mode 100644 packages/wyatt_form_bloc/lib/src/form/form_data.dart create mode 100644 packages/wyatt_form_bloc/lib/src/presentation/features/form_data/form_data_cubit.dart rename packages/wyatt_form_bloc/lib/src/{cubit => presentation/features/form_data}/form_data_state.dart (68%) create mode 100644 packages/wyatt_form_bloc/lib/src/presentation/features/form_data_impl/form_data_cubit_impl.dart create mode 100644 packages/wyatt_form_bloc/lib/src/presentation/features/form_data_impl/form_data_state_impl.dart create mode 100644 packages/wyatt_form_bloc/lib/src/presentation/features/widgets/input_builder.dart create mode 100644 packages/wyatt_form_bloc/lib/src/presentation/features/widgets/input_builder_metadata.dart create mode 100644 packages/wyatt_form_bloc/lib/src/presentation/features/widgets/submit_builder.dart create mode 100644 packages/wyatt_form_bloc/lib/src/presentation/presentation.dart delete mode 100644 packages/wyatt_form_bloc/lib/src/validators/inputs/iban.dart delete mode 100644 packages/wyatt_form_bloc/lib/src/validators/inputs/list_option.dart diff --git a/packages/wyatt_form_bloc/README.md b/packages/wyatt_form_bloc/README.md index 74a53324..82fb439a 100644 --- a/packages/wyatt_form_bloc/README.md +++ b/packages/wyatt_form_bloc/README.md @@ -16,16 +16,16 @@ * along with this program. If not, see . --> -# Dart - Form BLoC +# Flutter - Form BLoC

Style: Wyatt Analysis - SDK: Dart & Flutter + SDK: Flutter

-Form Bloc for Dart & Flutter. +Form Bloc for Flutter. ## Features diff --git a/packages/wyatt_form_bloc/analysis_options.yaml b/packages/wyatt_form_bloc/analysis_options.yaml index cc27c8ca..b0c6aced 100644 --- a/packages/wyatt_form_bloc/analysis_options.yaml +++ b/packages/wyatt_form_bloc/analysis_options.yaml @@ -1,4 +1,4 @@ -include: package:wyatt_analysis/analysis_options.yaml +include: package:wyatt_analysis/analysis_options.flutter.yaml analyzer: exclude: "!example/**" \ No newline at end of file diff --git a/packages/wyatt_form_bloc/example/ios/Flutter/AppFrameworkInfo.plist b/packages/wyatt_form_bloc/example/ios/Flutter/AppFrameworkInfo.plist index 8d4492f9..9625e105 100644 --- a/packages/wyatt_form_bloc/example/ios/Flutter/AppFrameworkInfo.plist +++ b/packages/wyatt_form_bloc/example/ios/Flutter/AppFrameworkInfo.plist @@ -21,6 +21,6 @@ CFBundleVersion 1.0 MinimumOSVersion - 9.0 + 11.0 diff --git a/packages/wyatt_form_bloc/example/ios/Runner.xcodeproj/project.pbxproj b/packages/wyatt_form_bloc/example/ios/Runner.xcodeproj/project.pbxproj index abeae7fe..8a34f9c6 100644 --- a/packages/wyatt_form_bloc/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/wyatt_form_bloc/example/ios/Runner.xcodeproj/project.pbxproj @@ -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; diff --git a/packages/wyatt_form_bloc/example/ios/Runner/Info.plist b/packages/wyatt_form_bloc/example/ios/Runner/Info.plist index 7f67e595..18f4a4be 100644 --- a/packages/wyatt_form_bloc/example/ios/Runner/Info.plist +++ b/packages/wyatt_form_bloc/example/ios/Runner/Info.plist @@ -43,5 +43,7 @@ UIViewControllerBasedStatusBarAppearance + CADisableMinimumFrameDurationOnPhone + diff --git a/packages/wyatt_form_bloc/example/lib/app/app.dart b/packages/wyatt_form_bloc/example/lib/app/app.dart index 611f20ac..f9000f08 100644 --- a/packages/wyatt_form_bloc/example/lib/app/app.dart +++ b/packages/wyatt_form_bloc/example/lib/app/app.dart @@ -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 getNormalEntries() { - return const [ - FormInput(formFieldName, Name.pure(), metadata: FormInputMetadata(extra: blue)), - FormInput(formFieldEmail, Email.pure(), metadata: FormInputMetadata(extra: blue)), - FormInput(formFieldPhone, Phone.pure(), metadata: FormInputMetadata(extra: red)), - FormInput( - formFieldList, ListOption.pure(defaultValue: ['checkbox3'])), - FormInput(formFieldRadio, TextString.pure()), - FormInput(formFieldPro, Boolean.pure(), - metadata: FormInputMetadata(name: 'business')), - FormInput(formFieldHidden, Boolean.pure(), - metadata: FormInputMetadata(export: false, extra: blue)), + return [ + FormInput(formFieldName, const Name.pure(), + metadata: const FormInputMetadata(extra: blue)), + FormInput(formFieldEmail, const Email.pure(), + metadata: const FormInputMetadata(extra: blue)), + FormInput(formFieldList, + const ListOption.pure(defaultValue: 'c3')), + FormInput(formFieldRadio, const TextString.pure()), + FormInput(formFieldPro, const Boolean.pure(), + metadata: const FormInputMetadata(name: 'business')), + FormInput(formFieldHidden, const Boolean.pure(), + metadata: + const FormInputMetadata(export: false, extra: blue)), ]; } static List getBusinessEntries() { - const entries = [ - FormInput(formFieldSiren, Siren.pure()), - FormInput(formFieldIban, Iban.pure()), + final entries = [ + FormInput(formFieldPhone, const Phone.pure(), + metadata: const FormInputMetadata(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( create: (context) => formCubit, child: const WidgetTree(), ); diff --git a/packages/wyatt_form_bloc/example/lib/app/metadata.dart b/packages/wyatt_form_bloc/example/lib/app/metadata.dart index b4e68725..6d66f961 100644 --- a/packages/wyatt_form_bloc/example/lib/app/metadata.dart +++ b/packages/wyatt_form_bloc/example/lib/app/metadata.dart @@ -28,7 +28,5 @@ class Metadata { }); @override - String toString() { - return category.toString(); - } + String toString() => 'Metadata(category: $category)'; } diff --git a/packages/wyatt_form_bloc/example/lib/constants.dart b/packages/wyatt_form_bloc/example/lib/constants.dart index c1c52795..573b5ef6 100644 --- a/packages/wyatt_form_bloc/example/lib/constants.dart +++ b/packages/wyatt_form_bloc/example/lib/constants.dart @@ -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'; diff --git a/packages/wyatt_form_bloc/example/lib/sign_up/widgets/sign_up_form.dart b/packages/wyatt_form_bloc/example/lib/sign_up/widgets/sign_up_form.dart index 947faf6d..14ac7fbc 100644 --- a/packages/wyatt_form_bloc/example/lib/sign_up/widgets/sign_up_form.dart +++ b/packages/wyatt_form_bloc/example/lib/sign_up/widgets/sign_up_form.dart @@ -14,39 +14,33 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -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) { - case Category.perso: - return Colors.blue; - case Category.business: - return Colors.red; - } +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( - builder: ((context, state) { - final meta = state.data.metadataOf(field).extra as Metadata; + return InputBuilderMetadata( + field: formFieldName, + builder: (context, cubit, state, field, inputValid, metadata) { + final meta = state.form.metadataOf(field).extra; final color = computeColor(meta); - return Row( children: [ Container( @@ -57,58 +51,24 @@ 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), + child: TextField( + onChanged: (value) => + cubit.dataChanged(field, Name.dirty(value)), + keyboardType: TextInputType.name, + decoration: InputDecoration( + labelText: 'name', + helperText: '', + errorText: !inputValid ? 'invalid name' : null, + ), + ), ) ], ); - }), - ); - } -} - -class _NameInput extends StatelessWidget { - @override - Widget build(BuildContext context) { - return CategoryIndicator( - field: formFieldName, - builder: (context, state) { - return TextField( - onChanged: (name) => context - .read() - .dataChanged(formFieldName, Name.dirty(name)), - keyboardType: TextInputType.name, - decoration: InputDecoration( - labelText: 'name', - helperText: '', - errorText: - state.data.isNotValid(formFieldName) ? '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() - .dataChanged(formFieldEmail, Email.dirty(email)), - keyboardType: TextInputType.emailAddress, - decoration: InputDecoration( - labelText: 'email', - helperText: '', - errorText: - state.data.isNotValid(formFieldEmail) ? 'invalid email' : null, - ), - ); }, ); } @@ -117,67 +77,58 @@ class _EmailInput extends StatelessWidget { class _PhoneInput extends StatelessWidget { @override Widget build(BuildContext context) { - return CategoryIndicator( - field: formFieldPhone, - builder: (context, state) { - return TextField( - onChanged: (phone) => context - .read() - .dataChanged(formFieldPhone, Phone.dirty(phone)), - keyboardType: TextInputType.phone, - decoration: InputDecoration( - labelText: 'phone', - helperText: '', - errorText: - state.data.isNotValid(formFieldPhone) ? 'invalid phone' : null, - ), + return InputBuilderMetadata( + field: formFieldPhone, + builder: (context, cubit, state, field, inputValid, metadata) { + final meta = state.form.metadataOf(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: !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() - .dataChanged(formFieldSiren, Siren.dirty(siren)), - keyboardType: TextInputType.number, - decoration: InputDecoration( - labelText: 'siren', - helperText: '', - errorText: - state.data.isNotValid(formFieldSiren) ? 'invalid SIREN' : null, - ), - ); - }, - ); - } -} - -class _IbanInput extends StatelessWidget { - @override - Widget build(BuildContext context) { - return BlocBuilder( - builder: (context, state) { - return TextField( - onChanged: (iban) => context - .read() - .dataChanged(formFieldIban, Iban.dirty(iban)), - keyboardType: TextInputType.text, - decoration: InputDecoration( - labelText: 'iban', - helperText: '', - errorText: - state.data.isNotValid(formFieldIban) ? 'invalid IBAN' : null, - ), - ); - }, + return InputBuilder( + field: formFieldEmail, + builder: (context, cubit, state, field, inputValid) => TextField( + onChanged: (value) => cubit.dataChanged(field, Email.dirty(value)), + keyboardType: TextInputType.emailAddress, + decoration: InputDecoration( + labelText: 'email', + helperText: '', + errorText: !inputValid ? 'invalid email' : null, + ), + ), ); } } @@ -185,11 +136,11 @@ class _IbanInput extends StatelessWidget { class _CheckListInput extends StatelessWidget { @override Widget build(BuildContext context) { - return BlocBuilder( - builder: (context, state) { - final input = - state.data.validatorOf>(formFieldList); - final options = input.value; + return InputBuilder( + field: formFieldList, + builder: (context, cubit, state, field, inputValid) { + final input = state.form.validatorOf>(field); + final choices = ['c1', 'c2', 'c3']; return Column( mainAxisSize: MainAxisSize.min, @@ -197,34 +148,34 @@ class _CheckListInput extends StatelessWidget { ListTile( title: const Text('Checkbox1'), trailing: Checkbox( - value: options.contains('checkbox1'), + value: input.value == choices[0], onChanged: (_) { - context.read().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().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().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( - builder: (context, state) { - final input = state.data.validatorOf(formFieldRadio); + return InputBuilder( + field: formFieldRadio, + builder: ((context, cubit, state, field, inputValid) { + final input = state.form.validatorOf(field); return Column( mainAxisSize: MainAxisSize.min, @@ -250,10 +202,10 @@ class _RadioListInput extends StatelessWidget { groupValue: true, value: input.value == 'radio1', onChanged: (_) { - context.read().dataChanged( - formFieldRadio, - const TextString.dirty('radio1'), - ); + cubit.dataChanged( + field, + const TextString.dirty('radio1'), + ); }), ), ListTile( @@ -262,10 +214,10 @@ class _RadioListInput extends StatelessWidget { groupValue: true, value: input.value == 'radio2', onChanged: (_) { - context.read().dataChanged( - formFieldRadio, - const TextString.dirty('radio2'), - ); + cubit.dataChanged( + field, + const TextString.dirty('radio2'), + ); }), ), ListTile( @@ -274,15 +226,15 @@ class _RadioListInput extends StatelessWidget { groupValue: true, value: input.value == 'radio3', onChanged: (_) { - context.read().dataChanged( - formFieldRadio, - const TextString.dirty('radio3'), - ); + cubit.dataChanged( + field, + const TextString.dirty('radio3'), + ); }), ), ], ); - }, + }), ); } } @@ -292,17 +244,18 @@ class _CheckHiddenInput extends StatelessWidget { Widget build(BuildContext context) { return ListTile( title: const Text('This input is not exported'), - trailing: BlocBuilder( - builder: (context, state) { + trailing: InputBuilder( + field: formFieldHidden, + builder: (context, cubit, state, field, inputValid) { return Checkbox( - value: state.data.validatorOf(formFieldHidden).value, + value: state.form.validatorOf(field).value, onChanged: (v) { final value = v!; // state is false, so value can't be null - context.read().dataChanged( - formFieldHidden, - Boolean.dirty(value: value), - ); + 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( - builder: (context, state) { + trailing: InputBuilder( + field: formFieldPro, + builder: (context, cubit, state, field, inputValid) { return Checkbox( - value: state.data.validatorOf(formFieldPro).value, - onChanged: (isPro) { - final value = isPro!; // state is false, so value can't be null + value: state.form.validatorOf(field).value, + onChanged: (v) { + final value = v!; // state is false, so value can't be null - context.read().dataChanged( - formFieldPro, - Boolean.dirty(value: value), - ); + cubit.dataChanged( + field, + Boolean.dirty(value: value), + ); if (value) { - context.read().updateFormData( - App.getProFormData(), + cubit.update(App.getProFormData(), operation: SetOperation.union); } else { - context.read().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( - buildWhen: (previous, current) => previous.status != current.status, - builder: (context, state) { - return state.status.isSubmissionInProgress - ? const CircularProgressIndicator() - : ElevatedButton( - onPressed: state.status.isValidated - ? () => context.read().submitForm() - : null, - child: const Text('SIGN UP'), - ); - }, - ); - } -} - -class _DebugButton extends StatelessWidget { - @override - Widget build(BuildContext context) { - return BlocBuilder( - builder: (context, state) { - return ElevatedButton( - onPressed: () { - log(state.toString()); - }, - child: const Text('DEBUG'), - ); - }, - ); + return SubmitBuilder( + builder: (context, cubit, status) { + return status.isSubmissionInProgress + ? const CircularProgressIndicator() + : ElevatedButton( + onPressed: status.isValidated ? () => cubit.submit() : null, + child: const Text('SIGN UP'), + ); + }); } } class _ResetButton extends StatelessWidget { @override Widget build(BuildContext context) { - return BlocBuilder( - builder: (context, state) { - return ElevatedButton( - onPressed: () => context.read().resetForm(), - child: const Text('RESET'), - ); - }, - ); + return SubmitBuilder( + builder: (context, cubit, status) { + return ElevatedButton( + onPressed: () => cubit.reset(), + child: const Text('RESET'), + ); + }); } } @@ -397,7 +328,7 @@ class SignUpForm extends StatelessWidget { @override Widget build(BuildContext context) { - return BlocListener( + return BlocListener( 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( + BlocBuilder( builder: (context, state) { - if (state.data.validatorOf(formFieldPro).value) { + if (state.form.validatorOf(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(), ], ), diff --git a/packages/wyatt_form_bloc/example/lib/cubit/custom_form_cubit.dart b/packages/wyatt_form_bloc/example/lib/simple_custom_form_cubit/simple_custom_form_cubit.dart similarity index 80% rename from packages/wyatt_form_bloc/example/lib/cubit/custom_form_cubit.dart rename to packages/wyatt_form_bloc/example/lib/simple_custom_form_cubit/simple_custom_form_cubit.dart index 3f211c60..b8b0dfca 100644 --- a/packages/wyatt_form_bloc/example/lib/cubit/custom_form_cubit.dart +++ b/packages/wyatt_form_bloc/example/lib/simple_custom_form_cubit/simple_custom_form_cubit.dart @@ -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 submitForm() { - log(state.data.toMap().toString()); + Future submit() { + final value = FormJsonEncoder().encode(state.form); + log(value); return Future.value(); } diff --git a/packages/wyatt_form_bloc/lib/src/validators/validators.dart b/packages/wyatt_form_bloc/lib/src/core/core.dart similarity index 60% rename from packages/wyatt_form_bloc/lib/src/validators/validators.dart rename to packages/wyatt_form_bloc/lib/src/core/core.dart index b9eaa4b6..9d6c64d8 100644 --- a/packages/wyatt_form_bloc/lib/src/validators/validators.dart +++ b/packages/wyatt_form_bloc/lib/src/core/core.dart @@ -14,17 +14,4 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -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'; diff --git a/packages/wyatt_form_bloc/lib/src/enums/enums.dart b/packages/wyatt_form_bloc/lib/src/core/enums/enums.dart similarity index 100% rename from packages/wyatt_form_bloc/lib/src/enums/enums.dart rename to packages/wyatt_form_bloc/lib/src/core/enums/enums.dart diff --git a/packages/wyatt_form_bloc/lib/src/enums/form_input_status.dart b/packages/wyatt_form_bloc/lib/src/core/enums/form_input_status.dart similarity index 100% rename from packages/wyatt_form_bloc/lib/src/enums/form_input_status.dart rename to packages/wyatt_form_bloc/lib/src/core/enums/form_input_status.dart diff --git a/packages/wyatt_form_bloc/lib/src/enums/form_status.dart b/packages/wyatt_form_bloc/lib/src/core/enums/form_status.dart similarity index 100% rename from packages/wyatt_form_bloc/lib/src/enums/form_status.dart rename to packages/wyatt_form_bloc/lib/src/core/enums/form_status.dart diff --git a/packages/wyatt_form_bloc/lib/src/core/enums/set_operations.dart b/packages/wyatt_form_bloc/lib/src/core/enums/set_operations.dart new file mode 100644 index 00000000..3e27f437 --- /dev/null +++ b/packages/wyatt_form_bloc/lib/src/core/enums/set_operations.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 . + +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); +} diff --git a/packages/wyatt_form_bloc/lib/src/enums/validation_error.dart b/packages/wyatt_form_bloc/lib/src/core/enums/validation_error.dart similarity index 97% rename from packages/wyatt_form_bloc/lib/src/enums/validation_error.dart rename to packages/wyatt_form_bloc/lib/src/core/enums/validation_error.dart index 5a0b436e..4966b6b8 100644 --- a/packages/wyatt_form_bloc/lib/src/enums/validation_error.dart +++ b/packages/wyatt_form_bloc/lib/src/core/enums/validation_error.dart @@ -23,5 +23,6 @@ abstract class ValidationError {} enum ValidationStandardError implements ValidationError { invalid, - empty + empty, + notEqual, } diff --git a/packages/wyatt_form_bloc/lib/src/cubit/form_data_cubit.dart b/packages/wyatt_form_bloc/lib/src/cubit/form_data_cubit.dart deleted file mode 100644 index 5434acac..00000000 --- a/packages/wyatt_form_bloc/lib/src/cubit/form_data_cubit.dart +++ /dev/null @@ -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 . - -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 { - 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( - String field, - FormInputValidator 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 submitForm(); -} diff --git a/packages/wyatt_form_bloc/lib/src/data/data.dart b/packages/wyatt_form_bloc/lib/src/data/data.dart new file mode 100644 index 00000000..743bef59 --- /dev/null +++ b/packages/wyatt_form_bloc/lib/src/data/data.dart @@ -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 . + +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'; diff --git a/packages/wyatt_form_bloc/lib/src/data/form/wyatt_form_impl.dart b/packages/wyatt_form_bloc/lib/src/data/form/wyatt_form_impl.dart new file mode 100644 index 00000000..235ce5c8 --- /dev/null +++ b/packages/wyatt_form_bloc/lib/src/data/form/wyatt_form_impl.dart @@ -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 . + +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>> _inputs; + final FormValidator _validator; + + late List< + FormInput, + dynamic>> _inputsInitial; + + WyattFormImpl( + this._inputs, { + FormValidator validationStrategy = const EveryInputValidator(), + }) : _validator = validationStrategy { + _inputsInitial = _inputs.map((input) => input.clone()).toList(); + } + + @override + List< + FormInput, + dynamic>> get inputs => _inputs; + + @override + FormValidator get formValidationStrategy => _validator; + + @override + bool containsKey(String key) => inputs.any((input) => input.key == key); + + @override + List> + asValidatorList() => inputs + .map>( + (input) => input.validator as FormInputValidator, + ) + .toList(); + + @override + FormInput, 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(String key) => + (inputOf(key).validator as FormInputValidator).error; + + @override + FormInputMetadata metadataOf(String key) => + inputOf(key).metadata as FormInputMetadata; + + @override + Validator validatorOf< + Validator extends FormInputValidator>( + String key, + ) => + inputOf(key).validator as Validator; + + @override + Value? valueOf(String key) => + (inputOf(key).validator as FormInputValidator).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 get props => _inputs; + + @override + void updateMetadata(String key, FormInputMetadata metadata) { + final index = _inputs.indexOf( + inputOf(key), + ); + _inputs[index] = _inputs[index].copyWith(metadata: metadata); + } + + @override + void updateValidator( + String key, + FormInputValidator 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); +} diff --git a/packages/wyatt_form_bloc/lib/src/data/form_encoders/form_json_encoder.dart b/packages/wyatt_form_bloc/lib/src/data/form_encoders/form_json_encoder.dart new file mode 100644 index 00000000..a4d3d636 --- /dev/null +++ b/packages/wyatt_form_bloc/lib/src/data/form_encoders/form_json_encoder.dart @@ -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 . + +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 { + @override + String encode(WyattForm form) { + final mapEncoder = FormMapEncoder(); + final map = mapEncoder.encode(form); + return jsonEncode(map); + } +} diff --git a/packages/wyatt_form_bloc/lib/src/data/form_encoders/form_map_encoder.dart b/packages/wyatt_form_bloc/lib/src/data/form_encoders/form_map_encoder.dart new file mode 100644 index 00000000..9d682976 --- /dev/null +++ b/packages/wyatt_form_bloc/lib/src/data/form_encoders/form_map_encoder.dart @@ -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 . + +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> { + @override + Map encode(WyattForm form) { + final inputs = form.inputs; + final map = {}; + for (final input in inputs) { + if (input.metadata.export) { + map[input.name] = input.validator.value; + } + } + return map; + } +} diff --git a/packages/wyatt_form_bloc/lib/src/data/form_operations/form_difference.dart b/packages/wyatt_form_bloc/lib/src/data/form_operations/form_difference.dart new file mode 100644 index 00000000..20a5fee4 --- /dev/null +++ b/packages/wyatt_form_bloc/lib/src/data/form_operations/form_difference.dart @@ -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 . + +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>>[]; + + 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, + ); + } +} diff --git a/packages/wyatt_form_bloc/lib/src/data/form_operations/form_intersection.dart b/packages/wyatt_form_bloc/lib/src/data/form_operations/form_intersection.dart new file mode 100644 index 00000000..1a5d75bb --- /dev/null +++ b/packages/wyatt_form_bloc/lib/src/data/form_operations/form_intersection.dart @@ -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 . + +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>>[]; + + for (final i in a.inputs) { + if (b.containsKey(i.key)) { + inputs.add(i); + } + } + + return WyattFormImpl( + inputs, + validationStrategy: a.formValidationStrategy, + ); + } +} diff --git a/packages/wyatt_form_bloc/lib/src/form/form.dart b/packages/wyatt_form_bloc/lib/src/data/form_operations/form_replace.dart similarity index 64% rename from packages/wyatt_form_bloc/lib/src/form/form.dart rename to packages/wyatt_form_bloc/lib/src/data/form_operations/form_replace.dart index 041263fc..9c5ab986 100644 --- a/packages/wyatt_form_bloc/lib/src/form/form.dart +++ b/packages/wyatt_form_bloc/lib/src/data/form_operations/form_replace.dart @@ -14,15 +14,12 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -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; +} diff --git a/packages/wyatt_form_bloc/lib/src/data/form_operations/form_union.dart b/packages/wyatt_form_bloc/lib/src/data/form_operations/form_union.dart new file mode 100644 index 00000000..06d0c067 --- /dev/null +++ b/packages/wyatt_form_bloc/lib/src/data/form_operations/form_union.dart @@ -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 . + +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>>[]; + + 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, + ); + } +} diff --git a/packages/wyatt_form_bloc/lib/src/validators/form/every_input_validator.dart b/packages/wyatt_form_bloc/lib/src/data/form_validators/every_input_validator.dart similarity index 73% rename from packages/wyatt_form_bloc/lib/src/validators/form/every_input_validator.dart rename to packages/wyatt_form_bloc/lib/src/data/form_validators/every_input_validator.dart index fba42286..adfd60bc 100644 --- a/packages/wyatt_form_bloc/lib/src/validators/form/every_input_validator.dart +++ b/packages/wyatt_form_bloc/lib/src/data/form_validators/every_input_validator.dart @@ -14,7 +14,10 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -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()); - } + final validators = form.asValidatorList(); - @override - FormStatus rawValidate( - List> validators, - ) { if (validators.any((validator) => validator.valid == false)) { return FormStatus.invalid; } diff --git a/packages/wyatt_form_bloc/lib/src/data/form_validators/only_required_input_validator.dart b/packages/wyatt_form_bloc/lib/src/data/form_validators/only_required_input_validator.dart new file mode 100644 index 00000000..bd83d295 --- /dev/null +++ b/packages/wyatt_form_bloc/lib/src/data/form_validators/only_required_input_validator.dart @@ -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 . + +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; + } +} diff --git a/packages/wyatt_form_bloc/lib/src/validators/inputs/boolean.dart b/packages/wyatt_form_bloc/lib/src/data/input_validators/boolean.dart similarity index 74% rename from packages/wyatt_form_bloc/lib/src/validators/inputs/boolean.dart rename to packages/wyatt_form_bloc/lib/src/data/input_validators/boolean.dart index e28f5ad1..71f470ca 100644 --- a/packages/wyatt_form_bloc/lib/src/validators/inputs/boolean.dart +++ b/packages/wyatt_form_bloc/lib/src/data/input_validators/boolean.dart @@ -14,21 +14,25 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -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 { +class Boolean extends NullableValidator { /// {@macro boolean} const Boolean.pure({bool? defaultValue = false}) : super.pure(defaultValue ?? false); /// {@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; + } diff --git a/packages/wyatt_form_bloc/lib/src/validators/inputs/confirmed_password.dart b/packages/wyatt_form_bloc/lib/src/data/input_validators/confirmed_password.dart similarity index 61% rename from packages/wyatt_form_bloc/lib/src/validators/inputs/confirmed_password.dart rename to packages/wyatt_form_bloc/lib/src/data/input_validators/confirmed_password.dart index 54899cf2..448efba2 100644 --- a/packages/wyatt_form_bloc/lib/src/validators/inputs/confirmed_password.dart +++ b/packages/wyatt_form_bloc/lib/src/data/input_validators/confirmed_password.dart @@ -14,25 +14,31 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -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 { + extends EqualityValidator { /// {@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; } diff --git a/packages/wyatt_form_bloc/lib/src/validators/inputs/email.dart b/packages/wyatt_form_bloc/lib/src/data/input_validators/email.dart similarity index 85% rename from packages/wyatt_form_bloc/lib/src/validators/inputs/email.dart rename to packages/wyatt_form_bloc/lib/src/data/input_validators/email.dart index 4961c672..c718dfdb 100644 --- a/packages/wyatt_form_bloc/lib/src/validators/inputs/email.dart +++ b/packages/wyatt_form_bloc/lib/src/data/input_validators/email.dart @@ -14,15 +14,15 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -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 { /// {@macro email} - const Email.pure() : super.pure(); + const Email.pure([super.value]) : super.pure(); /// {@macro email} const Email.dirty([super.value = '']) : super.dirty(); diff --git a/packages/wyatt_form_bloc/lib/src/validators/inputs/siren.dart b/packages/wyatt_form_bloc/lib/src/data/input_validators/enum_option.dart similarity index 52% rename from packages/wyatt_form_bloc/lib/src/validators/inputs/siren.dart rename to packages/wyatt_form_bloc/lib/src/data/input_validators/enum_option.dart index 798c33f4..8a939fba 100644 --- a/packages/wyatt_form_bloc/lib/src/validators/inputs/siren.dart +++ b/packages/wyatt_form_bloc/lib/src/data/input_validators/enum_option.dart @@ -14,24 +14,27 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -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 { - /// {@macro siren} - const Siren.pure() : super.pure(); +class EnumOption extends AnyValidator, 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 enums; @override - ValidationStandardError get onEmpty => ValidationStandardError.empty; - @override - ValidationStandardError get onError => ValidationStandardError.invalid; + List 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; } diff --git a/packages/wyatt_form_bloc/lib/src/data/input_validators/list_option.dart b/packages/wyatt_form_bloc/lib/src/data/input_validators/list_option.dart new file mode 100644 index 00000000..13f28986 --- /dev/null +++ b/packages/wyatt_form_bloc/lib/src/data/input_validators/list_option.dart @@ -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 . + +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 extends AnyValidator, 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 choices; + + @override + Iterable get allChoices => choices; + + @override + ValidationError get onError => ValidationStandardError.invalid; + + @override + ValidationError get onNull => ValidationStandardError.invalid; + + @override + bool test(T? element, T value) => element == value; +} diff --git a/packages/wyatt_form_bloc/lib/src/validators/inputs/name.dart b/packages/wyatt_form_bloc/lib/src/data/input_validators/name.dart similarity index 85% rename from packages/wyatt_form_bloc/lib/src/validators/inputs/name.dart rename to packages/wyatt_form_bloc/lib/src/data/input_validators/name.dart index cf3b6558..6ade3e21 100644 --- a/packages/wyatt_form_bloc/lib/src/validators/inputs/name.dart +++ b/packages/wyatt_form_bloc/lib/src/data/input_validators/name.dart @@ -14,15 +14,15 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -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 { /// {@macro name} - const Name.pure() : super.pure(); + const Name.pure([super.value]) : super.pure(); /// {@macro name} const Name.dirty([super.value = '']) : super.dirty(); diff --git a/packages/wyatt_form_bloc/lib/src/validators/inputs/password.dart b/packages/wyatt_form_bloc/lib/src/data/input_validators/password.dart similarity index 81% rename from packages/wyatt_form_bloc/lib/src/validators/inputs/password.dart rename to packages/wyatt_form_bloc/lib/src/data/input_validators/password.dart index 8a6beab0..5ec35d91 100644 --- a/packages/wyatt_form_bloc/lib/src/validators/inputs/password.dart +++ b/packages/wyatt_form_bloc/lib/src/data/input_validators/password.dart @@ -14,18 +14,18 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -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 { /// {@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; diff --git a/packages/wyatt_form_bloc/lib/src/validators/inputs/phone.dart b/packages/wyatt_form_bloc/lib/src/data/input_validators/phone.dart similarity index 85% rename from packages/wyatt_form_bloc/lib/src/validators/inputs/phone.dart rename to packages/wyatt_form_bloc/lib/src/data/input_validators/phone.dart index 9f4dd1af..74c93c2e 100644 --- a/packages/wyatt_form_bloc/lib/src/validators/inputs/phone.dart +++ b/packages/wyatt_form_bloc/lib/src/data/input_validators/phone.dart @@ -14,15 +14,15 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -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 { /// {@macro phone} - const Phone.pure() : super.pure(); + const Phone.pure([super.value]) : super.pure(); /// {@macro phone} const Phone.dirty([super.value = '']) : super.dirty(); diff --git a/packages/wyatt_form_bloc/lib/src/validators/inputs/text_string.dart b/packages/wyatt_form_bloc/lib/src/data/input_validators/text_string.dart similarity index 84% rename from packages/wyatt_form_bloc/lib/src/validators/inputs/text_string.dart rename to packages/wyatt_form_bloc/lib/src/data/input_validators/text_string.dart index 61d0def3..9bf9e0be 100644 --- a/packages/wyatt_form_bloc/lib/src/validators/inputs/text_string.dart +++ b/packages/wyatt_form_bloc/lib/src/data/input_validators/text_string.dart @@ -14,15 +14,15 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -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 { /// {@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(); diff --git a/packages/wyatt_form_bloc/lib/src/domain/domain.dart b/packages/wyatt_form_bloc/lib/src/domain/domain.dart new file mode 100644 index 00000000..9eada269 --- /dev/null +++ b/packages/wyatt_form_bloc/lib/src/domain/domain.dart @@ -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 . + +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'; diff --git a/packages/wyatt_form_bloc/lib/src/form/form_input.dart b/packages/wyatt_form_bloc/lib/src/domain/entities/form_input.dart similarity index 59% rename from packages/wyatt_form_bloc/lib/src/form/form_input.dart rename to packages/wyatt_form_bloc/lib/src/domain/entities/form_input.dart index 6ab60d99..41eb70aa 100644 --- a/packages/wyatt_form_bloc/lib/src/form/form_input.dart +++ b/packages/wyatt_form_bloc/lib/src/domain/entities/form_input.dart @@ -15,27 +15,36 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -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> - extends Equatable { +// ignore: must_be_immutable +class FormInput< + Value, + Validator extends FormInputValidator, + Extra> extends Equatable implements Entity { final String key; final Validator validator; - final FormInputMetadata metadata; + late FormInputMetadata 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(export: true), - }); + FormInputMetadata? metadata, + }) { + this.metadata = metadata ?? FormInputMetadata(); + } - FormInput copyWith({ + FormInput copyWith({ String? key, Validator? validator, - FormInputMetadata? metadata, + FormInputMetadata? metadata, }) => FormInput( key ?? this.key, @@ -43,11 +52,12 @@ class FormInput> metadata: metadata ?? this.metadata, ); - FormInput clone() => copyWith( + FormInput clone() => copyWith( key: key, validator: validator, metadata: metadata, ); + @override bool? get stringify => true; diff --git a/packages/wyatt_form_bloc/lib/src/form/form_input_metadata.dart b/packages/wyatt_form_bloc/lib/src/domain/entities/form_input_metadata.dart similarity index 69% rename from packages/wyatt_form_bloc/lib/src/form/form_input_metadata.dart rename to packages/wyatt_form_bloc/lib/src/domain/entities/form_input_metadata.dart index bed46368..df21c950 100644 --- a/packages/wyatt_form_bloc/lib/src/form/form_input_metadata.dart +++ b/packages/wyatt_form_bloc/lib/src/domain/entities/form_input_metadata.dart @@ -15,33 +15,35 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -part of 'form.dart'; +import 'package:equatable/equatable.dart'; +import 'package:wyatt_architecture/wyatt_architecture.dart'; -class FormInputMetadata extends Equatable { +class FormInputMetadata 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 copyWith({ + FormInputMetadata copyWith({ bool? export, String? name, - T? extra, + Extra? extra, }) => - FormInputMetadata( + FormInputMetadata( export: export ?? this.export, - name: name ?? _name, + name: name ?? this.name, extra: extra ?? this.extra, ); - FormInputMetadata clone() => copyWith( + FormInputMetadata clone() => copyWith( export: export, - name: _name, + name: name, extra: extra, ); @@ -49,5 +51,5 @@ class FormInputMetadata extends Equatable { bool? get stringify => true; @override - List get props => [export, _name, extra]; + List get props => [export, name, extra]; } diff --git a/packages/wyatt_form_bloc/lib/src/domain/form/wyatt_form.dart b/packages/wyatt_form_bloc/lib/src/domain/form/wyatt_form.dart new file mode 100644 index 00000000..718e5be5 --- /dev/null +++ b/packages/wyatt_form_bloc/lib/src/domain/form/wyatt_form.dart @@ -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 . + +// 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 get inputs; + FormValidator get formValidationStrategy; + + bool containsKey(String key); + + FormInput inputOf(String key); + Validator validatorOf< + Validator extends FormInputValidator>( + String key, + ); + Error? errorOf(String key); + Value? valueOf(String key); + FormInputMetadata metadataOf(String key); + + List> + asValidatorList(); + + void updateValidator( + String key, + FormInputValidator dirtyValue, + ); + + void updateMetadata( + String key, + FormInputMetadata metadata, + ); + + FormStatus validate(); + WyattForm clone(); + WyattForm operationWith(FormOperation operation, WyattForm other); + WyattForm reset(); +} diff --git a/packages/wyatt_form_bloc/lib/src/enums/set_operations.dart b/packages/wyatt_form_bloc/lib/src/domain/form_encoders/form_encoder.dart similarity index 75% rename from packages/wyatt_form_bloc/lib/src/enums/set_operations.dart rename to packages/wyatt_form_bloc/lib/src/domain/form_encoders/form_encoder.dart index fa203b98..c69c226d 100644 --- a/packages/wyatt_form_bloc/lib/src/enums/set_operations.dart +++ b/packages/wyatt_form_bloc/lib/src/domain/form_encoders/form_encoder.dart @@ -14,16 +14,9 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -enum SetOperation { - /// Replace entire set with new set. - replace, - - /// Keep common elements between sets. - intersection, - - /// Remove common elements between sets. - difference, - - /// Add new elements to set. - union +import 'package:wyatt_form_bloc/src/domain/form/wyatt_form.dart'; + +// ignore: one_member_abstracts +abstract class FormEncoder { + Output encode(WyattForm form); } diff --git a/packages/wyatt_form_bloc/lib/src/domain/form_operations/form_operation.dart b/packages/wyatt_form_bloc/lib/src/domain/form_operations/form_operation.dart new file mode 100644 index 00000000..ab5f34de --- /dev/null +++ b/packages/wyatt_form_bloc/lib/src/domain/form_operations/form_operation.dart @@ -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 . + +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); +} diff --git a/packages/wyatt_form_bloc/lib/src/form/form_validator.dart b/packages/wyatt_form_bloc/lib/src/domain/form_validators/form_validator.dart similarity index 74% rename from packages/wyatt_form_bloc/lib/src/form/form_validator.dart rename to packages/wyatt_form_bloc/lib/src/domain/form_validators/form_validator.dart index 958d4e3e..45137ab7 100644 --- a/packages/wyatt_form_bloc/lib/src/form/form_validator.dart +++ b/packages/wyatt_form_bloc/lib/src/domain/form_validators/form_validator.dart @@ -14,7 +14,9 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -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() + bool isPure(WyattForm form) => form + .asValidatorList() .every((validator) => validator.pure); - FormStatus validate(FormData form); - - FormStatus rawValidate( - List> validators, - ); + FormStatus validate(WyattForm form); } diff --git a/packages/wyatt_form_bloc/lib/src/domain/input_validators/any_validator.dart b/packages/wyatt_form_bloc/lib/src/domain/input_validators/any_validator.dart new file mode 100644 index 00000000..0869d8b5 --- /dev/null +++ b/packages/wyatt_form_bloc/lib/src/domain/input_validators/any_validator.dart @@ -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 . + +part of 'form_input_validator.dart'; + +abstract class AnyValidator, + E extends ValidationError> extends FormInputValidator { + 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; + } +} diff --git a/packages/wyatt_form_bloc/lib/src/domain/input_validators/equality_validator.dart b/packages/wyatt_form_bloc/lib/src/domain/input_validators/equality_validator.dart new file mode 100644 index 00000000..531d55ab --- /dev/null +++ b/packages/wyatt_form_bloc/lib/src/domain/input_validators/equality_validator.dart @@ -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 . + +part of 'form_input_validator.dart'; + +/// {@template equality_validator} +/// Abstract equality validator validating a value when equals to another. +/// {@endtemplate} +abstract class EqualityValidator + extends FormInputValidator { + 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; + } +} diff --git a/packages/wyatt_form_bloc/lib/src/form/form_input_validator.dart b/packages/wyatt_form_bloc/lib/src/domain/input_validators/form_input_validator.dart similarity index 85% rename from packages/wyatt_form_bloc/lib/src/form/form_input_validator.dart rename to packages/wyatt_form_bloc/lib/src/domain/input_validators/form_input_validator.dart index 61f47cf3..16d2e21a 100644 --- a/packages/wyatt_form_bloc/lib/src/form/form_input_validator.dart +++ b/packages/wyatt_form_bloc/lib/src/domain/input_validators/form_input_validator.dart @@ -14,7 +14,15 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -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 +abstract class FormInputValidator 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 /// 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 /// 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; diff --git a/packages/wyatt_form_bloc/lib/src/validators/inputs/enum_validator.dart b/packages/wyatt_form_bloc/lib/src/domain/input_validators/nullable_validator.dart similarity index 57% rename from packages/wyatt_form_bloc/lib/src/validators/inputs/enum_validator.dart rename to packages/wyatt_form_bloc/lib/src/domain/input_validators/nullable_validator.dart index 63c3ef60..e478e2ec 100644 --- a/packages/wyatt_form_bloc/lib/src/validators/inputs/enum_validator.dart +++ b/packages/wyatt_form_bloc/lib/src/domain/input_validators/nullable_validator.dart @@ -14,19 +14,24 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -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 extends TextValidator { - /// {@macro text_string} - const EnumValidator.pure() : super.pure(); +/// {@template nullable_validator} +/// Abstract nullable validator not validating null values. +/// {@endtemplate} +abstract class NullableValidator + extends FormInputValidator { + 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; + } } diff --git a/packages/wyatt_form_bloc/lib/src/validators/inputs/base/regex_validator.dart b/packages/wyatt_form_bloc/lib/src/domain/input_validators/regex_validator.dart similarity index 80% rename from packages/wyatt_form_bloc/lib/src/validators/inputs/base/regex_validator.dart rename to packages/wyatt_form_bloc/lib/src/domain/input_validators/regex_validator.dart index 16d41ab2..971e9566 100644 --- a/packages/wyatt_form_bloc/lib/src/validators/inputs/base/regex_validator.dart +++ b/packages/wyatt_form_bloc/lib/src/domain/input_validators/regex_validator.dart @@ -14,20 +14,21 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -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 - extends FormInputValidator { - const RegexValidator.pure() : super.pure(''); - const RegexValidator.dirty([super.value = '']) : super.dirty(); + extends FormInputValidator { + 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; diff --git a/packages/wyatt_form_bloc/lib/src/validators/inputs/base/text_validator.dart b/packages/wyatt_form_bloc/lib/src/domain/input_validators/text_validator.dart similarity index 78% rename from packages/wyatt_form_bloc/lib/src/validators/inputs/base/text_validator.dart rename to packages/wyatt_form_bloc/lib/src/domain/input_validators/text_validator.dart index c57697f1..6e70e027 100644 --- a/packages/wyatt_form_bloc/lib/src/validators/inputs/base/text_validator.dart +++ b/packages/wyatt_form_bloc/lib/src/domain/input_validators/text_validator.dart @@ -14,13 +14,12 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -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 - extends FormInputValidator { - const TextValidator.pure() : super.pure(''); - const TextValidator.dirty([super.value = '']) : super.dirty(); + extends FormInputValidator { + 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; diff --git a/packages/wyatt_form_bloc/lib/src/form/form_data.dart b/packages/wyatt_form_bloc/lib/src/form/form_data.dart deleted file mode 100644 index 86e67940..00000000 --- a/packages/wyatt_form_bloc/lib/src/form/form_data.dart +++ /dev/null @@ -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 . - -part of 'form.dart'; - -class FormData extends Equatable { - final List _inputs; - - const FormData(this._inputs); - const FormData.empty() : this(const []); - - /// Returns all inputs as a list - List 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> validators() => - _inputs - .map>( - (input) => input.validator as FormInputValidator, - ) - .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(String key) => inputOf(key).validator as T; - - /// Updates validator of a given input. (perform copyWith) - void updateValidator( - String key, - FormInputValidator 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(String key) => - (inputOf(key).validator as FormInputValidator).error; - - /// The value of the associated [FormInputValidator]. For example, - /// if you have a FormInputValidator for FirstName, the value could be 'Joe'. - V valueOf(String key) => - (inputOf(key).validator as FormInputValidator).value; - - /// Returns `true` if the [FormInputValidator] is not valid. - /// Same as `E? errorOf(String key) != null` - bool isNotValid(String key) => !inputOf(key).validator.valid; - - /// Returns all associated metadata as a list - List> metadata() => _inputs - .map>( - (input) => input.metadata as FormInputMetadata, - ) - .toList(); - - /// Returns the metadata associated. With `M` the type of extra data. - FormInputMetadata metadataOf(String key) => - inputOf(key).metadata as FormInputMetadata; - - /// Updates metadata of a given input. (perform copyWith) - void updateMetadata(String key, FormInputMetadata 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 inputs = []; - - 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 inputs = []; - - 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 inputs = []; - - 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 toMap() { - final map = {}; - 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 get props => _inputs; -} diff --git a/packages/wyatt_form_bloc/lib/src/presentation/features/form_data/form_data_cubit.dart b/packages/wyatt_form_bloc/lib/src/presentation/features/form_data/form_data_cubit.dart new file mode 100644 index 00000000..50ca2c34 --- /dev/null +++ b/packages/wyatt_form_bloc/lib/src/presentation/features/form_data/form_data_cubit.dart @@ -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 . + +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 extends Cubit { + FormDataCubit(super.initialState) : super(); + + FutureOr dataChanged( + String key, + FormInputValidator dirtyValue, + ); + + FutureOr update( + WyattForm form, { + SetOperation operation = SetOperation.replace, + }); + + FutureOr validate(); + + FutureOr reset(); + + FutureOr submit(); +} diff --git a/packages/wyatt_form_bloc/lib/src/cubit/form_data_state.dart b/packages/wyatt_form_bloc/lib/src/presentation/features/form_data/form_data_state.dart similarity index 68% rename from packages/wyatt_form_bloc/lib/src/cubit/form_data_state.dart rename to packages/wyatt_form_bloc/lib/src/presentation/features/form_data/form_data_state.dart index 4629b0e6..f898d4bf 100644 --- a/packages/wyatt_form_bloc/lib/src/cubit/form_data_state.dart +++ b/packages/wyatt_form_bloc/lib/src/presentation/features/form_data/form_data_state.dart @@ -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 get props => [status, data, errorMessage]; } diff --git a/packages/wyatt_form_bloc/lib/src/presentation/features/form_data_impl/form_data_cubit_impl.dart b/packages/wyatt_form_bloc/lib/src/presentation/features/form_data_impl/form_data_cubit_impl.dart new file mode 100644 index 00000000..b5729d71 --- /dev/null +++ b/packages/wyatt_form_bloc/lib/src/presentation/features/form_data_impl/form_data_cubit_impl.dart @@ -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 . + +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 { + FormDataCubitImpl(WyattForm form) : super(FormDataStateImpl(form: form)); + + @override + FutureOr dataChanged( + String key, + FormInputValidator dirtyValue, + ) { + final form = state.form.clone(); + + try { + form.updateValidator(key, dirtyValue); + } catch (e) { + rethrow; + } + + emit( + state.copyWith( + form: form, + status: form.validate(), + ), + ); + } + + @override + FutureOr 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 reset() { + final form = state.form.reset(); + emit( + state.copyWith( + form: form, + status: form.validate(), + ), + ); + } + + @override + FutureOr validate() { + emit( + state.copyWith( + status: state.form.validate(), + ), + ); + } +} diff --git a/packages/wyatt_form_bloc/lib/src/presentation/features/form_data_impl/form_data_state_impl.dart b/packages/wyatt_form_bloc/lib/src/presentation/features/form_data_impl/form_data_state_impl.dart new file mode 100644 index 00000000..37cb8e5a --- /dev/null +++ b/packages/wyatt_form_bloc/lib/src/presentation/features/form_data_impl/form_data_state_impl.dart @@ -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 . + +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 get props => [status, form, errorMessage]; +} diff --git a/packages/wyatt_form_bloc/lib/src/presentation/features/widgets/input_builder.dart b/packages/wyatt_form_bloc/lib/src/presentation/features/widgets/input_builder.dart new file mode 100644 index 00000000..b23e33d2 --- /dev/null +++ b/packages/wyatt_form_bloc/lib/src/presentation/features/widgets/input_builder.dart @@ -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 . + +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:wyatt_form_bloc/wyatt_form_bloc.dart'; + +class InputBuilder 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( + builder: (context, state) { + final cubit = context.read(); + final inputValid = state.form.validatorOf(field).valid; + return builder.call(context, cubit, state, field, inputValid); + }, + ); +} diff --git a/packages/wyatt_form_bloc/lib/src/presentation/features/widgets/input_builder_metadata.dart b/packages/wyatt_form_bloc/lib/src/presentation/features/widgets/input_builder_metadata.dart new file mode 100644 index 00000000..123287fc --- /dev/null +++ b/packages/wyatt_form_bloc/lib/src/presentation/features/widgets/input_builder_metadata.dart @@ -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 . + +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 + 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? metadata, + ) builder; + + @override + Widget build(BuildContext context) => BlocBuilder( + builder: (context, state) { + final cubit = context.read(); + final inputValid = state.form.validatorOf(field).valid; + final metadata = state.form.metadataOf(field); + return builder.call( + context, + cubit, + state, + field, + inputValid, + metadata, + ); + }, + ); +} diff --git a/packages/wyatt_form_bloc/lib/src/presentation/features/widgets/submit_builder.dart b/packages/wyatt_form_bloc/lib/src/presentation/features/widgets/submit_builder.dart new file mode 100644 index 00000000..ed1abd42 --- /dev/null +++ b/packages/wyatt_form_bloc/lib/src/presentation/features/widgets/submit_builder.dart @@ -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 . + +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 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( + buildWhen: (previous, current) => previous.status != current.status, + builder: (context, state) => builder.call( + context, + context.read(), + state.status, + ), + ); +} diff --git a/packages/wyatt_form_bloc/lib/src/presentation/presentation.dart b/packages/wyatt_form_bloc/lib/src/presentation/presentation.dart new file mode 100644 index 00000000..31292d02 --- /dev/null +++ b/packages/wyatt_form_bloc/lib/src/presentation/presentation.dart @@ -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 . + +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'; diff --git a/packages/wyatt_form_bloc/lib/src/src.dart b/packages/wyatt_form_bloc/lib/src/src.dart index dd7f4605..c75563a1 100644 --- a/packages/wyatt_form_bloc/lib/src/src.dart +++ b/packages/wyatt_form_bloc/lib/src/src.dart @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -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'; diff --git a/packages/wyatt_form_bloc/lib/src/validators/inputs/iban.dart b/packages/wyatt_form_bloc/lib/src/validators/inputs/iban.dart deleted file mode 100644 index 74bef3e3..00000000 --- a/packages/wyatt_form_bloc/lib/src/validators/inputs/iban.dart +++ /dev/null @@ -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 . - -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 { - /// {@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}))$', - ); -} diff --git a/packages/wyatt_form_bloc/lib/src/validators/inputs/list_option.dart b/packages/wyatt_form_bloc/lib/src/validators/inputs/list_option.dart deleted file mode 100644 index 00ddd74b..00000000 --- a/packages/wyatt_form_bloc/lib/src/validators/inputs/list_option.dart +++ /dev/null @@ -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 . - -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 - extends FormInputValidator, ValidationStandardError> { - /// {@macro list_option} - const ListOption.pure({List? defaultValue}) - : super.pure(defaultValue ?? const []); - - /// {@macro list_option} - const ListOption.dirty({List? value}) : super.dirty(value ?? const []); - - ListOption select(T? v) { - if (v == null) { - return this; - } - if (value.contains(v)) { - final List newValue = List.from(value)..remove(v); - return ListOption.dirty(value: newValue); - } else { - final List newValue = List.from(value)..add(v); - return ListOption.dirty(value: newValue); - } - } - - @override - ValidationStandardError? validator(List? value) => - value?.isNotEmpty ?? false == true - ? null - : ValidationStandardError.invalid; -} diff --git a/packages/wyatt_form_bloc/pubspec.yaml b/packages/wyatt_form_bloc/pubspec.yaml index 7b1e7c2a..7d9e5906 100644 --- a/packages/wyatt_form_bloc/pubspec.yaml +++ b/packages/wyatt_form_bloc/pubspec.yaml @@ -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: