mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
Merge pull request #1091 from AppFlowy-IO/fix/appflowy_overlay_bugs
Fix/appflowy overlay bugs
This commit is contained in:
commit
5896855dda
@ -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"
|
||||
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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"
|
||||
|
@ -191,8 +191,8 @@
|
||||
"aquaColor": "水色",
|
||||
"blueColor": "青",
|
||||
"deleteTag": "選択候補を削除",
|
||||
"colorPannelTitle": "色",
|
||||
"pannelTitle": "選択候補を検索 または 作成する",
|
||||
"colorPanelTitle": "色",
|
||||
"panelTitle": "選択候補を検索 または 作成する",
|
||||
"searchOption": "選択候補を検索"
|
||||
}
|
||||
},
|
||||
|
@ -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": "Нажмите на кнопку, когда завершите вход:"
|
||||
}
|
||||
},
|
||||
"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"
|
||||
"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": {
|
||||
"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"
|
||||
}
|
||||
|
||||
}
|
@ -206,8 +206,8 @@
|
||||
"aquaColor": "水蓝色",
|
||||
"blueColor": "蓝色",
|
||||
"deleteTag": "删除标签",
|
||||
"colorPannelTitle": "颜色",
|
||||
"pannelTitle": "选择或新建一个标签",
|
||||
"colorPanelTitle": "颜色",
|
||||
"panelTitle": "选择或新建一个标签",
|
||||
"searchOption": "搜索标签"
|
||||
},
|
||||
"menuName": "网格"
|
||||
|
@ -202,8 +202,8 @@
|
||||
"aquaColor": "水藍色",
|
||||
"blueColor": "藍色",
|
||||
"deleteTag": "刪除標籤",
|
||||
"colorPannelTitle": "顏色",
|
||||
"pannelTitle": "搜尋或建立選項",
|
||||
"colorPanelTitle": "顏色",
|
||||
"panelTitle": "搜尋或建立選項",
|
||||
"searchOption": "搜尋選項"
|
||||
},
|
||||
"menuName": "網格"
|
||||
|
@ -87,7 +87,7 @@ class _BoardSelectOptionCellState extends State<BoardSelectOptionCell> {
|
||||
SelectOptionCellEditor.editorPanelWidth,
|
||||
300,
|
||||
));
|
||||
return AppFlowyStylePopover(
|
||||
return AppFlowyPopover(
|
||||
controller: _popover,
|
||||
constraints: constraints,
|
||||
direction: PopoverDirection.bottomWithLeftAligned,
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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,
|
||||
);
|
||||
}
|
||||
}
|
@ -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(
|
@ -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);
|
||||
});
|
||||
},
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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),
|
||||
|
@ -181,7 +181,7 @@ class _SelectOptionWrapState extends State<SelectOptionWrap> {
|
||||
SelectOptionCellEditor.editorPanelWidth,
|
||||
300,
|
||||
));
|
||||
return AppFlowyStylePopover(
|
||||
return AppFlowyPopover(
|
||||
controller: _popover,
|
||||
constraints: constraints,
|
||||
direction: PopoverDirection.bottomWithLeftAligned,
|
||||
|
@ -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.
|
||||
);
|
||||
},
|
||||
);
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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)),
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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),
|
||||
|
@ -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,
|
||||
),
|
||||
|
@ -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"),
|
||||
);
|
||||
|
@ -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),
|
||||
),
|
||||
|
@ -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,
|
||||
|
@ -99,7 +99,6 @@ class _GridGroupCell extends StatelessWidget {
|
||||
),
|
||||
);
|
||||
onSelected();
|
||||
// FlowyOverlay.of(context).remove(GridGroupList.identifier());
|
||||
},
|
||||
),
|
||||
);
|
||||
|
@ -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),
|
||||
|
@ -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);
|
||||
});
|
||||
},
|
||||
|
@ -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),
|
||||
|
@ -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),
|
||||
);
|
||||
}
|
||||
}
|
@ -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,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,
|
||||
|
Loading…
x
Reference in New Issue
Block a user