Browse Source

Merge pull request #1091 from AppFlowy-IO/fix/appflowy_overlay_bugs

Fix/appflowy overlay bugs
Nathan.fooo 2 years ago
parent
commit
5896855dda
37 changed files with 533 additions and 498 deletions
  1. 2 2
      frontend/app_flowy/assets/translations/en.json
  2. 3 3
      frontend/app_flowy/assets/translations/es-VE.json
  3. 3 3
      frontend/app_flowy/assets/translations/fr-FR.json
  4. 2 2
      frontend/app_flowy/assets/translations/id-ID.json
  5. 2 2
      frontend/app_flowy/assets/translations/ja-JP.json
  6. 207 208
      frontend/app_flowy/assets/translations/ru-RU.json
  7. 2 2
      frontend/app_flowy/assets/translations/zh-CN.json
  8. 2 2
      frontend/app_flowy/assets/translations/zh-TW.json
  9. 1 1
      frontend/app_flowy/lib/plugins/board/presentation/card/board_select_option_cell.dart
  10. 63 27
      frontend/app_flowy/lib/plugins/board/presentation/card/card.dart
  11. 74 0
      frontend/app_flowy/lib/plugins/board/presentation/card/container/accessory.dart
  12. 21 68
      frontend/app_flowy/lib/plugins/board/presentation/card/container/card_container.dart
  13. 0 1
      frontend/app_flowy/lib/plugins/board/presentation/toolbar/board_setting.dart
  14. 1 1
      frontend/app_flowy/lib/plugins/board/presentation/toolbar/board_toolbar.dart
  15. 1 1
      frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/date_cell/date_cell.dart
  16. 1 1
      frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/date_cell/date_editor.dart
  17. 1 1
      frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/select_option_cell/select_option_cell.dart
  18. 49 16
      frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/select_option_cell/select_option_editor.dart
  19. 2 2
      frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/url_cell/url_cell.dart
  20. 1 1
      frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/field_cell.dart
  21. 3 3
      frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/field_editor.dart
  22. 1 1
      frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/field_type_option_editor.dart
  23. 1 1
      frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/grid_header.dart
  24. 2 2
      frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/type_option/date.dart
  25. 1 1
      frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/type_option/number.dart
  26. 1 1
      frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/type_option/select_option.dart
  27. 1 1
      frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/type_option/select_option_editor.dart
  28. 68 30
      frontend/app_flowy/lib/plugins/grid/presentation/widgets/row/grid_row.dart
  29. 2 33
      frontend/app_flowy/lib/plugins/grid/presentation/widgets/row/row_action_sheet.dart
  30. 1 1
      frontend/app_flowy/lib/plugins/grid/presentation/widgets/row/row_detail.dart
  31. 0 1
      frontend/app_flowy/lib/plugins/grid/presentation/widgets/toolbar/grid_group.dart
  32. 1 1
      frontend/app_flowy/lib/plugins/grid/presentation/widgets/toolbar/grid_property.dart
  33. 0 2
      frontend/app_flowy/lib/plugins/grid/presentation/widgets/toolbar/grid_setting.dart
  34. 1 1
      frontend/app_flowy/lib/plugins/grid/presentation/widgets/toolbar/grid_toolbar.dart
  35. 0 63
      frontend/app_flowy/lib/workspace/presentation/widgets/pop_up_window.dart
  36. 10 10
      frontend/app_flowy/packages/appflowy_popover/lib/popover.dart
  37. 2 2
      frontend/app_flowy/packages/flowy_infra_ui/lib/src/flowy_overlay/appflowy_stype_popover.dart

+ 2 - 2
frontend/app_flowy/assets/translations/en.json

@@ -213,8 +213,8 @@
       "aquaColor": "Aqua",
       "blueColor": "Blue",
       "deleteTag": "Delete tag",
-      "colorPannelTitle": "Colors",
-      "pannelTitle": "Select an option or create one",
+      "colorPanelTitle": "Colors",
+      "panelTitle": "Select an option or create one",
       "searchOption": "Search for an option"
     },
     "menuName": "Grid"

+ 3 - 3
frontend/app_flowy/assets/translations/es-VE.json

@@ -201,8 +201,8 @@
       "aquaColor": "Agua",
       "blueColor": "Azul",
       "deleteTag": "Borrar etiqueta",
-      "colorPannelTitle": "Colores",
-      "pannelTitle": "Selecciona una opción o crea una",
+      "colorPanelTitle": "Colores",
+      "panelTitle": "Selecciona una opción o crea una",
       "searchOption": "Buscar una opción"
     },
     "menuName": "Grid"
@@ -218,4 +218,4 @@
     "openSidebar": "Open sidebar",
     "closeSidebar": "Close sidebar"
   }
-}
+}

+ 3 - 3
frontend/app_flowy/assets/translations/fr-FR.json

@@ -199,8 +199,8 @@
       "aquaColor": "Aqua",
       "blueColor": "Bleu",
       "deleteTag": "Supprimer l'étiquette",
-      "colorPannelTitle": "Couleurs",
-      "pannelTitle": "Sélectionnez une option ou créez-en une",
+      "colorPanelTitle": "Couleurs",
+      "panelTitle": "Sélectionnez une option ou créez-en une",
       "searchOption": "Rechercher une option"
     },
     "menuName": "Grille"
@@ -212,4 +212,4 @@
       "timeHintTextInTwentyFourHour": "12:00"
     }
   }
-}
+}

+ 2 - 2
frontend/app_flowy/assets/translations/id-ID.json

@@ -202,8 +202,8 @@
       "aquaColor": "Air",
       "blueColor": "Biru",
       "deleteTag": "Hapus tag",
-      "colorPannelTitle": "Warna",
-      "pannelTitle": "Pilih opsi atau buat baru",
+      "colorPanelTitle": "Warna",
+      "panelTitle": "Pilih opsi atau buat baru",
       "searchOption": "Cari opsi"
     },
     "menuName": "Grid"

+ 2 - 2
frontend/app_flowy/assets/translations/ja-JP.json

@@ -191,8 +191,8 @@
       "aquaColor": "水色",
       "blueColor": "青",
       "deleteTag": "選択候補を削除",
-      "colorPannelTitle": "色",
-      "pannelTitle": "選択候補を検索 または 作成する",
+      "colorPanelTitle": "色",
+      "panelTitle": "選択候補を検索 または 作成する",
       "searchOption": "選択候補を検索"
     }
   },

+ 207 - 208
frontend/app_flowy/assets/translations/ru-RU.json

