diff --git a/docs/pages/docs/calendar.mdx b/docs/pages/docs/calendar.mdx index 157f93b36..69b6fa063 100644 --- a/docs/pages/docs/calendar.mdx +++ b/docs/pages/docs/calendar.mdx @@ -17,7 +17,7 @@ to customize the date selection behavior. ```dart FCalendar( - controller: FCalendarValueController(initialSelection: selected), + controller: FCalendarController.date(initialSelection: selected), start: DateTime.utc(2000), end: DateTime.utc(2030), ); @@ -31,7 +31,7 @@ to customize the date selection behavior. ```dart FCalendar( - controller: FCalendarValueController( + controller: FCalendarController.date( initialSelection: DateTime.utc(2024, 9, 13), selectable: (date) => allowedDates.contains(date), ), @@ -55,7 +55,7 @@ FCalendar( ```dart FCalendar( - controller: FCalendarSingleValueController(), + controller: FCalendarController.date(), start: DateTime.utc(2000), end: DateTime.utc(2030), ); @@ -66,12 +66,12 @@ FCalendar( ### Multiple Dates with Initial Selections - + ```dart FCalendar( - controller: FCalendarMultiValueController( + controller: FCalendarController.dates( initialSelections: {DateTime.utc(2024, 7, 17), DateTime.utc(2024, 7, 20)}, ), start: DateTime.utc(2000), @@ -90,7 +90,7 @@ FCalendar( ```dart FCalendar( - controller: FCalendarMultiValueController( + controller: FCalendarController.dates( initialSelections: {DateTime.utc(2024, 7, 17), DateTime.utc(2024, 7, 20)}, selectable: (date) => !{DateTime.utc(2024, 7, 18), DateTime.utc(2024, 7, 19)}.contains(date), ), @@ -110,7 +110,7 @@ FCalendar( ```dart FCalendar( - controller: FCalendarRangeController( + controller: FCalendarController.range( initialSelection: (DateTime.utc(2024, 7, 17), DateTime.utc(2024, 7, 20)), ), start: DateTime.utc(2000), diff --git a/forui/CHANGELOG.md b/forui/CHANGELOG.md index e5faa0e1b..e620b5d54 100644 --- a/forui/CHANGELOG.md +++ b/forui/CHANGELOG.md @@ -18,9 +18,11 @@ * **Breaking:** Rename `FCalendarEntryStyle.focusedTextStyle` to `FCalendarEntryStyle.hoveredTextStyle`. This only affects users that customized `FCalendarEntryStyle`. -* **Breaking:** Rename `FCalendarSingleValueController` to `FCalendarValueController`. +* **Breaking:** Move `FCalendarSingleValueController` to `FCalendarController.date(...)`. -* **Breaking:** Rename `FCalendarSingleRangeController` to `FCalendarRangeController`. +* **Breaking:** Move `FCalendarMultiValueController` to `FCalendarController.dates(...)`. + +* **Breaking:** Rename `FCalendarSingleRangeController` to `FCalendarRangeController.range(...)`. * **Breaking:** Rename `FSeparator` to `FDivider`. diff --git a/forui/lib/src/theme/theme_data.dart b/forui/lib/src/theme/theme_data.dart index ed229cb4c..c3cceb99a 100644 --- a/forui/lib/src/theme/theme_data.dart +++ b/forui/lib/src/theme/theme_data.dart @@ -199,7 +199,7 @@ final class FThemeData with Diagnosticable { FTabsStyle? tabsStyle, FTextFieldStyle? textFieldStyle, FScaffoldStyle? scaffoldStyle, - FDividerStyles? separatorStyles, + FDividerStyles? dividerStyles, FSwitchStyle? switchStyle, }) => FThemeData( @@ -215,13 +215,13 @@ final class FThemeData with Diagnosticable { cardStyle: cardStyle ?? this.cardStyle, checkboxStyle: checkboxStyle ?? this.checkboxStyle, dialogStyle: dialogStyle ?? this.dialogStyle, + dividerStyles: dividerStyles ?? this.dividerStyles, headerStyle: headerStyle ?? this.headerStyle, progressStyle: progressStyle ?? this.progressStyle, resizableStyle: resizableStyle ?? this.resizableStyle, tabsStyle: tabsStyle ?? this.tabsStyle, textFieldStyle: textFieldStyle ?? this.textFieldStyle, scaffoldStyle: scaffoldStyle ?? this.scaffoldStyle, - dividerStyles: separatorStyles ?? this.dividerStyles, switchStyle: switchStyle ?? this.switchStyle, ); diff --git a/forui/lib/src/widgets/calendar/calendar_controller.dart b/forui/lib/src/widgets/calendar/calendar_controller.dart index 372894ea5..564248976 100644 --- a/forui/lib/src/widgets/calendar/calendar_controller.dart +++ b/forui/lib/src/widgets/calendar/calendar_controller.dart @@ -9,10 +9,52 @@ bool _true(DateTime _) => true; /// The [DateTime]s are always in UTC timezone and truncated to the nearest day. /// /// This class should be extended to customize date selection. By default, the following controllers are provided: -/// * [FCalendarValueController] for selecting a single date. -/// * [FCalendarMultiValueController] for selecting multiple date. -/// * [FCalendarRangeController] for selecting a single range. +/// * [FCalendarController.date] for selecting a single date. +/// * [FCalendarController.dates] for selecting multiple date. +/// * [FCalendarController.range] for selecting a single range. abstract class FCalendarController extends ValueNotifier { + /// Creates a [FCalendarController] that allows only a single date to be selected, with the given initially selected + /// date. + /// + /// [selectable] will always return true if not given. + /// + /// ## Contract + /// Throws [AssertionError] if [initialSelection] is not in UTC timezone. + static FCalendarController date({ + DateTime? initialSelection, + Predicate? selectable, + }) => + _DateController(initialSelection: initialSelection, selectable: selectable); + + /// Creates a [FCalendarController] that allows multiple dates to be selected, with the given initial selected dates. + /// + /// [selectable] will always return true if not given. + /// + /// ## Contract + /// Throws [AssertionError] if the dates in [initialSelections] are not in UTC timezone. + static FCalendarController> dates({ + Set initialSelections = const {}, + Predicate? selectable, + }) => + _DatesController(initialSelections: initialSelections, selectable: selectable); + + /// Creates a [FCalendarController] that allows a single range to be selected, with the given initial range. + /// + /// [selectable] will always return true if not given. + /// + /// Both the start and end dates of the range is inclusive. The selected dates are always in UTC timezone and + /// truncated to the nearest day. Unselectable dates within the selected range are selected regardless. + /// + /// ## Contract + /// Throws [AssertionError] if: + /// * the given dates in [value] is not in UTC timezone. + /// * the end date is less than start date. + static FCalendarController<(DateTime, DateTime)?> range({ + (DateTime, DateTime)? initialSelection, + Predicate? selectable, + }) => + _RangeController(initialSelection: initialSelection, selectable: selectable); + /// Creates a [FCalendarController] with the given initial [value]. FCalendarController(super._value); @@ -36,19 +78,10 @@ abstract class FCalendarController extends ValueNotifier { void select(DateTime date); } -/// A controller that allows only a single date to be selected. -/// -/// The [DateTime]s are always in UTC timezone and truncated to the nearest date. -class FCalendarValueController extends FCalendarController { +class _DateController extends FCalendarController { final Predicate _selectable; - /// Creates a [FCalendarValueController] with the given initially selected date. - /// - /// [selectable] will always return true if not given. - /// - /// ## Contract - /// Throws [AssertionError] if [initialSelection] is not in UTC timezone. - FCalendarValueController({ + _DateController({ DateTime? initialSelection, Predicate? selectable, }) : assert(initialSelection?.isUtc ?? true, 'value must be in UTC timezone'), @@ -65,18 +98,10 @@ class FCalendarValueController extends FCalendarController { void select(DateTime date) => value = value?.toLocalDate() == date.toLocalDate() ? null : date; } -/// A controller that allows multiple dates to be selected. The maximum number of dates that can be selected is -/// determined by [max]. -/// -/// The [DateTime]s are always in UTC timezone and truncated to the nearest day. -class FCalendarMultiValueController extends FCalendarController> { +class _DatesController extends FCalendarController> { final Predicate _selectable; - /// Creates a [FCalendarMultiValueController] with the given initial [value]. - /// - /// ## Contract - /// Throws [AssertionError] if the dates in [initialSelections] are not in UTC timezone. - FCalendarMultiValueController({ + _DatesController({ Set initialSelections = const {}, Predicate? selectable, }) : assert(initialSelections.every((d) => d.isUtc), 'dates must be in UTC timezone'), @@ -96,20 +121,10 @@ class FCalendarMultiValueController extends FCalendarController> { } } -/// A date selection controller that allows a single range to be selected. -/// -/// Both the start and end dates of the range is inclusive. The selected dates are always in UTC timezone and truncated -/// to the nearest day. Unselectable dates within the selected range are selected regardless. -class FCalendarRangeController extends FCalendarController<(DateTime, DateTime)?> { +class _RangeController extends FCalendarController<(DateTime, DateTime)?> { final Predicate _selectable; - /// Creates a [FCalendarRangeController] with the given initial [value]. - /// - /// ## Contract - /// Throws [AssertionError] if: - /// * the given dates in [value] is not in UTC timezone. - /// * the end date is less than start date. - FCalendarRangeController({ + _RangeController({ (DateTime, DateTime)? initialSelection, Predicate? selectable, }) : assert( diff --git a/forui/test/src/widgets/calendar/calendar_controller_test.dart b/forui/test/src/widgets/calendar/calendar_controller_test.dart index f87f5e469..239196bd1 100644 --- a/forui/test/src/widgets/calendar/calendar_controller_test.dart +++ b/forui/test/src/widgets/calendar/calendar_controller_test.dart @@ -3,10 +3,10 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:forui/forui.dart'; void main() { - group('FCalendarValueController', () { + group('FCalendarController.date(...)', () { test( 'constructor throws error', - () => expect(() => FCalendarValueController(initialSelection: DateTime.now()), throwsAssertionError), + () => expect(() => FCalendarController.date(initialSelection: DateTime.now()), throwsAssertionError), ); for (final (date, expected) in [ @@ -14,7 +14,7 @@ void main() { (DateTime.utc(2024, 5, 5), false), ]) { test('selected(...) contains date', () { - final controller = FCalendarValueController(initialSelection: DateTime.utc(2024, 5, 4)); + final controller = FCalendarController.date(initialSelection: DateTime.utc(2024, 5, 4)); expect(controller.selected(date), expected); }); } @@ -26,19 +26,19 @@ void main() { (DateTime.utc(2024), DateTime.utc(2024), null), ]) { test('select(...)', () { - final controller = FCalendarValueController(initialSelection: initial)..select(date); + final controller = FCalendarController.date(initialSelection: initial)..select(date); expect(controller.value, expected); }); } }); - group('FCalendarMultiValueController', () { + group('FCalendarController.dates', () { for (final (date, expected) in [ (DateTime.utc(2024), true), (DateTime.utc(2025), false), ]) { test('selected(...)', () { - final controller = FCalendarMultiValueController(initialSelections: {DateTime.utc(2024)}); + final controller = FCalendarController.dates(initialSelections: {DateTime.utc(2024)}); expect(controller.selected(date), expected); }); } @@ -49,17 +49,17 @@ void main() { ({DateTime.utc(2024)}, DateTime.utc(2025), {DateTime.utc(2024), DateTime.utc(2025)}), ]) { test('select(...)', () { - final controller = FCalendarMultiValueController(initialSelections: initial)..select(date); + final controller = FCalendarController.dates(initialSelections: initial)..select(date); expect(controller.value, expected); }); } }); - group('FCalendarRangeController', () { + group('FCalendarController.range(...)', () { test( 'constructor throws error', () => expect( - () => FCalendarRangeController(initialSelection: (DateTime(2025), DateTime(2024))), + () => FCalendarController.range(initialSelection: (DateTime(2025), DateTime(2024))), throwsAssertionError, ), ); @@ -72,7 +72,7 @@ void main() { (null, DateTime.utc(2023), false), ]) { test('selected(...)', () { - final controller = FCalendarRangeController(initialSelection: initial); + final controller = FCalendarController.range(initialSelection: initial); expect(controller.selected(date), expected); }); } @@ -86,7 +86,7 @@ void main() { (null, DateTime.utc(2023), (DateTime.utc(2023), DateTime.utc(2023))), ]) { test('select(...)', () { - final controller = FCalendarRangeController(initialSelection: initial)..select(date); + final controller = FCalendarController.range(initialSelection: initial)..select(date); expect(controller.value, expected); }); } diff --git a/forui/test/src/widgets/calendar/calendar_golden_test.dart b/forui/test/src/widgets/calendar/calendar_golden_test.dart index 203ebce90..01751e52b 100644 --- a/forui/test/src/widgets/calendar/calendar_golden_test.dart +++ b/forui/test/src/widgets/calendar/calendar_golden_test.dart @@ -29,7 +29,7 @@ void main() { child: Padding( padding: const EdgeInsets.all(16), child: FCalendar( - controller: FCalendarMultiValueController( + controller: FCalendarController.dates( initialSelections: selected, selectable: (date) => date != DateTime.utc(2024, 7, 2), ), @@ -63,7 +63,7 @@ void main() { child: Padding( padding: const EdgeInsets.all(16), child: FCalendar( - controller: FCalendarMultiValueController(initialSelections: selected), + controller: FCalendarController.dates(initialSelections: selected), start: DateTime(1900, 1, 8), end: DateTime(2024, 7, 10), today: DateTime(2024, 6, 14), @@ -88,7 +88,7 @@ void main() { child: Padding( padding: const EdgeInsets.all(16), child: FCalendar( - controller: FCalendarMultiValueController(initialSelections: selected), + controller: FCalendarController.dates(initialSelections: selected), start: DateTime(1900, 1, 8), end: DateTime(2024, 7, 10), today: DateTime(2024, 7, 14), @@ -125,7 +125,7 @@ void main() { child: Padding( padding: const EdgeInsets.all(16), child: FCalendar( - controller: FCalendarMultiValueController(initialSelections: selected), + controller: FCalendarController.dates(initialSelections: selected), start: DateTime(1900, 1, 8), end: DateTime(2024, 7, 10), today: DateTime(2024, 7, 14), @@ -149,7 +149,7 @@ void main() { child: Padding( padding: const EdgeInsets.all(16), child: FCalendar( - controller: FCalendarMultiValueController(initialSelections: selected), + controller: FCalendarController.dates(initialSelections: selected), start: DateTime(1900, 1, 8), end: DateTime(2024, 7, 10), today: DateTime(2024, 7, 14), diff --git a/forui/test/src/widgets/calendar/calendar_test.dart b/forui/test/src/widgets/calendar/calendar_test.dart index 0ccd26ec2..825ffab71 100644 --- a/forui/test/src/widgets/calendar/calendar_test.dart +++ b/forui/test/src/widgets/calendar/calendar_test.dart @@ -11,7 +11,7 @@ void main() { TestScaffold( data: FThemes.zinc.light, child: FCalendar( - controller: FCalendarMultiValueController(selectable: (date) => date != DateTime.utc(2024, 7, 2)), + controller: FCalendarController.dates(selectable: (date) => date != DateTime.utc(2024, 7, 2)), start: DateTime(1900, 1, 8), end: DateTime(2024, 7, 10), today: DateTime(2024, 7, 14), @@ -32,7 +32,7 @@ void main() { TestScaffold( data: FThemes.zinc.light, child: FCalendar( - controller: FCalendarMultiValueController(selectable: (date) => date != DateTime.utc(2024, 7, 2)), + controller: FCalendarController.dates(selectable: (date) => date != DateTime.utc(2024, 7, 2)), start: DateTime(2024, 7), end: DateTime(2024, 7, 10), today: DateTime(2024, 7, 14), @@ -55,7 +55,7 @@ void main() { TestScaffold( data: FThemes.zinc.light, child: FCalendar( - controller: FCalendarMultiValueController(selectable: (date) => date != DateTime.utc(2024, 7, 2)), + controller: FCalendarController.dates(selectable: (date) => date != DateTime.utc(2024, 7, 2)), start: DateTime(1900, 1, 8), end: DateTime(2024, 8, 10), today: DateTime(2024, 7, 14), @@ -76,7 +76,7 @@ void main() { TestScaffold( data: FThemes.zinc.light, child: FCalendar( - controller: FCalendarMultiValueController(selectable: (date) => date != DateTime.utc(2024, 7, 2)), + controller: FCalendarController.dates(selectable: (date) => date != DateTime.utc(2024, 7, 2)), start: DateTime(2024), end: DateTime(2024, 7, 10), today: DateTime(2024, 7, 14), diff --git a/samples/lib/main.dart b/samples/lib/main.dart index 610be7ec9..b5eedc2e9 100644 --- a/samples/lib/main.dart +++ b/samples/lib/main.dart @@ -76,8 +76,8 @@ class _AppRouter extends RootStackRouter { page: CalendarRoute.page, ), AutoRoute( - path: '/calendar/multi-value', - page: MultiValueCalendarRoute.page, + path: '/calendar/dates', + page: DatesCalendarRoute.page, ), AutoRoute( path: '/calendar/unselectable', diff --git a/samples/lib/widgets/calendar.dart b/samples/lib/widgets/calendar.dart index cd19ba475..87333716d 100644 --- a/samples/lib/widgets/calendar.dart +++ b/samples/lib/widgets/calendar.dart @@ -24,21 +24,21 @@ class CalendarPage extends SampleScaffold { @override Widget child(BuildContext context) => FCalendar( - controller: FCalendarValueController(initialSelection: selected), + controller: FCalendarController.date(initialSelection: selected), start: DateTime.utc(2000), end: DateTime.utc(2030), ); } @RoutePage() -class MultiValueCalendarPage extends SampleScaffold { - MultiValueCalendarPage({ +class DatesCalendarPage extends SampleScaffold { + DatesCalendarPage({ @queryParam super.theme, }); @override Widget child(BuildContext context) => FCalendar( - controller: FCalendarMultiValueController( + controller: FCalendarController.dates( initialSelections: {DateTime.utc(2024, 7, 17), DateTime.utc(2024, 7, 20)}, ), start: DateTime.utc(2000), @@ -55,7 +55,7 @@ class UnselectableCalendarPage extends SampleScaffold { @override Widget child(BuildContext context) => FCalendar( - controller: FCalendarMultiValueController( + controller: FCalendarController.dates( initialSelections: {DateTime.utc(2024, 7, 17), DateTime.utc(2024, 7, 20)}, selectable: (date) => !{DateTime.utc(2024, 7, 18), DateTime.utc(2024, 7, 19)}.contains(date), ), @@ -73,7 +73,7 @@ class RangeCalendarPage extends SampleScaffold { @override Widget child(BuildContext context) => FCalendar( - controller: FCalendarRangeController( + controller: FCalendarController.range( initialSelection: (DateTime.utc(2024, 7, 17), DateTime.utc(2024, 7, 20)), ), start: DateTime.utc(2000),