Skip to content

Commit

Permalink
fix: Update NumberBox formatting pattern for precision (#1080)
Browse files Browse the repository at this point in the history
  • Loading branch information
bdlukaa authored Jul 9, 2024
2 parents b099b0f + efebfd0 commit 0b71276
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 14 deletions.
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
## NEXT
## next

- feat: ¹ `NumberBox` now supports precision greater than 4. ² Add `NumberBox.pattern`, `NumberBox.formatter`, `NumberBox.format` ([#1080](https://github.com/bdlukaa/fluent_ui/pull/1080))
* fix: Resolved issue where `PaneItem` within `PaneItemExpander` remained accessible in `NavigationPane` compact mode ([#1081](https://github.com/bdlukaa/fluent_ui/issues/1081))

## 4.9.0
Expand Down
96 changes: 83 additions & 13 deletions lib/src/controls/form/number_box.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import 'dart:math';
import 'dart:ui' as ui;

import 'package:fluent_ui/fluent_ui.dart';
Expand All @@ -10,6 +9,12 @@ import 'package:math_expressions/math_expressions.dart';
const kNumberBoxOverlayWidth = 60.0;
const kNumberBoxOverlayHeight = 100.0;

typedef NumberBoxFormatFunction = String? Function(num? number);

abstract interface class NumberBoxFormatter {
String format(dynamic number);
}

enum SpinButtonPlacementMode {
/// Two buttons will be added as a suffix of the number box field. A button
/// for increment the value and a button for decrement the value.
Expand Down Expand Up @@ -96,7 +101,29 @@ class NumberBox<T extends num> extends StatefulWidget {

/// The precision indicates the number of digits that's accepted for double
/// value.
final int precision;
///
/// If set, [pattern], [formatter] and [format] must be `null`.
///
/// Default is 2.
final int? precision;

/// The parttern for the number box. The pattern is used to format the number
/// when the user inputs a value.
///
/// If set, [precision], [formatter] and [format] must be `null`.
final String? pattern;

/// The formatter for the number box. The formatter is used to format the
/// number when the user inputs a value.
///
/// If set, [pattern], [precision] and [format] must be `null`.
final NumberBoxFormatter? formatter;

/// The format function for the number box. The format function is used to
/// format the number when the user input a value.
///
/// If set, [pattern], [formatter] and [precision] must be `null`.
final NumberBoxFormatFunction? format;

/// The minimum value allowed. If the user input a value below than min,
/// the value is replaced by min.
Expand Down Expand Up @@ -244,7 +271,10 @@ class NumberBox<T extends num> extends StatefulWidget {
this.clearButton = true,
this.smallChange = 1,
this.largeChange = 10,
this.precision = 2,
this.precision,
this.pattern,
this.formatter,
this.format,
this.min,
this.max,
this.allowExpressions = false,
Expand Down Expand Up @@ -275,7 +305,26 @@ class NumberBox<T extends num> extends StatefulWidget {
this.textDirection,
this.textInputAction,
this.onEditingComplete,
});
}) : assert((precision != null &&
pattern == null &&
formatter == null &&
format == null) ||
(precision == null &&
pattern != null &&
formatter == null &&
format == null) ||
(precision == null &&
pattern == null &&
formatter != null &&
format == null) ||
(precision == null &&
pattern == null &&
formatter == null &&
format != null) ||
(precision == null &&
pattern == null &&
formatter == null &&
format == null));

@override
State<NumberBox<T>> createState() => NumberBoxState<T>();
Expand All @@ -292,6 +341,10 @@ class NumberBoxState<T extends num> extends State<NumberBox<T>> {

late num? previousValidValue = widget.value;

// use dynamic to simulate duck typing
late final dynamic _formatter;
late final NumberBoxFormatFunction _format;

final controller = TextEditingController();

final LayerLink _layerLink = LayerLink();
Expand All @@ -312,6 +365,32 @@ class NumberBoxState<T extends num> extends State<NumberBox<T>> {
}
focusNode.addListener(_handleFocusChanged);

if (widget.precision == null &&
widget.pattern == null &&
widget.formatter == null) {
_formatter = NumberFormat('#.${List.filled(2, '#').join()}') as dynamic;
}
if (widget.precision != null) {
final pattern = '#.${List.filled(widget.precision!, '#').join()}';
_formatter = NumberFormat(pattern) as dynamic;
}
if (widget.pattern != null) {
_formatter = NumberFormat(widget.pattern) as dynamic;
}
if (widget.formatter != null) {
_formatter = widget.formatter!;
}
if (widget.format != null) {
_format = widget.format!;
} else {
_format = (num? value) {
if (value == null) return null;
if (value is int) {
return value.toString();
}
return _formatter.format(value);
};
}
controller.text = widget.value?.toString() ?? '';
}

Expand Down Expand Up @@ -622,15 +701,6 @@ class NumberBoxState<T extends num> extends State<NumberBox<T>> {
widget.onChanged!(value as T?);
}
}

String? _format(num? value) {
if (value == null) return null;
if (value is int) {
return value.toString();
}
final mul = pow(10, widget.precision);
return NumberFormat().format((value * mul).roundToDouble() / mul);
}
}

class _NumberBoxCompactOverlay extends StatelessWidget {
Expand Down

0 comments on commit 0b71276

Please sign in to comment.