diff --git a/frontend/app_flowy/assets/translations/en.json b/frontend/app_flowy/assets/translations/en.json index 965f1bb0a5..95ef4aee75 100644 --- a/frontend/app_flowy/assets/translations/en.json +++ b/frontend/app_flowy/assets/translations/en.json @@ -166,7 +166,7 @@ "dark": "Dark Mode", "system": "Adapt to System" }, - "theme":"Theme" + "theme": "Theme" }, "files": { "defaultLocation": "Where your data is stored now", @@ -322,6 +322,12 @@ } }, "calendar": { - "menuName": "Calendar" + "menuName": "Calendar", + "navigation": { + "today": "Today", + "jumpToday": "Jump to Today", + "previousMonth": "Previous Month", + "nextMonth": "Next Month" + } } } \ No newline at end of file diff --git a/frontend/app_flowy/lib/plugins/calendar/calendar.dart b/frontend/app_flowy/lib/plugins/calendar/calendar.dart index 70f8467b5a..7e23d8c7dd 100644 --- a/frontend/app_flowy/lib/plugins/calendar/calendar.dart +++ b/frontend/app_flowy/lib/plugins/calendar/calendar.dart @@ -7,6 +7,7 @@ import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart'; import 'package:flutter/material.dart'; import '../util.dart'; +import 'presentation/calendar_page.dart'; class CalendarPluginBuilder extends PluginBuilder { @override @@ -79,33 +80,10 @@ class CalendarPluginDisplay extends PluginDisplay { }); }); - return BlankPage(key: ValueKey(view.id)); - // return BoardPage(key: ValueKey(view.id), view: view); + return CalendarPage(key: ValueKey(view.id)); + // return CalendarPage(key: ValueKey(view.id), view: view); } @override List get navigationItems => [this]; } - -// mark for removal -class BlankPage extends StatefulWidget { - const BlankPage({Key? key}) : super(key: key); - - @override - State createState() => _BlankPageState(); -} - -class _BlankPageState extends State { - @override - Widget build(BuildContext context) { - return SizedBox.expand( - child: Container( - color: Theme.of(context).colorScheme.surface, - child: Padding( - padding: const EdgeInsets.all(10), - child: Container(), - ), - ), - ); - } -} diff --git a/frontend/app_flowy/lib/plugins/calendar/presentation/calendar_page.dart b/frontend/app_flowy/lib/plugins/calendar/presentation/calendar_page.dart new file mode 100644 index 0000000000..81db1747fc --- /dev/null +++ b/frontend/app_flowy/lib/plugins/calendar/presentation/calendar_page.dart @@ -0,0 +1,162 @@ +import 'package:app_flowy/generated/locale_keys.g.dart'; +import 'package:app_flowy/plugins/grid/presentation/layout/sizes.dart'; +import 'package:calendar_view/calendar_view.dart'; +import 'package:easy_localization/easy_localization.dart'; +import 'package:flowy_infra/image.dart'; +import 'package:flowy_infra/size.dart'; +import 'package:flowy_infra/theme_extension.dart'; +import 'package:flowy_infra_ui/style_widget/button.dart'; +import 'package:flowy_infra_ui/style_widget/icon_button.dart'; +import 'package:flowy_infra_ui/style_widget/text.dart'; +import 'package:flutter/material.dart'; +import 'package:styled_widget/styled_widget.dart'; + +import 'layout/sizes.dart'; +import 'toolbar/calendar_toolbar.dart'; + +class CalendarPage extends StatelessWidget { + const CalendarPage({super.key}); + + @override + Widget build(BuildContext context) { + return const CalendarContent(); + } +} + +class CalendarContent extends StatefulWidget { + const CalendarContent({super.key}); + + @override + State createState() => _CalendarContentState(); +} + +class _CalendarContentState extends State { + late EventController _eventController; + GlobalKey? _calendarState; + + @override + void initState() { + _eventController = EventController(); + _calendarState = GlobalKey(); + super.initState(); + } + + @override + Widget build(BuildContext context) { + return CalendarControllerProvider( + controller: _eventController, + child: Column( + children: [ + // const _ToolbarBlocAdaptor(), + _toolbar(), + _buildCalendar(_eventController), + ], + ), + ); + } + + Widget _toolbar() { + return const CalendarToolbar(); + } + + Widget _buildCalendar(EventController eventController) { + return Expanded( + child: MonthView( + key: _calendarState, + controller: _eventController, + cellAspectRatio: 1.75, + borderColor: Theme.of(context).dividerColor, + headerBuilder: _headerNavigatorBuilder, + weekDayBuilder: _headerWeekDayBuilder, + cellBuilder: _calendarDayBuilder, + ), + ); + } + + Widget _headerNavigatorBuilder(DateTime currentMonth) { + return Row( + children: [ + FlowyText.medium( + DateFormat('MMMM y', context.locale.toLanguageTag()) + .format(currentMonth), + ), + const Spacer(), + FlowyIconButton( + width: CalendarSize.navigatorButtonWidth, + height: CalendarSize.navigatorButtonHeight, + icon: svgWidget('home/arrow_left'), + tooltipText: LocaleKeys.calendar_navigation_previousMonth.tr(), + hoverColor: AFThemeExtension.of(context).lightGreyHover, + onPressed: () => _calendarState?.currentState?.previousPage(), + ), + FlowyTextButton( + LocaleKeys.calendar_navigation_today.tr(), + fillColor: Colors.transparent, + fontWeight: FontWeight.w500, + tooltip: LocaleKeys.calendar_navigation_jumpToday.tr(), + padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 4), + hoverColor: AFThemeExtension.of(context).lightGreyHover, + onPressed: () => + _calendarState?.currentState?.animateToMonth(DateTime.now()), + ), + FlowyIconButton( + width: CalendarSize.navigatorButtonWidth, + height: CalendarSize.navigatorButtonHeight, + icon: svgWidget('home/arrow_right'), + tooltipText: LocaleKeys.calendar_navigation_nextMonth.tr(), + hoverColor: AFThemeExtension.of(context).lightGreyHover, + onPressed: () => _calendarState?.currentState?.nextPage(), + ), + ], + ); + } + + Widget _headerWeekDayBuilder(day) { + final symbols = DateFormat.EEEE(context.locale.toLanguageTag()).dateSymbols; + final weekDayString = symbols.WEEKDAYS[day]; + return Center( + child: Padding( + padding: CalendarSize.daysOfWeekInsets, + child: FlowyText.medium( + weekDayString, + color: Theme.of(context).hintColor, + ), + ), + ); + } + + Widget _calendarDayBuilder(date, event, isToday, isInMonth) { + Color dayTextColor = Theme.of(context).colorScheme.onSurface; + Color cellBackgroundColor = Theme.of(context).colorScheme.surface; + String dayString = date.day == 1 + ? DateFormat('MMM d', context.locale.toLanguageTag()).format(date) + : date.day.toString(); + + if (isToday) { + dayTextColor = Theme.of(context).colorScheme.onPrimary; + } + if (!isInMonth) { + dayTextColor = Theme.of(context).disabledColor; + cellBackgroundColor = AFThemeExtension.of(context).lightGreyHover; + } + Widget day = Container( + decoration: BoxDecoration( + color: isToday ? Theme.of(context).colorScheme.primary : null, + borderRadius: Corners.s6Border, + ), + padding: GridSize.typeOptionContentInsets, + child: FlowyText.medium( + dayString, + color: dayTextColor, + ), + ); + + return Container( + color: cellBackgroundColor, + child: Align( + alignment: Alignment.topRight, + child: day.padding(all: 6.0), + ), + ); + } +} diff --git a/frontend/app_flowy/lib/plugins/calendar/presentation/layout/sizes.dart b/frontend/app_flowy/lib/plugins/calendar/presentation/layout/sizes.dart new file mode 100644 index 0000000000..4e9d68f821 --- /dev/null +++ b/frontend/app_flowy/lib/plugins/calendar/presentation/layout/sizes.dart @@ -0,0 +1,11 @@ +import 'package:flutter/widgets.dart'; + +class CalendarSize { + static double scale = 1; + + static double get scrollBarSize => 12 * scale; + static double get navigatorButtonWidth => 20 * scale; + static double get navigatorButtonHeight => 25 * scale; + static EdgeInsets get daysOfWeekInsets => + EdgeInsets.symmetric(vertical: 10.0 * scale); +} diff --git a/frontend/app_flowy/lib/plugins/calendar/presentation/toolbar/calendar_toolbar.dart b/frontend/app_flowy/lib/plugins/calendar/presentation/toolbar/calendar_toolbar.dart new file mode 100644 index 0000000000..4b1399763b --- /dev/null +++ b/frontend/app_flowy/lib/plugins/calendar/presentation/toolbar/calendar_toolbar.dart @@ -0,0 +1,23 @@ +import 'package:flowy_infra_ui/style_widget/button.dart'; +import 'package:flutter/material.dart'; + +class CalendarToolbar extends StatelessWidget { + const CalendarToolbar({super.key}); + + @override + Widget build(BuildContext context) { + return SizedBox( + height: 40, + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: const [ + FlowyTextButton( + "Settings", + fillColor: Colors.transparent, + padding: EdgeInsets.symmetric(horizontal: 6, vertical: 2), + ), + ], + ), + ); + } +} diff --git a/frontend/app_flowy/pubspec.lock b/frontend/app_flowy/pubspec.lock index b557a5af14..5709286955 100644 --- a/frontend/app_flowy/pubspec.lock +++ b/frontend/app_flowy/pubspec.lock @@ -155,6 +155,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "8.3.2" + calendar_view: + dependency: "direct main" + description: + name: calendar_view + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.1" characters: dependency: transitive description: diff --git a/frontend/app_flowy/pubspec.yaml b/frontend/app_flowy/pubspec.yaml index dc27afa1ef..ba06f83d10 100644 --- a/frontend/app_flowy/pubspec.yaml +++ b/frontend/app_flowy/pubspec.yaml @@ -93,6 +93,7 @@ dependencies: percent_indicator: ^4.0.1 appflowy_editor_plugins: path: packages/appflowy_editor_plugins + calendar_view: ^1.0.1 dev_dependencies: flutter_lints: ^2.0.1