TF-3956 Support click outside to close the date time dialog picker on web
Signed-off-by: dab246 <tdvu@linagora.com>
This commit is contained in:
@@ -0,0 +1,113 @@
|
|||||||
|
import 'package:core/presentation/extensions/color_extension.dart';
|
||||||
|
import 'package:core/utils/platform_info.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
|
||||||
|
class DateTimeDialogPicker {
|
||||||
|
static final _instance = DateTimeDialogPicker._();
|
||||||
|
|
||||||
|
factory DateTimeDialogPicker() => _instance;
|
||||||
|
|
||||||
|
DateTimeDialogPicker._();
|
||||||
|
|
||||||
|
final _isOpened = RxBool(false);
|
||||||
|
|
||||||
|
RxBool get isOpened => _isOpened;
|
||||||
|
|
||||||
|
Future<TimeOfDay?> showTwakeTimePicker({
|
||||||
|
required BuildContext context,
|
||||||
|
required TimeOfDay initialTime,
|
||||||
|
bool use24HourFormat = false,
|
||||||
|
String? cancelText,
|
||||||
|
String? confirmText,
|
||||||
|
}) {
|
||||||
|
_isOpened.value = true;
|
||||||
|
|
||||||
|
return showTimePicker(
|
||||||
|
context: context,
|
||||||
|
initialTime: initialTime,
|
||||||
|
builder: (context, child) {
|
||||||
|
return Theme(
|
||||||
|
data: Theme.of(context).copyWith(
|
||||||
|
colorScheme: const ColorScheme.light(
|
||||||
|
primary: AppColor.primaryColor,
|
||||||
|
onPrimary: Colors.white,
|
||||||
|
onSurface: Colors.black,
|
||||||
|
),
|
||||||
|
textButtonTheme: TextButtonThemeData(
|
||||||
|
style: TextButton.styleFrom(
|
||||||
|
foregroundColor: AppColor.primaryColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: MediaQuery(
|
||||||
|
data: MediaQueryData(alwaysUse24HourFormat: use24HourFormat),
|
||||||
|
child: PlatformInfo.isWeb
|
||||||
|
? KeyboardListener(
|
||||||
|
focusNode: FocusNode(),
|
||||||
|
autofocus: true,
|
||||||
|
onKeyEvent: (event) {
|
||||||
|
if (event.logicalKey == LogicalKeyboardKey.escape) {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: child!,
|
||||||
|
)
|
||||||
|
: child!,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
cancelText: cancelText,
|
||||||
|
confirmText: confirmText,
|
||||||
|
).whenComplete(() => _isOpened.value = false);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<DateTime?> showTwakeDatePicker({
|
||||||
|
required BuildContext context,
|
||||||
|
required DateTime initialDate,
|
||||||
|
String? cancelText,
|
||||||
|
String? confirmText,
|
||||||
|
}) {
|
||||||
|
_isOpened.value = true;
|
||||||
|
|
||||||
|
return showDatePicker(
|
||||||
|
context: context,
|
||||||
|
initialDate: initialDate,
|
||||||
|
initialDatePickerMode: DatePickerMode.day,
|
||||||
|
firstDate: DateTime(1900),
|
||||||
|
lastDate: DateTime(2100),
|
||||||
|
locale: Localizations.localeOf(context),
|
||||||
|
builder: (context, child) {
|
||||||
|
return Theme(
|
||||||
|
data: Theme.of(context).copyWith(
|
||||||
|
colorScheme: const ColorScheme.light(
|
||||||
|
primary: AppColor.primaryColor,
|
||||||
|
onPrimary: Colors.white,
|
||||||
|
onSurface: Colors.black,
|
||||||
|
),
|
||||||
|
textButtonTheme: TextButtonThemeData(
|
||||||
|
style: TextButton.styleFrom(
|
||||||
|
foregroundColor: AppColor.primaryColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: PlatformInfo.isWeb
|
||||||
|
? KeyboardListener(
|
||||||
|
focusNode: FocusNode(),
|
||||||
|
autofocus: true,
|
||||||
|
onKeyEvent: (event) {
|
||||||
|
if (event.logicalKey == LogicalKeyboardKey.escape) {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: child!,
|
||||||
|
)
|
||||||
|
: child!,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
cancelText: cancelText,
|
||||||
|
confirmText: confirmText,
|
||||||
|
).whenComplete(() => _isOpened.value = false);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,6 +15,7 @@ import 'package:rule_filter/rule_filter/capability_rule_filter.dart';
|
|||||||
import 'package:server_settings/server_settings/capability_server_settings.dart';
|
import 'package:server_settings/server_settings/capability_server_settings.dart';
|
||||||
import 'package:tmail_ui_user/features/base/action/ui_action.dart';
|
import 'package:tmail_ui_user/features/base/action/ui_action.dart';
|
||||||
import 'package:tmail_ui_user/features/base/reloadable/reloadable_controller.dart';
|
import 'package:tmail_ui_user/features/base/reloadable/reloadable_controller.dart';
|
||||||
|
import 'package:tmail_ui_user/features/base/widget/dialog_picker/date_time_dialog_picker.dart';
|
||||||
import 'package:tmail_ui_user/features/home/domain/extensions/session_extensions.dart';
|
import 'package:tmail_ui_user/features/home/domain/extensions/session_extensions.dart';
|
||||||
import 'package:tmail_ui_user/features/manage_account/domain/state/export_trace_log_state.dart';
|
import 'package:tmail_ui_user/features/manage_account/domain/state/export_trace_log_state.dart';
|
||||||
import 'package:tmail_ui_user/features/manage_account/domain/state/get_all_vacation_state.dart';
|
import 'package:tmail_ui_user/features/manage_account/domain/state/get_all_vacation_state.dart';
|
||||||
@@ -57,7 +58,6 @@ class ManageAccountDashBoardController extends ReloadableController {
|
|||||||
final ownEmailAddress = Rx<String>('');
|
final ownEmailAddress = Rx<String>('');
|
||||||
|
|
||||||
Session? sessionCurrent;
|
Session? sessionCurrent;
|
||||||
bool? isVacationDateDialogDisplayed;
|
|
||||||
Uri? previousUri;
|
Uri? previousUri;
|
||||||
int minInputLengthAutocomplete = AppConfig.defaultMinInputLengthAutocomplete;
|
int minInputLengthAutocomplete = AppConfig.defaultMinInputLengthAutocomplete;
|
||||||
|
|
||||||
@@ -383,7 +383,7 @@ class ManageAccountDashBoardController extends ReloadableController {
|
|||||||
|
|
||||||
bool _onBackButtonInterceptor(bool stopDefaultButtonEvent, RouteInfo routeInfo) {
|
bool _onBackButtonInterceptor(bool stopDefaultButtonEvent, RouteInfo routeInfo) {
|
||||||
log('ManageAccountDashBoardController::_onBackButtonInterceptor:currentRoute: ${Get.currentRoute} | _isDialogViewOpen: $_isDialogViewOpen');
|
log('ManageAccountDashBoardController::_onBackButtonInterceptor:currentRoute: ${Get.currentRoute} | _isDialogViewOpen: $_isDialogViewOpen');
|
||||||
if (_isDialogViewOpen || isVacationDateDialogDisplayed == true) {
|
if (_isDialogViewOpen || DateTimeDialogPicker().isOpened.isTrue) {
|
||||||
popBack();
|
popBack();
|
||||||
_replaceBrowserHistory();
|
_replaceBrowserHistory();
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -3,10 +3,10 @@ import 'package:dartz/dartz.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:jmap_dart_client/jmap/mail/vacation/vacation_response.dart';
|
import 'package:jmap_dart_client/jmap/mail/vacation/vacation_response.dart';
|
||||||
import 'package:pointer_interceptor/pointer_interceptor.dart';
|
|
||||||
import 'package:rich_text_composer/rich_text_composer.dart';
|
import 'package:rich_text_composer/rich_text_composer.dart';
|
||||||
import 'package:rich_text_composer/views/commons/constants.dart';
|
import 'package:rich_text_composer/views/commons/constants.dart';
|
||||||
import 'package:tmail_ui_user/features/base/base_controller.dart';
|
import 'package:tmail_ui_user/features/base/base_controller.dart';
|
||||||
|
import 'package:tmail_ui_user/features/base/widget/dialog_picker/date_time_dialog_picker.dart';
|
||||||
import 'package:tmail_ui_user/features/composer/presentation/controller/rich_text_web_controller.dart';
|
import 'package:tmail_ui_user/features/composer/presentation/controller/rich_text_web_controller.dart';
|
||||||
import 'package:tmail_ui_user/features/manage_account/domain/state/update_vacation_state.dart';
|
import 'package:tmail_ui_user/features/manage_account/domain/state/update_vacation_state.dart';
|
||||||
import 'package:tmail_ui_user/features/manage_account/domain/usecases/update_vacation_interactor.dart';
|
import 'package:tmail_ui_user/features/manage_account/domain/usecases/update_vacation_interactor.dart';
|
||||||
@@ -138,34 +138,10 @@ class VacationController extends BaseController {
|
|||||||
}
|
}
|
||||||
FocusScope.of(context).unfocus();
|
FocusScope.of(context).unfocus();
|
||||||
|
|
||||||
_accountDashBoardController.isVacationDateDialogDisplayed = true;
|
final datePicked = await DateTimeDialogPicker().showTwakeDatePicker(
|
||||||
final datePicked = await showDatePicker(
|
context: context,
|
||||||
context: context,
|
initialDate: currentDate ?? DateTime.now(),
|
||||||
initialDate: currentDate ?? DateTime.now(),
|
);
|
||||||
initialDatePickerMode: DatePickerMode.day,
|
|
||||||
firstDate: DateTime(1900),
|
|
||||||
lastDate: DateTime(2100),
|
|
||||||
locale: Localizations.localeOf(context),
|
|
||||||
builder: (context, child) {
|
|
||||||
return PointerInterceptor(
|
|
||||||
child: Theme(
|
|
||||||
data: Theme.of(context).copyWith(
|
|
||||||
colorScheme: const ColorScheme.light(
|
|
||||||
primary: AppColor.primaryColor,
|
|
||||||
onPrimary: Colors.white,
|
|
||||||
onSurface: Colors.black
|
|
||||||
),
|
|
||||||
textButtonTheme: TextButtonThemeData(
|
|
||||||
style: TextButton.styleFrom(
|
|
||||||
foregroundColor: AppColor.primaryColor
|
|
||||||
)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
child: child!
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
).whenComplete(() => _accountDashBoardController.isVacationDateDialogDisplayed = false);
|
|
||||||
|
|
||||||
if (datePicked == null) {
|
if (datePicked == null) {
|
||||||
return;
|
return;
|
||||||
@@ -184,27 +160,10 @@ class VacationController extends BaseController {
|
|||||||
}
|
}
|
||||||
FocusScope.of(context).unfocus();
|
FocusScope.of(context).unfocus();
|
||||||
|
|
||||||
_accountDashBoardController.isVacationDateDialogDisplayed = true;
|
final timePicked = await DateTimeDialogPicker().showTwakeTimePicker(
|
||||||
final timePicked = await showTimePicker(
|
|
||||||
context: context,
|
context: context,
|
||||||
initialTime: currentTime ?? TimeOfDay.now(),
|
initialTime: currentTime ?? TimeOfDay.now(),
|
||||||
builder: (context, child) {
|
);
|
||||||
return PointerInterceptor(
|
|
||||||
child: Theme(
|
|
||||||
data: Theme.of(context).copyWith(
|
|
||||||
colorScheme: const ColorScheme.light(
|
|
||||||
primary: AppColor.primaryColor,
|
|
||||||
onPrimary: Colors.white,
|
|
||||||
onSurface: Colors.black),
|
|
||||||
textButtonTheme: TextButtonThemeData(
|
|
||||||
style: TextButton.styleFrom(foregroundColor: AppColor.primaryColor))),
|
|
||||||
child: MediaQuery(
|
|
||||||
data: const MediaQueryData(alwaysUse24HourFormat: false),
|
|
||||||
child: child!),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
).whenComplete(() => _accountDashBoardController.isVacationDateDialogDisplayed = false);
|
|
||||||
|
|
||||||
if (timePicked == null) {
|
if (timePicked == null) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -12,8 +12,10 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter_portal/flutter_portal.dart';
|
import 'package:flutter_portal/flutter_portal.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:html_editor_enhanced/html_editor.dart' as html_editor_browser;
|
import 'package:html_editor_enhanced/html_editor.dart' as html_editor_browser;
|
||||||
|
import 'package:pointer_interceptor/pointer_interceptor.dart';
|
||||||
import 'package:rich_text_composer/rich_text_composer.dart';
|
import 'package:rich_text_composer/rich_text_composer.dart';
|
||||||
import 'package:rich_text_composer/views/widgets/rich_text_keyboard_toolbar.dart';
|
import 'package:rich_text_composer/views/widgets/rich_text_keyboard_toolbar.dart';
|
||||||
|
import 'package:tmail_ui_user/features/base/widget/dialog_picker/date_time_dialog_picker.dart';
|
||||||
import 'package:tmail_ui_user/features/base/widget/label_border_button_field.dart';
|
import 'package:tmail_ui_user/features/base/widget/label_border_button_field.dart';
|
||||||
import 'package:tmail_ui_user/features/base/widget/switch_label_button_widget.dart';
|
import 'package:tmail_ui_user/features/base/widget/switch_label_button_widget.dart';
|
||||||
import 'package:tmail_ui_user/features/base/widget/label_input_field_builder.dart';
|
import 'package:tmail_ui_user/features/base/widget/label_input_field_builder.dart';
|
||||||
@@ -399,7 +401,24 @@ class VacationView extends GetWidget<VacationController> {
|
|||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
Flexible(child: _buildMessageHtmlTextEditor(context)),
|
Flexible(
|
||||||
|
child: Stack(
|
||||||
|
children: [
|
||||||
|
_buildMessageHtmlTextEditor(context),
|
||||||
|
Obx(() {
|
||||||
|
if (DateTimeDialogPicker().isOpened.isTrue) {
|
||||||
|
return Positioned.fill(
|
||||||
|
child: PointerInterceptor(
|
||||||
|
child: const SizedBox.expand(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return const SizedBox.shrink();
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
ToolbarRichTextWidget(
|
ToolbarRichTextWidget(
|
||||||
richTextController: controller.richTextControllerForWeb!,
|
richTextController: controller.richTextControllerForWeb!,
|
||||||
scrollListController: controller.richTextButtonScrollController,
|
scrollListController: controller.richTextButtonScrollController,
|
||||||
@@ -444,7 +463,24 @@ class VacationView extends GetWidget<VacationController> {
|
|||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
Flexible(child: _buildMessageHtmlTextEditor(context)),
|
Flexible(
|
||||||
|
child: Stack(
|
||||||
|
children: [
|
||||||
|
_buildMessageHtmlTextEditor(context),
|
||||||
|
Obx(() {
|
||||||
|
if (DateTimeDialogPicker().isOpened.isTrue) {
|
||||||
|
return Positioned.fill(
|
||||||
|
child: PointerInterceptor(
|
||||||
|
child: const SizedBox.expand(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return const SizedBox.shrink();
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
ToolbarRichTextWidget(
|
ToolbarRichTextWidget(
|
||||||
richTextController: controller.richTextControllerForWeb!,
|
richTextController: controller.richTextControllerForWeb!,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"@@last_modified": "2025-08-08T16:24:37.279860",
|
"@@last_modified": "2025-08-18T13:21:17.206262",
|
||||||
"initializing_data": "Initializing data...",
|
"initializing_data": "Initializing data...",
|
||||||
"@initializing_data": {
|
"@initializing_data": {
|
||||||
"type": "text",
|
"type": "text",
|
||||||
@@ -4647,5 +4647,11 @@
|
|||||||
"type": "text",
|
"type": "text",
|
||||||
"placeholders_order": [],
|
"placeholders_order": [],
|
||||||
"placeholders": {}
|
"placeholders": {}
|
||||||
|
},
|
||||||
|
"ok": "OK",
|
||||||
|
"@ok": {
|
||||||
|
"type": "text",
|
||||||
|
"placeholders_order": [],
|
||||||
|
"placeholders": {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4898,4 +4898,11 @@ class AppLocalizations {
|
|||||||
name: 'creatingAnArchiveForDownloading',
|
name: 'creatingAnArchiveForDownloading',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String get ok {
|
||||||
|
return Intl.message(
|
||||||
|
'OK',
|
||||||
|
name: 'ok',
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user