From b4671c1d997e209a6dee8a493680d6f41787c5d8 Mon Sep 17 00:00:00 2001 From: nathan Date: Tue, 29 Nov 2022 14:17:43 +0800 Subject: [PATCH 1/6] feat: support checklist --- .../app_flowy/assets/translations/en.json | 1 + .../lib/plugins/board/application/group.dart | 8 -- .../board/presentation/board_page.dart | 2 + .../card/board_checklist_cell.dart | 10 ++ .../presentation/card/card_cell_builder.dart | 5 + .../cell/cell_service/cell_controller.dart | 1 + .../application/field/field_controller.dart | 12 +- .../type_option/type_option_context.dart | 12 ++ .../filter/filter_create_bloc.dart | 6 + .../application/filter/filter_service.dart | 19 +++ .../widgets/cell/cell_builder.dart | 5 + .../widgets/cell/checklist_cell.dart | 17 +++ .../widgets/filter/choicechip/checklist.dart | 14 +++ .../widgets/filter/menu_item.dart | 3 + .../widgets/header/field_type_extension.dart | 4 + .../widgets/header/type_option/builder.dart | 16 +++ .../widgets/header/type_option/checklist.dart | 11 ++ .../presentation/widgets/row/row_detail.dart | 4 + .../flowy-grid/src/entities/field_entities.rs | 9 +- .../filter_entities/checklist_filter.rs | 55 +++++++++ .../src/entities/filter_entities/mod.rs | 2 + .../src/entities/filter_entities/util.rs | 6 +- .../src/services/cell/any_cell_data.rs | 3 + .../src/services/cell/cell_operation.rs | 7 ++ .../src/services/field/type_option_builder.rs | 3 + .../selection_type_option/checklist_filter.rs | 8 ++ .../checklist_type_option.rs | 114 ++++++++++++++++++ .../type_options/selection_type_option/mod.rs | 5 + .../multi_select_type_option.rs | 2 +- .../selection_type_option/select_filter.rs | 14 ++- .../flowy-grid/src/services/filter/cache.rs | 9 +- .../src/services/filter/controller.rs | 14 +++ .../src/services/group/group_util.rs | 6 + .../tests/grid/block_test/script.rs | 18 +++ .../flowy-grid/tests/grid/block_test/util.rs | 15 ++- .../flowy-grid/tests/grid/cell_test/test.rs | 6 +- .../flowy-grid/tests/grid/grid_editor.rs | 13 ++ 37 files changed, 431 insertions(+), 28 deletions(-) create mode 100644 frontend/app_flowy/lib/plugins/board/presentation/card/board_checklist_cell.dart create mode 100644 frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/checklist_cell.dart create mode 100644 frontend/app_flowy/lib/plugins/grid/presentation/widgets/filter/choicechip/checklist.dart create mode 100644 frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/type_option/checklist.dart create mode 100644 frontend/rust-lib/flowy-grid/src/entities/filter_entities/checklist_filter.rs create mode 100644 frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/checklist_filter.rs create mode 100644 frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/checklist_type_option.rs diff --git a/frontend/app_flowy/assets/translations/en.json b/frontend/app_flowy/assets/translations/en.json index fb4c5f2e73..09aa4a2378 100644 --- a/frontend/app_flowy/assets/translations/en.json +++ b/frontend/app_flowy/assets/translations/en.json @@ -222,6 +222,7 @@ "singleSelectFieldName": "Select", "multiSelectFieldName": "Multiselect", "urlFieldName": "URL", + "checklistFieldName": "Checklist", "numberFormat": " Number format", "dateFormat": " Date format", "includeTime": " Include time", diff --git a/frontend/app_flowy/lib/plugins/board/application/group.dart b/frontend/app_flowy/lib/plugins/board/application/group.dart index 1e59350826..3065bbbe61 100644 --- a/frontend/app_flowy/lib/plugins/board/application/group.dart +++ b/frontend/app_flowy/lib/plugins/board/application/group.dart @@ -10,11 +10,3 @@ class BoardGroupService { groupField = field; } } - -abstract class CanBeGroupField { - String get groupContent; -} - -// class SingleSelectGroup extends CanBeGroupField { -// final SingleSelectTypeOptionContext typeOptionContext; -// } diff --git a/frontend/app_flowy/lib/plugins/board/presentation/board_page.dart b/frontend/app_flowy/lib/plugins/board/presentation/board_page.dart index 0ec834c526..0a40c0dd8b 100644 --- a/frontend/app_flowy/lib/plugins/board/presentation/board_page.dart +++ b/frontend/app_flowy/lib/plugins/board/presentation/board_page.dart @@ -358,6 +358,8 @@ Widget? _buildHeaderIcon(GroupData customData) { break; case FieldType.URL: break; + case FieldType.CheckList: + break; } if (widget != null) { diff --git a/frontend/app_flowy/lib/plugins/board/presentation/card/board_checklist_cell.dart b/frontend/app_flowy/lib/plugins/board/presentation/card/board_checklist_cell.dart new file mode 100644 index 0000000000..6f90f8cd3e --- /dev/null +++ b/frontend/app_flowy/lib/plugins/board/presentation/card/board_checklist_cell.dart @@ -0,0 +1,10 @@ +import 'package:flutter/material.dart'; + +class BoardChecklistCell extends StatelessWidget { + const BoardChecklistCell({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Container(); + } +} diff --git a/frontend/app_flowy/lib/plugins/board/presentation/card/card_cell_builder.dart b/frontend/app_flowy/lib/plugins/board/presentation/card/card_cell_builder.dart index 1485bb7bd0..2118efdb87 100644 --- a/frontend/app_flowy/lib/plugins/board/presentation/card/card_cell_builder.dart +++ b/frontend/app_flowy/lib/plugins/board/presentation/card/card_cell_builder.dart @@ -4,6 +4,7 @@ import 'package:flutter/material.dart'; import 'board_cell.dart'; import 'board_checkbox_cell.dart'; +import 'board_checklist_cell.dart'; import 'board_date_cell.dart'; import 'board_number_cell.dart'; import 'board_select_option_cell.dart'; @@ -58,6 +59,10 @@ class BoardCellBuilder { editableNotifier: cellNotifier, key: key, ); + case FieldType.CheckList: + return BoardChecklistCell( + key: key, + ); case FieldType.Number: return BoardNumberCell( groupId: groupId, diff --git a/frontend/app_flowy/lib/plugins/grid/application/cell/cell_service/cell_controller.dart b/frontend/app_flowy/lib/plugins/grid/application/cell/cell_service/cell_controller.dart index df97a9567a..7935d1e630 100644 --- a/frontend/app_flowy/lib/plugins/grid/application/cell/cell_service/cell_controller.dart +++ b/frontend/app_flowy/lib/plugins/grid/application/cell/cell_service/cell_controller.dart @@ -81,6 +81,7 @@ class GridCellControllerBuilder { ); case FieldType.MultiSelect: case FieldType.SingleSelect: + case FieldType.CheckList: final cellDataLoader = GridCellDataLoader( cellId: _cellId, parser: SelectOptionCellDataParser(), diff --git a/frontend/app_flowy/lib/plugins/grid/application/field/field_controller.dart b/frontend/app_flowy/lib/plugins/grid/application/field/field_controller.dart index 8c2cdf0b04..e38eb49038 100644 --- a/frontend/app_flowy/lib/plugins/grid/application/field/field_controller.dart +++ b/frontend/app_flowy/lib/plugins/grid/application/field/field_controller.dart @@ -506,22 +506,12 @@ class FieldInfo { bool get canGroup { switch (_field.fieldType) { case FieldType.Checkbox: - return true; - case FieldType.DateTime: - return false; case FieldType.MultiSelect: - return true; - case FieldType.Number: - return false; - case FieldType.RichText: - return false; case FieldType.SingleSelect: return true; - case FieldType.URL: + default: return false; } - - return false; } bool get canCreateFilter { diff --git a/frontend/app_flowy/lib/plugins/grid/application/field/type_option/type_option_context.dart b/frontend/app_flowy/lib/plugins/grid/application/field/type_option/type_option_context.dart index 432e2ac285..915f71142c 100644 --- a/frontend/app_flowy/lib/plugins/grid/application/field/type_option/type_option_context.dart +++ b/frontend/app_flowy/lib/plugins/grid/application/field/type_option/type_option_context.dart @@ -1,6 +1,7 @@ import 'package:flowy_sdk/dispatch/dispatch.dart'; import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/checkbox_type_option.pb.dart'; +import 'package:flowy_sdk/protobuf/flowy-grid/checklist_type_option.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/date_type_option.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart'; import 'package:dartz/dartz.dart'; @@ -95,6 +96,17 @@ class MultiSelectTypeOptionWidgetDataParser } } +// Multi-select +typedef ChecklistTypeOptionContext = TypeOptionContext; + +class ChecklistTypeOptionWidgetDataParser + extends TypeOptionDataParser { + @override + ChecklistTypeOptionPB fromBuffer(List buffer) { + return ChecklistTypeOptionPB.fromBuffer(buffer); + } +} + class TypeOptionContext { T? _typeOptionObject; final TypeOptionDataParser dataParser; diff --git a/frontend/app_flowy/lib/plugins/grid/application/filter/filter_create_bloc.dart b/frontend/app_flowy/lib/plugins/grid/application/filter/filter_create_bloc.dart index 77060cfc6f..7693a49321 100644 --- a/frontend/app_flowy/lib/plugins/grid/application/filter/filter_create_bloc.dart +++ b/frontend/app_flowy/lib/plugins/grid/application/filter/filter_create_bloc.dart @@ -2,6 +2,7 @@ import 'package:app_flowy/plugins/grid/application/field/field_controller.dart'; import 'package:dartz/dartz.dart'; import 'package:flowy_sdk/protobuf/flowy-error/errors.pbserver.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/checkbox_filter.pbenum.dart'; +import 'package:flowy_sdk/protobuf/flowy-grid/checklist_filter.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/date_filter.pbenum.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/number_filter.pb.dart'; @@ -104,6 +105,11 @@ class GridCreateFilterBloc condition: SelectOptionCondition.OptionIs, fieldType: FieldType.MultiSelect, ); + case FieldType.CheckList: + return _ffiService.insertChecklistFilter( + fieldId: fieldId, + condition: ChecklistFilterCondition.IsIncomplete, + ); case FieldType.Number: return _ffiService.insertNumberFilter( fieldId: fieldId, diff --git a/frontend/app_flowy/lib/plugins/grid/application/filter/filter_service.dart b/frontend/app_flowy/lib/plugins/grid/application/filter/filter_service.dart index eb8d7fcaa3..f57fef31ef 100644 --- a/frontend/app_flowy/lib/plugins/grid/application/filter/filter_service.dart +++ b/frontend/app_flowy/lib/plugins/grid/application/filter/filter_service.dart @@ -3,6 +3,7 @@ import 'package:flowy_sdk/dispatch/dispatch.dart'; import 'package:flowy_sdk/log.dart'; import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/checkbox_filter.pbserver.dart'; +import 'package:flowy_sdk/protobuf/flowy-grid/checklist_filter.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/date_filter.pbserver.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/grid_entities.pb.dart'; @@ -145,6 +146,24 @@ class FilterFFIService { ); } + Future> insertChecklistFilter({ + required String fieldId, + required ChecklistFilterCondition condition, + String? filterId, + List optionIds = const [], + }) { + final filter = ChecklistFilterPB() + ..condition = condition + ..optionIds.addAll(optionIds); + + return insertFilter( + fieldId: fieldId, + filterId: filterId, + fieldType: FieldType.CheckList, + data: filter.writeToBuffer(), + ); + } + Future> insertFilter({ required String fieldId, String? filterId, diff --git a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/cell_builder.dart b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/cell_builder.dart index 1f885c3821..887470bdbd 100755 --- a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/cell_builder.dart +++ b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/cell_builder.dart @@ -6,6 +6,7 @@ import 'package:flutter/material.dart'; import 'cell_accessory.dart'; import 'cell_shortcuts.dart'; import 'checkbox_cell.dart'; +import 'checklist_cell.dart'; import 'date_cell/date_cell.dart'; import 'number_cell.dart'; import 'select_option_cell/select_option_cell.dart'; @@ -55,6 +56,10 @@ class GridCellBuilder { style: style, key: key, ); + case FieldType.CheckList: + return GridChecklistCell( + key: key, + ); case FieldType.Number: return GridNumberCell( cellControllerBuilder: cellControllerBuilder, diff --git a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/checklist_cell.dart b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/checklist_cell.dart new file mode 100644 index 0000000000..6499789636 --- /dev/null +++ b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/checklist_cell.dart @@ -0,0 +1,17 @@ +import 'package:flutter/material.dart'; + +import 'cell_builder.dart'; + +class GridChecklistCell extends GridCellWidget { + GridChecklistCell({Key? key}) : super(key: key); + + @override + ChecklistCellState createState() => ChecklistCellState(); +} + +class ChecklistCellState extends State { + @override + Widget build(BuildContext context) { + return Container(); + } +} diff --git a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/filter/choicechip/checklist.dart b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/filter/choicechip/checklist.dart new file mode 100644 index 0000000000..641a61f009 --- /dev/null +++ b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/filter/choicechip/checklist.dart @@ -0,0 +1,14 @@ +import 'package:app_flowy/plugins/grid/presentation/widgets/filter/filter_info.dart'; +import 'package:flutter/material.dart'; +import 'choicechip.dart'; + +class ChecklistFilterChoicechip extends StatelessWidget { + final FilterInfo filterInfo; + const ChecklistFilterChoicechip({required this.filterInfo, Key? key}) + : super(key: key); + + @override + Widget build(BuildContext context) { + return ChoiceChipButton(filterInfo: filterInfo); + } +} diff --git a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/filter/menu_item.dart b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/filter/menu_item.dart index 722457e141..3c733443ab 100644 --- a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/filter/menu_item.dart +++ b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/filter/menu_item.dart @@ -2,6 +2,7 @@ import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pbenum.dart'; import 'package:flutter/material.dart'; import 'choicechip/checkbox.dart'; +import 'choicechip/checklist.dart'; import 'choicechip/date.dart'; import 'choicechip/number.dart'; import 'choicechip/select_option/select_option.dart'; @@ -35,6 +36,8 @@ Widget buildFilterChoicechip(FilterInfo filterInfo) { return SelectOptionFilterChoicechip(filterInfo: filterInfo); case FieldType.URL: return URLFilterChoicechip(filterInfo: filterInfo); + case FieldType.CheckList: + return ChecklistFilterChoicechip(filterInfo: filterInfo); default: return const SizedBox(); } diff --git a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/field_type_extension.dart b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/field_type_extension.dart index 0f6da9951e..afc7f1c74f 100644 --- a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/field_type_extension.dart +++ b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/field_type_extension.dart @@ -19,6 +19,8 @@ extension FieldTypeListExtension on FieldType { return "grid/field/single_select"; case FieldType.URL: return "grid/field/url"; + case FieldType.CheckList: + return "grid/field/checklist"; } throw UnimplementedError; } @@ -39,6 +41,8 @@ extension FieldTypeListExtension on FieldType { return LocaleKeys.grid_field_singleSelectFieldName.tr(); case FieldType.URL: return LocaleKeys.grid_field_urlFieldName.tr(); + case FieldType.CheckList: + return LocaleKeys.grid_field_checklistFieldName.tr(); } throw UnimplementedError; } diff --git a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/type_option/builder.dart b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/type_option/builder.dart index 981260c830..d20658bbd2 100644 --- a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/type_option/builder.dart +++ b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/type_option/builder.dart @@ -8,6 +8,7 @@ import 'package:flowy_sdk/protobuf/flowy-grid/checkbox_type_option.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/date_type_option.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/multi_select_type_option.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/number_type_option.pb.dart'; +import 'package:flowy_sdk/protobuf/flowy-grid/protobuf.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/single_select_type_option.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/text_type_option.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/url_type_option.pb.dart'; @@ -15,6 +16,7 @@ import 'package:protobuf/protobuf.dart' hide FieldInfo; import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart'; import 'package:flutter/material.dart'; import 'checkbox.dart'; +import 'checklist.dart'; import 'date.dart'; import 'multi_select.dart'; import 'number.dart'; @@ -124,6 +126,15 @@ TypeOptionWidgetBuilder makeTypeOptionWidgetBuilder({ dataController: dataController, ), ); + + case FieldType.CheckList: + return ChecklistTypeOptionWidgetBuilder( + makeTypeOptionContextWithDataController( + gridId: gridId, + fieldType: fieldType, + dataController: dataController, + ), + ); } throw UnimplementedError; } @@ -206,6 +217,11 @@ TypeOptionContext dataController: dataController, dataParser: MultiSelectTypeOptionWidgetDataParser(), ) as TypeOptionContext; + case FieldType.CheckList: + return ChecklistTypeOptionContext( + dataController: dataController, + dataParser: ChecklistTypeOptionWidgetDataParser(), + ) as TypeOptionContext; case FieldType.Number: return NumberTypeOptionContext( dataController: dataController, diff --git a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/type_option/checklist.dart b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/type_option/checklist.dart new file mode 100644 index 0000000000..9240d52fde --- /dev/null +++ b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/type_option/checklist.dart @@ -0,0 +1,11 @@ +import 'package:app_flowy/plugins/grid/application/field/type_option/type_option_context.dart'; +import 'package:flutter/material.dart'; +import 'builder.dart'; + +class ChecklistTypeOptionWidgetBuilder extends TypeOptionWidgetBuilder { + ChecklistTypeOptionWidgetBuilder( + ChecklistTypeOptionContext typeOptionContext); + + @override + Widget? build(BuildContext context) => null; +} diff --git a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/row/row_detail.dart b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/row/row_detail.dart index df88ba45fe..25011110a2 100644 --- a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/row/row_detail.dart +++ b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/row/row_detail.dart @@ -331,6 +331,10 @@ GridCellStyle? _customCellStyle(FieldType fieldType) { return SelectOptionCellStyle( placeholder: LocaleKeys.grid_row_textPlaceholder.tr(), ); + case FieldType.CheckList: + return SelectOptionCellStyle( + placeholder: LocaleKeys.grid_row_textPlaceholder.tr(), + ); case FieldType.Number: return null; case FieldType.RichText: diff --git a/frontend/rust-lib/flowy-grid/src/entities/field_entities.rs b/frontend/rust-lib/flowy-grid/src/entities/field_entities.rs index 3f32debcb3..c0127aa89c 100644 --- a/frontend/rust-lib/flowy-grid/src/entities/field_entities.rs +++ b/frontend/rust-lib/flowy-grid/src/entities/field_entities.rs @@ -491,6 +491,7 @@ pub enum FieldType { MultiSelect = 4, Checkbox = 5, URL = 6, + CheckList = 7, } pub const RICH_TEXT_FIELD: FieldType = FieldType::RichText; @@ -500,6 +501,7 @@ pub const SINGLE_SELECT_FIELD: FieldType = FieldType::SingleSelect; pub const MULTI_SELECT_FIELD: FieldType = FieldType::MultiSelect; pub const CHECKBOX_FIELD: FieldType = FieldType::Checkbox; pub const URL_FIELD: FieldType = FieldType::URL; +pub const CHECKLIST_FIELD: FieldType = FieldType::CheckList; impl std::default::Default for FieldType { fn default() -> Self { @@ -563,6 +565,10 @@ impl FieldType { self == &MULTI_SELECT_FIELD || self == &SINGLE_SELECT_FIELD } + pub fn is_check_list(&self) -> bool { + self == &CHECKLIST_FIELD + } + pub fn can_be_group(&self) -> bool { self.is_select_option() } @@ -596,8 +602,9 @@ impl std::convert::From for FieldType { 4 => FieldType::MultiSelect, 5 => FieldType::Checkbox, 6 => FieldType::URL, + 7 => FieldType::CheckList, _ => { - tracing::error!("Can't parser FieldTypeRevision: {} to FieldType", ty); + tracing::error!("Can't convert FieldTypeRevision: {} to FieldType", ty); FieldType::RichText } } diff --git a/frontend/rust-lib/flowy-grid/src/entities/filter_entities/checklist_filter.rs b/frontend/rust-lib/flowy-grid/src/entities/filter_entities/checklist_filter.rs new file mode 100644 index 0000000000..59c5ad885a --- /dev/null +++ b/frontend/rust-lib/flowy-grid/src/entities/filter_entities/checklist_filter.rs @@ -0,0 +1,55 @@ +use crate::services::field::SelectOptionIds; +use flowy_derive::{ProtoBuf, ProtoBuf_Enum}; +use flowy_error::ErrorCode; +use grid_rev_model::FilterRevision; + +#[derive(Eq, PartialEq, ProtoBuf, Debug, Default, Clone)] +pub struct ChecklistFilterPB { + #[pb(index = 1)] + pub condition: ChecklistFilterCondition, + + #[pb(index = 2)] + pub option_ids: Vec, +} + +#[derive(Debug, Clone, PartialEq, Eq, ProtoBuf_Enum)] +#[repr(u8)] +pub enum ChecklistFilterCondition { + IsComplete = 0, + IsIncomplete = 1, +} + +impl std::convert::From for u32 { + fn from(value: ChecklistFilterCondition) -> Self { + value as u32 + } +} + +impl std::default::Default for ChecklistFilterCondition { + fn default() -> Self { + ChecklistFilterCondition::IsIncomplete + } +} + +impl std::convert::TryFrom for ChecklistFilterCondition { + type Error = ErrorCode; + + fn try_from(value: u8) -> Result { + match value { + 0 => Ok(ChecklistFilterCondition::IsComplete), + 1 => Ok(ChecklistFilterCondition::IsIncomplete), + _ => Err(ErrorCode::InvalidData), + } + } +} + +impl std::convert::From<&FilterRevision> for ChecklistFilterPB { + fn from(rev: &FilterRevision) -> Self { + let ids = SelectOptionIds::from(rev.content.clone()); + ChecklistFilterPB { + condition: ChecklistFilterCondition::try_from(rev.condition) + .unwrap_or(ChecklistFilterCondition::IsIncomplete), + option_ids: ids.into_inner(), + } + } +} diff --git a/frontend/rust-lib/flowy-grid/src/entities/filter_entities/mod.rs b/frontend/rust-lib/flowy-grid/src/entities/filter_entities/mod.rs index 435dac56f2..d628a13801 100644 --- a/frontend/rust-lib/flowy-grid/src/entities/filter_entities/mod.rs +++ b/frontend/rust-lib/flowy-grid/src/entities/filter_entities/mod.rs @@ -1,4 +1,5 @@ mod checkbox_filter; +mod checklist_filter; mod date_filter; mod filter_changeset; mod number_filter; @@ -7,6 +8,7 @@ mod text_filter; mod util; pub use checkbox_filter::*; +pub use checklist_filter::*; pub use date_filter::*; pub use filter_changeset::*; pub use number_filter::*; diff --git a/frontend/rust-lib/flowy-grid/src/entities/filter_entities/util.rs b/frontend/rust-lib/flowy-grid/src/entities/filter_entities/util.rs index 18c73269f8..d553fefd45 100644 --- a/frontend/rust-lib/flowy-grid/src/entities/filter_entities/util.rs +++ b/frontend/rust-lib/flowy-grid/src/entities/filter_entities/util.rs @@ -1,6 +1,7 @@ use crate::entities::parser::NotEmptyStr; use crate::entities::{ - CheckboxFilterPB, DateFilterContent, DateFilterPB, FieldType, NumberFilterPB, SelectOptionFilterPB, TextFilterPB, + CheckboxFilterPB, ChecklistFilterPB, DateFilterContent, DateFilterPB, FieldType, NumberFilterPB, + SelectOptionFilterPB, TextFilterPB, }; use crate::services::field::SelectOptionIds; use crate::services::filter::FilterType; @@ -35,6 +36,7 @@ impl std::convert::From<&FilterRevision> for FilterPB { FieldType::DateTime => DateFilterPB::from(rev).try_into().unwrap(), FieldType::SingleSelect => SelectOptionFilterPB::from(rev).try_into().unwrap(), FieldType::MultiSelect => SelectOptionFilterPB::from(rev).try_into().unwrap(), + FieldType::CheckList => ChecklistFilterPB::from(rev).try_into().unwrap(), FieldType::Checkbox => CheckboxFilterPB::from(rev).try_into().unwrap(), FieldType::URL => TextFilterPB::from(rev).try_into().unwrap(), }; @@ -174,7 +176,7 @@ impl TryInto for AlterFilterPayloadPB { } .to_string(); } - FieldType::SingleSelect | FieldType::MultiSelect => { + FieldType::SingleSelect | FieldType::MultiSelect | FieldType::CheckList => { let filter = SelectOptionFilterPB::try_from(bytes).map_err(|_| ErrorCode::ProtobufSerde)?; condition = filter.condition as u8; content = SelectOptionIds::from(filter.option_ids).to_string(); diff --git a/frontend/rust-lib/flowy-grid/src/services/cell/any_cell_data.rs b/frontend/rust-lib/flowy-grid/src/services/cell/any_cell_data.rs index 098c67a150..caa76f4b54 100644 --- a/frontend/rust-lib/flowy-grid/src/services/cell/any_cell_data.rs +++ b/frontend/rust-lib/flowy-grid/src/services/cell/any_cell_data.rs @@ -104,6 +104,9 @@ impl TypeCellData { pub fn is_multi_select(&self) -> bool { self.field_type == FieldType::MultiSelect } + pub fn is_checklist(&self) -> bool { + self.field_type == FieldType::CheckList + } pub fn is_url(&self) -> bool { self.field_type == FieldType::URL diff --git a/frontend/rust-lib/flowy-grid/src/services/cell/cell_operation.rs b/frontend/rust-lib/flowy-grid/src/services/cell/cell_operation.rs index 5a564b2b67..31ec2a3d93 100644 --- a/frontend/rust-lib/flowy-grid/src/services/cell/cell_operation.rs +++ b/frontend/rust-lib/flowy-grid/src/services/cell/cell_operation.rs @@ -122,6 +122,7 @@ pub fn apply_cell_data_changeset>( SingleSelectTypeOptionPB::from(field_rev).apply_changeset(changeset.into(), cell_rev) } FieldType::MultiSelect => MultiSelectTypeOptionPB::from(field_rev).apply_changeset(changeset.into(), cell_rev), + FieldType::CheckList => ChecklistTypeOptionPB::from(field_rev).apply_changeset(changeset.into(), cell_rev), FieldType::Checkbox => CheckboxTypeOptionPB::from(field_rev).apply_changeset(changeset.into(), cell_rev), FieldType::URL => URLTypeOptionPB::from(field_rev).apply_changeset(changeset.into(), cell_rev), }?; @@ -180,6 +181,9 @@ pub fn decode_cell_data_to_string( FieldType::MultiSelect => field_rev .get_type_option::(field_type)? .displayed_cell_string(cell_data.into(), from_field_type, field_rev), + FieldType::CheckList => field_rev + .get_type_option::(field_type)? + .displayed_cell_string(cell_data.into(), from_field_type, field_rev), FieldType::Checkbox => field_rev .get_type_option::(field_type)? .displayed_cell_string(cell_data.into(), from_field_type, field_rev), @@ -230,6 +234,9 @@ pub fn try_decode_cell_data( FieldType::MultiSelect => field_rev .get_type_option::(field_type)? .decode_cell_data(cell_data.into(), from_field_type, field_rev), + FieldType::CheckList => field_rev + .get_type_option::(field_type)? + .decode_cell_data(cell_data.into(), from_field_type, field_rev), FieldType::Checkbox => field_rev .get_type_option::(field_type)? .decode_cell_data(cell_data.into(), from_field_type, field_rev), diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_option_builder.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_option_builder.rs index dfedde49aa..fcc5395686 100644 --- a/frontend/rust-lib/flowy-grid/src/services/field/type_option_builder.rs +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_option_builder.rs @@ -38,6 +38,7 @@ pub fn default_type_option_builder_from_type(field_type: &FieldType) -> Box MultiSelectTypeOptionPB::default().into(), FieldType::Checkbox => CheckboxTypeOptionPB::default().into(), FieldType::URL => URLTypeOptionPB::default().into(), + FieldType::CheckList => ChecklistTypeOptionPB::default().into(), }; type_option_builder_from_json_str(&s, field_type) @@ -52,6 +53,7 @@ pub fn type_option_builder_from_json_str(s: &str, field_type: &FieldType) -> Box FieldType::MultiSelect => Box::new(MultiSelectTypeOptionBuilder::from_json_str(s)), FieldType::Checkbox => Box::new(CheckboxTypeOptionBuilder::from_json_str(s)), FieldType::URL => Box::new(URLTypeOptionBuilder::from_json_str(s)), + FieldType::CheckList => Box::new(ChecklistTypeOptionBuilder::from_json_str(s)), } } @@ -65,5 +67,6 @@ pub fn type_option_builder_from_bytes>(bytes: T, field_type: &Fie FieldType::MultiSelect => Box::new(MultiSelectTypeOptionBuilder::from_protobuf_bytes(bytes)), FieldType::Checkbox => Box::new(CheckboxTypeOptionBuilder::from_protobuf_bytes(bytes)), FieldType::URL => Box::new(URLTypeOptionBuilder::from_protobuf_bytes(bytes)), + FieldType::CheckList => Box::new(ChecklistTypeOptionBuilder::from_protobuf_bytes(bytes)), } } diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/checklist_filter.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/checklist_filter.rs new file mode 100644 index 0000000000..e69b2dfa2e --- /dev/null +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/checklist_filter.rs @@ -0,0 +1,8 @@ +use crate::entities::ChecklistFilterPB; +use crate::services::field::SelectedSelectOptions; + +impl ChecklistFilterPB { + pub fn is_visible(&self, selected_options: &SelectedSelectOptions) -> bool { + true + } +} diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/checklist_type_option.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/checklist_type_option.rs new file mode 100644 index 0000000000..a6975d8f7d --- /dev/null +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/checklist_type_option.rs @@ -0,0 +1,114 @@ +use crate::entities::FieldType; +use crate::impl_type_option; +use crate::services::cell::{AnyCellChangeset, CellBytes, CellData, CellDataOperation, CellDisplayable}; +use crate::services::field::selection_type_option::type_option_transform::SelectOptionTypeOptionTransformer; +use crate::services::field::type_options::util::get_cell_data; +use crate::services::field::{ + BoxTypeOptionBuilder, SelectOptionCellChangeset, SelectOptionIds, SelectOptionPB, SelectTypeOptionSharedAction, + TypeOptionBuilder, +}; +use bytes::Bytes; +use flowy_derive::ProtoBuf; +use flowy_error::{FlowyError, FlowyResult}; +use grid_rev_model::{CellRevision, FieldRevision, TypeOptionDataDeserializer, TypeOptionDataSerializer}; +use serde::{Deserialize, Serialize}; + +// Multiple select +#[derive(Clone, Debug, Default, Serialize, Deserialize, ProtoBuf)] +pub struct ChecklistTypeOptionPB { + #[pb(index = 1)] + pub options: Vec, + + #[pb(index = 2)] + pub disable_color: bool, +} +impl_type_option!(ChecklistTypeOptionPB, FieldType::CheckList); + +impl SelectTypeOptionSharedAction for ChecklistTypeOptionPB { + fn number_of_max_options(&self) -> Option { + None + } + + fn options(&self) -> &Vec { + &self.options + } + + fn mut_options(&mut self) -> &mut Vec { + &mut self.options + } +} + +impl CellDataOperation for ChecklistTypeOptionPB { + fn decode_cell_data( + &self, + cell_data: CellData, + decoded_field_type: &FieldType, + field_rev: &FieldRevision, + ) -> FlowyResult { + self.displayed_cell_bytes(cell_data, decoded_field_type, field_rev) + } + + fn apply_changeset( + &self, + changeset: AnyCellChangeset, + cell_rev: Option, + ) -> Result { + let content_changeset = changeset.try_into_inner()?; + + let insert_option_ids = content_changeset + .insert_option_ids + .into_iter() + .filter(|insert_option_id| self.options.iter().any(|option| &option.id == insert_option_id)) + .collect::>(); + + let new_cell_data: String; + match cell_rev { + None => { + new_cell_data = SelectOptionIds::from(insert_option_ids).to_string(); + } + Some(cell_rev) => { + let cell_data = get_cell_data(&cell_rev); + let mut select_ids: SelectOptionIds = cell_data.into(); + for insert_option_id in insert_option_ids { + if !select_ids.contains(&insert_option_id) { + select_ids.push(insert_option_id); + } + } + + for delete_option_id in content_changeset.delete_option_ids { + select_ids.retain(|id| id != &delete_option_id); + } + + new_cell_data = select_ids.to_string(); + tracing::trace!("checklist's cell data: {}", &new_cell_data); + } + } + + Ok(new_cell_data) + } +} + +#[derive(Default)] +pub struct ChecklistTypeOptionBuilder(ChecklistTypeOptionPB); +impl_into_box_type_option_builder!(ChecklistTypeOptionBuilder); +impl_builder_from_json_str_and_from_bytes!(ChecklistTypeOptionBuilder, ChecklistTypeOptionPB); +impl ChecklistTypeOptionBuilder { + pub fn add_option(mut self, opt: SelectOptionPB) -> Self { + self.0.options.push(opt); + self + } +} + +impl TypeOptionBuilder for ChecklistTypeOptionBuilder { + fn field_type(&self) -> FieldType { + FieldType::CheckList + } + + fn serializer(&self) -> &dyn TypeOptionDataSerializer { + &self.0 + } + + fn transform(&mut self, field_type: &FieldType, type_option_data: String) { + SelectOptionTypeOptionTransformer::transform_type_option(&mut self.0, field_type, type_option_data) + } +} diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/mod.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/mod.rs index fe117d791f..15f69ad2c8 100644 --- a/frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/mod.rs +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/mod.rs @@ -1,9 +1,14 @@ +mod checklist_filter; +mod checklist_type_option; mod multi_select_type_option; mod select_filter; mod select_type_option; mod single_select_type_option; mod type_option_transform; +pub use checklist_type_option::*; pub use multi_select_type_option::*; pub use select_type_option::*; pub use single_select_type_option::*; + +pub use checklist_filter::*; diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/multi_select_type_option.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/multi_select_type_option.rs index a6068a0a6d..610e32f072 100644 --- a/frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/multi_select_type_option.rs +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/multi_select_type_option.rs @@ -80,7 +80,7 @@ impl CellDataOperation for MultiSele } new_cell_data = select_ids.to_string(); - tracing::trace!("Multi select cell data: {}", &new_cell_data); + tracing::trace!("Multi-select cell data: {}", &new_cell_data); } } diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/select_filter.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/select_filter.rs index 70e51b1d52..28a4735605 100644 --- a/frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/select_filter.rs +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/select_filter.rs @@ -1,8 +1,8 @@ #![allow(clippy::needless_collect)] -use crate::entities::{SelectOptionCondition, SelectOptionFilterPB}; +use crate::entities::{ChecklistFilterPB, SelectOptionCondition, SelectOptionFilterPB}; use crate::services::cell::{CellFilterOperation, TypeCellData}; -use crate::services::field::{MultiSelectTypeOptionPB, SingleSelectTypeOptionPB}; +use crate::services::field::{ChecklistTypeOptionPB, MultiSelectTypeOptionPB, SingleSelectTypeOptionPB}; use crate::services::field::{SelectTypeOptionSharedAction, SelectedSelectOptions}; use flowy_error::FlowyResult; @@ -61,6 +61,16 @@ impl CellFilterOperation for SingleSelectTypeOptionPB { } } +impl CellFilterOperation for ChecklistTypeOptionPB { + fn apply_filter(&self, any_cell_data: TypeCellData, filter: &ChecklistFilterPB) -> FlowyResult { + if !any_cell_data.is_checklist() { + return Ok(true); + } + let selected_options = SelectedSelectOptions::from(self.get_selected_options(any_cell_data.into())); + Ok(filter.is_visible(&selected_options)) + } +} + #[cfg(test)] mod tests { #![allow(clippy::all)] diff --git a/frontend/rust-lib/flowy-grid/src/services/filter/cache.rs b/frontend/rust-lib/flowy-grid/src/services/filter/cache.rs index ae799289fe..73b4e6bcc8 100644 --- a/frontend/rust-lib/flowy-grid/src/services/filter/cache.rs +++ b/frontend/rust-lib/flowy-grid/src/services/filter/cache.rs @@ -1,4 +1,6 @@ -use crate::entities::{CheckboxFilterPB, DateFilterPB, FieldType, NumberFilterPB, SelectOptionFilterPB, TextFilterPB}; +use crate::entities::{ + CheckboxFilterPB, ChecklistFilterPB, DateFilterPB, FieldType, NumberFilterPB, SelectOptionFilterPB, TextFilterPB, +}; use crate::services::filter::FilterType; use std::collections::HashMap; @@ -10,6 +12,7 @@ pub(crate) struct FilterMap { pub(crate) date_filter: HashMap, pub(crate) select_option_filter: HashMap, pub(crate) checkbox_filter: HashMap, + pub(crate) checklist_filter: HashMap, } impl FilterMap { @@ -26,6 +29,7 @@ impl FilterMap { FieldType::MultiSelect => self.select_option_filter.get(filter_type).is_some(), FieldType::Checkbox => self.checkbox_filter.get(filter_type).is_some(), FieldType::URL => self.url_filter.get(filter_type).is_some(), + FieldType::CheckList => self.checklist_filter.get(filter_type).is_some(), } } @@ -83,6 +87,9 @@ impl FilterMap { FieldType::URL => { let _ = self.url_filter.remove(filter_id); } + FieldType::CheckList => { + let _ = self.checklist_filter.remove(filter_id); + } }; } } diff --git a/frontend/rust-lib/flowy-grid/src/services/filter/controller.rs b/frontend/rust-lib/flowy-grid/src/services/filter/controller.rs index fbb78d35eb..d855d00d04 100644 --- a/frontend/rust-lib/flowy-grid/src/services/filter/controller.rs +++ b/frontend/rust-lib/flowy-grid/src/services/filter/controller.rs @@ -291,6 +291,12 @@ impl FilterController { .url_filter .insert(filter_type, TextFilterPB::from(filter_rev.as_ref())); } + FieldType::CheckList => { + let _ = self + .filter_map + .checklist_filter + .insert(filter_type, ChecklistFilterPB::from(filter_rev.as_ref())); + } } } } @@ -413,6 +419,14 @@ fn filter_cell( .ok(), ) }), + FieldType::CheckList => filter_map.checklist_filter.get(filter_id).and_then(|filter| { + Some( + field_rev + .get_type_option::(field_rev.ty)? + .apply_filter(any_cell_data, filter) + .ok(), + ) + }), }?; tracing::Span::current().record( "cell_content", diff --git a/frontend/rust-lib/flowy-grid/src/services/group/group_util.rs b/frontend/rust-lib/flowy-grid/src/services/group/group_util.rs index eb224ce493..ed9a36abc8 100644 --- a/frontend/rust-lib/flowy-grid/src/services/group/group_util.rs +++ b/frontend/rust-lib/flowy-grid/src/services/group/group_util.rs @@ -123,6 +123,12 @@ pub fn default_group_configuration(field_rev: &FieldRevision) -> GroupConfigurat SelectOptionGroupConfigurationRevision::default(), ) .unwrap(), + FieldType::CheckList => GroupConfigurationRevision::new( + field_id, + field_type_rev, + SelectOptionGroupConfigurationRevision::default(), + ) + .unwrap(), FieldType::Checkbox => { GroupConfigurationRevision::new(field_id, field_type_rev, CheckboxGroupConfigurationRevision::default()) .unwrap() diff --git a/frontend/rust-lib/flowy-grid/tests/grid/block_test/script.rs b/frontend/rust-lib/flowy-grid/tests/grid/block_test/script.rs index 703cf6889c..5ececf4b05 100644 --- a/frontend/rust-lib/flowy-grid/tests/grid/block_test/script.rs +++ b/frontend/rust-lib/flowy-grid/tests/grid/block_test/script.rs @@ -226,6 +226,24 @@ impl GridRowTest { assert_eq!(s, expected); } + FieldType::CheckList => { + let cell_data = self + .editor + .get_cell_bytes(&cell_id) + .await + .unwrap() + .parser::() + .unwrap(); + + let s = cell_data + .select_options + .into_iter() + .map(|option| option.name) + .collect::>() + .join(SELECTION_IDS_SEPARATOR); + + assert_eq!(s, expected); + } FieldType::Checkbox => { let cell_data = self .editor diff --git a/frontend/rust-lib/flowy-grid/tests/grid/block_test/util.rs b/frontend/rust-lib/flowy-grid/tests/grid/block_test/util.rs index 6179168ce1..dfc5e5aa23 100644 --- a/frontend/rust-lib/flowy-grid/tests/grid/block_test/util.rs +++ b/frontend/rust-lib/flowy-grid/tests/grid/block_test/util.rs @@ -2,7 +2,7 @@ use flowy_grid::entities::FieldType; use std::sync::Arc; use flowy_grid::services::field::{ - DateCellChangeset, MultiSelectTypeOptionPB, SelectOptionPB, SingleSelectTypeOptionPB, + ChecklistTypeOptionPB, DateCellChangeset, MultiSelectTypeOptionPB, SelectOptionPB, SingleSelectTypeOptionPB, }; use flowy_grid::services::row::RowRevisionBuilder; use grid_rev_model::{FieldRevision, RowRevision}; @@ -90,6 +90,19 @@ impl<'a> GridRowTestBuilder<'a> { multi_select_field.id.clone() } + pub fn insert_checklist_cell(&mut self, f: F) -> String + where + F: Fn(Vec) -> Vec, + { + let checklist_field = self.field_rev_with_type(&FieldType::CheckList); + let type_option = ChecklistTypeOptionPB::from(&checklist_field); + let options = f(type_option.options); + let ops_ids = options.iter().map(|option| option.id.clone()).collect::>(); + self.inner_builder + .insert_select_option_cell(&multi_select_field.id, ops_ids); + + checklist_field.id.clone() + } pub fn field_rev_with_type(&self, field_type: &FieldType) -> FieldRevision { self.field_revs .iter() diff --git a/frontend/rust-lib/flowy-grid/tests/grid/cell_test/test.rs b/frontend/rust-lib/flowy-grid/tests/grid/cell_test/test.rs index 09990bb35d..5885d4ad37 100644 --- a/frontend/rust-lib/flowy-grid/tests/grid/cell_test/test.rs +++ b/frontend/rust-lib/flowy-grid/tests/grid/cell_test/test.rs @@ -3,7 +3,7 @@ use crate::grid::cell_test::script::GridCellTest; use crate::grid::field_test::util::make_date_cell_string; use flowy_grid::entities::{CellChangesetPB, FieldType}; use flowy_grid::services::field::selection_type_option::SelectOptionCellChangeset; -use flowy_grid::services::field::{MultiSelectTypeOptionPB, SingleSelectTypeOptionPB}; +use flowy_grid::services::field::{ChecklistTypeOptionPB, MultiSelectTypeOptionPB, SingleSelectTypeOptionPB}; #[tokio::test] async fn grid_cell_update() { @@ -31,6 +31,10 @@ async fn grid_cell_update() { let type_option = MultiSelectTypeOptionPB::from(field_rev); SelectOptionCellChangeset::from_insert_option_id(&type_option.options.first().unwrap().id).to_str() } + FieldType::CheckList => { + let type_option = ChecklistTypeOptionPB::from(field_rev); + SelectOptionCellChangeset::from_insert_option_id(&type_option.options.first().unwrap().id).to_str() + } FieldType::Checkbox => "1".to_string(), FieldType::URL => "1".to_string(), }; diff --git a/frontend/rust-lib/flowy-grid/tests/grid/grid_editor.rs b/frontend/rust-lib/flowy-grid/tests/grid/grid_editor.rs index f099dc9cf6..a43ddde209 100644 --- a/frontend/rust-lib/flowy-grid/tests/grid/grid_editor.rs +++ b/frontend/rust-lib/flowy-grid/tests/grid/grid_editor.rs @@ -153,6 +153,9 @@ pub const COMPLETED: &str = "Completed"; pub const PLANNED: &str = "Planned"; pub const PAUSED: &str = "Paused"; +pub const FIRST_THING: &str = "Wake up at 6:00 am"; +pub const SECOND_THING: &str = "Get some coffee"; +pub const THIRD_THING: &str = "Start working"; // This grid is assumed to contain all the Fields. fn make_test_grid() -> BuildGridContext { let mut grid_builder = GridBuilder::new(); @@ -217,6 +220,14 @@ fn make_test_grid() -> BuildGridContext { let url_field = FieldBuilder::new(url).name("link").visibility(true).build(); grid_builder.add_field(url_field); } + FieldType::CheckList => { + let checklist = ChecklistTypeOptionBuilder::default() + .add_option(SelectOptionPB::new(FIRST_THING)) + .add_option(SelectOptionPB::new(SECOND_THING)) + .add_option(SelectOptionPB::new(THIRD_THING)); + let checklist_field = FieldBuilder::new(checklist).name("TODO").visibility(true).build(); + grid_builder.add_field(checklist_field); + } } } @@ -234,6 +245,7 @@ fn make_test_grid() -> BuildGridContext { FieldType::DateTime => row_builder.insert_date_cell("1647251762"), FieldType::MultiSelect => row_builder .insert_multi_select_cell(|mut options| vec![options.remove(0), options.remove(0)]), + FieldType::CheckList => row_builder.insert_checklist_cell(|options| options), FieldType::Checkbox => row_builder.insert_checkbox_cell("true"), _ => "".to_owned(), }; @@ -370,6 +382,7 @@ fn make_test_board() -> BuildGridContext { let url_field = FieldBuilder::new(url).name("link").visibility(true).build(); grid_builder.add_field(url_field); } + FieldType::CheckList => {} } } From 1ba299815ec9bee9e19c32b21635fc89e2994f6c Mon Sep 17 00:00:00 2001 From: nathan Date: Tue, 29 Nov 2022 22:40:49 +0800 Subject: [PATCH 2/6] feat: implement checklist UI --- .../app_flowy/assets/translations/en.json | 3 + .../board/presentation/board_page.dart | 2 +- .../presentation/card/card_cell_builder.dart | 2 +- .../cell/cell_service/cell_controller.dart | 4 +- .../application/cell/checklist_cell_bloc.dart | 93 +++++++++ .../cell/checklist_cell_editor_bloc.dart | 194 ++++++++++++++++++ .../cell/select_option_editor_bloc.dart | 6 +- .../cell/select_option_service.dart | 14 +- .../application/field/field_controller.dart | 2 +- .../filter/filter_create_bloc.dart | 2 +- .../application/filter/filter_service.dart | 6 +- .../widgets/cell/cell_builder.dart | 5 +- .../widgets/cell/checklist_cell.dart | 17 -- .../cell/checklist_cell/checklist_cell.dart | 91 ++++++++ .../checklist_cell/checklist_cell_editor.dart | 81 ++++++++ .../checklist_prograss_bar.dart | 89 ++++++++ .../widgets/cell/date_cell/date_cell.dart | 1 - .../widgets/cell/date_cell/date_editor.dart | 1 - .../select_option_cell.dart | 1 - .../select_option_editor.dart | 1 - .../widgets/filter/choicechip/checkbox.dart | 1 - .../select_option/select_option.dart | 1 - .../widgets/filter/choicechip/text.dart | 4 +- .../widgets/filter/create_filter_list.dart | 4 +- .../presentation/widgets/filter/menu.dart | 1 - .../widgets/filter/menu_item.dart | 2 +- .../widgets/header/field_cell.dart | 1 - .../widgets/header/field_type_extension.dart | 4 +- .../widgets/header/field_type_list.dart | 1 - .../header/field_type_option_editor.dart | 1 - .../widgets/header/grid_header.dart | 1 - .../widgets/header/type_option/builder.dart | 6 +- .../widgets/header/type_option/date.dart | 1 - .../widgets/header/type_option/number.dart | 1 - .../header/type_option/select_option.dart | 1 - .../presentation/widgets/row/row_detail.dart | 3 +- .../widgets/toolbar/grid_group.dart | 2 +- .../widgets/toolbar/grid_property.dart | 1 - .../settings/settings_dialog.dart | 1 - .../presentation/widgets/pop_up_action.dart | 1 - .../flowy_infra_ui/lib/flowy_infra_ui.dart | 2 + .../lib/style_widget}/text_field.dart | 8 +- frontend/app_flowy/pubspec.lock | 7 + frontend/app_flowy/pubspec.yaml | 1 + .../flowy-grid/src/entities/field_entities.rs | 6 +- .../filter_entities/checklist_filter.rs | 6 - .../src/entities/filter_entities/util.rs | 4 +- .../src/services/cell/any_cell_data.rs | 2 +- .../src/services/cell/cell_operation.rs | 6 +- .../src/services/field/type_option_builder.rs | 6 +- .../selection_type_option/checklist_filter.rs | 36 +++- .../checklist_type_option.rs | 4 +- .../selection_type_option/select_filter.rs | 2 +- .../select_type_option.rs | 6 +- .../flowy-grid/src/services/filter/cache.rs | 7 +- .../src/services/filter/controller.rs | 4 +- .../src/services/group/group_util.rs | 2 +- .../tests/grid/block_test/script.rs | 2 +- .../flowy-grid/tests/grid/block_test/util.rs | 4 +- .../flowy-grid/tests/grid/cell_test/test.rs | 2 +- .../grid/filter_test/checklist_filter_test.rs | 27 +++ .../flowy-grid/tests/grid/filter_test/mod.rs | 1 + .../tests/grid/filter_test/script.rs | 13 +- .../flowy-grid/tests/grid/grid_editor.rs | 14 +- 64 files changed, 715 insertions(+), 110 deletions(-) create mode 100644 frontend/app_flowy/lib/plugins/grid/application/cell/checklist_cell_bloc.dart create mode 100644 frontend/app_flowy/lib/plugins/grid/application/cell/checklist_cell_editor_bloc.dart delete mode 100644 frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/checklist_cell.dart create mode 100644 frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/checklist_cell/checklist_cell.dart create mode 100644 frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/checklist_cell/checklist_cell_editor.dart create mode 100644 frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/checklist_cell/checklist_prograss_bar.dart rename frontend/app_flowy/{lib/plugins/grid/presentation/widgets/filter => packages/flowy_infra_ui/lib/style_widget}/text_field.dart (89%) create mode 100644 frontend/rust-lib/flowy-grid/tests/grid/filter_test/checklist_filter_test.rs diff --git a/frontend/app_flowy/assets/translations/en.json b/frontend/app_flowy/assets/translations/en.json index 09aa4a2378..64f34ac1ca 100644 --- a/frontend/app_flowy/assets/translations/en.json +++ b/frontend/app_flowy/assets/translations/en.json @@ -265,6 +265,9 @@ "panelTitle": "Select an option or create one", "searchOption": "Search for an option" }, + "checklist": { + "panelTitle": "Search an option or create one" + }, "menuName": "Grid" }, "document": { diff --git a/frontend/app_flowy/lib/plugins/board/presentation/board_page.dart b/frontend/app_flowy/lib/plugins/board/presentation/board_page.dart index 0a40c0dd8b..0d736975ed 100644 --- a/frontend/app_flowy/lib/plugins/board/presentation/board_page.dart +++ b/frontend/app_flowy/lib/plugins/board/presentation/board_page.dart @@ -358,7 +358,7 @@ Widget? _buildHeaderIcon(GroupData customData) { break; case FieldType.URL: break; - case FieldType.CheckList: + case FieldType.Checklist: break; } diff --git a/frontend/app_flowy/lib/plugins/board/presentation/card/card_cell_builder.dart b/frontend/app_flowy/lib/plugins/board/presentation/card/card_cell_builder.dart index 2118efdb87..4e7c2daa8d 100644 --- a/frontend/app_flowy/lib/plugins/board/presentation/card/card_cell_builder.dart +++ b/frontend/app_flowy/lib/plugins/board/presentation/card/card_cell_builder.dart @@ -59,7 +59,7 @@ class BoardCellBuilder { editableNotifier: cellNotifier, key: key, ); - case FieldType.CheckList: + case FieldType.Checklist: return BoardChecklistCell( key: key, ); diff --git a/frontend/app_flowy/lib/plugins/grid/application/cell/cell_service/cell_controller.dart b/frontend/app_flowy/lib/plugins/grid/application/cell/cell_service/cell_controller.dart index 7935d1e630..a947ec327f 100644 --- a/frontend/app_flowy/lib/plugins/grid/application/cell/cell_service/cell_controller.dart +++ b/frontend/app_flowy/lib/plugins/grid/application/cell/cell_service/cell_controller.dart @@ -5,6 +5,8 @@ typedef GridCheckboxCellController = IGridCellController; typedef GridNumberCellController = IGridCellController; typedef GridSelectOptionCellController = IGridCellController; +typedef GridChecklistCellController + = IGridCellController; typedef GridDateCellController = IGridCellController; typedef GridURLCellController = IGridCellController; @@ -81,7 +83,7 @@ class GridCellControllerBuilder { ); case FieldType.MultiSelect: case FieldType.SingleSelect: - case FieldType.CheckList: + case FieldType.Checklist: final cellDataLoader = GridCellDataLoader( cellId: _cellId, parser: SelectOptionCellDataParser(), diff --git a/frontend/app_flowy/lib/plugins/grid/application/cell/checklist_cell_bloc.dart b/frontend/app_flowy/lib/plugins/grid/application/cell/checklist_cell_bloc.dart new file mode 100644 index 0000000000..02a212ced6 --- /dev/null +++ b/frontend/app_flowy/lib/plugins/grid/application/cell/checklist_cell_bloc.dart @@ -0,0 +1,93 @@ +import 'package:flowy_sdk/log.dart'; +import 'package:flowy_sdk/protobuf/flowy-grid/select_type_option.pb.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:freezed_annotation/freezed_annotation.dart'; +import 'dart:async'; +import 'cell_service/cell_service.dart'; +import 'select_option_service.dart'; +part 'checklist_cell_bloc.freezed.dart'; + +class ChecklistCellBloc extends Bloc { + final GridChecklistCellController cellController; + final SelectOptionFFIService _selectOptionService; + void Function()? _onCellChangedFn; + ChecklistCellBloc({ + required this.cellController, + }) : _selectOptionService = + SelectOptionFFIService(cellId: cellController.cellId), + super(ChecklistCellState.initial(cellController)) { + on( + (event, emit) async { + await event.when( + initial: () async { + _startListening(); + _loadOptions(); + }, + didReceiveOptions: (data) { + emit(state.copyWith( + allOptions: data.options, + selectedOptions: data.selectOptions, + percent: data.selectOptions.length.toDouble() / + data.options.length.toDouble(), + )); + }, + ); + }, + ); + } + + @override + Future close() async { + if (_onCellChangedFn != null) { + cellController.removeListener(_onCellChangedFn!); + _onCellChangedFn = null; + } + await cellController.dispose(); + return super.close(); + } + + void _startListening() { + _onCellChangedFn = cellController.startListening( + onCellFieldChanged: () { + _loadOptions(); + }, + onCellChanged: (_) {}, + ); + } + + void _loadOptions() { + _selectOptionService.getOptionContext().then((result) { + if (isClosed) return; + + return result.fold( + (data) => add(ChecklistCellEvent.didReceiveOptions(data)), + (err) => Log.error(err), + ); + }); + } +} + +@freezed +class ChecklistCellEvent with _$ChecklistCellEvent { + const factory ChecklistCellEvent.initial() = _InitialCell; + const factory ChecklistCellEvent.didReceiveOptions( + SelectOptionCellDataPB data) = _DidReceiveCellUpdate; +} + +@freezed +class ChecklistCellState with _$ChecklistCellState { + const factory ChecklistCellState({ + required List allOptions, + required List selectedOptions, + required double percent, + }) = _ChecklistCellState; + + factory ChecklistCellState.initial( + GridChecklistCellController cellController) { + return const ChecklistCellState( + allOptions: [], + selectedOptions: [], + percent: 0, + ); + } +} diff --git a/frontend/app_flowy/lib/plugins/grid/application/cell/checklist_cell_editor_bloc.dart b/frontend/app_flowy/lib/plugins/grid/application/cell/checklist_cell_editor_bloc.dart new file mode 100644 index 0000000000..f0eb3b50b7 --- /dev/null +++ b/frontend/app_flowy/lib/plugins/grid/application/cell/checklist_cell_editor_bloc.dart @@ -0,0 +1,194 @@ +import 'dart:async'; + +import 'package:app_flowy/plugins/grid/application/cell/cell_service/cell_service.dart'; +import 'package:dartz/dartz.dart'; +import 'package:flowy_sdk/log.dart'; +import 'package:flowy_sdk/protobuf/flowy-grid/select_type_option.pb.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:freezed_annotation/freezed_annotation.dart'; + +import 'select_option_service.dart'; + +part 'checklist_cell_editor_bloc.freezed.dart'; + +class ChecklistCellEditorBloc + extends Bloc { + final SelectOptionFFIService _selectOptionService; + final GridChecklistCellController cellController; + Timer? _delayOperation; + + ChecklistCellEditorBloc({ + required this.cellController, + }) : _selectOptionService = + SelectOptionFFIService(cellId: cellController.cellId), + super(ChecklistCellEditorState.initial(cellController)) { + on( + (event, emit) async { + await event.when( + initial: () async { + _startListening(); + _loadOptions(); + }, + didReceiveOptions: (data) { + emit(state.copyWith( + allOptions: _makeChecklistSelectOptions(data, state.predicate), + percent: _percentFromSelectOptionCellData(data), + )); + }, + newOption: (optionName) { + _createOption(optionName); + emit(state.copyWith( + predicate: '', + )); + }, + deleteOption: (option) { + _deleteOption([option]); + }, + updateOption: (option) { + _updateOption(option); + }, + selectOption: (optionId) { + _selectOptionService.select(optionIds: [optionId]); + }, + unSelectOption: (optionId) { + _selectOptionService.unSelect(optionIds: [optionId]); + }, + filterOption: (String predicate) {}, + ); + }, + ); + } + + @override + Future close() async { + _delayOperation?.cancel(); + await cellController.dispose(); + return super.close(); + } + + void _createOption(String name) async { + final result = await _selectOptionService.create( + name: name, + isSelected: false, + ); + result.fold((l) => {}, (err) => Log.error(err)); + } + + void _deleteOption(List options) async { + final result = await _selectOptionService.delete(options: options); + result.fold((l) => null, (err) => Log.error(err)); + } + + void _updateOption(SelectOptionPB option) async { + final result = await _selectOptionService.update( + option: option, + ); + + result.fold((l) => null, (err) => Log.error(err)); + } + + void _loadOptions() { + _selectOptionService.getOptionContext().then((result) { + if (isClosed) return; + + return result.fold( + (data) => add(ChecklistCellEditorEvent.didReceiveOptions(data)), + (err) => Log.error(err), + ); + }); + } + + void _startListening() { + cellController.startListening( + onCellChanged: ((data) { + if (!isClosed && data != null) { + add(ChecklistCellEditorEvent.didReceiveOptions(data)); + } + }), + onCellFieldChanged: () { + _loadOptions(); + }, + ); + } +} + +@freezed +class ChecklistCellEditorEvent with _$ChecklistCellEditorEvent { + const factory ChecklistCellEditorEvent.initial() = _Initial; + const factory ChecklistCellEditorEvent.didReceiveOptions( + SelectOptionCellDataPB data) = _DidReceiveOptions; + const factory ChecklistCellEditorEvent.newOption(String optionName) = + _NewOption; + const factory ChecklistCellEditorEvent.selectOption(String optionId) = + _SelectOption; + const factory ChecklistCellEditorEvent.unSelectOption(String optionId) = + _UnSelectOption; + const factory ChecklistCellEditorEvent.updateOption(SelectOptionPB option) = + _UpdateOption; + const factory ChecklistCellEditorEvent.deleteOption(SelectOptionPB option) = + _DeleteOption; + const factory ChecklistCellEditorEvent.filterOption(String predicate) = + _FilterOption; +} + +@freezed +class ChecklistCellEditorState with _$ChecklistCellEditorState { + const factory ChecklistCellEditorState({ + required List allOptions, + required Option createOption, + required double percent, + required String predicate, + }) = _ChecklistCellEditorState; + + factory ChecklistCellEditorState.initial( + GridSelectOptionCellController context) { + final data = context.getCellData(loadIfNotExist: true); + + return ChecklistCellEditorState( + allOptions: _makeChecklistSelectOptions(data, ''), + createOption: none(), + percent: _percentFromSelectOptionCellData(data), + predicate: '', + ); + } +} + +double _percentFromSelectOptionCellData(SelectOptionCellDataPB? data) { + if (data == null) return 0; + + final a = data.selectOptions.length.toDouble(); + final b = data.options.length.toDouble(); + + if (a > b) return 1.0; + + return a / b; +} + +List _makeChecklistSelectOptions( + SelectOptionCellDataPB? data, String predicate) { + if (data == null) { + return []; + } + + final List options = []; + final List allOptions = List.from(data.options); + if (predicate.isNotEmpty) { + allOptions.retainWhere((element) => element.name.contains(predicate)); + } + final selectedOptionIds = data.selectOptions.map((e) => e.id).toList(); + + for (final option in allOptions) { + options.add( + ChecklistSelectOption(selectedOptionIds.contains(option.id), option), + ); + } + + return options; +} + +class ChecklistSelectOption { + final bool isSelected; + final SelectOptionPB data; + + ChecklistSelectOption(this.isSelected, this.data); +} diff --git a/frontend/app_flowy/lib/plugins/grid/application/cell/select_option_editor_bloc.dart b/frontend/app_flowy/lib/plugins/grid/application/cell/select_option_editor_bloc.dart index 9c3eb9cf23..1b60c0057d 100644 --- a/frontend/app_flowy/lib/plugins/grid/application/cell/select_option_editor_bloc.dart +++ b/frontend/app_flowy/lib/plugins/grid/application/cell/select_option_editor_bloc.dart @@ -1,26 +1,24 @@ import 'dart:async'; - import 'package:app_flowy/plugins/grid/application/cell/cell_service/cell_service.dart'; import 'package:dartz/dartz.dart'; import 'package:flowy_sdk/log.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/select_type_option.pb.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; - import 'select_option_service.dart'; part 'select_option_editor_bloc.freezed.dart'; class SelectOptionCellEditorBloc extends Bloc { - final SelectOptionService _selectOptionService; + final SelectOptionFFIService _selectOptionService; final GridSelectOptionCellController cellController; Timer? _delayOperation; SelectOptionCellEditorBloc({ required this.cellController, }) : _selectOptionService = - SelectOptionService(cellId: cellController.cellId), + SelectOptionFFIService(cellId: cellController.cellId), super(SelectOptionEditorState.initial(cellController)) { on( (event, emit) async { diff --git a/frontend/app_flowy/lib/plugins/grid/application/cell/select_option_service.dart b/frontend/app_flowy/lib/plugins/grid/application/cell/select_option_service.dart index a179ccab30..5f8639967a 100644 --- a/frontend/app_flowy/lib/plugins/grid/application/cell/select_option_service.dart +++ b/frontend/app_flowy/lib/plugins/grid/application/cell/select_option_service.dart @@ -6,15 +6,16 @@ import 'package:app_flowy/plugins/grid/application/field/type_option/type_option import 'package:flowy_sdk/protobuf/flowy-grid/select_type_option.pb.dart'; import 'cell_service/cell_service.dart'; -class SelectOptionService { +class SelectOptionFFIService { final GridCellIdentifier cellId; - SelectOptionService({required this.cellId}); + SelectOptionFFIService({required this.cellId}); String get gridId => cellId.gridId; String get fieldId => cellId.fieldInfo.id; String get rowId => cellId.rowId; - Future> create({required String name}) { + Future> create( + {required String name, bool isSelected = true}) { return TypeOptionFFIService(gridId: gridId, fieldId: fieldId) .newOption(name: name) .then( @@ -26,8 +27,13 @@ class SelectOptionService { ..fieldId = fieldId ..rowId = rowId; final payload = SelectOptionChangesetPB.create() - ..insertOptions.add(option) ..cellIdentifier = cellIdentifier; + + if (isSelected) { + payload.insertOptions.add(option); + } else { + payload.updateOptions.add(option); + } return GridEventUpdateSelectOption(payload).send(); }, (r) => right(r), diff --git a/frontend/app_flowy/lib/plugins/grid/application/field/field_controller.dart b/frontend/app_flowy/lib/plugins/grid/application/field/field_controller.dart index e38eb49038..ffe50ffa72 100644 --- a/frontend/app_flowy/lib/plugins/grid/application/field/field_controller.dart +++ b/frontend/app_flowy/lib/plugins/grid/application/field/field_controller.dart @@ -503,7 +503,7 @@ class FieldInfo { bool get hasFilter => _hasFilter; - bool get canGroup { + bool get canBeGroup { switch (_field.fieldType) { case FieldType.Checkbox: case FieldType.MultiSelect: diff --git a/frontend/app_flowy/lib/plugins/grid/application/filter/filter_create_bloc.dart b/frontend/app_flowy/lib/plugins/grid/application/filter/filter_create_bloc.dart index 7693a49321..4b5bb56f32 100644 --- a/frontend/app_flowy/lib/plugins/grid/application/filter/filter_create_bloc.dart +++ b/frontend/app_flowy/lib/plugins/grid/application/filter/filter_create_bloc.dart @@ -105,7 +105,7 @@ class GridCreateFilterBloc condition: SelectOptionCondition.OptionIs, fieldType: FieldType.MultiSelect, ); - case FieldType.CheckList: + case FieldType.Checklist: return _ffiService.insertChecklistFilter( fieldId: fieldId, condition: ChecklistFilterCondition.IsIncomplete, diff --git a/frontend/app_flowy/lib/plugins/grid/application/filter/filter_service.dart b/frontend/app_flowy/lib/plugins/grid/application/filter/filter_service.dart index f57fef31ef..f30a9dc263 100644 --- a/frontend/app_flowy/lib/plugins/grid/application/filter/filter_service.dart +++ b/frontend/app_flowy/lib/plugins/grid/application/filter/filter_service.dart @@ -152,14 +152,12 @@ class FilterFFIService { String? filterId, List optionIds = const [], }) { - final filter = ChecklistFilterPB() - ..condition = condition - ..optionIds.addAll(optionIds); + final filter = ChecklistFilterPB()..condition = condition; return insertFilter( fieldId: fieldId, filterId: filterId, - fieldType: FieldType.CheckList, + fieldType: FieldType.Checklist, data: filter.writeToBuffer(), ); } diff --git a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/cell_builder.dart b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/cell_builder.dart index 887470bdbd..75ab94cad1 100755 --- a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/cell_builder.dart +++ b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/cell_builder.dart @@ -6,7 +6,7 @@ import 'package:flutter/material.dart'; import 'cell_accessory.dart'; import 'cell_shortcuts.dart'; import 'checkbox_cell.dart'; -import 'checklist_cell.dart'; +import 'checklist_cell/checklist_cell.dart'; import 'date_cell/date_cell.dart'; import 'number_cell.dart'; import 'select_option_cell/select_option_cell.dart'; @@ -56,8 +56,9 @@ class GridCellBuilder { style: style, key: key, ); - case FieldType.CheckList: + case FieldType.Checklist: return GridChecklistCell( + cellControllerBuilder: cellControllerBuilder, key: key, ); case FieldType.Number: diff --git a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/checklist_cell.dart b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/checklist_cell.dart deleted file mode 100644 index 6499789636..0000000000 --- a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/checklist_cell.dart +++ /dev/null @@ -1,17 +0,0 @@ -import 'package:flutter/material.dart'; - -import 'cell_builder.dart'; - -class GridChecklistCell extends GridCellWidget { - GridChecklistCell({Key? key}) : super(key: key); - - @override - ChecklistCellState createState() => ChecklistCellState(); -} - -class ChecklistCellState extends State { - @override - Widget build(BuildContext context) { - return Container(); - } -} diff --git a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/checklist_cell/checklist_cell.dart b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/checklist_cell/checklist_cell.dart new file mode 100644 index 0000000000..213aaef90e --- /dev/null +++ b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/checklist_cell/checklist_cell.dart @@ -0,0 +1,91 @@ +import 'package:app_flowy/plugins/grid/application/cell/cell_service/cell_service.dart'; +import 'package:app_flowy/plugins/grid/application/cell/checklist_cell_bloc.dart'; +import 'package:app_flowy/plugins/grid/presentation/layout/sizes.dart'; +import 'package:appflowy_popover/appflowy_popover.dart'; +import 'package:flowy_infra_ui/flowy_infra_ui.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import '../cell_builder.dart'; +import 'checklist_cell_editor.dart'; +import 'checklist_prograss_bar.dart'; + +class GridChecklistCell extends GridCellWidget { + final GridCellControllerBuilder cellControllerBuilder; + GridChecklistCell({required this.cellControllerBuilder, Key? key}) + : super(key: key); + + @override + GridChecklistCellState createState() => GridChecklistCellState(); +} + +class GridChecklistCellState extends State { + late PopoverController _popover; + late ChecklistCellBloc _cellBloc; + + @override + void initState() { + _popover = PopoverController(); + final cellController = + widget.cellControllerBuilder.build() as GridChecklistCellController; + _cellBloc = ChecklistCellBloc(cellController: cellController); + _cellBloc.add(const ChecklistCellEvent.initial()); + super.initState(); + } + + @override + Widget build(BuildContext context) { + return BlocProvider.value( + value: _cellBloc, + child: BlocBuilder( + builder: (context, state) { + return Stack( + alignment: AlignmentDirectional.center, + fit: StackFit.expand, + children: [ + Padding( + padding: GridSize.cellContentInsets, + child: _wrapPopover(const ChecklistProgressBar()), + ), + InkWell(onTap: () => _popover.show()), + ], + ); + }, + ), + ); + } + + Widget _wrapPopover(Widget child) { + return AppFlowyPopover( + controller: _popover, + constraints: BoxConstraints.loose(const Size(260, 400)), + direction: PopoverDirection.bottomWithLeftAligned, + triggerActions: PopoverTriggerFlags.none, + popupBuilder: (BuildContext context) { + WidgetsBinding.instance.addPostFrameCallback((_) { + widget.onCellEditing.value = true; + }); + return GridChecklistCellEditor( + cellController: widget.cellControllerBuilder.build() + as GridChecklistCellController, + ); + }, + onClose: () => widget.onCellEditing.value = false, + child: child, + ); + } +} + +class ChecklistProgressBar extends StatelessWidget { + const ChecklistProgressBar({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return BlocBuilder( + builder: (context, state) { + return ChecklistPrograssBar( + percent: state.percent, + ); + }, + ); + } +} diff --git a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/checklist_cell/checklist_cell_editor.dart b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/checklist_cell/checklist_cell_editor.dart new file mode 100644 index 0000000000..f3cb2fc4fb --- /dev/null +++ b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/checklist_cell/checklist_cell_editor.dart @@ -0,0 +1,81 @@ +import 'package:app_flowy/plugins/grid/application/cell/cell_service/cell_service.dart'; +import 'package:app_flowy/plugins/grid/application/cell/checklist_cell_editor_bloc.dart'; +import 'package:app_flowy/plugins/grid/presentation/layout/sizes.dart'; +import 'package:app_flowy/plugins/grid/presentation/widgets/cell/checklist_cell/checklist_prograss_bar.dart'; +import 'package:flowy_infra_ui/style_widget/scrolling/styled_list.dart'; +import 'package:flowy_infra_ui/widget/spacing.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +class GridChecklistCellEditor extends StatefulWidget { + final GridChecklistCellController cellController; + const GridChecklistCellEditor({required this.cellController, Key? key}) + : super(key: key); + + @override + State createState() => + _GridChecklistCellEditorState(); +} + +class _GridChecklistCellEditorState extends State { + late ChecklistCellEditorBloc bloc; + + @override + void initState() { + bloc = ChecklistCellEditorBloc(cellController: widget.cellController); + bloc.add(const ChecklistCellEditorEvent.initial()); + super.initState(); + } + + @override + void dispose() { + bloc.close(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return BlocProvider.value( + value: bloc, + child: BlocBuilder( + builder: (context, state) { + final List slivers = [ + const SliverChecklistPrograssBar(), + SliverToBoxAdapter( + child: ListView.separated( + controller: ScrollController(), + shrinkWrap: true, + itemCount: state.allOptions.length, + itemBuilder: (BuildContext context, int index) { + return _ChecklistOptionCell(option: state.allOptions[index]); + }, + separatorBuilder: (BuildContext context, int index) { + return VSpace(GridSize.typeOptionSeparatorHeight); + }, + ), + ), + ]; + return CustomScrollView( + shrinkWrap: true, + slivers: slivers, + controller: ScrollController(), + physics: StyledScrollPhysics(), + ); + }, + ), + ); + } +} + +class _ChecklistOptionCell extends StatelessWidget { + final ChecklistSelectOption option; + const _ChecklistOptionCell({ + required this.option, + Key? key, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Container(height: 20, width: 100, color: Colors.red); + } +} diff --git a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/checklist_cell/checklist_prograss_bar.dart b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/checklist_cell/checklist_prograss_bar.dart new file mode 100644 index 0000000000..bbb097309c --- /dev/null +++ b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/checklist_cell/checklist_prograss_bar.dart @@ -0,0 +1,89 @@ +import 'package:app_flowy/generated/locale_keys.g.dart'; +import 'package:app_flowy/plugins/grid/application/cell/checklist_cell_editor_bloc.dart'; +import 'package:easy_localization/easy_localization.dart'; +import 'package:flowy_infra/color_extension.dart'; +import 'package:flowy_infra_ui/flowy_infra_ui.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:percent_indicator/percent_indicator.dart'; + +class ChecklistPrograssBar extends StatelessWidget { + final double percent; + const ChecklistPrograssBar({required this.percent, Key? key}) + : super(key: key); + + @override + Widget build(BuildContext context) { + return LinearPercentIndicator( + lineHeight: 10.0, + percent: percent, + progressColor: Theme.of(context).colorScheme.primary, + backgroundColor: AFThemeExtension.of(context).tint9, + barRadius: const Radius.circular(5), + ); + } +} + +class SliverChecklistPrograssBar extends StatelessWidget { + const SliverChecklistPrograssBar({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return SliverPersistentHeader( + pinned: true, + delegate: _SliverChecklistPrograssBarDelegate(), + ); + } +} + +class _SliverChecklistPrograssBarDelegate + extends SliverPersistentHeaderDelegate { + _SliverChecklistPrograssBarDelegate(); + + double fixHeight = 80; + + @override + Widget build( + BuildContext context, double shrinkOffset, bool overlapsContent) { + return BlocBuilder( + builder: (context, state) { + return Column( + children: [ + if (state.percent != 0) + Padding( + padding: const EdgeInsets.symmetric(vertical: 8.0), + child: ChecklistPrograssBar(percent: state.percent), + ), + Padding( + padding: const EdgeInsets.all(8.0), + child: FlowyTextField( + hintText: LocaleKeys.grid_checklist_panelTitle.tr(), + onChanged: (text) { + context + .read() + .add(ChecklistCellEditorEvent.filterOption(text)); + }, + onSubmitted: (text) { + context + .read() + .add(ChecklistCellEditorEvent.newOption(text)); + }, + ), + ) + ], + ); + }, + ); + } + + @override + double get maxExtent => fixHeight; + + @override + double get minExtent => fixHeight; + + @override + bool shouldRebuild(covariant SliverPersistentHeaderDelegate oldDelegate) { + return false; + } +} diff --git a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/date_cell/date_cell.dart b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/date_cell/date_cell.dart index bfe9ffd65c..43fdcd647c 100644 --- a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/date_cell/date_cell.dart +++ b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/date_cell/date_cell.dart @@ -1,5 +1,4 @@ import 'package:flowy_infra_ui/flowy_infra_ui.dart'; -import 'package:flowy_infra_ui/style_widget/text.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:app_flowy/startup/startup.dart'; diff --git a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/date_cell/date_editor.dart b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/date_cell/date_editor.dart index 99920837d5..37bd8668de 100644 --- a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/date_cell/date_editor.dart +++ b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/date_cell/date_editor.dart @@ -11,7 +11,6 @@ import 'package:flowy_infra/image.dart'; import 'package:flowy_infra/size.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/text.dart'; import 'package:flowy_infra_ui/widget/rounded_input_field.dart'; import 'package:flowy_infra_ui/widget/spacing.dart'; import 'package:flowy_sdk/log.dart'; diff --git a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/select_option_cell/select_option_cell.dart b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/select_option_cell/select_option_cell.dart index 7927002d73..bf4966dd51 100644 --- a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/select_option_cell/select_option_cell.dart +++ b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/select_option_cell/select_option_cell.dart @@ -3,7 +3,6 @@ import 'package:app_flowy/plugins/grid/application/prelude.dart'; import 'package:appflowy_popover/appflowy_popover.dart'; import 'package:flowy_infra_ui/flowy_infra_ui.dart'; -import 'package:flowy_infra_ui/style_widget/text.dart'; // ignore: unused_import import 'package:flowy_sdk/log.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/select_type_option.pb.dart'; diff --git a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/select_option_cell/select_option_editor.dart b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/select_option_cell/select_option_editor.dart index 1d244c85a3..36b114a350 100644 --- a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/select_option_cell/select_option_editor.dart +++ b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/select_option_cell/select_option_editor.dart @@ -9,7 +9,6 @@ import 'package:flowy_infra_ui/flowy_infra_ui.dart'; import 'package:flowy_infra_ui/style_widget/icon_button.dart'; import 'package:flowy_infra_ui/style_widget/scrolling/styled_list.dart'; import 'package:flowy_infra_ui/widget/spacing.dart'; -import 'package:flowy_infra_ui/style_widget/text.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/select_type_option.pb.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; diff --git a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/filter/choicechip/checkbox.dart b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/filter/choicechip/checkbox.dart index 00f43fc01e..6f8edaa656 100644 --- a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/filter/choicechip/checkbox.dart +++ b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/filter/choicechip/checkbox.dart @@ -8,7 +8,6 @@ import 'package:appflowy_popover/appflowy_popover.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flowy_infra/image.dart'; import 'package:flowy_infra_ui/flowy_infra_ui.dart'; -import 'package:flowy_infra_ui/style_widget/text.dart'; import 'package:flowy_infra_ui/widget/spacing.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/checkbox_filter.pbenum.dart'; import 'package:flutter/material.dart'; diff --git a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/filter/choicechip/select_option/select_option.dart b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/filter/choicechip/select_option/select_option.dart index da78dcd09b..472abf7d8a 100644 --- a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/filter/choicechip/select_option/select_option.dart +++ b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/filter/choicechip/select_option/select_option.dart @@ -4,7 +4,6 @@ import 'package:app_flowy/plugins/grid/presentation/widgets/filter/filter_info.d import 'package:appflowy_popover/appflowy_popover.dart'; import 'package:flowy_infra_ui/flowy_infra_ui.dart'; import 'package:flowy_infra_ui/style_widget/scrolling/styled_list.dart'; -import 'package:flowy_infra_ui/style_widget/text.dart'; import 'package:flowy_infra_ui/widget/spacing.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/select_option_filter.pb.dart'; diff --git a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/filter/choicechip/text.dart b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/filter/choicechip/text.dart index 8da46df633..1f591ef421 100644 --- a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/filter/choicechip/text.dart +++ b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/filter/choicechip/text.dart @@ -3,13 +3,11 @@ import 'package:app_flowy/plugins/grid/application/filter/text_filter_editor_blo import 'package:app_flowy/plugins/grid/presentation/widgets/filter/condition_button.dart'; import 'package:app_flowy/plugins/grid/presentation/widgets/filter/disclosure_button.dart'; import 'package:app_flowy/plugins/grid/presentation/widgets/filter/filter_info.dart'; -import 'package:app_flowy/plugins/grid/presentation/widgets/filter/text_field.dart'; import 'package:app_flowy/workspace/presentation/widgets/pop_up_action.dart'; import 'package:appflowy_popover/appflowy_popover.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flowy_infra/image.dart'; import 'package:flowy_infra_ui/flowy_infra_ui.dart'; -import 'package:flowy_infra_ui/style_widget/text.dart'; import 'package:flowy_infra_ui/widget/spacing.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/text_filter.pb.dart'; import 'package:flutter/material.dart'; @@ -151,7 +149,7 @@ class _TextFilterEditorState extends State { Widget _buildFilterTextField( BuildContext context, TextFilterEditorState state) { - return FilterTextField( + return FlowyTextField( text: state.filter.content, hintText: LocaleKeys.grid_settings_typeAValue.tr(), autoFucous: false, diff --git a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/filter/create_filter_list.dart b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/filter/create_filter_list.dart index 4a424ddb92..20fd1b4cca 100644 --- a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/filter/create_filter_list.dart +++ b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/filter/create_filter_list.dart @@ -2,13 +2,13 @@ import 'package:app_flowy/generated/locale_keys.g.dart'; import 'package:app_flowy/plugins/grid/application/field/field_controller.dart'; import 'package:app_flowy/plugins/grid/application/filter/filter_create_bloc.dart'; import 'package:app_flowy/plugins/grid/presentation/layout/sizes.dart'; -import 'package:app_flowy/plugins/grid/presentation/widgets/filter/text_field.dart'; import 'package:app_flowy/plugins/grid/presentation/widgets/header/field_type_extension.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flowy_infra/image.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'; +import 'package:flowy_infra_ui/style_widget/text_field.dart'; import 'package:flowy_infra_ui/widget/spacing.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -121,7 +121,7 @@ class _FilterTextFieldDelegate extends SliverPersistentHeaderDelegate { child: Container( color: Theme.of(context).colorScheme.background, height: fixHeight, - child: FilterTextField( + child: FlowyTextField( hintText: LocaleKeys.grid_settings_filterBy.tr(), onChanged: (text) { context diff --git a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/filter/menu.dart b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/filter/menu.dart index 1b12e6f862..54938a19f7 100644 --- a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/filter/menu.dart +++ b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/filter/menu.dart @@ -7,7 +7,6 @@ import 'package:flowy_infra/color_extension.dart'; import 'package:flowy_infra/image.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/text.dart'; import 'package:flowy_infra_ui/widget/spacing.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; diff --git a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/filter/menu_item.dart b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/filter/menu_item.dart index 3c733443ab..062fc21cbd 100644 --- a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/filter/menu_item.dart +++ b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/filter/menu_item.dart @@ -36,7 +36,7 @@ Widget buildFilterChoicechip(FilterInfo filterInfo) { return SelectOptionFilterChoicechip(filterInfo: filterInfo); case FieldType.URL: return URLFilterChoicechip(filterInfo: filterInfo); - case FieldType.CheckList: + case FieldType.Checklist: return ChecklistFilterChoicechip(filterInfo: filterInfo); default: return const SizedBox(); diff --git a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/field_cell.dart b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/field_cell.dart index 09d72aaba0..a0d2ed1092 100755 --- a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/field_cell.dart +++ b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/field_cell.dart @@ -6,7 +6,6 @@ import 'package:flowy_infra/image.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/hover.dart'; -import 'package:flowy_infra_ui/style_widget/text.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; diff --git a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/field_type_extension.dart b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/field_type_extension.dart index afc7f1c74f..2c1a616d13 100644 --- a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/field_type_extension.dart +++ b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/field_type_extension.dart @@ -19,7 +19,7 @@ extension FieldTypeListExtension on FieldType { return "grid/field/single_select"; case FieldType.URL: return "grid/field/url"; - case FieldType.CheckList: + case FieldType.Checklist: return "grid/field/checklist"; } throw UnimplementedError; @@ -41,7 +41,7 @@ extension FieldTypeListExtension on FieldType { return LocaleKeys.grid_field_singleSelectFieldName.tr(); case FieldType.URL: return LocaleKeys.grid_field_urlFieldName.tr(); - case FieldType.CheckList: + case FieldType.Checklist: return LocaleKeys.grid_field_checklistFieldName.tr(); } throw UnimplementedError; diff --git a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/field_type_list.dart b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/field_type_list.dart index 55d495f0ec..5c69644f3e 100644 --- a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/field_type_list.dart +++ b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/field_type_list.dart @@ -3,7 +3,6 @@ import 'package:flowy_infra/image.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'; import 'package:flowy_infra_ui/widget/spacing.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart'; import 'package:flutter/material.dart'; diff --git a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/field_type_option_editor.dart b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/field_type_option_editor.dart index 8476a41e6f..2354b8ba7b 100644 --- a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/field_type_option_editor.dart +++ b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/field_type_option_editor.dart @@ -5,7 +5,6 @@ import 'package:dartz/dartz.dart' show Either; import 'package:flowy_infra/image.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/text.dart'; import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart'; import 'package:flutter/material.dart'; diff --git a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/grid_header.dart b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/grid_header.dart index 72870c2373..76de39b228 100644 --- a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/grid_header.dart +++ b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/grid_header.dart @@ -9,7 +9,6 @@ import 'package:flowy_infra/color_extension.dart'; import 'package:flowy_infra/image.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/text.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; diff --git a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/type_option/builder.dart b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/type_option/builder.dart index d20658bbd2..0e2f45d9be 100644 --- a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/type_option/builder.dart +++ b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/type_option/builder.dart @@ -5,10 +5,10 @@ import 'package:app_flowy/plugins/grid/application/field/type_option/type_option import 'package:app_flowy/plugins/grid/application/field/type_option/type_option_data_controller.dart'; import 'package:appflowy_popover/appflowy_popover.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/checkbox_type_option.pb.dart'; +import 'package:flowy_sdk/protobuf/flowy-grid/checklist_type_option.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/date_type_option.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/multi_select_type_option.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/number_type_option.pb.dart'; -import 'package:flowy_sdk/protobuf/flowy-grid/protobuf.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/single_select_type_option.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/text_type_option.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/url_type_option.pb.dart'; @@ -127,7 +127,7 @@ TypeOptionWidgetBuilder makeTypeOptionWidgetBuilder({ ), ); - case FieldType.CheckList: + case FieldType.Checklist: return ChecklistTypeOptionWidgetBuilder( makeTypeOptionContextWithDataController( gridId: gridId, @@ -217,7 +217,7 @@ TypeOptionContext dataController: dataController, dataParser: MultiSelectTypeOptionWidgetDataParser(), ) as TypeOptionContext; - case FieldType.CheckList: + case FieldType.Checklist: return ChecklistTypeOptionContext( dataController: dataController, dataParser: ChecklistTypeOptionWidgetDataParser(), diff --git a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/type_option/date.dart b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/type_option/date.dart index 37ca30c412..92ab58e11b 100644 --- a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/type_option/date.dart +++ b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/type_option/date.dart @@ -7,7 +7,6 @@ import 'package:app_flowy/generated/locale_keys.g.dart'; import 'package:flowy_infra/image.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/text.dart'; import 'package:flowy_infra_ui/widget/spacing.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/date_type_option_entities.pb.dart'; import 'package:flutter/material.dart'; diff --git a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/type_option/number.dart b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/type_option/number.dart index 8fc17ee548..00be72ee50 100644 --- a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/type_option/number.dart +++ b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/type_option/number.dart @@ -5,7 +5,6 @@ import 'package:appflowy_popover/appflowy_popover.dart'; import 'package:flowy_infra/image.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/text.dart'; import 'package:flowy_infra_ui/widget/spacing.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/format.pbenum.dart'; import 'package:flutter/material.dart'; diff --git a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/type_option/select_option.dart b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/type_option/select_option.dart index fb3cb995cb..f78a3bc5a4 100644 --- a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/type_option/select_option.dart +++ b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/type_option/select_option.dart @@ -3,7 +3,6 @@ import 'package:appflowy_popover/appflowy_popover.dart'; import 'package:flowy_infra/image.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/text.dart'; import 'package:flowy_infra_ui/widget/spacing.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/select_type_option.pb.dart'; import 'package:flutter/material.dart'; diff --git a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/row/row_detail.dart b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/row/row_detail.dart index 25011110a2..fc090eb914 100644 --- a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/row/row_detail.dart +++ b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/row/row_detail.dart @@ -9,7 +9,6 @@ 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/icon_button.dart'; import 'package:flowy_infra_ui/style_widget/scrolling/styled_scroll_bar.dart'; -import 'package:flowy_infra_ui/style_widget/text.dart'; import 'package:flowy_infra_ui/widget/spacing.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:app_flowy/generated/locale_keys.g.dart'; @@ -331,7 +330,7 @@ GridCellStyle? _customCellStyle(FieldType fieldType) { return SelectOptionCellStyle( placeholder: LocaleKeys.grid_row_textPlaceholder.tr(), ); - case FieldType.CheckList: + case FieldType.Checklist: return SelectOptionCellStyle( placeholder: LocaleKeys.grid_row_textPlaceholder.tr(), ); diff --git a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/toolbar/grid_group.dart b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/toolbar/grid_group.dart index 472e9fa05c..1124175363 100644 --- a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/toolbar/grid_group.dart +++ b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/toolbar/grid_group.dart @@ -37,7 +37,7 @@ class GridGroupList extends StatelessWidget { key: ValueKey(fieldInfo.id), ); - if (!fieldInfo.canGroup) { + if (!fieldInfo.canBeGroup) { cell = IgnorePointer(child: Opacity(opacity: 0.3, child: cell)); } return cell; diff --git a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/toolbar/grid_property.dart b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/toolbar/grid_property.dart index 01d5c20dcf..0cc07179b4 100644 --- a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/toolbar/grid_property.dart +++ b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/toolbar/grid_property.dart @@ -8,7 +8,6 @@ import 'package:flowy_infra/image.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/icon_button.dart'; -import 'package:flowy_infra_ui/style_widget/text.dart'; import 'package:flowy_infra_ui/widget/spacing.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; diff --git a/frontend/app_flowy/lib/workspace/presentation/settings/settings_dialog.dart b/frontend/app_flowy/lib/workspace/presentation/settings/settings_dialog.dart index bd987017cc..11c2956764 100644 --- a/frontend/app_flowy/lib/workspace/presentation/settings/settings_dialog.dart +++ b/frontend/app_flowy/lib/workspace/presentation/settings/settings_dialog.dart @@ -6,7 +6,6 @@ import 'package:app_flowy/workspace/presentation/settings/widgets/settings_user_ import 'package:app_flowy/workspace/presentation/settings/widgets/settings_menu.dart'; import 'package:app_flowy/workspace/application/settings/settings_dialog_bloc.dart'; import 'package:flowy_infra_ui/flowy_infra_ui.dart'; -import 'package:flowy_infra_ui/style_widget/text.dart'; import 'package:flowy_sdk/protobuf/flowy-user/user_profile.pb.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; diff --git a/frontend/app_flowy/lib/workspace/presentation/widgets/pop_up_action.dart b/frontend/app_flowy/lib/workspace/presentation/widgets/pop_up_action.dart index a2ce989b9d..f7c48fdaaa 100644 --- a/frontend/app_flowy/lib/workspace/presentation/widgets/pop_up_action.dart +++ b/frontend/app_flowy/lib/workspace/presentation/widgets/pop_up_action.dart @@ -1,7 +1,6 @@ import 'package:appflowy_popover/appflowy_popover.dart'; import 'package:flowy_infra_ui/flowy_infra_ui.dart'; import 'package:flowy_infra_ui/style_widget/hover.dart'; -import 'package:flowy_infra_ui/style_widget/text.dart'; import 'package:flowy_infra_ui/widget/spacing.dart'; import 'package:flutter/material.dart'; import 'package:styled_widget/styled_widget.dart'; diff --git a/frontend/app_flowy/packages/flowy_infra_ui/lib/flowy_infra_ui.dart b/frontend/app_flowy/packages/flowy_infra_ui/lib/flowy_infra_ui.dart index fa20e57294..3cf675207b 100644 --- a/frontend/app_flowy/packages/flowy_infra_ui/lib/flowy_infra_ui.dart +++ b/frontend/app_flowy/packages/flowy_infra_ui/lib/flowy_infra_ui.dart @@ -10,3 +10,5 @@ export 'src/flowy_overlay/list_overlay.dart'; export 'src/flowy_overlay/option_overlay.dart'; export 'src/flowy_overlay/flowy_dialog.dart'; export 'src/flowy_overlay/appflowy_popover.dart'; +export 'style_widget/text.dart'; +export 'style_widget/text_field.dart'; diff --git a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/filter/text_field.dart b/frontend/app_flowy/packages/flowy_infra_ui/lib/style_widget/text_field.dart similarity index 89% rename from frontend/app_flowy/lib/plugins/grid/presentation/widgets/filter/text_field.dart rename to frontend/app_flowy/packages/flowy_infra_ui/lib/style_widget/text_field.dart index a76b7209b8..50f21bfbce 100644 --- a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/filter/text_field.dart +++ b/frontend/app_flowy/packages/flowy_infra_ui/lib/style_widget/text_field.dart @@ -1,13 +1,13 @@ import 'package:flowy_infra/size.dart'; import 'package:flutter/material.dart'; -class FilterTextField extends StatefulWidget { +class FlowyTextField extends StatefulWidget { final String hintText; final String text; final void Function(String)? onChanged; final void Function(String)? onSubmitted; final bool autoFucous; - const FilterTextField({ + const FlowyTextField({ this.hintText = "", this.text = "", this.onChanged, @@ -17,10 +17,10 @@ class FilterTextField extends StatefulWidget { }) : super(key: key); @override - State createState() => FilterTextFieldState(); + State createState() => FlowyTextFieldState(); } -class FilterTextFieldState extends State { +class FlowyTextFieldState extends State { late FocusNode focusNode; late TextEditingController controller; diff --git a/frontend/app_flowy/pubspec.lock b/frontend/app_flowy/pubspec.lock index 1ea678f95c..9ccd2d3076 100644 --- a/frontend/app_flowy/pubspec.lock +++ b/frontend/app_flowy/pubspec.lock @@ -862,6 +862,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.0.6" + percent_indicator: + dependency: "direct main" + description: + name: percent_indicator + url: "https://pub.dartlang.org" + source: hosted + version: "4.2.2" petitparser: dependency: transitive description: diff --git a/frontend/app_flowy/pubspec.yaml b/frontend/app_flowy/pubspec.yaml index f2147d5777..2cdf5ca3f0 100644 --- a/frontend/app_flowy/pubspec.yaml +++ b/frontend/app_flowy/pubspec.yaml @@ -90,6 +90,7 @@ dependencies: shared_preferences: ^2.0.15 google_fonts: ^3.0.1 file_picker: <=5.0.0 + percent_indicator: ^4.0.1 dev_dependencies: flutter_lints: ^2.0.1 diff --git a/frontend/rust-lib/flowy-grid/src/entities/field_entities.rs b/frontend/rust-lib/flowy-grid/src/entities/field_entities.rs index c0127aa89c..ea432586b7 100644 --- a/frontend/rust-lib/flowy-grid/src/entities/field_entities.rs +++ b/frontend/rust-lib/flowy-grid/src/entities/field_entities.rs @@ -491,7 +491,7 @@ pub enum FieldType { MultiSelect = 4, Checkbox = 5, URL = 6, - CheckList = 7, + Checklist = 7, } pub const RICH_TEXT_FIELD: FieldType = FieldType::RichText; @@ -501,7 +501,7 @@ pub const SINGLE_SELECT_FIELD: FieldType = FieldType::SingleSelect; pub const MULTI_SELECT_FIELD: FieldType = FieldType::MultiSelect; pub const CHECKBOX_FIELD: FieldType = FieldType::Checkbox; pub const URL_FIELD: FieldType = FieldType::URL; -pub const CHECKLIST_FIELD: FieldType = FieldType::CheckList; +pub const CHECKLIST_FIELD: FieldType = FieldType::Checklist; impl std::default::Default for FieldType { fn default() -> Self { @@ -602,7 +602,7 @@ impl std::convert::From for FieldType { 4 => FieldType::MultiSelect, 5 => FieldType::Checkbox, 6 => FieldType::URL, - 7 => FieldType::CheckList, + 7 => FieldType::Checklist, _ => { tracing::error!("Can't convert FieldTypeRevision: {} to FieldType", ty); FieldType::RichText diff --git a/frontend/rust-lib/flowy-grid/src/entities/filter_entities/checklist_filter.rs b/frontend/rust-lib/flowy-grid/src/entities/filter_entities/checklist_filter.rs index 59c5ad885a..a697ebbe11 100644 --- a/frontend/rust-lib/flowy-grid/src/entities/filter_entities/checklist_filter.rs +++ b/frontend/rust-lib/flowy-grid/src/entities/filter_entities/checklist_filter.rs @@ -1,4 +1,3 @@ -use crate::services::field::SelectOptionIds; use flowy_derive::{ProtoBuf, ProtoBuf_Enum}; use flowy_error::ErrorCode; use grid_rev_model::FilterRevision; @@ -7,9 +6,6 @@ use grid_rev_model::FilterRevision; pub struct ChecklistFilterPB { #[pb(index = 1)] pub condition: ChecklistFilterCondition, - - #[pb(index = 2)] - pub option_ids: Vec, } #[derive(Debug, Clone, PartialEq, Eq, ProtoBuf_Enum)] @@ -45,11 +41,9 @@ impl std::convert::TryFrom for ChecklistFilterCondition { impl std::convert::From<&FilterRevision> for ChecklistFilterPB { fn from(rev: &FilterRevision) -> Self { - let ids = SelectOptionIds::from(rev.content.clone()); ChecklistFilterPB { condition: ChecklistFilterCondition::try_from(rev.condition) .unwrap_or(ChecklistFilterCondition::IsIncomplete), - option_ids: ids.into_inner(), } } } diff --git a/frontend/rust-lib/flowy-grid/src/entities/filter_entities/util.rs b/frontend/rust-lib/flowy-grid/src/entities/filter_entities/util.rs index d553fefd45..57c8db310e 100644 --- a/frontend/rust-lib/flowy-grid/src/entities/filter_entities/util.rs +++ b/frontend/rust-lib/flowy-grid/src/entities/filter_entities/util.rs @@ -36,7 +36,7 @@ impl std::convert::From<&FilterRevision> for FilterPB { FieldType::DateTime => DateFilterPB::from(rev).try_into().unwrap(), FieldType::SingleSelect => SelectOptionFilterPB::from(rev).try_into().unwrap(), FieldType::MultiSelect => SelectOptionFilterPB::from(rev).try_into().unwrap(), - FieldType::CheckList => ChecklistFilterPB::from(rev).try_into().unwrap(), + FieldType::Checklist => ChecklistFilterPB::from(rev).try_into().unwrap(), FieldType::Checkbox => CheckboxFilterPB::from(rev).try_into().unwrap(), FieldType::URL => TextFilterPB::from(rev).try_into().unwrap(), }; @@ -176,7 +176,7 @@ impl TryInto for AlterFilterPayloadPB { } .to_string(); } - FieldType::SingleSelect | FieldType::MultiSelect | FieldType::CheckList => { + FieldType::SingleSelect | FieldType::MultiSelect | FieldType::Checklist => { let filter = SelectOptionFilterPB::try_from(bytes).map_err(|_| ErrorCode::ProtobufSerde)?; condition = filter.condition as u8; content = SelectOptionIds::from(filter.option_ids).to_string(); diff --git a/frontend/rust-lib/flowy-grid/src/services/cell/any_cell_data.rs b/frontend/rust-lib/flowy-grid/src/services/cell/any_cell_data.rs index caa76f4b54..b623d784e0 100644 --- a/frontend/rust-lib/flowy-grid/src/services/cell/any_cell_data.rs +++ b/frontend/rust-lib/flowy-grid/src/services/cell/any_cell_data.rs @@ -105,7 +105,7 @@ impl TypeCellData { self.field_type == FieldType::MultiSelect } pub fn is_checklist(&self) -> bool { - self.field_type == FieldType::CheckList + self.field_type == FieldType::Checklist } pub fn is_url(&self) -> bool { diff --git a/frontend/rust-lib/flowy-grid/src/services/cell/cell_operation.rs b/frontend/rust-lib/flowy-grid/src/services/cell/cell_operation.rs index 31ec2a3d93..52c47f56ea 100644 --- a/frontend/rust-lib/flowy-grid/src/services/cell/cell_operation.rs +++ b/frontend/rust-lib/flowy-grid/src/services/cell/cell_operation.rs @@ -122,7 +122,7 @@ pub fn apply_cell_data_changeset>( SingleSelectTypeOptionPB::from(field_rev).apply_changeset(changeset.into(), cell_rev) } FieldType::MultiSelect => MultiSelectTypeOptionPB::from(field_rev).apply_changeset(changeset.into(), cell_rev), - FieldType::CheckList => ChecklistTypeOptionPB::from(field_rev).apply_changeset(changeset.into(), cell_rev), + FieldType::Checklist => ChecklistTypeOptionPB::from(field_rev).apply_changeset(changeset.into(), cell_rev), FieldType::Checkbox => CheckboxTypeOptionPB::from(field_rev).apply_changeset(changeset.into(), cell_rev), FieldType::URL => URLTypeOptionPB::from(field_rev).apply_changeset(changeset.into(), cell_rev), }?; @@ -181,7 +181,7 @@ pub fn decode_cell_data_to_string( FieldType::MultiSelect => field_rev .get_type_option::(field_type)? .displayed_cell_string(cell_data.into(), from_field_type, field_rev), - FieldType::CheckList => field_rev + FieldType::Checklist => field_rev .get_type_option::(field_type)? .displayed_cell_string(cell_data.into(), from_field_type, field_rev), FieldType::Checkbox => field_rev @@ -234,7 +234,7 @@ pub fn try_decode_cell_data( FieldType::MultiSelect => field_rev .get_type_option::(field_type)? .decode_cell_data(cell_data.into(), from_field_type, field_rev), - FieldType::CheckList => field_rev + FieldType::Checklist => field_rev .get_type_option::(field_type)? .decode_cell_data(cell_data.into(), from_field_type, field_rev), FieldType::Checkbox => field_rev diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_option_builder.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_option_builder.rs index fcc5395686..10cb8b3630 100644 --- a/frontend/rust-lib/flowy-grid/src/services/field/type_option_builder.rs +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_option_builder.rs @@ -38,7 +38,7 @@ pub fn default_type_option_builder_from_type(field_type: &FieldType) -> Box MultiSelectTypeOptionPB::default().into(), FieldType::Checkbox => CheckboxTypeOptionPB::default().into(), FieldType::URL => URLTypeOptionPB::default().into(), - FieldType::CheckList => ChecklistTypeOptionPB::default().into(), + FieldType::Checklist => ChecklistTypeOptionPB::default().into(), }; type_option_builder_from_json_str(&s, field_type) @@ -53,7 +53,7 @@ pub fn type_option_builder_from_json_str(s: &str, field_type: &FieldType) -> Box FieldType::MultiSelect => Box::new(MultiSelectTypeOptionBuilder::from_json_str(s)), FieldType::Checkbox => Box::new(CheckboxTypeOptionBuilder::from_json_str(s)), FieldType::URL => Box::new(URLTypeOptionBuilder::from_json_str(s)), - FieldType::CheckList => Box::new(ChecklistTypeOptionBuilder::from_json_str(s)), + FieldType::Checklist => Box::new(ChecklistTypeOptionBuilder::from_json_str(s)), } } @@ -67,6 +67,6 @@ pub fn type_option_builder_from_bytes>(bytes: T, field_type: &Fie FieldType::MultiSelect => Box::new(MultiSelectTypeOptionBuilder::from_protobuf_bytes(bytes)), FieldType::Checkbox => Box::new(CheckboxTypeOptionBuilder::from_protobuf_bytes(bytes)), FieldType::URL => Box::new(URLTypeOptionBuilder::from_protobuf_bytes(bytes)), - FieldType::CheckList => Box::new(ChecklistTypeOptionBuilder::from_protobuf_bytes(bytes)), + FieldType::Checklist => Box::new(ChecklistTypeOptionBuilder::from_protobuf_bytes(bytes)), } } diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/checklist_filter.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/checklist_filter.rs index e69b2dfa2e..a5d1d98021 100644 --- a/frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/checklist_filter.rs +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/checklist_filter.rs @@ -1,8 +1,36 @@ -use crate::entities::ChecklistFilterPB; -use crate::services::field::SelectedSelectOptions; +use crate::entities::{ChecklistFilterCondition, ChecklistFilterPB}; +use crate::services::field::{SelectOptionPB, SelectedSelectOptions}; impl ChecklistFilterPB { - pub fn is_visible(&self, selected_options: &SelectedSelectOptions) -> bool { - true + pub fn is_visible(&self, all_options: &[SelectOptionPB], selected_options: &SelectedSelectOptions) -> bool { + let selected_option_ids = selected_options + .options + .iter() + .map(|option| option.id.as_str()) + .collect::>(); + + let mut all_option_ids = all_options + .iter() + .map(|option| option.id.as_str()) + .collect::>(); + + return match self.condition { + ChecklistFilterCondition::IsComplete => { + if selected_option_ids.is_empty() { + return false; + } + + all_option_ids.retain(|option_id| !selected_option_ids.contains(option_id)); + all_option_ids.is_empty() + } + ChecklistFilterCondition::IsIncomplete => { + if selected_option_ids.is_empty() { + return true; + } + + all_option_ids.retain(|option_id| !selected_option_ids.contains(option_id)); + !all_option_ids.is_empty() + } + }; } } diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/checklist_type_option.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/checklist_type_option.rs index a6975d8f7d..ae3c3b689d 100644 --- a/frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/checklist_type_option.rs +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/checklist_type_option.rs @@ -22,7 +22,7 @@ pub struct ChecklistTypeOptionPB { #[pb(index = 2)] pub disable_color: bool, } -impl_type_option!(ChecklistTypeOptionPB, FieldType::CheckList); +impl_type_option!(ChecklistTypeOptionPB, FieldType::Checklist); impl SelectTypeOptionSharedAction for ChecklistTypeOptionPB { fn number_of_max_options(&self) -> Option { @@ -101,7 +101,7 @@ impl ChecklistTypeOptionBuilder { impl TypeOptionBuilder for ChecklistTypeOptionBuilder { fn field_type(&self) -> FieldType { - FieldType::CheckList + FieldType::Checklist } fn serializer(&self) -> &dyn TypeOptionDataSerializer { diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/select_filter.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/select_filter.rs index 28a4735605..239be335a4 100644 --- a/frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/select_filter.rs +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/select_filter.rs @@ -67,7 +67,7 @@ impl CellFilterOperation for ChecklistTypeOptionPB { return Ok(true); } let selected_options = SelectedSelectOptions::from(self.get_selected_options(any_cell_data.into())); - Ok(filter.is_visible(&selected_options)) + Ok(filter.is_visible(&self.options, &selected_options)) } } diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/select_type_option.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/select_type_option.rs index 2dadb1248a..1f961b868a 100644 --- a/frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/select_type_option.rs +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/select_type_option.rs @@ -4,7 +4,7 @@ use crate::services::cell::{ CellBytes, CellBytesParser, CellData, CellDataIsEmpty, CellDisplayable, FromCellChangeset, FromCellString, }; use crate::services::field::selection_type_option::type_option_transform::SelectOptionTypeOptionTransformer; -use crate::services::field::{MultiSelectTypeOptionPB, SingleSelectTypeOptionPB}; +use crate::services::field::{ChecklistTypeOptionPB, MultiSelectTypeOptionPB, SingleSelectTypeOptionPB}; use bytes::Bytes; use flowy_derive::{ProtoBuf, ProtoBuf_Enum}; use flowy_error::{internal_error, ErrorCode, FlowyResult}; @@ -212,6 +212,10 @@ pub fn select_type_option_from_field_rev( let type_option = MultiSelectTypeOptionPB::from(field_rev); Ok(Box::new(type_option)) } + FieldType::Checklist => { + let type_option = ChecklistTypeOptionPB::from(field_rev); + Ok(Box::new(type_option)) + } ty => { tracing::error!("Unsupported field type: {:?} for this handler", ty); Err(ErrorCode::FieldInvalidOperation.into()) diff --git a/frontend/rust-lib/flowy-grid/src/services/filter/cache.rs b/frontend/rust-lib/flowy-grid/src/services/filter/cache.rs index 73b4e6bcc8..caffc0d777 100644 --- a/frontend/rust-lib/flowy-grid/src/services/filter/cache.rs +++ b/frontend/rust-lib/flowy-grid/src/services/filter/cache.rs @@ -29,7 +29,7 @@ impl FilterMap { FieldType::MultiSelect => self.select_option_filter.get(filter_type).is_some(), FieldType::Checkbox => self.checkbox_filter.get(filter_type).is_some(), FieldType::URL => self.url_filter.get(filter_type).is_some(), - FieldType::CheckList => self.checklist_filter.get(filter_type).is_some(), + FieldType::Checklist => self.checklist_filter.get(filter_type).is_some(), } } @@ -61,6 +61,9 @@ impl FilterMap { if !self.checkbox_filter.is_empty() { return false; } + if !self.checklist_filter.is_empty() { + return false; + } true } @@ -87,7 +90,7 @@ impl FilterMap { FieldType::URL => { let _ = self.url_filter.remove(filter_id); } - FieldType::CheckList => { + FieldType::Checklist => { let _ = self.checklist_filter.remove(filter_id); } }; diff --git a/frontend/rust-lib/flowy-grid/src/services/filter/controller.rs b/frontend/rust-lib/flowy-grid/src/services/filter/controller.rs index d855d00d04..49aaad8586 100644 --- a/frontend/rust-lib/flowy-grid/src/services/filter/controller.rs +++ b/frontend/rust-lib/flowy-grid/src/services/filter/controller.rs @@ -291,7 +291,7 @@ impl FilterController { .url_filter .insert(filter_type, TextFilterPB::from(filter_rev.as_ref())); } - FieldType::CheckList => { + FieldType::Checklist => { let _ = self .filter_map .checklist_filter @@ -419,7 +419,7 @@ fn filter_cell( .ok(), ) }), - FieldType::CheckList => filter_map.checklist_filter.get(filter_id).and_then(|filter| { + FieldType::Checklist => filter_map.checklist_filter.get(filter_id).and_then(|filter| { Some( field_rev .get_type_option::(field_rev.ty)? diff --git a/frontend/rust-lib/flowy-grid/src/services/group/group_util.rs b/frontend/rust-lib/flowy-grid/src/services/group/group_util.rs index ed9a36abc8..b8d94100e9 100644 --- a/frontend/rust-lib/flowy-grid/src/services/group/group_util.rs +++ b/frontend/rust-lib/flowy-grid/src/services/group/group_util.rs @@ -123,7 +123,7 @@ pub fn default_group_configuration(field_rev: &FieldRevision) -> GroupConfigurat SelectOptionGroupConfigurationRevision::default(), ) .unwrap(), - FieldType::CheckList => GroupConfigurationRevision::new( + FieldType::Checklist => GroupConfigurationRevision::new( field_id, field_type_rev, SelectOptionGroupConfigurationRevision::default(), diff --git a/frontend/rust-lib/flowy-grid/tests/grid/block_test/script.rs b/frontend/rust-lib/flowy-grid/tests/grid/block_test/script.rs index 5ececf4b05..2a3aa12add 100644 --- a/frontend/rust-lib/flowy-grid/tests/grid/block_test/script.rs +++ b/frontend/rust-lib/flowy-grid/tests/grid/block_test/script.rs @@ -226,7 +226,7 @@ impl GridRowTest { assert_eq!(s, expected); } - FieldType::CheckList => { + FieldType::Checklist => { let cell_data = self .editor .get_cell_bytes(&cell_id) diff --git a/frontend/rust-lib/flowy-grid/tests/grid/block_test/util.rs b/frontend/rust-lib/flowy-grid/tests/grid/block_test/util.rs index dfc5e5aa23..7f3e43275b 100644 --- a/frontend/rust-lib/flowy-grid/tests/grid/block_test/util.rs +++ b/frontend/rust-lib/flowy-grid/tests/grid/block_test/util.rs @@ -94,12 +94,12 @@ impl<'a> GridRowTestBuilder<'a> { where F: Fn(Vec) -> Vec, { - let checklist_field = self.field_rev_with_type(&FieldType::CheckList); + let checklist_field = self.field_rev_with_type(&FieldType::Checklist); let type_option = ChecklistTypeOptionPB::from(&checklist_field); let options = f(type_option.options); let ops_ids = options.iter().map(|option| option.id.clone()).collect::>(); self.inner_builder - .insert_select_option_cell(&multi_select_field.id, ops_ids); + .insert_select_option_cell(&checklist_field.id, ops_ids); checklist_field.id.clone() } diff --git a/frontend/rust-lib/flowy-grid/tests/grid/cell_test/test.rs b/frontend/rust-lib/flowy-grid/tests/grid/cell_test/test.rs index 5885d4ad37..81d99256c7 100644 --- a/frontend/rust-lib/flowy-grid/tests/grid/cell_test/test.rs +++ b/frontend/rust-lib/flowy-grid/tests/grid/cell_test/test.rs @@ -31,7 +31,7 @@ async fn grid_cell_update() { let type_option = MultiSelectTypeOptionPB::from(field_rev); SelectOptionCellChangeset::from_insert_option_id(&type_option.options.first().unwrap().id).to_str() } - FieldType::CheckList => { + FieldType::Checklist => { let type_option = ChecklistTypeOptionPB::from(field_rev); SelectOptionCellChangeset::from_insert_option_id(&type_option.options.first().unwrap().id).to_str() } diff --git a/frontend/rust-lib/flowy-grid/tests/grid/filter_test/checklist_filter_test.rs b/frontend/rust-lib/flowy-grid/tests/grid/filter_test/checklist_filter_test.rs new file mode 100644 index 0000000000..c7688cacbc --- /dev/null +++ b/frontend/rust-lib/flowy-grid/tests/grid/filter_test/checklist_filter_test.rs @@ -0,0 +1,27 @@ +use crate::grid::filter_test::script::FilterScript::*; +use crate::grid::filter_test::script::GridFilterTest; +use flowy_grid::entities::{ChecklistFilterCondition, SelectOptionCondition}; + +#[tokio::test] +async fn grid_filter_checklist_is_incomplete_test() { + let mut test = GridFilterTest::new().await; + let scripts = vec![ + CreateChecklistFilter { + condition: ChecklistFilterCondition::IsIncomplete, + }, + AssertNumberOfVisibleRows { expected: 4 }, + ]; + test.run_scripts(scripts).await; +} + +#[tokio::test] +async fn grid_filter_checklist_is_complete_test() { + let mut test = GridFilterTest::new().await; + let scripts = vec![ + CreateChecklistFilter { + condition: ChecklistFilterCondition::IsComplete, + }, + AssertNumberOfVisibleRows { expected: 1 }, + ]; + test.run_scripts(scripts).await; +} diff --git a/frontend/rust-lib/flowy-grid/tests/grid/filter_test/mod.rs b/frontend/rust-lib/flowy-grid/tests/grid/filter_test/mod.rs index c26a66ea3b..160bf3427f 100644 --- a/frontend/rust-lib/flowy-grid/tests/grid/filter_test/mod.rs +++ b/frontend/rust-lib/flowy-grid/tests/grid/filter_test/mod.rs @@ -1,4 +1,5 @@ mod checkbox_filter_test; +mod checklist_filter_test; mod date_filter_test; mod number_filter_test; mod script; diff --git a/frontend/rust-lib/flowy-grid/tests/grid/filter_test/script.rs b/frontend/rust-lib/flowy-grid/tests/grid/filter_test/script.rs index e9d6ab49c6..d09c910d19 100644 --- a/frontend/rust-lib/flowy-grid/tests/grid/filter_test/script.rs +++ b/frontend/rust-lib/flowy-grid/tests/grid/filter_test/script.rs @@ -6,7 +6,7 @@ use std::time::Duration; use bytes::Bytes; use futures::TryFutureExt; -use flowy_grid::entities::{AlterFilterParams, AlterFilterPayloadPB, DeleteFilterParams, GridLayout, GridSettingChangesetParams, GridSettingPB, RowPB, TextFilterCondition, FieldType, NumberFilterCondition, CheckboxFilterCondition, DateFilterCondition, DateFilterContent, SelectOptionCondition, TextFilterPB, NumberFilterPB, CheckboxFilterPB, DateFilterPB, SelectOptionFilterPB, CellChangesetPB, FilterPB}; +use flowy_grid::entities::{AlterFilterParams, AlterFilterPayloadPB, DeleteFilterParams, GridLayout, GridSettingChangesetParams, GridSettingPB, RowPB, TextFilterCondition, FieldType, NumberFilterCondition, CheckboxFilterCondition, DateFilterCondition, DateFilterContent, SelectOptionCondition, TextFilterPB, NumberFilterPB, CheckboxFilterPB, DateFilterPB, SelectOptionFilterPB, CellChangesetPB, FilterPB, ChecklistFilterCondition, ChecklistFilterPB}; use flowy_grid::services::field::{SelectOptionCellChangeset, SelectOptionIds}; use flowy_grid::services::setting::GridSettingChangesetBuilder; use grid_rev_model::{FieldRevision, FieldTypeRevision}; @@ -57,6 +57,9 @@ pub enum FilterScript { condition: SelectOptionCondition, option_ids: Vec, }, + CreateChecklistFilter { + condition: ChecklistFilterCondition, + }, AssertFilterCount { count: i32, }, @@ -184,6 +187,14 @@ impl GridFilterTest { AlterFilterPayloadPB::new(field_rev, filter); self.insert_filter(payload).await; } + FilterScript::CreateChecklistFilter { condition} => { + let field_rev = self.get_first_field_rev(FieldType::Checklist); + // let type_option = self.get_checklist_type_option(&field_rev.id); + let filter = ChecklistFilterPB { condition }; + let payload = + AlterFilterPayloadPB::new(field_rev, filter); + self.insert_filter(payload).await; + } FilterScript::AssertFilterCount { count } => { let filters = self.editor.get_all_filters().await.unwrap(); assert_eq!(count as usize, filters.len()); diff --git a/frontend/rust-lib/flowy-grid/tests/grid/grid_editor.rs b/frontend/rust-lib/flowy-grid/tests/grid/grid_editor.rs index a43ddde209..487a283ea2 100644 --- a/frontend/rust-lib/flowy-grid/tests/grid/grid_editor.rs +++ b/frontend/rust-lib/flowy-grid/tests/grid/grid_editor.rs @@ -131,6 +131,14 @@ impl GridEditorTest { type_option } + pub fn get_checklist_type_option(&self, field_id: &str) -> ChecklistTypeOptionPB { + let field_type = FieldType::Checklist; + let field_rev = self.get_field_rev(field_id, field_type.clone()); + let type_option = field_rev + .get_type_option::(field_type.into()) + .unwrap(); + type_option + } pub fn get_checkbox_type_option(&self, field_id: &str) -> CheckboxTypeOptionPB { let field_type = FieldType::Checkbox; let field_rev = self.get_field_rev(field_id, field_type.clone()); @@ -220,7 +228,7 @@ fn make_test_grid() -> BuildGridContext { let url_field = FieldBuilder::new(url).name("link").visibility(true).build(); grid_builder.add_field(url_field); } - FieldType::CheckList => { + FieldType::Checklist => { let checklist = ChecklistTypeOptionBuilder::default() .add_option(SelectOptionPB::new(FIRST_THING)) .add_option(SelectOptionPB::new(SECOND_THING)) @@ -245,7 +253,7 @@ fn make_test_grid() -> BuildGridContext { FieldType::DateTime => row_builder.insert_date_cell("1647251762"), FieldType::MultiSelect => row_builder .insert_multi_select_cell(|mut options| vec![options.remove(0), options.remove(0)]), - FieldType::CheckList => row_builder.insert_checklist_cell(|options| options), + FieldType::Checklist => row_builder.insert_checklist_cell(|options| options), FieldType::Checkbox => row_builder.insert_checkbox_cell("true"), _ => "".to_owned(), }; @@ -382,7 +390,7 @@ fn make_test_board() -> BuildGridContext { let url_field = FieldBuilder::new(url).name("link").visibility(true).build(); grid_builder.add_field(url_field); } - FieldType::CheckList => {} + FieldType::Checklist => {} } } From 3cdd6665b33cc886d08fa9791218fafafba0ef30 Mon Sep 17 00:00:00 2001 From: nathan Date: Wed, 30 Nov 2022 10:08:34 +0800 Subject: [PATCH 3/6] chore: config checklist cell --- .../checklist_cell/checklist_cell_editor.dart | 86 +++++++++++++++++-- .../checklist_prograss_bar.dart | 28 +++--- 2 files changed, 92 insertions(+), 22 deletions(-) diff --git a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/checklist_cell/checklist_cell_editor.dart b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/checklist_cell/checklist_cell_editor.dart index f3cb2fc4fb..3004a5c035 100644 --- a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/checklist_cell/checklist_cell_editor.dart +++ b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/checklist_cell/checklist_cell_editor.dart @@ -2,6 +2,11 @@ import 'package:app_flowy/plugins/grid/application/cell/cell_service/cell_servic import 'package:app_flowy/plugins/grid/application/cell/checklist_cell_editor_bloc.dart'; import 'package:app_flowy/plugins/grid/presentation/layout/sizes.dart'; import 'package:app_flowy/plugins/grid/presentation/widgets/cell/checklist_cell/checklist_prograss_bar.dart'; +import 'package:app_flowy/plugins/grid/presentation/widgets/header/type_option/select_option_editor.dart'; +import 'package:appflowy_popover/appflowy_popover.dart'; +import 'package:flowy_infra/image.dart'; +import 'package:flowy_infra_ui/flowy_infra_ui.dart'; +import 'package:flowy_infra_ui/style_widget/icon_button.dart'; import 'package:flowy_infra_ui/style_widget/scrolling/styled_list.dart'; import 'package:flowy_infra_ui/widget/spacing.dart'; import 'package:flutter/material.dart'; @@ -41,6 +46,8 @@ class _GridChecklistCellEditorState extends State { builder: (context, state) { final List slivers = [ const SliverChecklistPrograssBar(), + SliverToBoxAdapter( + child: Container(color: Colors.red, height: 2, width: 2100)), SliverToBoxAdapter( child: ListView.separated( controller: ScrollController(), @@ -55,11 +62,14 @@ class _GridChecklistCellEditorState extends State { ), ), ]; - return CustomScrollView( - shrinkWrap: true, - slivers: slivers, - controller: ScrollController(), - physics: StyledScrollPhysics(), + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 8), + child: CustomScrollView( + shrinkWrap: true, + slivers: slivers, + controller: ScrollController(), + physics: StyledScrollPhysics(), + ), ); }, ), @@ -67,15 +77,77 @@ class _GridChecklistCellEditorState extends State { } } -class _ChecklistOptionCell extends StatelessWidget { +class _ChecklistOptionCell extends StatefulWidget { final ChecklistSelectOption option; const _ChecklistOptionCell({ required this.option, Key? key, }) : super(key: key); + @override + State<_ChecklistOptionCell> createState() => _ChecklistOptionCellState(); +} + +class _ChecklistOptionCellState extends State<_ChecklistOptionCell> { + late PopoverController _popoverController; + + @override + void initState() { + _popoverController = PopoverController(); + super.initState(); + } + @override Widget build(BuildContext context) { - return Container(height: 20, width: 100, color: Colors.red); + final icon = widget.option.isSelected + ? svgWidget('editor/editor_check') + : svgWidget('editor/editor_uncheck'); + return _wrapPopover( + SizedBox( + height: GridSize.typeOptionItemHeight, + child: Row( + children: [ + icon, + const HSpace(6), + FlowyText(widget.option.data.name), + const Spacer(), + _disclosureButton(), + ], + ), + ), + ); + } + + Widget _disclosureButton() { + return FlowyIconButton( + width: 20, + onPressed: () => _popoverController.show(), + hoverColor: Colors.transparent, + iconPadding: const EdgeInsets.fromLTRB(4, 4, 4, 4), + icon: svgWidget( + "editor/details", + color: Theme.of(context).colorScheme.onSurface, + ), + ); + } + + Widget _wrapPopover(Widget child) { + return AppFlowyPopover( + controller: _popoverController, + offset: const Offset(20, 0), + asBarrier: true, + constraints: BoxConstraints.loose(const Size(200, 300)), + child: child, + popupBuilder: (BuildContext popoverContext) { + return SelectOptionTypeOptionEditor( + option: widget.option.data, + onDeleted: () {}, + onUpdated: (updatedOption) {}, + key: ValueKey( + widget.option.data.id, + ), // Use ValueKey to refresh the UI, otherwise, it will remain the old value. + ); + }, + ); } } diff --git a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/checklist_cell/checklist_prograss_bar.dart b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/checklist_cell/checklist_prograss_bar.dart index bbb097309c..77d287d575 100644 --- a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/checklist_cell/checklist_prograss_bar.dart +++ b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/checklist_cell/checklist_prograss_bar.dart @@ -17,6 +17,7 @@ class ChecklistPrograssBar extends StatelessWidget { return LinearPercentIndicator( lineHeight: 10.0, percent: percent, + padding: EdgeInsets.zero, progressColor: Theme.of(context).colorScheme.primary, backgroundColor: AFThemeExtension.of(context).tint9, barRadius: const Radius.circular(5), @@ -54,21 +55,18 @@ class _SliverChecklistPrograssBarDelegate padding: const EdgeInsets.symmetric(vertical: 8.0), child: ChecklistPrograssBar(percent: state.percent), ), - Padding( - padding: const EdgeInsets.all(8.0), - child: FlowyTextField( - hintText: LocaleKeys.grid_checklist_panelTitle.tr(), - onChanged: (text) { - context - .read() - .add(ChecklistCellEditorEvent.filterOption(text)); - }, - onSubmitted: (text) { - context - .read() - .add(ChecklistCellEditorEvent.newOption(text)); - }, - ), + FlowyTextField( + hintText: LocaleKeys.grid_checklist_panelTitle.tr(), + onChanged: (text) { + context + .read() + .add(ChecklistCellEditorEvent.filterOption(text)); + }, + onSubmitted: (text) { + context + .read() + .add(ChecklistCellEditorEvent.newOption(text)); + }, ) ], ); From 29e07089cac3fc65ef231892030a7566b6d86331 Mon Sep 17 00:00:00 2001 From: nathan Date: Wed, 30 Nov 2022 14:24:26 +0800 Subject: [PATCH 4/6] fix: checklist cell did get notified after the cell content change --- .../application/cell/checklist_cell_bloc.dart | 6 +- .../cell/checklist_cell_editor_bloc.dart | 20 ++--- .../application/field/field_controller.dart | 1 + .../cell/checklist_cell/checklist_cell.dart | 24 +++--- .../checklist_cell/checklist_cell_editor.dart | 75 +++++++++++++------ .../checklist_prograss_bar.dart | 48 ++++++------ .../type_option/select_option_editor.dart | 39 +++++++--- .../appflowy_popover/lib/src/mask.dart | 3 +- .../lib/style_widget/text_field.dart | 56 +++++++++++++- .../checklist_type_option.rs | 2 +- .../type_option_transform.rs | 2 +- .../flowy-grid/tests/grid/grid_editor.rs | 9 ++- 12 files changed, 195 insertions(+), 90 deletions(-) diff --git a/frontend/app_flowy/lib/plugins/grid/application/cell/checklist_cell_bloc.dart b/frontend/app_flowy/lib/plugins/grid/application/cell/checklist_cell_bloc.dart index 02a212ced6..2737d8ea2c 100644 --- a/frontend/app_flowy/lib/plugins/grid/application/cell/checklist_cell_bloc.dart +++ b/frontend/app_flowy/lib/plugins/grid/application/cell/checklist_cell_bloc.dart @@ -51,7 +51,11 @@ class ChecklistCellBloc extends Bloc { onCellFieldChanged: () { _loadOptions(); }, - onCellChanged: (_) {}, + onCellChanged: (data) { + if (!isClosed && data != null) { + add(ChecklistCellEvent.didReceiveOptions(data)); + } + }, ); } diff --git a/frontend/app_flowy/lib/plugins/grid/application/cell/checklist_cell_editor_bloc.dart b/frontend/app_flowy/lib/plugins/grid/application/cell/checklist_cell_editor_bloc.dart index f0eb3b50b7..6eff79c932 100644 --- a/frontend/app_flowy/lib/plugins/grid/application/cell/checklist_cell_editor_bloc.dart +++ b/frontend/app_flowy/lib/plugins/grid/application/cell/checklist_cell_editor_bloc.dart @@ -15,7 +15,6 @@ class ChecklistCellEditorBloc extends Bloc { final SelectOptionFFIService _selectOptionService; final GridChecklistCellController cellController; - Timer? _delayOperation; ChecklistCellEditorBloc({ required this.cellController, @@ -27,7 +26,6 @@ class ChecklistCellEditorBloc await event.when( initial: () async { _startListening(); - _loadOptions(); }, didReceiveOptions: (data) { emit(state.copyWith( @@ -47,11 +45,12 @@ class ChecklistCellEditorBloc updateOption: (option) { _updateOption(option); }, - selectOption: (optionId) { - _selectOptionService.select(optionIds: [optionId]); - }, - unSelectOption: (optionId) { - _selectOptionService.unSelect(optionIds: [optionId]); + selectOption: (option) async { + if (option.isSelected) { + await _selectOptionService.unSelect(optionIds: [option.data.id]); + } else { + await _selectOptionService.select(optionIds: [option.data.id]); + } }, filterOption: (String predicate) {}, ); @@ -61,7 +60,6 @@ class ChecklistCellEditorBloc @override Future close() async { - _delayOperation?.cancel(); await cellController.dispose(); return super.close(); } @@ -119,10 +117,8 @@ class ChecklistCellEditorEvent with _$ChecklistCellEditorEvent { SelectOptionCellDataPB data) = _DidReceiveOptions; const factory ChecklistCellEditorEvent.newOption(String optionName) = _NewOption; - const factory ChecklistCellEditorEvent.selectOption(String optionId) = - _SelectOption; - const factory ChecklistCellEditorEvent.unSelectOption(String optionId) = - _UnSelectOption; + const factory ChecklistCellEditorEvent.selectOption( + ChecklistSelectOption option) = _SelectOption; const factory ChecklistCellEditorEvent.updateOption(SelectOptionPB option) = _UpdateOption; const factory ChecklistCellEditorEvent.deleteOption(SelectOptionPB option) = diff --git a/frontend/app_flowy/lib/plugins/grid/application/field/field_controller.dart b/frontend/app_flowy/lib/plugins/grid/application/field/field_controller.dart index ffe50ffa72..0c520fe7d7 100644 --- a/frontend/app_flowy/lib/plugins/grid/application/field/field_controller.dart +++ b/frontend/app_flowy/lib/plugins/grid/application/field/field_controller.dart @@ -522,6 +522,7 @@ class FieldInfo { case FieldType.MultiSelect: case FieldType.RichText: case FieldType.SingleSelect: + // case FieldType.Checklist: return true; default: return false; diff --git a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/checklist_cell/checklist_cell.dart b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/checklist_cell/checklist_cell.dart index 213aaef90e..23f6cc43d3 100644 --- a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/checklist_cell/checklist_cell.dart +++ b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/checklist_cell/checklist_cell.dart @@ -36,20 +36,16 @@ class GridChecklistCellState extends State { Widget build(BuildContext context) { return BlocProvider.value( value: _cellBloc, - child: BlocBuilder( - builder: (context, state) { - return Stack( - alignment: AlignmentDirectional.center, - fit: StackFit.expand, - children: [ - Padding( - padding: GridSize.cellContentInsets, - child: _wrapPopover(const ChecklistProgressBar()), - ), - InkWell(onTap: () => _popover.show()), - ], - ); - }, + child: Stack( + alignment: AlignmentDirectional.center, + fit: StackFit.expand, + children: [ + Padding( + padding: GridSize.cellContentInsets, + child: _wrapPopover(const ChecklistProgressBar()), + ), + InkWell(onTap: () => _popover.show()), + ], ), ); } diff --git a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/checklist_cell/checklist_cell_editor.dart b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/checklist_cell/checklist_cell_editor.dart index 3004a5c035..49d8ed01e3 100644 --- a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/checklist_cell/checklist_cell_editor.dart +++ b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/checklist_cell/checklist_cell_editor.dart @@ -6,6 +6,7 @@ import 'package:app_flowy/plugins/grid/presentation/widgets/header/type_option/s import 'package:appflowy_popover/appflowy_popover.dart'; import 'package:flowy_infra/image.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/icon_button.dart'; import 'package:flowy_infra_ui/style_widget/scrolling/styled_list.dart'; import 'package:flowy_infra_ui/widget/spacing.dart'; @@ -24,9 +25,11 @@ class GridChecklistCellEditor extends StatefulWidget { class _GridChecklistCellEditorState extends State { late ChecklistCellEditorBloc bloc; + late PopoverMutex popoverMutex; @override void initState() { + popoverMutex = PopoverMutex(); bloc = ChecklistCellEditorBloc(cellController: widget.cellController); bloc.add(const ChecklistCellEditorEvent.initial()); super.initState(); @@ -47,23 +50,28 @@ class _GridChecklistCellEditorState extends State { final List slivers = [ const SliverChecklistPrograssBar(), SliverToBoxAdapter( - child: Container(color: Colors.red, height: 2, width: 2100)), - SliverToBoxAdapter( - child: ListView.separated( - controller: ScrollController(), - shrinkWrap: true, - itemCount: state.allOptions.length, - itemBuilder: (BuildContext context, int index) { - return _ChecklistOptionCell(option: state.allOptions[index]); - }, - separatorBuilder: (BuildContext context, int index) { - return VSpace(GridSize.typeOptionSeparatorHeight); - }, + child: Padding( + padding: GridSize.typeOptionContentInsets, + child: ListView.separated( + controller: ScrollController(), + shrinkWrap: true, + itemCount: state.allOptions.length, + itemBuilder: (BuildContext context, int index) { + return _ChecklistOptionCell( + option: state.allOptions[index], + popoverMutex: popoverMutex, + ); + }, + separatorBuilder: (BuildContext context, int index) { + return VSpace(GridSize.typeOptionSeparatorHeight); + }, + ), ), ), ]; - return Padding( - padding: const EdgeInsets.symmetric(horizontal: 8), + + return ScrollConfiguration( + behavior: const ScrollBehavior().copyWith(scrollbars: false), child: CustomScrollView( shrinkWrap: true, slivers: slivers, @@ -79,8 +87,10 @@ class _GridChecklistCellEditorState extends State { class _ChecklistOptionCell extends StatefulWidget { final ChecklistSelectOption option; + final PopoverMutex popoverMutex; const _ChecklistOptionCell({ required this.option, + required this.popoverMutex, Key? key, }) : super(key: key); @@ -107,10 +117,15 @@ class _ChecklistOptionCellState extends State<_ChecklistOptionCell> { height: GridSize.typeOptionItemHeight, child: Row( children: [ - icon, - const HSpace(6), - FlowyText(widget.option.data.name), - const Spacer(), + Expanded( + child: FlowyButton( + text: FlowyText(widget.option.data.name), + leftIcon: icon, + onTap: () => context + .read() + .add(ChecklistCellEditorEvent.selectOption(widget.option)), + ), + ), _disclosureButton(), ], ), @@ -122,8 +137,7 @@ class _ChecklistOptionCellState extends State<_ChecklistOptionCell> { return FlowyIconButton( width: 20, onPressed: () => _popoverController.show(), - hoverColor: Colors.transparent, - iconPadding: const EdgeInsets.fromLTRB(4, 4, 4, 4), + iconPadding: const EdgeInsets.fromLTRB(2, 2, 2, 2), icon: svgWidget( "editor/details", color: Theme.of(context).colorScheme.onSurface, @@ -137,15 +151,30 @@ class _ChecklistOptionCellState extends State<_ChecklistOptionCell> { offset: const Offset(20, 0), asBarrier: true, constraints: BoxConstraints.loose(const Size(200, 300)), + mutex: widget.popoverMutex, + triggerActions: PopoverTriggerFlags.none, child: child, popupBuilder: (BuildContext popoverContext) { return SelectOptionTypeOptionEditor( option: widget.option.data, - onDeleted: () {}, - onUpdated: (updatedOption) {}, + onDeleted: () { + context.read().add( + ChecklistCellEditorEvent.deleteOption(widget.option.data), + ); + + _popoverController.close(); + }, + onUpdated: (updatedOption) { + context.read().add( + ChecklistCellEditorEvent.updateOption(widget.option.data), + ); + }, + showOptions: false, + autoFocus: false, + // Use ValueKey to refresh the UI, otherwise, it will remain the old value. key: ValueKey( widget.option.data.id, - ), // Use ValueKey to refresh the UI, otherwise, it will remain the old value. + ), ); }, ); diff --git a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/checklist_cell/checklist_prograss_bar.dart b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/checklist_cell/checklist_prograss_bar.dart index 77d287d575..d00d88c83d 100644 --- a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/checklist_cell/checklist_prograss_bar.dart +++ b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/checklist_cell/checklist_prograss_bar.dart @@ -1,5 +1,6 @@ import 'package:app_flowy/generated/locale_keys.g.dart'; import 'package:app_flowy/plugins/grid/application/cell/checklist_cell_editor_bloc.dart'; +import 'package:app_flowy/plugins/grid/presentation/layout/sizes.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flowy_infra/color_extension.dart'; import 'package:flowy_infra_ui/flowy_infra_ui.dart'; @@ -41,34 +42,39 @@ class _SliverChecklistPrograssBarDelegate extends SliverPersistentHeaderDelegate { _SliverChecklistPrograssBarDelegate(); - double fixHeight = 80; + double fixHeight = 60; @override Widget build( BuildContext context, double shrinkOffset, bool overlapsContent) { return BlocBuilder( builder: (context, state) { - return Column( - children: [ - if (state.percent != 0) - Padding( - padding: const EdgeInsets.symmetric(vertical: 8.0), - child: ChecklistPrograssBar(percent: state.percent), + return Container( + color: Theme.of(context).colorScheme.background, + padding: GridSize.typeOptionContentInsets, + child: Column( + children: [ + FlowyTextField( + autoClearWhenDone: true, + hintText: LocaleKeys.grid_checklist_panelTitle.tr(), + onChanged: (text) { + context + .read() + .add(ChecklistCellEditorEvent.filterOption(text)); + }, + onSubmitted: (text) { + context + .read() + .add(ChecklistCellEditorEvent.newOption(text)); + }, ), - FlowyTextField( - hintText: LocaleKeys.grid_checklist_panelTitle.tr(), - onChanged: (text) { - context - .read() - .add(ChecklistCellEditorEvent.filterOption(text)); - }, - onSubmitted: (text) { - context - .read() - .add(ChecklistCellEditorEvent.newOption(text)); - }, - ) - ], + if (state.percent != 0) + Padding( + padding: const EdgeInsets.symmetric(vertical: 8.0), + child: ChecklistPrograssBar(percent: state.percent), + ), + ], + ), ); }, ); diff --git a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/type_option/select_option_editor.dart b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/type_option/select_option_editor.dart index 0ce9dba994..7ccc49d7b6 100644 --- a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/type_option/select_option_editor.dart +++ b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/type_option/select_option_editor.dart @@ -4,6 +4,7 @@ import 'package:flowy_infra/image.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'; +import 'package:flowy_infra_ui/style_widget/text_field.dart'; import 'package:flowy_infra_ui/widget/spacing.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/select_type_option.pb.dart'; import 'package:flutter/material.dart'; @@ -18,10 +19,14 @@ class SelectOptionTypeOptionEditor extends StatelessWidget { final SelectOptionPB option; final VoidCallback onDeleted; final Function(SelectOptionPB) onUpdated; + final bool showOptions; + final bool autoFocus; const SelectOptionTypeOptionEditor({ required this.option, required this.onDeleted, required this.onUpdated, + this.showOptions = true, + this.autoFocus = true, Key? key, }) : super(key: key); @@ -50,21 +55,29 @@ class SelectOptionTypeOptionEditor extends StatelessWidget { builder: (context, state) { List slivers = [ SliverToBoxAdapter( - child: _OptionNameTextField(state.option.name)), + child: _OptionNameTextField( + name: state.option.name, + autoFocus: autoFocus, + )), const SliverToBoxAdapter(child: VSpace(10)), const SliverToBoxAdapter(child: _DeleteTag()), - const SliverToBoxAdapter(child: TypeOptionSeparator()), - SliverToBoxAdapter( - child: - SelectOptionColorList(selectedColor: state.option.color)), ]; + if (showOptions) { + slivers + .add(const SliverToBoxAdapter(child: TypeOptionSeparator())); + slivers.add(SliverToBoxAdapter( + child: SelectOptionColorList( + selectedColor: state.option.color))); + } + return SizedBox( width: 160, child: Padding( padding: const EdgeInsets.all(6.0), child: CustomScrollView( slivers: slivers, + shrinkWrap: true, controller: ScrollController(), physics: StyledScrollPhysics(), ), @@ -102,19 +115,21 @@ class _DeleteTag extends StatelessWidget { class _OptionNameTextField extends StatelessWidget { final String name; - const _OptionNameTextField(this.name, {Key? key}) : super(key: key); + final bool autoFocus; + const _OptionNameTextField( + {required this.name, required this.autoFocus, Key? key}) + : super(key: key); @override Widget build(BuildContext context) { - return InputTextField( + return FlowyTextField( + autoFucous: autoFocus, text: name, - maxLength: 30, - onCanceled: () {}, - onDone: (optionName) { - if (name != optionName) { + onSubmitted: (newName) { + if (name != newName) { context .read() - .add(EditSelectOptionEvent.updateName(optionName)); + .add(EditSelectOptionEvent.updateName(newName)); } }, ); diff --git a/frontend/app_flowy/packages/appflowy_popover/lib/src/mask.dart b/frontend/app_flowy/packages/appflowy_popover/lib/src/mask.dart index 815321774f..4a79a1731f 100644 --- a/frontend/app_flowy/packages/appflowy_popover/lib/src/mask.dart +++ b/frontend/app_flowy/packages/appflowy_popover/lib/src/mask.dart @@ -88,7 +88,8 @@ class _PopoverMaskState extends State { } bool _handleGlobalKeyEvent(KeyEvent event) { - if (event.logicalKey == LogicalKeyboardKey.escape) { + if (event.logicalKey == LogicalKeyboardKey.escape && + event is KeyDownEvent) { if (widget.onExit != null) { widget.onExit!(); } diff --git a/frontend/app_flowy/packages/flowy_infra_ui/lib/style_widget/text_field.dart b/frontend/app_flowy/packages/flowy_infra_ui/lib/style_widget/text_field.dart index 50f21bfbce..1823b30e97 100644 --- a/frontend/app_flowy/packages/flowy_infra_ui/lib/style_widget/text_field.dart +++ b/frontend/app_flowy/packages/flowy_infra_ui/lib/style_widget/text_field.dart @@ -1,18 +1,28 @@ import 'package:flowy_infra/size.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:textstyle_extensions/textstyle_extensions.dart'; class FlowyTextField extends StatefulWidget { final String hintText; final String text; final void Function(String)? onChanged; final void Function(String)? onSubmitted; + final void Function()? onCanceled; final bool autoFucous; + final int? maxLength; + final TextEditingController? controller; + final bool autoClearWhenDone; const FlowyTextField({ this.hintText = "", this.text = "", this.onChanged, this.onSubmitted, + this.onCanceled, this.autoFucous = true, + this.maxLength, + this.controller, + this.autoClearWhenDone = false, Key? key, }) : super(key: key); @@ -23,12 +33,19 @@ class FlowyTextField extends StatefulWidget { class FlowyTextFieldState extends State { late FocusNode focusNode; late TextEditingController controller; + var textLength = 0; @override void initState() { focusNode = FocusNode(); - controller = TextEditingController(); - controller.text = widget.text; + focusNode.addListener(notifyDidEndEditing); + + if (widget.controller != null) { + controller = widget.controller!; + } else { + controller = TextEditingController(); + controller.text = widget.text; + } if (widget.autoFucous) { WidgetsBinding.instance.addPostFrameCallback((_) { focusNode.requestFocus(); @@ -47,9 +64,15 @@ class FlowyTextFieldState extends State { }, onSubmitted: (text) { widget.onSubmitted?.call(text); + + if (widget.autoClearWhenDone) { + controller.text = ""; + } }, maxLines: 1, - style: Theme.of(context).textTheme.bodyMedium, + maxLength: widget.maxLength, + maxLengthEnforcement: MaxLengthEnforcement.truncateAfterCompositionEnds, + style: TextStyles.body1.size(FontSizes.s12), decoration: InputDecoration( contentPadding: const EdgeInsets.all(10), enabledBorder: OutlineInputBorder( @@ -61,6 +84,8 @@ class FlowyTextFieldState extends State { ), isDense: true, hintText: widget.hintText, + suffixText: _suffixText(), + counterText: "", focusedBorder: OutlineInputBorder( borderSide: BorderSide( color: Theme.of(context).colorScheme.primary, @@ -71,4 +96,29 @@ class FlowyTextFieldState extends State { ), ); } + + @override + void dispose() { + focusNode.removeListener(notifyDidEndEditing); + focusNode.dispose(); + super.dispose(); + } + + void notifyDidEndEditing() { + if (!focusNode.hasFocus) { + if (controller.text.isEmpty) { + widget.onCanceled?.call(); + } else { + widget.onSubmitted?.call(controller.text); + } + } + } + + String? _suffixText() { + if (widget.maxLength != null) { + return '${textLength.toString()}/${widget.maxLength.toString()}'; + } else { + return null; + } + } } diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/checklist_type_option.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/checklist_type_option.rs index ae3c3b689d..f099ea2403 100644 --- a/frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/checklist_type_option.rs +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/checklist_type_option.rs @@ -80,10 +80,10 @@ impl CellDataOperation for Checklist } new_cell_data = select_ids.to_string(); - tracing::trace!("checklist's cell data: {}", &new_cell_data); } } + tracing::trace!("checklist's cell data: {}", &new_cell_data); Ok(new_cell_data) } } diff --git a/frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/type_option_transform.rs b/frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/type_option_transform.rs index 65f86c0cf2..7af1328496 100644 --- a/frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/type_option_transform.rs +++ b/frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/type_option_transform.rs @@ -48,7 +48,7 @@ impl SelectOptionTypeOptionTransformer { T: SelectTypeOptionSharedAction, { match decoded_field_type { - FieldType::SingleSelect | FieldType::MultiSelect => { + FieldType::SingleSelect | FieldType::MultiSelect | FieldType::Checklist => { // CellBytes::from(shared.get_selected_options(cell_data)) } diff --git a/frontend/rust-lib/flowy-grid/tests/grid/grid_editor.rs b/frontend/rust-lib/flowy-grid/tests/grid/grid_editor.rs index 487a283ea2..67fb69fde6 100644 --- a/frontend/rust-lib/flowy-grid/tests/grid/grid_editor.rs +++ b/frontend/rust-lib/flowy-grid/tests/grid/grid_editor.rs @@ -390,7 +390,14 @@ fn make_test_board() -> BuildGridContext { let url_field = FieldBuilder::new(url).name("link").visibility(true).build(); grid_builder.add_field(url_field); } - FieldType::Checklist => {} + FieldType::Checklist => { + let checklist = ChecklistTypeOptionBuilder::default() + .add_option(SelectOptionPB::new(FIRST_THING)) + .add_option(SelectOptionPB::new(SECOND_THING)) + .add_option(SelectOptionPB::new(THIRD_THING)); + let checklist_field = FieldBuilder::new(checklist).name("TODO").visibility(true).build(); + grid_builder.add_field(checklist_field); + } } } From a800e01f0b8d2320c9d5b25986eb9bdf95583db1 Mon Sep 17 00:00:00 2001 From: nathan Date: Wed, 30 Nov 2022 14:44:55 +0800 Subject: [PATCH 5/6] chore: config checklist board UI --- .../card/board_checklist_cell.dart | 31 +++++++++++++++++-- .../presentation/card/card_cell_builder.dart | 1 + .../application/cell/checklist_cell_bloc.dart | 4 +-- .../cell/checklist_cell_editor_bloc.dart | 12 ++++--- .../checklist_prograss_bar.dart | 13 ++++---- 5 files changed, 45 insertions(+), 16 deletions(-) diff --git a/frontend/app_flowy/lib/plugins/board/presentation/card/board_checklist_cell.dart b/frontend/app_flowy/lib/plugins/board/presentation/card/board_checklist_cell.dart index 6f90f8cd3e..1acbb07f33 100644 --- a/frontend/app_flowy/lib/plugins/board/presentation/card/board_checklist_cell.dart +++ b/frontend/app_flowy/lib/plugins/board/presentation/card/board_checklist_cell.dart @@ -1,10 +1,35 @@ +import 'package:app_flowy/plugins/grid/application/cell/cell_service/cell_service.dart'; +import 'package:app_flowy/plugins/grid/application/cell/checklist_cell_bloc.dart'; +import 'package:app_flowy/plugins/grid/presentation/widgets/cell/checklist_cell/checklist_cell.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; -class BoardChecklistCell extends StatelessWidget { - const BoardChecklistCell({Key? key}) : super(key: key); +class BoardChecklistCell extends StatefulWidget { + final GridCellControllerBuilder cellControllerBuilder; + const BoardChecklistCell({required this.cellControllerBuilder, Key? key}) + : super(key: key); + + @override + State createState() => _BoardChecklistCellState(); +} + +class _BoardChecklistCellState extends State { + late ChecklistCellBloc _cellBloc; + + @override + void initState() { + final cellController = + widget.cellControllerBuilder.build() as GridChecklistCellController; + _cellBloc = ChecklistCellBloc(cellController: cellController); + _cellBloc.add(const ChecklistCellEvent.initial()); + super.initState(); + } @override Widget build(BuildContext context) { - return Container(); + return BlocProvider.value( + value: _cellBloc, + child: const ChecklistProgressBar(), + ); } } diff --git a/frontend/app_flowy/lib/plugins/board/presentation/card/card_cell_builder.dart b/frontend/app_flowy/lib/plugins/board/presentation/card/card_cell_builder.dart index 4e7c2daa8d..d25ec9af2f 100644 --- a/frontend/app_flowy/lib/plugins/board/presentation/card/card_cell_builder.dart +++ b/frontend/app_flowy/lib/plugins/board/presentation/card/card_cell_builder.dart @@ -61,6 +61,7 @@ class BoardCellBuilder { ); case FieldType.Checklist: return BoardChecklistCell( + cellControllerBuilder: cellControllerBuilder, key: key, ); case FieldType.Number: diff --git a/frontend/app_flowy/lib/plugins/grid/application/cell/checklist_cell_bloc.dart b/frontend/app_flowy/lib/plugins/grid/application/cell/checklist_cell_bloc.dart index 2737d8ea2c..e2db336377 100644 --- a/frontend/app_flowy/lib/plugins/grid/application/cell/checklist_cell_bloc.dart +++ b/frontend/app_flowy/lib/plugins/grid/application/cell/checklist_cell_bloc.dart @@ -4,6 +4,7 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; import 'dart:async'; import 'cell_service/cell_service.dart'; +import 'checklist_cell_editor_bloc.dart'; import 'select_option_service.dart'; part 'checklist_cell_bloc.freezed.dart'; @@ -27,8 +28,7 @@ class ChecklistCellBloc extends Bloc { emit(state.copyWith( allOptions: data.options, selectedOptions: data.selectOptions, - percent: data.selectOptions.length.toDouble() / - data.options.length.toDouble(), + percent: percentFromSelectOptionCellData(data), )); }, ); diff --git a/frontend/app_flowy/lib/plugins/grid/application/cell/checklist_cell_editor_bloc.dart b/frontend/app_flowy/lib/plugins/grid/application/cell/checklist_cell_editor_bloc.dart index 6eff79c932..fc3a0b4b1a 100644 --- a/frontend/app_flowy/lib/plugins/grid/application/cell/checklist_cell_editor_bloc.dart +++ b/frontend/app_flowy/lib/plugins/grid/application/cell/checklist_cell_editor_bloc.dart @@ -26,11 +26,12 @@ class ChecklistCellEditorBloc await event.when( initial: () async { _startListening(); + _loadOptions(); }, didReceiveOptions: (data) { emit(state.copyWith( allOptions: _makeChecklistSelectOptions(data, state.predicate), - percent: _percentFromSelectOptionCellData(data), + percent: percentFromSelectOptionCellData(data), )); }, newOption: (optionName) { @@ -143,18 +144,21 @@ class ChecklistCellEditorState with _$ChecklistCellEditorState { return ChecklistCellEditorState( allOptions: _makeChecklistSelectOptions(data, ''), createOption: none(), - percent: _percentFromSelectOptionCellData(data), + percent: percentFromSelectOptionCellData(data), predicate: '', ); } } -double _percentFromSelectOptionCellData(SelectOptionCellDataPB? data) { +double percentFromSelectOptionCellData(SelectOptionCellDataPB? data) { if (data == null) return 0; - final a = data.selectOptions.length.toDouble(); final b = data.options.length.toDouble(); + if (b == 0) { + return 0; + } + final a = data.selectOptions.length.toDouble(); if (a > b) return 1.0; return a / b; diff --git a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/checklist_cell/checklist_prograss_bar.dart b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/checklist_cell/checklist_prograss_bar.dart index d00d88c83d..191c251e72 100644 --- a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/checklist_cell/checklist_prograss_bar.dart +++ b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/checklist_cell/checklist_prograss_bar.dart @@ -42,7 +42,7 @@ class _SliverChecklistPrograssBarDelegate extends SliverPersistentHeaderDelegate { _SliverChecklistPrograssBarDelegate(); - double fixHeight = 60; + double fixHeight = 54; @override Widget build( @@ -68,11 +68,10 @@ class _SliverChecklistPrograssBarDelegate .add(ChecklistCellEditorEvent.newOption(text)); }, ), - if (state.percent != 0) - Padding( - padding: const EdgeInsets.symmetric(vertical: 8.0), - child: ChecklistPrograssBar(percent: state.percent), - ), + Padding( + padding: const EdgeInsets.only(top: 10.0), + child: ChecklistPrograssBar(percent: state.percent), + ), ], ), ); @@ -88,6 +87,6 @@ class _SliverChecklistPrograssBarDelegate @override bool shouldRebuild(covariant SliverPersistentHeaderDelegate oldDelegate) { - return false; + return true; } } From d89a89131ee43702a88d4fa477d88ef6f060424f Mon Sep 17 00:00:00 2001 From: nathan Date: Wed, 30 Nov 2022 15:16:01 +0800 Subject: [PATCH 6/6] chore: remove textstyle ref --- .../grid/presentation/widgets/cell/url_cell/url_cell.dart | 1 - .../packages/flowy_infra_ui/lib/style_widget/text_field.dart | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/url_cell/url_cell.dart b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/url_cell/url_cell.dart index 48edfd2eed..c7847c17a3 100644 --- a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/url_cell/url_cell.dart +++ b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/url_cell/url_cell.dart @@ -6,7 +6,6 @@ import 'package:appflowy_popover/appflowy_popover.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flowy_infra/image.dart'; import 'package:flowy_infra_ui/flowy_infra_ui.dart'; -import 'package:flowy_infra_ui/style_widget/text.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; diff --git a/frontend/app_flowy/packages/flowy_infra_ui/lib/style_widget/text_field.dart b/frontend/app_flowy/packages/flowy_infra_ui/lib/style_widget/text_field.dart index 1823b30e97..085a719977 100644 --- a/frontend/app_flowy/packages/flowy_infra_ui/lib/style_widget/text_field.dart +++ b/frontend/app_flowy/packages/flowy_infra_ui/lib/style_widget/text_field.dart @@ -1,7 +1,6 @@ import 'package:flowy_infra/size.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:textstyle_extensions/textstyle_extensions.dart'; class FlowyTextField extends StatefulWidget { final String hintText; @@ -72,7 +71,7 @@ class FlowyTextFieldState extends State { maxLines: 1, maxLength: widget.maxLength, maxLengthEnforcement: MaxLengthEnforcement.truncateAfterCompositionEnds, - style: TextStyles.body1.size(FontSizes.s12), + style: Theme.of(context).textTheme.bodySmall, decoration: InputDecoration( contentPadding: const EdgeInsets.all(10), enabledBorder: OutlineInputBorder(