diff --git a/packages/wyatt_form_bloc/lib/src/cubit/form_cubit.dart b/packages/wyatt_form_bloc/lib/src/cubit/form_cubit.dart new file mode 100644 index 00000000..98878f86 --- /dev/null +++ b/packages/wyatt_form_bloc/lib/src/cubit/form_cubit.dart @@ -0,0 +1,90 @@ +// 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:meta/meta.dart'; +import 'package:wyatt_form_bloc/src/form/form.dart'; +import 'package:wyatt_form_bloc/src/utils/set_operations.dart'; + +part 'form_state.dart'; + +class FormCubit extends Cubit { + final Future Function(FormState state)? _onSubmit; + + FormCubit({ + required FormData entries, + Future Function(FormState state)? onSubmit, + }) : _onSubmit = onSubmit, + super(FormState(data: entries)); + + void dataChanged(String field, FormInput dirtyValue) { + final _form = state.data.clone(); + + if (_form.contains(field)) { + _form.update(field, dirtyValue); + } else { + throw Exception('Form field $field not found'); + } + + emit( + state.copyWith( + data: _form, + status: FormValidator.validate(_form.inputs()), + ), + ); + } + + 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: FormValidator.validate(_form.inputs()), + ), + ); + } + + Future submitForm() async { + unawaited( + _onSubmit?.call(state).then((bool reemit) { + if (reemit) { + emit(state); + } + }), + ); + } +} diff --git a/packages/wyatt_form_bloc/lib/src/cubit/form_state.dart b/packages/wyatt_form_bloc/lib/src/cubit/form_state.dart new file mode 100644 index 00000000..a5c29a49 --- /dev/null +++ b/packages/wyatt_form_bloc/lib/src/cubit/form_state.dart @@ -0,0 +1,63 @@ +// 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_cubit.dart'; + +@immutable +class FormState { + final FormStatus status; + final FormData data; + final String? errorMessage; + + const FormState({ + required this.data, + this.status = FormStatus.pure, + this.errorMessage, + }); + + FormState copyWith({ + FormStatus? status, + FormData? data, + String? errorMessage, + }) { + return FormState( + status: status ?? this.status, + data: data ?? this.data, + errorMessage: errorMessage ?? this.errorMessage, + ); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) return true; + + return other is FormState && + other.status == status && + other.data == data && + other.errorMessage == errorMessage; + } + + @override + int get hashCode { + return status.hashCode ^ data.hashCode ^ errorMessage.hashCode; + } + + @override + String toString() { + return 'FormState(status: $status, data: $data, ' + 'errorMessage: $errorMessage)'; + } +} diff --git a/packages/wyatt_form_bloc/lib/src/form/form.dart b/packages/wyatt_form_bloc/lib/src/form/form.dart new file mode 100644 index 00000000..62dfe624 --- /dev/null +++ b/packages/wyatt_form_bloc/lib/src/form/form.dart @@ -0,0 +1,22 @@ +// 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_data.dart'; +export 'form_entry.dart'; +export 'form_input.dart'; +export 'form_input_status.dart'; +export 'form_status.dart'; +export 'form_validator.dart'; diff --git a/packages/wyatt_form_bloc/lib/src/form/form_data.dart b/packages/wyatt_form_bloc/lib/src/form/form_data.dart new file mode 100644 index 00000000..d5e40442 --- /dev/null +++ b/packages/wyatt_form_bloc/lib/src/form/form_data.dart @@ -0,0 +1,137 @@ +// 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:meta/meta.dart'; +import 'package:wyatt_form_bloc/src/form/form.dart'; +import 'package:wyatt_form_bloc/src/utils/list_equals.dart'; +import 'package:wyatt_form_bloc/src/validators/validators.dart'; + +@immutable +class FormData { + const FormData(this._entries); + + FormData.empty() : this([]); + + final List _entries; + + List get entries => _entries; + + List> inputs() { + return _entries + .map((FormEntry entry) => entry.input as FormInput) + .toList(); + } + + FormInput input(String field) { + if (contains(field)) { + return _entries + .firstWhere((FormEntry entry) => entry.field == field) + .input as FormInput; + } else { + throw Exception('Field $field does not exist in form'); + } + } + + bool contains(String field) { + return _entries.any((FormEntry entry) => entry.field == field); + } + + FormData intersection(FormData other) { + final List entries = []; + + for (final FormEntry entry in _entries) { + if (other.contains(entry.field)) { + entries.add(entry); + } + } + + return FormData(entries); + } + + FormData difference(FormData other) { + final List entries = []; + + for (final FormEntry otherEntry in other._entries) { + if (!contains(otherEntry.field)) { + entries.add(otherEntry); + } + } + + for (final FormEntry entry in _entries) { + if (!other.contains(entry.field)) { + entries.add(entry); + } + } + + return FormData(entries); + } + + FormData union(FormData other) { + final List entries = []; + + for (final FormEntry entry in _entries) { + entries.add(entry); + } + + for (final FormEntry otherEntry in other._entries) { + if (!contains(otherEntry.field)) { + entries.add(otherEntry); + } + } + + return FormData(entries); + } + + void update(String field, FormInput input) { + if (contains(field)) { + final index = _entries.indexOf( + _entries.firstWhere((FormEntry entry) => entry.field == field), + ); + _entries[index] = _entries[index].copyWith(input: input); + } + } + + FormData clone() { + return FormData( + _entries + .map((FormEntry entry) => entry.clone()) + .toList(), + ); + } + + Map toMap() { + final map = {}; + for (final entry in _entries) { + if (entry.export) { + map[entry.fieldName ?? entry.field] = entry.input.value; + } + } + return map; + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) return true; + + return other is FormData && listEquals(other._entries, _entries); + } + + @override + int get hashCode => _entries.hashCode; + + @override + String toString() => 'FormData(entries: $_entries)'; +} diff --git a/packages/wyatt_form_bloc/lib/src/form/form_entry.dart b/packages/wyatt_form_bloc/lib/src/form/form_entry.dart new file mode 100644 index 00000000..1cf3b799 --- /dev/null +++ b/packages/wyatt_form_bloc/lib/src/form/form_entry.dart @@ -0,0 +1,76 @@ +// 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:meta/meta.dart'; +import 'package:wyatt_form_bloc/src/form/form.dart'; + +@immutable +class FormEntry { + const FormEntry(this.field, this.input, {this.export = true, this.fieldName}); + + final String field; + final FormInput input; + final bool export; + final String? fieldName; + + FormEntry copyWith({ + String? field, + FormInput? input, + bool? export, + String? fieldName, + }) { + return FormEntry( + field ?? this.field, + input ?? this.input, + export: export ?? this.export, + fieldName: fieldName ?? this.fieldName, + ); + } + + FormEntry clone() { + return FormEntry( + field, + input, + export: export, + fieldName: fieldName, + ); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) return true; + + return other is FormEntry && + other.field == field && + other.input == input && + other.export == export && + other.fieldName == fieldName; + } + + @override + int get hashCode { + return field.hashCode ^ + input.hashCode ^ + export.hashCode ^ + fieldName.hashCode; + } + + @override + String toString() { + return 'FormEntry(field: $field, input: $input, ' + 'export: $export, fieldName: $fieldName)'; + } +} diff --git a/packages/wyatt_form_bloc/lib/src/form/form_input.dart b/packages/wyatt_form_bloc/lib/src/form/form_input.dart new file mode 100644 index 00000000..3759106a --- /dev/null +++ b/packages/wyatt_form_bloc/lib/src/form/form_input.dart @@ -0,0 +1,111 @@ +// 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:meta/meta.dart'; +import 'package:wyatt_form_bloc/src/form/form.dart'; + +/// {@template form_input} +/// A [FormInput] represents the value of a single form input field. +/// It contains information about the [FormInputStatus], [value], as well +/// as validation status. +/// +/// [FormInput] should be extended to define custom [FormInput] instances. +/// +/// ```dart +/// enum FirstNameError { empty } +/// class FirstName extends FormInput { +/// const FirstName.pure({String value = ''}) : super.pure(value); +/// const FirstName.dirty({String value = ''}) : super.dirty(value); +/// +/// @override +/// FirstNameError? validator(String value) { +/// return value.isEmpty ? FirstNameError.empty : null; +/// } +/// } +/// ``` +/// {@endtemplate} +@immutable +abstract class FormInput { + const FormInput._(this.value, [this.pure = true]); + + /// Constructor which create a `pure` [FormInput] with a given value. + const FormInput.pure(T value) : this._(value); + + /// Constructor which create a `dirty` [FormInput] with a given value. + const FormInput.dirty(T value) : this._(value, false); + + /// The value of the given [FormInput]. + /// For example, if you have a `FormInput` for `FirstName`, + /// the value could be 'Joe'. + final T value; + + /// If the [FormInput] is pure (has been touched/modified). + /// Typically when the `FormInput` is initially created, + /// it is created using the `FormInput.pure` constructor to + /// signify that the user has not modified it. + /// + /// For subsequent changes (in response to user input), the + /// `FormInput.dirty` constructor should be used to signify that + /// the `FormInput` has been manipulated. + final bool pure; + + /// The [FormInputStatus] which can be one of the following: + /// * [FormInputStatus.pure] + /// - if the input has not been modified. + /// * [FormInputStatus.invalid] + /// - if the input has been modified and validation failed. + /// * [FormInputStatus.valid] + /// - if the input has been modified and validation succeeded. + FormInputStatus get status => pure + ? FormInputStatus.pure + : valid + ? FormInputStatus.valid + : FormInputStatus.invalid; + + /// Returns a validation error if the [FormInput] is invalid. + /// Returns `null` if the [FormInput] is valid. + E? get error => validator(value); + + /// Whether the [FormInput] value is valid according to the + /// overridden `validator`. + /// + /// Returns `true` if `validator` returns `null` for the + /// current [FormInput] value and `false` otherwise. + bool get valid => validator(value) == null; + + /// Whether the [FormInput] value is not valid. + /// A value is invalid when the overridden `validator` + /// returns an error (non-null value). + bool get invalid => status == FormInputStatus.invalid; + + /// A function that must return a validation error if the provided + /// [value] is invalid and `null` otherwise. + E? validator(T value); + + @override + int get hashCode => value.hashCode ^ pure.hashCode; + + @override + bool operator ==(Object other) { + if (other.runtimeType != runtimeType) return false; + return other is FormInput && + other.value == value && + other.pure == pure; + } + + @override + String toString() => '$runtimeType($value, $pure)'; +} diff --git a/packages/wyatt_form_bloc/lib/src/form/form_input_status.dart b/packages/wyatt_form_bloc/lib/src/form/form_input_status.dart new file mode 100644 index 00000000..f8abb53e --- /dev/null +++ b/packages/wyatt_form_bloc/lib/src/form/form_input_status.dart @@ -0,0 +1,27 @@ +// 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 . + +/// Enum representing the status of a form input at any given point in time. +enum FormInputStatus { + /// The form input has not been touched. + pure, + + /// The form input is valid. + valid, + + /// The form input is not valid. + invalid, +} diff --git a/packages/wyatt_form_bloc/lib/src/form/form_status.dart b/packages/wyatt_form_bloc/lib/src/form/form_status.dart new file mode 100644 index 00000000..8e0709be --- /dev/null +++ b/packages/wyatt_form_bloc/lib/src/form/form_status.dart @@ -0,0 +1,79 @@ +// 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 . + +/// Enum representing the status of a form at any given point in time. +enum FormStatus { + /// The form has not been touched. + pure, + + /// The form has been completely validated. + valid, + + /// The form contains one or more invalid inputs. + invalid, + + /// The form is in the process of being submitted. + submissionInProgress, + + /// The form has been submitted successfully. + submissionSuccess, + + /// The form submission failed. + submissionFailure, + + /// The form submission has been canceled. + submissionCanceled +} + +const Set _validatedFormStatuses = { + FormStatus.valid, + FormStatus.submissionInProgress, + FormStatus.submissionSuccess, + FormStatus.submissionFailure, + FormStatus.submissionCanceled, +}; + +/// Useful extensions on [FormStatus] +extension FormStatusX on FormStatus { + /// Indicates whether the form is untouched. + bool get isPure => this == FormStatus.pure; + + /// Indicates whether the form is completely validated. + bool get isValid => this == FormStatus.valid; + + /// Indicates whether the form has been validated successfully. + /// This means the [FormStatus] is either: + /// * `FormStatus.valid` + /// * `FormStatus.submissionInProgress` + /// * `FormStatus.submissionSuccess` + /// * `FormStatus.submissionFailure` + bool get isValidated => _validatedFormStatuses.contains(this); + + /// Indicates whether the form contains one or more invalid inputs. + bool get isInvalid => this == FormStatus.invalid; + + /// Indicates whether the form is in the process of being submitted. + bool get isSubmissionInProgress => this == FormStatus.submissionInProgress; + + /// Indicates whether the form has been submitted successfully. + bool get isSubmissionSuccess => this == FormStatus.submissionSuccess; + + /// Indicates whether the form submission failed. + bool get isSubmissionFailure => this == FormStatus.submissionFailure; + + /// Indicates whether the form submission has been canceled. + bool get isSubmissionCanceled => this == FormStatus.submissionCanceled; +} diff --git a/packages/wyatt_form_bloc/lib/src/form/form_validator.dart b/packages/wyatt_form_bloc/lib/src/form/form_validator.dart new file mode 100644 index 00000000..07fae1ec --- /dev/null +++ b/packages/wyatt_form_bloc/lib/src/form/form_validator.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 'package:wyatt_form_bloc/src/form/form.dart'; + +/// Class which contains methods that help manipulate and manage +/// [FormStatus] and [FormInputStatus] instances. +class FormValidator { + /// Returns a [FormStatus] given a list of [FormInput]. + static FormStatus validate(List inputs) { + return inputs.every((FormInput element) => element.pure) + ? FormStatus.pure + : inputs.any((FormInput input) => input.valid == false) + ? FormStatus.invalid + : FormStatus.valid; + } +} diff --git a/packages/wyatt_form_bloc/lib/src/src.dart b/packages/wyatt_form_bloc/lib/src/src.dart new file mode 100644 index 00000000..cd8aa4f1 --- /dev/null +++ b/packages/wyatt_form_bloc/lib/src/src.dart @@ -0,0 +1,19 @@ +// 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/form.dart'; +export 'utils/set_operations.dart'; +export 'validators/validators.dart'; diff --git a/packages/wyatt_form_bloc/lib/src/utils/list_equals.dart b/packages/wyatt_form_bloc/lib/src/utils/list_equals.dart new file mode 100644 index 00000000..e7d1989c --- /dev/null +++ b/packages/wyatt_form_bloc/lib/src/utils/list_equals.dart @@ -0,0 +1,25 @@ +// 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 . + +bool listEquals(List? a, List? b) { + if (a == null) return b == null; + if (b == null || a.length != b.length) return false; + if (identical(a, b)) return true; + for (int index = 0; index < a.length; index += 1) { + if (a[index] != b[index]) return false; + } + return true; +} diff --git a/packages/wyatt_form_bloc/lib/src/utils/set_operations.dart b/packages/wyatt_form_bloc/lib/src/utils/set_operations.dart new file mode 100644 index 00000000..09658a52 --- /dev/null +++ b/packages/wyatt_form_bloc/lib/src/utils/set_operations.dart @@ -0,0 +1,17 @@ +// 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 . + +enum SetOperation { replace, intersection, difference, union } diff --git a/packages/wyatt_form_bloc/lib/src/validators/boolean.dart b/packages/wyatt_form_bloc/lib/src/validators/boolean.dart new file mode 100644 index 00000000..6d38b598 --- /dev/null +++ b/packages/wyatt_form_bloc/lib/src/validators/boolean.dart @@ -0,0 +1,35 @@ +// 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/form/form.dart'; +import 'package:wyatt_form_bloc/src/validators/validators.dart'; + +/// {@template boolean} +/// Form input for a bool input +/// {@endtemplate} +class Boolean extends FormInput { + /// {@macro boolean} + const Boolean.pure({bool? defaultValue = false}) + : super.pure(defaultValue ?? false); + + /// {@macro boolean} + const Boolean.dirty({bool value = false}) : super.dirty(value); + + @override + ValidationError? validator(bool? value) { + return value != null ? null : ValidationError.invalid; + } +} diff --git a/packages/wyatt_form_bloc/lib/src/validators/confirmed_password.dart b/packages/wyatt_form_bloc/lib/src/validators/confirmed_password.dart new file mode 100644 index 00000000..99bbc9cf --- /dev/null +++ b/packages/wyatt_form_bloc/lib/src/validators/confirmed_password.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/form/form.dart'; +import 'package:wyatt_form_bloc/src/validators/validators.dart'; + +/// {@template confirmed_password} +/// Form input for a confirmed password input. +/// {@endtemplate} +class ConfirmedPassword + extends FormInput { + /// {@macro confirmed_password} + const ConfirmedPassword.pure({this.password = ''}) : super.pure(''); + + /// {@macro confirmed_password} + const ConfirmedPassword.dirty({required this.password, String value = ''}) + : super.dirty(value); + + /// The original password. + final String password; + + @override + ValidationError? validator(String? value) { + return password == value ? null : ValidationError.invalid; + } +} diff --git a/packages/wyatt_form_bloc/lib/src/validators/email.dart b/packages/wyatt_form_bloc/lib/src/validators/email.dart new file mode 100644 index 00000000..24bbafcf --- /dev/null +++ b/packages/wyatt_form_bloc/lib/src/validators/email.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/form/form.dart'; +import 'package:wyatt_form_bloc/src/validators/validators.dart'; + +/// {@template email} +/// Form input for an email input. +/// {@endtemplate} +class Email extends FormInput { + /// {@macro email} + const Email.pure() : super.pure(''); + + /// {@macro email} + const Email.dirty([String value = '']) : super.dirty(value); + + static final RegExp _emailRegExp = RegExp( + r'^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$', + ); + + @override + ValidationError? validator(String? value) { + return _emailRegExp.hasMatch(value ?? '') + ? null + : ValidationError.invalid; + } +} diff --git a/packages/wyatt_form_bloc/lib/src/validators/iban.dart b/packages/wyatt_form_bloc/lib/src/validators/iban.dart new file mode 100644 index 00000000..23a9d476 --- /dev/null +++ b/packages/wyatt_form_bloc/lib/src/validators/iban.dart @@ -0,0 +1,38 @@ +// 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/form/form.dart'; +import 'package:wyatt_form_bloc/src/validators/validators.dart'; + +/// {@template iban} +/// Form input for an IBAN input. +/// {@endtemplate} +class Iban extends FormInput { + /// {@macro iban} + const Iban.pure() : super.pure(''); + + /// {@macro iban} + const Iban.dirty([String value = '']) : super.dirty(value); + + static final RegExp _regExp = 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}))$', + ); + + @override + ValidationError? validator(String? value) { + return _regExp.hasMatch(value ?? '') ? null : ValidationError.invalid; + } +} diff --git a/packages/wyatt_form_bloc/lib/src/validators/name.dart b/packages/wyatt_form_bloc/lib/src/validators/name.dart new file mode 100644 index 00000000..00dde6c6 --- /dev/null +++ b/packages/wyatt_form_bloc/lib/src/validators/name.dart @@ -0,0 +1,36 @@ +// 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/form/form.dart'; +import 'package:wyatt_form_bloc/src/validators/validators.dart'; + +/// {@template name} +/// Form input for a name input. +/// {@endtemplate} +class Name extends FormInput { + /// {@macro name} + const Name.pure() : super.pure(''); + + /// {@macro name} + const Name.dirty([String value = '']) : super.dirty(value); + + static final RegExp _nameRegExp = RegExp(r"^([ \u00c0-\u01ffa-zA-Z'\-])+$"); + + @override + ValidationError? validator(String? value) { + return _nameRegExp.hasMatch(value ?? '') ? null : ValidationError.invalid; + } +} diff --git a/packages/wyatt_form_bloc/lib/src/validators/password.dart b/packages/wyatt_form_bloc/lib/src/validators/password.dart new file mode 100644 index 00000000..c755dab9 --- /dev/null +++ b/packages/wyatt_form_bloc/lib/src/validators/password.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/form/form.dart'; +import 'package:wyatt_form_bloc/src/validators/validators.dart'; + +/// {@template password} +/// Form input for a password input. +/// {@endtemplate} +class Password extends FormInput { + /// {@macro password} + const Password.pure() : super.pure(''); + + /// {@macro password} + const Password.dirty([String value = '']) : super.dirty(value); + + static final RegExp _passwordRegExp = + RegExp(r'^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]{8,}$'); + + @override + ValidationError? validator(String? value) { + return _passwordRegExp.hasMatch(value ?? '') + ? null + : ValidationError.invalid; + } +} diff --git a/packages/wyatt_form_bloc/lib/src/validators/phone.dart b/packages/wyatt_form_bloc/lib/src/validators/phone.dart new file mode 100644 index 00000000..7c3ca018 --- /dev/null +++ b/packages/wyatt_form_bloc/lib/src/validators/phone.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:wyatt_form_bloc/src/form/form.dart'; +import 'package:wyatt_form_bloc/src/validators/validators.dart'; + +/// {@template phone} +/// Form input for a phone input. +/// {@endtemplate} +class Phone extends FormInput { + /// {@macro phone} + const Phone.pure() : super.pure(''); + + /// {@macro phone} + const Phone.dirty([String value = '']) : super.dirty(value); + + static final RegExp _phoneRegExp = + RegExp(r'^[\+]?[(]?[0-9]{3}[)]?[-\s\.]?[0-9]{3}[-\s\.]?[0-9]{4,6}$'); + + @override + ValidationError? validator(String? value) { + return _phoneRegExp.hasMatch(value ?? '') ? null : ValidationError.invalid; + } +} diff --git a/packages/wyatt_form_bloc/lib/src/validators/siren.dart b/packages/wyatt_form_bloc/lib/src/validators/siren.dart new file mode 100644 index 00000000..5e2b2864 --- /dev/null +++ b/packages/wyatt_form_bloc/lib/src/validators/siren.dart @@ -0,0 +1,36 @@ +// 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/form/form.dart'; +import 'package:wyatt_form_bloc/src/validators/validators.dart'; + +/// {@template siren} +/// Form input for a SIREN input. +/// {@endtemplate} +class Siren extends FormInput { + /// {@macro siren} + const Siren.pure() : super.pure(''); + + /// {@macro siren} + const Siren.dirty([String value = '']) : super.dirty(value); + + static final RegExp _regExp = RegExp(r'(\d{9}|\d{3}[ ]\d{3}[ ]\d{3})$'); + + @override + ValidationError? validator(String? value) { + return _regExp.hasMatch(value ?? '') ? null : ValidationError.invalid; + } +} diff --git a/packages/wyatt_form_bloc/lib/src/validators/text_string.dart b/packages/wyatt_form_bloc/lib/src/validators/text_string.dart new file mode 100644 index 00000000..137cf0cb --- /dev/null +++ b/packages/wyatt_form_bloc/lib/src/validators/text_string.dart @@ -0,0 +1,36 @@ +// 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/form/form.dart'; +import 'package:wyatt_form_bloc/src/validators/validators.dart'; + +/// {@template text_string} +/// Form input for a text input +/// {@endtemplate} +class TextString extends FormInput { + /// {@macro text_string} + const TextString.pure() : super.pure(''); + + /// {@macro text_string} + const TextString.dirty([String value = '']) : super.dirty(value); + + @override + ValidationError? validator(String? value) { + return (value?.isNotEmpty ?? false) + ? null + : ValidationError.invalid; + } +} diff --git a/packages/wyatt_form_bloc/lib/src/validators/validation_error.dart b/packages/wyatt_form_bloc/lib/src/validators/validation_error.dart new file mode 100644 index 00000000..d5ae6088 --- /dev/null +++ b/packages/wyatt_form_bloc/lib/src/validators/validation_error.dart @@ -0,0 +1,20 @@ +// 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 . + +enum ValidationError { + /// Generic invalid error. + invalid +} diff --git a/packages/wyatt_form_bloc/lib/src/validators/validators.dart b/packages/wyatt_form_bloc/lib/src/validators/validators.dart new file mode 100644 index 00000000..4a4a3fb5 --- /dev/null +++ b/packages/wyatt_form_bloc/lib/src/validators/validators.dart @@ -0,0 +1,26 @@ +// 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 'boolean.dart'; +export 'confirmed_password.dart'; +export 'email.dart'; +export 'iban.dart'; +export 'name.dart'; +export 'password.dart'; +export 'phone.dart'; +export 'siren.dart'; +export 'text_string.dart'; +export 'validation_error.dart'; diff --git a/packages/wyatt_form_bloc/lib/wyatt_form_bloc.dart b/packages/wyatt_form_bloc/lib/wyatt_form_bloc.dart new file mode 100644 index 00000000..066e4a1f --- /dev/null +++ b/packages/wyatt_form_bloc/lib/wyatt_form_bloc.dart @@ -0,0 +1,20 @@ +// 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 . + +/// A form library for BLoC. +library wyatt_form_bloc; + +export 'src/src.dart';