diff --git a/lib/features/base/widget/drop_down_button_widget.dart b/lib/features/base/widget/drop_down_button_widget.dart index d65f16dad..29a01c03a 100644 --- a/lib/features/base/widget/drop_down_button_widget.dart +++ b/lib/features/base/widget/drop_down_button_widget.dart @@ -27,6 +27,7 @@ class DropDownButtonWidget extends StatelessWidget { final Widget? iconArrowDown; final Color? colorButton; final String tooltip; + final double? dropdownWidth; const DropDownButtonWidget({ Key? key, @@ -41,6 +42,7 @@ class DropDownButtonWidget extends StatelessWidget { this.radiusButton = 10, this.opacity = 1.0, this.iconArrowDown, + this.dropdownWidth, this.colorButton = Colors.white, this.tooltip = '', }) : super(key: key); @@ -147,6 +149,7 @@ class DropDownButtonWidget extends StatelessWidget { scrollbarRadius: const Radius.circular(40), scrollbarThickness: 6, onMenuStateChange: onMenuStateChange, + dropdownWidth: dropdownWidth, ), ), ); diff --git a/lib/features/composer/presentation/composer_view.dart b/lib/features/composer/presentation/composer_view.dart index 7b4c67d42..da1d2ed28 100644 --- a/lib/features/composer/presentation/composer_view.dart +++ b/lib/features/composer/presentation/composer_view.dart @@ -11,10 +11,13 @@ import 'package:jmap_dart_client/jmap/identities/identity.dart'; import 'package:model/model.dart'; import 'package:tmail_ui_user/features/base/mixin/app_loader_mixin.dart'; import 'package:tmail_ui_user/features/base/widget/drop_down_button_widget.dart'; +import 'package:tmail_ui_user/features/base/widget/popup_menu_overlay_widget.dart'; import 'package:tmail_ui_user/features/composer/presentation/composer_controller.dart'; import 'package:tmail_ui_user/features/composer/presentation/mixin/composer_loading_mixin.dart'; import 'package:tmail_ui_user/features/composer/presentation/mixin/rich_text_button_mixin.dart'; import 'package:tmail_ui_user/features/composer/presentation/model/header_style_type.dart'; +import 'package:tmail_ui_user/features/composer/presentation/model/order_list_type.dart'; +import 'package:tmail_ui_user/features/composer/presentation/model/paragraph_type.dart'; import 'package:tmail_ui_user/features/composer/presentation/model/rich_text_style_type.dart'; import 'package:tmail_ui_user/features/composer/presentation/widgets/attachment_file_composer_builder.dart'; import 'package:tmail_ui_user/features/composer/presentation/widgets/drop_down_menu_header_style_widget.dart'; @@ -494,6 +497,7 @@ class ComposerView extends GetWidget Widget _buildToolbarRichTextWidget(BuildContext context) { return Obx(() { final richTextMobileTabletController = controller.richTextMobileTabletController; + return Container( padding: const EdgeInsets.only(left: 20, top: 8, bottom: 8), alignment: Alignment.centerLeft, @@ -503,16 +507,16 @@ class ComposerView extends GetWidget children: [ DropDownMenuHeaderStyleWidget( icon: buildWrapIconStyleText( - isSelected: richTextMobileTabletController.isMenuHeaderStyleOpen, - icon: SvgPicture.asset(RichTextStyleType.headerStyle.getIcon(imagePaths), - color: AppColor.colorDefaultRichTextButton, - fit: BoxFit.fill), - padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 5), + isSelected: richTextMobileTabletController.isMenuHeaderStyleOpen, + icon: SvgPicture.asset(RichTextStyleType.headerStyle.getIcon(imagePaths), + color: AppColor.colorDefaultRichTextButton, + fit: BoxFit.fill), + padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 5), ), items: HeaderStyleType.values, onChanged: (newStyle) => richTextMobileTabletController.applyHeaderStyle(newStyle)), Container( - width: 200, + width: 120, padding: const EdgeInsets.only(right: 2, left: 8), child: DropDownButtonWidget( items: SafeFont.values, @@ -520,6 +524,7 @@ class ComposerView extends GetWidget onChanged: (newFont) => richTextMobileTabletController.applyNewFontStyle(newFont), heightItem: 38, sizeIconChecked: 16, + dropdownWidth: 200, radiusButton: 5, colorButton: Colors.white, supportSelectionIcon: true)), @@ -564,6 +569,46 @@ class ComposerView extends GetWidget isSelected: richTextMobileTabletController.isTextStyleTypeSelected(RichTextStyleType.strikeThrough), onTap: () => richTextMobileTabletController.applyRichTextStyle(context, RichTextStyleType.strikeThrough)) ])), + Padding( + padding: const EdgeInsets.only(left: 8), + child: PopupMenuOverlayWidget( + controller: richTextMobileTabletController.menuParagraphController, + listButtonAction: ParagraphType.values + .map((paragraph) => paragraph.buildButtonWidget( + context, + imagePaths, + (paragraph) => richTextMobileTabletController.applyParagraphType(paragraph))) + .toList(), + iconButton: buildWrapIconStyleText( + padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 5), + spacing: 3, + isSelected: richTextMobileTabletController.focusMenuParagraph.value, + icon: buildIcon( + path: richTextMobileTabletController.selectedParagraph.value.getIcon(imagePaths), + color: AppColor.colorDefaultRichTextButton, + )), + ), + ), + Padding( + padding: const EdgeInsets.only(left: 8), + child: PopupMenuOverlayWidget( + controller: richTextMobileTabletController.menuOrderListController, + listButtonAction: OrderListType.values + .map((orderType) => orderType.buildButtonWidget( + context, + imagePaths, + (orderType) => richTextMobileTabletController.applyOrderListType(orderType))) + .toList(), + iconButton: buildWrapIconStyleText( + padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 5), + spacing: 3, + isSelected: richTextMobileTabletController.focusMenuOrderList.value, + icon: buildIcon( + path: richTextMobileTabletController.selectedOrderList.value.getIcon(imagePaths), + color: AppColor.colorDefaultRichTextButton, + )), + ), + ) ], ), ); 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 0c5e3fc5b..10ff8ca2f 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 @@ -1,5 +1,6 @@ import 'package:collection/collection.dart'; import 'package:core/core.dart'; +import 'package:custom_pop_up_menu/custom_pop_up_menu.dart'; import 'package:dartz/dartz.dart'; import 'package:enough_html_editor/enough_html_editor.dart'; import 'package:flutter/material.dart'; @@ -11,6 +12,8 @@ import 'package:tmail_ui_user/features/composer/presentation/model/dropdown_menu import 'package:tmail_ui_user/features/composer/presentation/model/header_style_type.dart'; import 'package:tmail_ui_user/features/composer/presentation/model/image_source.dart'; import 'package:tmail_ui_user/features/composer/presentation/model/inline_image.dart'; +import 'package:tmail_ui_user/features/composer/presentation/model/order_list_type.dart'; +import 'package:tmail_ui_user/features/composer/presentation/model/paragraph_type.dart'; import 'package:tmail_ui_user/features/composer/presentation/model/rich_text_style_type.dart'; class RichTextMobileTabletController extends BaseRichTextController { @@ -24,6 +27,26 @@ class RichTextMobileTabletController extends BaseRichTextController { bool get isMenuHeaderStyleOpen => menuHeaderStyleStatus.value == DropdownMenuFontStatus.open; + final selectedParagraph = ParagraphType.alignLeft.obs; + final selectedOrderList = OrderListType.bulletedList.obs; + + final focusMenuOrderList = RxBool(false); + final focusMenuParagraph = RxBool(false); + + final menuParagraphController = CustomPopupMenuController(); + final menuOrderListController = CustomPopupMenuController(); + + @override + void onReady() { + super.onReady(); + menuParagraphController.addListener(() { + focusMenuParagraph.value = menuParagraphController.menuIsShowing; + }); + menuOrderListController.addListener(() { + focusMenuOrderList.value = menuOrderListController.menuIsShowing; + }); + } + void listenHtmlEditorApi() { htmlEditorApi?.onFormatSettingsChanged = (formatSettings) { listTextStyleApply.clear(); @@ -63,11 +86,13 @@ class RichTextMobileTabletController extends BaseRichTextController { openMenuSelectColor(context, selectedTextBackgroundColor.value, onResetToDefault: () { selectedTextBackgroundColor.value = Colors.white; - htmlEditorApi?.setColorTextBackground(selectedTextBackgroundColor.value); + htmlEditorApi + ?.setColorTextBackground(selectedTextBackgroundColor.value); }, onSelectColor: (selectedColor) { final newColor = selectedColor ?? Colors.white; selectedTextBackgroundColor.value = newColor; - htmlEditorApi?.setColorTextBackground(selectedTextBackgroundColor.value); + htmlEditorApi + ?.setColorTextBackground(selectedTextBackgroundColor.value); }); break; case RichTextStyleType.bold: @@ -105,11 +130,12 @@ class RichTextMobileTabletController extends BaseRichTextController { Future>> refactorContentHasInlineImage( String emailContent, - Map mapInlineAttachments + Map mapInlineAttachments, ) async { final document = parse(emailContent); final listImgTag = document.querySelectorAll('img[src^="data:image/"]'); - final listInlineAttachment = await Future.wait(listImgTag.map((imgTag) async { + final listInlineAttachment = + await Future.wait(listImgTag.map((imgTag) async { final cid = imgTag.attributes['id']; log('RichTextMobileTabletController::refactorContentHasInlineImage(): cid: $cid'); imgTag.attributes['src'] = 'cid:$cid'; @@ -154,4 +180,22 @@ class RichTextMobileTabletController extends BaseRichTextController { htmlEditorApi?.formatHeader(styleSelected.styleValue); } -} \ No newline at end of file + void applyParagraphType(ParagraphType newParagraph) { + selectedParagraph.value = newParagraph; + htmlEditorApi?.execCommand(newParagraph.commandAction); + menuParagraphController.hideMenu(); + } + + void applyOrderListType(OrderListType newOrderList) { + selectedOrderList.value = newOrderList; + htmlEditorApi?.execCommand(newOrderList.commandAction); + menuOrderListController.hideMenu(); + } + + @override + void onClose() { + menuParagraphController.dispose(); + menuOrderListController.dispose(); + super.onClose(); + } +}