瀏覽代碼

chore: add documentation

appflowy 2 年之前
父節點
當前提交
3a7660108c
共有 25 個文件被更改,包括 160 次插入115 次删除
  1. 1 1
      frontend/app_flowy/lib/plugins/board/application/card/board_select_option_cell_bloc.dart
  2. 1 1
      frontend/app_flowy/lib/plugins/grid/application/cell/cell_service/cell_service.dart
  3. 1 1
      frontend/app_flowy/lib/plugins/grid/application/cell/select_option_cell_bloc.dart
  4. 1 1
      frontend/app_flowy/lib/plugins/grid/application/cell/select_option_editor_bloc.dart
  5. 1 1
      frontend/app_flowy/lib/plugins/grid/application/cell/select_option_service.dart
  6. 9 5
      frontend/app_flowy/lib/plugins/grid/application/field/type_option/edit_select_option_bloc.dart
  7. 1 1
      frontend/app_flowy/lib/plugins/grid/application/field/type_option/multi_select_type_option.dart
  8. 1 1
      frontend/app_flowy/lib/plugins/grid/application/field/type_option/select_option_type_option_bloc.dart
  9. 1 1
      frontend/app_flowy/lib/plugins/grid/application/field/type_option/single_select_type_option.dart
  10. 1 1
      frontend/app_flowy/lib/plugins/grid/application/field/type_option/type_option_service.dart
  11. 1 1
      frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/select_option_cell/extension.dart
  12. 1 1
      frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/select_option_cell/select_option_cell.dart
  13. 1 1
      frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/select_option_cell/select_option_editor.dart
  14. 1 1
      frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/select_option_cell/text_field.dart
  15. 1 1
      frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/type_option/select_option.dart
  16. 1 1
      frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/type_option/select_option_editor.dart
  17. 6 6
      frontend/rust-lib/flowy-grid/src/event_handler.rs
  18. 38 12
      frontend/rust-lib/flowy-grid/src/services/cell/cell_operation.rs
  19. 3 0
      frontend/rust-lib/flowy-grid/src/services/field/type_option_builder.rs
  20. 2 2
      frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/mod.rs
  21. 9 26
      frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/multi_select_type_option.rs
  22. 67 16
      frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/select_type_option.rs
  23. 7 29
      frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/single_select_type_option.rs
  24. 3 3
      frontend/rust-lib/flowy-grid/src/services/filter/impls/select_option_filter.rs
  25. 1 1
      frontend/rust-lib/flowy-grid/tests/grid/group_test/script.rs

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

@@ -1,5 +1,5 @@
 import 'dart:async';
-import 'package:flowy_sdk/protobuf/flowy-grid/select_option.pb.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 'package:app_flowy/plugins/grid/application/cell/cell_service/cell_service.dart';

+ 1 - 1
frontend/app_flowy/lib/plugins/grid/application/cell/cell_service/cell_service.dart

@@ -8,7 +8,7 @@ import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/cell_entities.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/date_type_option_entities.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
-import 'package:flowy_sdk/protobuf/flowy-grid/select_option.pb.dart';
+import 'package:flowy_sdk/protobuf/flowy-grid/select_type_option.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/url_type_option_entities.pb.dart';
 import 'package:flutter/foundation.dart';
 import 'package:freezed_annotation/freezed_annotation.dart';

+ 1 - 1
frontend/app_flowy/lib/plugins/grid/application/cell/select_option_cell_bloc.dart

@@ -1,5 +1,5 @@
 import 'dart:async';
-import 'package:flowy_sdk/protobuf/flowy-grid/select_option.pb.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 'package:app_flowy/plugins/grid/application/cell/cell_service/cell_service.dart';

+ 1 - 1
frontend/app_flowy/lib/plugins/grid/application/cell/select_option_editor_bloc.dart

@@ -4,7 +4,7 @@ import 'package:app_flowy/plugins/grid/application/cell/cell_service/cell_servic
 import 'package:collection/collection.dart';
 import 'package:dartz/dartz.dart';
 import 'package:flowy_sdk/log.dart';
