TF-4233 Show popup menu when click three dot on label item
This commit is contained in:
@@ -1,20 +1,25 @@
|
||||
import 'package:core/presentation/extensions/color_extension.dart';
|
||||
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:labels/extensions/label_extension.dart';
|
||||
import 'package:labels/model/label.dart';
|
||||
import 'package:tmail_ui_user/features/mailbox/presentation/styles/mailbox_item_widget_styles.dart';
|
||||
import 'package:tmail_ui_user/features/mailbox/presentation/styles/trailing_mailbox_item_widget_styles.dart';
|
||||
import 'package:tmail_ui_user/features/mailbox/presentation/utils/labels/label_method_action_define.dart';
|
||||
import 'package:tmail_ui_user/features/mailbox/presentation/widgets/labels/label_icon_widget.dart';
|
||||
import 'package:tmail_ui_user/features/mailbox/presentation/widgets/labels/label_name_widget.dart';
|
||||
|
||||
typedef OnOpenLabelCallback = void Function(Label label);
|
||||
|
||||
class LabelListItem extends StatelessWidget {
|
||||
class LabelListItem extends StatefulWidget {
|
||||
final Label label;
|
||||
final ImagePaths imagePaths;
|
||||
final bool isDesktop;
|
||||
final bool isSelected;
|
||||
final bool isMobileResponsive;
|
||||
final OnOpenLabelCallback onOpenLabelCallback;
|
||||
final OnOpenLabelContextMenuAction? onOpenContextMenu;
|
||||
|
||||
const LabelListItem({
|
||||
super.key,
|
||||
@@ -23,11 +28,30 @@ class LabelListItem extends StatelessWidget {
|
||||
required this.onOpenLabelCallback,
|
||||
this.isDesktop = false,
|
||||
this.isSelected = false,
|
||||
this.isMobileResponsive = false,
|
||||
this.onOpenContextMenu,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final borderRadius = BorderRadius.all(
|
||||
State<LabelListItem> createState() => _LabelListItemState();
|
||||
}
|
||||
|
||||
class _LabelListItemState extends State<LabelListItem> {
|
||||
bool _isContextMenuVisible = false;
|
||||
bool _isItemHovered = false;
|
||||
|
||||
late final BorderRadius _borderRadius;
|
||||
late final EdgeInsetsGeometry _itemPadding;
|
||||
late final double _itemHeight;
|
||||
late final double _iconSpacing;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
final isDesktop = widget.isDesktop;
|
||||
|
||||
_borderRadius = BorderRadius.all(
|
||||
Radius.circular(
|
||||
isDesktop
|
||||
? MailboxItemWidgetStyles.borderRadius
|
||||
@@ -35,41 +59,57 @@ class LabelListItem extends StatelessWidget {
|
||||
),
|
||||
);
|
||||
|
||||
_itemPadding = EdgeInsetsDirectional.symmetric(
|
||||
horizontal: isDesktop
|
||||
? MailboxItemWidgetStyles.itemPadding
|
||||
: MailboxItemWidgetStyles.mobileItemPadding,
|
||||
);
|
||||
|
||||
_itemHeight = isDesktop
|
||||
? MailboxItemWidgetStyles.height
|
||||
: MailboxItemWidgetStyles.mobileHeight;
|
||||
|
||||
_iconSpacing = isDesktop
|
||||
? MailboxItemWidgetStyles.labelIconSpace
|
||||
: MailboxItemWidgetStyles.mobileLabelIconSpace;
|
||||
}
|
||||
|
||||
bool get _isMenuButtonVisible => _isContextMenuVisible || _isItemHovered;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Material(
|
||||
type: MaterialType.transparency,
|
||||
child: InkWell(
|
||||
borderRadius: borderRadius,
|
||||
onTap: () => onOpenLabelCallback(label),
|
||||
borderRadius: _borderRadius,
|
||||
onHover: _handleHoverChanged,
|
||||
onTap: () => widget.onOpenLabelCallback(widget.label),
|
||||
child: Container(
|
||||
height: _itemHeight,
|
||||
padding: _itemPadding,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: borderRadius,
|
||||
borderRadius: _borderRadius,
|
||||
color: _backgroundColorItem,
|
||||
),
|
||||
padding: EdgeInsetsDirectional.symmetric(
|
||||
horizontal: isDesktop
|
||||
? MailboxItemWidgetStyles.itemPadding
|
||||
: MailboxItemWidgetStyles.mobileItemPadding,
|
||||
),
|
||||
height: isDesktop
|
||||
? MailboxItemWidgetStyles.height
|
||||
: MailboxItemWidgetStyles.mobileHeight,
|
||||
child: Row(
|
||||
children: [
|
||||
LabelIconWidget(
|
||||
icon: imagePaths.icTag,
|
||||
color: label.backgroundColor,
|
||||
padding: EdgeInsetsDirectional.only(
|
||||
end: isDesktop
|
||||
? MailboxItemWidgetStyles.labelIconSpace
|
||||
: MailboxItemWidgetStyles.mobileLabelIconSpace,
|
||||
),
|
||||
_LabelIcon(
|
||||
icon: widget.imagePaths.icTag,
|
||||
color: widget.label.backgroundColor,
|
||||
spacing: _iconSpacing,
|
||||
),
|
||||
Expanded(
|
||||
child: LabelNameWidget(
|
||||
name: label.safeDisplayName,
|
||||
isDesktop: isDesktop,
|
||||
name: widget.label.safeDisplayName,
|
||||
isDesktop: widget.isDesktop,
|
||||
),
|
||||
),
|
||||
_ContextMenuButton(
|
||||
visible: _isMenuButtonVisible,
|
||||
backgroundColor: _menuButtonBackgroundColor(context),
|
||||
imagePaths: widget.imagePaths,
|
||||
onTap: _handleMenuTap,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -78,5 +118,90 @@ class LabelListItem extends StatelessWidget {
|
||||
}
|
||||
|
||||
Color get _backgroundColorItem =>
|
||||
isSelected ? AppColor.blue100 : Colors.transparent;
|
||||
widget.isSelected ? AppColor.blue100 : Colors.transparent;
|
||||
|
||||
Color _menuButtonBackgroundColor(BuildContext context) {
|
||||
if (_isContextMenuVisible) {
|
||||
return Theme.of(context).colorScheme.outline.withValues(alpha: 0.08);
|
||||
}
|
||||
return TrailingMailboxItemWidgetStyles.menuIconBackgroundColor;
|
||||
}
|
||||
|
||||
void _handleHoverChanged(bool hovered) {
|
||||
if (_isItemHovered == hovered) return;
|
||||
|
||||
setState(() {
|
||||
_isItemHovered = hovered;
|
||||
});
|
||||
}
|
||||
|
||||
void _setContextMenuVisible(bool visible) {
|
||||
if (_isContextMenuVisible == visible) return;
|
||||
|
||||
setState(() {
|
||||
_isContextMenuVisible = visible;
|
||||
});
|
||||
}
|
||||
|
||||
void _handleMenuTap(RelativeRect position) {
|
||||
if (!widget.isMobileResponsive) {
|
||||
_setContextMenuVisible(true);
|
||||
}
|
||||
|
||||
widget.onOpenContextMenu?.call(widget.label, position).whenComplete(() {
|
||||
if (mounted && !widget.isMobileResponsive) {
|
||||
_setContextMenuVisible(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class _LabelIcon extends StatelessWidget {
|
||||
final String icon;
|
||||
final Color? color;
|
||||
final double spacing;
|
||||
|
||||
const _LabelIcon({
|
||||
required this.icon,
|
||||
required this.color,
|
||||
required this.spacing,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return LabelIconWidget(
|
||||
icon: icon,
|
||||
color: color,
|
||||
padding: EdgeInsetsDirectional.only(end: spacing),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _ContextMenuButton extends StatelessWidget {
|
||||
final bool visible;
|
||||
final Color backgroundColor;
|
||||
final ImagePaths imagePaths;
|
||||
final ValueChanged<RelativeRect> onTap;
|
||||
|
||||
const _ContextMenuButton({
|
||||
required this.visible,
|
||||
required this.backgroundColor,
|
||||
required this.imagePaths,
|
||||
required this.onTap,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Offstage(
|
||||
offstage: !visible,
|
||||
child: TMailButtonWidget.fromIcon(
|
||||
margin: TrailingMailboxItemWidgetStyles.menuIconMargin,
|
||||
icon: imagePaths.icMoreVertical,
|
||||
iconSize: TrailingMailboxItemWidgetStyles.menuIconSize,
|
||||
padding: TrailingMailboxItemWidgetStyles.menuIconPadding,
|
||||
backgroundColor: backgroundColor,
|
||||
onTapActionAtPositionCallback: onTap,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import 'package:core/presentation/resources/image_paths.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:jmap_dart_client/jmap/core/id.dart';
|
||||
import 'package:labels/model/label.dart';
|
||||
import 'package:tmail_ui_user/features/mailbox/presentation/utils/labels/label_method_action_define.dart';
|
||||
import 'package:tmail_ui_user/features/mailbox/presentation/widgets/labels/label_list_item.dart';
|
||||
|
||||
class LabelListView extends StatelessWidget {
|
||||
@@ -10,6 +11,8 @@ class LabelListView extends StatelessWidget {
|
||||
final bool isDesktop;
|
||||
final Id? labelIdSelected;
|
||||
final OnOpenLabelCallback onOpenLabelCallback;
|
||||
final bool isMobileResponsive;
|
||||
final OnOpenLabelContextMenuAction? onOpenContextMenu;
|
||||
|
||||
const LabelListView({
|
||||
super.key,
|
||||
@@ -18,6 +21,8 @@ class LabelListView extends StatelessWidget {
|
||||
required this.onOpenLabelCallback,
|
||||
this.isDesktop = false,
|
||||
this.labelIdSelected,
|
||||
this.isMobileResponsive = false,
|
||||
this.onOpenContextMenu,
|
||||
});
|
||||
|
||||
@override
|
||||
@@ -36,6 +41,8 @@ class LabelListView extends StatelessWidget {
|
||||
isSelected: label.id == labelIdSelected,
|
||||
isDesktop: isDesktop,
|
||||
onOpenLabelCallback: onOpenLabelCallback,
|
||||
isMobileResponsive: isMobileResponsive,
|
||||
onOpenContextMenu: onOpenContextMenu,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user