|
1 | 1 | import 'package:codelessly_json_annotation/codelessly_json_annotation.dart'; |
2 | 2 | import 'package:equatable/equatable.dart'; |
3 | 3 |
|
4 | | -import '../mixins.dart'; |
| 4 | +import '../../../codelessly_api.dart'; |
5 | 5 |
|
6 | 6 | part 'text_input_formatter_model.g.dart'; |
7 | 7 |
|
| 8 | +/// Represents the type of formatter. |
| 9 | +enum TextInputFormatterType { |
| 10 | + /// Equivalent to no formatter. |
| 11 | + none, |
| 12 | + |
| 13 | + /// A formatter that uses a regular expression to match the text. |
| 14 | + regex, |
| 15 | +} |
| 16 | + |
8 | 17 | /// Text formatters that can be applied to the text field input to restrict the |
9 | 18 | /// input to a specific format. |
| 19 | +sealed class TextInputFormatterModel with EquatableMixin, SerializableMixin { |
| 20 | + /// The name of the formatter. |
| 21 | + final String name; |
| 22 | + |
| 23 | + /// The type of formatter. |
| 24 | + @JsonKey(required: true, includeToJson: true) |
| 25 | + final TextInputFormatterType type; |
| 26 | + |
| 27 | + /// Creates a new [TextInputFormatterModel] instance. |
| 28 | + const TextInputFormatterModel({required this.name, required this.type}); |
| 29 | + |
| 30 | + /// Creates a [TextInputFormatterModel] instance from a JSON object. |
| 31 | + factory TextInputFormatterModel.fromJson(Map<String, dynamic> json) { |
| 32 | + final TextInputFormatterType type = |
| 33 | + TextInputFormatterType.values.byName(json['type']); |
| 34 | + return switch (type) { |
| 35 | + TextInputFormatterType.none => NoneTextInputFormatter.fromJson(json), |
| 36 | + TextInputFormatterType.regex => |
| 37 | + RegexTextInputFormatterModel.fromJson(json), |
| 38 | + }; |
| 39 | + } |
| 40 | + |
| 41 | + /// A list of all available text field formatters. |
| 42 | + static const List<TextInputFormatterModel> formatters = [ |
| 43 | + NoneTextInputFormatter(), |
| 44 | + ...RegexTextInputFormatterModel.formatters, |
| 45 | + ]; |
| 46 | + |
| 47 | + /// A list of all available text field formatter names. |
| 48 | + static List<String> formatterNames = [ |
| 49 | + for (final formatter in formatters) formatter.name, |
| 50 | + ]; |
| 51 | + |
| 52 | + @override |
| 53 | + List<Object?> get props => [name, type]; |
| 54 | +} |
| 55 | + |
| 56 | +/// A formatter than does not restrict the input in any way. |
10 | 57 | @JsonSerializable() |
11 | | -class TextInputFormatterModel with EquatableMixin, SerializableMixin { |
| 58 | +class NoneTextInputFormatter extends TextInputFormatterModel { |
| 59 | + /// Creates a new [NoneTextInputFormatter] instance. |
| 60 | + const NoneTextInputFormatter() |
| 61 | + : super(name: 'None', type: TextInputFormatterType.none); |
| 62 | + |
| 63 | + /// Creates a [TextInputFormatterModel] instance from a JSON object. |
| 64 | + factory NoneTextInputFormatter.fromJson(Map<String, dynamic> json) => |
| 65 | + _$NoneTextInputFormatterFromJson(json); |
| 66 | + |
| 67 | + @override |
| 68 | + Map toJson() => _$NoneTextInputFormatterToJson(this); |
| 69 | +} |
| 70 | + |
| 71 | +/// Text formatters that can be applied to the text field input to restrict the |
| 72 | +/// input to a specific format. |
| 73 | +@JsonSerializable() |
| 74 | +class RegexTextInputFormatterModel extends TextInputFormatterModel { |
12 | 75 | /// The regular expression to match the text. |
13 | 76 | final String pattern; |
14 | 77 |
|
15 | | - /// The name of the formatter. |
16 | | - final String name; |
| 78 | + /// Whether the regex pattern is case sensitive. |
| 79 | + final bool caseSensitive; |
| 80 | + |
| 81 | + /// Whether the `.` pattern should match all characters, including |
| 82 | + /// line terminators. |
| 83 | + /// Defaults to `false`. |
| 84 | + final bool dotAll; |
| 85 | + |
| 86 | + /// Whether the pattern should match across multiple lines. |
| 87 | + /// Defaults to `false`. |
| 88 | + final bool multiLine; |
| 89 | + |
| 90 | + /// Whether the pattern should match unicode characters. |
| 91 | + /// Defaults to `false`. |
| 92 | + final bool unicode; |
| 93 | + |
| 94 | + /// The string to replace the unmatched text with. |
| 95 | + final String replacementString; |
| 96 | + |
| 97 | + /// Whether to allow or deny the pattern match. |
| 98 | + final bool allow; |
17 | 99 |
|
18 | 100 | /// Allow only digits in the text field. |
19 | | - static const TextInputFormatterModel none = TextInputFormatterModel( |
| 101 | + static const RegexTextInputFormatterModel none = RegexTextInputFormatterModel( |
20 | 102 | name: 'None', |
21 | 103 | pattern: r'.*', |
22 | 104 | ); |
23 | 105 |
|
24 | 106 | /// Allow only digits in the text field. |
25 | | - static const TextInputFormatterModel digitsOnly = TextInputFormatterModel( |
| 107 | + static const RegexTextInputFormatterModel digitsOnly = |
| 108 | + RegexTextInputFormatterModel( |
26 | 109 | name: 'Digits Only', |
27 | 110 | pattern: r'[0-9]', |
28 | 111 | ); |
29 | 112 |
|
30 | 113 | /// Allow only alphabets in the text field. |
31 | | - static const TextInputFormatterModel alphabetsOnly = |
32 | | - TextInputFormatterModel(name: 'Alphabets Only', pattern: r'[a-zA-Z]'); |
| 114 | + static const RegexTextInputFormatterModel alphabetsOnly = |
| 115 | + RegexTextInputFormatterModel( |
| 116 | + name: 'Alphabets Only', pattern: r'[a-zA-Z]'); |
33 | 117 |
|
34 | 118 | /// Allow only alpha-numeric characters in the text field. |
35 | | - static const TextInputFormatterModel alphaNumeric = |
36 | | - TextInputFormatterModel(name: 'Alpha-numeric', pattern: r'[a-zA-Z0-9]'); |
| 119 | + static const RegexTextInputFormatterModel alphaNumeric = |
| 120 | + RegexTextInputFormatterModel( |
| 121 | + name: 'Alpha-numeric', pattern: r'[a-zA-Z0-9]'); |
37 | 122 |
|
38 | 123 | /// Allow only phone number format in the text field. |
39 | | - static const TextInputFormatterModel noSpaces = |
40 | | - TextInputFormatterModel(name: 'No Spaces', pattern: r'^[^\s]*$'); |
41 | | - |
42 | | - /// Allow only email format in the text field. |
43 | | - static const TextInputFormatterModel email = |
44 | | - TextInputFormatterModel(name: 'Email', pattern: '^(|\\S)+\$'); |
| 124 | + static const RegexTextInputFormatterModel noSpaces = |
| 125 | + RegexTextInputFormatterModel(name: 'No Spaces', pattern: r'^[^\s]*$'); |
45 | 126 |
|
46 | 127 | /// Allow only phone number format in the text field. |
47 | | - static const TextInputFormatterModel phoneNumber = |
48 | | - TextInputFormatterModel(name: 'Phone Number', pattern: r'^+?[0-9]*$'); |
| 128 | + static const RegexTextInputFormatterModel custom = |
| 129 | + RegexTextInputFormatterModel(name: 'Custom Regex', pattern: r''); |
| 130 | + |
| 131 | + /// Whether the formatter is a custom formatter. |
| 132 | + bool get isCustom => name == custom.name; |
49 | 133 |
|
50 | 134 | /// List of all available text field formatters. |
51 | | - static const List<TextInputFormatterModel> values = [ |
52 | | - none, |
| 135 | + static const List<RegexTextInputFormatterModel> formatters = [ |
53 | 136 | digitsOnly, |
54 | 137 | alphabetsOnly, |
55 | 138 | alphaNumeric, |
56 | 139 | noSpaces, |
57 | | - email, |
58 | | - phoneNumber, |
| 140 | + custom, |
59 | 141 | ]; |
60 | 142 |
|
61 | | - /// Returns true if the formatter is [none]. |
62 | | - bool get isNone => this == none; |
63 | | - |
64 | | - /// Creates a new [TextInputFormatterModel] instance with the given pattern. |
65 | | - const TextInputFormatterModel({required this.name, required this.pattern}); |
| 143 | + /// Creates a new [RegexTextInputFormatterModel] instance with the given pattern. |
| 144 | + const RegexTextInputFormatterModel({ |
| 145 | + required super.name, |
| 146 | + required this.pattern, |
| 147 | + this.allow = true, |
| 148 | + this.caseSensitive = true, |
| 149 | + this.dotAll = false, |
| 150 | + this.multiLine = false, |
| 151 | + this.unicode = false, |
| 152 | + this.replacementString = '', |
| 153 | + }) : super(type: TextInputFormatterType.regex); |
66 | 154 |
|
67 | 155 | /// copyWith |
68 | | - TextInputFormatterModel copyWith({ |
69 | | - String? name, |
| 156 | + RegexTextInputFormatterModel copyWith({ |
70 | 157 | String? pattern, |
| 158 | + bool? caseSensitive, |
| 159 | + bool? dotAll, |
| 160 | + bool? multiLine, |
| 161 | + bool? unicode, |
| 162 | + bool? allow, |
| 163 | + String? replacementString, |
71 | 164 | }) { |
72 | | - return TextInputFormatterModel( |
73 | | - name: name ?? this.name, |
| 165 | + return RegexTextInputFormatterModel( |
| 166 | + name: name, |
74 | 167 | pattern: pattern ?? this.pattern, |
| 168 | + caseSensitive: caseSensitive ?? this.caseSensitive, |
| 169 | + dotAll: dotAll ?? this.dotAll, |
| 170 | + multiLine: multiLine ?? this.multiLine, |
| 171 | + unicode: unicode ?? this.unicode, |
| 172 | + allow: allow ?? this.allow, |
| 173 | + replacementString: replacementString ?? this.replacementString, |
75 | 174 | ); |
76 | 175 | } |
77 | 176 |
|
78 | 177 | /// Creates a [TextInputFormatterModel] instance from a JSON object. |
79 | | - factory TextInputFormatterModel.fromJson(Map json) => |
80 | | - _$TextInputFormatterModelFromJson(json); |
| 178 | + factory RegexTextInputFormatterModel.fromJson(Map json) => |
| 179 | + _$RegexTextInputFormatterModelFromJson(json); |
81 | 180 |
|
82 | 181 | @override |
83 | | - Map toJson() => _$TextInputFormatterModelToJson(this); |
| 182 | + Map toJson() => _$RegexTextInputFormatterModelToJson(this); |
84 | 183 |
|
85 | 184 | @override |
86 | | - List<Object?> get props => [name]; |
| 185 | + List<Object?> get props => [ |
| 186 | + ...super.props, |
| 187 | + pattern, |
| 188 | + caseSensitive, |
| 189 | + dotAll, |
| 190 | + multiLine, |
| 191 | + unicode, |
| 192 | + replacementString, |
| 193 | + allow, |
| 194 | + ]; |
87 | 195 | } |
0 commit comments