From 58a8d93ed33f5948efe7e2ff95e69a380075a1b1 Mon Sep 17 00:00:00 2001 From: dab246 Date: Fri, 11 Jul 2025 02:48:26 +0700 Subject: [PATCH] Reuse widgets for advanced search form view Signed-off-by: dab246 --- ...efault_button_arrow_down_field_widget.dart | 55 ++ ..._arrow_down_field_with_tab_key_widget.dart | 40 ++ ...art => default_date_drop_down_button.dart} | 0 .../default_date_drop_down_field_widget.dart | 2 +- ...fault_input_field_with_tab_key_widget.dart | 8 +- .../advanced_filter_controller.dart | 9 +- .../advanced_search_input_form_style.dart | 2 + .../styles/sort_by_drop_down_style.dart | 27 +- ..._autocomplete_email_address_web_style.dart | 45 -- .../advanced_search_field_widget.dart | 35 ++ .../advanced_search_input_form.dart | 468 ++++-------------- .../sort_by_drop_down_button.dart | 64 +-- ..._field_autocomplete_email_address_web.dart | 409 --------------- .../advanced_filter_controller_test.dart | 1 - 14 files changed, 292 insertions(+), 873 deletions(-) create mode 100644 lib/features/base/widget/default_field/default_button_arrow_down_field_widget.dart create mode 100644 lib/features/base/widget/default_field/default_button_arrow_down_field_with_tab_key_widget.dart rename lib/features/base/widget/default_field/{date_drop_down_button.dart => default_date_drop_down_button.dart} (100%) delete mode 100644 lib/features/mailbox_dashboard/presentation/styles/text_field_autocomplete_email_address_web_style.dart create mode 100644 lib/features/mailbox_dashboard/presentation/widgets/advanced_search/advanced_search_field_widget.dart delete mode 100644 lib/features/mailbox_dashboard/presentation/widgets/advanced_search/text_field_autocomplete_email_address_web.dart diff --git a/lib/features/base/widget/default_field/default_button_arrow_down_field_widget.dart b/lib/features/base/widget/default_field/default_button_arrow_down_field_widget.dart new file mode 100644 index 000000000..1fa1f377b --- /dev/null +++ b/lib/features/base/widget/default_field/default_button_arrow_down_field_widget.dart @@ -0,0 +1,55 @@ +import 'package:core/presentation/extensions/color_extension.dart'; +import 'package:core/presentation/utils/theme_utils.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; + +class DefaultButtonArrowDownFieldWidget extends StatelessWidget { + final String text; + final String iconArrowDown; + final VoidCallback onTap; + + const DefaultButtonArrowDownFieldWidget({ + super.key, + required this.text, + required this.iconArrowDown, + required this.onTap, + }); + + @override + Widget build(BuildContext context) { + return Material( + type: MaterialType.transparency, + child: InkWell( + onTap: onTap, + borderRadius: const BorderRadius.all(Radius.circular(10)), + focusColor: AppColor.colorMailboxHovered, + child: Container( + height: 40, + decoration: BoxDecoration( + borderRadius: const BorderRadius.all(Radius.circular(10)), + border: Border.all( + color: AppColor.m3Neutral90, + width: 1, + ), + ), + padding: const EdgeInsetsDirectional.only(start: 12, end: 8), + child: Row( + children: [ + Expanded( + child: Text( + text, + style: ThemeUtils.textStyleBodyBody3( + color: AppColor.m3SurfaceBackground, + ), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + ), + SvgPicture.asset(iconArrowDown), + ], + ), + ), + ), + ); + } +} diff --git a/lib/features/base/widget/default_field/default_button_arrow_down_field_with_tab_key_widget.dart b/lib/features/base/widget/default_field/default_button_arrow_down_field_with_tab_key_widget.dart new file mode 100644 index 000000000..781c629a7 --- /dev/null +++ b/lib/features/base/widget/default_field/default_button_arrow_down_field_with_tab_key_widget.dart @@ -0,0 +1,40 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:tmail_ui_user/features/base/widget/default_field/default_button_arrow_down_field_widget.dart'; + +class DefaultButtonArrowDownFieldWithTabKeyWidget extends StatelessWidget { + final String text; + final String iconArrowDown; + final VoidCallback onTap; + final FocusNode currentFocusNode; + final FocusNode nextFocusNode; + + const DefaultButtonArrowDownFieldWithTabKeyWidget({ + super.key, + required this.text, + required this.iconArrowDown, + required this.onTap, + required this.currentFocusNode, + required this.nextFocusNode, + }); + + @override + Widget build(BuildContext context) { + return KeyboardListener( + focusNode: currentFocusNode, + onKeyEvent: _onKeyEvent, + child: DefaultButtonArrowDownFieldWidget( + text: text, + iconArrowDown: iconArrowDown, + onTap: onTap, + ), + ); + } + + void _onKeyEvent(KeyEvent event) { + if (event is KeyDownEvent && + event.logicalKey == LogicalKeyboardKey.tab) { + nextFocusNode.requestFocus(); + } + } +} diff --git a/lib/features/base/widget/default_field/date_drop_down_button.dart b/lib/features/base/widget/default_field/default_date_drop_down_button.dart similarity index 100% rename from lib/features/base/widget/default_field/date_drop_down_button.dart rename to lib/features/base/widget/default_field/default_date_drop_down_button.dart diff --git a/lib/features/base/widget/default_field/default_date_drop_down_field_widget.dart b/lib/features/base/widget/default_field/default_date_drop_down_field_widget.dart index b9bd27020..0b2d27912 100644 --- a/lib/features/base/widget/default_field/default_date_drop_down_field_widget.dart +++ b/lib/features/base/widget/default_field/default_date_drop_down_field_widget.dart @@ -3,7 +3,7 @@ import 'package:core/presentation/resources/image_paths.dart'; import 'package:core/presentation/views/button/tmail_button_widget.dart'; import 'package:flutter/material.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/email_receive_time_type.dart'; -import 'package:tmail_ui_user/features/base/widget/default_field/date_drop_down_button.dart'; +import 'package:tmail_ui_user/features/base/widget/default_field/default_date_drop_down_button.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; typedef OnOpenDatPicker = void Function(); diff --git a/lib/features/base/widget/default_field/default_input_field_with_tab_key_widget.dart b/lib/features/base/widget/default_field/default_input_field_with_tab_key_widget.dart index bb37cbaea..e094aa4e6 100644 --- a/lib/features/base/widget/default_field/default_input_field_with_tab_key_widget.dart +++ b/lib/features/base/widget/default_field/default_input_field_with_tab_key_widget.dart @@ -8,7 +8,7 @@ typedef OnTextChange = void Function(String text); class DefaultInputFieldWithTabKeyWidget extends StatelessWidget { final TextEditingController textEditingController; final FocusNode currentFocusNode; - final FocusNode? nextFocusNode; + final FocusNode nextFocusNode; final String? hintText; final OnTextChange? onTextChange; final OnTextSubmitted? onTextSubmitted; @@ -18,7 +18,7 @@ class DefaultInputFieldWithTabKeyWidget extends StatelessWidget { super.key, required this.textEditingController, required this.currentFocusNode, - this.nextFocusNode, + required this.nextFocusNode, this.hintText, this.onTextChange, this.onTextSubmitted, @@ -29,7 +29,7 @@ class DefaultInputFieldWithTabKeyWidget extends StatelessWidget { Widget build(BuildContext context) { final bodyWidget = KeyboardListener( focusNode: currentFocusNode, - onKeyEvent: nextFocusNode == null ? null : _onKeyEvent, + onKeyEvent: _onKeyEvent, child: DefaultInputFieldWidget( textEditingController: textEditingController, hintText: hintText, @@ -48,7 +48,7 @@ class DefaultInputFieldWithTabKeyWidget extends StatelessWidget { void _onKeyEvent(KeyEvent event) { if (event is KeyDownEvent && event.logicalKey == LogicalKeyboardKey.tab) { - nextFocusNode?.requestFocus(); + nextFocusNode.requestFocus(); } } } diff --git a/lib/features/mailbox_dashboard/presentation/controller/advanced_filter_controller.dart b/lib/features/mailbox_dashboard/presentation/controller/advanced_filter_controller.dart index 107c2812a..7fea47b69 100644 --- a/lib/features/mailbox_dashboard/presentation/controller/advanced_filter_controller.dart +++ b/lib/features/mailbox_dashboard/presentation/controller/advanced_filter_controller.dart @@ -37,6 +37,7 @@ class AdvancedFilterController extends BaseController { final startDate = Rxn(); final endDate = Rxn(); final sortOrderType = EmailSortOrderType.mostRecent.obs; + final selectedFolderName = Rxn(); final GlobalKey keyFromEmailTagEditor = GlobalKey(); final GlobalKey keyToEmailTagEditor = GlobalKey(); @@ -51,7 +52,6 @@ class AdvancedFilterController extends BaseController { TextEditingController subjectFilterInputController = TextEditingController(); TextEditingController hasKeyWordFilterInputController = TextEditingController(); TextEditingController notKeyWordFilterInputController = TextEditingController(); - TextEditingController mailBoxFilterInputController = TextEditingController(); final search.SearchController searchController = Get.find(); final MailboxDashBoardController _mailboxDashBoardController = Get.find(); @@ -206,7 +206,7 @@ class AdvancedFilterController extends BaseController { final mailboxName = context.mounted ? _destinationMailboxSelected?.getDisplayName(context) : _destinationMailboxSelected?.name?.name; - mailBoxFilterInputController.text = StringConvert.writeNullToEmpty(mailboxName); + selectedFolderName.value = StringConvert.writeNullToEmpty(mailboxName); _updateMemorySearchFilter(mailboxOption: optionOf(_destinationMailboxSelected)); } @@ -276,7 +276,7 @@ class AdvancedFilterController extends BaseController { } if (context != null) { - mailBoxFilterInputController.text = _memorySearchFilter.mailbox == null + selectedFolderName.value = _memorySearchFilter.mailbox == null ? AppLocalizations.of(context).allFolders : StringConvert.writeNullToEmpty( _memorySearchFilter.mailbox?.getDisplayName(context)); @@ -450,6 +450,7 @@ class AdvancedFilterController extends BaseController { endDate.value = null; receiveTimeType.value = EmailReceiveTimeType.allTime; hasAttachment.value = false; + selectedFolderName.value = null; listFromEmailAddress.clear(); listToEmailAddress.clear(); _destinationMailboxSelected = null; @@ -459,7 +460,6 @@ class AdvancedFilterController extends BaseController { subjectFilterInputController.clear(); hasKeyWordFilterInputController.clear(); notKeyWordFilterInputController.clear(); - mailBoxFilterInputController.clear(); fromEmailAddressController.clear(); toEmailAddressController.clear(); } @@ -600,7 +600,6 @@ class AdvancedFilterController extends BaseController { subjectFilterInputController.dispose(); hasKeyWordFilterInputController.dispose(); notKeyWordFilterInputController.dispose(); - mailBoxFilterInputController.dispose(); toEmailAddressController.dispose(); fromEmailAddressController.dispose(); _unregisterWorkerListener(); diff --git a/lib/features/mailbox_dashboard/presentation/styles/advanced_search_input_form_style.dart b/lib/features/mailbox_dashboard/presentation/styles/advanced_search_input_form_style.dart index b0d338148..a8a95db03 100644 --- a/lib/features/mailbox_dashboard/presentation/styles/advanced_search_input_form_style.dart +++ b/lib/features/mailbox_dashboard/presentation/styles/advanced_search_input_form_style.dart @@ -10,6 +10,8 @@ class AdvancedSearchInputFormStyle { static const double inputFieldBorderRadius = 10; static const double inputFieldBorderWidth = 1; static const double inputFieldHeight = 40; + static const double labelMaxWidth = 112; + static const double labelSpace = 12; static const Color inputFieldBorderColor = AppColor.m3Neutral90; static const Color inputFieldBackgroundColor = Colors.white; diff --git a/lib/features/mailbox_dashboard/presentation/styles/sort_by_drop_down_style.dart b/lib/features/mailbox_dashboard/presentation/styles/sort_by_drop_down_style.dart index 6e6b79143..ca721a906 100644 --- a/lib/features/mailbox_dashboard/presentation/styles/sort_by_drop_down_style.dart +++ b/lib/features/mailbox_dashboard/presentation/styles/sort_by_drop_down_style.dart @@ -1,26 +1,41 @@ +import 'package:core/presentation/extensions/color_extension.dart'; +import 'package:core/presentation/utils/theme_utils.dart'; import 'package:dropdown_button2/dropdown_button2.dart'; import 'package:flutter/material.dart'; class SortByDropdownStyle { - static const int dropdownElevation = 4; + static const int dropdownElevation = 0; static const double height = 44; static const double dropdownMaxHeight = 332; static const double scrollbarThickness = 6; static const double checkedIconSize = 20; - static const Offset dropdownOffset = Offset(0.0, -8.0); + static const Offset dropdownOffset = Offset(0.0, -3.0); - static const BoxDecoration dropdownDecoration = BoxDecoration( - borderRadius: BorderRadius.all(Radius.circular(10)), - color: Colors.white + static BoxDecoration dropdownDecoration = BoxDecoration( + borderRadius: const BorderRadius.all(Radius.circular(16)), + color: Colors.white, + border: Border.all(color: AppColor.m3Tertiary60), + boxShadow: const [BoxShadow(color: Colors.black12, blurRadius: 24)], ); - static const EdgeInsetsDirectional buttonPadding = EdgeInsetsDirectional.only(start: 12, end: 8); + static const EdgeInsetsGeometry buttonPadding = EdgeInsetsDirectional.only( + start: 12, + end: 8, + ); + static const EdgeInsetsGeometry dropdownPadding = EdgeInsets.all(12); static const MenuItemStyleData menuItemStyleData = MenuItemStyleData( height: height, padding: EdgeInsets.symmetric(horizontal: 12), ); static const Radius dropdownScrollbarRadius = Radius.circular(40); + + static TextStyle menuItemStyle = ThemeUtils.textStyleInter400.copyWith( + fontSize: 15, + height: 20 / 15, + letterSpacing: -0.15, + color: Colors.black, + ); } \ No newline at end of file diff --git a/lib/features/mailbox_dashboard/presentation/styles/text_field_autocomplete_email_address_web_style.dart b/lib/features/mailbox_dashboard/presentation/styles/text_field_autocomplete_email_address_web_style.dart deleted file mode 100644 index dc680cfb2..000000000 --- a/lib/features/mailbox_dashboard/presentation/styles/text_field_autocomplete_email_address_web_style.dart +++ /dev/null @@ -1,45 +0,0 @@ -import 'package:core/presentation/extensions/color_extension.dart'; -import 'package:core/presentation/utils/theme_utils.dart'; -import 'package:flutter/material.dart'; -import 'package:tmail_ui_user/features/composer/presentation/styles/composer_style.dart'; -import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/styles/advanced_search_input_form_style.dart'; - -class TextFieldAutoCompleteEmailAddressWebStyles { - static const EdgeInsetsGeometry textInputContentPadding = - EdgeInsetsDirectional.only( - top: 14, - bottom: 14, - start: 12, - end: 8, - ); - static const EdgeInsetsGeometry textInputContentPaddingWithSomeTag = - EdgeInsetsDirectional.symmetric(vertical: 14); - static const EdgeInsets tagEditorPadding = EdgeInsets.symmetric(horizontal: 12); - - static const double suggestionBoxRadius = 20.0; - - static const double minTextFieldWidth = 40.0; - - static const double suggestionBoxElevation = 20.0; - static const double suggestionBoxMaxHeight = 350.0; - static const double suggestionBoxItemHeight = ComposerStyle.suggestionItemHeight; - - static const double fieldTitleWidth = 112.0; - static const double space = 12.0; - - static const InputBorder textInputBorder = OutlineInputBorder( - borderRadius: BorderRadius.all(Radius.circular(AdvancedSearchInputFormStyle.inputFieldBorderRadius)), - borderSide: BorderSide.none, - ); - - static const Color textInputFillColor = Colors.white; - static const Color focusedBorderColor = AppColor.primaryColor; - static const Color suggestionBoxBackgroundColor = Colors.white; - static const Color cursorColor = AppColor.primaryColor; - - static TextStyle textInputHintStyle = ThemeUtils.textStyleBodyBody3( - color: AppColor.m3Tertiary, - ); - - static const Duration debounceDuration = Duration(milliseconds: 150); -} \ No newline at end of file diff --git a/lib/features/mailbox_dashboard/presentation/widgets/advanced_search/advanced_search_field_widget.dart b/lib/features/mailbox_dashboard/presentation/widgets/advanced_search/advanced_search_field_widget.dart new file mode 100644 index 000000000..e6f3185e4 --- /dev/null +++ b/lib/features/mailbox_dashboard/presentation/widgets/advanced_search/advanced_search_field_widget.dart @@ -0,0 +1,35 @@ +import 'package:flutter/material.dart'; +import 'package:tmail_ui_user/features/base/model/filter_filter.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/styles/advanced_search_input_form_style.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/widgets/advanced_search/label_advanced_search_field_widget.dart'; +import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; + +class AdvancedSearchFieldWidget extends StatelessWidget { + final FilterField filterField; + final Widget child; + + const AdvancedSearchFieldWidget({ + super.key, + required this.filterField, + required this.child, + }); + + @override + Widget build(BuildContext context) { + return SizedBox( + height: AdvancedSearchInputFormStyle.inputFieldHeight, + child: Row( + children: [ + SizedBox( + width: AdvancedSearchInputFormStyle.labelMaxWidth, + child: LabelAdvancedSearchFieldWidget( + name: filterField.getTitle(AppLocalizations.of(context)), + ), + ), + const SizedBox(width: AdvancedSearchInputFormStyle.labelSpace), + Expanded(child: child), + ], + ), + ); + } +} diff --git a/lib/features/mailbox_dashboard/presentation/widgets/advanced_search/advanced_search_input_form.dart b/lib/features/mailbox_dashboard/presentation/widgets/advanced_search/advanced_search_input_form.dart index 72ae72c4f..d8c6168c8 100644 --- a/lib/features/mailbox_dashboard/presentation/widgets/advanced_search/advanced_search_input_form.dart +++ b/lib/features/mailbox_dashboard/presentation/widgets/advanced_search/advanced_search_input_form.dart @@ -1,157 +1,146 @@ -import 'package:core/core.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:flutter_svg/flutter_svg.dart'; import 'package:get/get.dart'; -import 'package:tmail_ui_user/features/base/mixin/popup_context_menu_action_mixin.dart'; -import 'package:tmail_ui_user/features/base/widget/popup_item_no_icon_widget.dart'; +import 'package:tmail_ui_user/features/base/widget/default_field/default_autocomplete_input_field_widget.dart'; +import 'package:tmail_ui_user/features/base/widget/default_field/default_button_arrow_down_field_with_tab_key_widget.dart'; +import 'package:tmail_ui_user/features/base/widget/default_field/default_date_drop_down_field_widget.dart'; +import 'package:tmail_ui_user/features/base/widget/default_field/default_input_field_with_tab_key_widget.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/advanced_filter_controller.dart'; import 'package:tmail_ui_user/features/base/model/filter_filter.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/model/search/email_receive_time_type.dart'; -import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/styles/advanced_search_input_form_style.dart'; +import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/widgets/advanced_search/advanced_search_field_widget.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/widgets/advanced_search/advanced_search_filter_form_bottom_view.dart'; -import 'package:tmail_ui_user/features/base/widget/default_field/date_drop_down_button.dart'; -import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/widgets/advanced_search/label_advanced_search_field_widget.dart'; import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/widgets/advanced_search/sort_by_drop_down_button.dart'; -import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/widgets/advanced_search/text_field_autocomplete_email_address_web.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; -import 'package:tmail_ui_user/main/routes/route_navigation.dart'; -class AdvancedSearchInputForm extends GetWidget - with PopupContextMenuActionMixin { - AdvancedSearchInputForm({ - Key? key, - }) : super(key: key); +class AdvancedSearchInputForm extends GetWidget { + const AdvancedSearchInputForm({Key? key}) : super(key: key); @override Widget build(BuildContext context) { + final appLocalizations = AppLocalizations.of(context); return FocusTraversalGroup( child: Column( children: [ - Obx(() => TextFieldAutocompleteEmailAddressWeb( - field: FilterField.from, - listEmailAddress: controller.listFromEmailAddress, - expandMode: controller.fromAddressExpandMode.value, - minInputLengthAutocomplete: controller - .mailboxDashBoardController - .minInputLengthAutocomplete, - controller: controller.fromEmailAddressController, - focusNode: controller.focusManager.fromFieldFocusNode, - nextFocusNode: controller.focusManager.toFieldFocusNode, - keyTagEditor: controller.keyFromEmailTagEditor, - onShowFullListEmailAddressAction: controller.showFullEmailAddress, - onUpdateListEmailAddressAction: controller.updateListEmailAddress, - onSuggestionEmailAddress: controller.getAutoCompleteSuggestion, - onSearchAction: controller.onSearchAction, - onRemoveDraggableEmailAddressAction: controller.removeDraggableEmailAddress, - )), + AdvancedSearchFieldWidget( + filterField: FilterField.from, + child: Obx(() => DefaultAutocompleteInputFieldWidget( + field: FilterField.from, + listEmailAddress: controller.listFromEmailAddress, + expandMode: controller.fromAddressExpandMode.value, + minInputLengthAutocomplete: controller + .mailboxDashBoardController + .minInputLengthAutocomplete, + controller: controller.fromEmailAddressController, + focusNode: controller.focusManager.fromFieldFocusNode, + nextFocusNode: controller.focusManager.toFieldFocusNode, + keyTagEditor: controller.keyFromEmailTagEditor, + onShowFullListEmailAddressAction: controller.showFullEmailAddress, + onUpdateListEmailAddressAction: controller.updateListEmailAddress, + onSuggestionEmailAddress: controller.getAutoCompleteSuggestion, + onSearchAction: controller.onSearchAction, + onRemoveDraggableEmailAddressAction: controller.removeDraggableEmailAddress, + )), + ), const SizedBox(height: 12), - Obx(() => TextFieldAutocompleteEmailAddressWeb( - field: FilterField.to, - listEmailAddress: controller.listToEmailAddress, - expandMode: controller.toAddressExpandMode.value, - minInputLengthAutocomplete: controller - .mailboxDashBoardController - .minInputLengthAutocomplete, - controller: controller.toEmailAddressController, - focusNode: controller.focusManager.toFieldFocusNode, - nextFocusNode: controller.focusManager.subjectFieldFocusNode, - keyTagEditor: controller.keyToEmailTagEditor, - onShowFullListEmailAddressAction: controller.showFullEmailAddress, - onUpdateListEmailAddressAction: controller.updateListEmailAddress, - onSuggestionEmailAddress: controller.getAutoCompleteSuggestion, - onSearchAction: controller.onSearchAction, - onRemoveDraggableEmailAddressAction: controller.removeDraggableEmailAddress, - )), + AdvancedSearchFieldWidget( + filterField: FilterField.to, + child: Obx(() => DefaultAutocompleteInputFieldWidget( + field: FilterField.to, + listEmailAddress: controller.listToEmailAddress, + expandMode: controller.toAddressExpandMode.value, + minInputLengthAutocomplete: controller + .mailboxDashBoardController + .minInputLengthAutocomplete, + controller: controller.toEmailAddressController, + focusNode: controller.focusManager.toFieldFocusNode, + nextFocusNode: controller.focusManager.subjectFieldFocusNode, + keyTagEditor: controller.keyToEmailTagEditor, + onShowFullListEmailAddressAction: controller.showFullEmailAddress, + onUpdateListEmailAddressAction: controller.updateListEmailAddress, + onSuggestionEmailAddress: controller.getAutoCompleteSuggestion, + onSearchAction: controller.onSearchAction, + onRemoveDraggableEmailAddressAction: controller.removeDraggableEmailAddress, + )), + ), const SizedBox(height: 12), - _buildFilterField( - textEditingController: controller.subjectFilterInputController, - context: context, + AdvancedSearchFieldWidget( filterField: FilterField.subject, - currentFocusNode: controller.focusManager.subjectFieldFocusNode, - nextFocusNode: controller.focusManager.hasKeywordFieldFocusNode, - onTextChange: (value) => controller.onTextChanged( - FilterField.subject, - value + child: DefaultInputFieldWithTabKeyWidget( + textEditingController: controller.subjectFilterInputController, + currentFocusNode: controller.focusManager.subjectFieldFocusNode, + nextFocusNode: controller.focusManager.hasKeywordFieldFocusNode, + hintText: FilterField.subject.getHintText(appLocalizations), + onTextChange: (value) => controller.onTextChanged( + FilterField.subject, + value, + ), ), ), const SizedBox(height: 12), - _buildFilterField( - textEditingController: controller.hasKeyWordFilterInputController, - context: context, + AdvancedSearchFieldWidget( filterField: FilterField.hasKeyword, - currentFocusNode: controller.focusManager.hasKeywordFieldFocusNode, - nextFocusNode: controller.focusManager.notKeywordFieldFocusNode, - onTextChange: (value) => controller.onTextChanged( - FilterField.hasKeyword, - value + child: DefaultInputFieldWithTabKeyWidget( + textEditingController: controller.hasKeyWordFilterInputController, + currentFocusNode: controller.focusManager.hasKeywordFieldFocusNode, + nextFocusNode: controller.focusManager.notKeywordFieldFocusNode, + hintText: FilterField.hasKeyword.getHintText(appLocalizations), + onTextChange: (value) => controller.onTextChanged( + FilterField.hasKeyword, + value, + ), ), ), const SizedBox(height: 12), - _buildFilterField( - textEditingController: controller.notKeyWordFilterInputController, - context: context, + AdvancedSearchFieldWidget( filterField: FilterField.notKeyword, - currentFocusNode: controller.focusManager.notKeywordFieldFocusNode, - nextFocusNode: controller.focusManager.mailboxFieldFocusNode, - onTextChange: (value) => controller.onTextChanged( - FilterField.notKeyword, - value + child: DefaultInputFieldWithTabKeyWidget( + textEditingController: controller.notKeyWordFilterInputController, + currentFocusNode: controller.focusManager.notKeywordFieldFocusNode, + nextFocusNode: controller.focusManager.mailboxFieldFocusNode, + hintText: FilterField.notKeyword.getHintText(appLocalizations), + onTextChange: (value) => controller.onTextChanged( + FilterField.notKeyword, + value, + ), ), ), const SizedBox(height: 12), - _buildFilterField( - textEditingController: controller.mailBoxFilterInputController, - context: context, + AdvancedSearchFieldWidget( filterField: FilterField.mailBox, - isSelectFormList: true, - currentFocusNode: controller.focusManager.mailboxFieldFocusNode, - nextFocusNode: controller.focusManager.attachmentCheckboxFocusNode, - mouseCursor: SystemMouseCursors.click, - onTap: () => controller.selectedMailBox(context) + child: Obx(() => DefaultButtonArrowDownFieldWithTabKeyWidget( + text: controller.selectedFolderName.value ?? + AppLocalizations.of(context).allFolders, + iconArrowDown: controller.imagePaths.icDropDown, + currentFocusNode: controller.focusManager.mailboxFieldFocusNode, + nextFocusNode: controller.focusManager.attachmentCheckboxFocusNode, + onTap: () => controller.selectedMailBox(context), + )), ), const SizedBox(height: 12), - Row(children: [ - Expanded(child: _buildFilterField( - context: context, - filterField: FilterField.date, - isSelectFormList: true, - onTap: () { - openContextMenuAction( - context, - _buildEmailReceiveTimeTypeActionTiles(context), - ); - }, - )), - const SizedBox(width: 8), - Container( - decoration: BoxDecoration( - border: Border.all( - color: AdvancedSearchInputFormStyle.inputFieldBorderColor, - width: AdvancedSearchInputFormStyle.inputFieldBorderWidth, - ), - borderRadius: const BorderRadius.all( - Radius.circular( - AdvancedSearchInputFormStyle.inputFieldBorderRadius, + AdvancedSearchFieldWidget( + filterField: FilterField.date, + child: Obx(() => DefaultDateDropDownFieldWidget( + imagePaths: controller.imagePaths, + receiveTimeTypes: EmailReceiveTimeType.valuesForSearch, + startDate: controller.startDate.value, + endDate: controller.endDate.value, + receiveTimeTypeSelected: controller.receiveTimeType.value, + onReceiveTimeSelected: (receiveTime) => + controller.updateReceiveDateSearchFilter( + context, + receiveTime, ), - ), - ), - height: AdvancedSearchInputFormStyle.inputFieldHeight, - width: AdvancedSearchInputFormStyle.inputFieldHeight, - child: TMailButtonWidget.fromIcon( - icon: controller.imagePaths.icCalendarSB, - iconSize: 22, - iconColor: AppColor.steelGray400, - backgroundColor: Colors.transparent, - tooltipMessage: AppLocalizations.of(context).selectDate, - onTapActionCallback: () => controller.selectDateRange(context), - ), - ), - ]), + onOpenDatPicker: () => controller.selectDateRange(context), + )), + ), const SizedBox(height: 12), - _buildFilterField( - context: context, - filterField: FilterField.sortBy + AdvancedSearchFieldWidget( + filterField: FilterField.sortBy, + child: Obx(() => SortByDropDownButton( + imagePaths: controller.imagePaths, + sortOrderSelected: controller.sortOrderType.value, + onSortOrderSelected: controller.updateSortOrder, + )), ), const SizedBox(height: 24), AdvancedSearchFilterFormBottomView(focusManager: controller.focusManager) @@ -159,243 +148,4 @@ class AdvancedSearchInputForm extends GetWidget ), ); } - - List _buildEmailReceiveTimeTypeActionTiles(BuildContext context) { - return EmailReceiveTimeType.valuesForSearch - .map((receiveTime) => PopupMenuItem( - padding: EdgeInsets.zero, - child: PopupItemNoIconWidget( - receiveTime.getTitle(context), - svgIconSelected: controller.imagePaths.icFilterSelected, - maxWidth: 320, - isSelected: controller.receiveTimeType.value == receiveTime, - onCallbackAction: () => controller.updateReceiveDateSearchFilter(context, receiveTime), - ))) - .toList(); - } - - Widget _buildFilterField({ - required BuildContext context, - required FilterField filterField, - TextEditingController? textEditingController, - VoidCallback? onTap, - bool isSelectFormList = false, - MouseCursor? mouseCursor, - FocusNode? currentFocusNode, - FocusNode? nextFocusNode, - ValueChanged? onTextChange, - }) { - final child = [ - SizedBox( - width: _isVerticalArrange(context) ? null : 112, - child: LabelAdvancedSearchFieldWidget( - name: filterField.getTitle(AppLocalizations.of(context)), - ), - ), - if (_isVerticalArrange(context)) - const SizedBox(height: 12) - else - const SizedBox(width: 12), - if (controller.responsiveUtils.isMobile(context)) - _buildTextField( - isSelectFormList: isSelectFormList, - onTap: onTap, - context: context, - mouseCursor: mouseCursor, - currentFocusNode: currentFocusNode, - nextFocusNode: nextFocusNode, - filterField: filterField, - textEditingController: textEditingController, - onTextChange: onTextChange, - ) - else if (controller.responsiveUtils.landscapeTabletSupported(context)) - if (filterField == FilterField.date) - Obx(() => DateDropDownButton( - imagePaths: controller.imagePaths, - receiveTimeTypes: EmailReceiveTimeType.valuesForSearch, - startDate: controller.startDate.value, - endDate: controller.endDate.value, - receiveTimeTypeSelected: controller.receiveTimeType.value, - onReceiveTimeSelected: (receiveTime) => controller.updateReceiveDateSearchFilter(context, receiveTime), - )) - else if (filterField == FilterField.sortBy) - Obx(() => SortByDropDownButton( - imagePaths: controller.imagePaths, - sortOrderSelected: controller.sortOrderType.value, - onSortOrderSelected: controller.updateSortOrder, - )) - else - _buildTextField( - isSelectFormList: isSelectFormList, - onTap: onTap, - context: context, - mouseCursor: mouseCursor, - currentFocusNode: currentFocusNode, - nextFocusNode: nextFocusNode, - filterField: filterField, - textEditingController: textEditingController, - onTextChange: onTextChange, - ) - else - Expanded( - child: _buildTextFieldFilterForWeb( - isSelectFormList: isSelectFormList, - onTap: onTap, - context: context, - mouseCursor: mouseCursor, - currentFocusNode: currentFocusNode, - nextFocusNode: nextFocusNode, - filterField: filterField, - textEditingController: textEditingController, - onTextChange: onTextChange, - ), - ) - ]; - return _isVerticalArrange(context) - ? Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: child, - ) - : SizedBox( - height: AdvancedSearchInputFormStyle.inputFieldHeight, - child: Row(children: child), - ); - } - - bool _isVerticalArrange(BuildContext context) => - controller.responsiveUtils.isMobile(context) || - controller.responsiveUtils.landscapeTabletSupported(context); - - Widget _buildTextFieldFilterForWeb({ - required BuildContext context, - required FilterField filterField, - TextEditingController? textEditingController, - VoidCallback? onTap, - bool isSelectFormList = false, - MouseCursor? mouseCursor, - FocusNode? currentFocusNode, - FocusNode? nextFocusNode, - ValueChanged? onTextChange, - }) { - switch (filterField) { - case FilterField.date: - return Obx(() => DateDropDownButton( - imagePaths: controller.imagePaths, - receiveTimeTypes: EmailReceiveTimeType.valuesForSearch, - startDate: controller.startDate.value, - endDate: controller.endDate.value, - receiveTimeTypeSelected: controller.receiveTimeType.value, - onReceiveTimeSelected: (receiveTime) => controller.updateReceiveDateSearchFilter(context, receiveTime), - )); - case FilterField.sortBy: - return Obx(() => SortByDropDownButton( - imagePaths: controller.imagePaths, - sortOrderSelected: controller.sortOrderType.value, - onSortOrderSelected: controller.updateSortOrder, - )); - default: - return _buildTextField( - isSelectFormList: isSelectFormList, - onTap: onTap, - context: context, - mouseCursor: mouseCursor, - currentFocusNode: currentFocusNode, - nextFocusNode: nextFocusNode, - filterField: filterField, - textEditingController: textEditingController, - onTextChange: onTextChange, - ); - } - } - - Widget _buildTextField({ - required BuildContext context, - required FilterField filterField, - TextEditingController? textEditingController, - VoidCallback? onTap, - bool isSelectFormList = false, - MouseCursor? mouseCursor, - FocusNode? currentFocusNode, - FocusNode? nextFocusNode, - ValueChanged? onTextChange, - }) { - return KeyboardListener( - focusNode: currentFocusNode ?? FocusNode(), - onKeyEvent: (event) { - if (event is KeyDownEvent && - event.logicalKey == LogicalKeyboardKey.tab) { - nextFocusNode?.requestFocus(); - } - }, - child: TextFieldBuilder( - controller: textEditingController, - readOnly: isSelectFormList, - mouseCursor: mouseCursor, - maxLines: 1, - textInputAction: isSelectFormList ? TextInputAction.done : TextInputAction.next, - textStyle: AdvancedSearchInputFormStyle.inputTextStyle, - onTap: onTap, - onTextSubmitted: (value) { - if (isSelectFormList) { - onTap?.call(); - } else { - controller.onSearchAction(); - popBack(); - } - }, - onTextChange: onTextChange, - decoration: InputDecoration( - constraints: const BoxConstraints( - maxHeight: AdvancedSearchInputFormStyle.inputFieldHeight, - ), - filled: true, - fillColor: AdvancedSearchInputFormStyle.inputFieldBackgroundColor, - contentPadding: const EdgeInsetsDirectional.only(start: 12, end: 8), - enabledBorder: const OutlineInputBorder( - borderRadius: BorderRadius.all( - Radius.circular(AdvancedSearchInputFormStyle.inputFieldBorderRadius), - ), - borderSide: BorderSide( - width: AdvancedSearchInputFormStyle.inputFieldBorderWidth, - color: AdvancedSearchInputFormStyle.inputFieldBorderColor, - ), - ), - border: const OutlineInputBorder( - borderRadius: BorderRadius.all( - Radius.circular(AdvancedSearchInputFormStyle.inputFieldBorderRadius), - ), - borderSide: BorderSide( - width: AdvancedSearchInputFormStyle.inputFieldBorderWidth, - color: AdvancedSearchInputFormStyle.inputFieldBorderColor, - ), - ), - focusedBorder: const OutlineInputBorder( - borderRadius: BorderRadius.all( - Radius.circular(AdvancedSearchInputFormStyle.inputFieldBorderRadius), - ), - borderSide: BorderSide( - width: AdvancedSearchInputFormStyle.inputFieldBorderWidth, - color: AppColor.primaryColor, - ), - ), - hintText: filterField.getHintText(AppLocalizations.of(context)), - hintStyle: ThemeUtils.textStyleBodyBody3( - color: filterField == FilterField.mailBox - ? Colors.black - : AppColor.m3Tertiary, - ), - suffixIconConstraints: const BoxConstraints(minHeight: 24, minWidth: 24), - suffixIcon: isSelectFormList - ? buildIconWeb( - icon: SvgPicture.asset( - controller.imagePaths.icDropDown, - ), - ) - : null, - ), - ), - ); - } } diff --git a/lib/features/mailbox_dashboard/presentation/widgets/advanced_search/sort_by_drop_down_button.dart b/lib/features/mailbox_dashboard/presentation/widgets/advanced_search/sort_by_drop_down_button.dart index 2ecdc904f..b7aa609a7 100644 --- a/lib/features/mailbox_dashboard/presentation/widgets/advanced_search/sort_by_drop_down_button.dart +++ b/lib/features/mailbox_dashboard/presentation/widgets/advanced_search/sort_by_drop_down_button.dart @@ -1,5 +1,4 @@ import 'package:core/presentation/resources/image_paths.dart'; -import 'package:core/presentation/utils/style_utils.dart'; import 'package:dropdown_button2/dropdown_button2.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; @@ -32,30 +31,26 @@ class SortByDropDownButton extends StatelessWidget { items: EmailSortOrderType.values .map((sortType) => DropdownMenuItem( value: sortType, + enabled: sortType != sortOrderSelected, child: PointerInterceptor( - child: Container( - color: Colors.transparent, - height: SortByDropdownStyle.height, - child: Row( - children: [ - Expanded( - child: Text( - sortType.getTitle(context), - style: sortType.getTextStyle(isInDropdown: true), - maxLines: 1, - softWrap: CommonTextStyle.defaultSoftWrap, - overflow: CommonTextStyle.defaultTextOverFlow, - ), + child: Row( + children: [ + Expanded( + child: Text( + sortType.getTitle(context), + style: SortByDropdownStyle.menuItemStyle, + maxLines: 1, + overflow: TextOverflow.ellipsis, ), - if (sortType == sortOrderSelected) - SvgPicture.asset( - imagePaths.icChecked, - width: SortByDropdownStyle.checkedIconSize, - height: SortByDropdownStyle.checkedIconSize, - fit: BoxFit.fill, - ) - ], - ), + ), + if (sortType == sortOrderSelected) + SvgPicture.asset( + imagePaths.icChecked, + width: SortByDropdownStyle.checkedIconSize, + height: SortByDropdownStyle.checkedIconSize, + fit: BoxFit.fill, + ), + ], ) ) )).toList(), @@ -80,8 +75,7 @@ class SortByDropDownButton extends StatelessWidget { sortOrderSelected?.getTitle(context) ?? '', style: AdvancedSearchInputFormStyle.inputTextStyle, maxLines: 1, - softWrap: CommonTextStyle.defaultSoftWrap, - overflow: CommonTextStyle.defaultTextOverFlow, + overflow: TextOverflow.ellipsis, ) ), SvgPicture.asset(imagePaths.icDropDown) @@ -89,35 +83,19 @@ class SortByDropDownButton extends StatelessWidget { ), ), onChanged: onSortOrderSelected, - buttonStyleData: ButtonStyleData( - height: AdvancedSearchInputFormStyle.inputFieldHeight, - padding: SortByDropdownStyle.buttonPadding, - decoration: BoxDecoration( - borderRadius: const BorderRadius.all( - Radius.circular(AdvancedSearchInputFormStyle.inputFieldBorderRadius), - ), - border: Border.all( - color: AdvancedSearchInputFormStyle.inputFieldBorderColor, - width: AdvancedSearchInputFormStyle.inputFieldBorderWidth, - ), - color: AdvancedSearchInputFormStyle.inputFieldBackgroundColor, - ) - ), dropdownStyleData: DropdownStyleData( maxHeight: SortByDropdownStyle.dropdownMaxHeight, decoration: SortByDropdownStyle.dropdownDecoration, elevation: SortByDropdownStyle.dropdownElevation, offset: SortByDropdownStyle.dropdownOffset, + padding: SortByDropdownStyle.dropdownPadding, scrollbarTheme: ScrollbarThemeData( radius: SortByDropdownStyle.dropdownScrollbarRadius, thickness: WidgetStateProperty.all(SortByDropdownStyle.scrollbarThickness), thumbVisibility: WidgetStateProperty.all(true), ) ), - iconStyleData: IconStyleData( - icon: SvgPicture.asset(imagePaths.icDropDown), - ), - menuItemStyleData: SortByDropdownStyle.menuItemStyleData + menuItemStyleData: SortByDropdownStyle.menuItemStyleData, ) ) ); diff --git a/lib/features/mailbox_dashboard/presentation/widgets/advanced_search/text_field_autocomplete_email_address_web.dart b/lib/features/mailbox_dashboard/presentation/widgets/advanced_search/text_field_autocomplete_email_address_web.dart deleted file mode 100644 index 27b1f8117..000000000 --- a/lib/features/mailbox_dashboard/presentation/widgets/advanced_search/text_field_autocomplete_email_address_web.dart +++ /dev/null @@ -1,409 +0,0 @@ -import 'dart:async'; -import 'dart:math'; - -import 'package:collection/collection.dart'; -import 'package:core/presentation/extensions/color_extension.dart'; -import 'package:core/presentation/resources/image_paths.dart'; -import 'package:core/presentation/utils/responsive_utils.dart'; -import 'package:core/utils/app_logger.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:get/get.dart'; -import 'package:jmap_dart_client/jmap/mail/email/email_address.dart'; -import 'package:model/extensions/email_address_extension.dart'; -import 'package:model/mailbox/expand_mode.dart'; -import 'package:super_tag_editor/tag_editor.dart'; -import 'package:tmail_ui_user/features/base/model/filter_filter.dart'; -import 'package:tmail_ui_user/features/base/widget/default_field/default_autocomplete_tag_item_widget.dart'; -import 'package:tmail_ui_user/features/composer/presentation/model/draggable_email_address.dart'; -import 'package:tmail_ui_user/features/composer/presentation/model/suggestion_email_address.dart'; -import 'package:tmail_ui_user/features/composer/presentation/widgets/recipient_composer_widget.dart'; -import 'package:tmail_ui_user/features/composer/presentation/widgets/recipient_suggestion_item_widget.dart'; -import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/styles/advanced_search_input_form_style.dart'; -import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/styles/text_field_autocomplete_email_address_web_style.dart'; -import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/widgets/advanced_search/label_advanced_search_field_widget.dart'; -import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; -import 'package:tmail_ui_user/main/utils/app_config.dart'; - -typedef OnSuggestionEmailAddress = Future> Function(String word, {int? limit}); -typedef OnUpdateListEmailAddressAction = void Function(FilterField field, List newData); -typedef OnDeleteEmailAddressTypeAction = void Function(FilterField field); -typedef OnShowFullListEmailAddressAction = void Function(FilterField field); -typedef OnDeleteTagAction = void Function(EmailAddress emailAddress); - -class TextFieldAutocompleteEmailAddressWeb extends StatefulWidget { - - final FilterField field; - final List listEmailAddress; - final ExpandMode expandMode; - final FocusNode? focusNode; - final GlobalKey? keyTagEditor; - final FocusNode? nextFocusNode; - final EdgeInsetsGeometry? padding; - final EdgeInsetsGeometry? margin; - final OnSuggestionEmailAddress? onSuggestionEmailAddress; - final OnDeleteTagAction? onDeleteTag; - final OnUpdateListEmailAddressAction? onUpdateListEmailAddressAction; - final OnDeleteEmailAddressTypeAction? onDeleteEmailAddressTypeAction; - final OnShowFullListEmailAddressAction? onShowFullListEmailAddressAction; - final OnRemoveDraggableEmailAddressAction? onRemoveDraggableEmailAddressAction; - final TextEditingController? controller; - final VoidCallback? onSearchAction; - final int minInputLengthAutocomplete; - - const TextFieldAutocompleteEmailAddressWeb({ - Key? key, - required this.field, - required this.listEmailAddress, - this.expandMode = ExpandMode.EXPAND, - this.minInputLengthAutocomplete = AppConfig.defaultMinInputLengthAutocomplete, - this.focusNode, - this.keyTagEditor, - this.nextFocusNode, - this.padding, - this.margin, - this.onSuggestionEmailAddress, - this.onDeleteTag, - this.onUpdateListEmailAddressAction, - this.onDeleteEmailAddressTypeAction, - this.onShowFullListEmailAddressAction, - this.onRemoveDraggableEmailAddressAction, - this.controller, - this.onSearchAction, - }) : super(key: key); - - @override - State createState() => _TextFieldAutocompleteEmailAddressWebState(); -} - -class _TextFieldAutocompleteEmailAddressWebState extends State { - final _imagePaths = Get.find(); - - bool _lastTagFocused = false; - bool _isDragging = false; - late List _currentListEmailAddress; - Timer? _gapBetweenTagChangedAndFindSuggestion; - - @override - void initState() { - super.initState(); - _currentListEmailAddress = widget.listEmailAddress; - } - - @override - void didUpdateWidget(covariant TextFieldAutocompleteEmailAddressWeb oldWidget) { - super.didUpdateWidget(oldWidget); - if (widget.listEmailAddress != oldWidget.listEmailAddress) { - _currentListEmailAddress = widget.listEmailAddress; - } - } - - @override - Widget build(BuildContext context) { - return LayoutBuilder( - builder: ((context, constraints) { - return Row( - children: [ - SizedBox( - width: TextFieldAutoCompleteEmailAddressWebStyles.fieldTitleWidth, - child: LabelAdvancedSearchFieldWidget( - name: widget.field.getTitle(AppLocalizations.of(context)), - ), - ), - const SizedBox(width: TextFieldAutoCompleteEmailAddressWebStyles.space), - Expanded( - child: StatefulBuilder( - builder: ((context, setState) { - return DragTarget( - onAcceptWithDetails: (draggableEmailAddress) => - _handleAcceptDraggableEmailAddressAction( - draggableEmailAddress.data, - setState - ), - onLeave: (draggableEmailAddress) { - if (_isDragging) { - setState(() => _isDragging = false); - } - }, - onMove: (details) { - if (!_isDragging) { - setState(() => _isDragging = true); - } - }, - builder: (_, __, ___) { - return TagEditor( - key: widget.keyTagEditor, - length: _collapsedListEmailAddress.length, - controller: widget.controller, - focusNodeKeyboard: widget.focusNode, - keyboardType: TextInputType.emailAddress, - textInputAction: TextInputAction.done, - cursorColor: TextFieldAutoCompleteEmailAddressWebStyles.cursorColor, - debounceDuration: TextFieldAutoCompleteEmailAddressWebStyles.debounceDuration, - inputDecoration: InputDecoration( - filled: true, - constraints: const BoxConstraints( - maxHeight: - AdvancedSearchInputFormStyle.inputFieldHeight, - ), - fillColor: TextFieldAutoCompleteEmailAddressWebStyles.textInputFillColor, - border: TextFieldAutoCompleteEmailAddressWebStyles.textInputBorder, - hintText: widget.field.getHintText(AppLocalizations.of(context)), - hintStyle: TextFieldAutoCompleteEmailAddressWebStyles.textInputHintStyle, - isDense: true, - contentPadding: _currentListEmailAddress.isNotEmpty - ? TextFieldAutoCompleteEmailAddressWebStyles.textInputContentPaddingWithSomeTag - : TextFieldAutoCompleteEmailAddressWebStyles.textInputContentPadding - ), - padding: _currentListEmailAddress.isNotEmpty - ? TextFieldAutoCompleteEmailAddressWebStyles.tagEditorPadding - : EdgeInsets.zero, - borderRadius: AdvancedSearchInputFormStyle.inputFieldBorderRadius, - borderSize: AdvancedSearchInputFormStyle.inputFieldBorderWidth, - focusedBorderColor: TextFieldAutoCompleteEmailAddressWebStyles.focusedBorderColor, - enableBorder: true, - enableBorderColor: _isDragging - ? AppColor.primaryColor - : AdvancedSearchInputFormStyle.inputFieldBorderColor, - minTextFieldWidth: TextFieldAutoCompleteEmailAddressWebStyles.minTextFieldWidth, - resetTextOnSubmitted: true, - autoScrollToInput: false, - suggestionsBoxElevation: TextFieldAutoCompleteEmailAddressWebStyles.suggestionBoxElevation, - suggestionsBoxBackgroundColor: TextFieldAutoCompleteEmailAddressWebStyles.suggestionBoxBackgroundColor, - suggestionsBoxRadius: TextFieldAutoCompleteEmailAddressWebStyles.suggestionBoxRadius, - suggestionsBoxMaxHeight: TextFieldAutoCompleteEmailAddressWebStyles.suggestionBoxMaxHeight, - suggestionItemHeight: TextFieldAutoCompleteEmailAddressWebStyles.suggestionBoxItemHeight, - suggestionBoxWidth: _getSuggestionBoxWidth(constraints.maxWidth), - textStyle: AdvancedSearchInputFormStyle.inputTextStyle, - onFocusTagAction: (focused) => _handleFocusTagAction.call(focused, setState), - onDeleteTagAction: () => _handleDeleteLatestTagAction.call(setState), - onSelectOptionAction: (item) => _handleSelectOptionAction.call(item, setState), - onSubmitted: (value) => _handleSubmitTagAction.call(value, setState), - tagBuilder: (context, index) { - final currentEmailAddress = _currentListEmailAddress.elementAt(index); - final isLatestEmail = currentEmailAddress == _currentListEmailAddress.last; - return DefaultAutocompleteTagItemWidget( - field: widget.field, - currentEmailAddress: currentEmailAddress, - currentListEmailAddress: _currentListEmailAddress, - collapsedListEmailAddress: _collapsedListEmailAddress, - isLatestEmail: isLatestEmail, - isCollapsed: _isCollapse, - isLatestTagFocused: _lastTagFocused, - iconClose: _imagePaths.icClose, - onDeleteTagAction: (emailAddress) => _handleDeleteTagAction.call(emailAddress, setState), - onShowFullAction: widget.onShowFullListEmailAddressAction, - ); - }, - onTagChanged: (tag) => _handleOnTagChangeAction.call(tag, setState), - findSuggestions: (queryString) => _findSuggestions( - queryString, - limit: AppConfig.defaultLimitAutocomplete, - ), - isLoadMoreOnlyOnce: true, - isLoadMoreReplaceAllOld: false, - loadMoreSuggestions: _findSuggestions, - suggestionBuilder: (context, tagEditorState, suggestionEmailAddress, index, length, highlight, suggestionValid) { - return RecipientSuggestionItemWidget( - imagePaths: _imagePaths, - suggestionState: suggestionEmailAddress.state, - emailAddress: suggestionEmailAddress.emailAddress, - suggestionValid: suggestionValid, - highlight: highlight, - onSelectedAction: (emailAddress) { - setState(() => _currentListEmailAddress.add(emailAddress)); - _updateListEmailAddressAction(); - tagEditorState.resetTextField(); - tagEditorState.closeSuggestionBox(); - }, - ); - }, - onHandleKeyEventAction: (event) { - if (event is KeyDownEvent) { - switch (event.logicalKey) { - case LogicalKeyboardKey.tab: - widget.nextFocusNode?.requestFocus(); - break; - default: - break; - } - } - }, - ); - } - ); - }) - ), - ), - ], - ); - }) - ); - } - - bool get _isCollapse => _currentListEmailAddress.length > 1 && widget.expandMode == ExpandMode.COLLAPSE; - - List get _collapsedListEmailAddress => _isCollapse - ? _currentListEmailAddress.sublist(0, 1) - : _currentListEmailAddress; - - FutureOr> _findSuggestions(String query, {int? limit}) async { - if (_gapBetweenTagChangedAndFindSuggestion?.isActive ?? false) { - return []; - } - - final processedQuery = query.trim(); - if (processedQuery.isEmpty) { - return []; - } - - final tmailSuggestion = List.empty(growable: true); - if (processedQuery.length >= widget.minInputLengthAutocomplete && - widget.onSuggestionEmailAddress != null) { - final listEmailAddress = await widget.onSuggestionEmailAddress!( - processedQuery, - limit: limit, - ); - final listSuggestionEmailAddress = listEmailAddress.map((emailAddress) => _toSuggestionEmailAddress(emailAddress, _currentListEmailAddress)); - tmailSuggestion.addAll(listSuggestionEmailAddress); - } - - tmailSuggestion.addAll(_matchedSuggestionEmailAddress(processedQuery, _currentListEmailAddress)); - - final currentTextOnTextField = widget.controller?.text ?? ''; - if (currentTextOnTextField.isEmpty) { - return []; - } - - return tmailSuggestion.toSet().toList(); - } - - bool _isDuplicated(String inputEmail) { - if (inputEmail.isEmpty) { - return false; - } - return _currentListEmailAddress - .map((emailAddress) => emailAddress.email) - .whereNotNull() - .contains(inputEmail); - } - - SuggestionEmailAddress _toSuggestionEmailAddress(EmailAddress item, List addedEmailAddresses) { - if (addedEmailAddresses.contains(item)) { - return SuggestionEmailAddress(item, state: SuggestionEmailState.duplicated); - } else { - return SuggestionEmailAddress(item); - } - } - - Iterable _matchedSuggestionEmailAddress(String query, List addedEmailAddress) { - return addedEmailAddress - .where((addedMail) => addedMail.emailAddress.contains(query)) - .map((emailAddress) => SuggestionEmailAddress( - emailAddress, - state: SuggestionEmailState.duplicated - )); - } - - void _handleGapBetweenTagChangedAndFindSuggestion() { - log('_TextFieldAutocompleteEmailAddressWebState::_handleGapBetweenTagChangedAndFindSuggestion:Timeout'); - } - - void _updateListEmailAddressAction() { - widget.onUpdateListEmailAddressAction?.call( - widget.field, - _currentListEmailAddress - ); - } - - void _handleFocusTagAction(bool focused, StateSetter stateSetter) { - if (mounted) { - stateSetter(() => _lastTagFocused = focused); - } - } - - void _handleDeleteLatestTagAction(StateSetter stateSetter) { - if (_currentListEmailAddress.isNotEmpty) { - stateSetter(_currentListEmailAddress.removeLast); - _updateListEmailAddressAction(); - } - } - - void _handleDeleteTagAction(EmailAddress emailAddress, StateSetter stateSetter) { - if (_currentListEmailAddress.isNotEmpty) { - stateSetter(() => _currentListEmailAddress.remove(emailAddress)); - _updateListEmailAddressAction(); - } - } - - void _handleSelectOptionAction( - SuggestionEmailAddress suggestionEmailAddress, - StateSetter stateSetter - ) { - if (!_isDuplicated(suggestionEmailAddress.emailAddress.emailAddress)) { - stateSetter(() => _currentListEmailAddress.add(suggestionEmailAddress.emailAddress)); - _updateListEmailAddressAction(); - } - } - - void _handleSubmitTagAction( - String value, - StateSetter stateSetter - ) { - final textTrim = value.trim(); - if (textTrim.isEmpty) { - widget.onSearchAction?.call(); - return; - } - - if (!_isDuplicated(textTrim)) { - stateSetter(() => _currentListEmailAddress.add(EmailAddress(null, textTrim))); - _updateListEmailAddressAction(); - } - } - - void _handleOnTagChangeAction( - String value, - StateSetter stateSetter - ) { - final textTrim = value.trim(); - if (!_isDuplicated(textTrim)) { - stateSetter(() => _currentListEmailAddress.add(EmailAddress(null, textTrim))); - _updateListEmailAddressAction(); - } - _gapBetweenTagChangedAndFindSuggestion = Timer( - const Duration(seconds: 1), - _handleGapBetweenTagChangedAndFindSuggestion - ); - } - - double? _getSuggestionBoxWidth(double maxWidth) { - if (maxWidth < ResponsiveUtils.minTabletWidth) { - final newWidth = min(maxWidth, 300.0); - return newWidth; - } else { - return null; - } - } - - void _handleAcceptDraggableEmailAddressAction( - DraggableEmailAddress draggableEmailAddress, - StateSetter stateSetter - ) { - log('_TextFieldAutocompleteEmailAddressWebState::_handleAcceptDraggableEmailAddressAction:draggableEmailAddress = $draggableEmailAddress'); - if (draggableEmailAddress.filterField != widget.field) { - if (!_currentListEmailAddress.contains(draggableEmailAddress.emailAddress)) { - stateSetter(() { - _currentListEmailAddress.add(draggableEmailAddress.emailAddress); - _isDragging = false; - }); - _updateListEmailAddressAction(); - } else if (_isDragging) { - stateSetter(() => _isDragging = false); - } - widget.onRemoveDraggableEmailAddressAction?.call(draggableEmailAddress); - } else if (_isDragging) { - stateSetter(() => _isDragging = false); - } - } -} \ No newline at end of file diff --git a/test/features/mailbox_dashboard/presentation/controller/advanced_filter_controller_test.dart b/test/features/mailbox_dashboard/presentation/controller/advanced_filter_controller_test.dart index 1ef8a4de3..efbfb3b4e 100644 --- a/test/features/mailbox_dashboard/presentation/controller/advanced_filter_controller_test.dart +++ b/test/features/mailbox_dashboard/presentation/controller/advanced_filter_controller_test.dart @@ -215,7 +215,6 @@ void main() { expect(advancedFilterController.notKeyWordFilterInputController.text, equals('hello,nice')); expect(advancedFilterController.receiveTimeType.value, equals(EmailReceiveTimeType.last7Days)); expect(advancedFilterController.sortOrderType.value, equals(EmailSortOrderType.oldest)); - expect(advancedFilterController.mailBoxFilterInputController.text, equals('mailbox1')); expect(advancedFilterController.hasAttachment.value, equals(true)); expect(advancedFilterController.listFromEmailAddress, equals([EmailAddress(null, 'user1@example.com')])); expect(advancedFilterController.listToEmailAddress, equals([EmailAddress(null, 'user2@example.com')]));