From b7f24ad3d9cc8127ebbed7846545a8eb28fec538 Mon Sep 17 00:00:00 2001 From: Mathias Mogensen Date: Sat, 23 Mar 2024 13:56:59 +0100 Subject: [PATCH] chore: bugfix + improvements --- .../date_picker/appflowy_date_picker.dart | 153 +++++++++--------- .../date_picker/widgets/time_text_field.dart | 29 +++- .../lib/style_widget/text_field.dart | 7 +- 3 files changed, 110 insertions(+), 79 deletions(-) diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/widgets/date_picker/appflowy_date_picker.dart b/frontend/appflowy_flutter/lib/workspace/presentation/widgets/date_picker/appflowy_date_picker.dart index 93ab2b2754d61..6c9215132b2f9 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/widgets/date_picker/appflowy_date_picker.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/widgets/date_picker/appflowy_date_picker.dart @@ -174,85 +174,92 @@ class _AppFlowyDatePickerState extends State { } Widget buildDesktopPicker() { - return Padding( - padding: const EdgeInsets.only(top: 18.0, bottom: 12.0), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - StartTextField( - includeTime: widget.includeTime, - timeFormat: widget.timeFormat, - timeHintText: widget.timeHintText, - parseEndTimeError: widget.parseEndTimeError, - parseTimeError: widget.parseTimeError, - timeStr: widget.timeStr, - popoverMutex: widget.popoverMutex, - onSubmitted: widget.onStartTimeSubmitted, - ), - EndTextField( - includeTime: widget.includeTime, - timeFormat: widget.timeFormat, - isRange: widget.isRange, - endTimeStr: widget.endTimeStr, - popoverMutex: widget.popoverMutex, - onSubmitted: widget.onEndTimeSubmitted, - ), - DatePicker( - isRange: widget.isRange, - onDaySelected: (selectedDay, focusedDay) { - widget.onDaySelected?.call(selectedDay, focusedDay); - - if (widget.rebuildOnDaySelected) { - setState(() => _selectedDay = selectedDay); - } - }, - onRangeSelected: widget.onRangeSelected, - selectedDay: - widget.rebuildOnDaySelected ? _selectedDay : widget.selectedDay, - firstDay: widget.firstDay, - lastDay: widget.lastDay, - startDay: widget.startDay, - endDay: widget.endDay, - onCalendarCreated: widget.onCalendarCreated, - onPageChanged: widget.onPageChanged, - ), - const TypeOptionSeparator(spacing: 12.0), - if (widget.enableRanges && widget.onIsRangeChanged != null) ...[ - EndTimeButton( + // GestureDetector is a workaround to stop popover from closing + // when clicking on the date picker. + return GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: () {}, + child: Padding( + padding: const EdgeInsets.only(top: 18.0, bottom: 12.0), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + StartTextField( + includeTime: widget.includeTime, + timeFormat: widget.timeFormat, + timeHintText: widget.timeHintText, + parseEndTimeError: widget.parseEndTimeError, + parseTimeError: widget.parseTimeError, + timeStr: widget.timeStr, + popoverMutex: widget.popoverMutex, + onSubmitted: widget.onStartTimeSubmitted, + ), + EndTextField( + includeTime: widget.includeTime, + timeFormat: widget.timeFormat, isRange: widget.isRange, - onChanged: widget.onIsRangeChanged!, + endTimeStr: widget.endTimeStr, + popoverMutex: widget.popoverMutex, + onSubmitted: widget.onEndTimeSubmitted, ), - const VSpace(4.0), - ], - Padding( - padding: const EdgeInsets.symmetric(horizontal: 12.0), - child: IncludeTimeButton( - value: widget.includeTime, - onChanged: widget.onIncludeTimeChanged, + DatePicker( + isRange: widget.isRange, + onDaySelected: (selectedDay, focusedDay) { + widget.onDaySelected?.call(selectedDay, focusedDay); + + if (widget.rebuildOnDaySelected) { + setState(() => _selectedDay = selectedDay); + } + }, + onRangeSelected: widget.onRangeSelected, + selectedDay: widget.rebuildOnDaySelected + ? _selectedDay + : widget.selectedDay, + firstDay: widget.firstDay, + lastDay: widget.lastDay, + startDay: widget.startDay, + endDay: widget.endDay, + onCalendarCreated: widget.onCalendarCreated, + onPageChanged: widget.onPageChanged, + ), + const TypeOptionSeparator(spacing: 12.0), + if (widget.enableRanges && widget.onIsRangeChanged != null) ...[ + EndTimeButton( + isRange: widget.isRange, + onChanged: widget.onIsRangeChanged!, + ), + const VSpace(4.0), + ], + Padding( + padding: const EdgeInsets.symmetric(horizontal: 12.0), + child: IncludeTimeButton( + value: widget.includeTime, + onChanged: widget.onIncludeTimeChanged, + ), ), - ), - const _GroupSeparator(), - ReminderSelector( - mutex: widget.popoverMutex, - hasTime: widget.includeTime, - timeFormat: widget.timeFormat, - selectedOption: _selectedReminderOption, - onOptionSelected: (option) { - setState(() => _selectedReminderOption = option); - widget.onReminderSelected?.call(option); - }, - ), - if (widget.options?.isNotEmpty ?? false) ...[ const _GroupSeparator(), - ListView.separated( - shrinkWrap: true, - itemCount: widget.options!.length, - separatorBuilder: (_, __) => const _GroupSeparator(), - itemBuilder: (_, index) => - _renderGroupOptions(widget.options![index].options), + ReminderSelector( + mutex: widget.popoverMutex, + hasTime: widget.includeTime, + timeFormat: widget.timeFormat, + selectedOption: _selectedReminderOption, + onOptionSelected: (option) { + setState(() => _selectedReminderOption = option); + widget.onReminderSelected?.call(option); + }, ), + if (widget.options?.isNotEmpty ?? false) ...[ + const _GroupSeparator(), + ListView.separated( + shrinkWrap: true, + itemCount: widget.options!.length, + separatorBuilder: (_, __) => const _GroupSeparator(), + itemBuilder: (_, index) => + _renderGroupOptions(widget.options![index].options), + ), + ], ], - ], + ), ), ); } diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/widgets/date_picker/widgets/time_text_field.dart b/frontend/appflowy_flutter/lib/workspace/presentation/widgets/date_picker/widgets/time_text_field.dart index 40197189cbbfd..c0f5cc8308109 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/widgets/date_picker/widgets/time_text_field.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/widgets/date_picker/widgets/time_text_field.dart @@ -94,6 +94,7 @@ class _TimeTextFieldState extends State { padding: const EdgeInsets.symmetric(horizontal: 18.0), child: FlowyTextField( text: text, + keyboardType: TextInputType.datetime, focusNode: _focusNode, autoFocus: false, controller: _textController, @@ -105,7 +106,16 @@ class _TimeTextFieldState extends State { ? _maxLengthTwelveHour : _maxLengthTwentyFourHour, showCounter: false, - inputFormatters: [TimeInputFormatter(widget.timeFormat)], + inputFormatters: [ + if (widget.timeFormat == TimeFormatPB.TwelveHour) ...[ + // Allow for AM/PM if time format is 12-hour + FilteringTextInputFormatter.allow(RegExp('[0-9:aApPmM ]')), + ] else ...[ + // Default allow for hh:mm format + FilteringTextInputFormatter.allow(RegExp('[0-9:]')), + ], + TimeInputFormatter(widget.timeFormat), + ], onSubmitted: widget.onSubmitted, ), ); @@ -142,9 +152,20 @@ class TimeInputFormatter extends TextInputFormatter { return _formatText(newText, spacePosition, ' '); } - return TextEditingValue( - text: newText.toUpperCase(), - ); + if (timeFormat == TimeFormatPB.TwentyFourHour && + newValue.text.length == 5) { + final prefix = newValue.text.substring(0, 3); + final suffix = newValue.text.length > 5 ? newValue.text.substring(6) : ''; + + final minutes = int.tryParse(newValue.text.substring(3, 5)); + if (minutes == null || minutes <= 0) { + return newValue.copyWith(text: '${prefix}00$suffix'.toUpperCase()); + } else if (minutes > 59) { + return newValue.copyWith(text: '${prefix}59$suffix'.toUpperCase()); + } + } + + return newValue.copyWith(text: newText.toUpperCase()); } TextEditingValue _formatText(String text, int index, String separator) { diff --git a/frontend/appflowy_flutter/packages/flowy_infra_ui/lib/style_widget/text_field.dart b/frontend/appflowy_flutter/packages/flowy_infra_ui/lib/style_widget/text_field.dart index 994e42720203b..6593ae65678ce 100644 --- a/frontend/appflowy_flutter/packages/flowy_infra_ui/lib/style_widget/text_field.dart +++ b/frontend/appflowy_flutter/packages/flowy_infra_ui/lib/style_widget/text_field.dart @@ -1,9 +1,10 @@ import 'dart:async'; -import 'package:flowy_infra/size.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:flowy_infra/size.dart'; + class FlowyTextField extends StatefulWidget { final String? hintText; final String? text; @@ -31,6 +32,7 @@ class FlowyTextField extends StatefulWidget { final InputDecoration? decoration; final TextAlignVertical? textAlignVertical; final TextInputAction? textInputAction; + final TextInputType? keyboardType; final List? inputFormatters; const FlowyTextField({ @@ -61,6 +63,7 @@ class FlowyTextField extends StatefulWidget { this.decoration, this.textAlignVertical, this.textInputAction, + this.keyboardType, this.inputFormatters, }); @@ -154,7 +157,7 @@ class FlowyTextFieldState extends State { maxLengthEnforcement: MaxLengthEnforcement.truncateAfterCompositionEnds, style: widget.textStyle ?? Theme.of(context).textTheme.bodySmall, textAlignVertical: widget.textAlignVertical ?? TextAlignVertical.center, - keyboardType: TextInputType.multiline, + keyboardType: widget.keyboardType ?? TextInputType.multiline, inputFormatters: widget.inputFormatters, decoration: widget.decoration ?? InputDecoration(