From a4b0885477cd1581a5064a24fa386dd1ea106130 Mon Sep 17 00:00:00 2001 From: dab246 Date: Tue, 26 Aug 2025 17:39:00 +0700 Subject: [PATCH] Close color picker when click outside in composer --- .../dialog/color_picker_dialog_builder.dart | 207 +++++++++--------- .../dialog_picker/color_dialog_picker.dart | 46 ++++ .../presentation/composer_view_web.dart | 8 +- .../controller/base_rich_text_controller.dart | 35 --- .../rich_text_mobile_tablet_controller.dart | 4 +- .../controller/rich_text_web_controller.dart | 8 +- .../email/presentation/email_view.dart | 8 +- 7 files changed, 166 insertions(+), 150 deletions(-) create mode 100644 lib/features/base/widget/dialog_picker/color_dialog_picker.dart delete mode 100644 lib/features/composer/presentation/controller/base_rich_text_controller.dart diff --git a/core/lib/presentation/views/dialog/color_picker_dialog_builder.dart b/core/lib/presentation/views/dialog/color_picker_dialog_builder.dart index 3883d5dbf..194407791 100644 --- a/core/lib/presentation/views/dialog/color_picker_dialog_builder.dart +++ b/core/lib/presentation/views/dialog/color_picker_dialog_builder.dart @@ -5,7 +5,6 @@ import 'package:core/presentation/views/button/icon_button_web.dart'; import 'package:flex_color_picker/flex_color_picker.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; -import 'package:pointer_interceptor/pointer_interceptor.dart'; typedef SelectColorActionCallback = Function(Color? colorSelected); @@ -41,117 +40,115 @@ class ColorPickerDialogBuilder { Future show() async { return Get.dialog( - PointerInterceptor( - child: AlertDialog( - title: Text( - title ?? '', - textAlign: TextAlign.center, - style: ThemeUtils.defaultTextStyleInterFont.copyWith( - fontWeight: FontWeight.bold, - fontSize: 20, - color: Colors.black)), - titleTextStyle: ThemeUtils.defaultTextStyleInterFont.copyWith( + AlertDialog( + title: Text( + title ?? '', + textAlign: TextAlign.center, + style: ThemeUtils.defaultTextStyleInterFont.copyWith( fontWeight: FontWeight.bold, fontSize: 20, - color: Colors.black), - titlePadding: const EdgeInsets.symmetric(vertical: 16, horizontal: 16), - contentPadding: const EdgeInsets.symmetric(vertical: 0, horizontal: 16), - actionsPadding: const EdgeInsets.symmetric(vertical: 8, horizontal: 16), - actionsAlignment: MainAxisAlignment.center, - actionsOverflowButtonSpacing: 8, - backgroundColor: Colors.white, - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15)), - scrollable: true, - elevation: 10, - content: ValueListenableBuilder( - valueListenable: _currentColor, - builder: (context, _, __) { - return Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Container( - width: 500, - decoration: const BoxDecoration( - borderRadius: BorderRadius.all(Radius.circular(4)), - color: Colors.white - ), - child: Center( - child: Wrap(children: AppColor.listColorsPicker - .map((color) => _itemColorWidget(context, color)) - .toList(), - ), + color: Colors.black)), + titleTextStyle: ThemeUtils.defaultTextStyleInterFont.copyWith( + fontWeight: FontWeight.bold, + fontSize: 20, + color: Colors.black), + titlePadding: const EdgeInsets.symmetric(vertical: 16, horizontal: 16), + contentPadding: const EdgeInsets.symmetric(vertical: 0, horizontal: 16), + actionsPadding: const EdgeInsets.symmetric(vertical: 8, horizontal: 16), + actionsAlignment: MainAxisAlignment.center, + actionsOverflowButtonSpacing: 8, + backgroundColor: Colors.white, + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15)), + scrollable: true, + elevation: 10, + content: ValueListenableBuilder( + valueListenable: _currentColor, + builder: (context, _, __) { + return Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Container( + width: 500, + decoration: const BoxDecoration( + borderRadius: BorderRadius.all(Radius.circular(4)), + color: Colors.white + ), + child: Center( + child: Wrap(children: AppColor.listColorsPicker + .map((color) => _itemColorWidget(context, color)) + .toList(), ), ), - Padding( - padding: const EdgeInsets.symmetric(vertical: 16), - child: ColorCodeField( - color: _currentColor.value, - colorCodeHasColor: true, - shouldUpdate: _shouldUpdate, - onColorChanged: (Color color) { - if (AppColor.listColorsPicker.any((element) => element.toInt() == color.toInt())) { - _shouldUpdate = true; - _currentColor.value = color; - } else { - _shouldUpdate = false; - _currentColor.value = Colors.black; - _colorCode = color; - } - }, - onEditFocused: (bool editInFocus) { - _shouldUpdate = editInFocus ? true : false; - }, - copyPasteBehavior: const ColorPickerCopyPasteBehavior( - parseShortHexCode: true, - ), - toolIcons: const ColorPickerActionButtons( - dialogActionButtons: true, - ), + ), + Padding( + padding: const EdgeInsets.symmetric(vertical: 16), + child: ColorCodeField( + color: _currentColor.value, + colorCodeHasColor: true, + shouldUpdate: _shouldUpdate, + onColorChanged: (Color color) { + if (AppColor.listColorsPicker.any((element) => element.toInt() == color.toInt())) { + _shouldUpdate = true; + _currentColor.value = color; + } else { + _shouldUpdate = false; + _currentColor.value = Colors.black; + _colorCode = color; + } + }, + onEditFocused: (bool editInFocus) { + _shouldUpdate = editInFocus ? true : false; + }, + copyPasteBehavior: const ColorPickerCopyPasteBehavior( + parseShortHexCode: true, + ), + toolIcons: const ColorPickerActionButtons( + dialogActionButtons: true, ), ), - ], - ); - }, - ), - actions: [ - buildButtonWrapText( - textActionCancel ?? '', - radius: 5, - height: 30, - textStyle: ThemeUtils.defaultTextStyleInterFont.copyWith( - color: Colors.black, - fontSize: 16, - fontWeight: FontWeight.normal), - bgColor: AppColor.colorShadowComposer, - onTap: () => cancelActionCallback?.call()), - buildButtonWrapText( - textActionResetDefault ?? '', - radius: 5, - height: 30, - textStyle: ThemeUtils.defaultTextStyleInterFont.copyWith( - color: Colors.black, - fontSize: 16, - fontWeight: FontWeight.normal), - bgColor: Colors.white, - borderColor: Colors.black26, - onTap: () => resetToDefaultActionCallback?.call()), - buildButtonWrapText( - textActionSetColor ?? '', - radius: 5, - height: 30, - textStyle: ThemeUtils.defaultTextStyleInterFont.copyWith( - color: Colors.white, - fontSize: 16, - fontWeight: FontWeight.w500), - onTap: () { - if (!_shouldUpdate) { - setColorActionCallback?.call(_colorCode); - } else { - setColorActionCallback?.call(_currentColor.value); - } - }) - ], + ), + ], + ); + }, ), + actions: [ + buildButtonWrapText( + textActionCancel ?? '', + radius: 5, + height: 30, + textStyle: ThemeUtils.defaultTextStyleInterFont.copyWith( + color: Colors.black, + fontSize: 16, + fontWeight: FontWeight.normal), + bgColor: AppColor.colorShadowComposer, + onTap: () => cancelActionCallback?.call()), + buildButtonWrapText( + textActionResetDefault ?? '', + radius: 5, + height: 30, + textStyle: ThemeUtils.defaultTextStyleInterFont.copyWith( + color: Colors.black, + fontSize: 16, + fontWeight: FontWeight.normal), + bgColor: Colors.white, + borderColor: Colors.black26, + onTap: () => resetToDefaultActionCallback?.call()), + buildButtonWrapText( + textActionSetColor ?? '', + radius: 5, + height: 30, + textStyle: ThemeUtils.defaultTextStyleInterFont.copyWith( + color: Colors.white, + fontSize: 16, + fontWeight: FontWeight.w500), + onTap: () { + if (!_shouldUpdate) { + setColorActionCallback?.call(_colorCode); + } else { + setColorActionCallback?.call(_currentColor.value); + } + }) + ], ) ); } diff --git a/lib/features/base/widget/dialog_picker/color_dialog_picker.dart b/lib/features/base/widget/dialog_picker/color_dialog_picker.dart new file mode 100644 index 000000000..a19b6aaa2 --- /dev/null +++ b/lib/features/base/widget/dialog_picker/color_dialog_picker.dart @@ -0,0 +1,46 @@ +import 'package:core/presentation/views/dialog/color_picker_dialog_builder.dart'; +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; +import 'package:tmail_ui_user/main/routes/route_navigation.dart'; + +class ColorDialogPicker { + static final _instance = ColorDialogPicker._(); + + factory ColorDialogPicker() => _instance; + + ColorDialogPicker._(); + + final _isOpened = RxBool(false); + + RxBool get isOpened => _isOpened; + + Future showTwakeColorPicker( + BuildContext context, + Color currentColor, + { + Function(Color?)? onSelectColor, + VoidCallback? onResetToDefault, + } + ) async { + _isOpened.value = true; + final appLocalizations = AppLocalizations.of(context); + + return ColorPickerDialogBuilder( + ValueNotifier(currentColor), + title: appLocalizations.chooseAColor, + textActionSetColor: appLocalizations.setColor, + textActionResetDefault: appLocalizations.resetToDefault, + textActionCancel: appLocalizations.cancel, + cancelActionCallback: popBack, + resetToDefaultActionCallback: () { + onResetToDefault?.call(); + popBack(); + }, + setColorActionCallback: (selectedColor) { + onSelectColor?.call(selectedColor); + popBack(); + }, + ).show().whenComplete(() => _isOpened.value = false); + } +} diff --git a/lib/features/composer/presentation/composer_view_web.dart b/lib/features/composer/presentation/composer_view_web.dart index 1137e74c3..2dbe00a24 100644 --- a/lib/features/composer/presentation/composer_view_web.dart +++ b/lib/features/composer/presentation/composer_view_web.dart @@ -5,6 +5,7 @@ import 'package:get/get.dart'; import 'package:model/email/prefix_email_address.dart'; import 'package:pointer_interceptor/pointer_interceptor.dart'; import 'package:tmail_ui_user/features/base/mixin/message_dialog_action_manager.dart'; +import 'package:tmail_ui_user/features/base/widget/dialog_picker/color_dialog_picker.dart'; import 'package:tmail_ui_user/features/composer/presentation/composer_controller.dart'; import 'package:tmail_ui_user/features/composer/presentation/extensions/composer_print_draft_extension.dart'; import 'package:tmail_ui_user/features/composer/presentation/extensions/handle_edit_recipient_extension.dart'; @@ -42,8 +43,11 @@ class ComposerView extends GetWidget { @override Widget build(BuildContext context) { final iframeOverlay = Obx(() { - if (controller.mailboxDashBoardController.isDisplayedOverlayViewOnIFrame || - MessageDialogActionManager().isDialogOpened) { + bool isOverlayEnabled = controller.mailboxDashBoardController.isDisplayedOverlayViewOnIFrame || + MessageDialogActionManager().isDialogOpened || + ColorDialogPicker().isOpened.isTrue; + + if (isOverlayEnabled) { return Positioned.fill( key: const ValueKey('tap-to-close'), child: PointerInterceptor( diff --git a/lib/features/composer/presentation/controller/base_rich_text_controller.dart b/lib/features/composer/presentation/controller/base_rich_text_controller.dart deleted file mode 100644 index 52d3f8c46..000000000 --- a/lib/features/composer/presentation/controller/base_rich_text_controller.dart +++ /dev/null @@ -1,35 +0,0 @@ - -import 'package:core/presentation/views/dialog/color_picker_dialog_builder.dart'; -import 'package:flutter/material.dart'; -import 'package:get/get.dart'; -import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; -import 'package:tmail_ui_user/main/routes/route_navigation.dart'; - -abstract class BaseRichTextController extends GetxController { - - void openMenuSelectColor( - BuildContext context, - Color currentColor, - { - Function(Color?)? onSelectColor, - VoidCallback? onResetToDefault, - } - ) async { - await ColorPickerDialogBuilder( - ValueNotifier(currentColor), - title: AppLocalizations.of(context).chooseAColor, - textActionSetColor: AppLocalizations.of(context).setColor, - textActionResetDefault: AppLocalizations.of(context).resetToDefault, - textActionCancel: AppLocalizations.of(context).cancel, - cancelActionCallback: () => popBack(), - resetToDefaultActionCallback: () { - onResetToDefault?.call(); - popBack(); - }, - setColorActionCallback: (selectedColor) { - onSelectColor?.call(selectedColor); - popBack(); - } - ).show(); - } -} \ No newline at end of file diff --git a/lib/features/composer/presentation/controller/rich_text_mobile_tablet_controller.dart b/lib/features/composer/presentation/controller/rich_text_mobile_tablet_controller.dart index 0501ab12a..e07c2a569 100644 --- a/lib/features/composer/presentation/controller/rich_text_mobile_tablet_controller.dart +++ b/lib/features/composer/presentation/controller/rich_text_mobile_tablet_controller.dart @@ -5,13 +5,13 @@ import 'package:core/utils/html/html_utils.dart'; import 'package:core/utils/platform_info.dart'; import 'package:file_picker/file_picker.dart'; import 'package:flutter/cupertino.dart'; +import 'package:get/get.dart'; import 'package:rich_text_composer/rich_text_composer.dart'; -import 'package:tmail_ui_user/features/composer/presentation/controller/base_rich_text_controller.dart'; import 'package:tmail_ui_user/features/composer/presentation/model/header_style_type.dart'; import 'package:tmail_ui_user/features/composer/presentation/model/inline_image.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; -class RichTextMobileTabletController extends BaseRichTextController { +class RichTextMobileTabletController extends GetxController { HtmlEditorApi? htmlEditorApi; final RichTextController richTextController = RichTextController(); diff --git a/lib/features/composer/presentation/controller/rich_text_web_controller.dart b/lib/features/composer/presentation/controller/rich_text_web_controller.dart index 636b7e96e..d83c94631 100644 --- a/lib/features/composer/presentation/controller/rich_text_web_controller.dart +++ b/lib/features/composer/presentation/controller/rich_text_web_controller.dart @@ -9,7 +9,7 @@ import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:html_editor_enhanced/html_editor.dart'; -import 'package:tmail_ui_user/features/composer/presentation/controller/base_rich_text_controller.dart'; +import 'package:tmail_ui_user/features/base/widget/dialog_picker/color_dialog_picker.dart'; import 'package:tmail_ui_user/features/composer/presentation/model/code_view_state.dart'; import 'package:tmail_ui_user/features/composer/presentation/model/dropdown_menu_font_status.dart'; import 'package:tmail_ui_user/features/composer/presentation/model/font_name_type.dart'; @@ -21,7 +21,7 @@ import 'package:tmail_ui_user/features/composer/presentation/model/paragraph_typ import 'package:tmail_ui_user/features/composer/presentation/model/rich_text_style_type.dart'; import 'package:tmail_ui_user/main/routes/route_navigation.dart'; -class RichTextWebController extends BaseRichTextController { +class RichTextWebController extends GetxController { static const List fontSizeList = [10, 12, 14, 15, 16, 18, 24, 36, 48, 64]; static const int fontSizeDefault = 16; @@ -140,7 +140,7 @@ class RichTextWebController extends BaseRichTextController { void applyRichTextStyle(BuildContext context, RichTextStyleType textStyleType) { switch(textStyleType) { case RichTextStyleType.textColor: - openMenuSelectColor( + ColorDialogPicker().showTwakeColorPicker( context, selectedTextColor.value, onResetToDefault: () => _applyForegroundColor(Colors.black), @@ -148,7 +148,7 @@ class RichTextWebController extends BaseRichTextController { ); break; case RichTextStyleType.textBackgroundColor: - openMenuSelectColor( + ColorDialogPicker().showTwakeColorPicker( context, selectedTextBackgroundColor.value, onResetToDefault: () => applyBackgroundColor(Colors.white), diff --git a/lib/features/email/presentation/email_view.dart b/lib/features/email/presentation/email_view.dart index f591bdb19..8a103ce2a 100644 --- a/lib/features/email/presentation/email_view.dart +++ b/lib/features/email/presentation/email_view.dart @@ -14,6 +14,7 @@ import 'package:model/extensions/presentation_email_extension.dart'; import 'package:model/mailbox/presentation_mailbox.dart'; import 'package:pointer_interceptor/pointer_interceptor.dart'; import 'package:tmail_ui_user/features/base/mixin/message_dialog_action_manager.dart'; +import 'package:tmail_ui_user/features/base/widget/dialog_picker/color_dialog_picker.dart'; import 'package:tmail_ui_user/features/base/widget/optional_expanded.dart'; import 'package:tmail_ui_user/features/base/widget/optional_scroll.dart'; import 'package:tmail_ui_user/features/email/presentation/controller/single_email_controller.dart'; @@ -529,9 +530,12 @@ class EmailView extends GetWidget { ), ), Obx(() { - if (controller.mailboxDashBoardController.isDisplayedOverlayViewOnIFrame || + bool isOverlayEnabled = controller.mailboxDashBoardController.isDisplayedOverlayViewOnIFrame || MessageDialogActionManager().isDialogOpened || - EmailActionReactor.isDialogOpened) { + EmailActionReactor.isDialogOpened || + ColorDialogPicker().isOpened.isTrue; + + if (isOverlayEnabled) { return Positioned.fill( child: PointerInterceptor( child: const SizedBox.expand(),