@@ -1,212 +1,211 @@
 {
-    "appName": "AppFlowy",
-    "defaultUsername": "Я",
-    "welcomeText": "Добро пожаловать в @:appName",
-    "githubStarText": "Поставить звезду на GitHub",
-    "subscribeNewsletterText": "Подписаться на рассылку",
-    "letsGoButtonText": "Начнём",
-    "title": "Заголовок",
-    "signUp": {
-      "buttonText": "Зарегистрироваться",
-      "title": "Регистрация в @:appName",
-      "getStartedText": "Начать",
-      "emptyPasswordError": "Пароль не может быть пустым",
-      "repeatPasswordEmptyError": "Повтор пароля не может быть пустым",
-      "unmatchedPasswordError": "Пароли не совпадают",
-      "alreadyHaveAnAccount": "Уже есть аккаунт?",
-      "emailHint": "Электронная почта",
-      "passwordHint": "Пароль",
-      "repeatPasswordHint": "Повторите пароль"
-    },
-    "signIn": {
-      "loginTitle": "Войти в @:appName",
-      "loginButtonText": "Войти",
-      "buttonText": "Авторизация",
-      "forgotPassword": "Забыли пароль?",
-      "emailHint": "Электронная почта",
-      "passwordHint": "Пароль",
-      "dontHaveAnAccount": "Нет аккаунта?",
-      "repeatPasswordEmptyError": "Повтор пароля не может быть пустым",
-      "unmatchedPasswordError": "Пароли не совпадают"
-    },
-    "workspace": {
-      "create": "Создать рабочее пространство",
-      "hint": "рабочее пространство",
-      "notFoundError": "Нет такого рабочего пространства"
-    },
-    "shareAction": {
-      "buttonText": "Поделиться",
-      "workInProgress": "В разработке",
-      "markdown": "Markdown",
-      "copyLink": "Скопировать ссылку"
-    },
-    "disclosureAction": {
-      "rename": "Переименовать",
-      "delete": "Удалить",
-      "duplicate": "Дублировать"
-    },
-    "blankPageTitle": "Пустая страница",
-    "newPageText": "Новая страница",
-    "trash": {
-      "text": "Корзина",
-      "restoreAll": "Восстановить всё",
-      "deleteAll": "Очистить",
-      "pageHeader": {
-        "fileName": "Имя",
-        "lastModified": "Последнее изменение",
-        "created": "Создан"
-      }
-    },
-    "deletePagePrompt": {
-      "text": "Эта страница в Корзине",
-      "restore": "Восстановить страницу",
-      "deletePermanent": "Удалить навсегда"
-    },
-    "dialogCreatePageNameHint": "Имя",
-    "questionBubble": {
-      "whatsNew": "Что нового?",
-      "help": "Помощь",
-      "debug": {
-        "name": "Отладочная информация",
-        "success": "Скопировано в буфер обмена!",
-        "fail": "Не получилось скопировать"
-      }
-    },
-    "menuAppHeader": {
-      "addPageTooltip": "Быстро добавить новую страницу",
-      "defaultNewPageName": "Без заголовка",
-      "renameDialog": "Переименовать"
-    },
-    "toolbar": {
-      "undo": "Отменить",
-      "redo": "Повторить",
-      "bold": "Жирный",
-      "italic": "Курсив",
-      "underline": "Подчёркнутый",
-      "strike": "Зачёркнутый",
-      "numList": "Нумерованный список",
-      "bulletList": "Маркированный список",
-      "checkList": "Список To-Do",
-      "inlineCode": "Код",
-      "quote": "Цитата",
-      "header": "Заголовок",
-      "highlight": "Выделение"
-    },
-    "tooltip": {
-      "lightMode": "Переключиться в светлую тему",
-      "darkMode": "Переключиться в тёмную тему"
-    },
-    "contactsPage": {
-      "title": "Контакты",
-      "whatsHappening": "Что происходит на этой неделе?",
-      "addContact": "Новый контакт",
-      "editContact": "Редактировать"
-    },
-    "button": {
-      "OK": "OK",
-      "Cancel": "Отмена",
-      "signIn": "Войти",
-      "signOut": "Выйти",
-      "complete": "Завершить",
-      "save": "Сохранить"
-    },
-    "label": {
-      "welcome": "Добро пожаловать!",
-      "firstName": "Имя",
-      "middleName": "Отчество",
-      "lastName": "Фамилия",
-      "stepX": "Этап {X}"
-    },
-    "oAuth": {
-      "err": {
-        "failedTitle": "Ошибка подключения к аккаунту.",
-        "failedMsg": "Убедитесь, что вы завершили вход в своём браузере."
-      },
-      "google": {
-        "title": "Вход через Google",
-        "instruction1": "Чтобы импортировать ваши Google Контакты, вам нужно будет авторизовать приложение через браузер.",
-        "instruction2": "Скопируйте этот код в буфер обмена (нажав кнопку или выделив текст):",
-        "instruction3": "Пройдите по ссылке и введите этот код:",
-        "instruction4": "Нажмите на кнопку, когда завершите вход:"
-      }
-    },
+  "appName": "AppFlowy",
+  "defaultUsername": "Я",
+  "welcomeText": "Добро пожаловать в @:appName",
+  "githubStarText": "Поставить звезду на GitHub",
+  "subscribeNewsletterText": "Подписаться на рассылку",
+  "letsGoButtonText": "Начнём",
+  "title": "Заголовок",
+  "signUp": {
+    "buttonText": "Зарегистрироваться",
+    "title": "Регистрация в @:appName",
+    "getStartedText": "Начать",
+    "emptyPasswordError": "Пароль не может быть пустым",
+    "repeatPasswordEmptyError": "Повтор пароля не может быть пустым",
+    "unmatchedPasswordError": "Пароли не совпадают",
+    "alreadyHaveAnAccount": "Уже есть аккаунт?",
+    "emailHint": "Электронная почта",
+    "passwordHint": "Пароль",
+    "repeatPasswordHint": "Повторите пароль"
+  },
+  "signIn": {
+    "loginTitle": "Войти в @:appName",
+    "loginButtonText": "Войти",
+    "buttonText": "Авторизация",
+    "forgotPassword": "Забыли пароль?",
+    "emailHint": "Электронная почта",
+    "passwordHint": "Пароль",
+    "dontHaveAnAccount": "Нет аккаунта?",
+    "repeatPasswordEmptyError": "Повтор пароля не может быть пустым",
+    "unmatchedPasswordError": "Пароли не совпадают"
+  },
+  "workspace": {
+    "create": "Создать рабочее пространство",
+    "hint": "рабочее пространство",
+    "notFoundError": "Нет такого рабочего пространства"
+  },
+  "shareAction": {
+    "buttonText": "Поделиться",
+    "workInProgress": "В разработке",
+    "markdown": "Markdown",
+    "copyLink": "Скопировать ссылку"
+  },
+  "disclosureAction": {
+    "rename": "Переименовать",
+    "delete": "Удалить",
+    "duplicate": "Дублировать"
+  },
+  "blankPageTitle": "Пустая страница",
+  "newPageText": "Новая страница",
+  "trash": {
+    "text": "Корзина",
+    "restoreAll": "Восстановить всё",
+    "deleteAll": "Очистить",
+    "pageHeader": {
+      "fileName": "Имя",
+      "lastModified": "Последнее изменение",
+      "created": "Создан"
+    }
+  },
+  "deletePagePrompt": {
+    "text": "Эта страница в Корзине",
+    "restore": "Восстановить страницу",
+    "deletePermanent": "Удалить навсегда"
+  },
+  "dialogCreatePageNameHint": "Имя",
+  "questionBubble": {
+    "whatsNew": "Что нового?",
+    "help": "Помощь",
+    "debug": {
+      "name": "Отладочная информация",
+      "success": "Скопировано в буфер обмена!",
+      "fail": "Не получилось скопировать"
+    }
+  },
+  "menuAppHeader": {
+    "addPageTooltip": "Быстро добавить новую страницу",
+    "defaultNewPageName": "Без заголовка",
+    "renameDialog": "Переименовать"
+  },
+  "toolbar": {
+    "undo": "Отменить",
+    "redo": "Повторить",
+    "bold": "Жирный",
+    "italic": "Курсив",
+    "underline": "Подчёркнутый",
+    "strike": "Зачёркнутый",
+    "numList": "Нумерованный список",
+    "bulletList": "Маркированный список",
+    "checkList": "Список To-Do",
+    "inlineCode": "Код",
+    "quote": "Цитата",
+    "header": "Заголовок",
+    "highlight": "Выделение"
+  },
+  "tooltip": {
+    "lightMode": "Переключиться в светлую тему",
+    "darkMode": "Переключиться в тёмную тему"
+  },
+  "contactsPage": {
+    "title": "Контакты",
+    "whatsHappening": "Что происходит на этой неделе?",
+    "addContact": "Новый контакт",
+    "editContact": "Редактировать"
+  },
+  "button": {
+    "OK": "OK",
+    "Cancel": "Отмена",
+    "signIn": "Войти",
+    "signOut": "Выйти",
+    "complete": "Завершить",
+    "save": "Сохранить"
+  },
+  "label": {
+    "welcome": "Добро пожаловать!",
+    "firstName": "Имя",
+    "middleName": "Отчество",
+    "lastName": "Фамилия",
+    "stepX": "Этап {X}"
+  },
+  "oAuth": {
+    "err": {
+      "failedTitle": "Ошибка подключения к аккаунту.",
+      "failedMsg": "Убедитесь, что вы завершили вход в своём браузере."
+    },
+    "google": {
+      "title": "Вход через Google",
+      "instruction1": "Чтобы импортировать ваши Google Контакты, вам нужно будет авторизовать приложение через браузер.",
+      "instruction2": "Скопируйте этот код в буфер обмена (нажав кнопку или выделив текст):",
+      "instruction3": "Пройдите по ссылке и введите этот код:",
+      "instruction4": "Нажмите на кнопку, когда завершите вход:"
+    }
+  },
+  "settings": {
+    "title": "Настройки",
+    "menu": {
+      "appearance": "Внешний вид",
+      "language": "Язык",
+      "open": "Открыть настройки"
+    },
+    "appearance": {
+      "lightLabel": "Светлая тема",
+      "darkLabel": "Тёмная тема"
+    }
+  },
+  "grid": {
     "settings": {
-      "title": "Настройки",
-      "menu": {
-        "appearance": "Внешний вид",
-        "language": "Язык",
-        "open": "Открыть настройки"
-      },
-      "appearance": {
-        "lightLabel": "Светлая тема",
-        "darkLabel": "Тёмная тема"
-      }
-    },
-    "grid": {
-      "settings": {
-        "filter": "Фильтр",
-        "sortBy": "Сортировать",
-        "Properties": "Свойства"
-      },
-      "field": {
-        "hide": "Скрыть",
-        "insertLeft": "Вставить слева",
-        "insertRight": "Вставить справа",
-        "duplicate": "Дублировать",
-        "delete": "Удалить",
-        "textFieldName": "Текст",
-        "checkboxFieldName": "Checkbox",
-        "dateFieldName": "Дата",
-        "numberFieldName": "Число",
-        "singleSelectFieldName": "Выбор",
-        "multiSelectFieldName": "Выбор многих",
-        "urlFieldName": "URL",
-        "numberFormat": " Формат числа",
-        "dateFormat": " Формат даты",
-        "includeTime": " Время",
-        "dateFormatFriendly": "День Месяц, Год",
-        "dateFormatISO": "Год-Месяц-День",
-        "dateFormatLocal": "Год/Месяц/День",
-        "dateFormatUS": "Год/Месяц/День",
-        "timeFormat": " Форматировать время",
-        "invalidTimeFormat": "Неверный формат",
-        "timeFormatTwelveHour": "12 часов",
-        "timeFormatTwentyFourHour": "24 часа",
-        "addSelectOption": "Добавить вариант",
-        "optionTitle": "Варианты",
-        "addOption": "Добавить",
-        "editProperty": "Редактировать свойство"
-      },
-      "row": {
-        "duplicate": "Дублировать",
-        "delete": "Удалить",
-        "textPlaceholder": "Пусто",
-        "copyProperty": "Свойство скопировано"
-      },
-      "selectOption": {
-        "create": "Создать",
-        "purpleColor": "Фиолетовый",
-        "pinkColor": "Розовый",
-        "lightPinkColor": "Светло-розовый",
-        "orangeColor": "Оранжевый",
-        "yellowColor": "Желтый",
-        "limeColor": "Ярко-зелёный",
-        "greenColor": "Зелёный",
-        "aquaColor": "Морской волны",
-        "blueColor": "Синий",
-        "deleteTag": "Удалить вариант",
-        "colorPannelTitle": "Цвета",
-        "pannelTitle": "Выберите или создайте вариант",
-        "searchOption": "Поиск"
-      },
-      "date": {
-        "timeHintTextInTwelveHour": "12:00 AM",
-        "timeHintTextInTwentyFourHour": "12:00"
-      }
-    },
-    "sideBar": {
-      "openSidebar": "Open sidebar",
-      "closeSidebar": "Close sidebar"
+      "filter": "Фильтр",
+      "sortBy": "Сортировать",
+      "Properties": "Свойства"
+    },
+    "field": {
+      "hide": "Скрыть",
+      "insertLeft": "Вставить слева",
+      "insertRight": "Вставить справа",
+      "duplicate": "Дублировать",
+      "delete": "Удалить",
+      "textFieldName": "Текст",
+      "checkboxFieldName": "Checkbox",
+      "dateFieldName": "Дата",
+      "numberFieldName": "Число",
+      "singleSelectFieldName": "Выбор",
+      "multiSelectFieldName": "Выбор многих",
+      "urlFieldName": "URL",
+      "numberFormat": " Формат числа",
+      "dateFormat": " Формат даты",
+      "includeTime": " Время",
+      "dateFormatFriendly": "День Месяц, Год",
+      "dateFormatISO": "Год-Месяц-День",
+      "dateFormatLocal": "Год/Месяц/День",
+      "dateFormatUS": "Год/Месяц/День",
+      "timeFormat": " Форматировать время",
+      "invalidTimeFormat": "Неверный формат",
+      "timeFormatTwelveHour": "12 часов",
+      "timeFormatTwentyFourHour": "24 часа",
+      "addSelectOption": "Добавить вариант",
+      "optionTitle": "Варианты",
+      "addOption": "Добавить",
+      "editProperty": "Редактировать свойство"
+    },
+    "row": {
+      "duplicate": "Дублировать",
+      "delete": "Удалить",
+      "textPlaceholder": "Пусто",
+      "copyProperty": "Свойство скопировано"
+    },
+    "selectOption": {
+      "create": "Создать",
+      "purpleColor": "Фиолетовый",
+      "pinkColor": "Розовый",
+      "lightPinkColor": "Светло-розовый",
+      "orangeColor": "Оранжевый",
+      "yellowColor": "Желтый",
+      "limeColor": "Ярко-зелёный",
+      "greenColor": "Зелёный",
+      "aquaColor": "Морской волны",
+      "blueColor": "Синий",
+      "deleteTag": "Удалить вариант",
+      "colorPanelTitle": "Цвета",
+      "panelTitle": "Выберите или создайте вариант",
+      "searchOption": "Поиск"
+    },
+    "date": {
+      "timeHintTextInTwelveHour": "12:00 AM",
+      "timeHintTextInTwentyFourHour": "12:00"
     }
+  },
+  "sideBar": {
+    "openSidebar": "Open sidebar",
+    "closeSidebar": "Close sidebar"
   }
-  
+}

