Browse Source

Merge pull request #1097 from AppFlowy-IO/refactor/appflowy_overlay

Refactor/appflowy overlay
Nathan.fooo 2 years ago
parent
commit
56e68d1346
35 changed files with 483 additions and 418 deletions
  1. 1 1
      frontend/app_flowy/lib/plugins/board/presentation/card/board_select_option_cell.dart
  2. 1 1
      frontend/app_flowy/lib/plugins/board/presentation/card/card.dart
  3. 1 1
      frontend/app_flowy/lib/plugins/board/presentation/toolbar/board_setting.dart
  4. 2 2
      frontend/app_flowy/lib/plugins/board/presentation/toolbar/board_toolbar.dart
  5. 1 1
      frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/date_cell/date_cell.dart
  6. 16 23
      frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/date_cell/date_editor.dart
  7. 1 1
      frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/select_option_cell/select_option_cell.dart
  8. 2 1
      frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/select_option_cell/select_option_editor.dart
  9. 2 2
      frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/url_cell/url_cell.dart
  10. 2 2
      frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/field_cell.dart
  11. 1 1
      frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/field_cell_action_sheet.dart
  12. 2 2
      frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/field_editor.dart
  13. 1 1
      frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/field_type_list.dart
  14. 3 3
      frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/field_type_option_editor.dart
  15. 4 3
      frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/grid_header.dart
  16. 1 1
      frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/type_option/builder.dart
  17. 3 5
      frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/type_option/date.dart
  18. 1 1
      frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/type_option/multi_select.dart
  19. 3 3
      frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/type_option/number.dart
  20. 2 1
      frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/type_option/select_option.dart
  21. 1 1
      frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/type_option/single_select.dart
  22. 1 1
      frontend/app_flowy/lib/plugins/grid/presentation/widgets/row/grid_row.dart
  23. 3 3
      frontend/app_flowy/lib/plugins/grid/presentation/widgets/row/row_detail.dart
  24. 2 2
      frontend/app_flowy/lib/plugins/grid/presentation/widgets/toolbar/grid_property.dart
  25. 2 2
      frontend/app_flowy/lib/plugins/grid/presentation/widgets/toolbar/grid_toolbar.dart
  26. 6 6
      frontend/app_flowy/packages/appflowy_popover/example/lib/example_button.dart
  27. 1 1
      frontend/app_flowy/packages/appflowy_popover/example/lib/main.dart
  28. 5 0
      frontend/app_flowy/packages/appflowy_popover/lib/appflowy_popover.dart
  29. 0 345
      frontend/app_flowy/packages/appflowy_popover/lib/popover.dart
  30. 0 0
      frontend/app_flowy/packages/appflowy_popover/lib/src/follower.dart
  31. 0 0
      frontend/app_flowy/packages/appflowy_popover/lib/src/layout.dart
  32. 116 0
      frontend/app_flowy/packages/appflowy_popover/lib/src/mask.dart
  33. 44 0
      frontend/app_flowy/packages/appflowy_popover/lib/src/mutex.dart
  34. 248 0
      frontend/app_flowy/packages/appflowy_popover/lib/src/popover.dart
  35. 4 1
      frontend/app_flowy/packages/flowy_infra_ui/lib/src/flowy_overlay/appflowy_stype_popover.dart

+ 1 - 1
frontend/app_flowy/lib/plugins/board/presentation/card/board_select_option_cell.dart

@@ -2,7 +2,7 @@ import 'package:app_flowy/plugins/board/application/card/board_select_option_cel
 import 'package:app_flowy/plugins/grid/application/cell/cell_service/cell_service.dart';
 import 'package:app_flowy/plugins/grid/presentation/widgets/cell/select_option_cell/extension.dart';
 import 'package:app_flowy/plugins/grid/presentation/widgets/cell/select_option_cell/select_option_editor.dart';
-import 'package:appflowy_popover/popover.dart';
+import 'package:appflowy_popover/appflowy_popover.dart';
 import 'package:flowy_infra_ui/flowy_infra_ui.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';

+ 1 - 1
frontend/app_flowy/lib/plugins/board/presentation/card/card.dart

@@ -1,7 +1,7 @@
 import 'package:app_flowy/plugins/board/application/card/card_bloc.dart';
 import 'package:app_flowy/plugins/board/application/card/card_data_controller.dart';
 import 'package:app_flowy/plugins/grid/presentation/widgets/row/row_action_sheet.dart';
-import 'package:appflowy_popover/popover.dart';
+import 'package:appflowy_popover/appflowy_popover.dart';
 import 'package:flowy_infra/image.dart';
 import 'package:flowy_infra/theme.dart';
 import 'package:flowy_infra_ui/flowy_infra_ui.dart';

+ 1 - 1
frontend/app_flowy/lib/plugins/board/presentation/toolbar/board_setting.dart

@@ -4,7 +4,7 @@ import 'package:app_flowy/plugins/grid/application/field/field_controller.dart';
 import 'package:app_flowy/plugins/grid/presentation/layout/sizes.dart';
 import 'package:app_flowy/plugins/grid/presentation/widgets/toolbar/grid_group.dart';
 import 'package:app_flowy/plugins/grid/presentation/widgets/toolbar/grid_property.dart';
-import 'package:appflowy_popover/popover.dart';
+import 'package:appflowy_popover/appflowy_popover.dart';
 import 'package:easy_localization/easy_localization.dart';
 import 'package:flowy_infra/image.dart';
 import 'package:flowy_infra/theme.dart';

+ 2 - 2
frontend/app_flowy/lib/plugins/board/presentation/toolbar/board_toolbar.dart

@@ -1,5 +1,5 @@
 import 'package:app_flowy/plugins/grid/application/field/field_controller.dart';
-import 'package:appflowy_popover/popover.dart';
+import 'package:appflowy_popover/appflowy_popover.dart';
 import 'package:flowy_infra/image.dart';
 import 'package:flowy_infra/theme.dart';
 import 'package:flowy_infra_ui/flowy_infra_ui.dart';