-import 'package:flowy_sdk/protobuf/flowy-grid/select_option.pb.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';
 

+ 1 - 1
frontend/app_flowy/lib/plugins/grid/application/cell/select_option_service.dart

@@ -3,7 +3,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/cell_entities.pb.dart';
 import 'package:app_flowy/plugins/grid/application/field/type_option/type_option_service.dart';
-import 'package:flowy_sdk/protobuf/flowy-grid/select_option.pb.dart';
+import 'package:flowy_sdk/protobuf/flowy-grid/select_type_option.pb.dart';
 import 'cell_service/cell_service.dart';
 
 class SelectOptionService {

+ 9 - 5
frontend/app_flowy/lib/plugins/grid/application/field/type_option/edit_select_option_bloc.dart

@@ -1,4 +1,4 @@
-import 'package:flowy_sdk/protobuf/flowy-grid/select_option.pb.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';
@@ -6,8 +6,10 @@ import 'package:protobuf/protobuf.dart';
 import 'package:dartz/dartz.dart';
 part 'edit_select_option_bloc.freezed.dart';
 
-class EditSelectOptionBloc extends Bloc<EditSelectOptionEvent, EditSelectOptionState> {
-  EditSelectOptionBloc({required SelectOptionPB option}) : super(EditSelectOptionState.initial(option)) {
+class EditSelectOptionBloc
+    extends Bloc<EditSelectOptionEvent, EditSelectOptionState> {
+  EditSelectOptionBloc({required SelectOptionPB option})
+      : super(EditSelectOptionState.initial(option)) {
     on<EditSelectOptionEvent>(
       (event, emit) async {
         event.map(
@@ -48,7 +50,8 @@ class EditSelectOptionBloc extends Bloc<EditSelectOptionEvent, EditSelectOptionS
 @freezed
 class EditSelectOptionEvent with _$EditSelectOptionEvent {
   const factory EditSelectOptionEvent.updateName(String name) = _UpdateName;
-  const factory EditSelectOptionEvent.updateColor(SelectOptionColorPB color) = _UpdateColor;
+  const factory EditSelectOptionEvent.updateColor(SelectOptionColorPB color) =
+      _UpdateColor;
   const factory EditSelectOptionEvent.delete() = _Delete;
 }
 
@@ -59,7 +62,8 @@ class EditSelectOptionState with _$EditSelectOptionState {
     required Option<bool> deleted,
   }) = _EditSelectOptionState;
 
-  factory EditSelectOptionState.initial(SelectOptionPB option) => EditSelectOptionState(
+  factory EditSelectOptionState.initial(SelectOptionPB option) =>
+      EditSelectOptionState(
         option: option,
         deleted: none(),
       );

+ 1 - 1
frontend/app_flowy/lib/plugins/grid/application/field/type_option/multi_select_type_option.dart

@@ -1,6 +1,6 @@
 import 'package:flowy_sdk/log.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/multi_select_type_option.pb.dart';
-import 'package:flowy_sdk/protobuf/flowy-grid/select_option.pb.dart';
+import 'package:flowy_sdk/protobuf/flowy-grid/select_type_option.pb.dart';
 import 'dart:async';
 import 'select_option_type_option_bloc.dart';
 import 'type_option_context.dart';

+ 1 - 1
frontend/app_flowy/lib/plugins/grid/application/field/type_option/select_option_type_option_bloc.dart

@@ -1,4 +1,4 @@
-import 'package:flowy_sdk/protobuf/flowy-grid/select_option.pb.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';

+ 1 - 1
frontend/app_flowy/lib/plugins/grid/application/field/type_option/single_select_type_option.dart

@@ -1,5 +1,5 @@
 import 'package:flowy_sdk/log.dart';
-import 'package:flowy_sdk/protobuf/flowy-grid/select_option.pb.dart';
+import 'package:flowy_sdk/protobuf/flowy-grid/select_type_option.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/single_select_type_option.pb.dart';
 import 'dart:async';
 import 'package:protobuf/protobuf.dart';

+ 1 - 1
frontend/app_flowy/lib/plugins/grid/application/field/type_option/type_option_service.dart

@@ -2,7 +2,7 @@ import 'package:dartz/dartz.dart';
 import 'package:flowy_sdk/dispatch/dispatch.dart';
 import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/cell_entities.pb.dart';
-import 'package:flowy_sdk/protobuf/flowy-grid/select_option.pb.dart';
+import 'package:flowy_sdk/protobuf/flowy-grid/select_type_option.pb.dart';
 
 class TypeOptionFFIService {
   final String gridId;

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

@@ -1,7 +1,7 @@
 import 'package:flowy_infra/theme.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/select_option.pb.dart';
+import 'package:flowy_sdk/protobuf/flowy-grid/select_type_option.pb.dart';
 import 'package:flutter/material.dart';
 import 'package:easy_localization/easy_localization.dart';
 import 'package:app_flowy/generated/locale_keys.g.dart';

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

@@ -7,7 +7,7 @@ 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_option.pb.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';
 

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

@@ -10,7 +10,7 @@ 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_option.pb.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';
 import 'package:easy_localization/easy_localization.dart';

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

@@ -2,7 +2,7 @@ import 'dart:collection';
 
 import 'package:flowy_infra/size.dart';
 import 'package:flowy_infra/theme.dart';
-import 'package:flowy_sdk/protobuf/flowy-grid/select_option.pb.dart';
+import 'package:flowy_sdk/protobuf/flowy-grid/select_type_option.pb.dart';
 import 'package:flutter/material.dart';
 import 'package:easy_localization/easy_localization.dart';
 import 'package:app_flowy/generated/locale_keys.g.dart';

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

@@ -6,7 +6,7 @@ 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_option.pb.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';
 import 'package:easy_localization/easy_localization.dart';

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

@@ -6,7 +6,7 @@ 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/select_option.pb.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';
 import 'package:easy_localization/easy_localization.dart';

+ 6 - 6
frontend/rust-lib/flowy-grid/src/event_handler.rs

@@ -2,7 +2,7 @@ use crate::entities::*;
 use crate::manager::GridManager;
 use crate::services::cell::AnyCellData;
 use crate::services::field::{
-    default_type_option_builder_from_type, select_option_operation, type_option_builder_from_json_str,
+    default_type_option_builder_from_type, select_type_option_from_field_rev, type_option_builder_from_json_str,
     DateChangesetParams, DateChangesetPayloadPB, SelectOptionCellChangeset, SelectOptionCellChangesetParams,
     SelectOptionCellChangesetPayloadPB, SelectOptionCellDataPB, SelectOptionChangeset, SelectOptionChangesetPayloadPB,
     SelectOptionPB,
@@ -321,7 +321,7 @@ pub(crate) async fn new_select_option_handler(
     match editor.get_field_rev(&params.field_id).await {
         None => Err(ErrorCode::InvalidData.into()),
         Some(field_rev) => {
-            let type_option = select_option_operation(&field_rev)?;
+            let type_option = select_type_option_from_field_rev(&field_rev)?;
             let select_option = type_option.create_option(&params.option_name);
             data_result(select_option)
         }
@@ -338,7 +338,7 @@ pub(crate) async fn update_select_option_handler(
 
     let _ = editor
         .modify_field_rev(&changeset.cell_identifier.field_id, |field_rev| {
-            let mut type_option = select_option_operation(field_rev)?;
+            let mut type_option = select_type_option_from_field_rev(field_rev)?;
             let mut cell_content_changeset = None;
             let mut is_changed = None;
 
@@ -400,7 +400,7 @@ pub(crate) async fn get_select_option_handler(
         Some(field_rev) => {
             //
             let cell_rev = editor.get_cell_rev(&params.row_id, &params.field_id).await?;
-            let type_option = select_option_operation(&field_rev)?;
+            let type_option = select_type_option_from_field_rev(&field_rev)?;
             let any_cell_data: AnyCellData = match cell_rev {
                 None => AnyCellData {
                     data: "".to_string(),
@@ -408,8 +408,8 @@ pub(crate) async fn get_select_option_handler(
                 },
                 Some(cell_rev) => cell_rev.try_into()?,
             };
-            let option_context = type_option.selected_select_option(any_cell_data.into());
-            data_result(option_context)
+            let selected_options = type_option.get_selected_options(any_cell_data.into());
+            data_result(selected_options)
         }
     }
 }

+ 38 - 12
frontend/rust-lib/flowy-grid/src/services/cell/cell_operation.rs

@@ -18,10 +18,30 @@ pub trait CellGroupOperation {
 
 /// Return object that describes the cell.
 pub trait CellDisplayable<CD> {
-    /// Serialize the cell data into `CellBytes` that will be posted to `Dart` side
+    /// Serialize the cell data into `CellBytes` that will be posted to the `Dart` side. Using the
+    /// corresponding protobuf struct implement in `Dart` to deserialize the data.
     ///
-    /// Using `utf8` to encode the cell data if the cell data using `String` as its data container.
-    /// Using `protobuf` to encode the cell data if the cell data using `Protobuf struct` as its data container.
+    /// Using `utf8` to encode the cell data if the cell data use `String` as its data container.
+    /// Using `protobuf` to encode the cell data if the cell data use `Protobuf struct` as its data container.
+    ///
+    /// When switching the field type of the `FieldRevision` to another field type. The `field_type`
+    /// of the `FieldRevision` is not equal to the `decoded_field_type`. The cell data is need to do
+    /// some custom transformation.
+    ///
+    /// For example, the current field type of the `FieldRevision` is a checkbox. When switching the field
+    /// type from the checkbox to single select, the `TypeOptionBuilder`'s transform method gets called.
+    /// It will create two new options,`Yes` and `No`, if they don't exist. But the cell data didn't change,
+    /// because we can't iterate all the rows to transform the cell data that can be parsed by the current
+    /// field type. One approach is to transform the cell data when it get read. For the moment,
+    /// the cell data is a string, `Yes` or `No`. It needs to compare with the option's name, if match
+    /// return the id of the option. Otherwise, return a default value of `CellBytes`.
+    ///
+    /// # Arguments
+    ///
+    /// * `cell_data`: the generic annotation `CD` represents as the deserialize data type of the cell.
+    /// * `decoded_field_type`: the field type of the cell_data when doing serialization
+    ///
+    /// returns: Result<CellBytes, FlowyError>
     ///
     fn displayed_cell_bytes(
         &self,
@@ -35,10 +55,10 @@ pub trait CellDisplayable<CD> {
     /// The cell data is not readable which means it can't display the cell data directly to user.
     /// For example,
     /// 1. the cell data is timestamp if its field type is FieldType::Date that is not readable.
-    /// it needs to be parsed as the date string.
+    /// It needs to be parsed as the date string.
     ///
     /// 2. the cell data is a commas separated id if its field type if FieldType::MultiSelect that is not readable.
-    /// it needs to be parsed as a commas separated option name.
+    /// It needs to be parsed as a commas separated option name.
     ///
     fn displayed_cell_string(
         &self,
@@ -48,16 +68,19 @@ pub trait CellDisplayable<CD> {
     ) -> FlowyResult<String>;
 }
 
-// CD: Short for CellData. This type is the type return by apply_changeset function.
-// CS: Short for Changeset. Parse the string into specific Changeset type.
 pub trait CellDataOperation<CD, CS> {
-    /// Decode the cell data into `CD` that is certain type of data.
+    /// The generic annotation `CD` represents as the deserialize data type of the cell data.
+    /// The Serialize/Deserialize struct of the cell is base on the field type of the cell.
     ///
-    /// Each `CD` type represents as a specific field type data. For example:
+    /// For example:
     /// FieldType::URL => URLCellData
     /// FieldType::Date=> DateCellData
     ///
-    /// `decoded_field_type`: the field type of the cell data
+    /// Each cell data is a opaque data, it needs to deserialized to a concrete data struct
+    ///
+    /// `cell_data`: the opaque data of the cell.
+    /// `decoded_field_type`: the field type of the cell data when doing serialization
+    /// `field_rev`: the field of the cell data
     ///
     /// Returns the error if the cell data can't be parsed into `CD`.
     ///
@@ -68,9 +91,12 @@ pub trait CellDataOperation<CD, CS> {
         field_rev: &FieldRevision,
     ) -> FlowyResult<CellBytes>;
 
-    /// The changeset is able to parse into the specific data if CS impl the FromCellChangeset trait.
+    /// The changeset is able to parse into the concrete data struct if CS implements  
+    /// the `FromCellChangeset` trait.
+    ///
     /// For example:
-    /// SelectOptionCellChangeset,DateCellChangeset. etc.  
+    /// SelectOptionCellChangeset,DateCellChangeset. etc.
+    ///  
     fn apply_changeset(&self, changeset: CellDataChangeset<CS>, cell_rev: Option<CellRevision>) -> FlowyResult<String>;
 }
 

+ 3 - 0
frontend/rust-lib/flowy-grid/src/services/field/type_option_builder.rs

@@ -4,7 +4,10 @@ use bytes::Bytes;
 use flowy_grid_data_model::revision::TypeOptionDataSerializer;
 
 pub trait TypeOptionBuilder {
+    /// Returns the type of the type-option data
     fn field_type(&self) -> FieldType;
+
+    /// Returns a serializer that can be used to serialize the type-option data
     fn serializer(&self) -> &dyn TypeOptionDataSerializer;
 
     /// Transform the data from passed-in type-option to current type-option

+ 2 - 2
frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/mod.rs

@@ -1,7 +1,7 @@
 mod multi_select_type_option;
-mod select_option;
+mod select_type_option;
 mod single_select_type_option;
 
 pub use multi_select_type_option::*;
-pub use select_option::*;
+pub use select_type_option::*;
 pub use single_select_type_option::*;

+ 9 - 26
frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/multi_select_type_option.rs

@@ -3,8 +3,8 @@ use crate::impl_type_option;
 use crate::services::cell::{CellBytes, CellData, CellDataChangeset, CellDataOperation, CellDisplayable};
 use crate::services::field::type_options::util::get_cell_data;
 use crate::services::field::{
-    make_selected_select_options, BoxTypeOptionBuilder, SelectOptionCellChangeset, SelectOptionCellDataPB,
-    SelectOptionColorPB, SelectOptionIds, SelectOptionOperation, SelectOptionPB, TypeOptionBuilder, CHECK, UNCHECK,
+    BoxTypeOptionBuilder, SelectOptionCellChangeset, SelectOptionIds, SelectOptionPB, SelectTypeOptionSharedAction,
+    TypeOptionBuilder,
 };
 use bytes::Bytes;
 use flowy_derive::ProtoBuf;
@@ -25,13 +25,9 @@ pub struct MultiSelectTypeOptionPB {
 }
 impl_type_option!(MultiSelectTypeOptionPB, FieldType::MultiSelect);
 
-impl SelectOptionOperation for MultiSelectTypeOptionPB {
-    fn selected_select_option(&self, cell_data: CellData<SelectOptionIds>) -> SelectOptionCellDataPB {
-        let select_options = make_selected_select_options(cell_data, &self.options);
-        SelectOptionCellDataPB {
-            options: self.options.clone(),
-            select_options,
-        }
+impl SelectTypeOptionSharedAction for MultiSelectTypeOptionPB {
+    fn number_of_max_options(&self) -> Option<usize> {
+        None
     }
 
     fn options(&self) -> &Vec<SelectOptionPB> {
@@ -113,23 +109,8 @@ impl TypeOptionBuilder for MultiSelectTypeOptionBuilder {
         &self.0
     }
 
-    fn transform(&mut self, field_type: &FieldType, _type_option_data: String) {
-        match field_type {
-            FieldType::Checkbox => {
-                //Add Yes and No options if it's not exist.
-                if !self.0.options.iter().any(|option| option.name == CHECK) {
-                    let check_option = SelectOptionPB::with_color(CHECK, SelectOptionColorPB::Green);
-                    self.0.options.push(check_option);
-                }
-
-                if !self.0.options.iter().any(|option| option.name == UNCHECK) {
-                    let uncheck_option = SelectOptionPB::with_color(UNCHECK, SelectOptionColorPB::Yellow);
-                    self.0.options.push(uncheck_option);
-                }
-            }
-            FieldType::SingleSelect => {}
-            _ => {}
-        }
+    fn transform(&mut self, field_type: &FieldType, type_option_data: String) {
+        self.0.transform_type_option(field_type, type_option_data);
     }
 }
 #[cfg(test)]
@@ -154,6 +135,8 @@ mod tests {
         debug_assert_eq!(multi_select.0.options.len(), 2);
     }
 
+    // #[test]
+
     #[test]
     fn multi_select_insert_multi_option_test() {
         let google = SelectOptionPB::new("Google");

+ 67 - 16
frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/select_option.rs → frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/select_type_option.rs

@@ -2,7 +2,7 @@ use crate::entities::{CellChangesetPB, FieldType, GridCellIdPB, GridCellIdParams
 use crate::services::cell::{
     CellBytes, CellBytesParser, CellData, CellDataIsEmpty, CellDisplayable, FromCellChangeset, FromCellString,
 };
-use crate::services::field::{MultiSelectTypeOptionPB, SingleSelectTypeOptionPB};
+use crate::services::field::{MultiSelectTypeOptionPB, SingleSelectTypeOptionPB, CHECK, UNCHECK};
 use bytes::Bytes;
 use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
 use flowy_error::{internal_error, ErrorCode, FlowyResult};
@@ -64,10 +64,7 @@ impl std::default::Default for SelectOptionColorPB {
     }
 }
 
-pub fn make_selected_select_options(
-    cell_data: CellData<SelectOptionIds>,
-    options: &[SelectOptionPB],
-) -> Vec<SelectOptionPB> {
+pub fn make_selected_options(cell_data: CellData<SelectOptionIds>, options: &[SelectOptionPB]) -> Vec<SelectOptionPB> {
     if let Ok(ids) = cell_data.try_into_inner() {
         ids.iter()
             .flat_map(|option_id| options.iter().find(|option| &option.id == option_id).cloned())
@@ -76,8 +73,11 @@ pub fn make_selected_select_options(
         vec![]
     }
 }
+/// Defines the shared actions used by SingleSelect or Multi-Select.
+pub trait SelectTypeOptionSharedAction: TypeOptionDataSerializer + Send + Sync {
+    /// Returns `None` means there is no limited
+    fn number_of_max_options(&self) -> Option<usize>;
 
-pub trait SelectOptionOperation: TypeOptionDataSerializer + Send + Sync {
     /// Insert the `SelectOptionPB` into corresponding type option.
     fn insert_option(&mut self, new_option: SelectOptionPB) {
         let options = self.mut_options();
@@ -104,7 +104,60 @@ pub trait SelectOptionOperation: TypeOptionDataSerializer + Send + Sync {
         SelectOptionPB::with_color(name, color)
     }
 
-    fn selected_select_option(&self, cell_data: CellData<SelectOptionIds>) -> SelectOptionCellDataPB;
+    /// Return a list of options that are selected by user
+    fn get_selected_options(&self, cell_data: CellData<SelectOptionIds>) -> SelectOptionCellDataPB {
+        let mut select_options = make_selected_options(cell_data, self.options());
+        match self.number_of_max_options() {
+            None => {}
+            Some(number_of_max_options) => {
+                select_options.truncate(number_of_max_options);
+            }
+        }
+        SelectOptionCellDataPB {
+            options: self.options().clone(),
+            select_options,
+        }
+    }
+
+    fn transform_type_option(&mut self, field_type: &FieldType, _type_option_data: String) {
+        match field_type {
+            FieldType::Checkbox => {
+                //add Yes and No options if it's not exist.
+                if !self.options().iter().any(|option| option.name == CHECK) {
+                    let check_option = SelectOptionPB::with_color(CHECK, SelectOptionColorPB::Green);
+                    self.mut_options().push(check_option);
+                }
+
+                if !self.options().iter().any(|option| option.name == UNCHECK) {
+                    let uncheck_option = SelectOptionPB::with_color(UNCHECK, SelectOptionColorPB::Yellow);
+                    self.mut_options().push(uncheck_option);
+                }
+            }
+            FieldType::MultiSelect => {}
+            _ => {}
+        }
+    }
+
+    fn transform_cell_data(
+        &self,
+        cell_data: CellData<SelectOptionIds>,
+        decoded_field_type: &FieldType,
+        _field_rev: &FieldRevision,
+    ) -> FlowyResult<CellBytes> {
+        match decoded_field_type {
+            FieldType::SingleSelect | FieldType::MultiSelect => {
+                // Do nothing
+            }
+            FieldType::Checkbox => {
+                // transform the cell data to the option id
+            }
+            _ => {
+                return Ok(CellBytes::default());
+            }
+        }
+
+        CellBytes::from(self.get_selected_options(cell_data))
+    }
 
     fn options(&self) -> &Vec<SelectOptionPB>;
 
@@ -113,19 +166,15 @@ pub trait SelectOptionOperation: TypeOptionDataSerializer + Send + Sync {
 
 impl<T> CellDisplayable<SelectOptionIds> for T
 where
-    T: SelectOptionOperation,
+    T: SelectTypeOptionSharedAction,
 {
     fn displayed_cell_bytes(
         &self,
         cell_data: CellData<SelectOptionIds>,
         decoded_field_type: &FieldType,
-        _field_rev: &FieldRevision,
+        field_rev: &FieldRevision,
     ) -> FlowyResult<CellBytes> {
-        if !decoded_field_type.is_select_option() {
-            return Ok(CellBytes::default());
-        }
-
-        CellBytes::from(self.selected_select_option(cell_data))
+        self.transform_cell_data(cell_data, decoded_field_type, field_rev)
     }
 
     fn displayed_cell_string(
@@ -135,7 +184,7 @@ where
         _field_rev: &FieldRevision,
     ) -> FlowyResult<String> {
         Ok(self
-            .selected_select_option(cell_data)
+            .get_selected_options(cell_data)
             .select_options
             .into_iter()
             .map(|option| option.name)
@@ -144,7 +193,9 @@ where
     }
 }
 
-pub fn select_option_operation(field_rev: &FieldRevision) -> FlowyResult<Box<dyn SelectOptionOperation>> {
+pub fn select_type_option_from_field_rev(
+    field_rev: &FieldRevision,
+) -> FlowyResult<Box<dyn SelectTypeOptionSharedAction>> {
     let field_type: FieldType = field_rev.ty.into();
     match &field_type {
         FieldType::SingleSelect => {

+ 7 - 29
frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/single_select_type_option.rs

@@ -1,11 +1,10 @@
 use crate::entities::FieldType;
 use crate::impl_type_option;
 use crate::services::cell::{CellBytes, CellData, CellDataChangeset, CellDataOperation, CellDisplayable};
+use crate::services::field::{BoxTypeOptionBuilder, TypeOptionBuilder};
 use crate::services::field::{
-    make_selected_select_options, SelectOptionCellChangeset, SelectOptionCellDataPB, SelectOptionColorPB,
-    SelectOptionIds, SelectOptionOperation, SelectOptionPB, CHECK, UNCHECK,
+    SelectOptionCellChangeset, SelectOptionIds, SelectOptionPB, SelectTypeOptionSharedAction,
 };
-use crate::services::field::{BoxTypeOptionBuilder, TypeOptionBuilder};
 use bytes::Bytes;
 use flowy_derive::ProtoBuf;
 use flowy_error::{FlowyError, FlowyResult};
@@ -25,15 +24,9 @@ pub struct SingleSelectTypeOptionPB {
 }
 impl_type_option!(SingleSelectTypeOptionPB, FieldType::SingleSelect);
 
-impl SelectOptionOperation for SingleSelectTypeOptionPB {
-    fn selected_select_option(&self, cell_data: CellData<SelectOptionIds>) -> SelectOptionCellDataPB {
-        let mut select_options = make_selected_select_options(cell_data, &self.options);
-        // only keep option in single select
-        select_options.truncate(1);
-        SelectOptionCellDataPB {
-            options: self.options.clone(),
-            select_options,
-        }
+impl SelectTypeOptionSharedAction for SingleSelectTypeOptionPB {
+    fn number_of_max_options(&self) -> Option<usize> {
+        Some(1)
     }
 
     fn options(&self) -> &Vec<SelectOptionPB> {
@@ -102,23 +95,8 @@ impl TypeOptionBuilder for SingleSelectTypeOptionBuilder {
         &self.0
     }
 
-    fn transform(&mut self, field_type: &FieldType, _type_option_data: String) {
-        match field_type {
-            FieldType::Checkbox => {
-                //add Yes and No options if it's not exist.
-                if !self.0.options.iter().any(|option| option.name == CHECK) {
-                    let check_option = SelectOptionPB::with_color(CHECK, SelectOptionColorPB::Green);
-                    self.0.options.push(check_option);
-                }
-
-                if !self.0.options.iter().any(|option| option.name == UNCHECK) {
-                    let uncheck_option = SelectOptionPB::with_color(UNCHECK, SelectOptionColorPB::Yellow);
-                    self.0.options.push(uncheck_option);
-                }
-            }
-            FieldType::MultiSelect => {}
-            _ => {}
-        }
+    fn transform(&mut self, field_type: &FieldType, type_option_data: String) {
+        self.0.transform_type_option(field_type, type_option_data);
     }
 }
 

+ 3 - 3
frontend/rust-lib/flowy-grid/src/services/filter/impls/select_option_filter.rs

@@ -3,7 +3,7 @@
 use crate::entities::{SelectOptionCondition, SelectOptionFilterConfigurationPB};
 use crate::services::cell::{AnyCellData, CellFilterOperation};
 use crate::services::field::{MultiSelectTypeOptionPB, SingleSelectTypeOptionPB};
-use crate::services::field::{SelectOptionOperation, SelectedSelectOptions};
+use crate::services::field::{SelectTypeOptionSharedAction, SelectedSelectOptions};
 use flowy_error::FlowyResult;
 
 impl SelectOptionFilterConfigurationPB {
@@ -49,7 +49,7 @@ impl CellFilterOperation<SelectOptionFilterConfigurationPB> for MultiSelectTypeO
             return Ok(true);
         }
 
-        let selected_options = SelectedSelectOptions::from(self.selected_select_option(any_cell_data.into()));
+        let selected_options = SelectedSelectOptions::from(self.get_selected_options(any_cell_data.into()));
         Ok(filter.is_visible(&selected_options))
     }
 }
@@ -63,7 +63,7 @@ impl CellFilterOperation<SelectOptionFilterConfigurationPB> for SingleSelectType
         if !any_cell_data.is_single_select() {
             return Ok(true);
         }
-        let selected_options = SelectedSelectOptions::from(self.selected_select_option(any_cell_data.into()));
+        let selected_options = SelectedSelectOptions::from(self.get_selected_options(any_cell_data.into()));
         Ok(filter.is_visible(&selected_options))
     }
 }

+ 1 - 1
frontend/rust-lib/flowy-grid/tests/grid/group_test/script.rs

@@ -4,7 +4,7 @@ use flowy_grid::entities::{
 };
 use flowy_grid::services::cell::{delete_select_option_cell, insert_select_option_cell};
 use flowy_grid::services::field::{
-    edit_single_select_type_option, SelectOptionOperation, SelectOptionPB, SingleSelectTypeOptionPB,
+    edit_single_select_type_option, SelectOptionPB, SelectTypeOptionSharedAction, SingleSelectTypeOptionPB,
 };
 use flowy_grid_data_model::revision::{FieldRevision, RowChangeset};
 use std::sync::Arc;