TF-4195 Show submenu when hover Label As item
This commit is contained in:
@@ -3,16 +3,23 @@ import 'package:equatable/equatable.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
typedef OnPopupMenuActionClick = void Function(PopupMenuItemAction action);
|
||||
typedef OnHoverShowSubmenu = void Function(GlobalKey key);
|
||||
|
||||
abstract class PopupMenuItemAction<T> with EquatableMixin {
|
||||
final T action;
|
||||
final String? key;
|
||||
final int category;
|
||||
final Widget? submenu;
|
||||
|
||||
PopupMenuItemAction(this.action, {this.key, this.category = -1});
|
||||
PopupMenuItemAction(
|
||||
this.action, {
|
||||
this.key,
|
||||
this.category = -1,
|
||||
this.submenu,
|
||||
});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [action, key, category];
|
||||
List<Object?> get props => [action, key, category, submenu];
|
||||
|
||||
String get actionName;
|
||||
|
||||
@@ -40,9 +47,22 @@ mixin OptionalPopupSelectedIcon<T> {
|
||||
double get selectedIconSize => 16.0;
|
||||
}
|
||||
|
||||
mixin OptionalPopupHoverIcon {
|
||||
String get hoverIcon;
|
||||
|
||||
Color get hoverIconColor => AppColor.steelGrayA540;
|
||||
|
||||
double get hoverIconSize => 16.0;
|
||||
}
|
||||
|
||||
abstract class PopupMenuItemActionRequiredIcon<T> extends PopupMenuItemAction<T>
|
||||
with OptionalPopupIcon {
|
||||
PopupMenuItemActionRequiredIcon(super.action, {super.key, super.category});
|
||||
with OptionalPopupIcon, OptionalPopupHoverIcon {
|
||||
PopupMenuItemActionRequiredIcon(
|
||||
super.action, {
|
||||
super.key,
|
||||
super.category,
|
||||
super.submenu,
|
||||
});
|
||||
}
|
||||
|
||||
abstract class PopupMenuItemActionRequiredSelectedIcon<T>
|
||||
@@ -54,6 +74,7 @@ abstract class PopupMenuItemActionRequiredSelectedIcon<T>
|
||||
this.selectedAction, {
|
||||
super.key,
|
||||
super.category,
|
||||
super.submenu,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -66,6 +87,7 @@ abstract class PopupMenuItemActionRequiredFull<T> extends PopupMenuItemAction<T>
|
||||
this.selectedAction, {
|
||||
super.key,
|
||||
super.category,
|
||||
super.submenu,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -79,5 +101,6 @@ abstract class PopupMenuItemActionRequiredIconWithMultipleSelected<T>
|
||||
this.selectedActions, {
|
||||
super.key,
|
||||
super.category,
|
||||
super.submenu,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import 'package:tmail_ui_user/features/base/extensions/popup_menu_action_list_ex
|
||||
import 'package:tmail_ui_user/features/base/mixin/popup_context_menu_action_mixin.dart';
|
||||
import 'package:tmail_ui_user/features/base/model/popup_menu_item_action.dart';
|
||||
import 'package:tmail_ui_user/features/base/widget/popup_menu/popup_menu_item_action_widget.dart';
|
||||
import 'package:tmail_ui_user/features/base/widget/popup_menu/popup_submenu_controller.dart';
|
||||
|
||||
typedef OnPopupMenuActionSelected = void Function(PopupMenuItemAction action);
|
||||
|
||||
@@ -11,11 +12,13 @@ class PopupMenuActionGroupWidget with PopupContextMenuActionMixin {
|
||||
final List<PopupMenuItemAction> actions;
|
||||
final OnPopupMenuActionSelected onActionSelected;
|
||||
final double dividerOpacity;
|
||||
final PopupSubmenuController? submenuController;
|
||||
|
||||
const PopupMenuActionGroupWidget({
|
||||
PopupMenuActionGroupWidget({
|
||||
required this.actions,
|
||||
required this.onActionSelected,
|
||||
this.dividerOpacity = 0.12,
|
||||
this.submenuController,
|
||||
});
|
||||
|
||||
Future<void> show(
|
||||
@@ -37,6 +40,15 @@ class PopupMenuActionGroupWidget with PopupContextMenuActionMixin {
|
||||
Navigator.pop(context);
|
||||
onActionSelected(menuAction);
|
||||
},
|
||||
onHoverShowSubmenu: submenuController != null && menuAction.submenu != null
|
||||
? (itemKey) => _showPopupSubmenu(
|
||||
context: context,
|
||||
itemKey: itemKey,
|
||||
submenuController: submenuController!,
|
||||
submenu: menuAction.submenu!,
|
||||
)
|
||||
: null,
|
||||
onHoverOtherItem: submenuController?.hide,
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -50,4 +62,23 @@ class PopupMenuActionGroupWidget with PopupContextMenuActionMixin {
|
||||
|
||||
return openPopupMenuAction(context, position, popupMenuItems);
|
||||
}
|
||||
|
||||
void _showPopupSubmenu({
|
||||
required BuildContext context,
|
||||
required GlobalKey itemKey,
|
||||
required PopupSubmenuController submenuController,
|
||||
required Widget submenu,
|
||||
}) {
|
||||
final renderBox = itemKey.currentContext?.findRenderObject() as RenderBox?;
|
||||
if (renderBox == null) return;
|
||||
|
||||
final offset = renderBox.localToGlobal(Offset.zero);
|
||||
final rect = offset & renderBox.size;
|
||||
|
||||
submenuController.show(
|
||||
context: context,
|
||||
anchor: rect,
|
||||
submenu: submenu,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,27 +2,52 @@ 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';
|
||||
import 'package:model/email/email_action_type.dart';
|
||||
import 'package:pointer_interceptor/pointer_interceptor.dart';
|
||||
import 'package:tmail_ui_user/features/base/model/popup_menu_item_action.dart';
|
||||
import 'package:tmail_ui_user/features/email/presentation/model/popup_menu_item_email_action.dart';
|
||||
|
||||
class PopupMenuItemActionWidget extends StatelessWidget {
|
||||
class PopupMenuItemActionWidget extends StatefulWidget {
|
||||
final PopupMenuItemAction menuAction;
|
||||
final OnPopupMenuActionClick menuActionClick;
|
||||
final OnHoverShowSubmenu? onHoverShowSubmenu;
|
||||
final VoidCallback? onHoverOtherItem;
|
||||
|
||||
const PopupMenuItemActionWidget({
|
||||
super.key,
|
||||
required this.menuAction,
|
||||
required this.menuActionClick,
|
||||
this.onHoverShowSubmenu,
|
||||
this.onHoverOtherItem,
|
||||
});
|
||||
|
||||
@override
|
||||
State<PopupMenuItemActionWidget> createState() =>
|
||||
_PopupMenuItemActionWidgetState();
|
||||
}
|
||||
|
||||
class _PopupMenuItemActionWidgetState extends State<PopupMenuItemActionWidget> {
|
||||
GlobalKey? _itemKey;
|
||||
ValueNotifier<bool>? _hoverItemNotifier;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
if (widget.menuAction is PopupMenuItemEmailAction) {
|
||||
_itemKey = GlobalKey();
|
||||
_hoverItemNotifier = ValueNotifier(false);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Widget? iconWidget;
|
||||
Widget? selectedIconWidget;
|
||||
bool isSelected = false;
|
||||
|
||||
if (menuAction is PopupMenuItemActionRequiredIcon) {
|
||||
final specificMenuAction = menuAction as PopupMenuItemActionRequiredIcon;
|
||||
if (widget.menuAction is PopupMenuItemActionRequiredIcon) {
|
||||
final specificMenuAction =
|
||||
widget.menuAction as PopupMenuItemActionRequiredIcon;
|
||||
iconWidget = Padding(
|
||||
padding: const EdgeInsetsDirectional.only(end: 16),
|
||||
child: SvgPicture.asset(
|
||||
@@ -33,9 +58,9 @@ class PopupMenuItemActionWidget extends StatelessWidget {
|
||||
fit: BoxFit.fill,
|
||||
),
|
||||
);
|
||||
} else if (menuAction is PopupMenuItemActionRequiredSelectedIcon) {
|
||||
} else if (widget.menuAction is PopupMenuItemActionRequiredSelectedIcon) {
|
||||
final specificMenuAction =
|
||||
menuAction as PopupMenuItemActionRequiredSelectedIcon;
|
||||
widget.menuAction as PopupMenuItemActionRequiredSelectedIcon;
|
||||
selectedIconWidget = Padding(
|
||||
padding: const EdgeInsetsDirectional.only(start: 16),
|
||||
child: SvgPicture.asset(
|
||||
@@ -46,34 +71,11 @@ class PopupMenuItemActionWidget extends StatelessWidget {
|
||||
fit: BoxFit.fill,
|
||||
),
|
||||
);
|
||||
isSelected = specificMenuAction.selectedAction == menuAction.action;
|
||||
} else if (menuAction is PopupMenuItemActionRequiredFull) {
|
||||
final specificMenuAction = menuAction as PopupMenuItemActionRequiredFull;
|
||||
iconWidget = Padding(
|
||||
padding: const EdgeInsetsDirectional.only(end: 16),
|
||||
child: SvgPicture.asset(
|
||||
specificMenuAction.actionIcon,
|
||||
width: specificMenuAction.actionIconSize,
|
||||
height: specificMenuAction.actionIconSize,
|
||||
colorFilter: specificMenuAction.actionIconColor.asFilter(),
|
||||
fit: BoxFit.fill,
|
||||
),
|
||||
);
|
||||
selectedIconWidget = Padding(
|
||||
padding: const EdgeInsetsDirectional.only(start: 16),
|
||||
child: SvgPicture.asset(
|
||||
specificMenuAction.selectedIcon,
|
||||
width: specificMenuAction.selectedIconSize,
|
||||
height: specificMenuAction.selectedIconSize,
|
||||
colorFilter: specificMenuAction.selectedIconColor.asFilter(),
|
||||
fit: BoxFit.fill,
|
||||
),
|
||||
);
|
||||
isSelected = specificMenuAction.selectedAction == menuAction.action;
|
||||
} else if (menuAction
|
||||
is PopupMenuItemActionRequiredIconWithMultipleSelected) {
|
||||
isSelected =
|
||||
specificMenuAction.selectedAction == widget.menuAction.action;
|
||||
} else if (widget.menuAction is PopupMenuItemActionRequiredFull) {
|
||||
final specificMenuAction =
|
||||
menuAction as PopupMenuItemActionRequiredIconWithMultipleSelected;
|
||||
widget.menuAction as PopupMenuItemActionRequiredFull;
|
||||
iconWidget = Padding(
|
||||
padding: const EdgeInsetsDirectional.only(end: 16),
|
||||
child: SvgPicture.asset(
|
||||
@@ -95,27 +97,137 @@ class PopupMenuItemActionWidget extends StatelessWidget {
|
||||
),
|
||||
);
|
||||
isSelected =
|
||||
specificMenuAction.selectedActions.contains(menuAction.action);
|
||||
specificMenuAction.selectedAction == widget.menuAction.action;
|
||||
} else if (widget.menuAction
|
||||
is PopupMenuItemActionRequiredIconWithMultipleSelected) {
|
||||
final specificMenuAction = widget.menuAction
|
||||
as PopupMenuItemActionRequiredIconWithMultipleSelected;
|
||||
iconWidget = Padding(
|
||||
padding: const EdgeInsetsDirectional.only(end: 16),
|
||||
child: SvgPicture.asset(
|
||||
specificMenuAction.actionIcon,
|
||||
width: specificMenuAction.actionIconSize,
|
||||
height: specificMenuAction.actionIconSize,
|
||||
colorFilter: specificMenuAction.actionIconColor.asFilter(),
|
||||
fit: BoxFit.fill,
|
||||
),
|
||||
);
|
||||
selectedIconWidget = Padding(
|
||||
padding: const EdgeInsetsDirectional.only(start: 16),
|
||||
child: SvgPicture.asset(
|
||||
specificMenuAction.selectedIcon,
|
||||
width: specificMenuAction.selectedIconSize,
|
||||
height: specificMenuAction.selectedIconSize,
|
||||
colorFilter: specificMenuAction.selectedIconColor.asFilter(),
|
||||
fit: BoxFit.fill,
|
||||
),
|
||||
);
|
||||
isSelected =
|
||||
specificMenuAction.selectedActions.contains(widget.menuAction.action);
|
||||
}
|
||||
|
||||
if (widget.menuAction is PopupMenuItemEmailAction &&
|
||||
widget.menuAction.action == EmailActionType.labelAs) {
|
||||
final specificMenuAction = widget.menuAction as PopupMenuItemEmailAction;
|
||||
|
||||
return PointerInterceptor(
|
||||
child: MouseRegion(
|
||||
onEnter: (_) {
|
||||
if (_hoverItemNotifier != null) {
|
||||
_hoverItemNotifier?.value = true;
|
||||
}
|
||||
if (_itemKey != null) {
|
||||
widget.onHoverShowSubmenu?.call(_itemKey!);
|
||||
} else {
|
||||
widget.onHoverOtherItem?.call();
|
||||
}
|
||||
},
|
||||
onExit: (_) {
|
||||
if (_hoverItemNotifier != null) {
|
||||
_hoverItemNotifier?.value = false;
|
||||
}
|
||||
},
|
||||
child: Material(
|
||||
type: MaterialType.transparency,
|
||||
child: InkWell(
|
||||
onTap: () => specificMenuAction.onClick(widget.menuActionClick),
|
||||
hoverColor: AppColor.popupMenuItemHovered,
|
||||
child: Container(
|
||||
key: _itemKey,
|
||||
height: 48,
|
||||
width: double.infinity,
|
||||
padding: specificMenuAction.itemPadding,
|
||||
child: Row(
|
||||
children: [
|
||||
if (iconWidget != null) iconWidget,
|
||||
Expanded(
|
||||
child: Text(
|
||||
specificMenuAction.actionName,
|
||||
style: ThemeUtils.textStyleBodyBody3(
|
||||
color: specificMenuAction.actionNameColor,
|
||||
),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
if (isSelected && selectedIconWidget != null)
|
||||
selectedIconWidget,
|
||||
if (_hoverItemNotifier != null)
|
||||
ValueListenableBuilder(
|
||||
valueListenable: _hoverItemNotifier!,
|
||||
builder: (_, value, __) {
|
||||
if (value) {
|
||||
return Padding(
|
||||
padding:
|
||||
const EdgeInsetsDirectional.only(start: 16),
|
||||
child: SvgPicture.asset(
|
||||
specificMenuAction.hoverIcon,
|
||||
width: specificMenuAction.hoverIconSize,
|
||||
height: specificMenuAction.hoverIconSize,
|
||||
colorFilter: specificMenuAction.hoverIconColor
|
||||
.asFilter(),
|
||||
fit: BoxFit.fill,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
},
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return PointerInterceptor(
|
||||
child: Material(
|
||||
type: MaterialType.transparency,
|
||||
child: InkWell(
|
||||
onTap: () => menuAction.onClick(menuActionClick),
|
||||
onTap: () => widget.menuAction.onClick(widget.menuActionClick),
|
||||
hoverColor: AppColor.popupMenuItemHovered,
|
||||
onHover: (_) {
|
||||
if (_hoverItemNotifier != null) {
|
||||
_hoverItemNotifier?.value = false;
|
||||
}
|
||||
widget.onHoverOtherItem?.call();
|
||||
},
|
||||
child: Container(
|
||||
key: _itemKey,
|
||||
height: 48,
|
||||
width: double.infinity,
|
||||
padding: menuAction.itemPadding,
|
||||
padding: widget.menuAction.itemPadding,
|
||||
child: Row(
|
||||
children: [
|
||||
if (iconWidget != null) iconWidget,
|
||||
Expanded(
|
||||
child: Text(
|
||||
menuAction.actionName,
|
||||
widget.menuAction.actionName,
|
||||
style: ThemeUtils.textStyleBodyBody3(
|
||||
color: menuAction.actionNameColor,
|
||||
color: widget.menuAction.actionNameColor,
|
||||
),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
@@ -130,4 +242,13 @@ class PopupMenuItemActionWidget extends StatelessWidget {
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
if (_hoverItemNotifier != null) {
|
||||
_hoverItemNotifier?.dispose();
|
||||
_hoverItemNotifier = null;
|
||||
}
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
enum SubmenuDirection { left, right, auto }
|
||||
|
||||
class PopupSubmenuController {
|
||||
OverlayEntry? _submenuEntry;
|
||||
|
||||
bool get isShowing => _submenuEntry != null;
|
||||
|
||||
void show({
|
||||
required BuildContext context,
|
||||
required Rect anchor,
|
||||
required Widget submenu,
|
||||
SubmenuDirection direction = SubmenuDirection.auto,
|
||||
double submenuWidth = 249,
|
||||
double submenuMaxHeight = 400,
|
||||
double offset = 0,
|
||||
}) {
|
||||
hide();
|
||||
|
||||
final screenWidth = MediaQuery.of(context).size.width;
|
||||
|
||||
final rightPosition = anchor.right + offset;
|
||||
final leftPosition = anchor.left - submenuWidth - offset;
|
||||
|
||||
bool canShowRight = rightPosition + submenuWidth <= screenWidth;
|
||||
bool canShowLeft = leftPosition >= 0;
|
||||
|
||||
double? finalLeft;
|
||||
|
||||
if (direction == SubmenuDirection.right) {
|
||||
finalLeft = rightPosition;
|
||||
} else if (direction == SubmenuDirection.left) {
|
||||
finalLeft = leftPosition;
|
||||
} else {
|
||||
if (canShowRight) {
|
||||
finalLeft = rightPosition;
|
||||
} else if (canShowLeft) {
|
||||
finalLeft = leftPosition;
|
||||
} else {
|
||||
finalLeft = rightPosition;
|
||||
}
|
||||
}
|
||||
|
||||
_submenuEntry = OverlayEntry(
|
||||
builder: (_) {
|
||||
return Positioned(
|
||||
left: finalLeft,
|
||||
top: anchor.top,
|
||||
child: MouseRegion(
|
||||
onExit: (_) => hide(),
|
||||
child: Material(
|
||||
elevation: 8,
|
||||
color: Colors.white,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
),
|
||||
child: SizedBox(
|
||||
width: submenuWidth,
|
||||
height: submenuMaxHeight,
|
||||
child: submenu,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
Overlay.of(context).insert(_submenuEntry!);
|
||||
}
|
||||
|
||||
void hide() {
|
||||
_submenuEntry?.remove();
|
||||
_submenuEntry = null;
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
hide();
|
||||
}
|
||||
}
|
||||
@@ -103,6 +103,7 @@ class EmailView extends GetWidget<SingleEmailController> {
|
||||
additionalActions: [],
|
||||
emailIsRead: presentationEmail.hasRead,
|
||||
isLabelFeatureEnabled: controller.isLabelFeatureEnabled,
|
||||
labels: controller.mailboxDashBoardController.labelController.labels,
|
||||
openBottomSheetContextMenu: controller.mailboxDashBoardController.openBottomSheetContextMenu,
|
||||
openPopupMenu: controller.mailboxDashBoardController.openPopupMenuActionGroup,
|
||||
);
|
||||
@@ -309,6 +310,7 @@ class EmailView extends GetWidget<SingleEmailController> {
|
||||
],
|
||||
emailIsRead: presentationEmail.hasRead,
|
||||
isLabelFeatureEnabled: controller.isLabelFeatureEnabled,
|
||||
labels: controller.mailboxDashBoardController.labelController.labels,
|
||||
openBottomSheetContextMenu: controller.mailboxDashBoardController.openBottomSheetContextMenu,
|
||||
openPopupMenu: controller.mailboxDashBoardController.openPopupMenuActionGroup,
|
||||
),
|
||||
|
||||
@@ -16,6 +16,7 @@ class PopupMenuItemEmailAction
|
||||
this.imagePaths, {
|
||||
super.key,
|
||||
super.category,
|
||||
super.submenu,
|
||||
});
|
||||
|
||||
@override
|
||||
@@ -29,4 +30,7 @@ class PopupMenuItemEmailAction
|
||||
|
||||
@override
|
||||
Color get actionNameColor => action.getPopupMenuTitleColor();
|
||||
|
||||
@override
|
||||
String get hoverIcon => imagePaths.icThumbsUp;
|
||||
}
|
||||
|
||||
+34
-1
@@ -20,6 +20,7 @@ import 'package:jmap_dart_client/jmap/core/session/session.dart';
|
||||
import 'package:jmap_dart_client/jmap/mail/email/email.dart';
|
||||
import 'package:jmap_dart_client/jmap/mail/email/email_address.dart';
|
||||
import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart';
|
||||
import 'package:labels/model/label.dart';
|
||||
import 'package:model/email/email_action_type.dart';
|
||||
import 'package:model/email/mark_star_action.dart';
|
||||
import 'package:model/email/presentation_email.dart';
|
||||
@@ -32,6 +33,7 @@ import 'package:model/mailbox/presentation_mailbox.dart';
|
||||
import 'package:tmail_ui_user/features/base/mixin/message_dialog_action_manager.dart';
|
||||
import 'package:tmail_ui_user/features/base/widget/context_menu/context_menu_item_action.dart';
|
||||
import 'package:tmail_ui_user/features/base/widget/popup_menu/popup_menu_action_group_widget.dart';
|
||||
import 'package:tmail_ui_user/features/base/widget/popup_menu/popup_submenu_controller.dart';
|
||||
import 'package:tmail_ui_user/features/destination_picker/presentation/model/destination_picker_arguments.dart';
|
||||
import 'package:tmail_ui_user/features/email/domain/model/email_print.dart';
|
||||
import 'package:tmail_ui_user/features/email/domain/model/mark_read_action.dart';
|
||||
@@ -50,6 +52,7 @@ import 'package:tmail_ui_user/features/email/presentation/model/popup_menu_item_
|
||||
import 'package:tmail_ui_user/features/email/presentation/utils/email_utils.dart';
|
||||
import 'package:tmail_ui_user/features/email/presentation/widgets/email_address_bottom_sheet_builder.dart';
|
||||
import 'package:tmail_ui_user/features/email/presentation/widgets/email_address_dialog_builder.dart';
|
||||
import 'package:tmail_ui_user/features/labels/presentation/widgets/label_list_context_menu.dart';
|
||||
import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_actions.dart';
|
||||
import 'package:tmail_ui_user/features/manage_account/domain/model/create_new_email_rule_filter_request.dart';
|
||||
import 'package:tmail_ui_user/features/manage_account/domain/usecases/create_new_email_rule_filter_interactor.dart';
|
||||
@@ -486,6 +489,7 @@ class EmailActionReactor {
|
||||
required bool isLabelFeatureEnabled,
|
||||
required OpenBottomSheetContextMenuAction openBottomSheetContextMenu,
|
||||
required OpenPopupMenuActionGroup openPopupMenu,
|
||||
List<Label>? labels,
|
||||
}) {
|
||||
if (currentContext == null) return;
|
||||
|
||||
@@ -566,20 +570,49 @@ class EmailActionReactor {
|
||||
imagePaths,
|
||||
key: '${actionType.name}_action',
|
||||
category: actionType.category,
|
||||
submenu: _getEmailActionSubmenu(
|
||||
actionType: actionType,
|
||||
imagePaths: imagePaths,
|
||||
presentationEmail: presentationEmail,
|
||||
labels: labels,
|
||||
),
|
||||
);
|
||||
}).toList();
|
||||
|
||||
final submenuController = PopupSubmenuController();
|
||||
|
||||
final popupMenuWidget = PopupMenuActionGroupWidget(
|
||||
actions: popupMenuItemEmailActions,
|
||||
submenuController: submenuController,
|
||||
onActionSelected: (action) {
|
||||
handleEmailAction(presentationEmail, action.action);
|
||||
},
|
||||
);
|
||||
|
||||
openPopupMenu(currentContext!, position, popupMenuWidget);
|
||||
openPopupMenu(
|
||||
currentContext!,
|
||||
position,
|
||||
popupMenuWidget,
|
||||
).whenComplete(submenuController.hide);
|
||||
}
|
||||
}
|
||||
|
||||
Widget? _getEmailActionSubmenu({
|
||||
required EmailActionType actionType,
|
||||
required ImagePaths imagePaths,
|
||||
required PresentationEmail presentationEmail,
|
||||
required List<Label>? labels,
|
||||
}) {
|
||||
if (actionType == EmailActionType.labelAs) {
|
||||
return LabelListContextMenu(
|
||||
labelList: labels ?? [],
|
||||
presentationEmail: presentationEmail,
|
||||
imagePaths: imagePaths,
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
bool _canDeletePermanently(
|
||||
PresentationEmail email,
|
||||
PresentationMailbox? mailboxContain,
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
import 'package:core/presentation/extensions/color_extension.dart';
|
||||
import 'package:core/presentation/resources/image_paths.dart';
|
||||
import 'package:core/presentation/utils/theme_utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:labels/extensions/label_extension.dart';
|
||||
import 'package:labels/model/label.dart';
|
||||
|
||||
class LabelItemContextMenu extends StatelessWidget {
|
||||
final Label label;
|
||||
final ImagePaths imagePaths;
|
||||
final bool isSelected;
|
||||
|
||||
const LabelItemContextMenu({
|
||||
super.key,
|
||||
required this.label,
|
||||
required this.imagePaths,
|
||||
required this.isSelected,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Material(
|
||||
type: MaterialType.transparency,
|
||||
child: InkWell(
|
||||
onTap: () {},
|
||||
child: Container(
|
||||
height: 48,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10),
|
||||
child: Row(
|
||||
children: [
|
||||
SvgPicture.asset(
|
||||
isSelected
|
||||
? imagePaths.icCheckboxSelected
|
||||
: imagePaths.icCheckboxUnselected,
|
||||
width: 20,
|
||||
height: 20,
|
||||
colorFilter: isSelected
|
||||
? AppColor.primaryMain.asFilter()
|
||||
: AppColor.steelGrayA540.asFilter(),
|
||||
fit: BoxFit.fill,
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
Expanded(
|
||||
child: Text(
|
||||
label.safeDisplayName,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: ThemeUtils.textStyleBodyBody3(color: Colors.black),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
import 'package:core/presentation/resources/image_paths.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:labels/model/label.dart';
|
||||
import 'package:model/email/presentation_email.dart';
|
||||
import 'package:tmail_ui_user/features/email/presentation/extensions/presentation_email_extension.dart';
|
||||
import 'package:tmail_ui_user/features/labels/presentation/widgets/label_item_context_menu.dart';
|
||||
|
||||
class LabelListContextMenu extends StatelessWidget {
|
||||
final PresentationEmail presentationEmail;
|
||||
final List<Label> labelList;
|
||||
final ImagePaths imagePaths;
|
||||
|
||||
const LabelListContextMenu({
|
||||
super.key,
|
||||
required this.labelList,
|
||||
required this.presentationEmail,
|
||||
required this.imagePaths,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final emailLabels = presentationEmail.getLabelList(labelList);
|
||||
|
||||
return ListView.builder(
|
||||
shrinkWrap: true,
|
||||
itemCount: labelList.length,
|
||||
itemBuilder: (context, index) {
|
||||
final label = labelList[index];
|
||||
final isSelected = emailLabels.contains(label);
|
||||
return LabelItemContextMenu(
|
||||
label: label,
|
||||
imagePaths: imagePaths,
|
||||
isSelected: isSelected,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -26,4 +26,7 @@ class PopupMenuItemMailboxAction
|
||||
|
||||
@override
|
||||
Color get actionNameColor => action.getPopupMenuTitleColor();
|
||||
|
||||
@override
|
||||
String get hoverIcon => imagePaths.icThumbsUp;
|
||||
}
|
||||
|
||||
+3
@@ -27,4 +27,7 @@ class PopupMenuItemProfileSettingTypeAction
|
||||
|
||||
@override
|
||||
EdgeInsetsGeometry get itemPadding => const EdgeInsets.symmetric(horizontal: 16);
|
||||
|
||||
@override
|
||||
String get hoverIcon => imagePaths.icThumbsUp;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user