Переглянути джерело

chore: unfocus the textField after switching to another popover

nathan 2 роки тому
батько
коміт
c493ba79e0

+ 108 - 30
frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/field_editor.dart

@@ -7,12 +7,12 @@ import 'package:flowy_infra/theme.dart';
 import 'package:flowy_infra_ui/flowy_infra_ui.dart';
 import 'package:flowy_infra_ui/flowy_infra_ui.dart';
 import 'package:flowy_infra_ui/style_widget/button.dart';
 import 'package:flowy_infra_ui/style_widget/button.dart';
 import 'package:flowy_infra_ui/style_widget/text.dart';
 import 'package:flowy_infra_ui/style_widget/text.dart';
+import 'package:flowy_infra_ui/widget/rounded_input_field.dart';
 import 'package:flowy_infra_ui/widget/spacing.dart';
 import 'package:flowy_infra_ui/widget/spacing.dart';
 import 'package:flowy_sdk/log.dart';
 import 'package:flowy_sdk/log.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:app_flowy/generated/locale_keys.g.dart';
 import 'package:app_flowy/generated/locale_keys.g.dart';
-import 'field_name_input.dart';
 import 'field_type_option_editor.dart';
 import 'field_type_option_editor.dart';
 
 
 class FieldEditor extends StatefulWidget {
 class FieldEditor extends StatefulWidget {
@@ -44,6 +44,12 @@ class _FieldEditorState extends State<FieldEditor> {
     super.initState();
     super.initState();
   }
   }
 
 
+  @override
+  void dispose() {
+    popoverMutex.dispose();
+    super.dispose();
+  }
+
   @override
   @override
   Widget build(BuildContext context) {
   Widget build(BuildContext context) {
     return BlocProvider(
     return BlocProvider(
@@ -58,21 +64,13 @@ class _FieldEditorState extends State<FieldEditor> {
           return ListView(
           return ListView(
             shrinkWrap: true,
             shrinkWrap: true,
             children: [
             children: [
-              FlowyText.medium(LocaleKeys.grid_field_editProperty.tr(),
-                  fontSize: 12),
-              const VSpace(10),
-              const _FieldNameCell(),
-              const VSpace(10),
-              _DeleteFieldButton(
-                popoverMutex: popoverMutex,
-                onDeleted: () {
-                  state.field.fold(
-                    () => Log.error('Can not delete the field'),
-                    (field) => widget.onDeleted?.call(field.id),
-                  );
-                },
+              FlowyText.medium(
+                LocaleKeys.grid_field_editProperty.tr(),
+                fontSize: 12,
               ),
               ),
               const VSpace(10),
               const VSpace(10),
+              _FieldNameTextField(popoverMutex: popoverMutex),
+              ..._addDeleteFieldButton(state),
               _FieldTypeOptionCell(popoverMutex: popoverMutex),
               _FieldTypeOptionCell(popoverMutex: popoverMutex),
             ],
             ],
           );
           );
@@ -80,6 +78,24 @@ class _FieldEditorState extends State<FieldEditor> {
       ),
       ),
     );
     );
   }
   }
+
+  List<Widget> _addDeleteFieldButton(FieldEditorState state) {
+    if (widget.onDeleted == null) {
+      return [];
+    }
+    return [
+      const VSpace(10),
+      _DeleteFieldButton(
+        popoverMutex: popoverMutex,
+        onDeleted: () {
+          state.field.fold(
+            () => Log.error('Can not delete the field'),
+            (field) => widget.onDeleted?.call(field.id),
+          );
+        },
+      ),
+    ];
+  }
 }
 }
 
 
 class _FieldTypeOptionCell extends StatelessWidget {
 class _FieldTypeOptionCell extends StatelessWidget {
@@ -111,25 +127,89 @@ class _FieldTypeOptionCell extends StatelessWidget {
   }
   }
 }
 }
 
 