+ 2 - 2
frontend/app_flowy/assets/translations/zh-CN.json

@@ -206,8 +206,8 @@
       "aquaColor": "水蓝色",
       "blueColor": "蓝色",
       "deleteTag": "删除标签",
-      "colorPannelTitle": "颜色",
-      "pannelTitle": "选择或新建一个标签",
+      "colorPanelTitle": "颜色",
+      "panelTitle": "选择或新建一个标签",
       "searchOption": "搜索标签"
     },
     "menuName": "网格"

+ 2 - 2
frontend/app_flowy/assets/translations/zh-TW.json

@@ -202,8 +202,8 @@
       "aquaColor": "水藍色",
       "blueColor": "藍色",
       "deleteTag": "刪除標籤",
-      "colorPannelTitle": "顏色",
-      "pannelTitle": "搜尋或建立選項",
+      "colorPanelTitle": "顏色",
+      "panelTitle": "搜尋或建立選項",
       "searchOption": "搜尋選項"
     },
     "menuName": "網格"

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

@@ -87,7 +87,7 @@ class _BoardSelectOptionCellState extends State<BoardSelectOptionCell> {
       SelectOptionCellEditor.editorPanelWidth,
       300,
     ));
-    return AppFlowyStylePopover(
+    return AppFlowyPopover(
       controller: _popover,
       constraints: constraints,
       direction: PopoverDirection.bottomWithLeftAligned,

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

@@ -1,15 +1,17 @@
 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:flowy_infra/image.dart';
 import 'package:flowy_infra/theme.dart';
-import 'package:flowy_infra_ui/flowy_infra_ui_web.dart';
+import 'package:flowy_infra_ui/flowy_infra_ui.dart';
 import 'package:flutter/foundation.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 import 'board_cell.dart';
 import 'card_cell_builder.dart';
-import 'card_container.dart';
+import 'container/accessory.dart';
+import 'container/card_container.dart';
 
 class BoardCard extends StatefulWidget {
   final String gridId;
@@ -38,6 +40,8 @@ class BoardCard extends StatefulWidget {
 class _BoardCardState extends State<BoardCard> {
   late BoardCardBloc _cardBloc;
   late EditableRowNotifier rowNotifier;
+  late PopoverController popoverController;
+  AccessoryType? accessoryType;
 
   @override
   void initState() {
@@ -54,6 +58,7 @@ class _BoardCardState extends State<BoardCard> {
       _cardBloc.add(BoardCardEvent.setIsEditing(rowNotifier.isEditing.value));
     });
 
+    popoverController = PopoverController();
     super.initState();
   }
 
@@ -64,32 +69,42 @@ class _BoardCardState extends State<BoardCard> {
       child: BlocBuilder<BoardCardBloc, BoardCardState>(
         buildWhen: (previous, current) {
           // Rebuild when:
-          // 1.If the lenght of the cells is not the same
+          // 1.If the length of the cells is not the same
           // 2.isEditing changed
           if (previous.cells.length != current.cells.length ||
               previous.isEditing != current.isEditing) {
             return true;
           }
 
-          // 3.Compare the content of the cells. The cells consisits of
+          // 3.Compare the content of the cells. The cells consists of
           // list of [BoardCellEquatable] that extends the [Equatable].
           return !listEquals(previous.cells, current.cells);
         },
         builder: (context, state) {
-          return BoardCardContainer(
-            buildAccessoryWhen: () => state.isEditing == false,
-            accessoryBuilder: (context) {
-              return [
-                _CardEditOption(rowNotifier: rowNotifier),
-                const _CardMoreOption(),
-              ];
-            },
-            onTap: (context) => widget.openCard(context),
-            child: _CellColumn(
-              groupId: widget.groupId,
-              rowNotifier: rowNotifier,
-              cellBuilder: widget.cellBuilder,
-              cells: state.cells,
+          return AppFlowyPopover(
+            controller: popoverController,
+            constraints: BoxConstraints.loose(const Size(140, 200)),
+            direction: PopoverDirection.rightWithCenterAligned,
+            popupBuilder: (popoverContext) => _handlePopoverBuilder(
+              context,
+              popoverContext,
+            ),
+            child: BoardCardContainer(
+              buildAccessoryWhen: () => state.isEditing == false,
+              accessoryBuilder: (context) {
+                return [
+                  _CardEditOption(rowNotifier: rowNotifier),
+                  _CardMoreOption(),
+                ];
+              },
+              openAccessory: _handleOpenAccessory,
+              openCard: (context) => widget.openCard(context),
+              child: _CellColumn(
+                groupId: widget.groupId,
+                rowNotifier: rowNotifier,
+                cellBuilder: widget.cellBuilder,
+                cells: state.cells,
+              ),
             ),
           );
         },
@@ -97,6 +112,30 @@ class _BoardCardState extends State<BoardCard> {
     );
   }
 
+  void _handleOpenAccessory(AccessoryType newAccessoryType) {
+    accessoryType = newAccessoryType;
+    switch (newAccessoryType) {
+      case AccessoryType.edit:
+        break;
+      case AccessoryType.more:
+        popoverController.show();
+        break;
+    }
+  }
+
+  Widget _handlePopoverBuilder(
+    BuildContext context,
+    BuildContext popoverContext,
+  ) {
+    switch (accessoryType!) {
+      case AccessoryType.edit:
+        throw UnimplementedError();
+      case AccessoryType.more:
+        return GridRowActionSheet(
+            rowData: context.read<BoardCardBloc>().rowInfo());
+    }
+  }
+
   @override
   Future<void> dispose() async {
     rowNotifier.dispose();
@@ -163,7 +202,7 @@ class _CellColumn extends StatelessWidget {
 }
 
 class _CardMoreOption extends StatelessWidget with CardAccessory {
-  const _CardMoreOption({Key? key}) : super(key: key);
+  _CardMoreOption({Key? key}) : super(key: key);
 
   @override
   Widget build(BuildContext context) {
@@ -175,11 +214,7 @@ class _CardMoreOption extends StatelessWidget with CardAccessory {
   }
 
   @override
-  void onTap(BuildContext context) {
-    GridRowActionSheet(
-      rowData: context.read<BoardCardBloc>().rowInfo(),
-    ).show(context, direction: AnchorDirection.bottomWithCenterAligned);
-  }
+  AccessoryType get type => AccessoryType.more;
 }
 
 class _CardEditOption extends StatelessWidget with CardAccessory {
@@ -201,7 +236,8 @@ class _CardEditOption extends StatelessWidget with CardAccessory {
   }
 
   @override
-  void onTap(BuildContext context) {
-    rowNotifier.becomeFirstResponder();
-  }
+  void onTap(BuildContext context) => rowNotifier.becomeFirstResponder();
+
+  @override
+  AccessoryType get type => AccessoryType.edit;
 }

+ 74 - 0
frontend/app_flowy/lib/plugins/board/presentation/card/container/accessory.dart

@@ -0,0 +1,74 @@
+import 'package:flowy_infra/theme.dart';
+import 'package:flowy_infra_ui/style_widget/hover.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+
+enum AccessoryType {
+  edit,
+  more,
+}
+
+abstract class CardAccessory implements Widget {
+  AccessoryType get type;
+  void onTap(BuildContext context) {}
+}
+
+typedef CardAccessoryBuilder = List<CardAccessory> Function(
+  BuildContext buildContext,
+);
+
+class CardAccessoryContainer extends StatelessWidget {
+  final void Function(AccessoryType) onTapAccessory;
+  final List<CardAccessory> accessories;
+  const CardAccessoryContainer({
+    required this.accessories,
+    required this.onTapAccessory,
+    Key? key,
+  }) : super(key: key);
+
+  @override
+  Widget build(BuildContext context) {
+    final theme = context.read<AppTheme>();
+    final children = accessories.map((accessory) {
+      return GestureDetector(
+        behavior: HitTestBehavior.opaque,
+        onTap: () {
+          accessory.onTap(context);
+          onTapAccessory(accessory.type);
+        },
+        child: _wrapHover(theme, accessory),
+      );
+    }).toList();
+    return _wrapDecoration(context, Row(children: children));
+  }
+
+  FlowyHover _wrapHover(AppTheme theme, CardAccessory accessory) {
+    return FlowyHover(
+      style: HoverStyle(
+        hoverColor: theme.hover,
+        backgroundColor: theme.surface,
+        borderRadius: BorderRadius.zero,
+      ),
+      builder: (_, onHover) => SizedBox(
+        width: 24,
+        height: 24,
+        child: accessory,
+      ),
+    );
+  }
+
+  Widget _wrapDecoration(BuildContext context, Widget child) {
+    final theme = context.read<AppTheme>();
+    final borderSide = BorderSide(color: theme.shader6, width: 1.0);
+    final decoration = BoxDecoration(
+      color: Colors.transparent,
+      border: Border.fromBorderSide(borderSide),
+      borderRadius: const BorderRadius.all(Radius.circular(4)),
+    );
+    return Container(
+      clipBehavior: Clip.hardEdge,
+      decoration: decoration,
+      child: child,
+    );
+  }
+}

+ 21 - 68
frontend/app_flowy/lib/plugins/board/presentation/card/card_container.dart → frontend/app_flowy/lib/plugins/board/presentation/card/container/card_container.dart

@@ -1,17 +1,19 @@
-import 'package:flowy_infra/theme.dart';
-import 'package:flowy_infra_ui/style_widget/hover.dart';
 import 'package:flutter/material.dart';
 import 'package:provider/provider.dart';
 import 'package:styled_widget/styled_widget.dart';
 
+import 'accessory.dart';
+
 class BoardCardContainer extends StatelessWidget {
   final Widget child;
   final CardAccessoryBuilder? accessoryBuilder;
   final bool Function()? buildAccessoryWhen;
-  final void Function(BuildContext) onTap;
+  final void Function(BuildContext) openCard;
+  final void Function(AccessoryType) openAccessory;
   const BoardCardContainer({
     required this.child,
-    required this.onTap,
+    required this.openCard,
+    required this.openAccessory,
     this.accessoryBuilder,
     this.buildAccessoryWhen,
     Key? key,
@@ -34,13 +36,14 @@ class BoardCardContainer extends StatelessWidget {
             if (accessories.isNotEmpty) {
               container = _CardEnterRegion(
                 accessories: accessories,
+                onTapAccessory: openAccessory,
                 child: container,
               );
             }
           }
 
           return GestureDetector(
-            onTap: () => onTap(context),
+            onTap: () => openCard(context),
             child: Padding(
               padding: const EdgeInsets.all(8),
               child: ConstrainedBox(
@@ -55,69 +58,16 @@ class BoardCardContainer extends StatelessWidget {
   }
 }
 
-abstract class CardAccessory implements Widget {
-  void onTap(BuildContext context);
-}
-
-typedef CardAccessoryBuilder = List<CardAccessory> Function(
-  BuildContext buildContext,
-);
-
-class CardAccessoryContainer extends StatelessWidget {
-  final List<CardAccessory> accessories;
-  const CardAccessoryContainer({required this.accessories, Key? key})
-      : super(key: key);
-
-  @override
-  Widget build(BuildContext context) {
-    final theme = context.read<AppTheme>();
-    final children = accessories.map((accessory) {
-      return GestureDetector(
-        behavior: HitTestBehavior.opaque,
-        onTap: () => accessory.onTap(context),
-        child: _wrapHover(theme, accessory),
-      );
-    }).toList();
-    return _wrapDecoration(context, Row(children: children));
-  }
-
-  FlowyHover _wrapHover(AppTheme theme, CardAccessory accessory) {
-    return FlowyHover(
-      style: HoverStyle(
-        hoverColor: theme.hover,
-        backgroundColor: theme.surface,
-        borderRadius: BorderRadius.zero,
-      ),
-      builder: (_, onHover) => SizedBox(
-        width: 24,
-        height: 24,
-        child: accessory,
-      ),
-    );
-  }
-
-  Widget _wrapDecoration(BuildContext context, Widget child) {
-    final theme = context.read<AppTheme>();
-    final borderSide = BorderSide(color: theme.shader6, width: 1.0);
-    final decoration = BoxDecoration(
-      color: Colors.transparent,
-      border: Border.fromBorderSide(borderSide),
-      borderRadius: const BorderRadius.all(Radius.circular(4)),
-    );
-    return Container(
-      clipBehavior: Clip.hardEdge,
-      decoration: decoration,
-      child: child,
-    );
-  }
-}
-
 class _CardEnterRegion extends StatelessWidget {
   final Widget child;
   final List<CardAccessory> accessories;
-  const _CardEnterRegion(
-      {required this.child, required this.accessories, Key? key})
-      : super(key: key);
+  final void Function(AccessoryType) onTapAccessory;
+  const _CardEnterRegion({
+    required this.child,
+    required this.accessories,
+    required this.onTapAccessory,
+    Key? key,
+  }) : super(key: key);
 
   @override
   Widget build(BuildContext context) {
@@ -126,9 +76,12 @@ class _CardEnterRegion extends StatelessWidget {
       builder: (context, onEnter, _) {
         List<Widget> children = [child];
         if (onEnter) {
-          children.add(CardAccessoryContainer(
-            accessories: accessories,
-          ).positioned(right: 0));
+          children.add(
+            CardAccessoryContainer(
+              accessories: accessories,
+              onTapAccessory: onTapAccessory,
+            ).positioned(right: 0),
+          );
         }
 
         return MouseRegion(

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

@@ -50,7 +50,6 @@ class BoardSettingList extends StatelessWidget {
             previous.selectedAction != current.selectedAction,
         listener: (context, state) {
           state.selectedAction.foldLeft(null, (_, action) {
-            // FlowyOverlay.of(context).remove(identifier());
             onAction(action, settingContext);
           });
         },

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

@@ -62,7 +62,7 @@ class _SettingButtonState extends State<_SettingButton> {
   @override
   Widget build(BuildContext context) {
     final theme = context.read<AppTheme>();
-    return AppFlowyStylePopover(
+    return AppFlowyPopover(
       controller: popoverController,
       constraints: BoxConstraints.loose(const Size(260, 400)),
       triggerActions: PopoverTriggerActionFlags.click,

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

@@ -63,7 +63,7 @@ class _DateCellState extends GridCellState<GridDateCell> {
       value: _cellBloc,
       child: BlocBuilder<DateCellBloc, DateCellState>(
         builder: (context, state) {
-          return AppFlowyStylePopover(
+          return AppFlowyPopover(
             controller: _popover,
             offset: const Offset(0, 20),
             direction: PopoverDirection.bottomWithLeftAligned,

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

@@ -299,7 +299,7 @@ class _DateTypeOptionButton extends StatelessWidget {
     return BlocSelector<DateCalBloc, DateCalState, DateTypeOptionPB>(
       selector: (state) => state.dateTypeOptionPB,
       builder: (context, dateTypeOptionPB) {
-        return AppFlowyStylePopover(
+        return AppFlowyPopover(
           triggerActions:
               PopoverTriggerActionFlags.hover | PopoverTriggerActionFlags.click,
           offset: const Offset(20, 0),

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

@@ -181,7 +181,7 @@ class _SelectOptionWrapState extends State<SelectOptionWrap> {
       SelectOptionCellEditor.editorPanelWidth,
       300,
     ));
-    return AppFlowyStylePopover(
+    return AppFlowyPopover(
       controller: _popover,
       constraints: constraints,
       direction: PopoverDirection.bottomWithLeftAligned,

+ 49 - 16
frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/select_option_cell/select_option_editor.dart

@@ -25,31 +25,47 @@ import 'text_field.dart';
 
 const double _editorPanelWidth = 300;
 
-class SelectOptionCellEditor extends StatelessWidget {
+class SelectOptionCellEditor extends StatefulWidget {
   final GridSelectOptionCellController cellController;
-
   static double editorPanelWidth = 300;
 
   const SelectOptionCellEditor({required this.cellController, Key? key})
       : super(key: key);
 
+  @override
+  State<SelectOptionCellEditor> createState() => _SelectOptionCellEditorState();
+}
+
+class _SelectOptionCellEditorState extends State<SelectOptionCellEditor> {
+  late PopoverMutex popoverMutex;
+
+  @override
+  void initState() {
+    popoverMutex = PopoverMutex();
+    super.initState();
+  }
+
   @override
   Widget build(BuildContext context) {
     return BlocProvider(
       create: (context) => SelectOptionCellEditorBloc(
-        cellController: cellController,
+        cellController: widget.cellController,
       )..add(const SelectOptionEditorEvent.initial()),
       child: BlocBuilder<SelectOptionCellEditorBloc, SelectOptionEditorState>(
         builder: (context, state) {
           return CustomScrollView(
             shrinkWrap: true,
             slivers: [
-              SliverToBoxAdapter(child: _TextField()),
+              SliverToBoxAdapter(
+                child: _TextField(popoverMutex: popoverMutex),
+              ),
               const SliverToBoxAdapter(child: VSpace(6)),
               const SliverToBoxAdapter(child: TypeOptionSeparator()),
               const SliverToBoxAdapter(child: VSpace(6)),
               const SliverToBoxAdapter(child: _Title()),
-              const SliverToBoxAdapter(child: _OptionList()),
+              SliverToBoxAdapter(
+                child: _OptionList(popoverMutex: popoverMutex),
+              ),
             ],
           );
         },
@@ -59,7 +75,11 @@ class SelectOptionCellEditor extends StatelessWidget {
 }
 
 class _OptionList extends StatelessWidget {
-  const _OptionList({Key? key}) : super(key: key);
+  final PopoverMutex popoverMutex;
+  const _OptionList({
+    required this.popoverMutex,
+    Key? key,
+  }) : super(key: key);
 
   @override
   Widget build(BuildContext context) {
@@ -68,7 +88,10 @@ class _OptionList extends StatelessWidget {
         List<Widget> cells = [];
         cells.addAll(state.options.map((option) {
           return _SelectOptionCell(
-              option, state.selectedOptions.contains(option));
+            option: option,
+            isSelected: state.selectedOptions.contains(option),
+            popoverMutex: popoverMutex,
+          );
         }).toList());
 
         state.createOption.fold(
@@ -101,9 +124,13 @@ class _OptionList extends StatelessWidget {
 }
 
 class _TextField extends StatelessWidget {
+  final PopoverMutex popoverMutex;
   final TextfieldTagsController _tagController = TextfieldTagsController();
 
-  _TextField({Key? key}) : super(key: key);
+  _TextField({
+    required this.popoverMutex,
+    Key? key,
+  }) : super(key: key);
 
   @override
   Widget build(BuildContext context) {
@@ -121,8 +148,7 @@ class _TextField extends StatelessWidget {
             selectedOptionMap: optionMap,
             distanceToText: _editorPanelWidth * 0.7,
             tagController: _tagController,
-            onClick: () => FlowyOverlay.of(context)
-                .remove(SelectOptionTypeOptionEditor.identifier),
+            onClick: () => popoverMutex.close(),
             newText: (text) {
               context
                   .read<SelectOptionCellEditorBloc>()
@@ -151,7 +177,7 @@ class _Title extends StatelessWidget {
       child: Padding(
         padding: const EdgeInsets.symmetric(horizontal: 6),
         child: FlowyText.medium(
-          LocaleKeys.grid_selectOption_pannelTitle.tr(),
+          LocaleKeys.grid_selectOption_panelTitle.tr(),
           fontSize: 12,
           color: theme.shader3,
         ),
@@ -189,9 +215,14 @@ class _CreateOptionCell extends StatelessWidget {
 
 class _SelectOptionCell extends StatefulWidget {
   final SelectOptionPB option;
+  final PopoverMutex popoverMutex;
   final bool isSelected;
-  const _SelectOptionCell(this.option, this.isSelected, {Key? key})
-      : super(key: key);
+  const _SelectOptionCell({
+    required this.option,
+    required this.isSelected,
+    required this.popoverMutex,
+    Key? key,
+  }) : super(key: key);
 
   @override
   State<_SelectOptionCell> createState() => _SelectOptionCellState();
@@ -209,10 +240,11 @@ class _SelectOptionCellState extends State<_SelectOptionCell> {
   @override
   Widget build(BuildContext context) {
     final theme = context.watch<AppTheme>();
-    return AppFlowyStylePopover(
+    return AppFlowyPopover(
       controller: _popoverController,
       offset: const Offset(20, 0),
       constraints: BoxConstraints.loose(const Size(200, 300)),
+      mutex: widget.popoverMutex,
       child: SizedBox(
         height: GridSize.typeOptionItemHeight,
         child: Row(
@@ -257,8 +289,9 @@ class _SelectOptionCellState extends State<_SelectOptionCell> {
                 .read<SelectOptionCellEditorBloc>()
                 .add(SelectOptionEditorEvent.updateOption(updatedOption));
           },
-          key: ValueKey(widget.option
-              .id), // Use ValueKey to refresh the UI, otherwise, it will remain the old value.
+          key: ValueKey(
+            widget.option.id,
+          ), // Use ValueKey to refresh the UI, otherwise, it will remain the old value.
         );
       },
     );

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

@@ -130,7 +130,7 @@ class _GridURLCellState extends GridCellState<GridURLCell> {
             ),
           );
 
-          return AppFlowyStylePopover(
+          return AppFlowyPopover(
             controller: _popoverController,
             constraints: BoxConstraints.loose(const Size(300, 160)),
             direction: PopoverDirection.bottomWithLeftAligned,
@@ -216,7 +216,7 @@ class _EditURLAccessoryState extends State<_EditURLAccessory>
   @override
   Widget build(BuildContext context) {
     final theme = context.watch<AppTheme>();
-    return AppFlowyStylePopover(
+    return AppFlowyPopover(
       constraints: BoxConstraints.loose(const Size(300, 160)),
       controller: _popoverController,
       direction: PopoverDirection.bottomWithLeftAligned,

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

@@ -30,7 +30,7 @@ class GridFieldCell extends StatelessWidget {
       },
       child: BlocBuilder<FieldCellBloc, FieldCellState>(
         builder: (context, state) {
-          final button = AppFlowyStylePopover(
+          final button = AppFlowyPopover(
             constraints: BoxConstraints.loose(const Size(240, 840)),
             direction: PopoverDirection.bottomWithLeftAligned,
             triggerActions: PopoverTriggerActionFlags.click,

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

@@ -167,7 +167,7 @@ class _FieldNameTextFieldState extends State<_FieldNameTextField> {
       },
       child: BlocBuilder<FieldEditorBloc, FieldEditorState>(
         builder: (context, state) {
-          listenOnPopoverChhanged(context);
+          listenOnPopoverChanged(context);
 
           return RoundedInputField(
             height: 36,
@@ -191,7 +191,7 @@ class _FieldNameTextFieldState extends State<_FieldNameTextField> {
     );
   }
 
-  void listenOnPopoverChhanged(BuildContext context) {
+  void listenOnPopoverChanged(BuildContext context) {
     if (_popoverCallback != null) {
       widget.popoverMutex.removePopoverStateListener(_popoverCallback!);
     }
@@ -243,7 +243,7 @@ class _DeleteFieldButton extends StatelessWidget {
   }
 
   Widget _wrapPopover(Widget widget) {
-    return AppFlowyStylePopover(
+    return AppFlowyPopover(
       triggerActions: PopoverTriggerActionFlags.click,
       constraints: BoxConstraints.loose(const Size(400, 240)),
       mutex: popoverMutex,

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

@@ -64,7 +64,7 @@ class FieldTypeOptionEditor extends StatelessWidget {
     final theme = context.watch<AppTheme>();
     return SizedBox(
       height: GridSize.typeOptionItemHeight,
-      child: AppFlowyStylePopover(
+      child: AppFlowyPopover(
         constraints: BoxConstraints.loose(const Size(460, 440)),
         triggerActions:
             PopoverTriggerActionFlags.click | PopoverTriggerActionFlags.hover,

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

@@ -176,7 +176,7 @@ class CreateFieldButton extends StatelessWidget {
   Widget build(BuildContext context) {
     final theme = context.watch<AppTheme>();
 
-    return AppFlowyStylePopover(
+    return AppFlowyPopover(
       triggerActions: PopoverTriggerActionFlags.click,
       direction: PopoverDirection.bottomWithRightAligned,
       constraints: BoxConstraints.loose(const Size(240, 200)),

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

@@ -62,7 +62,7 @@ class DateTypeOptionWidget extends TypeOptionWidget {
   }
 
   Widget _renderDateFormatButton(BuildContext context, DateFormat dataFormat) {
-    return AppFlowyStylePopover(
+    return AppFlowyPopover(
       mutex: popoverMutex,
       triggerActions:
           PopoverTriggerActionFlags.hover | PopoverTriggerActionFlags.click,
@@ -84,7 +84,7 @@ class DateTypeOptionWidget extends TypeOptionWidget {
   }
 
   Widget _renderTimeFormatButton(BuildContext context, TimeFormat timeFormat) {
-    return AppFlowyStylePopover(
+    return AppFlowyPopover(
       mutex: popoverMutex,
       triggerActions:
           PopoverTriggerActionFlags.hover | PopoverTriggerActionFlags.click,

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

@@ -55,7 +55,7 @@ class NumberTypeOptionWidget extends TypeOptionWidget {
           listener: (context, state) =>
               typeOptionContext.typeOption = state.typeOption,
           builder: (context, state) {
-            return AppFlowyStylePopover(
+            return AppFlowyPopover(
               mutex: popoverMutex,
               triggerActions: PopoverTriggerActionFlags.hover |
                   PopoverTriggerActionFlags.click,

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

@@ -180,7 +180,7 @@ class _OptionCellState extends State<_OptionCell> {
   Widget build(BuildContext context) {
     final theme = context.watch<AppTheme>();
 
-    return AppFlowyStylePopover(
+    return AppFlowyPopover(
       controller: _popoverController,
       mutex: widget.popoverMutex,
       offset: const Offset(20, 0),

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

@@ -139,7 +139,7 @@ class SelectOptionColorList extends StatelessWidget {
           child: SizedBox(
             height: GridSize.typeOptionItemHeight,
             child: FlowyText.medium(
-              LocaleKeys.grid_selectOption_colorPannelTitle.tr(),
+              LocaleKeys.grid_selectOption_colorPanelTitle.tr(),
               fontSize: 12,
               textAlign: TextAlign.left,
             ),

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

@@ -1,8 +1,10 @@
 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:flowy_infra/image.dart';
 import 'package:flowy_infra/theme.dart';
+import 'package:flowy_infra_ui/flowy_infra_ui.dart';
 import 'package:flowy_infra_ui/style_widget/icon_button.dart';
 import 'package:flutter/foundation.dart';
 import 'package:flutter/material.dart';
@@ -56,20 +58,21 @@ class _GridRowWidgetState extends State<GridRowWidget> {
         child: BlocBuilder<RowBloc, RowState>(
           buildWhen: (p, c) => p.rowInfo.rowPB.height != c.rowInfo.rowPB.height,
           builder: (context, state) {
-            final children = [
-              const _RowLeading(),
-              Expanded(
-                child: RowContent(
-                  builder: widget.cellBuilder,
-                  onExpand: () => widget.openDetailPage(
-                    context,
-                    widget.cellBuilder,
-                  ),
+            final content = Expanded(
+              child: RowContent(
+                builder: widget.cellBuilder,
+                onExpand: () => widget.openDetailPage(
+                  context,
+                  widget.cellBuilder,
                 ),
               ),
+            );
+
+            return Row(children: [
+              const _RowLeading(),
+              content,
               const _RowTrailing(),
-            ];
-            return Row(children: children);
+            ]);
           },
         ),
       ),
@@ -83,26 +86,51 @@ class _GridRowWidgetState extends State<GridRowWidget> {
   }
 }
 
-class _RowLeading extends StatelessWidget {
+class _RowLeading extends StatefulWidget {
   const _RowLeading({Key? key}) : super(key: key);
 
+  @override
+  State<_RowLeading> createState() => _RowLeadingState();
+}
+
+class _RowLeadingState extends State<_RowLeading> {
+  late PopoverController popoverController;
+
+  @override
+  void initState() {
+    popoverController = PopoverController();
+    super.initState();
+  }
+
   @override
   Widget build(BuildContext context) {
-    return Consumer<RegionStateNotifier>(
-      builder: (context, state, _) {
-        return SizedBox(
-            width: GridSize.leadingHeaderPadding,
-            child: state.onEnter ? _activeWidget() : null);
+    return AppFlowyPopover(
+      controller: popoverController,
+      constraints: BoxConstraints.loose(const Size(140, 200)),
+      direction: PopoverDirection.rightWithCenterAligned,
+      popupBuilder: (BuildContext popoverContext) {
+        return GridRowActionSheet(
+            rowData: context.read<RowBloc>().state.rowInfo);
       },
+      child: Consumer<RegionStateNotifier>(
+        builder: (context, state, _) {
+          return SizedBox(
+            width: GridSize.leadingHeaderPadding,
+            child: state.onEnter ? _activeWidget() : null,
+          );
+        },
+      ),
     );
   }
 
   Widget _activeWidget() {
     return Row(
       mainAxisAlignment: MainAxisAlignment.center,
-      children: const [
-        _InsertRowButton(),
-        _DeleteRowButton(),
+      children: [
+        const _InsertButton(),
+        _MenuButton(openMenu: () {
+          popoverController.show();
+        }),
       ],
     );
   }
@@ -117,8 +145,8 @@ class _RowTrailing extends StatelessWidget {
   }
 }
 
-class _InsertRowButton extends StatelessWidget {
-  const _InsertRowButton({Key? key}) : super(key: key);
+class _InsertButton extends StatelessWidget {
+  const _InsertButton({Key? key}) : super(key: key);
 
   @override
   Widget build(BuildContext context) {
@@ -128,17 +156,29 @@ class _InsertRowButton extends StatelessWidget {
       hoverColor: theme.hover,
       width: 20,
       height: 30,
-      onPressed: () => context.read<RowBloc>().add(
-            const RowEvent.createRow(),
-          ),
+      onPressed: () => context.read<RowBloc>().add(const RowEvent.createRow()),
       iconPadding: const EdgeInsets.all(3),
       icon: svgWidget("home/add"),
     );
   }
 }
 
-class _DeleteRowButton extends StatelessWidget {
-  const _DeleteRowButton({Key? key}) : super(key: key);
+class _MenuButton extends StatefulWidget {
+  final VoidCallback openMenu;
+  const _MenuButton({
+    required this.openMenu,
+    Key? key,
+  }) : super(key: key);
+
+  @override
+  State<_MenuButton> createState() => _MenuButtonState();
+}
+
+class _MenuButtonState extends State<_MenuButton> {
+  @override
+  void initState() {
+    super.initState();
+  }
 
   @override
   Widget build(BuildContext context) {
@@ -148,9 +188,7 @@ class _DeleteRowButton extends StatelessWidget {
       hoverColor: theme.hover,
       width: 20,
       height: 30,
-      onPressed: () => GridRowActionSheet(
-        rowData: context.read<RowBloc>().state.rowInfo,
-      ).show(context),
+      onPressed: () => widget.openMenu(),
       iconPadding: const EdgeInsets.all(3),
       icon: svgWidget("editor/details"),
     );

+ 2 - 33
frontend/app_flowy/lib/plugins/grid/presentation/widgets/row/row_action_sheet.dart

@@ -3,7 +3,6 @@ import 'package:easy_localization/easy_localization.dart';
 import 'package:app_flowy/generated/locale_keys.g.dart';
 import 'package:flowy_infra/image.dart';
 import 'package:flowy_infra/theme.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/scrolling/styled_list.dart';
 import 'package:flowy_infra_ui/style_widget/text.dart';
@@ -27,10 +26,7 @@ class GridRowActionSheet extends StatelessWidget {
           final cells = _RowAction.values
               .where((value) => value.enable())
               .map(
-                (action) => _RowActionCell(
-                  action: action,
-                  onDismissed: () => remove(context),
-                ),
+                (action) => _RowActionCell(action: action),
               )
               .toList();
 
@@ -52,37 +48,11 @@ class GridRowActionSheet extends StatelessWidget {
       ),
     );
   }
-
-  void show(
-    BuildContext overlayContext, {
-    AnchorDirection direction = AnchorDirection.leftWithCenterAligned,
-  }) {
-    FlowyOverlay.of(overlayContext).insertWithAnchor(
-      widget: OverlayContainer(
-        constraints: BoxConstraints.loose(const Size(140, 200)),
-        child: this,
-      ),
-      identifier: GridRowActionSheet.identifier(),
-      anchorContext: overlayContext,
-      anchorDirection: direction,
-    );
-  }
-
-  void remove(BuildContext overlayContext) {
-    FlowyOverlay.of(overlayContext).remove(GridRowActionSheet.identifier());
-  }
-
-  static String identifier() {
-    return (GridRowActionSheet).toString();
-  }
 }
 
 class _RowActionCell extends StatelessWidget {
   final _RowAction action;
-  final VoidCallback onDismissed;
-  const _RowActionCell(
-      {required this.action, required this.onDismissed, Key? key})
-      : super(key: key);
+  const _RowActionCell({required this.action, Key? key}) : super(key: key);
 
   @override
   Widget build(BuildContext context) {
@@ -101,7 +71,6 @@ class _RowActionCell extends StatelessWidget {
           if (action.enable()) {
             action.performAction(context);
           }
-          onDismissed();
         },
         leftIcon: svgWidget(action.iconName(), color: theme.iconColor),
       ),

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

@@ -194,7 +194,7 @@ class _CreateFieldButtonState extends State<_CreateFieldButton> {
   Widget build(BuildContext context) {
     final theme = context.read<AppTheme>();
 
-    return AppFlowyStylePopover(
+    return AppFlowyPopover(
       constraints: BoxConstraints.loose(const Size(240, 200)),
       controller: popoverController,
       triggerActions: PopoverTriggerActionFlags.click,

+ 0 - 1
frontend/app_flowy/lib/plugins/grid/presentation/widgets/toolbar/grid_group.dart

@@ -99,7 +99,6 @@ class _GridGroupCell extends StatelessWidget {
                 ),
               );
           onSelected();
-          // FlowyOverlay.of(context).remove(GridGroupList.identifier());
         },
       ),
     );

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

@@ -116,7 +116,7 @@ class _GridPropertyCell extends StatelessWidget {
   }
 
   Widget _editFieldButton(AppTheme theme, BuildContext context) {
-    return AppFlowyStylePopover(
+    return AppFlowyPopover(
       mutex: popoverMutex,
       triggerActions: PopoverTriggerActionFlags.click,
       offset: const Offset(20, 0),

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

@@ -2,7 +2,6 @@ import 'package:app_flowy/plugins/grid/application/setting/setting_bloc.dart';
 import 'package:easy_localization/easy_localization.dart';
 import 'package:flowy_infra/image.dart';
 import 'package:flowy_infra/theme.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/scrolling/styled_list.dart';
 import 'package:flowy_infra_ui/style_widget/text.dart';
@@ -40,7 +39,6 @@ class GridSettingList extends StatelessWidget {
             previous.selectedAction != current.selectedAction,
         listener: (context, state) {
           state.selectedAction.foldLeft(null, (_, action) {
-            FlowyOverlay.of(context).remove(identifier());
             onAction(action, settingContext);
           });
         },

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

@@ -53,7 +53,7 @@ class _SettingButton extends StatelessWidget {
   @override
   Widget build(BuildContext context) {
     final theme = context.watch<AppTheme>();
-    return AppFlowyStylePopover(
+    return AppFlowyPopover(
       constraints: BoxConstraints.loose(const Size(260, 400)),
       triggerActions: PopoverTriggerActionFlags.click,
       offset: const Offset(0, 10),

+ 0 - 63
frontend/app_flowy/lib/workspace/presentation/widgets/pop_up_window.dart

@@ -1,63 +0,0 @@
-import 'package:flowy_infra_ui/flowy_infra_ui.dart';
-import 'package:flowy_infra_ui/widget/rounded_input_field.dart';
-import 'package:flutter/material.dart';
-import 'package:window_size/window_size.dart';
-
-class FlowyPoppuWindow extends StatelessWidget {
-  final Widget child;
-  const FlowyPoppuWindow({Key? key, required this.child}) : super(key: key);
-
-  @override
-  Widget build(BuildContext context) {
-    return Material(
-      type: MaterialType.transparency,
-      child: child,
-    );
-  }
-
-  static Future<void> show(
-    BuildContext context, {
-    required Widget child,
-    required Size size,
-  }) async {
-    final window = await getWindowInfo();
-    // ignore: use_build_context_synchronously
-    FlowyOverlay.of(context).insertWithRect(
-      widget: FlowyPoppuWindow(child: child),
-      identifier: 'FlowyPoppuWindow',
-      anchorPosition: Offset(-size.width / 2.0, -size.height / 2.0),
-      anchorSize: window.frame.size,
-      anchorDirection: AnchorDirection.center,
-      style: FlowyOverlayStyle(blur: false),
-    );
-  }
-}
-
-class PopupTextField extends StatelessWidget {
-  final void Function(String) textDidChange;
-  const PopupTextField({
-    Key? key,
-    required this.textDidChange,
-  }) : super(key: key);
-
-  @override
-  Widget build(BuildContext context) {
-    return RoundedInputField(
-      style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w500),
-      hintText: '',
-      normalBorderColor: const Color(0xffbdbdbd),
-      onChanged: textDidChange,
-    );
-  }
-
-  static void show(
-      {required BuildContext context,
-      required Size size,
-      required void Function(String) textDidChange}) {
-    FlowyPoppuWindow.show(
-      context,
-      size: size,
-      child: PopupTextField(textDidChange: textDidChange),
-    );
-  }
-}

+ 10 - 10
frontend/app_flowy/packages/appflowy_popover/lib/popover.dart

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

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

@@ -2,7 +2,7 @@ import 'package:flowy_infra_ui/flowy_infra_ui_web.dart';
 import 'package:appflowy_popover/popover.dart';
 import 'package:flutter/material.dart';
 
-class AppFlowyStylePopover extends StatelessWidget {
+class AppFlowyPopover extends StatelessWidget {
   final Widget child;
   final PopoverController? controller;
   final Widget Function(BuildContext context) popupBuilder;
@@ -13,7 +13,7 @@ class AppFlowyStylePopover extends StatelessWidget {
   final PopoverMutex? mutex;
   final Offset? offset;
 
-  const AppFlowyStylePopover({
+  const AppFlowyPopover({
     Key? key,
     required this.child,
     required this.popupBuilder,