Skip to content

Commit dbe60bd

Browse files
committed
Add currency text formatter and donut chart type
- CurrencyTextInputFormatterModel (formatter type currency) with a bindable currencyCode, for amount fields that group as you type. - ChartType.donut with donutThickness, sliceGap and startAngle, and selectOnHover for every chart type. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015oUVftgvTVsZQJ5PqZ7Lkh
1 parent 79c3a55 commit dbe60bd

4 files changed

Lines changed: 111 additions & 1 deletion

File tree

‎lib/src/api/models/text_input_formatter_model.dart‎

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ enum TextInputFormatterType {
1212

1313
/// A formatter that uses a regular expression to match the text.
1414
regex,
15+
16+
/// Groups an amount as it is typed, in a currency's format.
17+
currency,
1518
}
1619

1720
/// Text formatters that can be applied to the text field input to restrict the
@@ -37,13 +40,16 @@ sealed class TextInputFormatterModel with Equatable, SerializableMixin {
3740
TextInputFormatterType.regex => RegexTextInputFormatterModel.fromJson(
3841
json,
3942
),
43+
TextInputFormatterType.currency =>
44+
CurrencyTextInputFormatterModel.fromJson(json),
4045
};
4146
}
4247

4348
/// A list of all available text field formatters.
4449
static const List<TextInputFormatterModel> formatters = [
4550
NoneTextInputFormatterModel(),
4651
...RegexTextInputFormatterModel.formatters,
52+
CurrencyTextInputFormatterModel(),
4753
];
4854

4955
/// A list of all available text field formatter names.
@@ -202,3 +208,37 @@ class RegexTextInputFormatterModel extends TextInputFormatterModel {
202208
allow,
203209
];
204210
}
211+
212+
213+
/// Formats an amount while it is typed, the way [currencyCode] writes
214+
/// money: digits grouped (`12,345.6`, `12.345,6` for EUR), decimals capped
215+
/// at the currency's (none for JPY), "." accepted as the decimal mark of a
216+
/// comma currency. [currencyCode] may be a variable or formula
217+
/// (`${account.currency}`), so the field follows the selected account.
218+
class CurrencyTextInputFormatterModel extends TextInputFormatterModel {
219+
/// ISO code, e.g. USD, EUR, GBP, JPY; or a `${...}` binding.
220+
final String currencyCode;
221+
222+
const CurrencyTextInputFormatterModel({this.currencyCode = 'USD'})
223+
: super(name: 'Currency Amount', type: TextInputFormatterType.currency);
224+
225+
CurrencyTextInputFormatterModel copyWith({String? currencyCode}) =>
226+
CurrencyTextInputFormatterModel(
227+
currencyCode: currencyCode ?? this.currencyCode,
228+
);
229+
230+
factory CurrencyTextInputFormatterModel.fromJson(Map json) =>
231+
CurrencyTextInputFormatterModel(
232+
currencyCode: json['currencyCode'] as String? ?? 'USD',
233+
);
234+
235+
@override
236+
Map toJson() => {
237+
'name': name,
238+
'type': type.name,
239+
if (currencyCode != 'USD') 'currencyCode': currencyCode,
240+
};
241+
242+
@override
243+
List<Object?> get props => [...super.props, currencyCode];
244+
}

‎lib/src/api/models/text_input_formatter_model.g.dart‎

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎lib/src/api/nodes/chart_node.dart‎

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,18 @@ enum ChartType {
2727

2828
/// One horizontal band of colored segments over a numeric domain, e.g. a
2929
/// machine's states across a shift.
30-
timeline;
30+
timeline,
31+
32+
/// A ring of slices, one per data item, sized by the first series' field
33+
/// and colored by its colorField (e.g. spending by category).
34+
donut;
3135

3236
/// Display name.
3337
String get prettify => switch (this) {
3438
ChartType.bar => 'Bar',
3539
ChartType.line => 'Line',
3640
ChartType.timeline => 'Timeline',
41+
ChartType.donut => 'Donut',
3742
};
3843
}
3944

@@ -420,6 +425,21 @@ class ChartProperties extends CustomProperties with FieldsHolder {
420425
/// Timeline: divisions marked with ticks (e.g. hours).
421426
int divisions;
422427

428+
/// Donut: ring width as a fraction of the chart's diameter.
429+
double donutThickness;
430+
431+
/// Donut: gap between slices, in degrees.
432+
double sliceGap;
433+
434+
/// Donut: where the first slice starts, in degrees clockwise from 12
435+
/// o'clock.
436+
double startAngle;
437+
438+
/// Hovering a category (bar group, line point or slice) selects it, as a
439+
/// tap does, and leaving the chart clears the selection (-1); bind
440+
/// selectedIndex to show what is under the pointer.
441+
bool selectOnHover;
442+
423443
/// Creates [ChartProperties].
424444
ChartProperties({
425445
this.chartType = ChartType.bar,
@@ -469,6 +489,10 @@ class ChartProperties extends CustomProperties with FieldsHolder {
469489
a: 1,
470490
),
471491
this.divisions = 8,
492+
this.donutThickness = 0.15,
493+
this.sliceGap = 1.5,
494+
this.startAngle = 0,
495+
this.selectOnHover = false,
472496
});
473497

474498
/// Duplicates these properties with the given overrides.
@@ -505,6 +529,10 @@ class ChartProperties extends CustomProperties with FieldsHolder {
505529
ColorRGBA? nowColor,
506530
ColorRGBA? trackColor,
507531
int? divisions,
532+
double? donutThickness,
533+
double? sliceGap,
534+
double? startAngle,
535+
bool? selectOnHover,
508536
}) => ChartProperties(
509537
chartType: chartType ?? this.chartType,
510538
series: series ?? this.series,
@@ -538,11 +566,33 @@ class ChartProperties extends CustomProperties with FieldsHolder {
538566
nowColor: nowColor ?? this.nowColor,
539567
trackColor: trackColor ?? this.trackColor,
540568
divisions: divisions ?? this.divisions,
569+
donutThickness: donutThickness ?? this.donutThickness,
570+
sliceGap: sliceGap ?? this.sliceGap,
571+
startAngle: startAngle ?? this.startAngle,
572+
selectOnHover: selectOnHover ?? this.selectOnHover,
541573
);
542574

543575
@override
544576
FieldsMap generateFields() => {
545577
...super.generateFields(),
578+
'donutThickness': NumFieldAccess<double>(
579+
'Ring Width',
580+
'Donut ring width as a fraction of the diameter',
581+
() => donutThickness,
582+
(v) => donutThickness = v,
583+
),
584+
'sliceGap': NumFieldAccess<double>(
585+
'Slice Gap',
586+
'Gap between donut slices in degrees',
587+
() => sliceGap,
588+
(v) => sliceGap = v,
589+
),
590+
'selectOnHover': BoolFieldAccess(
591+
'Select On Hover',
592+
'Hovering a category selects it',
593+
() => selectOnHover,
594+
(v) => selectOnHover = v,
595+
),
546596
'labelField': StringFieldAccess(
547597
'Label Field',
548598
'Field holding x-axis labels',
@@ -627,6 +677,10 @@ class ChartProperties extends CustomProperties with FieldsHolder {
627677
nowColor,
628678
trackColor,
629679
divisions,
680+
donutThickness,
681+
sliceGap,
682+
startAngle,
683+
selectOnHover,
630684
];
631685

632686
/// Creates [ChartProperties] from JSON.

‎lib/src/api/nodes/chart_node.g.dart‎

Lines changed: 15 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)