TF-3759 Update style popup context menu for filter message option on mailbox dashboard view

Signed-off-by: dab246 <tdvu@linagora.com>
This commit is contained in:
dab246
2025-06-14 01:17:15 +07:00
committed by Dat H. Pham
parent b7e570293c
commit 506603b44f
7 changed files with 131 additions and 171 deletions
@@ -1,4 +1,3 @@
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';
@@ -7,57 +6,66 @@ import 'package:flutter_svg/flutter_svg.dart';
import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/styles/filter_message_button_style.dart';
import 'package:tmail_ui_user/features/thread/domain/model/filter_message_option.dart';
typedef OnSelectFilterMessageOptionAction = Function(
BuildContext context,
FilterMessageOption filterMessageOption,
RelativeRect buttonPosition);
typedef OnDeleteFilterMessageOptionAction = Function(FilterMessageOption filterMessageOption);
class FilterMessageButton extends StatelessWidget {
typedef OnSelectFilterMessageOptionAction = Function(BuildContext context,
FilterMessageOption filterMessageOption, RelativeRect buttonPosition);
typedef OnDeleteFilterMessageOptionAction = Function(
FilterMessageOption filterMessageOption);
class FilterMessageButton extends StatefulWidget {
static const int _titleCharactersMaximum = 20;
final FilterMessageOption filterMessageOption;
final bool isSelected;
final ImagePaths imagePaths;
final OnSelectFilterMessageOptionAction? onSelectFilterMessageOptionAction;
final OnDeleteFilterMessageOptionAction? onDeleteFilterMessageOptionAction;
final OnSelectFilterMessageOptionAction onSelectFilterMessageOptionAction;
final OnDeleteFilterMessageOptionAction onDeleteFilterMessageOptionAction;
const FilterMessageButton({
super.key,
required this.filterMessageOption,
required this.imagePaths,
this.isSelected = false,
this.onSelectFilterMessageOptionAction,
this.onDeleteFilterMessageOptionAction,
required this.onSelectFilterMessageOptionAction,
required this.onDeleteFilterMessageOptionAction,
});
@override
State<FilterMessageButton> createState() => _FilterMessageButtonState();
}
class _FilterMessageButtonState extends State<FilterMessageButton> {
final GlobalKey _buttonKey = GlobalKey();
@override
Widget build(BuildContext context) {
final filterMessageTitle = filterMessageOption.getTitle(context);
final filterMessageTitle = widget.filterMessageOption.getTitle(context);
final deleteButtonWidget = TMailButtonWidget.fromIcon(
icon: imagePaths.icDeleteSelection,
icon: widget.imagePaths.icDeleteSelection,
iconSize: FilterMessageButtonStyle.deleteIconSize,
iconColor: AppColor.colorFilterMessageIcon,
padding: const EdgeInsets.all(8),
backgroundColor: Colors.transparent,
onTapActionCallback: () => onDeleteFilterMessageOptionAction?.call(filterMessageOption),
onTapActionCallback: () => widget.onDeleteFilterMessageOptionAction
.call(widget.filterMessageOption),
);
final listComponentsWidget = [
SvgPicture.asset(
filterMessageOption.getIcon(imagePaths),
widget.filterMessageOption.getIcon(widget.imagePaths),
width: FilterMessageButtonStyle.iconSize,
height: FilterMessageButtonStyle.iconSize,
colorFilter: filterMessageOption.getIconColor().asFilter(),
fit: BoxFit.fill
colorFilter: widget.filterMessageOption.getIconColor().asFilter(),
fit: BoxFit.fill,
),
const SizedBox(width: FilterMessageButtonStyle.spaceSize),
Text(
filterMessageTitle.length > _titleCharactersMaximum
? '${filterMessageTitle.substring(0, _titleCharactersMaximum)}...'
: filterMessageTitle,
filterMessageTitle.length > FilterMessageButton._titleCharactersMaximum
? '${filterMessageTitle.substring(
0,
FilterMessageButton._titleCharactersMaximum,
)}...'
: filterMessageTitle,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: FilterMessageButtonStyle.titleStyle,
@@ -65,78 +73,108 @@ class FilterMessageButton extends StatelessWidget {
Padding(
padding: FilterMessageButtonStyle.elementPadding,
child: SvgPicture.asset(
imagePaths.icDropDown,
widget.imagePaths.icDropDown,
width: FilterMessageButtonStyle.arrowDownIconSize,
height: FilterMessageButtonStyle.arrowDownIconSize,
colorFilter: AppColor.colorFilterMessageIcon.asFilter(),
fit: BoxFit.fill
fit: BoxFit.fill,
),
),
];
final childItem = _buildContainerForComponents(
children: [
... listComponentsWidget,
if (isSelected) deleteButtonWidget,
]
...listComponentsWidget,
if (widget.isSelected) deleteButtonWidget,
],
);
if (onSelectFilterMessageOptionAction == null) {
return childItem;
}
if (!isSelected) {
if (widget.isSelected) {
return _buildContainerForComponents(
key: _buttonKey,
children: [
InkWell(
onTapDown: (details) => _onTapDownAction(context, details),
borderRadius: FilterMessageButtonStyle.borderRadius,
child: Row(
mainAxisSize: MainAxisSize.min,
children: listComponentsWidget,
),
),
if (widget.isSelected) deleteButtonWidget,
],
);
} else {
return Material(
type: MaterialType.transparency,
child: InkWell(
key: _buttonKey,
onTapDown: (details) => _onTapDownAction(context, details),
borderRadius: FilterMessageButtonStyle.borderRadius,
child: childItem
child: childItem,
),
);
}
return _buildContainerForComponents(
children: [
InkWell(
onTapDown: (details) => _onTapDownAction(context, details),
borderRadius: FilterMessageButtonStyle.borderRadius,
child: Row(
mainAxisSize: MainAxisSize.min,
children: listComponentsWidget,
),
),
if (isSelected) deleteButtonWidget,
]
);
}
Widget _buildContainerForComponents({required List<Widget> children}) {
Widget _buildContainerForComponents({
required List<Widget> children,
Key? key,
}) {
return Container(
key: key,
decoration: BoxDecoration(
borderRadius: FilterMessageButtonStyle.borderRadius,
color: filterMessageOption.getBackgroundColor(isSelected: isSelected)
color: widget.filterMessageOption.getBackgroundColor(
isSelected: widget.isSelected,
),
),
padding: FilterMessageButtonStyle.getButtonPadding(isSelected),
padding: FilterMessageButtonStyle.getButtonPadding(widget.isSelected),
child: Row(
mainAxisSize: MainAxisSize.min,
children: children
)
children: children,
),
);
}
void _onTapDownAction(BuildContext context, TapDownDetails details) {
final screenSize = MediaQuery.sizeOf(context);
final offset = details.globalPosition;
final position = RelativeRect.fromLTRB(
offset.dx,
offset.dy,
screenSize.width - offset.dx,
screenSize.height - offset.dy);
RelativeRect? position;
onSelectFilterMessageOptionAction?.call(
final buttonBox =
_buttonKey.currentContext?.findRenderObject() as RenderBox?;
final overlayBox =
Overlay.maybeOf(context)?.context.findRenderObject() as RenderBox?;
if (buttonBox == null || overlayBox == null) {
final screenSize = MediaQuery.sizeOf(context);
final offset = details.globalPosition;
position = RelativeRect.fromLTRB(
offset.dx,
offset.dy,
screenSize.width - offset.dx,
screenSize.height - offset.dy,
);
} else {
final buttonPosition = buttonBox.localToGlobal(
Offset.zero,
ancestor: overlayBox,
);
final buttonSize = buttonBox.size;
position = RelativeRect.fromRect(
Rect.fromLTWH(
buttonPosition.dx,
buttonPosition.dy + buttonSize.height + 8,
0,
0,
),
Offset.zero & overlayBox.size,
);
}
widget.onSelectFilterMessageOptionAction.call(
context,
filterMessageOption,
position);
widget.filterMessageOption,
position,
);
}
}
}