TF-2116 Apply new design composer mobile

(cherry picked from commit fe4ec144d1a18a94de782f87fff56bbf1cd3a00a)
This commit is contained in:
dab246
2023-09-07 09:53:34 +07:00
committed by Dat H. Pham
parent cbba97279a
commit 3809a05a35
33 changed files with 803 additions and 1002 deletions
@@ -0,0 +1,80 @@
import 'package:flutter/material.dart';
import 'package:tmail_ui_user/features/composer/presentation/styles/web/attachment_composer_widget_style.dart';
import 'package:tmail_ui_user/features/composer/presentation/widgets/attachment_item_composer_widget.dart';
import 'package:tmail_ui_user/features/composer/presentation/widgets/attachment_header_composer_widget.dart';
import 'package:tmail_ui_user/features/upload/presentation/model/upload_file_state.dart';
typedef OnToggleExpandAttachmentAction = void Function(bool isCollapsed);
class AttachmentComposerWidget extends StatefulWidget {
final List<UploadFileState> listFileUploaded;
final bool isCollapsed;
final OnDeleteAttachmentAction onDeleteAttachmentAction;
final OnToggleExpandAttachmentAction onToggleExpandAttachmentAction;
const AttachmentComposerWidget({
super.key,
required this.listFileUploaded,
required this.isCollapsed,
required this.onDeleteAttachmentAction,
required this.onToggleExpandAttachmentAction,
});
@override
State<AttachmentComposerWidget> createState() => _AttachmentComposerWidgetState();
}
class _AttachmentComposerWidgetState extends State<AttachmentComposerWidget> {
bool _isCollapsed = false;
@override
void initState() {
super.initState();
_isCollapsed = widget.isCollapsed;
}
@override
Widget build(BuildContext context) {
return Container(
decoration: const BoxDecoration(
color: AttachmentComposerWidgetStyle.backgroundColor,
boxShadow: AttachmentComposerWidgetStyle.shadow
),
width: double.infinity,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
AttachmentHeaderComposerWidget(
listFileUploaded: widget.listFileUploaded,
isCollapsed: _isCollapsed,
onToggleExpandAction: (isCollapsed) {
setState(() => _isCollapsed = !isCollapsed);
widget.onToggleExpandAttachmentAction(!isCollapsed);
},
),
if (!_isCollapsed)
Container(
width: double.infinity,
padding: AttachmentComposerWidgetStyle.listItemPadding,
constraints: const BoxConstraints(maxHeight: AttachmentComposerWidgetStyle.maxHeight),
child: SingleChildScrollView(
child: Wrap(
spacing: AttachmentComposerWidgetStyle.listItemSpace,
runSpacing: AttachmentComposerWidgetStyle.listItemSpace,
children: widget.listFileUploaded
.map((file) => AttachmentItemComposerWidget(
fileState: file,
onDeleteAttachmentAction: widget.onDeleteAttachmentAction
))
.toList(),
),
),
),
]
),
);
}
}
@@ -0,0 +1,131 @@
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:get/get.dart';
import 'package:tmail_ui_user/features/composer/presentation/styles/web/bottom_bar_composer_widget_style.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
class BottomBarComposerWidget extends StatelessWidget {
final bool isCodeViewEnabled;
final bool isFormattingOptionsEnabled;
final VoidCallback openRichToolbarAction;
final VoidCallback attachFileAction;
final VoidCallback insertImageAction;
final VoidCallback showCodeViewAction;
final VoidCallback deleteComposerAction;
final VoidCallback saveToDraftAction;
final VoidCallback sendMessageAction;
final _imagePaths = Get.find<ImagePaths>();
BottomBarComposerWidget({
super.key,
required this.isCodeViewEnabled,
required this.isFormattingOptionsEnabled,
required this.openRichToolbarAction,
required this.attachFileAction,
required this.insertImageAction,
required this.showCodeViewAction,
required this.deleteComposerAction,
required this.saveToDraftAction,
required this.sendMessageAction,
});
@override
Widget build(BuildContext context) {
return Container(
padding: BottomBarComposerWidgetStyle.padding,
color: BottomBarComposerWidgetStyle.backgroundColor,
child: Row(
children: [
TMailButtonWidget.fromIcon(
icon: _imagePaths.icRichToolbar,
borderRadius: BottomBarComposerWidgetStyle.iconRadius,
padding: BottomBarComposerWidgetStyle.richTextIconPadding,
backgroundColor: isFormattingOptionsEnabled
? BottomBarComposerWidgetStyle.selectedBackgroundColor
: Colors.transparent,
iconSize: BottomBarComposerWidgetStyle.richTextIconSize,
iconColor: isFormattingOptionsEnabled
? BottomBarComposerWidgetStyle.selectedIconColor
: BottomBarComposerWidgetStyle.iconColor,
tooltipMessage: AppLocalizations.of(context).formattingOptions,
onTapActionCallback: openRichToolbarAction,
),
const SizedBox(width: BottomBarComposerWidgetStyle.space),
TMailButtonWidget.fromIcon(
icon: _imagePaths.icAttachFile,
iconColor: BottomBarComposerWidgetStyle.iconColor,
borderRadius: BottomBarComposerWidgetStyle.iconRadius,
backgroundColor: Colors.transparent,
padding: BottomBarComposerWidgetStyle.iconPadding,
iconSize: BottomBarComposerWidgetStyle.iconSize,
tooltipMessage: AppLocalizations.of(context).attach_file,
onTapActionCallback: attachFileAction,
),
const SizedBox(width: BottomBarComposerWidgetStyle.space),
AbsorbPointer(
absorbing: isCodeViewEnabled,
child: TMailButtonWidget.fromIcon(
icon: _imagePaths.icInsertImage,
iconColor: BottomBarComposerWidgetStyle.iconColor,
borderRadius: BottomBarComposerWidgetStyle.iconRadius,
backgroundColor: Colors.transparent,
padding: BottomBarComposerWidgetStyle.iconPadding,
iconSize: BottomBarComposerWidgetStyle.iconSize,
tooltipMessage: AppLocalizations.of(context).insertImage,
onTapActionCallback: insertImageAction,
),
),
const SizedBox(width: BottomBarComposerWidgetStyle.space),
TMailButtonWidget.fromIcon(
icon: _imagePaths.icStyleCodeView,
iconColor: isCodeViewEnabled
? BottomBarComposerWidgetStyle.selectedIconColor
: BottomBarComposerWidgetStyle.iconColor,
borderRadius: BottomBarComposerWidgetStyle.iconRadius,
backgroundColor: isCodeViewEnabled
? BottomBarComposerWidgetStyle.selectedBackgroundColor
: Colors.transparent,
padding: BottomBarComposerWidgetStyle.iconPadding,
iconSize: BottomBarComposerWidgetStyle.iconSize,
tooltipMessage: AppLocalizations.of(context).embedCode,
onTapActionCallback: showCodeViewAction,
),
const Spacer(),
TMailButtonWidget.fromIcon(
icon: _imagePaths.icDeleteMailbox,
borderRadius: BottomBarComposerWidgetStyle.iconRadius,
padding: BottomBarComposerWidgetStyle.iconPadding,
iconSize: BottomBarComposerWidgetStyle.iconSize,
tooltipMessage: AppLocalizations.of(context).delete,
onTapActionCallback: deleteComposerAction,
),
const SizedBox(width: BottomBarComposerWidgetStyle.space),
TMailButtonWidget.fromIcon(
icon: _imagePaths.icSaveToDraft,
borderRadius: BottomBarComposerWidgetStyle.iconRadius,
padding: BottomBarComposerWidgetStyle.iconPadding,
iconSize: BottomBarComposerWidgetStyle.iconSize,
tooltipMessage: AppLocalizations.of(context).save_to_drafts,
onTapActionCallback: saveToDraftAction,
),
const SizedBox(width: BottomBarComposerWidgetStyle.sendButtonSpace),
TMailButtonWidget(
text: AppLocalizations.of(context).send,
icon: _imagePaths.icSend,
iconAlignment: TextDirection.rtl,
padding: BottomBarComposerWidgetStyle.sendButtonPadding,
iconSize: BottomBarComposerWidgetStyle.iconSize,
iconSpace: BottomBarComposerWidgetStyle.sendButtonIconSpace,
textStyle: BottomBarComposerWidgetStyle.sendButtonTextStyle,
backgroundColor: BottomBarComposerWidgetStyle.sendButtonBackgroundColor,
borderRadius: BottomBarComposerWidgetStyle.sendButtonRadius,
onTapActionCallback: sendMessageAction,
)
]
),
);
}
}
@@ -0,0 +1,107 @@
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:get/get.dart';
import 'package:tmail_ui_user/features/composer/presentation/model/screen_display_mode.dart';
import 'package:tmail_ui_user/features/composer/presentation/styles/app_bar_composer_widget_style.dart';
import 'package:tmail_ui_user/features/composer/presentation/widgets/web/minimize_composer_widget.dart';
import 'package:tmail_ui_user/features/composer/presentation/widgets/title_composer_widget.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
class DesktopAppBarComposerWidget extends StatelessWidget {
final String emailSubject;
final VoidCallback onCloseViewAction;
final ScreenDisplayMode? displayMode;
final OnChangeDisplayModeAction? onChangeDisplayModeAction;
final BoxConstraints? constraints;
final _imagePaths = Get.find<ImagePaths>();
DesktopAppBarComposerWidget({
super.key,
required this.emailSubject,
required this.onCloseViewAction,
this.displayMode,
this.onChangeDisplayModeAction,
this.constraints,
});
@override
Widget build(BuildContext context) {
return Container(
height: AppBarComposerWidgetStyle.height,
padding: AppBarComposerWidgetStyle.padding,
color: AppBarComposerWidgetStyle.backgroundColor,
child: Stack(
children: [
Center(
child: Container(
constraints: constraints != null
? BoxConstraints(maxWidth: constraints!.maxWidth / 2)
: null,
child: TitleComposerWidget(emailSubject: emailSubject),
),
),
if (onChangeDisplayModeAction != null)
Align(
alignment: AlignmentDirectional.centerEnd,
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
TMailButtonWidget.fromIcon(
icon: _imagePaths.icMinimize,
backgroundColor: Colors.transparent,
tooltipMessage: AppLocalizations.of(context).minimize,
iconSize: AppBarComposerWidgetStyle.iconSize,
iconColor: AppBarComposerWidgetStyle.iconColor,
padding: AppBarComposerWidgetStyle.iconPadding,
onTapActionCallback: () => onChangeDisplayModeAction!(ScreenDisplayMode.minimize)
),
const SizedBox(width: AppBarComposerWidgetStyle.space),
TMailButtonWidget.fromIcon(
icon: displayMode == ScreenDisplayMode.fullScreen
? _imagePaths.icFullScreenExit
: _imagePaths.icFullScreen,
backgroundColor: Colors.transparent,
tooltipMessage: AppLocalizations.of(context).fullscreen,
iconSize: AppBarComposerWidgetStyle.iconSize,
iconColor: AppBarComposerWidgetStyle.iconColor,
padding: AppBarComposerWidgetStyle.iconPadding,
onTapActionCallback: () => onChangeDisplayModeAction!(
displayMode == ScreenDisplayMode.fullScreen
? ScreenDisplayMode.normal
: ScreenDisplayMode.fullScreen
)
),
const SizedBox(width: AppBarComposerWidgetStyle.space),
TMailButtonWidget.fromIcon(
icon: _imagePaths.icCancel,
backgroundColor: Colors.transparent,
tooltipMessage: AppLocalizations.of(context).saveAndClose,
iconSize: AppBarComposerWidgetStyle.iconSize,
iconColor: AppBarComposerWidgetStyle.iconColor,
padding: AppBarComposerWidgetStyle.iconPadding,
onTapActionCallback: onCloseViewAction
),
]
),
)
else
Align(
alignment: AlignmentDirectional.centerEnd,
child: TMailButtonWidget.fromIcon(
icon: _imagePaths.icCancel,
backgroundColor: Colors.transparent,
tooltipMessage: AppLocalizations.of(context).saveAndClose,
iconSize: AppBarComposerWidgetStyle.iconSize,
iconColor: AppBarComposerWidgetStyle.iconColor,
padding: AppBarComposerWidgetStyle.iconPadding,
onTapActionCallback: onCloseViewAction
),
)
],
),
);
}
}
@@ -0,0 +1,90 @@
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:get/get.dart';
import 'package:tmail_ui_user/features/composer/presentation/model/screen_display_mode.dart';
import 'package:tmail_ui_user/features/composer/presentation/styles/minimize_composer_widget_style.dart';
import 'package:tmail_ui_user/features/composer/presentation/widgets/title_composer_widget.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
typedef OnChangeDisplayModeAction = Function(ScreenDisplayMode mode);
class MinimizeComposerWidget extends StatelessWidget {
final OnChangeDisplayModeAction onChangeDisplayModeAction;
final VoidCallback onCloseViewAction;
final String emailSubject;
final _imagePaths = Get.find<ImagePaths>();
MinimizeComposerWidget({
super.key,
required this.emailSubject,
required this.onChangeDisplayModeAction,
required this.onCloseViewAction,
});
@override
Widget build(BuildContext context) {
return Card(
elevation: MinimizeComposerWidgetStyle.elevation,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(MinimizeComposerWidgetStyle.radius))
),
clipBehavior: Clip.antiAlias,
child: Container(
color: MinimizeComposerWidgetStyle.backgroundColor,
width: MinimizeComposerWidgetStyle.width,
height: MinimizeComposerWidgetStyle.height,
padding: MinimizeComposerWidgetStyle.padding,
child: Stack(
children: [
Center(
child: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: MinimizeComposerWidgetStyle.width / 2),
child: TitleComposerWidget(emailSubject: emailSubject),
),
),
Align(
alignment: AlignmentDirectional.centerStart,
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
TMailButtonWidget.fromIcon(
icon: _imagePaths.icCancel,
backgroundColor: Colors.transparent,
tooltipMessage: AppLocalizations.of(context).saveAndClose,
iconSize: MinimizeComposerWidgetStyle.iconSize,
iconColor: MinimizeComposerWidgetStyle.iconColor,
padding: MinimizeComposerWidgetStyle.iconPadding,
onTapActionCallback: onCloseViewAction
),
const SizedBox(width: MinimizeComposerWidgetStyle.space),
TMailButtonWidget.fromIcon(
icon: _imagePaths.icFullScreen,
backgroundColor: Colors.transparent,
tooltipMessage: AppLocalizations.of(context).fullscreen,
iconSize: MinimizeComposerWidgetStyle.iconSize,
iconColor: MinimizeComposerWidgetStyle.iconColor,
padding: MinimizeComposerWidgetStyle.iconPadding,
onTapActionCallback: () => onChangeDisplayModeAction(ScreenDisplayMode.fullScreen)
),
const SizedBox(width: MinimizeComposerWidgetStyle.space),
TMailButtonWidget.fromIcon(
icon: _imagePaths.icChevronUp,
backgroundColor: Colors.transparent,
tooltipMessage: AppLocalizations.of(context).show,
iconSize: MinimizeComposerWidgetStyle.iconSize,
iconColor: MinimizeComposerWidgetStyle.iconColor,
padding: MinimizeComposerWidgetStyle.iconPadding,
onTapActionCallback: () => onChangeDisplayModeAction(ScreenDisplayMode.normal)
),
]
),
)
],
)
),
);
}
}
@@ -0,0 +1,118 @@
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:get/get.dart';
import 'package:tmail_ui_user/features/composer/presentation/styles/mobile_app_bar_composer_widget_style.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
class MobileResponsiveAppBarComposerWidget extends StatelessWidget {
final bool isCodeViewEnabled;
final bool isSendButtonEnabled;
final bool isFormattingOptionsEnabled;
final VoidCallback onCloseViewAction;
final VoidCallback attachFileAction;
final VoidCallback insertImageAction;
final VoidCallback sendMessageAction;
final VoidCallback openContextMenuAction;
final VoidCallback openRichToolbarAction;
final _imagePaths = Get.find<ImagePaths>();
MobileResponsiveAppBarComposerWidget({
super.key,
required this.isCodeViewEnabled,
required this.isFormattingOptionsEnabled,
required this.isSendButtonEnabled,
required this.openRichToolbarAction,
required this.onCloseViewAction,
required this.attachFileAction,
required this.insertImageAction,
required this.sendMessageAction,
required this.openContextMenuAction,
});
@override
Widget build(BuildContext context) {
return Container(
height: MobileAppBarComposerWidgetStyle.height,
color: MobileAppBarComposerWidgetStyle.backgroundColor,
padding: MobileAppBarComposerWidgetStyle.padding,
child: Row(
children: [
TMailButtonWidget.fromIcon(
icon: _imagePaths.icCancel,
backgroundColor: Colors.transparent,
tooltipMessage: AppLocalizations.of(context).saveAndClose,
iconSize: MobileAppBarComposerWidgetStyle.iconSize,
iconColor: MobileAppBarComposerWidgetStyle.iconColor,
padding: MobileAppBarComposerWidgetStyle.iconPadding,
onTapActionCallback: onCloseViewAction
),
const Spacer(),
TMailButtonWidget.fromIcon(
icon: _imagePaths.icRichToolbar,
borderRadius: MobileAppBarComposerWidgetStyle.iconRadius,
padding: MobileAppBarComposerWidgetStyle.richTextIconPadding,
backgroundColor: isFormattingOptionsEnabled
? MobileAppBarComposerWidgetStyle.selectedBackgroundColor
: Colors.transparent,
iconSize: MobileAppBarComposerWidgetStyle.richTextIconSize,
iconColor: isFormattingOptionsEnabled
? MobileAppBarComposerWidgetStyle.selectedIconColor
: MobileAppBarComposerWidgetStyle.iconColor,
tooltipMessage: AppLocalizations.of(context).formattingOptions,
onTapActionCallback: openRichToolbarAction,
),
const SizedBox(width: MobileAppBarComposerWidgetStyle.space),
TMailButtonWidget.fromIcon(
icon: _imagePaths.icAttachFile,
iconColor: MobileAppBarComposerWidgetStyle.iconColor,
borderRadius: MobileAppBarComposerWidgetStyle.iconRadius,
backgroundColor: Colors.transparent,
padding: MobileAppBarComposerWidgetStyle.iconPadding,
iconSize: MobileAppBarComposerWidgetStyle.iconSize,
tooltipMessage: AppLocalizations.of(context).attach_file,
onTapActionCallback: attachFileAction,
),
const SizedBox(width: MobileAppBarComposerWidgetStyle.space),
AbsorbPointer(
absorbing: isCodeViewEnabled,
child: TMailButtonWidget.fromIcon(
icon: _imagePaths.icInsertImage,
iconColor: MobileAppBarComposerWidgetStyle.iconColor,
borderRadius: MobileAppBarComposerWidgetStyle.iconRadius,
backgroundColor: Colors.transparent,
padding: MobileAppBarComposerWidgetStyle.iconPadding,
iconSize: MobileAppBarComposerWidgetStyle.iconSize,
tooltipMessage: AppLocalizations.of(context).insertImage,
onTapActionCallback: insertImageAction,
),
),
const SizedBox(width: MobileAppBarComposerWidgetStyle.space),
TMailButtonWidget.fromIcon(
icon: isSendButtonEnabled
? _imagePaths.icSendMobile
: _imagePaths.icSendDisable,
backgroundColor: Colors.transparent,
padding: MobileAppBarComposerWidgetStyle.iconPadding,
iconSize: MobileAppBarComposerWidgetStyle.sendButtonIconSize,
tooltipMessage: AppLocalizations.of(context).send,
onTapActionCallback: sendMessageAction,
),
const SizedBox(width: MobileAppBarComposerWidgetStyle.space),
TMailButtonWidget.fromIcon(
icon: _imagePaths.icMore,
iconColor: MobileAppBarComposerWidgetStyle.iconColor,
borderRadius: MobileAppBarComposerWidgetStyle.iconRadius,
backgroundColor: Colors.transparent,
padding: MobileAppBarComposerWidgetStyle.iconPadding,
iconSize: MobileAppBarComposerWidgetStyle.iconSize,
tooltipMessage: AppLocalizations.of(context).more,
onTapActionCallback: openContextMenuAction,
),
],
),
);
}
}
@@ -0,0 +1,232 @@
import 'package:core/presentation/extensions/color_extension.dart';
import 'package:core/presentation/resources/image_paths.dart';
import 'package:core/utils/app_logger.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:get/get.dart';
import 'package:pointer_interceptor/pointer_interceptor.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/controller/rich_text_web_controller.dart';
import 'package:tmail_ui_user/features/composer/presentation/mixin/rich_text_button_mixin.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';
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/drop_down_menu_header_style_widget.dart';
class ToolbarRichTextWebBuilder extends StatelessWidget with RichTextButtonMixin {
final RichTextWebController richTextWebController;
final ImagePaths _imagePaths = Get.find<ImagePaths>();
final EdgeInsetsGeometry? padding;
final List<Widget>? extendedOption;
final AlignmentGeometry? alignment;
final Decoration? decoration;
ToolbarRichTextWebBuilder({
Key? key,
required this.richTextWebController,
this.padding,
this.extendedOption,
this.alignment,
this.decoration,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Obx(() {
final codeViewEnabled = richTextWebController.codeViewEnabled;
final opacity = codeViewEnabled ? 0.5 : 1.0;
return PointerInterceptor(
child: Container(
padding: padding ?? const EdgeInsetsDirectional.only(start: 20, top: 8, bottom: 8),
decoration: decoration,
width: double.infinity,
child: Wrap(
crossAxisAlignment: WrapCrossAlignment.center,
runSpacing: 8,
children: [
if (extendedOption?.isNotEmpty == true)
...extendedOption!,
AbsorbPointer(
absorbing: codeViewEnabled,
child: DropDownMenuHeaderStyleWidget(
icon: buildWrapIconStyleText(
isSelected: richTextWebController.isMenuHeaderStyleOpen,
icon: SvgPicture.asset(
RichTextStyleType.headerStyle.getIcon(_imagePaths),
colorFilter: AppColor.colorDefaultRichTextButton.withOpacity(opacity).asFilter(),
fit: BoxFit.fill),
padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 5),
tooltip: RichTextStyleType.headerStyle.getTooltipButton(context)
),
items: HeaderStyleType.values,
onMenuStateChange: (isOpen) {
log('ComposerView::_buildToolbarRichTextWidget(): MenuHeaderStyleStatus: $isOpen');
final newStatus = isOpen
? DropdownMenuFontStatus.open
: DropdownMenuFontStatus.closed;
richTextWebController.menuHeaderStyleStatus.value = newStatus;
},
onChanged: (newStyle) => richTextWebController.applyHeaderStyle(newStyle)),
),
AbsorbPointer(
absorbing: codeViewEnabled,
child: Container(
width: 130,
padding: const EdgeInsets.only(left: 4.0, right: 4.0),
child: DropDownButtonWidget<FontNameType>(
items: FontNameType.values,
itemSelected: richTextWebController.selectedFontName.value,
onChanged: (newFont) => richTextWebController.applyNewFontStyle(newFont),
onMenuStateChange: (isOpen) {
log('ComposerView::_buildToolbarRichTextWidget(): MenuFontStatus: $isOpen');
final newStatus = isOpen
? DropdownMenuFontStatus.open
: DropdownMenuFontStatus.closed;
richTextWebController.menuFontStatus.value = newStatus;
},
heightItem: 40,
sizeIconChecked: 16,
radiusButton: 8,
opacity: opacity,
dropdownWidth: 200,
colorButton: richTextWebController.isMenuFontOpen
? AppColor.colorBackgroundWrapIconStyleCode
: Colors.white,
iconArrowDown: SvgPicture.asset(_imagePaths.icStyleArrowDown),
tooltip: RichTextStyleType.fontName.getTooltipButton(context),
supportSelectionIcon: true)),
),
Padding(
padding: const EdgeInsetsDirectional.only(end: 4.0),
child: AbsorbPointer(
absorbing: codeViewEnabled,
child: buildWrapIconStyleText(
icon: buildIconWithTooltip(
path: RichTextStyleType.textColor.getIcon(_imagePaths),
color: richTextWebController.selectedTextColor.value,
tooltip: RichTextStyleType.textColor.getTooltipButton(context),
opacity: opacity),
onTap: () => richTextWebController.applyRichTextStyle(context, RichTextStyleType.textColor)),
),
),
Padding(
padding: const EdgeInsetsDirectional.only(end: 4.0),
child: AbsorbPointer(
absorbing: codeViewEnabled,
child: buildWrapIconStyleText(
padding: const EdgeInsets.symmetric(vertical: 9, horizontal: 7),
spacing: 3,
icon: buildIconColorBackgroundText(
iconData: RichTextStyleType.textBackgroundColor.getIconData(),
colorSelected: richTextWebController.selectedTextBackgroundColor.value,
tooltip: RichTextStyleType.textBackgroundColor.getTooltipButton(context),
opacity: opacity),
onTap: () => richTextWebController.applyRichTextStyle(context, RichTextStyleType.textBackgroundColor)),
),
),
Padding(
padding: const EdgeInsetsDirectional.only(end: 4.0),
child: buildWrapIconStyleText(
hasDropdown: false,
padding: const EdgeInsets.symmetric(vertical: 3, horizontal: 5),
icon: Wrap(children: [
AbsorbPointer(
absorbing: codeViewEnabled,
child: buildIconStyleText(
path: RichTextStyleType.bold.getIcon(_imagePaths),
isSelected: richTextWebController.isTextStyleTypeSelected(RichTextStyleType.bold),
tooltip: RichTextStyleType.bold.getTooltipButton(context),
opacity: opacity,
onTap: () => richTextWebController.applyRichTextStyle(context, RichTextStyleType.bold)),
),
AbsorbPointer(
absorbing: codeViewEnabled,
child: buildIconStyleText(
path: RichTextStyleType.italic.getIcon(_imagePaths),
isSelected: richTextWebController.isTextStyleTypeSelected(RichTextStyleType.italic),
tooltip: RichTextStyleType.italic.getTooltipButton(context),
opacity: opacity,
onTap: () => richTextWebController.applyRichTextStyle(context, RichTextStyleType.italic)),
),
AbsorbPointer(
absorbing: codeViewEnabled,
child: buildIconStyleText(
path: RichTextStyleType.underline.getIcon(_imagePaths),
isSelected: richTextWebController.isTextStyleTypeSelected(RichTextStyleType.underline),
tooltip: RichTextStyleType.underline.getTooltipButton(context),
opacity: opacity,
onTap: () => richTextWebController.applyRichTextStyle(context, RichTextStyleType.underline)),
),
AbsorbPointer(
absorbing: codeViewEnabled,
child: buildIconStyleText(
path: RichTextStyleType.strikeThrough.getIcon(_imagePaths),
isSelected: richTextWebController.isTextStyleTypeSelected(
RichTextStyleType.strikeThrough),
tooltip: RichTextStyleType.strikeThrough.getTooltipButton(context),
opacity: opacity,
onTap: () => richTextWebController.applyRichTextStyle(context, RichTextStyleType.strikeThrough)),
)
])),
),
Padding(
padding: const EdgeInsetsDirectional.only(end: 4.0),
child: AbsorbPointer(
absorbing: codeViewEnabled,
child: PopupMenuOverlayWidget(
controller: richTextWebController.menuParagraphController,
listButtonAction: ParagraphType.values
.map((paragraph) => paragraph.buildButtonWidget(
context,
_imagePaths,
(paragraph) => richTextWebController.applyParagraphType(paragraph)))
.toList(),
iconButton: buildWrapIconStyleText(
padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 5),
spacing: 3,
isSelected: richTextWebController.focusMenuParagraph.value,
icon: buildIconWithTooltip(
path: richTextWebController.selectedParagraph.value.getIcon(_imagePaths),
color: AppColor.colorDefaultRichTextButton,
opacity: opacity,
tooltip: RichTextStyleType.paragraph.getTooltipButton(context))),
),
),
),
Padding(
padding: const EdgeInsetsDirectional.only(end: 4.0),
child: AbsorbPointer(
absorbing: codeViewEnabled,
child: PopupMenuOverlayWidget(
controller: richTextWebController.menuOrderListController,
listButtonAction: OrderListType.values
.map((orderType) => orderType.buildButtonWidget(
context,
_imagePaths,
(orderType) => richTextWebController.applyOrderListType(orderType)))
.toList(),
iconButton: buildWrapIconStyleText(
padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 5),
spacing: 3,
isSelected: richTextWebController.focusMenuOrderList.value,
icon: buildIconWithTooltip(
path: richTextWebController.selectedOrderList.value.getIcon(_imagePaths),
color: AppColor.colorDefaultRichTextButton,
opacity: opacity,
tooltip: RichTextStyleType.orderList.getTooltipButton(context))),
),
),
)
]
),
),
);
});
}
}
@@ -0,0 +1,89 @@
import 'package:core/presentation/utils/html_transformer/html_utils.dart';
import 'package:core/utils/app_logger.dart';
import 'package:flutter/material.dart';
import 'package:html_editor_enhanced/html_editor.dart';
typedef OnChangeContentEditorAction = Function(String? text);
typedef OnInitialContentEditorAction = Function(String text);
typedef OnMouseDownEditorAction = Function(BuildContext context);
typedef OnEditorSettingsChange = Function(EditorSettings settings);
class WebEditorWidget extends StatefulWidget {
final String content;
final TextDirection direction;
final HtmlEditorController editorController;
final OnInitialContentEditorAction? onInitial;
final OnChangeContentEditorAction? onChangeContent;
final VoidCallback? onFocus;
final VoidCallback? onUnFocus;
final OnMouseDownEditorAction? onMouseDown;
final OnEditorSettingsChange? onEditorSettings;
const WebEditorWidget({
super.key,
required this.content,
required this.direction,
required this.editorController,
this.onInitial,
this.onChangeContent,
this.onFocus,
this.onUnFocus,
this.onMouseDown,
this.onEditorSettings,
});
@override
State<WebEditorWidget> createState() => _WebEditorState();
}
class _WebEditorState extends State<WebEditorWidget> {
late HtmlEditorController _editorController;
@override
void initState() {
_editorController = widget.editorController;
super.initState();
}
@override
void didUpdateWidget(covariant WebEditorWidget oldWidget) {
log('_EmailEditorState::didUpdateWidget():Old: ${oldWidget.direction} | current: ${widget.direction}');
if (oldWidget.direction != widget.direction) {
_editorController.updateBodyDirection(widget.direction.name);
}
super.didUpdateWidget(oldWidget);
}
@override
Widget build(BuildContext context) {
return HtmlEditor(
key: const Key('web_editor'),
controller: _editorController,
htmlEditorOptions: HtmlEditorOptions(
shouldEnsureVisible: true,
hint: '',
darkMode: false,
customBodyCssStyle: HtmlUtils.customCssStyleHtmlEditor(direction: widget.direction),
),
blockQuotedContent: widget.content,
htmlToolbarOptions: const HtmlToolbarOptions(
toolbarType: ToolbarType.hide,
defaultToolbarButtons: []
),
otherOptions: const OtherOptions(height: 550),
callbacks: Callbacks(
onBeforeCommand: widget.onChangeContent,
onChangeContent: widget.onChangeContent,
onInit: () => widget.onInitial?.call(widget.content),
onFocus: widget.onFocus,
onBlur: widget.onUnFocus,
onMouseDown: () => widget.onMouseDown?.call(context),
onChangeSelection: widget.onEditorSettings,
onChangeCodeview: widget.onChangeContent
),
);
}
}