TF-4004 Apply new design for create rule view on web

This commit is contained in:
dab246
2025-09-08 14:30:17 +07:00
committed by Dat H. Pham
parent dc3b4d46dd
commit 8c3c7b1feb
24 changed files with 819 additions and 764 deletions
@@ -9,6 +9,8 @@ typedef OnTextChange = void Function(String text);
class DefaultInputFieldWidget extends StatelessWidget { class DefaultInputFieldWidget extends StatelessWidget {
final TextEditingController textEditingController; final TextEditingController textEditingController;
final String? hintText; final String? hintText;
final String? errorText;
final Color? inputColor;
final FocusNode? focusNode; final FocusNode? focusNode;
final OnTextChange? onTextChange; final OnTextChange? onTextChange;
final OnTextSubmitted? onTextSubmitted; final OnTextSubmitted? onTextSubmitted;
@@ -17,7 +19,9 @@ class DefaultInputFieldWidget extends StatelessWidget {
super.key, super.key,
required this.textEditingController, required this.textEditingController,
this.hintText, this.hintText,
this.errorText,
this.focusNode, this.focusNode,
this.inputColor,
this.onTextChange, this.onTextChange,
this.onTextSubmitted, this.onTextSubmitted,
}); });
@@ -29,16 +33,22 @@ class DefaultInputFieldWidget extends StatelessWidget {
maxLines: 1, maxLines: 1,
textInputAction: TextInputAction.next, textInputAction: TextInputAction.next,
textStyle: ThemeUtils.textStyleBodyBody3( textStyle: ThemeUtils.textStyleBodyBody3(
color: AppColor.m3SurfaceBackground, color: inputColor ?? AppColor.m3SurfaceBackground,
), ),
focusNode: focusNode, focusNode: focusNode,
onTextSubmitted: onTextSubmitted, onTextSubmitted: onTextSubmitted,
onTextChange: onTextChange, onTextChange: onTextChange,
decoration: InputDecoration( decoration: InputDecoration(
constraints: const BoxConstraints(maxHeight: 40), constraints: BoxConstraints(
maxHeight: errorText?.isNotEmpty == true ? 60 : 40,
),
filled: true, filled: true,
fillColor: Colors.white, fillColor: errorText?.isNotEmpty == true
contentPadding: const EdgeInsetsDirectional.only(start: 12, end: 8), ? AppColor.colorInputBackgroundErrorVerifyName
: Colors.white,
contentPadding: errorText?.isNotEmpty == true
? const EdgeInsetsDirectional.only(start: 12, end: 8)
: const EdgeInsetsDirectional.only(start: 12, end: 8, top: 12, bottom: 12),
enabledBorder: const OutlineInputBorder( enabledBorder: const OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(10)), borderRadius: BorderRadius.all(Radius.circular(10)),
borderSide: BorderSide(width: 1, color: AppColor.m3Neutral90), borderSide: BorderSide(width: 1, color: AppColor.m3Neutral90),
@@ -51,8 +61,18 @@ class DefaultInputFieldWidget extends StatelessWidget {
borderRadius: BorderRadius.all(Radius.circular(10)), borderRadius: BorderRadius.all(Radius.circular(10)),
borderSide: BorderSide(width: 1, color: AppColor.primaryColor), borderSide: BorderSide(width: 1, color: AppColor.primaryColor),
), ),
errorBorder: const OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(10)),
borderSide: BorderSide(width: 1, color: AppColor.redFF3347),
),
focusedErrorBorder: const OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(10)),
borderSide: BorderSide(width: 1, color: AppColor.redFF3347),
),
hintText: hintText, hintText: hintText,
hintStyle: ThemeUtils.textStyleBodyBody3(color: AppColor.m3Tertiary), hintStyle: ThemeUtils.textStyleBodyBody3(color: AppColor.m3Tertiary),
errorText: errorText,
errorStyle: ThemeUtils.textStyleBodyBody3(color: AppColor.redFF3347),
), ),
); );
} }
@@ -35,6 +35,8 @@ class DropDownButtonWidget<T> extends StatelessWidget {
final double? dropdownWidth; final double? dropdownWidth;
final double? dropdownMaxHeight; final double? dropdownMaxHeight;
final String? hintText; final String? hintText;
final TextStyle? labelTextStyle;
final TextStyle? hintTextStyle;
const DropDownButtonWidget({ const DropDownButtonWidget({
Key? key, Key? key,
@@ -54,6 +56,8 @@ class DropDownButtonWidget<T> extends StatelessWidget {
this.colorButton = Colors.white, this.colorButton = Colors.white,
this.tooltip = '', this.tooltip = '',
this.hintText, this.hintText,
this.labelTextStyle,
this.hintTextStyle,
}) : super(key: key); }) : super(key: key);
@override @override
@@ -68,7 +72,7 @@ class DropDownButtonWidget<T> extends StatelessWidget {
? Row(children: [ ? Row(children: [
Expanded(child: Text( Expanded(child: Text(
_getTextItemDropdown(context, item: itemSelected), _getTextItemDropdown(context, item: itemSelected),
style: ThemeUtils.defaultTextStyleInterFont.copyWith(fontSize: 16, style: hintTextStyle ?? ThemeUtils.defaultTextStyleInterFont.copyWith(fontSize: 16,
fontWeight: FontWeight.normal, fontWeight: FontWeight.normal,
color: Colors.black.withValues(alpha: opacity)), color: Colors.black.withValues(alpha: opacity)),
maxLines: 1, maxLines: 1,
@@ -86,7 +90,7 @@ class DropDownButtonWidget<T> extends StatelessWidget {
height: heightItem, height: heightItem,
child: Row(children: [ child: Row(children: [
Expanded(child: Text(_getTextItemDropdown(context, item: item), Expanded(child: Text(_getTextItemDropdown(context, item: item),
style: ThemeUtils.defaultTextStyleInterFont.copyWith( style: labelTextStyle ?? ThemeUtils.defaultTextStyleInterFont.copyWith(
fontSize: 16, fontSize: 16,
fontWeight: FontWeight.normal, fontWeight: FontWeight.normal,
color: Colors.black), color: Colors.black),
@@ -122,7 +126,7 @@ class DropDownButtonWidget<T> extends StatelessWidget {
child: Row(children: [ child: Row(children: [
Expanded(child: Text( Expanded(child: Text(
_getTextItemDropdown(context, item: itemSelected), _getTextItemDropdown(context, item: itemSelected),
style: ThemeUtils.defaultTextStyleInterFont.copyWith(fontSize: 16, style: labelTextStyle ?? ThemeUtils.defaultTextStyleInterFont.copyWith(fontSize: 16,
fontWeight: FontWeight.normal, fontWeight: FontWeight.normal,
color: itemSelected != null ? Colors.black.withValues(alpha: opacity) : AppColor.textFieldHintColor), color: itemSelected != null ? Colors.black.withValues(alpha: opacity) : AppColor.textFieldHintColor),
maxLines: 1, maxLines: 1,
@@ -7,8 +7,12 @@ class LabelInputFieldBuilder extends StatelessWidget {
final TextEditingController textEditingController; final TextEditingController textEditingController;
final String? errorText; final String? errorText;
final String? hintText; final String? hintText;
final TextStyle? labelStyle;
final FocusNode? focusNode; final FocusNode? focusNode;
final double? runSpacing;
final double? inputFieldMaxWidth;
final bool arrangeHorizontally; final bool arrangeHorizontally;
final bool isLabelHasColon;
final OnTextChange? onTextChange; final OnTextChange? onTextChange;
const LabelInputFieldBuilder({ const LabelInputFieldBuilder({
@@ -16,19 +20,24 @@ class LabelInputFieldBuilder extends StatelessWidget {
required this.label, required this.label,
required this.textEditingController, required this.textEditingController,
this.arrangeHorizontally = true, this.arrangeHorizontally = true,
this.isLabelHasColon = true,
this.hintText, this.hintText,
this.errorText, this.errorText,
this.focusNode, this.focusNode,
this.labelStyle,
this.runSpacing,
this.inputFieldMaxWidth,
this.onTextChange, this.onTextChange,
}); });
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
Widget bodyWidget = ConstrainedBox( Widget bodyWidget = ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 565), constraints: BoxConstraints(maxWidth: inputFieldMaxWidth ?? 565),
child: DefaultInputFieldWidget( child: DefaultInputFieldWidget(
textEditingController: textEditingController, textEditingController: textEditingController,
hintText: hintText, hintText: hintText,
errorText: errorText,
focusNode: focusNode, focusNode: focusNode,
onTextChange: onTextChange, onTextChange: onTextChange,
), ),
@@ -41,7 +50,7 @@ class LabelInputFieldBuilder extends StatelessWidget {
ConstrainedBox( ConstrainedBox(
constraints: const BoxConstraints(minWidth: 83), constraints: const BoxConstraints(minWidth: 83),
child: Text( child: Text(
'$label:', isLabelHasColon ? '$label:' : label,
style: ThemeUtils.textStyleBodyBody3(color: Colors.black), style: ThemeUtils.textStyleBodyBody3(color: Colors.black),
), ),
), ),
@@ -54,10 +63,10 @@ class LabelInputFieldBuilder extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( Text(
'$label:', isLabelHasColon ? '$label:' : label,
style: ThemeUtils.textStyleBodyBody3(color: Colors.black), style: labelStyle ?? ThemeUtils.textStyleBodyBody3(color: Colors.black),
), ),
const SizedBox(height: 10), SizedBox(height: runSpacing ?? 10),
bodyWidget, bodyWidget,
], ],
); );
@@ -10,33 +10,37 @@ import 'package:tmail_ui_user/main/routes/route_utils.dart';
extension PresentationMailboxExtension on PresentationMailbox { extension PresentationMailboxExtension on PresentationMailbox {
String getDisplayName(BuildContext context) { String getDisplayNameByLocalizations(AppLocalizations appLocalizations) {
if (isDefault) { if (isDefault) {
switch(role!.value.toLowerCase()) { switch(role!.value.toLowerCase()) {
case PresentationMailbox.inboxRole: case PresentationMailbox.inboxRole:
return AppLocalizations.of(context).inboxMailboxDisplayName; return appLocalizations.inboxMailboxDisplayName;
case PresentationMailbox.archiveRole: case PresentationMailbox.archiveRole:
return AppLocalizations.of(context).archiveMailboxDisplayName; return appLocalizations.archiveMailboxDisplayName;
case PresentationMailbox.draftsRole: case PresentationMailbox.draftsRole:
return AppLocalizations.of(context).draftsMailboxDisplayName; return appLocalizations.draftsMailboxDisplayName;
case PresentationMailbox.sentRole: case PresentationMailbox.sentRole:
return AppLocalizations.of(context).sentMailboxDisplayName; return appLocalizations.sentMailboxDisplayName;
case PresentationMailbox.outboxRole: case PresentationMailbox.outboxRole:
return AppLocalizations.of(context).outboxMailboxDisplayName; return appLocalizations.outboxMailboxDisplayName;
case PresentationMailbox.trashRole: case PresentationMailbox.trashRole:
return AppLocalizations.of(context).trashMailboxDisplayName; return appLocalizations.trashMailboxDisplayName;
case PresentationMailbox.spamRole: case PresentationMailbox.spamRole:
case PresentationMailbox.junkRole: case PresentationMailbox.junkRole:
return AppLocalizations.of(context).spamMailboxDisplayName; return appLocalizations.spamMailboxDisplayName;
case PresentationMailbox.templatesRole: case PresentationMailbox.templatesRole:
return AppLocalizations.of(context).templatesMailboxDisplayName; return appLocalizations.templatesMailboxDisplayName;
case PresentationMailbox.recoveredRole: case PresentationMailbox.recoveredRole:
return AppLocalizations.of(context).recoveredMailboxDisplayName; return appLocalizations.recoveredMailboxDisplayName;
} }
} }
return name?.name ?? ''; return name?.name ?? '';
} }
String getDisplayName(BuildContext context) {
return getDisplayNameByLocalizations(AppLocalizations.of(context));
}
String getMailboxIcon(ImagePaths imagePaths) { String getMailboxIcon(ImagePaths imagePaths) {
if (hasRole()) { if (hasRole()) {
switch(role!.value) { switch(role!.value) {
@@ -7,12 +7,12 @@ enum CreatorActionType {
create, create,
edit; edit;
String getTitle(BuildContext context) { String getTitle(AppLocalizations appLocalizations) {
switch(this) { switch(this) {
case CreatorActionType.create: case CreatorActionType.create:
return AppLocalizations.of(context).createNewRule.inCaps; return appLocalizations.createANewRule;
case CreatorActionType.edit: case CreatorActionType.edit:
return AppLocalizations.of(context).editRule.inCaps; return appLocalizations.editRule.capitalizeFirstEach;
} }
} }
@@ -3,19 +3,19 @@ import 'package:flutter/material.dart';
class RulesFilterInputFieldArguments with EquatableMixin { class RulesFilterInputFieldArguments with EquatableMixin {
final FocusNode focusNode; final FocusNode focusNode;
final String errorText;
final TextEditingController controller; final TextEditingController controller;
final String? errorText;
RulesFilterInputFieldArguments({ RulesFilterInputFieldArguments({
required this.focusNode, required this.focusNode,
required this.errorText,
required this.controller, required this.controller,
this.errorText,
}); });
@override @override
List<Object?> get props => [ List<Object?> get props => [
focusNode, focusNode,
errorText,
controller, controller,
errorText,
]; ];
} }
@@ -166,7 +166,6 @@ class RulesFilterCreatorController extends BaseMailboxController {
listRuleCondition.add(newRuleCondition); listRuleCondition.add(newRuleCondition);
RulesFilterInputFieldArguments newRuleConditionValueArguments = RulesFilterInputFieldArguments( RulesFilterInputFieldArguments newRuleConditionValueArguments = RulesFilterInputFieldArguments(
focusNode: FocusNode(), focusNode: FocusNode(),
errorText: '',
controller: TextEditingController(), controller: TextEditingController(),
); );
listRuleConditionValueArguments.add(newRuleConditionValueArguments); listRuleConditionValueArguments.add(newRuleConditionValueArguments);
@@ -207,7 +206,6 @@ class RulesFilterCreatorController extends BaseMailboxController {
listRuleCondition.add(condition); listRuleCondition.add(condition);
RulesFilterInputFieldArguments newRuleConditionValueArguments = RulesFilterInputFieldArguments( RulesFilterInputFieldArguments newRuleConditionValueArguments = RulesFilterInputFieldArguments(
focusNode: FocusNode(), focusNode: FocusNode(),
errorText: '',
controller: TextEditingController(), controller: TextEditingController(),
); );
listRuleConditionValueArguments.add(newRuleConditionValueArguments); listRuleConditionValueArguments.add(newRuleConditionValueArguments);
@@ -300,7 +298,7 @@ class RulesFilterCreatorController extends BaseMailboxController {
String? errorString = _getErrorStringByInputValue(context, listRuleCondition[ruleConditionIndex].value); String? errorString = _getErrorStringByInputValue(context, listRuleCondition[ruleConditionIndex].value);
RulesFilterInputFieldArguments newRuleConditionValueArguments = RulesFilterInputFieldArguments( RulesFilterInputFieldArguments newRuleConditionValueArguments = RulesFilterInputFieldArguments(
focusNode: listRuleConditionValueArguments[ruleConditionIndex].focusNode, focusNode: listRuleConditionValueArguments[ruleConditionIndex].focusNode,
errorText: errorString ?? '', errorText: errorString,
controller: listRuleConditionValueArguments[ruleConditionIndex].controller, controller: listRuleConditionValueArguments[ruleConditionIndex].controller,
); );
if (listRuleConditionValueArguments.length > ruleConditionIndex) { if (listRuleConditionValueArguments.length > ruleConditionIndex) {
@@ -611,7 +609,6 @@ class RulesFilterCreatorController extends BaseMailboxController {
listRuleCondition.add(newRuleCondition); listRuleCondition.add(newRuleCondition);
listRuleConditionValueArguments.add(RulesFilterInputFieldArguments( listRuleConditionValueArguments.add(RulesFilterInputFieldArguments(
focusNode: FocusNode(), focusNode: FocusNode(),
errorText: '',
controller: TextEditingController(), controller: TextEditingController(),
)); ));
} }
@@ -1,16 +1,22 @@
import 'dart:math' as math;
import 'package:core/core.dart'; import 'package:core/core.dart';
import 'package:core/presentation/views/dialog/confirm_dialog_button.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/flutter_svg.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:pointer_interceptor/pointer_interceptor.dart';
import 'package:rule_filter/rule_filter/rule_condition_group.dart'; import 'package:rule_filter/rule_filter/rule_condition_group.dart';
import 'package:tmail_ui_user/features/base/widget/default_field/default_close_button_widget.dart';
import 'package:tmail_ui_user/features/base/widget/label_input_field_builder.dart';
import 'package:tmail_ui_user/features/base/widget/pop_back_barrier_widget.dart'; import 'package:tmail_ui_user/features/base/widget/pop_back_barrier_widget.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/extensions/select_rule_action_field_extension.dart'; import 'package:tmail_ui_user/features/rules_filter_creator/presentation/extensions/select_rule_action_field_extension.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/model/email_rule_filter_action.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/model/rule_filter_action_arguments.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/model/rule_filter_condition_type.dart'; import 'package:tmail_ui_user/features/rules_filter_creator/presentation/model/rule_filter_condition_type.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/rules_filter_creator_controller.dart'; import 'package:tmail_ui_user/features/rules_filter_creator/presentation/rules_filter_creator_controller.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/styles/rule_filter_action_styles.dart'; import 'package:tmail_ui_user/features/rules_filter_creator/presentation/widgets/rule_filter_action_widget.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/widgets/rule_filter_action_list.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/widgets/rule_filter_condition_widget.dart'; import 'package:tmail_ui_user/features/rules_filter_creator/presentation/widgets/rule_filter_condition_widget.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/widgets/rule_filter_list_action_widget.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/widgets/rule_filter_title_builder.dart'; import 'package:tmail_ui_user/features/rules_filter_creator/presentation/widgets/rule_filter_title_builder.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/widgets/rules_filter_input_field_builder.dart'; import 'package:tmail_ui_user/features/rules_filter_creator/presentation/widgets/rules_filter_input_field_builder.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
@@ -25,402 +31,258 @@ class RuleFilterCreatorView extends GetWidget<RulesFilterCreatorController> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final responsiveUtil = controller.responsiveUtils; final responsiveUtil = controller.responsiveUtils;
final isMobile = responsiveUtil.isMobile(context);
final focusScope = FocusScope.of(context); final focusScope = FocusScope.of(context);
final currentScreenHeight = responsiveUtil.getSizeScreenHeight(context);
final currentScreenWidth = responsiveUtil.getSizeScreenWidth(context);
return PointerInterceptor( if (!isMobile) {
child: ResponsiveWidget( return Center(
responsiveUtils: responsiveUtil, child: Container(
mobile: Scaffold( decoration: BoxDecoration(
backgroundColor: color: Colors.white,
PlatformInfo.isWeb ? Colors.black.withAlpha(24) : Colors.black38, borderRadius: const BorderRadius.all(Radius.circular(16)),
body: PopBackBarrierWidget( boxShadow: [
child: SafeArea( BoxShadow(
bottom: false, color: Colors.black.withValues(alpha: 0.08),
left: false, blurRadius: 24,
right: false, offset: const Offset(0, 2),
child: GestureDetector(
onTap: focusScope.unfocus,
child: Container(
decoration: const BoxDecoration(
borderRadius: BorderRadius.vertical(
top: Radius.circular(16),
),
color: Colors.white,
),
margin: EdgeInsets.only(top: PlatformInfo.isWeb ? 70 : 0),
child: SafeArea(
child: _buildRulesFilterFormOnMobile(context),
),
),
), ),
BoxShadow(
color: Colors.black.withValues(alpha: 0.08),
blurRadius: 2,
),
],
),
width: math.min(
currentScreenWidth,
612,
),
constraints: BoxConstraints(
maxHeight: math.min(
currentScreenHeight - 100,
674,
), ),
), ),
clipBehavior: Clip.antiAlias,
child: _buildRulesFilterFormOnDesktop(context),
), ),
tablet: Scaffold( );
backgroundColor: Colors.black.withAlpha(24), }
body: PopBackBarrierWidget(
child: Align( return ResponsiveWidget(
alignment: Alignment.bottomCenter, responsiveUtils: responsiveUtil,
child: GestureDetector( mobile: Scaffold(
onTap: focusScope.unfocus, backgroundColor:
child: Container( PlatformInfo.isWeb ? Colors.black.withAlpha(24) : Colors.black38,
decoration: const BoxDecoration( body: PopBackBarrierWidget(
color: Colors.white, child: SafeArea(
borderRadius: BorderRadius.vertical( bottom: false,
top: Radius.circular(16), left: false,
), right: false,
child: GestureDetector(
onTap: focusScope.unfocus,
child: Container(
decoration: const BoxDecoration(
borderRadius: BorderRadius.vertical(
top: Radius.circular(16),
), ),
height: responsiveUtil.getSizeScreenHeight(context) * 0.7,
child: _buildRulesFilterFormOnTablet(context),
),
),
),
),
),
desktop: Scaffold(
backgroundColor: Colors.black.withAlpha(24),
body: PopBackBarrierWidget(
child: Center(
child: GestureDetector(
onTap: focusScope.unfocus,
child: Card(
color: Colors.white, color: Colors.white,
shape: const RoundedRectangleBorder( ),
borderRadius: BorderRadius.all(Radius.circular(16)), margin: EdgeInsets.only(top: PlatformInfo.isWeb ? 70 : 0),
), child: SafeArea(
clipBehavior: Clip.hardEdge, child: _buildRulesFilterFormOnMobile(context),
child: SizedBox(
width: responsiveUtil.getSizeScreenWidth(context) * 0.6,
height: responsiveUtil.getSizeScreenHeight(context) * 0.7,
child: _buildRulesFilterFormOnDesktop(context),
),
), ),
), ),
), ),
), ),
), ),
), ),
tablet: Scaffold(
backgroundColor: Colors.black.withAlpha(24),
body: Center(
child: GestureDetector(
onTap: focusScope.unfocus,
child: Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: const BorderRadius.all(Radius.circular(16)),
boxShadow: [
BoxShadow(
color: Colors.black.withValues(alpha: 0.08),
blurRadius: 24,
offset: const Offset(0, 2),
),
BoxShadow(
color: Colors.black.withValues(alpha: 0.08),
blurRadius: 2,
),
],
),
width: math.min(
currentScreenWidth,
612,
),
constraints: BoxConstraints(
maxHeight: math.min(
currentScreenHeight - 100,
674,
),
),
clipBehavior: Clip.antiAlias,
child: _buildRulesFilterFormOnDesktop(context),
),
),
),
),
); );
} }
Widget _buildRulesFilterFormOnDesktop(BuildContext context) { Widget _buildRulesFilterFormOnDesktop(BuildContext context) {
final appLocalizations = AppLocalizations.of(context);
return Stack( return Stack(
children: [ children: [
Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ Column(
Container( crossAxisAlignment: CrossAxisAlignment.start,
padding: const EdgeInsets.only(top: 16), children: [
alignment: Alignment.center, Container(
child: Obx(() => Text( padding: const EdgeInsets.only(top: 24, bottom: 12),
controller.actionType.value.getTitle(context), alignment: Alignment.center,
style: ThemeUtils.defaultTextStyleInterFont.copyWith( child: Obx(
fontWeight: FontWeight.bold, () => Text(
fontSize: 20, controller.actionType.value.getTitle(appLocalizations),
color: Colors.black)))), textAlign: TextAlign.center,
Expanded(child: SingleChildScrollView( style: Theme.of(context).textTheme.headlineSmall?.copyWith(
physics: const ClampingScrollPhysics(), color: AppColor.m3SurfaceBackground,
child: Padding(
padding: const EdgeInsets.all(24.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Obx(() => RulesFilterInputField(
hintText: AppLocalizations.of(context).rulesNameHintTextInput,
errorText: controller.errorRuleName.value,
editingController: controller.inputRuleNameController,
focusNode: controller.inputRuleNameFocusNode,
onChangeAction: (value) => controller.updateRuleName(context, value),)),
const SizedBox(height: 24),
Obx(() => RuleFilterTitle(
conditionCombinerType: controller.conditionCombinerType.value,
tapActionCallback: (value) => controller.selectConditionCombiner(value),
ruleFilterConditionScreenType: RuleFilterConditionScreenType.desktop,
)),
const SizedBox(height: 24),
_buildListRuleFilterConditionList(context, RuleFilterConditionScreenType.desktop),
Container(
padding: const EdgeInsets.only(top: 8),
child: InkWell(
onTap: controller.tapAddCondition,
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
SvgPicture.asset(
controller.imagePaths.icAddNewFolder,
fit: BoxFit.fill,
),
const SizedBox(width: 15,),
Text(
AppLocalizations.of(context).addCondition,
maxLines: 1,
style: ThemeUtils.defaultTextStyleInterFont.copyWith(
fontWeight: FontWeight.w500,
fontSize: 17,
color: AppColor.primaryColor
)
)
],
), ),
), ),
),
const SizedBox(height: 24),
Text(AppLocalizations.of(context).actionTitleRulesFilter,
overflow: CommonTextStyle.defaultTextOverFlow,
softWrap: CommonTextStyle.defaultSoftWrap,
maxLines: 1,
style: ThemeUtils.defaultTextStyleInterFont.copyWith(
fontWeight: FontWeight.w500,
fontSize: 16,
color: Colors.black)),
const SizedBox(height: 24),
Obx(() {
return RuleFilterActionListWidget(
responsiveUtils: controller.responsiveUtils,
actionList: controller.listEmailRuleFilterActionSelected,
onActionChanged: (newAction, index) {
controller.updateEmailRuleFilterAction(context, newAction, index);
},
forwardEmailEditingController: controller.forwardEmailController,
forwardEmailFocusNode: controller.forwardEmailFocusNode,
onChangeForwardEmail: (value, index) => controller.updateForwardEmailValue(context, value, index),
tapActionDetailedCallback: (index) {
KeyboardUtils.hideKeyboard(context);
controller.selectMailbox(context, index);
},
tapRemoveCallback: (index) => controller.tapRemoveAction(index),
imagePaths: controller.imagePaths,
errorForwardEmail: controller.errorForwardEmailValue.value,
errorMailboxSelected: controller.errorMailboxSelectedValue.value,
);
}),
Obx(() {
if (controller.isShowAddAction.value == true) {
return Container(
padding: const EdgeInsets.only(top: 8),
child: InkWell(
onTap: controller.tapAddAction,
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
SvgPicture.asset(
controller.imagePaths.icAddNewFolder,
fit: BoxFit.fill,
),
const SizedBox(width: 15,),
Text(
AppLocalizations.of(context).addAction,
maxLines: RuleFilterActionStyles.maxLines,
style: RuleFilterActionStyles.addActionButtonTextStyle
)
],
),
),
);
} else {
return const SizedBox.shrink();
}
})
]
), ),
), ),
)), Expanded(
Container( child: SingleChildScrollView(
padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 24), physics: const ClampingScrollPhysics(),
alignment: Alignment.centerRight, child: Padding(
color: Colors.white, padding: const EdgeInsets.symmetric(horizontal: 32),
child: Row( child: Column(
mainAxisSize: MainAxisSize.min,
children: [
buildTextButton(
AppLocalizations.of(context).cancel,
textStyle: ThemeUtils.defaultTextStyleInterFont.copyWith(
fontWeight: FontWeight.w500,
fontSize: 17,
color: AppColor.colorTextButton),
backgroundColor: AppColor.emailAddressChipColor,
width: 128,
height: 44,
radius: 10,
onTap: () => controller.closeView(context)),
const SizedBox(width: 12),
Obx(() => buildTextButton(
controller.actionType.value.getActionName(context),
width: 128,
height: 44,
backgroundColor: AppColor.colorTextButton,
radius: 10,
onTap: () => controller.createNewRuleFilter(context))),
]
),
)
]),
Positioned(top: 8, right: 8,
child: buildIconWeb(
icon: SvgPicture.asset(
controller.imagePaths.icCircleClose,
fit: BoxFit.fill),
tooltip: AppLocalizations.of(context).close,
onTap: () => controller.closeView(context)))
]
);
}
Widget _buildRulesFilterFormOnTablet(BuildContext context) {
return Stack(
children: [
Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
Container(
padding: const EdgeInsets.only(top: 16),
alignment: Alignment.center,
child: Obx(() => Text(
controller.actionType.value.getTitle(context),
style: ThemeUtils.defaultTextStyleInterFont.copyWith(
fontWeight: FontWeight.bold,
fontSize: 20,
color: Colors.black)))),
Expanded(child: SingleChildScrollView(
physics: const ClampingScrollPhysics(),
child: Padding(
padding: const EdgeInsets.all(24.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Obx(() => RulesFilterInputField( Obx(
hintText: AppLocalizations.of(context).rulesNameHintTextInput, () => LabelInputFieldBuilder(
errorText: controller.errorRuleName.value, label: appLocalizations.ruleName,
editingController: controller.inputRuleNameController, hintText: appLocalizations.rulesNameHintTextInput,
focusNode: controller.inputRuleNameFocusNode, textEditingController:
onChangeAction: (value) => controller.updateRuleName(context, value),)), controller.inputRuleNameController,
const SizedBox(height: 24), focusNode: controller.inputRuleNameFocusNode,
Obx(() => RuleFilterTitle( errorText: controller.errorRuleName.value,
conditionCombinerType: controller.conditionCombinerType.value, arrangeHorizontally: false,
tapActionCallback: (value) => controller.selectConditionCombiner(value), isLabelHasColon: false,
ruleFilterConditionScreenType: RuleFilterConditionScreenType.tablet, labelStyle: ThemeUtils.textStyleInter600().copyWith(
)), fontSize: 14,
const SizedBox(height: 24), height: 18 / 14,
_buildListRuleFilterConditionList(context, RuleFilterConditionScreenType.tablet), color: Colors.black,
Container( ),
padding: const EdgeInsets.only(top: 8), runSpacing: 16,
child: InkWell( inputFieldMaxWidth: double.infinity,
onTap: controller.tapAddCondition, onTextChange: (value) =>
child: Row( controller.updateRuleName(context, value),
crossAxisAlignment: CrossAxisAlignment.center, ),
children: [ ),
SvgPicture.asset( Padding(
controller.imagePaths.icAddNewFolder, padding: const EdgeInsets.symmetric(
fit: BoxFit.fill, vertical: 24,
), ),
const SizedBox(width: 15,), child: Text(
Text( appLocalizations.condition,
AppLocalizations.of(context).addCondition, style: ThemeUtils.textStyleInter600().copyWith(
maxLines: 1, fontSize: 14,
style: ThemeUtils.defaultTextStyleInterFont.copyWith( height: 18 / 14,
fontWeight: FontWeight.w500, color: Colors.black,
fontSize: 17,
color: AppColor.primaryColor
)
)
],
), ),
), ),
), ),
const SizedBox(height: 24), Obx(
Text(AppLocalizations.of(context).actionTitleRulesFilter, () => RuleFilterTitle(
overflow: CommonTextStyle.defaultTextOverFlow, conditionCombinerType:
softWrap: CommonTextStyle.defaultSoftWrap, controller.conditionCombinerType.value,
maxLines: 1,
style: ThemeUtils.defaultTextStyleInterFont.copyWith(
fontWeight: FontWeight.w500,
fontSize: 16,
color: Colors.black)),
const SizedBox(height: 24),
Obx(() {
return RuleFilterActionListWidget(
responsiveUtils: controller.responsiveUtils, responsiveUtils: controller.responsiveUtils,
actionList: controller.listEmailRuleFilterActionSelected, tapActionCallback: (value) =>
onActionChanged: (newAction, index) { controller.selectConditionCombiner(value),
controller.updateEmailRuleFilterAction(context, newAction, index); ruleFilterConditionScreenType:
}, RuleFilterConditionScreenType.desktop,
forwardEmailEditingController: controller.forwardEmailController, ),
forwardEmailFocusNode: controller.forwardEmailFocusNode, ),
onChangeForwardEmail: (value, index) => controller.updateForwardEmailValue(context, value, index), const SizedBox(height: 8),
tapActionDetailedCallback: (index) { _buildListRuleFilterConditionList(
KeyboardUtils.hideKeyboard(context); context,
controller.selectMailbox(context, index); RuleFilterConditionScreenType.desktop,
}, ),
tapRemoveCallback: (index) => controller.tapRemoveAction(index), Container(
imagePaths: controller.imagePaths, constraints: const BoxConstraints(minWidth: 161),
errorForwardEmail: controller.errorForwardEmailValue.value, height: 36,
errorMailboxSelected: controller.errorMailboxSelectedValue.value, margin: const EdgeInsetsDirectional.only(top: 12),
); child: ConfirmDialogButton(
}), label: AppLocalizations.of(context).addACondition,
backgroundColor: Colors.white,
textColor: AppColor.primaryMain,
borderColor: AppColor.primaryMain,
icon: controller.imagePaths.icAddIdentity,
onTapAction: controller.tapAddCondition,
),
),
Padding(
padding: const EdgeInsetsDirectional.only(
top: 24,
bottom: 8,
),
child: Text(
appLocalizations.actionsToPerform,
style: ThemeUtils.textStyleInter600().copyWith(
fontSize: 14,
height: 18 / 14,
color: Colors.black,
),
),
),
_buildListRuleFilterActionList(context),
Obx(() { Obx(() {
if (controller.isShowAddAction.value == true) { if (controller.isShowAddAction.value == true) {
return Container( return Container(
padding: const EdgeInsets.only(top: 8), constraints: const BoxConstraints(minWidth: 161),
child: InkWell( height: 36,
onTap: controller.tapAddAction, margin: const EdgeInsetsDirectional.only(top: 4),
child: Row( child: ConfirmDialogButton(
crossAxisAlignment: CrossAxisAlignment.center, label: AppLocalizations.of(context).addAnAction,
children: [ backgroundColor: Colors.white,
SvgPicture.asset( textColor: AppColor.primaryMain,
controller.imagePaths.icAddNewFolder, borderColor: AppColor.primaryMain,
fit: BoxFit.fill, icon: controller.imagePaths.icAddIdentity,
), onTapAction: controller.tapAddAction,
const SizedBox(width: 15,),
Text(
AppLocalizations.of(context).addAction,
maxLines: RuleFilterActionStyles.maxLines,
style: RuleFilterActionStyles.addActionButtonTextStyle
)
],
),
), ),
); );
} else { } else {
return const SizedBox.shrink(); return const SizedBox.shrink();
} }
}) }),
] ],
),
), ),
), ),
)), ),
Container( RuleFilterListActionWidget(
padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 24), positiveLabel: appLocalizations.createRule,
alignment: Alignment.center, negativeLabel: appLocalizations.cancel,
color: Colors.white, padding: const EdgeInsets.symmetric(vertical: 25, horizontal: 32),
child: Row( onPositiveAction: () => controller.createNewRuleFilter(context),
children: [ onNegativeAction: () => controller.closeView(context),
Expanded(child: buildTextButton( ),
AppLocalizations.of(context).cancel, ],
textStyle: ThemeUtils.defaultTextStyleInterFont.copyWith( ),
fontWeight: FontWeight.w500, DefaultCloseButtonWidget(
fontSize: 17, iconClose: controller.imagePaths.icCloseDialog,
color: AppColor.colorTextButton), onTapActionCallback: () => controller.closeView(context),
backgroundColor: AppColor.emailAddressChipColor, ),
width: 128, ],
height: 44,
radius: 10,
onTap: () => controller.closeView(context))),
const SizedBox(width: 12),
Expanded(child: Obx(() => buildTextButton(
controller.actionType.value.getActionName(context),
width: 128,
height: 44,
backgroundColor: AppColor.colorTextButton,
radius: 10,
onTap: () => controller.createNewRuleFilter(context)))),
]
),
)
]),
Positioned(top: 8, right: 8,
child: buildIconWeb(
icon: SvgPicture.asset(
controller.imagePaths.icCircleClose,
fit: BoxFit.fill),
tooltip: AppLocalizations.of(context).close,
onTap: () => controller.closeView(context)))
]
); );
} }
@@ -432,7 +294,7 @@ class RuleFilterCreatorView extends GetWidget<RulesFilterCreatorController> {
padding: const EdgeInsets.only(top: 16), padding: const EdgeInsets.only(top: 16),
alignment: Alignment.center, alignment: Alignment.center,
child: Obx(() => Text( child: Obx(() => Text(
controller.actionType.value.getTitle(context), controller.actionType.value.getTitle(AppLocalizations.of(context)),
style: ThemeUtils.defaultTextStyleInterFont.copyWith( style: ThemeUtils.defaultTextStyleInterFont.copyWith(
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
fontSize: 20, fontSize: 20,
@@ -456,6 +318,7 @@ class RuleFilterCreatorView extends GetWidget<RulesFilterCreatorController> {
), ),
Obx(() => RuleFilterTitle( Obx(() => RuleFilterTitle(
conditionCombinerType: controller.conditionCombinerType.value, conditionCombinerType: controller.conditionCombinerType.value,
responsiveUtils: controller.responsiveUtils,
ruleFilterConditionScreenType: RuleFilterConditionScreenType.mobile, ruleFilterConditionScreenType: RuleFilterConditionScreenType.mobile,
tapActionCallback: (_) { tapActionCallback: (_) {
controller.selectRuleConditionCombinerAction( controller.selectRuleConditionCombinerAction(
@@ -506,58 +369,26 @@ class RuleFilterCreatorView extends GetWidget<RulesFilterCreatorController> {
fontSize: 16, fontSize: 16,
color: Colors.black)), color: Colors.black)),
const SizedBox(height: 24), const SizedBox(height: 24),
Obx(() { _buildListRuleFilterActionList(context),
return RuleFilterActionListWidget(
responsiveUtils: controller.responsiveUtils,
actionList: controller.listEmailRuleFilterActionSelected,
onActionChangeMobile: (currentAction, index) {
controller.selectRuleFilterAction(
context,
currentAction,
index
);
},
forwardEmailEditingController: controller.forwardEmailController,
forwardEmailFocusNode: controller.forwardEmailFocusNode,
onChangeForwardEmail: (value, index) => controller.updateForwardEmailValue(context, value, index),
tapActionDetailedCallback: (index) {
KeyboardUtils.hideKeyboard(context);
controller.selectMailbox(context, index);
},
tapRemoveCallback: (index) => controller.tapRemoveAction(index),
imagePaths: controller.imagePaths,
errorForwardEmail: controller.errorForwardEmailValue.value,
errorMailboxSelected: controller.errorMailboxSelectedValue.value,
);
}),
Obx(() { Obx(() {
if (controller.isShowAddAction.value == true) { if (controller.isShowAddAction.value == true) {
return Container( return Container(
padding: const EdgeInsets.only(top: 12), constraints: const BoxConstraints(minWidth: 161),
child: InkWell( height: 36,
onTap: controller.tapAddAction, margin: const EdgeInsetsDirectional.only(top: 4),
child: Row( child: ConfirmDialogButton(
crossAxisAlignment: CrossAxisAlignment.center, label: AppLocalizations.of(context).addAnAction,
mainAxisAlignment: MainAxisAlignment.center, backgroundColor: Colors.white,
children: [ textColor: AppColor.primaryMain,
SvgPicture.asset( borderColor: AppColor.primaryMain,
controller.imagePaths.icAddNewFolder, icon: controller.imagePaths.icAddIdentity,
fit: BoxFit.fill, onTapAction: controller.tapAddAction,
),
const SizedBox(width: 15,),
Text(
AppLocalizations.of(context).addAction,
maxLines: RuleFilterActionStyles.maxLines,
style: RuleFilterActionStyles.addActionButtonTextStyle
)
],
),
), ),
); );
} else { } else {
return const SizedBox.shrink(); return const SizedBox.shrink();
} }
}) }),
] ]
), ),
), ),
@@ -604,21 +435,23 @@ class RuleFilterCreatorView extends GetWidget<RulesFilterCreatorController> {
Widget _buildListRuleFilterConditionList( Widget _buildListRuleFilterConditionList(
BuildContext context, BuildContext context,
RuleFilterConditionScreenType ruleFilterConditionScreenType RuleFilterConditionScreenType ruleFilterConditionScreenType,
) { ) {
return Obx(() { return Obx(() {
return ListView.separated( return ListView.builder(
shrinkWrap: true, shrinkWrap: true,
itemCount: controller.listRuleCondition.length, itemCount: controller.listRuleCondition.length,
itemBuilder: (context, index) { itemBuilder: (context, index) {
final conditionItem =
controller.listRuleConditionValueArguments[index];
return RuleFilterConditionWidget( return RuleFilterConditionWidget(
key: ValueKey(controller.listRuleConditionValueArguments[index].focusNode), key: ValueKey(conditionItem.focusNode),
ruleFilterConditionScreenType: ruleFilterConditionScreenType, ruleFilterConditionScreenType: ruleFilterConditionScreenType,
ruleCondition: controller.listRuleCondition[index], ruleCondition: controller.listRuleCondition[index],
imagePaths: controller.imagePaths, imagePaths: controller.imagePaths,
conditionValueErrorText: controller.listRuleConditionValueArguments[index].errorText, conditionValueErrorText: conditionItem.errorText,
conditionValueFocusNode: controller.listRuleConditionValueArguments[index].focusNode, conditionValueFocusNode: conditionItem.focusNode,
conditionValueEditingController: controller.listRuleConditionValueArguments[index].controller, textEditingController: conditionItem.controller,
tapRuleConditionFieldCallback: (value) => tapRuleConditionFieldCallback: (value) =>
controller.selectRuleConditionFieldAction( controller.selectRuleConditionFieldAction(
context, context,
@@ -637,11 +470,66 @@ class RuleFilterCreatorView extends GetWidget<RulesFilterCreatorController> {
), ),
conditionValueOnChangeAction: (value) => conditionValueOnChangeAction: (value) =>
controller.updateConditionValue(context, value, index), controller.updateConditionValue(context, value, index),
tapRemoveRuleFilterConditionCallback: () => controller.tapRemoveCondition(index), onDeleteRuleConditionAction: () =>
controller.tapRemoveCondition(index),
); );
}, },
separatorBuilder: (context, index) { );
return const SizedBox(height: 12,); });
}
Widget _buildListRuleFilterActionList(BuildContext context) {
return Obx(() {
return ListView.builder(
shrinkWrap: true,
itemCount: controller.listEmailRuleFilterActionSelected.length,
padding: const EdgeInsetsDirectional.only(bottom: 8),
itemBuilder: (context, index) {
final currentAction = controller.listEmailRuleFilterActionSelected[index];
String? errorValue;
if (currentAction is ForwardActionArguments) {
errorValue = controller.errorForwardEmailValue.value;
} else {
errorValue = controller.errorMailboxSelectedValue.value;
}
return RuleFilterActionWidget(
responsiveUtils: controller.responsiveUtils,
mailboxSelected: currentAction is MoveMessageActionArguments
? currentAction.mailbox
: null,
errorValue: errorValue,
onActionChangeMobile: () {
controller.selectRuleFilterAction(
context,
currentAction.action,
index,
);
},
onActionChanged: (newAction) {
if (newAction != currentAction.action) {
controller.updateEmailRuleFilterAction(
context,
newAction,
index,
);
}
},
forwardEmailEditingController: currentAction.action == EmailRuleFilterAction.forwardTo
? controller.forwardEmailController
: null,
forwardEmailFocusNode: currentAction.action == EmailRuleFilterAction.forwardTo
? controller.forwardEmailFocusNode
: null,
onChangeForwardEmail: (value) => controller.updateForwardEmailValue(context, value, index),
actionSelected: currentAction.action,
tapActionDetailedCallback: () {
KeyboardUtils.hideKeyboard(context);
controller.selectMailbox(context, index);
},
onDeleteRuleConditionAction: () =>
controller.tapRemoveAction(index),
imagePaths: controller.imagePaths,
);
}, },
); );
}); });
@@ -1,24 +1,7 @@
import 'package:core/presentation/extensions/color_extension.dart';
import 'package:core/presentation/utils/theme_utils.dart';
import 'package:flutter/material.dart';
class RuleFilterActionStyles { class RuleFilterActionStyles {
static const double extentRatio = 0.1; static const double extentRatio = 0.1;
static const double removeButtonRadius = 110.0; static const double removeButtonRadius = 110.0;
static const double mainPadding = 12.0; static const double mainPadding = 12.0;
static const double mainBorderRadius = 12.0; static const double mainBorderRadius = 12.0;
static const int maxLines = 1;
static const double fontSize = 16.0;
static const Color color = Colors.black;
static TextStyle textStyle = ThemeUtils.defaultTextStyleInterFont.copyWith(
fontWeight: FontWeight.normal,
fontSize: fontSize,
color: color,
);
static const double itemDistance = 12.0; static const double itemDistance = 12.0;
static TextStyle addActionButtonTextStyle = ThemeUtils.defaultTextStyleInterFont.copyWith( }
fontWeight: FontWeight.w500,
fontSize: 17.0,
color: AppColor.primaryColor,
);
}
@@ -1,15 +0,0 @@
import 'package:core/presentation/utils/theme_utils.dart';
import 'package:flutter/material.dart';
class RuleFilterTitleStyles {
static const double fontSize = 16.0;
static const Color textColor = Colors.black;
static TextStyle textStyle = ThemeUtils.defaultTextStyleInterFont.copyWith(
fontWeight: FontWeight.w500,
fontSize: fontSize,
color: textColor,
);
static const int textMaxLines = 1;
static const double combinerTypeSelectionAreaWith = 200.0;
static const double combinerTypeSelectionAreaPadding = 12.0;
}
@@ -1,6 +1,7 @@
import 'package:core/core.dart'; import 'package:core/core.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:model/mailbox/presentation_mailbox.dart'; import 'package:model/mailbox/presentation_mailbox.dart';
import 'package:tmail_ui_user/features/base/widget/default_field/default_input_field_widget.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/model/email_rule_filter_action.dart'; import 'package:tmail_ui_user/features/rules_filter_creator/presentation/model/email_rule_filter_action.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/widgets/rule_filter_button_field.dart'; import 'package:tmail_ui_user/features/rules_filter_creator/presentation/widgets/rule_filter_button_field.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/widgets/rules_filter_input_field_builder.dart'; import 'package:tmail_ui_user/features/rules_filter_creator/presentation/widgets/rules_filter_input_field_builder.dart';
@@ -39,12 +40,13 @@ class RuleFilterActionDetailed extends StatelessWidget {
}, },
); );
case EmailRuleFilterAction.forwardTo: case EmailRuleFilterAction.forwardTo:
return RulesFilterInputField( return DefaultInputFieldWidget(
errorText: errorValue, errorText: errorValue,
hintText: AppLocalizations.of(context).forwardEmailHintText, hintText: AppLocalizations.of(context).forwardEmailHintText,
editingController: forwardEmailEditingController, textEditingController: forwardEmailEditingController!,
focusNode: forwardEmailFocusNode, focusNode: forwardEmailFocusNode,
onChangeAction: forwardEmailOnChangeAction, inputColor: Colors.black,
onTextChange: forwardEmailOnChangeAction,
); );
case EmailRuleFilterAction.maskAsSeen: case EmailRuleFilterAction.maskAsSeen:
case EmailRuleFilterAction.starIt: case EmailRuleFilterAction.starIt:
@@ -1,80 +0,0 @@
import 'package:core/presentation/resources/image_paths.dart';
import 'package:core/presentation/utils/responsive_utils.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/model/email_rule_filter_action.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/model/rule_filter_action_arguments.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/widgets/rule_filter_action_widget.dart';
class RuleFilterActionListWidget extends StatelessWidget {
final ResponsiveUtils responsiveUtils;
final List<RuleFilterActionArguments> actionList;
final Function(EmailRuleFilterAction?, int)? onActionChangeMobile;
final Function(EmailRuleFilterAction?, int)? onActionChanged;
final TextEditingController? forwardEmailEditingController;
final FocusNode? forwardEmailFocusNode;
final Function(int)? tapActionDetailedCallback;
final Function(int)? tapRemoveCallback;
final ImagePaths? imagePaths;
final Function(String?, int)? onChangeForwardEmail;
final String? errorForwardEmail;
final String? errorMailboxSelected;
const RuleFilterActionListWidget({
Key? key,
required this.responsiveUtils,
required this.actionList,
this.onActionChangeMobile,
this.onActionChanged,
this.forwardEmailEditingController,
this.forwardEmailFocusNode,
this.tapActionDetailedCallback,
this.tapRemoveCallback,
this.imagePaths,
this.onChangeForwardEmail,
this.errorForwardEmail,
this.errorMailboxSelected,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Obx(() {
return ListView.separated(
shrinkWrap: true,
itemCount: actionList.length,
separatorBuilder: (context, index) {
return const SizedBox(height: 12,);
},
itemBuilder: (context, index) {
final RuleFilterActionArguments currentAction = actionList[index];
String? errorValue;
if (currentAction is ForwardActionArguments) {
errorValue = errorForwardEmail;
} else {
errorValue = errorMailboxSelected;
}
return RuleFilterActionWidget(
responsiveUtils: responsiveUtils,
mailboxSelected: currentAction is MoveMessageActionArguments ? currentAction.mailbox : null,
errorValue: errorValue,
onActionChangeMobile: () {
onActionChangeMobile!(currentAction.action, index);
},
onActionChanged: (newAction) {
if (newAction != currentAction.action) {
onActionChanged!(newAction, index);
}
},
forwardEmailEditingController: currentAction.action == EmailRuleFilterAction.forwardTo ? forwardEmailEditingController : null,
forwardEmailFocusNode: currentAction.action == EmailRuleFilterAction.forwardTo ? forwardEmailFocusNode : null,
onChangeForwardEmail: (value) => onChangeForwardEmail!(value, index),
actionSelected: currentAction.action,
tapActionDetailedCallback: () => tapActionDetailedCallback!(index),
tapRemoveCallback: () => tapRemoveCallback!(index),
imagePaths: imagePaths,
);
},
);
});
}
}
@@ -1,24 +1,25 @@
import 'package:core/presentation/extensions/color_extension.dart';
import 'package:core/presentation/resources/image_paths.dart'; import 'package:core/presentation/resources/image_paths.dart';
import 'package:core/presentation/utils/style_utils.dart'; import 'package:core/presentation/utils/theme_utils.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:model/mailbox/presentation_mailbox.dart'; import 'package:model/mailbox/presentation_mailbox.dart';
import 'package:tmail_ui_user/features/base/widget/default_field/default_input_field_widget.dart';
import 'package:tmail_ui_user/features/base/widget/drop_down_button_widget.dart'; import 'package:tmail_ui_user/features/base/widget/drop_down_button_widget.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/model/email_rule_filter_action.dart'; import 'package:tmail_ui_user/features/rules_filter_creator/presentation/model/email_rule_filter_action.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/styles/rule_filter_action_styles.dart'; import 'package:tmail_ui_user/features/rules_filter_creator/presentation/widgets/rule_filter_button_field.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/widgets/rule_filter_action_detailed_builder.dart'; import 'package:tmail_ui_user/features/rules_filter_creator/presentation/widgets/rule_filter_delete_button_widget.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/widgets/rule_filter_condition_remove_button_builder.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/widgets/rules_filter_input_field_builder.dart'; import 'package:tmail_ui_user/features/rules_filter_creator/presentation/widgets/rules_filter_input_field_builder.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
class RuleFilterActionRow extends StatelessWidget { class RuleFilterActionRow extends StatelessWidget {
final List<EmailRuleFilterAction> actionList; final List<EmailRuleFilterAction> actionList;
final ImagePaths imagePaths;
final OnDeleteRuleConditionAction onDeleteRuleConditionAction;
final EmailRuleFilterAction? actionSelected; final EmailRuleFilterAction? actionSelected;
final Function(EmailRuleFilterAction?)? onActionChanged; final Function(EmailRuleFilterAction?)? onActionChanged;
final PresentationMailbox? mailboxSelected; final PresentationMailbox? mailboxSelected;
final String? errorValue; final String? errorValue;
final Function()? tapActionDetailedCallback; final Function()? tapActionDetailedCallback;
final ImagePaths? imagePaths;
final Function()? tapRemoveActionCallback;
final TextEditingController? forwardEmailEditingController; final TextEditingController? forwardEmailEditingController;
final FocusNode? forwardEmailFocusNode; final FocusNode? forwardEmailFocusNode;
final OnChangeFilterInputAction? onChangeForwardEmail; final OnChangeFilterInputAction? onChangeForwardEmail;
@@ -26,13 +27,13 @@ class RuleFilterActionRow extends StatelessWidget {
const RuleFilterActionRow({ const RuleFilterActionRow({
Key? key, Key? key,
required this.actionList, required this.actionList,
required this.imagePaths,
required this.onDeleteRuleConditionAction,
this.actionSelected, this.actionSelected,
this.onActionChanged, this.onActionChanged,
this.mailboxSelected, this.mailboxSelected,
this.errorValue, this.errorValue,
this.tapActionDetailedCallback, this.tapActionDetailedCallback,
this.imagePaths,
this.tapRemoveActionCallback,
this.forwardEmailEditingController, this.forwardEmailEditingController,
this.forwardEmailFocusNode, this.forwardEmailFocusNode,
this.onChangeForwardEmail, this.onChangeForwardEmail,
@@ -40,9 +41,11 @@ class RuleFilterActionRow extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final supportedAction = actionList.where((action) => action.isSupported).toList(); final supportedAction = actionList
.where((action) => action.isSupported)
.toList();
return Row( return Row(
crossAxisAlignment: actionSelected == EmailRuleFilterAction.moveMessage ? CrossAxisAlignment.center : CrossAxisAlignment.start,
children: [ children: [
Expanded( Expanded(
child: DropDownButtonWidget<EmailRuleFilterAction>( child: DropDownButtonWidget<EmailRuleFilterAction>(
@@ -51,41 +54,54 @@ class RuleFilterActionRow extends StatelessWidget {
onChanged: (newAction) => onActionChanged!(newAction), onChanged: (newAction) => onActionChanged!(newAction),
supportSelectionIcon: true, supportSelectionIcon: true,
supportHint: true, supportHint: true,
labelTextStyle: ThemeUtils.textStyleBodyBody3(
color: Colors.black,
),
hintTextStyle: ThemeUtils.textStyleBodyBody3(
color: AppColor.steelGray400,
),
hintText: AppLocalizations.of(context).selectAction, hintText: AppLocalizations.of(context).selectAction,
), ),
), ),
actionSelected == EmailRuleFilterAction.moveMessage if (actionSelected == EmailRuleFilterAction.moveMessage)
? Padding( ...[
padding: const EdgeInsets.symmetric(horizontal: RuleFilterActionStyles.mainPadding), Padding(
padding: const EdgeInsets.symmetric(horizontal: 8),
child: Text( child: Text(
AppLocalizations.of(context).toFolder, AppLocalizations.of(context).toFolder,
overflow: CommonTextStyle.defaultTextOverFlow, overflow: TextOverflow.ellipsis,
softWrap: CommonTextStyle.defaultSoftWrap, maxLines: 1,
maxLines: RuleFilterActionStyles.maxLines, style: ThemeUtils.textStyleInter400.copyWith(
style: RuleFilterActionStyles.textStyle, fontSize: 14,
height: 18 / 14,
color: Colors.black,
),
), ),
)
: SizedBox(
width: actionSelected == EmailRuleFilterAction.forwardTo ? RuleFilterActionStyles.itemDistance : 0,
), ),
Expanded( Expanded(
child: RuleFilterActionDetailed( child: RuleFilterButtonField<PresentationMailbox>(
actionType: actionSelected, value: mailboxSelected,
mailboxSelected: mailboxSelected, borderColor: errorValue?.isNotEmpty == true
errorValue: errorValue, ? AppColor.redFF3347
tapActionDetailedCallback: tapActionDetailedCallback, : AppColor.m3Neutral90,
forwardEmailEditingController: forwardEmailEditingController, tapActionCallback: (_) => tapActionDetailedCallback?.call(),
forwardEmailFocusNode: forwardEmailFocusNode, ),
forwardEmailOnChangeAction: onChangeForwardEmail, ),
), ]
), else if (actionSelected == EmailRuleFilterAction.forwardTo)
Container( Expanded(
padding: const EdgeInsets.only(left: RuleFilterActionStyles.mainPadding), child: DefaultInputFieldWidget(
alignment: Alignment.center, errorText: errorValue,
child: RuleFilterConditionRemoveButton( hintText: AppLocalizations.of(context).forwardEmailHintText,
imagePath: imagePaths, textEditingController: forwardEmailEditingController!,
tapRemoveRuleFilterConditionCallback: tapRemoveActionCallback, focusNode: forwardEmailFocusNode,
inputColor: Colors.black,
onTextChange: onChangeForwardEmail,
),
), ),
RuleFilterDeleteButtonWidget(
imagePaths: imagePaths,
onDeleteRuleConditionAction: onDeleteRuleConditionAction,
) )
], ],
); );
@@ -48,10 +48,13 @@ class RuleFilterActionRowMobile extends StatelessWidget {
padding: const EdgeInsets.symmetric(vertical: RuleFilterActionStyles.mainPadding), padding: const EdgeInsets.symmetric(vertical: RuleFilterActionStyles.mainPadding),
child: Text( child: Text(
AppLocalizations.of(context).toFolder, AppLocalizations.of(context).toFolder,
overflow: CommonTextStyle.defaultTextOverFlow, overflow: TextOverflow.ellipsis,
softWrap: CommonTextStyle.defaultSoftWrap, maxLines: 1,
maxLines: RuleFilterActionStyles.maxLines, style: ThemeUtils.textStyleInter400.copyWith(
style: RuleFilterActionStyles.textStyle, fontSize: 14,
height: 18 / 14,
color: Colors.black,
),
), ),
) )
: SizedBox( : SizedBox(
@@ -9,12 +9,13 @@ import 'package:tmail_ui_user/features/rules_filter_creator/presentation/model/e
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/styles/rule_filter_action_styles.dart'; import 'package:tmail_ui_user/features/rules_filter_creator/presentation/styles/rule_filter_action_styles.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/widgets/rule_filter_action_row_builder.dart'; import 'package:tmail_ui_user/features/rules_filter_creator/presentation/widgets/rule_filter_action_row_builder.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/widgets/rule_filter_action_row_mobile_builder.dart'; import 'package:tmail_ui_user/features/rules_filter_creator/presentation/widgets/rule_filter_action_row_mobile_builder.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/widgets/rule_filter_delete_button_widget.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/widgets/rules_filter_input_field_builder.dart'; import 'package:tmail_ui_user/features/rules_filter_creator/presentation/widgets/rules_filter_input_field_builder.dart';
class RuleFilterActionWidget extends StatelessWidget { class RuleFilterActionWidget extends StatelessWidget {
final ResponsiveUtils responsiveUtils; final ResponsiveUtils responsiveUtils;
final Function()? tapRemoveCallback; final ImagePaths imagePaths;
final ImagePaths? imagePaths; final OnDeleteRuleConditionAction onDeleteRuleConditionAction;
final EmailRuleFilterAction? actionSelected; final EmailRuleFilterAction? actionSelected;
final Function(EmailRuleFilterAction?)? onActionChanged; final Function(EmailRuleFilterAction?)? onActionChanged;
final Function()? onActionChangeMobile; final Function()? onActionChangeMobile;
@@ -28,8 +29,8 @@ class RuleFilterActionWidget extends StatelessWidget {
const RuleFilterActionWidget({ const RuleFilterActionWidget({
Key? key, Key? key,
required this.responsiveUtils, required this.responsiveUtils,
this.tapRemoveCallback, required this.imagePaths,
this.imagePaths, required this.onDeleteRuleConditionAction,
this.actionSelected, this.actionSelected,
this.onActionChanged, this.onActionChanged,
this.mailboxSelected, this.mailboxSelected,
@@ -43,6 +44,47 @@ class RuleFilterActionWidget extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final isMobile = responsiveUtils.isMobile(context);
Widget bodyWidget = Container(
padding: const EdgeInsetsDirectional.only(start: 12),
margin: const EdgeInsetsDirectional.only(top: 8),
decoration: const BoxDecoration(
color: AppColor.lightGrayF9FAFB,
borderRadius: BorderRadius.all(Radius.circular(10)),
),
height: 72,
alignment: Alignment.center,
child: isMobile
? RuleFilterActionRowMobile(
actionSelected: actionSelected,
mailboxSelected: mailboxSelected,
errorValue: errorValue,
tapActionDetailedCallback: tapActionDetailedCallback,
forwardEmailEditingController: forwardEmailEditingController,
forwardEmailFocusNode: forwardEmailFocusNode,
onChangeForwardEmail: onChangeForwardEmail,
tapActionCallback: onActionChangeMobile,
)
: RuleFilterActionRow(
actionList: EmailRuleFilterAction.values,
actionSelected: actionSelected,
onActionChanged: onActionChanged,
mailboxSelected: mailboxSelected,
errorValue: errorValue,
tapActionDetailedCallback: tapActionDetailedCallback,
imagePaths: imagePaths,
onDeleteRuleConditionAction: onDeleteRuleConditionAction,
forwardEmailEditingController: forwardEmailEditingController,
forwardEmailFocusNode: forwardEmailFocusNode,
onChangeForwardEmail: onChangeForwardEmail,
),
);
if (!isMobile) {
return bodyWidget;
}
return Slidable( return Slidable(
enabled: responsiveUtils.isMobile(context) ? true : false, enabled: responsiveUtils.isMobile(context) ? true : false,
endActionPane: ActionPane( endActionPane: ActionPane(
@@ -55,13 +97,13 @@ class RuleFilterActionWidget extends StatelessWidget {
topRight: Radius.circular(RuleFilterActionStyles.mainBorderRadius), topRight: Radius.circular(RuleFilterActionStyles.mainBorderRadius),
bottomRight: Radius.circular(RuleFilterActionStyles.mainBorderRadius), bottomRight: Radius.circular(RuleFilterActionStyles.mainBorderRadius),
), ),
onPressed: (_) => tapRemoveCallback!(), onPressed: (_) => onDeleteRuleConditionAction(),
backgroundColor: AppColor.colorBackgroundFieldConditionRulesFilter, backgroundColor: AppColor.colorBackgroundFieldConditionRulesFilter,
child: CircleAvatar( child: CircleAvatar(
backgroundColor: AppColor.colorRemoveRuleFilterConditionButton, backgroundColor: AppColor.colorRemoveRuleFilterConditionButton,
radius: RuleFilterActionStyles.removeButtonRadius, radius: RuleFilterActionStyles.removeButtonRadius,
child: SvgPicture.asset( child: SvgPicture.asset(
imagePaths!.icMinimize, imagePaths.icMinimize,
fit: BoxFit.fill, fit: BoxFit.fill,
colorFilter: AppColor.colorDeletePermanentlyButton.asFilter(), colorFilter: AppColor.colorDeletePermanentlyButton.asFilter(),
), ),
@@ -106,7 +148,7 @@ class RuleFilterActionWidget extends StatelessWidget {
errorValue: errorValue, errorValue: errorValue,
tapActionDetailedCallback: tapActionDetailedCallback, tapActionDetailedCallback: tapActionDetailedCallback,
imagePaths: imagePaths, imagePaths: imagePaths,
tapRemoveActionCallback: tapRemoveCallback, onDeleteRuleConditionAction: onDeleteRuleConditionAction,
forwardEmailEditingController: forwardEmailEditingController, forwardEmailEditingController: forwardEmailEditingController,
forwardEmailFocusNode: forwardEmailFocusNode, forwardEmailFocusNode: forwardEmailFocusNode,
onChangeForwardEmail: onChangeForwardEmail, onChangeForwardEmail: onChangeForwardEmail,
@@ -50,10 +50,7 @@ class RuleFilterButtonField<T> extends StatelessWidget {
child: Row(children: [ child: Row(children: [
Expanded(child: Text( Expanded(child: Text(
_getName(context, value), _getName(context, value),
style: ThemeUtils.defaultTextStyleInterFont.copyWith( style: ThemeUtils.textStyleBodyBody3(color: textColor),
fontSize: 16,
fontWeight: FontWeight.normal,
color: textColor),
maxLines: 1, maxLines: 1,
softWrap: CommonTextStyle.defaultSoftWrap, softWrap: CommonTextStyle.defaultSoftWrap,
overflow: CommonTextStyle.defaultTextOverFlow, overflow: CommonTextStyle.defaultTextOverFlow,
@@ -1,24 +0,0 @@
import 'package:core/presentation/resources/image_paths.dart';
import 'package:core/presentation/views/button/tmail_button_widget.dart';
import 'package:flutter/material.dart';
class RuleFilterConditionRemoveButton extends StatelessWidget {
final Function()? tapRemoveRuleFilterConditionCallback;
final ImagePaths? imagePath;
const RuleFilterConditionRemoveButton({
Key? key,
this.tapRemoveRuleFilterConditionCallback,
this.imagePath,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return TMailButtonWidget.fromIcon(
icon: imagePath!.icRemoveRule,
backgroundColor: Colors.transparent,
padding: EdgeInsets.zero,
onTapActionCallback: tapRemoveRuleFilterConditionCallback,
);
}
}
@@ -1,123 +1,139 @@
import 'package:core/core.dart';
import 'package:core/presentation/resources/image_paths.dart'; import 'package:core/presentation/resources/image_paths.dart';
import 'package:core/presentation/utils/keyboard_utils.dart'; import 'package:core/presentation/utils/keyboard_utils.dart';
import 'package:core/presentation/utils/theme_utils.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:rule_filter/rule_filter/rule_condition.dart'; import 'package:rule_filter/rule_filter/rule_condition.dart';
import 'package:rule_filter/rule_filter/rule_condition.dart' as rule_condition; import 'package:rule_filter/rule_filter/rule_condition.dart' as rule_condition;
import 'package:tmail_ui_user/features/base/widget/default_field/default_input_field_widget.dart';
import 'package:tmail_ui_user/features/base/widget/drop_down_button_widget.dart'; import 'package:tmail_ui_user/features/base/widget/drop_down_button_widget.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/model/rule_filter_condition_type.dart'; import 'package:tmail_ui_user/features/rules_filter_creator/presentation/model/rule_filter_condition_type.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/widgets/rule_filter_button_field.dart'; import 'package:tmail_ui_user/features/rules_filter_creator/presentation/widgets/rule_filter_button_field.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/widgets/rule_filter_condition_remove_button_builder.dart'; import 'package:tmail_ui_user/features/rules_filter_creator/presentation/widgets/rule_filter_delete_button_widget.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/widgets/rules_filter_input_field_builder.dart'; import 'package:tmail_ui_user/features/rules_filter_creator/presentation/widgets/rules_filter_input_field_builder.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
class RuleFilterConditionRow extends StatelessWidget { class RuleFilterConditionRow extends StatelessWidget {
final RuleFilterConditionScreenType? ruleFilterConditionScreenType; final RuleFilterConditionScreenType? ruleFilterConditionScreenType;
final RuleCondition ruleCondition; final RuleCondition ruleCondition;
final ImagePaths imagePaths;
final OnDeleteRuleConditionAction onDeleteRuleConditionAction;
final Function(Field?)? tapRuleConditionFieldCallback; final Function(Field?)? tapRuleConditionFieldCallback;
final Function(Comparator?)? tapRuleConditionComparatorCallback; final Function(Comparator?)? tapRuleConditionComparatorCallback;
final String? conditionValueErrorText; final String? conditionValueErrorText;
final TextEditingController? conditionValueEditingController; final TextEditingController textEditingController;
final FocusNode? conditionValueFocusNode; final FocusNode? conditionValueFocusNode;
final OnChangeFilterInputAction? conditionValueOnChangeAction; final OnChangeFilterInputAction? conditionValueOnChangeAction;
final Function()? tapRemoveRuleFilterConditionCallback;
final ImagePaths? imagePaths;
const RuleFilterConditionRow({ const RuleFilterConditionRow({
Key? key, Key? key,
required this.ruleFilterConditionScreenType, required this.ruleFilterConditionScreenType,
required this.ruleCondition, required this.ruleCondition,
required this.imagePaths,
required this.textEditingController,
required this.onDeleteRuleConditionAction,
this.tapRuleConditionFieldCallback, this.tapRuleConditionFieldCallback,
this.tapRuleConditionComparatorCallback, this.tapRuleConditionComparatorCallback,
this.conditionValueErrorText, this.conditionValueErrorText,
this.conditionValueEditingController,
this.conditionValueFocusNode, this.conditionValueFocusNode,
this.conditionValueOnChangeAction, this.conditionValueOnChangeAction,
this.tapRemoveRuleFilterConditionCallback,
this.imagePaths,
}) : super(key: key); }) : super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
switch (ruleFilterConditionScreenType) { switch (ruleFilterConditionScreenType) {
case RuleFilterConditionScreenType.mobile: case RuleFilterConditionScreenType.mobile:
return Column( return Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
RuleFilterButtonField<rule_condition.Field>( RuleFilterButtonField<rule_condition.Field>(
value: ruleCondition.field, value: ruleCondition.field,
tapActionCallback: (value) {
KeyboardUtils.hideKeyboard(context);
tapRuleConditionFieldCallback!(ruleCondition.field);
},
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 12),
child: RuleFilterButtonField<rule_condition.Comparator>(
value: ruleCondition.comparator,
tapActionCallback: (value) { tapActionCallback: (value) {
KeyboardUtils.hideKeyboard(context); KeyboardUtils.hideKeyboard(context);
tapRuleConditionComparatorCallback!(ruleCondition.comparator); tapRuleConditionFieldCallback!(ruleCondition.field);
}, },
) ),
), Padding(
RulesFilterInputField( padding: const EdgeInsets.symmetric(vertical: 12),
hintText: AppLocalizations.of(context).conditionValueHintTextInput, child: RuleFilterButtonField<rule_condition.Comparator>(
errorText: conditionValueErrorText, value: ruleCondition.comparator,
focusNode: conditionValueFocusNode, tapActionCallback: (value) {
onChangeAction: conditionValueOnChangeAction, KeyboardUtils.hideKeyboard(context);
editingController: conditionValueEditingController, tapRuleConditionComparatorCallback!(ruleCondition.comparator);
), },
], )
); ),
RulesFilterInputField(
hintText: AppLocalizations.of(context).conditionValueHintTextInput,
errorText: conditionValueErrorText,
focusNode: conditionValueFocusNode,
onChangeAction: conditionValueOnChangeAction,
editingController: textEditingController,
),
],
);
case RuleFilterConditionScreenType.tablet: case RuleFilterConditionScreenType.tablet:
case RuleFilterConditionScreenType.desktop: case RuleFilterConditionScreenType.desktop:
return Row( return Row(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
const SizedBox(width: 12),
Expanded( Expanded(
child: DropDownButtonWidget<rule_condition.Field>( child: DropDownButtonWidget<rule_condition.Field>(
items: rule_condition.Field.values, items: rule_condition.Field.values,
itemSelected: ruleCondition.field, itemSelected: ruleCondition.field,
dropdownMaxHeight: 250, dropdownMaxHeight: 250,
heightItem: 40,
labelTextStyle: ThemeUtils.textStyleBodyBody3(
color: Colors.black,
),
hintTextStyle: ThemeUtils.textStyleBodyBody3(
color: AppColor.steelGray400,
),
onChanged: (newField) => { onChanged: (newField) => {
tapRuleConditionFieldCallback!(newField) tapRuleConditionFieldCallback!(newField)
}, },
supportSelectionIcon: true, supportSelectionIcon: true,
) )
), ),
Container( const SizedBox(width: 8),
width: 220, Expanded(
padding: const EdgeInsets.symmetric(horizontal: 12),
child: DropDownButtonWidget<rule_condition.Comparator>( child: DropDownButtonWidget<rule_condition.Comparator>(
items: rule_condition.Comparator.values, items: rule_condition.Comparator.values,
itemSelected: ruleCondition.comparator, itemSelected: ruleCondition.comparator,
heightItem: 40,
labelTextStyle: ThemeUtils.textStyleBodyBody3(
color: Colors.black,
),
hintTextStyle: ThemeUtils.textStyleBodyBody3(
color: AppColor.steelGray400,
),
onChanged: (newComparator) => { onChanged: (newComparator) => {
tapRuleConditionComparatorCallback!(newComparator) tapRuleConditionComparatorCallback!(newComparator)
}, },
supportSelectionIcon: true, supportSelectionIcon: true,
) )
), ),
const SizedBox(width: 8),
Expanded( Expanded(
child: RulesFilterInputField( child: DefaultInputFieldWidget(
hintText: AppLocalizations.of(context).conditionValueHintTextInput, hintText: AppLocalizations.of(context).conditionValueHintTextInput,
errorText: conditionValueErrorText, errorText: conditionValueErrorText,
focusNode: conditionValueFocusNode, focusNode: conditionValueFocusNode,
onChangeAction: conditionValueOnChangeAction, inputColor: Colors.black,
editingController: conditionValueEditingController, onTextChange: conditionValueOnChangeAction,
) textEditingController: textEditingController,
),
), ),
Container( RuleFilterDeleteButtonWidget(
padding: const EdgeInsetsDirectional.only(start: 12), imagePaths: imagePaths,
alignment: Alignment.center, margin: const EdgeInsetsDirectional.only(top: 2),
child: RuleFilterConditionRemoveButton( onDeleteRuleConditionAction: onDeleteRuleConditionAction,
tapRemoveRuleFilterConditionCallback: tapRemoveRuleFilterConditionCallback,
imagePath: imagePaths,
)
), ),
], ],
); );
default: default:
return const SizedBox.shrink(); return const SizedBox.shrink();
} }
} }
} }
@@ -4,6 +4,7 @@ import 'package:flutter_slidable/flutter_slidable.dart';
import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/flutter_svg.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/model/rule_filter_condition_type.dart'; import 'package:tmail_ui_user/features/rules_filter_creator/presentation/model/rule_filter_condition_type.dart';
import 'package:rule_filter/rule_filter/rule_condition.dart'; import 'package:rule_filter/rule_filter/rule_condition.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/widgets/rule_filter_delete_button_widget.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/widgets/rule_filter_condition_row_builder.dart'; import 'package:tmail_ui_user/features/rules_filter_creator/presentation/widgets/rule_filter_condition_row_builder.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/widgets/rules_filter_input_field_builder.dart'; import 'package:tmail_ui_user/features/rules_filter_creator/presentation/widgets/rules_filter_input_field_builder.dart';
import 'package:core/presentation/resources/image_paths.dart'; import 'package:core/presentation/resources/image_paths.dart';
@@ -11,31 +12,57 @@ import 'package:core/presentation/resources/image_paths.dart';
class RuleFilterConditionWidget extends StatelessWidget { class RuleFilterConditionWidget extends StatelessWidget {
final RuleFilterConditionScreenType? ruleFilterConditionScreenType; final RuleFilterConditionScreenType? ruleFilterConditionScreenType;
final RuleCondition ruleCondition; final RuleCondition ruleCondition;
final TextEditingController textEditingController;
final Function(Field?)? tapRuleConditionFieldCallback; final Function(Field?)? tapRuleConditionFieldCallback;
final Function(Comparator?)? tapRuleConditionComparatorCallback; final Function(Comparator?)? tapRuleConditionComparatorCallback;
final String? conditionValueErrorText; final String? conditionValueErrorText;
final TextEditingController? conditionValueEditingController;
final FocusNode? conditionValueFocusNode; final FocusNode? conditionValueFocusNode;
final OnChangeFilterInputAction? conditionValueOnChangeAction; final OnChangeFilterInputAction? conditionValueOnChangeAction;
final ImagePaths? imagePaths; final ImagePaths imagePaths;
final Function()? tapRemoveRuleFilterConditionCallback; final OnDeleteRuleConditionAction onDeleteRuleConditionAction;
const RuleFilterConditionWidget({ const RuleFilterConditionWidget({
super.key, super.key,
this.ruleFilterConditionScreenType, this.ruleFilterConditionScreenType,
required this.ruleCondition, required this.ruleCondition,
required this.imagePaths,
required this.textEditingController,
required this.onDeleteRuleConditionAction,
this.tapRuleConditionFieldCallback, this.tapRuleConditionFieldCallback,
this.tapRuleConditionComparatorCallback, this.tapRuleConditionComparatorCallback,
this.conditionValueErrorText, this.conditionValueErrorText,
this.conditionValueEditingController,
this.conditionValueFocusNode, this.conditionValueFocusNode,
this.conditionValueOnChangeAction, this.conditionValueOnChangeAction,
this.imagePaths,
this.tapRemoveRuleFilterConditionCallback,
}); });
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
Widget bodyWidget = Container(
padding: const EdgeInsetsDirectional.symmetric(vertical: 12),
margin: const EdgeInsetsDirectional.only(top: 8),
decoration: const BoxDecoration(
color: AppColor.lightGrayF9FAFB,
borderRadius: BorderRadius.all(Radius.circular(10)),
),
alignment: Alignment.center,
child: RuleFilterConditionRow(
ruleFilterConditionScreenType: ruleFilterConditionScreenType,
ruleCondition: ruleCondition,
tapRuleConditionFieldCallback: tapRuleConditionFieldCallback,
tapRuleConditionComparatorCallback: tapRuleConditionComparatorCallback,
conditionValueErrorText: conditionValueErrorText,
textEditingController: textEditingController,
conditionValueFocusNode: conditionValueFocusNode,
conditionValueOnChangeAction: conditionValueOnChangeAction,
onDeleteRuleConditionAction: onDeleteRuleConditionAction,
imagePaths: imagePaths,
),
);
if (ruleFilterConditionScreenType != RuleFilterConditionScreenType.mobile) {
return bodyWidget;
}
return Slidable( return Slidable(
enabled: ruleFilterConditionScreenType == RuleFilterConditionScreenType.mobile ? true : false, enabled: ruleFilterConditionScreenType == RuleFilterConditionScreenType.mobile ? true : false,
endActionPane: ActionPane( endActionPane: ActionPane(
@@ -45,13 +72,13 @@ class RuleFilterConditionWidget extends StatelessWidget {
CustomSlidableAction( CustomSlidableAction(
padding: const EdgeInsets.only(right: 12), padding: const EdgeInsets.only(right: 12),
borderRadius: const BorderRadius.only(topRight: Radius.circular(12), bottomRight: Radius.circular(12)), borderRadius: const BorderRadius.only(topRight: Radius.circular(12), bottomRight: Radius.circular(12)),
onPressed: (_) => tapRemoveRuleFilterConditionCallback!(), onPressed: (_) => onDeleteRuleConditionAction(),
backgroundColor: AppColor.colorBackgroundFieldConditionRulesFilter, backgroundColor: AppColor.colorBackgroundFieldConditionRulesFilter,
child: CircleAvatar( child: CircleAvatar(
backgroundColor: AppColor.colorRemoveRuleFilterConditionButton, backgroundColor: AppColor.colorRemoveRuleFilterConditionButton,
radius: 110, radius: 110,
child: SvgPicture.asset( child: SvgPicture.asset(
imagePaths!.icMinimize, imagePaths.icMinimize,
fit: BoxFit.fill, fit: BoxFit.fill,
colorFilter: AppColor.colorDeletePermanentlyButton.asFilter(), colorFilter: AppColor.colorDeletePermanentlyButton.asFilter(),
), ),
@@ -59,40 +86,10 @@ class RuleFilterConditionWidget extends StatelessWidget {
) )
] ]
), ),
child: Builder(builder: (context) { child: ValueListenableBuilder<int>(
SlidableController? slideController = Slidable.of(context); valueListenable: Slidable.of(context)?.direction ?? ValueNotifier<int>(0),
return ValueListenableBuilder<int>( builder: (_, __, ___) => bodyWidget,
valueListenable: slideController?.direction ?? ValueNotifier<int>(0), ),
builder: (context, value, _) {
var borderRadius = value != -1 ?
BorderRadius.circular(12) :
const BorderRadius.only(
bottomLeft: Radius.circular(12),
topLeft: Radius.circular(12)
);
return Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: AppColor.colorBackgroundFieldConditionRulesFilter,
borderRadius: borderRadius,
),
child: RuleFilterConditionRow(
ruleFilterConditionScreenType: ruleFilterConditionScreenType,
ruleCondition: ruleCondition,
tapRuleConditionFieldCallback: tapRuleConditionFieldCallback,
tapRuleConditionComparatorCallback: tapRuleConditionComparatorCallback,
conditionValueErrorText: conditionValueErrorText,
conditionValueEditingController: conditionValueEditingController,
conditionValueFocusNode: conditionValueFocusNode,
conditionValueOnChangeAction: conditionValueOnChangeAction,
tapRemoveRuleFilterConditionCallback: tapRemoveRuleFilterConditionCallback,
imagePaths: imagePaths,
)
);
}
);
})
); );
} }
} }
@@ -0,0 +1,31 @@
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';
typedef OnDeleteRuleConditionAction = Function();
class RuleFilterDeleteButtonWidget extends StatelessWidget {
final ImagePaths imagePaths;
final OnDeleteRuleConditionAction onDeleteRuleConditionAction;
final EdgeInsetsGeometry? margin;
const RuleFilterDeleteButtonWidget({
Key? key,
required this.imagePaths,
required this.onDeleteRuleConditionAction,
this.margin,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return TMailButtonWidget.fromIcon(
icon: imagePaths.icDeleteComposer,
iconSize: 20,
iconColor: AppColor.steelGrayA540,
backgroundColor: Colors.transparent,
margin: margin,
onTapActionCallback: onDeleteRuleConditionAction,
);
}
}
@@ -0,0 +1,58 @@
import 'package:core/presentation/extensions/color_extension.dart';
import 'package:core/presentation/views/dialog/confirm_dialog_button.dart';
import 'package:flutter/material.dart';
class RuleFilterListActionWidget extends StatelessWidget {
final String positiveLabel;
final String negativeLabel;
final VoidCallback onNegativeAction;
final VoidCallback onPositiveAction;
final EdgeInsetsGeometry? padding;
const RuleFilterListActionWidget({
super.key,
required this.positiveLabel,
required this.negativeLabel,
required this.onPositiveAction,
required this.onNegativeAction,
this.padding,
});
@override
Widget build(BuildContext context) {
Widget negativeButton = Container(
constraints: const BoxConstraints(minWidth: 67),
height: 48,
child: ConfirmDialogButton(
label: negativeLabel,
onTapAction: onNegativeAction,
),
);
Widget positiveButton = Container(
constraints: const BoxConstraints(minWidth: 143),
height: 48,
child: ConfirmDialogButton(
label: positiveLabel,
backgroundColor: AppColor.primaryMain,
textColor: Colors.white,
onTapAction: onPositiveAction,
),
);
Widget bodyWidget = Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Flexible(child: negativeButton),
const SizedBox(width: 8),
Flexible(child: positiveButton),
],
);
if (padding != null) {
return Padding(padding: padding!, child: bodyWidget);
} else {
return bodyWidget;
}
}
}
@@ -1,71 +1,87 @@
import 'package:core/presentation/extensions/color_extension.dart';
import 'package:core/presentation/utils/keyboard_utils.dart'; import 'package:core/presentation/utils/keyboard_utils.dart';
import 'package:core/presentation/utils/style_utils.dart'; import 'package:core/presentation/utils/responsive_utils.dart';
import 'package:core/presentation/utils/theme_utils.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:rule_filter/rule_filter/rule_condition_group.dart'; import 'package:rule_filter/rule_filter/rule_condition_group.dart';
import 'package:tmail_ui_user/features/base/widget/drop_down_button_widget.dart'; import 'package:tmail_ui_user/features/base/widget/drop_down_button_widget.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/model/rule_filter_condition_type.dart'; import 'package:tmail_ui_user/features/rules_filter_creator/presentation/model/rule_filter_condition_type.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/styles/rule_filter_title_styles.dart';
import 'package:tmail_ui_user/features/rules_filter_creator/presentation/widgets/rule_filter_button_field.dart'; import 'package:tmail_ui_user/features/rules_filter_creator/presentation/widgets/rule_filter_button_field.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
typedef OnTapActionCallback<RuleConditionCombiner> = Function(ConditionCombiner? value); typedef OnTapActionCallback<RuleConditionCombiner> = Function(
ConditionCombiner? value,
);
class RuleFilterTitle extends StatelessWidget { class RuleFilterTitle extends StatelessWidget {
final ConditionCombiner? conditionCombinerType; final ConditionCombiner? conditionCombinerType;
final OnTapActionCallback? tapActionCallback; final OnTapActionCallback? tapActionCallback;
final RuleFilterConditionScreenType ruleFilterConditionScreenType; final RuleFilterConditionScreenType ruleFilterConditionScreenType;
final ResponsiveUtils responsiveUtils;
const RuleFilterTitle({ const RuleFilterTitle({
Key? key, Key? key,
required this.ruleFilterConditionScreenType, required this.ruleFilterConditionScreenType,
required this.responsiveUtils,
this.conditionCombinerType, this.conditionCombinerType,
this.tapActionCallback, this.tapActionCallback,
}) : super(key: key); }) : super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final appLocalizations = AppLocalizations.of(context);
final isMobile = responsiveUtils.isMobile(context);
return Row( return Wrap(
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: WrapCrossAlignment.center,
mainAxisSize: MainAxisSize.min, runAlignment: WrapAlignment.center,
runSpacing: 8,
children: [ children: [
Text( Text(
AppLocalizations.of(context).conditionTitleRulesFilterBeforeCombiner, appLocalizations.conditionTitleRulesFilterBeforeCombiner,
overflow: CommonTextStyle.defaultTextOverFlow, style: ThemeUtils.textStyleInter400.copyWith(
softWrap: CommonTextStyle.defaultSoftWrap, fontSize: 14,
maxLines: RuleFilterTitleStyles.textMaxLines, height: 18 / 14,
style: RuleFilterTitleStyles.textStyle, color: Colors.black,
),
), ),
Container( Container(
padding: const EdgeInsets.symmetric(horizontal: RuleFilterTitleStyles.combinerTypeSelectionAreaPadding), padding: const EdgeInsets.symmetric(horizontal: 12),
width: RuleFilterTitleStyles.combinerTypeSelectionAreaWith, width: isMobile ? double.infinity : 158,
child: ruleFilterConditionScreenType == RuleFilterConditionScreenType.mobile child: ruleFilterConditionScreenType ==
RuleFilterConditionScreenType.mobile
? RuleFilterButtonField<ConditionCombiner>( ? RuleFilterButtonField<ConditionCombiner>(
value: conditionCombinerType, value: conditionCombinerType,
tapActionCallback: (value) { tapActionCallback: (value) {
KeyboardUtils.hideKeyboard(context); KeyboardUtils.hideKeyboard(context);
tapActionCallback?.call(value); tapActionCallback?.call(value);
}, },
) )
: DropDownButtonWidget<ConditionCombiner>( : DropDownButtonWidget<ConditionCombiner>(
items: ConditionCombiner.values, items: ConditionCombiner.values,
itemSelected: conditionCombinerType, itemSelected: conditionCombinerType,
supportSelectionIcon: true, supportSelectionIcon: true,
onChanged: (value) { labelTextStyle: ThemeUtils.textStyleBodyBody3(
KeyboardUtils.hideKeyboard(context); color: Colors.black,
tapActionCallback?.call(value); ),
}, hintTextStyle: ThemeUtils.textStyleBodyBody3(
), color: AppColor.steelGray400,
),
onChanged: (value) {
KeyboardUtils.hideKeyboard(context);
tapActionCallback?.call(value);
},
),
), ),
Expanded( Text(
child: Text( appLocalizations.conditionTitleRulesFilterAfterCombiner,
AppLocalizations.of(context).conditionTitleRulesFilterAfterCombiner, style: ThemeUtils.textStyleInter400.copyWith(
overflow: CommonTextStyle.defaultTextOverFlow, fontSize: 14,
style: RuleFilterTitleStyles.textStyle, height: 18 / 14,
color: Colors.black,
), ),
) )
] ],
); );
} }
} }
+42
View File
@@ -4723,5 +4723,47 @@
"type": "text", "type": "text",
"placeholders_order": [], "placeholders_order": [],
"placeholders": {} "placeholders": {}
},
"createANewRule": "Create a New Rule",
"@createANewRule": {
"type": "text",
"placeholders_order": [],
"placeholders": {}
},
"ruleName": "Rule Name",
"@ruleName": {
"type": "text",
"placeholders_order": [],
"placeholders": {}
},
"condition": "Condition",
"@condition": {
"type": "text",
"placeholders_order": [],
"placeholders": {}
},
"addACondition": "Add a condition",
"@addACondition": {
"type": "text",
"placeholders_order": [],
"placeholders": {}
},
"actionsToPerform": "Actions to Perform",
"@actionsToPerform": {
"type": "text",
"placeholders_order": [],
"placeholders": {}
},
"addAnAction": "Add an action",
"@addAnAction": {
"type": "text",
"placeholders_order": [],
"placeholders": {}
},
"createRule": "Create Rule",
"@createRule": {
"type": "text",
"placeholders_order": [],
"placeholders": {}
} }
} }
@@ -4985,4 +4985,53 @@ class AppLocalizations {
name: 'createMyFirstRule', name: 'createMyFirstRule',
); );
} }
String get createANewRule {
return Intl.message(
'Create a New Rule',
name: 'createANewRule',
);
}
String get ruleName {
return Intl.message(
'Rule Name',
name: 'ruleName',
);
}
String get condition {
return Intl.message(
'Condition',
name: 'condition',
);
}
String get addACondition {
return Intl.message(
'Add a condition',
name: 'addACondition',
);
}
String get actionsToPerform {
return Intl.message(
'Actions to Perform',
name: 'actionsToPerform',
);
}
String get addAnAction {
return Intl.message(
'Add an action',
name: 'addAnAction',
);
}
String get createRule {
return Intl.message(
'Create Rule',
name: 'createRule',
);
}
} }