@@ -65,7 +65,7 @@ class _SettingButtonState extends State<_SettingButton> {
     return AppFlowyPopover(
       controller: popoverController,
       constraints: BoxConstraints.loose(const Size(260, 400)),
-      triggerActions: PopoverTriggerActionFlags.click,
+      triggerActions: PopoverTriggerFlags.click,
       child: FlowyIconButton(
         hoverColor: theme.hover,
         width: 22,

+ 1 - 1
frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/date_cell/date_cell.dart

@@ -4,7 +4,7 @@ import 'package:flutter/widgets.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:app_flowy/startup/startup.dart';
 import 'package:app_flowy/plugins/grid/application/prelude.dart';
-import 'package:appflowy_popover/popover.dart';
+import 'package:appflowy_popover/appflowy_popover.dart';
 
 import '../cell_builder.dart';
 import 'date_editor.dart';

+ 16 - 23
frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/date_cell/date_editor.dart

@@ -1,7 +1,7 @@
 import 'package:app_flowy/generated/locale_keys.g.dart';
 import 'package:app_flowy/plugins/grid/application/cell/date_cal_bloc.dart';
 import 'package:app_flowy/plugins/grid/application/field/type_option/type_option_context.dart';
-import 'package:appflowy_popover/popover.dart';
+import 'package:appflowy_popover/appflowy_popover.dart';
 import 'package:easy_localization/easy_localization.dart';
 import 'package:flowy_infra/image.dart';
 import 'package:flowy_infra/theme.dart';
@@ -300,8 +300,7 @@ class _DateTypeOptionButton extends StatelessWidget {
       selector: (state) => state.dateTypeOptionPB,
       builder: (context, dateTypeOptionPB) {
         return AppFlowyPopover(
-          triggerActions:
-              PopoverTriggerActionFlags.hover | PopoverTriggerActionFlags.click,
+          triggerActions: PopoverTriggerFlags.hover | PopoverTriggerFlags.click,
           offset: const Offset(20, 0),
           constraints: BoxConstraints.loose(const Size(140, 100)),
           child: FlowyButton(
@@ -340,36 +339,30 @@ class _CalDateTimeSettingState extends State<_CalDateTimeSetting> {
   @override
   Widget build(BuildContext context) {
     List<Widget> children = [
-      Popover(
+      AppFlowyPopover(
         mutex: _popoverMutex,
-        triggerActions:
-            PopoverTriggerActionFlags.hover | PopoverTriggerActionFlags.click,
+        asBarrier: true,
+        triggerActions: PopoverTriggerFlags.hover | PopoverTriggerFlags.click,
         offset: const Offset(20, 0),
         popupBuilder: (BuildContext context) {
-          return OverlayContainer(
-            constraints: BoxConstraints.loose(const Size(460, 440)),
-            child: DateFormatList(
-              selectedFormat: widget.dateTypeOptionPB.dateFormat,
-              onSelected: (format) =>
-                  widget.onEvent(DateCalEvent.setDateFormat(format)),
-            ),
+          return DateFormatList(
+            selectedFormat: widget.dateTypeOptionPB.dateFormat,
+            onSelected: (format) =>
+                widget.onEvent(DateCalEvent.setDateFormat(format)),
           );
         },
         child: const DateFormatButton(),
       ),
-      Popover(
+      AppFlowyPopover(
         mutex: _popoverMutex,
-        triggerActions:
-            PopoverTriggerActionFlags.hover | PopoverTriggerActionFlags.click,
+        asBarrier: true,
+        triggerActions: PopoverTriggerFlags.hover | PopoverTriggerFlags.click,
         offset: const Offset(20, 0),
         popupBuilder: (BuildContext context) {
-          return OverlayContainer(
-            constraints: BoxConstraints.loose(const Size(460, 440)),
-            child: TimeFormatList(
-              selectedFormat: widget.dateTypeOptionPB.timeFormat,
-              onSelected: (format) =>
-                  widget.onEvent(DateCalEvent.setTimeFormat(format)),
-            ),
+          return TimeFormatList(
+            selectedFormat: widget.dateTypeOptionPB.timeFormat,
+            onSelected: (format) =>
+                widget.onEvent(DateCalEvent.setTimeFormat(format)),
           );
         },
         child: TimeFormatButton(timeFormat: widget.dateTypeOptionPB.timeFormat),

+ 1 - 1
frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/select_option_cell/select_option_cell.dart

@@ -1,6 +1,6 @@
 import 'package:app_flowy/startup/startup.dart';
 import 'package:app_flowy/plugins/grid/application/prelude.dart';
-import 'package:appflowy_popover/popover.dart';
+import 'package:appflowy_popover/appflowy_popover.dart';
 
 import 'package:flowy_infra/theme.dart';
 import 'package:flowy_infra_ui/flowy_infra_ui.dart';

+ 2 - 1
frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/select_option_cell/select_option_editor.dart

@@ -1,7 +1,7 @@
 import 'dart:collection';
 import 'package:app_flowy/plugins/grid/application/cell/cell_service/cell_service.dart';
 import 'package:app_flowy/plugins/grid/application/cell/select_option_editor_bloc.dart';
-import 'package:appflowy_popover/popover.dart';
+import 'package:appflowy_popover/appflowy_popover.dart';
 
 import 'package:flowy_infra/image.dart';
 import 'package:flowy_infra/theme.dart';
@@ -243,6 +243,7 @@ class _SelectOptionCellState extends State<_SelectOptionCell> {
     return AppFlowyPopover(
       controller: _popoverController,
       offset: const Offset(20, 0),
+      asBarrier: true,
       constraints: BoxConstraints.loose(const Size(200, 300)),
       mutex: widget.popoverMutex,
       child: SizedBox(

+ 2 - 2
frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/url_cell/url_cell.dart

@@ -2,7 +2,7 @@ import 'dart:async';
 import 'package:app_flowy/generated/locale_keys.g.dart';
 import 'package:app_flowy/plugins/grid/application/cell/url_cell_bloc.dart';
 import 'package:app_flowy/workspace/presentation/home/toast.dart';
-import 'package:appflowy_popover/popover.dart';
+import 'package:appflowy_popover/appflowy_popover.dart';
 import 'package:easy_localization/easy_localization.dart';
 import 'package:flowy_infra/image.dart';
 import 'package:flowy_infra/theme.dart';
@@ -220,7 +220,7 @@ class _EditURLAccessoryState extends State<_EditURLAccessory>
       constraints: BoxConstraints.loose(const Size(300, 160)),
       controller: _popoverController,
       direction: PopoverDirection.bottomWithLeftAligned,
-      triggerActions: PopoverTriggerActionFlags.click,
+      triggerActions: PopoverTriggerFlags.click,
       offset: const Offset(0, 20),
       child: svgWidget("editor/edit", color: theme.iconColor),
       popupBuilder: (BuildContext popoverContext) {

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

@@ -1,6 +1,6 @@
 import 'package:app_flowy/plugins/grid/application/field/field_cell_bloc.dart';
 import 'package:app_flowy/plugins/grid/application/field/field_service.dart';
-import 'package:appflowy_popover/popover.dart';
+import 'package:appflowy_popover/appflowy_popover.dart';
 import 'package:flowy_infra/image.dart';
 import 'package:flowy_infra/theme.dart';
 import 'package:flowy_infra_ui/flowy_infra_ui.dart';
@@ -33,7 +33,7 @@ class GridFieldCell extends StatelessWidget {
           final button = AppFlowyPopover(
             constraints: BoxConstraints.loose(const Size(240, 840)),
             direction: PopoverDirection.bottomWithLeftAligned,
-            triggerActions: PopoverTriggerActionFlags.click,
+            triggerActions: PopoverTriggerFlags.click,
             offset: const Offset(0, 10),
             popupBuilder: (BuildContext context) {
               return GridFieldCellActionSheet(

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

@@ -3,7 +3,7 @@ import 'package:app_flowy/plugins/grid/presentation/widgets/header/field_editor.
 import 'package:app_flowy/startup/startup.dart';
 import 'package:app_flowy/plugins/grid/application/prelude.dart';
 import 'package:app_flowy/workspace/presentation/widgets/dialogs.dart';
-import 'package:appflowy_popover/popover.dart';
+import 'package:appflowy_popover/appflowy_popover.dart';
 import 'package:flowy_infra/image.dart';
 import 'package:flowy_infra/theme.dart';
 import 'package:flowy_infra_ui/style_widget/button.dart';

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

@@ -1,6 +1,6 @@
 import 'package:app_flowy/plugins/grid/application/field/field_editor_bloc.dart';
 import 'package:app_flowy/plugins/grid/application/field/type_option/type_option_context.dart';
-import 'package:appflowy_popover/popover.dart';
+import 'package:appflowy_popover/appflowy_popover.dart';
 import 'package:easy_localization/easy_localization.dart';
 import 'package:app_flowy/workspace/presentation/widgets/dialogs.dart';
 import 'package:flowy_infra/theme.dart';
@@ -244,7 +244,7 @@ class _DeleteFieldButton extends StatelessWidget {
 
   Widget _wrapPopover(Widget widget) {
     return AppFlowyPopover(
-      triggerActions: PopoverTriggerActionFlags.click,
+      triggerActions: PopoverTriggerFlags.click,
       constraints: BoxConstraints.loose(const Size(400, 240)),
       mutex: popoverMutex,
       direction: PopoverDirection.center,

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

@@ -1,4 +1,4 @@
-import 'package:appflowy_popover/popover.dart';
+import 'package:appflowy_popover/appflowy_popover.dart';
 import 'package:flowy_infra/image.dart';
 import 'package:flowy_infra/theme.dart';
 import 'package:flowy_infra_ui/flowy_infra_ui.dart';

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

@@ -1,6 +1,6 @@
 import 'dart:typed_data';
 import 'package:app_flowy/plugins/grid/application/field/type_option/type_option_data_controller.dart';
-import 'package:appflowy_popover/popover.dart';
+import 'package:appflowy_popover/appflowy_popover.dart';
 import 'package:dartz/dartz.dart' show Either;
 import 'package:flowy_infra/image.dart';
 import 'package:flowy_infra/theme.dart';
@@ -66,8 +66,8 @@ class FieldTypeOptionEditor extends StatelessWidget {
       height: GridSize.typeOptionItemHeight,
       child: AppFlowyPopover(
         constraints: BoxConstraints.loose(const Size(460, 440)),
-        triggerActions:
-            PopoverTriggerActionFlags.click | PopoverTriggerActionFlags.hover,
+        asBarrier: true,
+        triggerActions: PopoverTriggerFlags.click | PopoverTriggerFlags.hover,
         mutex: popoverMutex,
         offset: const Offset(20, 0),
         popupBuilder: (context) {

+ 4 - 3
frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/grid_header.dart

@@ -4,7 +4,7 @@ import 'package:app_flowy/plugins/grid/application/field/type_option/type_option
 import 'package:app_flowy/startup/startup.dart';
 import 'package:app_flowy/plugins/grid/application/prelude.dart';
 import 'package:easy_localization/easy_localization.dart';
-import 'package:appflowy_popover/popover.dart';
+import 'package:appflowy_popover/appflowy_popover.dart';
 import 'package:flowy_infra/image.dart';
 import 'package:flowy_infra/theme.dart';
 import 'package:flowy_infra_ui/flowy_infra_ui.dart';
@@ -177,9 +177,10 @@ class CreateFieldButton extends StatelessWidget {
     final theme = context.watch<AppTheme>();
 
     return AppFlowyPopover(
-      triggerActions: PopoverTriggerActionFlags.click,
+      triggerActions: PopoverTriggerFlags.click,
       direction: PopoverDirection.bottomWithRightAligned,
-      constraints: BoxConstraints.loose(const Size(240, 200)),
+      asBarrier: true,
+      constraints: BoxConstraints.loose(const Size(240, 600)),
       child: FlowyButton(
         text: FlowyText.medium(
           LocaleKeys.grid_field_newColumn.tr(),

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

@@ -3,7 +3,7 @@ import 'dart:typed_data';
 import 'package:app_flowy/plugins/grid/application/field/field_controller.dart';
 import 'package:app_flowy/plugins/grid/application/field/type_option/type_option_context.dart';
 import 'package:app_flowy/plugins/grid/application/field/type_option/type_option_data_controller.dart';
-import 'package:appflowy_popover/popover.dart';
+import 'package:appflowy_popover/appflowy_popover.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/checkbox_type_option.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/date_type_option.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/multi_select_type_option.pb.dart';

+ 3 - 5
frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/type_option/date.dart

@@ -11,7 +11,7 @@ import 'package:flowy_infra_ui/widget/spacing.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/date_type_option_entities.pb.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
-import 'package:appflowy_popover/popover.dart';
+import 'package:appflowy_popover/appflowy_popover.dart';
 import '../../../layout/sizes.dart';
 import '../field_type_option_editor.dart';
 import 'builder.dart';
@@ -64,8 +64,7 @@ class DateTypeOptionWidget extends TypeOptionWidget {
   Widget _renderDateFormatButton(BuildContext context, DateFormat dataFormat) {
     return AppFlowyPopover(
       mutex: popoverMutex,
-      triggerActions:
-          PopoverTriggerActionFlags.hover | PopoverTriggerActionFlags.click,
+      triggerActions: PopoverTriggerFlags.hover | PopoverTriggerFlags.click,
       offset: const Offset(20, 0),
       constraints: BoxConstraints.loose(const Size(460, 440)),
       popupBuilder: (popoverContext) {
@@ -86,8 +85,7 @@ class DateTypeOptionWidget extends TypeOptionWidget {
   Widget _renderTimeFormatButton(BuildContext context, TimeFormat timeFormat) {
     return AppFlowyPopover(
       mutex: popoverMutex,
-      triggerActions:
-          PopoverTriggerActionFlags.hover | PopoverTriggerActionFlags.click,
+      triggerActions: PopoverTriggerFlags.hover | PopoverTriggerFlags.click,
       offset: const Offset(20, 0),
       constraints: BoxConstraints.loose(const Size(460, 440)),
       popupBuilder: (BuildContext popoverContext) {

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

@@ -1,7 +1,7 @@
 import 'package:app_flowy/plugins/grid/application/field/type_option/multi_select_type_option.dart';
 import 'package:app_flowy/plugins/grid/application/field/type_option/type_option_context.dart';
 import 'package:flutter/material.dart';
-import 'package:appflowy_popover/popover.dart';
+import 'package:appflowy_popover/appflowy_popover.dart';
 
 import '../field_type_option_editor.dart';
 import 'builder.dart';

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

@@ -1,7 +1,7 @@
 import 'package:app_flowy/plugins/grid/application/field/type_option/number_bloc.dart';
 import 'package:app_flowy/plugins/grid/application/field/type_option/number_format_bloc.dart';
 import 'package:app_flowy/plugins/grid/application/field/type_option/type_option_context.dart';
-import 'package:appflowy_popover/popover.dart';
+import 'package:appflowy_popover/appflowy_popover.dart';
 import 'package:flowy_infra/image.dart';
 import 'package:flowy_infra/theme.dart';
 import 'package:flowy_infra_ui/flowy_infra_ui.dart';
@@ -57,8 +57,8 @@ class NumberTypeOptionWidget extends TypeOptionWidget {
           builder: (context, state) {
             return AppFlowyPopover(
               mutex: popoverMutex,
-              triggerActions: PopoverTriggerActionFlags.hover |
-                  PopoverTriggerActionFlags.click,
+              triggerActions:
+                  PopoverTriggerFlags.hover | PopoverTriggerFlags.click,
               offset: const Offset(20, 0),
               constraints: BoxConstraints.loose(const Size(460, 440)),
               child: FlowyButton(

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

@@ -1,5 +1,5 @@
 import 'package:app_flowy/plugins/grid/application/field/type_option/select_option_type_option_bloc.dart';
-import 'package:appflowy_popover/popover.dart';
+import 'package:appflowy_popover/appflowy_popover.dart';
 import 'package:flowy_infra/image.dart';
 import 'package:flowy_infra/theme.dart';
 import 'package:flowy_infra_ui/flowy_infra_ui.dart';
@@ -184,6 +184,7 @@ class _OptionCellState extends State<_OptionCell> {
       controller: _popoverController,
       mutex: widget.popoverMutex,
       offset: const Offset(20, 0),
+      asBarrier: true,
       constraints: BoxConstraints.loose(const Size(460, 440)),
       child: SizedBox(
         height: GridSize.typeOptionItemHeight,

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

@@ -2,7 +2,7 @@ import 'package:app_flowy/plugins/grid/application/field/type_option/single_sele
 import 'package:app_flowy/plugins/grid/application/field/type_option/type_option_context.dart';
 import 'package:flutter/material.dart';
 import '../field_type_option_editor.dart';
-import 'package:appflowy_popover/popover.dart';
+import 'package:appflowy_popover/appflowy_popover.dart';
 import 'builder.dart';
 import 'select_option.dart';
 

+ 1 - 1
frontend/app_flowy/lib/plugins/grid/presentation/widgets/row/grid_row.dart

@@ -1,7 +1,7 @@
 import 'package:app_flowy/plugins/grid/application/prelude.dart';
 import 'package:app_flowy/plugins/grid/application/row/row_cache.dart';
 import 'package:app_flowy/plugins/grid/application/row/row_data_controller.dart';
-import 'package:appflowy_popover/popover.dart';
+import 'package:appflowy_popover/appflowy_popover.dart';
 import 'package:flowy_infra/image.dart';
 import 'package:flowy_infra/theme.dart';
 import 'package:flowy_infra_ui/flowy_infra_ui.dart';

+ 3 - 3
frontend/app_flowy/lib/plugins/grid/presentation/widgets/row/row_detail.dart

@@ -15,7 +15,7 @@ import 'package:app_flowy/generated/locale_keys.g.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
-import 'package:appflowy_popover/popover.dart';
+import 'package:appflowy_popover/appflowy_popover.dart';
 
 import '../../layout/sizes.dart';
 import '../cell/cell_accessory.dart';
@@ -197,7 +197,7 @@ class _CreateFieldButtonState extends State<_CreateFieldButton> {
     return AppFlowyPopover(
       constraints: BoxConstraints.loose(const Size(240, 200)),
       controller: popoverController,
-      triggerActions: PopoverTriggerActionFlags.click,
+      triggerActions: PopoverTriggerFlags.click,
       direction: PopoverDirection.topWithLeftAligned,
       onClose: widget.onClosed,
       child: Container(
@@ -274,7 +274,7 @@ class _RowDetailCellState extends State<_RowDetailCell> {
                 offset: const Offset(20, 0),
                 popupBuilder: (popoverContext) {
                   return OverlayContainer(
-                    constraints: BoxConstraints.loose(const Size(240, 200)),
+                    constraints: BoxConstraints.loose(const Size(240, 600)),
                     child: FieldEditor(
                       gridId: widget.cellId.gridId,
                       fieldName: widget.cellId.fieldContext.field.name,

+ 2 - 2
frontend/app_flowy/lib/plugins/grid/presentation/widgets/toolbar/grid_property.dart

@@ -3,7 +3,7 @@ import 'package:app_flowy/plugins/grid/presentation/widgets/header/field_editor.
 import 'package:app_flowy/startup/startup.dart';
 import 'package:app_flowy/plugins/grid/application/setting/property_bloc.dart';
 import 'package:app_flowy/plugins/grid/presentation/widgets/header/field_type_extension.dart';
-import 'package:appflowy_popover/popover.dart';
+import 'package:appflowy_popover/appflowy_popover.dart';
 import 'package:flowy_infra/image.dart';
 import 'package:flowy_infra/theme.dart';
 import 'package:flowy_infra_ui/flowy_infra_ui.dart';
@@ -118,7 +118,7 @@ class _GridPropertyCell extends StatelessWidget {
   Widget _editFieldButton(AppTheme theme, BuildContext context) {
     return AppFlowyPopover(
       mutex: popoverMutex,
-      triggerActions: PopoverTriggerActionFlags.click,
+      triggerActions: PopoverTriggerFlags.click,
       offset: const Offset(20, 0),
       constraints: BoxConstraints.loose(const Size(240, 200)),
       child: FlowyButton(

+ 2 - 2
frontend/app_flowy/lib/plugins/grid/presentation/widgets/toolbar/grid_toolbar.dart

@@ -1,4 +1,4 @@
-import 'package:appflowy_popover/popover.dart';
+import 'package:appflowy_popover/appflowy_popover.dart';
 import 'package:app_flowy/plugins/grid/application/setting/setting_bloc.dart';
 import 'package:flowy_infra/image.dart';
 import 'package:flowy_infra/theme.dart';
@@ -55,7 +55,7 @@ class _SettingButton extends StatelessWidget {
     final theme = context.watch<AppTheme>();
     return AppFlowyPopover(
       constraints: BoxConstraints.loose(const Size(260, 400)),
-      triggerActions: PopoverTriggerActionFlags.click,
+      triggerActions: PopoverTriggerFlags.click,
       offset: const Offset(0, 10),
       child: FlowyIconButton(
         width: 22,

+ 6 - 6
frontend/app_flowy/packages/appflowy_popover/example/lib/example_button.dart

@@ -1,5 +1,5 @@
 import 'package:flutter/material.dart';
-import 'package:appflowy_popover/popover.dart';
+import 'package:appflowy_popover/appflowy_popover.dart';
 
 class PopoverMenu extends StatefulWidget {
   const PopoverMenu({Key? key}) : super(key: key);
@@ -41,8 +41,8 @@ class _PopoverMenuState extends State<PopoverMenu> {
                       decoration: null)),
             ),
             Popover(
-              triggerActions: PopoverTriggerActionFlags.hover |
-                  PopoverTriggerActionFlags.click,
+              triggerActions:
+                  PopoverTriggerFlags.hover | PopoverTriggerFlags.click,
               mutex: popOverMutex,
               offset: const Offset(10, 0),
               popupBuilder: (BuildContext context) {
@@ -54,8 +54,8 @@ class _PopoverMenuState extends State<PopoverMenu> {
               ),
             ),
             Popover(
-              triggerActions: PopoverTriggerActionFlags.hover |
-                  PopoverTriggerActionFlags.click,
+              triggerActions:
+                  PopoverTriggerFlags.hover | PopoverTriggerFlags.click,
               mutex: popOverMutex,
               offset: const Offset(10, 0),
               popupBuilder: (BuildContext context) {
@@ -86,7 +86,7 @@ class ExampleButton extends StatelessWidget {
   @override
   Widget build(BuildContext context) {
     return Popover(
-      triggerActions: PopoverTriggerActionFlags.click,
+      triggerActions: PopoverTriggerFlags.click,
       offset: offset,
       direction: direction ?? PopoverDirection.rightWithTopAligned,
       child: TextButton(child: Text(label), onPressed: () {}),

+ 1 - 1
frontend/app_flowy/packages/appflowy_popover/example/lib/main.dart

@@ -1,4 +1,4 @@
-import 'package:appflowy_popover/popover.dart';
+import 'package:appflowy_popover/appflowy_popover.dart';
 import 'package:flutter/material.dart';
 import "./example_button.dart";
 

+ 5 - 0
frontend/app_flowy/packages/appflowy_popover/lib/appflowy_popover.dart

@@ -0,0 +1,5 @@
+/// AppFlowyBoard library
+library appflowy_popover;
+
+export 'src/mutex.dart';
+export 'src/popover.dart';

+ 0 - 345
frontend/app_flowy/packages/appflowy_popover/lib/popover.dart

@@ -1,345 +0,0 @@
-import 'package:appflowy_popover/layout.dart';
-import 'package:flutter/gestures.dart';
-import 'package:flutter/material.dart';
-import 'package:flutter/services.dart';
-
-/// If multiple popovers are exclusive,
-/// pass the same mutex to them.
-class PopoverMutex {
-  final ValueNotifier<PopoverState?> _stateNotifier = ValueNotifier(null);
-  PopoverMutex();
-
-  void removePopoverStateListener(VoidCallback listener) {
-    _stateNotifier.removeListener(listener);
-  }
-
-  VoidCallback listenOnPopoverStateChanged(VoidCallback callback) {
-    listenerCallback() {
-      callback();
-    }
-
-    _stateNotifier.addListener(listenerCallback);
-    return listenerCallback;
-  }
-
-  void close() {
-    _stateNotifier.value?.close();
-  }
-
-  PopoverState? get state => _stateNotifier.value;
-
-  set state(PopoverState? newState) {
-    if (_stateNotifier.value != null && _stateNotifier.value != newState) {
-      _stateNotifier.value?.close();
-    }
-    _stateNotifier.value = newState;
-  }
-
-  void _removeState() {
-    _stateNotifier.value = null;
-  }
-
-  void dispose() {
-    _stateNotifier.dispose();
-  }
-}
-
-class PopoverController {
-  PopoverState? state;
-
-  close() {
-    state?.close();
-  }
-
-  show() {
-    state?.showOverlay();
-  }
-}
-
-class PopoverTriggerActionFlags {
-  static int click = 0x01;
-  static int hover = 0x02;
-}
-
-enum PopoverDirection {
-  // Corner aligned with a corner of the SourceWidget
-  topLeft,
-  topRight,
-  bottomLeft,
-  bottomRight,
-  center,
-
-  // Edge aligned with a edge of the SourceWidget
-  topWithLeftAligned,
-  topWithCenterAligned,
-  topWithRightAligned,
-  rightWithTopAligned,
-  rightWithCenterAligned,
-  rightWithBottomAligned,
-  bottomWithLeftAligned,
-  bottomWithCenterAligned,
-  bottomWithRightAligned,
-  leftWithTopAligned,
-  leftWithCenterAligned,
-  leftWithBottomAligned,
-
-  custom,
-}
-
-class Popover extends StatefulWidget {
-  final PopoverController? controller;
-
-  final Offset? offset;
-
-  final Decoration? maskDecoration;
-
-  /// The function used to build the popover.
-  final Widget? Function(BuildContext context) popupBuilder;
-
-  final int triggerActions;
-
-  /// If multiple popovers are exclusive,
-  /// pass the same mutex to them.
-  final PopoverMutex? mutex;
-
-  /// The direction of the popover
-  final PopoverDirection direction;
-
-  final void Function()? onClose;
-
-  /// The content area of the popover.
-  final Widget child;
-
-  const Popover({
-    Key? key,
-    required this.child,
-    required this.popupBuilder,
-    this.controller,
-    this.offset,
-    this.maskDecoration,
-    this.triggerActions = 0,
-    this.direction = PopoverDirection.rightWithTopAligned,
-    this.mutex,
-    this.onClose,
-  }) : super(key: key);
-
-  @override
-  State<Popover> createState() => PopoverState();
-}
-
-class PopoverState extends State<Popover> {
-  final PopoverLink popoverLink = PopoverLink();
-  OverlayEntry? _overlayEntry;
-  bool hasMask = true;
-
-  static PopoverState? _popoverWithMask;
-
-  @override
-  void initState() {
-    widget.controller?.state = this;
-    super.initState();
-  }
-
-  showOverlay() {
-    close();
-
-    if (widget.mutex != null) {
-      widget.mutex?.state = this;
-    }
-
-    if (_popoverWithMask == null) {
-      _popoverWithMask = this;
-    } else {
-      hasMask = false;
-    }
-
-    final newEntry = OverlayEntry(builder: (context) {
-      final children = <Widget>[];
-
-      if (hasMask) {
-        children.add(_PopoverMask(
-          decoration: widget.maskDecoration,
-          onTap: () => close(),
-          onExit: () => close(),
-        ));
-      }
-
-      children.add(PopoverContainer(
-        direction: widget.direction,
-        popoverLink: popoverLink,
-        offset: widget.offset ?? Offset.zero,
-        popupBuilder: widget.popupBuilder,
-        onClose: () => close(),
-        onCloseAll: () => closeAll(),
-      ));
-
-      return Stack(children: children);
-    });
-
-    _overlayEntry = newEntry;
-
-    Overlay.of(context)?.insert(newEntry);
-  }
-
-  close() {
-    if (_overlayEntry != null) {
-      _overlayEntry!.remove();
-      _overlayEntry = null;
-      if (_popoverWithMask == this) {
-        _popoverWithMask = null;
-      }
-      if (widget.onClose != null) {
-        widget.onClose!();
-      }
-    }
-
-    if (widget.mutex?.state == this) {
-      widget.mutex?._removeState();
-    }
-  }
-
-  closeAll() {
-    _popoverWithMask?.close();
-  }
-
-  @override
-  void deactivate() {
-    close();
-    super.deactivate();
-  }
-
-  _handleTargetPointerDown(PointerDownEvent event) {
-    if (widget.triggerActions & PopoverTriggerActionFlags.click != 0) {
-      showOverlay();
-    }
-  }
-
-  _handleTargetPointerEnter(PointerEnterEvent event) {
-    if (widget.triggerActions & PopoverTriggerActionFlags.hover != 0) {
-      showOverlay();
-    }
-  }
-
-  _buildContent(BuildContext context) {
-    if (widget.triggerActions == 0) {
-      return widget.child;
-    }
-
-    return MouseRegion(
-      onEnter: _handleTargetPointerEnter,
-      child: Listener(
-        onPointerDown: _handleTargetPointerDown,
-        child: widget.child,
-      ),
-    );
-  }
-
-  @override
-  Widget build(BuildContext context) {
-    return PopoverTarget(
-      link: popoverLink,
-      child: _buildContent(context),
-    );
-  }
-}
-
-class _PopoverMask extends StatefulWidget {
-  final void Function() onTap;
-  final void Function()? onExit;
-  final Decoration? decoration;
-
-  const _PopoverMask(
-      {Key? key, required this.onTap, this.onExit, this.decoration})
-      : super(key: key);
-
-  @override
-  State<StatefulWidget> createState() => _PopoverMaskState();
-}
-
-class _PopoverMaskState extends State<_PopoverMask> {
-  @override
-  void initState() {
-    HardwareKeyboard.instance.addHandler(_handleGlobalKeyEvent);
-    super.initState();
-  }
-
-  bool _handleGlobalKeyEvent(KeyEvent event) {
-    if (event.logicalKey == LogicalKeyboardKey.escape) {
-      if (widget.onExit != null) {
-        widget.onExit!();
-      }
-
-      return true;
-    }
-    return false;
-  }
-
-  @override
-  void deactivate() {
-    HardwareKeyboard.instance.removeHandler(_handleGlobalKeyEvent);
-    super.deactivate();
-  }
-
-  @override
-  Widget build(BuildContext context) {
-    return GestureDetector(
-      onTap: widget.onTap,
-      child: Container(
-        // decoration: widget.decoration,
-        decoration: widget.decoration ??
-            const BoxDecoration(
-              color: Color.fromARGB(0, 244, 67, 54),
-            ),
-      ),
-    );
-  }
-}
-
-class PopoverContainer extends StatefulWidget {
-  final Widget? Function(BuildContext context) popupBuilder;
-  final PopoverDirection direction;
-  final PopoverLink popoverLink;
-  final Offset offset;
-  final void Function() onClose;
-  final void Function() onCloseAll;
-
-  const PopoverContainer({
-    Key? key,
-    required this.popupBuilder,
-    required this.direction,
-    required this.popoverLink,
-    required this.offset,
-    required this.onClose,
-    required this.onCloseAll,
-  }) : super(key: key);
-
-  @override
-  State<StatefulWidget> createState() => PopoverContainerState();
-
-  static PopoverContainerState of(BuildContext context) {
-    if (context is StatefulElement && context.state is PopoverContainerState) {
-      return context.state as PopoverContainerState;
-    }
-    final PopoverContainerState? result =
-        context.findAncestorStateOfType<PopoverContainerState>();
-    return result!;
-  }
-}
-
-class PopoverContainerState extends State<PopoverContainer> {
-  @override
-  Widget build(BuildContext context) {
-    return CustomSingleChildLayout(
-      delegate: PopoverLayoutDelegate(
-        direction: widget.direction,
-        link: widget.popoverLink,
-        offset: widget.offset,
-      ),
-      child: widget.popupBuilder(context),
-    );
-  }
-
-  close() => widget.onClose();
-
-  closeAll() => widget.onCloseAll();
-}

+ 0 - 0
frontend/app_flowy/packages/appflowy_popover/lib/follower.dart → frontend/app_flowy/packages/appflowy_popover/lib/src/follower.dart


+ 0 - 0
frontend/app_flowy/packages/appflowy_popover/lib/layout.dart → frontend/app_flowy/packages/appflowy_popover/lib/src/layout.dart


+ 116 - 0
frontend/app_flowy/packages/appflowy_popover/lib/src/mask.dart

@@ -0,0 +1,116 @@
+import 'dart:collection';
+
+import 'package:appflowy_popover/appflowy_popover.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
+
+typedef EntryMap = LinkedHashMap<PopoverState, OverlayEntryContext>;
+
+class RootOverlayEntry {
+  final EntryMap _entries = EntryMap();
+  RootOverlayEntry();
+
+  void addEntry(
+    BuildContext context,
+    PopoverState newState,
+    OverlayEntry entry,
+    bool asBarrier,
+  ) {
+    _entries[newState] = OverlayEntryContext(entry, newState, asBarrier);
+    Overlay.of(context)?.insert(entry);
+  }
+
+  bool contains(PopoverState oldState) {
+    return _entries.containsKey(oldState);
+  }
+
+  void removeEntry(PopoverState oldState) {
+    if (_entries.isEmpty) return;
+
+    final removedEntry = _entries.remove(oldState);
+    removedEntry?.overlayEntry.remove();
+  }
+
+  bool get isEmpty => _entries.isEmpty;
+
+  bool get isNotEmpty => _entries.isNotEmpty;
+
+  bool hasEntry() {
+    return _entries.isNotEmpty;
+  }
+
+  PopoverState? popEntry() {
+    if (_entries.isEmpty) return null;
+
+    final lastEntry = _entries.values.last;
+    _entries.remove(lastEntry.popoverState);
+    lastEntry.overlayEntry.remove();
+    lastEntry.popoverState.widget.onClose?.call();
+
+    if (lastEntry.asBarrier) {
+      return lastEntry.popoverState;
+    } else {
+      return popEntry();
+    }
+  }
+}
+
+class OverlayEntryContext {
+  final bool asBarrier;
+  final PopoverState popoverState;
+  final OverlayEntry overlayEntry;
+
+  OverlayEntryContext(
+    this.overlayEntry,
+    this.popoverState,
+    this.asBarrier,
+  );
+}
+
+class PopoverMask extends StatefulWidget {
+  final void Function() onTap;
+  final void Function()? onExit;
+  final Decoration? decoration;
+
+  const PopoverMask(
+      {Key? key, required this.onTap, this.onExit, this.decoration})
+      : super(key: key);
+
+  @override
+  State<StatefulWidget> createState() => _PopoverMaskState();
+}
+
+class _PopoverMaskState extends State<PopoverMask> {
+  @override
+  void initState() {
+    HardwareKeyboard.instance.addHandler(_handleGlobalKeyEvent);
+    super.initState();
+  }
+
+  bool _handleGlobalKeyEvent(KeyEvent event) {
+    if (event.logicalKey == LogicalKeyboardKey.escape) {
+      if (widget.onExit != null) {
+        widget.onExit!();
+      }
+      return true;
+    } else {
+      return false;
+    }
+  }
+
+  @override
+  void deactivate() {
+    HardwareKeyboard.instance.removeHandler(_handleGlobalKeyEvent);
+    super.deactivate();
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return GestureDetector(
+      onTap: widget.onTap,
+      child: Container(
+        decoration: widget.decoration,
+      ),
+    );
+  }
+}

+ 44 - 0
frontend/app_flowy/packages/appflowy_popover/lib/src/mutex.dart

@@ -0,0 +1,44 @@
+import 'package:flutter/material.dart';
+
+import 'popover.dart';
+
+/// If multiple popovers are exclusive,
+/// pass the same mutex to them.
+class PopoverMutex {
+  final ValueNotifier<PopoverState?> _stateNotifier = ValueNotifier(null);
+  PopoverMutex();
+
+  void removePopoverStateListener(VoidCallback listener) {
+    _stateNotifier.removeListener(listener);
+  }
+
+  VoidCallback listenOnPopoverStateChanged(VoidCallback callback) {
+    listenerCallback() {
+      callback();
+    }
+
+    _stateNotifier.addListener(listenerCallback);
+    return listenerCallback;
+  }
+
+  void close() {
+    _stateNotifier.value?.close();
+  }
+
+  PopoverState? get state => _stateNotifier.value;
+
+  set state(PopoverState? newState) {
+    if (_stateNotifier.value != null && _stateNotifier.value != newState) {
+      _stateNotifier.value?.close();
+    }
+    _stateNotifier.value = newState;
+  }
+
+  void removeState() {
+    _stateNotifier.value = null;
+  }
+
+  void dispose() {
+    _stateNotifier.dispose();
+  }
+}

+ 248 - 0
frontend/app_flowy/packages/appflowy_popover/lib/src/popover.dart

@@ -0,0 +1,248 @@
+import 'dart:async';
+import 'package:appflowy_popover/src/layout.dart';
+import 'package:flutter/material.dart';
+import 'mask.dart';
+import 'mutex.dart';
+
+class PopoverController {
+  PopoverState? _state;
+
+  close() {
+    _state?.close();
+  }
+
+  show() {
+    _state?.showOverlay();
+  }
+}
+
+class PopoverTriggerFlags {
+  static int click = 0x01;
+  static int hover = 0x02;
+}
+
+enum PopoverDirection {
+  // Corner aligned with a corner of the SourceWidget
+  topLeft,
+  topRight,
+  bottomLeft,
+  bottomRight,
+  center,
+
+  // Edge aligned with a edge of the SourceWidget
+  topWithLeftAligned,
+  topWithCenterAligned,
+  topWithRightAligned,
+  rightWithTopAligned,
+  rightWithCenterAligned,
+  rightWithBottomAligned,
+  bottomWithLeftAligned,
+  bottomWithCenterAligned,
+  bottomWithRightAligned,
+  leftWithTopAligned,
+  leftWithCenterAligned,
+  leftWithBottomAligned,
+
+  custom,
+}
+
+class Popover extends StatefulWidget {
+  final PopoverController? controller;
+
+  final Offset? offset;
+
+  final Decoration? maskDecoration;
+
+  /// The function used to build the popover.
+  final Widget? Function(BuildContext context) popupBuilder;
+
+  final int triggerActions;
+
+  /// If multiple popovers are exclusive,
+  /// pass the same mutex to them.
+  final PopoverMutex? mutex;
+
+  /// The direction of the popover
+  final PopoverDirection direction;
+
+  final void Function()? onClose;
+
+  final bool asBarrier;
+
+  /// The content area of the popover.
+  final Widget child;
+
+  const Popover({
+    Key? key,
+    required this.child,
+    required this.popupBuilder,
+    this.controller,
+    this.offset,
+    this.maskDecoration = const BoxDecoration(
+      color: Color.fromARGB(0, 244, 67, 54),
+    ),
+    this.triggerActions = 0,
+    this.direction = PopoverDirection.rightWithTopAligned,
+    this.mutex,
+    this.onClose,
+    this.asBarrier = false,
+  }) : super(key: key);
+
+  @override
+  State<Popover> createState() => PopoverState();
+}
+
+class PopoverState extends State<Popover> {
+  static final RootOverlayEntry _rootEntry = RootOverlayEntry();
+  final PopoverLink popoverLink = PopoverLink();
+  Timer? _debounceEnterRegionAction;
+
+  @override
+  void initState() {
+    widget.controller?._state = this;
+    super.initState();
+  }
+
+  void showOverlay() {
+    close();
+
+    if (widget.mutex != null) {
+      widget.mutex?.state = this;
+    }
+    final shouldAddMask = _rootEntry.isEmpty;
+    final newEntry = OverlayEntry(builder: (context) {
+      final children = <Widget>[];
+      if (shouldAddMask) {
+        children.add(
+          PopoverMask(
+            decoration: widget.maskDecoration,
+            onTap: () => _removeRootOverlay(),
+            onExit: () => _removeRootOverlay(),
+          ),
+        );
+      }
+
+      children.add(
+        PopoverContainer(
+          direction: widget.direction,
+          popoverLink: popoverLink,
+          offset: widget.offset ?? Offset.zero,
+          popupBuilder: widget.popupBuilder,
+          onClose: () => close(),
+          onCloseAll: () => _removeRootOverlay(),
+        ),
+      );
+
+      return Stack(children: children);
+    });
+
+    _rootEntry.addEntry(context, this, newEntry, widget.asBarrier);
+  }
+
+  void close() {
+    if (_rootEntry.contains(this)) {
+      _rootEntry.removeEntry(this);
+      widget.onClose?.call();
+    }
+  }
+
+  void _removeRootOverlay() {
+    _rootEntry.popEntry();
+
+    if (widget.mutex?.state == this) {
+      widget.mutex?.removeState();
+    }
+  }
+
+  @override
+  void deactivate() {
+    close();
+    super.deactivate();
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return PopoverTarget(
+      link: popoverLink,
+      child: _buildChild(context),
+    );
+  }
+
+  Widget _buildChild(BuildContext context) {
+    if (widget.triggerActions == 0) {
+      return widget.child;
+    }
+
+    return MouseRegion(
+      onEnter: (event) {
+        _debounceEnterRegionAction =
+            Timer(const Duration(milliseconds: 200), () {
+          if (widget.triggerActions & PopoverTriggerFlags.hover != 0) {
+            showOverlay();
+          }
+        });
+      },
+      onExit: (event) {
+        _debounceEnterRegionAction?.cancel();
+        _debounceEnterRegionAction = null;
+      },
+      child: Listener(
+        child: widget.child,
+        onPointerDown: (PointerDownEvent event) {
+          if (widget.triggerActions & PopoverTriggerFlags.click != 0) {
+            showOverlay();
+          }
+        },
+      ),
+    );
+  }
+}
+
+class PopoverContainer extends StatefulWidget {
+  final Widget? Function(BuildContext context) popupBuilder;
+  final PopoverDirection direction;
+  final PopoverLink popoverLink;
+  final Offset offset;
+  final void Function() onClose;
+  final void Function() onCloseAll;
+
+  const PopoverContainer({
+    Key? key,
+    required this.popupBuilder,
+    required this.direction,
+    required this.popoverLink,
+    required this.offset,
+    required this.onClose,
+    required this.onCloseAll,
+  }) : super(key: key);
+
+  @override
+  State<StatefulWidget> createState() => PopoverContainerState();
+
+  static PopoverContainerState of(BuildContext context) {
+    if (context is StatefulElement && context.state is PopoverContainerState) {
+      return context.state as PopoverContainerState;
+    }
+    final PopoverContainerState? result =
+        context.findAncestorStateOfType<PopoverContainerState>();
+    return result!;
+  }
+}
+
+class PopoverContainerState extends State<PopoverContainer> {
+  @override
+  Widget build(BuildContext context) {
+    return CustomSingleChildLayout(
+      delegate: PopoverLayoutDelegate(
+        direction: widget.direction,
+        link: widget.popoverLink,
+        offset: widget.offset,
+      ),
+      child: widget.popupBuilder(context),
+    );
+  }
+
+  close() => widget.onClose();
+
+  closeAll() => widget.onCloseAll();
+}

+ 4 - 1
frontend/app_flowy/packages/flowy_infra_ui/lib/src/flowy_overlay/appflowy_stype_popover.dart

@@ -1,5 +1,5 @@
 import 'package:flowy_infra_ui/flowy_infra_ui_web.dart';
-import 'package:appflowy_popover/popover.dart';
+import 'package:appflowy_popover/appflowy_popover.dart';
 import 'package:flutter/material.dart';
 
 class AppFlowyPopover extends StatelessWidget {
@@ -12,6 +12,7 @@ class AppFlowyPopover extends StatelessWidget {
   final void Function()? onClose;
   final PopoverMutex? mutex;
   final Offset? offset;
+  final bool asBarrier;
 
   const AppFlowyPopover({
     Key? key,
@@ -24,6 +25,7 @@ class AppFlowyPopover extends StatelessWidget {
     this.triggerActions = 0,
     this.offset,
     this.controller,
+    this.asBarrier = false,
   }) : super(key: key);
 
   @override
@@ -33,6 +35,7 @@ class AppFlowyPopover extends StatelessWidget {
       onClose: onClose,
       direction: direction,
       mutex: mutex,
+      asBarrier: asBarrier,
       triggerActions: triggerActions,
       popupBuilder: (context) {
         final child = popupBuilder(context);