-class _FieldNameCell extends StatelessWidget {
-  const _FieldNameCell({Key? key}) : super(key: key);
+class _FieldNameTextField extends StatefulWidget {
+  final PopoverMutex popoverMutex;
+  const _FieldNameTextField({
+    required this.popoverMutex,
+    Key? key,
+  }) : super(key: key);
+
+  @override
+  State<_FieldNameTextField> createState() => _FieldNameTextFieldState();
+}
+
+class _FieldNameTextFieldState extends State<_FieldNameTextField> {
+  late String name;
+  FocusNode focusNode = FocusNode();
+  VoidCallback? _popoverCallback;
+  TextEditingController controller = TextEditingController();
+
+  @override
+  void initState() {
+    focusNode.addListener(() {
+      if (focusNode.hasFocus) {
+        widget.popoverMutex.close();
+      }
+    });
+
+    super.initState();
+  }
 
 
   @override
   @override
   Widget build(BuildContext context) {
   Widget build(BuildContext context) {
-    return BlocBuilder<FieldEditorBloc, FieldEditorState>(
-      builder: (context, state) {
-        return FieldNameTextField(
-          name: state.name,
-          errorText: context.read<FieldEditorBloc>().state.errorText,
-          onNameChanged: (newName) {
-            context
-                .read<FieldEditorBloc>()
-                .add(FieldEditorEvent.updateName(newName));
-          },
-        );
+    final theme = context.watch<AppTheme>();
+
+    controller.text = context.read<FieldEditorBloc>().state.name;
+    return BlocListener<FieldEditorBloc, FieldEditorState>(
+      listenWhen: (previous, current) => previous.name != current.name,
+      listener: (context, state) {
+        controller.text = state.name;
       },
       },
+      child: BlocBuilder<FieldEditorBloc, FieldEditorState>(
+        builder: (context, state) {
+          listenOnPopoverChhanged(context);
+
+          return RoundedInputField(
+            height: 36,
+            autoFocus: true,
+            focusNode: focusNode,
+            style: const TextStyle(fontSize: 13, fontWeight: FontWeight.w500),
+            controller: controller,
+            normalBorderColor: theme.shader4,
+            errorBorderColor: theme.red,
+            focusBorderColor: theme.main1,
+            cursorColor: theme.main1,
+            errorText: context.read<FieldEditorBloc>().state.errorText,
+            onChanged: (newName) {
+              context
+                  .read<FieldEditorBloc>()
+                  .add(FieldEditorEvent.updateName(newName));
+            },
+          );
+        },
+      ),
     );
     );
   }
   }
+
+  void listenOnPopoverChhanged(BuildContext context) {
+    if (_popoverCallback != null) {
+      widget.popoverMutex.removePopoverStateListener(_popoverCallback!);
+    }
+    _popoverCallback = widget.popoverMutex.listenOnPopoverStateChanged(() {
+      if (focusNode.hasFocus) {
+        final node = FocusScope.of(context);
+        node.unfocus();
+      }
+    });
+  }
+
+  @override
+  void didUpdateWidget(covariant _FieldNameTextField oldWidget) {
+    controller.selection = TextSelection.fromPosition(
+        TextPosition(offset: controller.text.length));
+
+    super.didUpdateWidget(oldWidget);
+  }
 }
 }
 
 
 class _DeleteFieldButton extends StatelessWidget {
 class _DeleteFieldButton extends StatelessWidget {
@@ -171,12 +251,10 @@ class _DeleteFieldButton extends StatelessWidget {
       popupBuilder: (popupContext) {
       popupBuilder: (popupContext) {
         return PopoverAlertView(
         return PopoverAlertView(
           title: LocaleKeys.grid_field_deleteFieldPromptMessage.tr(),
           title: LocaleKeys.grid_field_deleteFieldPromptMessage.tr(),
-          cancel: () => popoverMutex.state?.close(),
+          cancel: () {},
           confirm: () {
           confirm: () {
             onDeleted?.call();
             onDeleted?.call();
-            popoverMutex.state?.close();
           },
           },
-          popoverMutex: popoverMutex,
         );
         );
       },
       },
       child: widget,
       child: widget,

+ 0 - 56
frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/field_name_input.dart

@@ -1,56 +0,0 @@
-import 'package:flowy_infra/theme.dart';
-import 'package:flowy_infra_ui/widget/rounded_input_field.dart';
-import 'package:flutter/material.dart';
-import 'package:flutter_bloc/flutter_bloc.dart';
-
-class FieldNameTextField extends StatefulWidget {
-  final void Function(String) onNameChanged;
-  final String name;
-  final String errorText;
-  const FieldNameTextField({
-    required this.name,
-    required this.errorText,
-    required this.onNameChanged,
-    Key? key,
-  }) : super(key: key);
-
-  @override
-  State<FieldNameTextField> createState() => _FieldNameTextFieldState();
-}
-
-class _FieldNameTextFieldState extends State<FieldNameTextField> {
-  late String name;
-  TextEditingController controller = TextEditingController();
-
-  @override
-  void initState() {
-    controller.text = widget.name;
-    super.initState();
-  }
-
-  @override
-  Widget build(BuildContext context) {
-    final theme = context.watch<AppTheme>();
-    return RoundedInputField(
-      height: 36,
-      autoFocus: true,
-      style: const TextStyle(fontSize: 13, fontWeight: FontWeight.w500),
-      controller: controller,
-      normalBorderColor: theme.shader4,
-      errorBorderColor: theme.red,
-      focusBorderColor: theme.main1,
-      cursorColor: theme.main1,
-      errorText: widget.errorText,
-      onChanged: widget.onNameChanged,
-    );
-  }
-
-  @override
-  void didUpdateWidget(covariant FieldNameTextField oldWidget) {
-    controller.text = widget.name;
-    controller.selection = TextSelection.fromPosition(
-        TextPosition(offset: controller.text.length));
-
-    super.didUpdateWidget(oldWidget);
-  }
-}

+ 2 - 1
frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/field_type_option_editor.dart

@@ -66,7 +66,8 @@ class FieldTypeOptionEditor extends StatelessWidget {
       height: GridSize.typeOptionItemHeight,
       height: GridSize.typeOptionItemHeight,
       child: AppFlowyStylePopover(
       child: AppFlowyStylePopover(
         constraints: BoxConstraints.loose(const Size(460, 440)),
         constraints: BoxConstraints.loose(const Size(460, 440)),
-        triggerActions: PopoverTriggerActionFlags.click,
+        triggerActions:
+            PopoverTriggerActionFlags.click | PopoverTriggerActionFlags.hover,
         mutex: popoverMutex,
         mutex: popoverMutex,
         offset: const Offset(20, 0),
         offset: const Offset(20, 0),
         popupBuilder: (context) {
         popupBuilder: (context) {

+ 3 - 1
frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/type_option/builder.dart

@@ -50,7 +50,9 @@ Widget? makeTypeOptionWidget({
   required PopoverMutex popoverMutex,
   required PopoverMutex popoverMutex,
 }) {
 }) {
   final builder = makeTypeOptionWidgetBuilder(
   final builder = makeTypeOptionWidgetBuilder(
-      dataController: dataController, popoverMutex: popoverMutex);
+    dataController: dataController,
+    popoverMutex: popoverMutex,
+  );
   return builder.build(context);
   return builder.build(context);
 }
 }
 
 

+ 0 - 2
frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/type_option/number.dart

@@ -123,8 +123,6 @@ class NumberFormatList extends StatelessWidget {
                       format: format,
                       format: format,
                       onSelected: (format) {
                       onSelected: (format) {
                         onSelected(format);
                         onSelected(format);
-                        FlowyOverlay.of(context)
-                            .remove(NumberFormatList.identifier());
                       });
                       });
                 }).toList();
                 }).toList();
 
 

+ 0 - 3
frontend/app_flowy/lib/workspace/presentation/widgets/dialogs.dart

@@ -1,4 +1,3 @@
-import 'package:appflowy_popover/popover.dart';
 import 'package:easy_localization/easy_localization.dart';
 import 'package:easy_localization/easy_localization.dart';
 import 'package:flowy_infra/text_style.dart';
 import 'package:flowy_infra/text_style.dart';
 import 'package:flowy_infra/theme.dart';
 import 'package:flowy_infra/theme.dart';
@@ -88,13 +87,11 @@ class _CreateTextFieldDialog extends State<NavigatorTextFieldDialog> {
 }
 }
 
 
 class PopoverAlertView extends StatelessWidget {
 class PopoverAlertView extends StatelessWidget {
-  final PopoverMutex popoverMutex;
   final String title;
   final String title;
   final void Function()? cancel;
   final void Function()? cancel;
   final void Function()? confirm;
   final void Function()? confirm;
 
 
   const PopoverAlertView({
   const PopoverAlertView({
-    required this.popoverMutex,
     required this.title,
     required this.title,
     this.confirm,
     this.confirm,
     this.cancel,
     this.cancel,

+ 38 - 7
frontend/app_flowy/packages/appflowy_popover/lib/popover.dart

@@ -6,7 +6,42 @@ import 'package:flutter/services.dart';
 /// If multiple popovers are exclusive,
 /// If multiple popovers are exclusive,
 /// pass the same mutex to them.
 /// pass the same mutex to them.
 class PopoverMutex {
 class PopoverMutex {
-  PopoverState? state;
+  final ValueNotifier<PopoverState?> _stateNofitier = ValueNotifier(null);
+  PopoverMutex();
+
+  void removePopoverStateListener(VoidCallback listener) {
+    _stateNofitier.removeListener(listener);
+  }
+
+  VoidCallback listenOnPopoverStateChanged(VoidCallback callback) {
+    listenerCallback() {
+      callback();
+    }
+
+    _stateNofitier.addListener(listenerCallback);
+    return listenerCallback;
+  }
+
+  void close() {
+    _stateNofitier.value?.close();
+  }
+
+  PopoverState? get state => _stateNofitier.value;
+
+  set state(PopoverState? newState) {
+    if (_stateNofitier.value != null && _stateNofitier.value != newState) {
+      _stateNofitier.value?.close();
+    }
+    _stateNofitier.value = newState;
+  }
+
+  void _removeState() {
+    _stateNofitier.value = null;
+  }
+
+  void dispose() {
+    _stateNofitier.dispose();
+  }
 }
 }
 
 
 class PopoverController {
 class PopoverController {
@@ -109,11 +144,7 @@ class PopoverState extends State<Popover> {
     close();
     close();
 
 
     if (widget.mutex != null) {
     if (widget.mutex != null) {
-      if (widget.mutex!.state != null && widget.mutex!.state != this) {
-        widget.mutex!.state!.close();
-      }
-
-      widget.mutex!.state = this;
+      widget.mutex?.state = this;
     }
     }
 
 
     if (_popoverWithMask == null) {
     if (_popoverWithMask == null) {
@@ -163,7 +194,7 @@ class PopoverState extends State<Popover> {
     }
     }
 
 
     if (widget.mutex?.state == this) {
     if (widget.mutex?.state == this) {
-      widget.mutex!.state = null;
+      widget.mutex?._removeState();
     }
     }
   }
   }