TF-3956 Adjust UI for vacation section in setting on web

Signed-off-by: dab246 <tdvu@linagora.com>
This commit is contained in:
dab246
2025-08-08 16:25:05 +07:00
committed by Dat H. Pham
parent d4a56afaa1
commit f68965fd2a
17 changed files with 983 additions and 771 deletions
@@ -0,0 +1,83 @@
import 'package:core/presentation/extensions/color_extension.dart';
import 'package:core/presentation/resources/image_paths.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
class ArrowDownIconBorderButtonWidget extends StatelessWidget {
final ImagePaths imagePaths;
final String? icon;
final IconData? iconData;
final Color? iconColor;
final Color? backgroundColor;
final String? tooltipMessage;
final double? height;
final EdgeInsetsGeometry? padding;
final VoidCallback? onTap;
const ArrowDownIconBorderButtonWidget({
super.key,
required this.imagePaths,
this.icon,
this.iconData,
this.iconColor,
this.backgroundColor,
this.tooltipMessage,
this.height,
this.padding,
this.onTap,
});
@override
Widget build(BuildContext context) {
if (icon == null && iconData == null) return const SizedBox.shrink();
Widget buttonIcon = Container(
padding: const EdgeInsets.all(5),
decoration: BoxDecoration(
color: backgroundColor,
border: Border.all(
color: AppColor.m3Neutral90,
),
borderRadius: const BorderRadius.all(Radius.circular(8)),
),
height: height,
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
if (iconData != null)
Icon(iconData, color: iconColor)
else if (icon != null)
SvgPicture.asset(
icon!,
colorFilter: iconColor.asFilter(),
),
SvgPicture.asset(imagePaths.icStyleArrowDown),
],
),
);
if (onTap != null) {
buttonIcon = Material(
type: MaterialType.transparency,
child: InkWell(
onTap: onTap,
borderRadius: const BorderRadius.all(Radius.circular(8)),
child: buttonIcon,
),
);
}
if (tooltipMessage != null) {
buttonIcon = Tooltip(
message: tooltipMessage,
child: buttonIcon,
);
}
if (padding != null) {
buttonIcon = Padding(padding: padding!, child: buttonIcon);
}
return buttonIcon;
}
}
@@ -1,104 +0,0 @@
import 'package:core/presentation/extensions/color_extension.dart';
import 'package:core/presentation/utils/style_utils.dart';
import 'package:core/presentation/utils/theme_utils.dart';
import 'package:flutter/material.dart';
import 'package:tmail_ui_user/features/manage_account/presentation/extensions/datetime_extension.dart';
typedef OnTapActionCallback<T> = Function(T? value);
class BorderButtonField<T> extends StatelessWidget {
final T? value;
final OnTapActionCallback? tapActionCallback;
final Widget? icon;
final TextStyle? textStyle;
final MouseCursor? mouseCursor;
final String? hintText;
final bool isEmpty;
final Color? backgroundColor;
final String? label;
const BorderButtonField({
super.key,
this.label,
this.value,
this.tapActionCallback,
this.icon,
this.textStyle,
this.mouseCursor,
this.hintText,
this.isEmpty = false,
this.backgroundColor,
});
@override
Widget build(BuildContext context) {
final buttonField = InkWell(
onTap: () => tapActionCallback?.call(value),
mouseCursor: mouseCursor,
child: Container(
height: 44,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
border: Border.all(
color: _getBorderColor(),
width: 0.5),
color: backgroundColor ?? Colors.white),
padding: const EdgeInsets.symmetric(horizontal: 12),
child: Row(children: [
Expanded(child: Text(
_getName(context, value),
style: _getTextStyle(value),
maxLines: 1,
softWrap: CommonTextStyle.defaultSoftWrap,
overflow: CommonTextStyle.defaultTextOverFlow,
)),
if (icon != null) icon!
]),
),
);
if (label != null) {
return Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
Text(label!, style: ThemeUtils.defaultTextStyleInterFont.copyWith(
fontSize: 14,
fontWeight: FontWeight.normal,
color: AppColor.colorContentEmail)),
const SizedBox(height: 8),
buttonField
]);
} else {
return buttonField;
}
}
TextStyle? _getTextStyle(T? value) {
if (hintText != null && value == null) {
return ThemeUtils.defaultTextStyleInterFont.copyWith(
fontSize: 16,
fontWeight: FontWeight.normal,
color: AppColor.colorHintInputCreateMailbox);
}
return textStyle ?? ThemeUtils.defaultTextStyleInterFont.copyWith(
fontSize: 16,
fontWeight: FontWeight.normal,
color: Colors.black);
}
String _getName(BuildContext context, T? value) {
if (value is DateTime) {
return value.formatDate(locale: Localizations.localeOf(context).toLanguageTag());
}
if (value is TimeOfDay) {
return value.formatTime(context);
}
return hintText ?? '';
}
Color _getBorderColor() {
if (!isEmpty) {
return AppColor.colorInputBorderCreateMailbox;
}
return AppColor.colorInputBorderErrorVerifyName;
}
}
@@ -9,6 +9,7 @@ typedef OnTextChange = void Function(String text);
class DefaultInputFieldWidget extends StatelessWidget {
final TextEditingController textEditingController;
final String? hintText;
final FocusNode? focusNode;
final OnTextChange? onTextChange;
final OnTextSubmitted? onTextSubmitted;
@@ -16,6 +17,7 @@ class DefaultInputFieldWidget extends StatelessWidget {
super.key,
required this.textEditingController,
this.hintText,
this.focusNode,
this.onTextChange,
this.onTextSubmitted,
});
@@ -29,6 +31,7 @@ class DefaultInputFieldWidget extends StatelessWidget {
textStyle: ThemeUtils.textStyleBodyBody3(
color: AppColor.m3SurfaceBackground,
),
focusNode: focusNode,
onTextSubmitted: onTextSubmitted,
onTextChange: onTextChange,
decoration: InputDecoration(
@@ -0,0 +1,111 @@
import 'package:core/presentation/extensions/color_extension.dart';
import 'package:core/presentation/utils/theme_utils.dart';
import 'package:flutter/material.dart';
import 'package:tmail_ui_user/features/manage_account/presentation/extensions/datetime_extension.dart';
typedef OnSelectValueAction<T> = Function(T? value);
class LabelBorderButtonField<T> extends StatelessWidget {
final T? value;
final bool isEmpty;
final bool arrangeHorizontally;
final String label;
final String? hintText;
final double? horizontalSpacing;
final double? minWidth;
final OnSelectValueAction onSelectValueAction;
const LabelBorderButtonField({
super.key,
required this.value,
required this.label,
required this.onSelectValueAction,
this.isEmpty = false,
this.arrangeHorizontally = true,
this.hintText,
this.minWidth,
this.horizontalSpacing,
});
@override
Widget build(BuildContext context) {
Widget buttonField = Material(
type: MaterialType.transparency,
child: InkWell(
onTap: () => onSelectValueAction(value),
borderRadius: const BorderRadius.all(Radius.circular(10)),
child: Container(
height: 40,
constraints: BoxConstraints(minWidth: minWidth ?? 106),
decoration: BoxDecoration(
borderRadius: const BorderRadius.all(Radius.circular(10)),
border: Border.all(color: _getBorderColor(), width: 1),
),
padding: const EdgeInsets.symmetric(horizontal: 8),
alignment: Alignment.center,
child: Text(
_getName(context, value),
style: _getTextStyle(value),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
),
);
if (arrangeHorizontally) {
buttonField = Row(
mainAxisSize: MainAxisSize.min,
children: [
ConstrainedBox(
constraints: const BoxConstraints(minWidth: 83),
child: Text(
'$label:',
style: ThemeUtils.textStyleBodyBody3(color: Colors.black),
),
),
SizedBox(width: horizontalSpacing ?? 12),
buttonField,
],
);
} else {
buttonField = Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'$label:',
style: ThemeUtils.textStyleBodyBody3(color: Colors.black),
),
const SizedBox(height: 10),
buttonField,
],
);
}
return buttonField;
}
TextStyle? _getTextStyle(T? value) {
if (hintText != null && value == null) {
return ThemeUtils.textStyleBodyBody3(color: AppColor.steelGray400);
} else {
return ThemeUtils.textStyleBodyBody3(color: Colors.black);
}
}
String _getName(BuildContext context, T? value) {
if (value is DateTime) {
return value.formatDate(
pattern: 'dd/MM/yyyy',
locale: Localizations.localeOf(context).toLanguageTag(),
);
}
if (value is TimeOfDay) {
return value.formatTime(context);
}
return hintText ?? '';
}
Color _getBorderColor() =>
isEmpty ? AppColor.redFF3347 : AppColor.m3Neutral90;
}
@@ -0,0 +1,68 @@
import 'package:core/presentation/utils/theme_utils.dart';
import 'package:flutter/material.dart';
import 'package:tmail_ui_user/features/base/widget/default_field/default_input_field_widget.dart';
class LabelInputFieldBuilder extends StatelessWidget {
final String label;
final TextEditingController textEditingController;
final String? errorText;
final String? hintText;
final FocusNode? focusNode;
final bool arrangeHorizontally;
final OnTextChange? onTextChange;
const LabelInputFieldBuilder({
super.key,
required this.label,
required this.textEditingController,
this.arrangeHorizontally = true,
this.hintText,
this.errorText,
this.focusNode,
this.onTextChange,
});
@override
Widget build(BuildContext context) {
Widget bodyWidget = ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 565),
child: DefaultInputFieldWidget(
textEditingController: textEditingController,
hintText: hintText,
focusNode: focusNode,
onTextChange: onTextChange,
),
);
if (arrangeHorizontally) {
bodyWidget = Row(
mainAxisSize: MainAxisSize.min,
children: [
ConstrainedBox(
constraints: const BoxConstraints(minWidth: 83),
child: Text(
'$label:',
style: ThemeUtils.textStyleBodyBody3(color: Colors.black),
),
),
const SizedBox(width: 12),
Flexible(child: bodyWidget),
],
);
} else {
bodyWidget = Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'$label:',
style: ThemeUtils.textStyleBodyBody3(color: Colors.black),
),
const SizedBox(height: 10),
bodyWidget,
],
);
}
return bodyWidget;
}
}
@@ -0,0 +1,51 @@
import 'package:core/presentation/resources/image_paths.dart';
import 'package:core/presentation/utils/theme_utils.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
class SwitchLabelButtonWidget extends StatelessWidget {
final ImagePaths imagePaths;
final bool isActive;
final String label;
final VoidCallback onSwitchAction;
final EdgeInsetsGeometry? padding;
const SwitchLabelButtonWidget({
super.key,
required this.imagePaths,
required this.isActive,
required this.label,
required this.onSwitchAction,
this.padding,
});
@override
Widget build(BuildContext context) {
final bodyWidget = Row(
children: [
InkWell(
onTap: onSwitchAction,
child: SvgPicture.asset(
isActive ? imagePaths.icSwitchOn : imagePaths.icSwitchOff,
fit: BoxFit.fill,
width: 52,
height: 32,
),
),
const SizedBox(width: 16),
Expanded(
child: Text(
label,
style: ThemeUtils.textStyleBodyBody2(color: Colors.black),
),
)
],
);
if (padding != null) {
return Padding(padding: padding!, child: bodyWidget);
} else {
return bodyWidget;
}
}
}
@@ -1,69 +0,0 @@
import 'package:core/core.dart';
import 'package:core/utils/direction_utils.dart';
import 'package:flutter/material.dart';
import 'package:tmail_ui_user/features/base/widget/text_input_decoration_builder.dart';
typedef OnChangeInputAction = Function(String value);
class TextInputFieldBuilder extends StatelessWidget {
final String? label;
final String? error;
final String? hint;
final TextEditingController? editingController;
final FocusNode? focusNode;
final TextInputType? inputType;
final bool isMandatory;
final int? minLines;
final int? maxLines;
final Color? backgroundColor;
final OnChangeInputAction? onChangeInputAction;
const TextInputFieldBuilder({
Key? key,
this.label,
this.hint,
this.error,
this.isMandatory = false,
this.editingController,
this.focusNode,
this.inputType,
this.minLines,
this.maxLines,
this.backgroundColor,
this.onChangeInputAction,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
if (label != null)
...[
Text(isMandatory ? '${label!}*' : label!,
style: ThemeUtils.defaultTextStyleInterFont.copyWith(
fontSize: 14,
fontWeight: FontWeight.normal,
color: AppColor.colorContentEmail)),
const SizedBox(height: 8)
],
TextFieldBuilder(
onTextChange: onChangeInputAction,
textInputAction: TextInputAction.next,
controller: editingController,
focusNode: focusNode,
textStyle: ThemeUtils.defaultTextStyleInterFont.copyWith(color: Colors.black, fontSize: 16),
keyboardType: inputType ?? TextInputType.text,
textDirection: DirectionUtils.getDirectionByLanguage(context),
minLines: minLines,
maxLines: maxLines,
decoration: (TextInputDecorationBuilder()
..setContentPadding(EdgeInsets.symmetric(vertical: PlatformInfo.isWeb ? 16 : 12, horizontal: 12))
..setHintText(hint)
..setFillColor(backgroundColor)
..setErrorText(error))
.build(),
)
]);
}
}