瀏覽代碼

chore: update and delete select option

appflowy 3 年之前
父節點
當前提交
d09a5bf42b
共有 83 個文件被更改,包括 4282 次插入2074 次删除
  1. 0 1
      frontend/app_flowy/lib/startup/deps_resolver.dart
  2. 0 41
      frontend/app_flowy/lib/workspace/application/grid/cell_bloc/cell_service.dart
  3. 109 0
      frontend/app_flowy/lib/workspace/application/grid/cell_bloc/select_option_service.dart
  4. 4 5
      frontend/app_flowy/lib/workspace/application/grid/cell_bloc/selection_cell_bloc.dart
  5. 94 46
      frontend/app_flowy/lib/workspace/application/grid/cell_bloc/selection_editor_bloc.dart
  6. 1 1
      frontend/app_flowy/lib/workspace/application/grid/field/field_editor_bloc.dart
  7. 2 1
      frontend/app_flowy/lib/workspace/application/grid/field/field_service.dart
  8. 1 1
      frontend/app_flowy/lib/workspace/application/grid/field/grid_listenr.dart
  9. 1 1
      frontend/app_flowy/lib/workspace/application/grid/field/type_option/multi_select_bloc.dart
  10. 1 1
      frontend/app_flowy/lib/workspace/application/grid/field/type_option/single_select_bloc.dart
  11. 5 7
      frontend/app_flowy/lib/workspace/application/grid/field/type_option/type_option_service.dart
  12. 2 1
      frontend/app_flowy/lib/workspace/application/grid/row/row_service.dart
  13. 11 6
      frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/selection_cell/extension.dart
  14. 0 2
      frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/selection_cell/selection_cell.dart
  15. 45 7
      frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/selection_cell/selection_editor.dart
  16. 0 1
      frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/option_pannel.dart
  17. 3 1
      frontend/app_flowy/packages/flowy_infra_ui/lib/src/flowy_overlay/overlay_container.dart
  18. 29 12
      frontend/app_flowy/packages/flowy_sdk/lib/dispatch/dart_event/flowy-grid/dart_event.dart
  19. 3 0
      frontend/app_flowy/packages/flowy_sdk/lib/dispatch/dispatch.dart
  20. 4 0
      frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-error-code/code.pbenum.dart
  21. 3 1
      frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-error-code/code.pbjson.dart
  22. 0 258
      frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/grid.pb.dart
  23. 0 45
      frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/grid.pbjson.dart
  24. 1 1
      frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/protobuf.dart
  25. 196 0
      frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/cell_entities.pb.dart
  26. 7 0
      frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/cell_entities.pbenum.dart
  27. 43 0
      frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/cell_entities.pbjson.dart
  28. 9 0
      frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/cell_entities.pbserver.dart
  29. 2 2
      frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/dart_notification.pbenum.dart
  30. 2 2
      frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/dart_notification.pbjson.dart
  31. 8 6
      frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/event_map.pbenum.dart
  32. 5 4
      frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/event_map.pbjson.dart
  33. 72 0
      frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/field_entities.pb.dart
  34. 7 0
      frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/field_entities.pbenum.dart
  35. 21 0
      frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/field_entities.pbjson.dart
  36. 9 0
      frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/field_entities.pbserver.dart
  37. 3 0
      frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/protobuf.dart
  38. 72 0
      frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/row_entities.pb.dart
  39. 7 0
      frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/row_entities.pbenum.dart
  40. 21 0
      frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/row_entities.pbjson.dart
  41. 9 0
      frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/row_entities.pbserver.dart
  42. 132 23
      frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/selection_type_option.pb.dart
  43. 18 2
      frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/selection_type_option.pbjson.dart
  44. 1 1
      frontend/rust-lib/dart-ffi/Cargo.toml
  45. 8 1
      frontend/rust-lib/flowy-grid/Flowy.toml
  46. 1 1
      frontend/rust-lib/flowy-grid/src/dart_notification.rs
  47. 93 62
      frontend/rust-lib/flowy-grid/src/event_handler.rs
  48. 14 10
      frontend/rust-lib/flowy-grid/src/event_map.rs
  49. 664 0
      frontend/rust-lib/flowy-grid/src/protobuf/model/cell_entities.rs
  50. 6 6
      frontend/rust-lib/flowy-grid/src/protobuf/model/dart_notification.rs
  51. 18 14
      frontend/rust-lib/flowy-grid/src/protobuf/model/event_map.rs
  52. 243 0
      frontend/rust-lib/flowy-grid/src/protobuf/model/field_entities.rs
  53. 9 0
      frontend/rust-lib/flowy-grid/src/protobuf/model/mod.rs
  54. 243 0
      frontend/rust-lib/flowy-grid/src/protobuf/model/row_entities.rs
  55. 428 70
      frontend/rust-lib/flowy-grid/src/protobuf/model/selection_type_option.rs
  56. 14 0
      frontend/rust-lib/flowy-grid/src/protobuf/proto/cell_entities.proto
  57. 1 1
      frontend/rust-lib/flowy-grid/src/protobuf/proto/dart_notification.proto
  58. 4 3
      frontend/rust-lib/flowy-grid/src/protobuf/proto/event_map.proto
  59. 6 0
      frontend/rust-lib/flowy-grid/src/protobuf/proto/field_entities.proto
  60. 6 0
      frontend/rust-lib/flowy-grid/src/protobuf/proto/row_entities.proto
  61. 6 0
      frontend/rust-lib/flowy-grid/src/protobuf/proto/selection_type_option.proto
  62. 2 2
      frontend/rust-lib/flowy-grid/src/services/block_meta_editor.rs
  63. 69 0
      frontend/rust-lib/flowy-grid/src/services/cell/cell_entities.rs
  64. 3 0
      frontend/rust-lib/flowy-grid/src/services/cell/mod.rs
  65. 30 0
      frontend/rust-lib/flowy-grid/src/services/field/field_entities.rs
  66. 3 1
      frontend/rust-lib/flowy-grid/src/services/field/mod.rs
  67. 123 31
      frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option.rs
  68. 30 20
      frontend/rust-lib/flowy-grid/src/services/grid_editor.rs
  69. 1 0
      frontend/rust-lib/flowy-grid/src/services/mod.rs
  70. 2 3
      frontend/rust-lib/flowy-grid/src/services/row/cell_data_operation.rs
  71. 1 0
      frontend/rust-lib/flowy-grid/src/services/row/mod.rs
  72. 31 0
      frontend/rust-lib/flowy-grid/src/services/row/row_entities.rs
  73. 2 11
      frontend/rust-lib/flowy-grid/tests/grid/grid_test.rs
  74. 4 0
      shared-lib/flowy-error-code/src/code.rs
  75. 11 4
      shared-lib/flowy-error-code/src/protobuf/model/code.rs
  76. 2 0
      shared-lib/flowy-error-code/src/protobuf/proto/code.proto
  77. 0 114
      shared-lib/flowy-grid-data-model/src/entities/grid.rs
  78. 161 1180
      shared-lib/flowy-grid-data-model/src/protobuf/model/grid.rs
  79. 3 0
      shared-lib/flowy-grid-data-model/src/protobuf/model/mod.rs
  80. 978 0
      shared-lib/flowy-grid-data-model/src/protobuf/model/type_option.rs
  81. 0 17
      shared-lib/flowy-grid-data-model/src/protobuf/proto/grid.proto
  82. 29 0
      shared-lib/flowy-grid-data-model/src/protobuf/proto/type_option.proto
  83. 70 43
      shared-lib/flowy-sync/src/client_grid/grid_meta_pad.rs

+ 0 - 1
frontend/app_flowy/lib/startup/deps_resolver.dart

@@ -188,7 +188,6 @@ void _resolveGridDeps(GetIt getIt) {
 
   getIt.registerFactoryParam<SelectionCellBloc, CellData, void>(
     (cellData, _) => SelectionCellBloc(
-      service: CellService(),
       cellData: cellData,
     ),
   );

+ 0 - 41
frontend/app_flowy/lib/workspace/application/grid/cell_bloc/cell_service.dart

@@ -8,47 +8,6 @@ import 'package:flowy_sdk/protobuf/flowy-grid/selection_type_option.pb.dart';
 class CellService {
   CellService();
 
-  Future<Either<void, FlowyError>> addSelectOpiton({
-    required String gridId,
-    required String fieldId,
-    required String rowId,
-    required String optionId,
-  }) {
-    final payload = SelectOptionChangesetPayload.create()
-      ..gridId = gridId
-      ..fieldId = fieldId
-      ..rowId = rowId
-      ..insertOptionId = optionId;
-    return GridEventApplySelectOptionChangeset(payload).send();
-  }
-
-  Future<Either<SelectOptionContext, FlowyError>> getSelectOpitonContext({
-    required String gridId,
-    required String fieldId,
-    required String rowId,
-  }) {
-    final payload = CellIdentifierPayload.create()
-      ..gridId = gridId
-      ..fieldId = fieldId
-      ..rowId = rowId;
-
-    return GridEventGetSelectOptions(payload).send();
-  }
-
-  Future<Either<void, FlowyError>> removeSelectOpiton({
-    required String gridId,
-    required String fieldId,
-    required String rowId,
-    required String optionId,
-  }) {
-    final payload = SelectOptionChangesetPayload.create()
-      ..gridId = gridId
-      ..fieldId = fieldId
-      ..rowId = rowId
-      ..deleteOptionId = optionId;
-    return GridEventApplySelectOptionChangeset(payload).send();
-  }
-
   Future<Either<void, FlowyError>> updateCell({
     required String gridId,
     required String fieldId,

+ 109 - 0
frontend/app_flowy/lib/workspace/application/grid/cell_bloc/select_option_service.dart

@@ -0,0 +1,109 @@
+import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
+import 'package:dartz/dartz.dart';
+import 'package:flowy_sdk/dispatch/dispatch.dart';
+import 'package:flowy_sdk/protobuf/flowy-grid/cell_entities.pb.dart';
+import 'package:flowy_sdk/protobuf/flowy-grid/selection_type_option.pb.dart';
+
+class SelectOptionService {
+  SelectOptionService();
+
+  Future<Either<Unit, FlowyError>> create({
+    required String gridId,
+    required String fieldId,
+    required String rowId,
+    required String name,
+  }) {
+    return GridEventNewSelectOption(SelectOptionName.create()..name = name).send().then(
+      (result) {
+        return result.fold(
+          (option) {
+            final cellIdentifier = CellIdentifierPayload.create()
+              ..gridId = gridId
+              ..fieldId = fieldId
+              ..rowId = rowId;
+            final payload = SelectOptionChangesetPayload.create()
+              ..insertOption = option
+              ..cellIdentifier = cellIdentifier;
+            return GridEventApplySelectOptionChangeset(payload).send();
+          },
+          (r) => right(r),
+        );
+      },
+    );
+  }
+
+  Future<Either<Unit, FlowyError>> insert({
+    required String gridId,
+    required String fieldId,
+    required String rowId,
+    required SelectOption option,
+  }) {
+    final cellIdentifier = CellIdentifierPayload.create()
+      ..gridId = gridId
+      ..fieldId = fieldId
+      ..rowId = rowId;
+    final payload = SelectOptionChangesetPayload.create()
+      ..insertOption = option
+      ..cellIdentifier = cellIdentifier;
+    return GridEventApplySelectOptionChangeset(payload).send();
+  }
+
+  Future<Either<Unit, FlowyError>> delete({
+    required String gridId,
+    required String fieldId,
+    required String rowId,
+    required SelectOption option,
+  }) {
+    final cellIdentifier = CellIdentifierPayload.create()
+      ..gridId = gridId
+      ..fieldId = fieldId
+      ..rowId = rowId;
+
+    final payload = SelectOptionChangesetPayload.create()
+      ..deleteOption = option
+      ..cellIdentifier = cellIdentifier;
+
+    return GridEventApplySelectOptionChangeset(payload).send();
+  }
+
+  Future<Either<SelectOptionContext, FlowyError>> getOpitonContext({
+    required String gridId,
+    required String fieldId,
+    required String rowId,
+  }) {
+    final payload = CellIdentifierPayload.create()
+      ..gridId = gridId
+      ..fieldId = fieldId
+      ..rowId = rowId;
+
+    return GridEventGetSelectOptionContext(payload).send();
+  }
+
+  Future<Either<void, FlowyError>> select({
+    required String gridId,
+    required String fieldId,
+    required String rowId,
+    required String optionId,
+  }) {
+    final payload = SelectOptionCellChangesetPayload.create()
+      ..gridId = gridId
+      ..fieldId = fieldId
+      ..rowId = rowId
+      ..insertOptionId = optionId;
+    return GridEventApplySelectOptionCellChangeset(payload).send();
+  }
+
+  Future<Either<void, FlowyError>> remove({
+    required String gridId,
+    required String fieldId,
+    required String rowId,
+    required String optionId,
+  }) {
+    final payload = SelectOptionCellChangesetPayload.create()
+      ..gridId = gridId
+      ..fieldId = fieldId
+      ..rowId = rowId
+      ..deleteOptionId = optionId;
+    return GridEventApplySelectOptionCellChangeset(payload).send();
+  }
+}

+ 4 - 5
frontend/app_flowy/lib/workspace/application/grid/cell_bloc/selection_cell_bloc.dart

@@ -1,22 +1,21 @@
 import 'package:app_flowy/workspace/application/grid/cell_bloc/cell_listener.dart';
+import 'package:app_flowy/workspace/application/grid/cell_bloc/select_option_service.dart';
 import 'package:app_flowy/workspace/application/grid/row/row_service.dart';
 import 'package:flowy_sdk/log.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/selection_type_option.pb.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:freezed_annotation/freezed_annotation.dart';
 import 'dart:async';
-import 'cell_service.dart';
 
 part 'selection_cell_bloc.freezed.dart';
 
 class SelectionCellBloc extends Bloc<SelectionCellEvent, SelectionCellState> {
-  final CellService _service;
+  final SelectOptionService _service;
   final CellListener _listener;
 
   SelectionCellBloc({
-    required CellService service,
     required CellData cellData,
-  })  : _service = service,
+  })  : _service = SelectOptionService(),
         _listener = CellListener(rowId: cellData.rowId, fieldId: cellData.field.id),
         super(SelectionCellState.initial(cellData)) {
     on<SelectionCellEvent>(
@@ -41,7 +40,7 @@ class SelectionCellBloc extends Bloc<SelectionCellEvent, SelectionCellState> {
   }
 
   void _loadOptions() async {
-    final result = await _service.getSelectOpitonContext(
+    final result = await _service.getOpitonContext(
       gridId: state.cellData.gridId,
       fieldId: state.cellData.field.id,
       rowId: state.cellData.rowId,

+ 94 - 46
frontend/app_flowy/lib/workspace/application/grid/cell_bloc/selection_editor_bloc.dart

@@ -1,56 +1,56 @@
+import 'package:app_flowy/workspace/application/grid/cell_bloc/cell_listener.dart';
 import 'package:app_flowy/workspace/application/grid/field/field_listener.dart';
-import 'package:app_flowy/workspace/application/grid/field/field_service.dart';
-import 'package:app_flowy/workspace/application/grid/field/type_option/type_option_service.dart';
 import 'package:app_flowy/workspace/application/grid/row/row_service.dart';
 import 'package:flowy_sdk/log.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart';
-import 'package:flowy_sdk/protobuf/flowy-grid-data-model/meta.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/selection_type_option.pb.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:freezed_annotation/freezed_annotation.dart';
 import 'dart:async';
-import 'cell_service.dart';
+import 'select_option_service.dart';
 
 part 'selection_editor_bloc.freezed.dart';
 
 class SelectOptionEditorBloc extends Bloc<SelectOptionEditorEvent, SelectOptionEditorState> {
-  final TypeOptionService _typeOptionService;
-  final CellService _cellService;
-  final FieldListener _listener;
+  final SelectOptionService _selectOptionService;
+  final FieldListener _fieldListener;
+  final CellListener _cellListener;
 
   SelectOptionEditorBloc({
     required CellData cellData,
     required List<SelectOption> options,
     required List<SelectOption> selectedOptions,
-  })  : _cellService = CellService(),
-        _typeOptionService = TypeOptionService(fieldId: cellData.field.id),
-        _listener = FieldListener(fieldId: cellData.field.id),
+  })  : _selectOptionService = SelectOptionService(),
+        _fieldListener = FieldListener(fieldId: cellData.field.id),
+        _cellListener = CellListener(rowId: cellData.rowId, fieldId: cellData.field.id),
         super(SelectOptionEditorState.initial(cellData, options, selectedOptions)) {
     on<SelectOptionEditorEvent>(
       (event, emit) async {
         await event.map(
           initial: (_Initial value) async {
             _startListening();
-            _loadOptions();
           },
           didReceiveFieldUpdate: (_DidReceiveFieldUpdate value) {
             emit(state.copyWith(field: value.field));
             _loadOptions();
           },
           didReceiveOptions: (_DidReceiveOptions value) {
-            emit(state.copyWith(options: value.options));
+            emit(state.copyWith(
+              options: value.options,
+              selectedOptions: value.selectedOptions,
+            ));
+          },
+          newOption: (_NewOption value) {
+            _createOption(value.optionName);
+          },
+          deleteOption: (_DeleteOption value) {
+            _deleteOption(value.option);
           },
-          newOption: (_NewOption value) async {
-            final result = await _typeOptionService.createOption(value.optionName, selected: true);
-            result.fold((l) => null, (err) => Log.error(err));
+          updateOption: (_UpdateOption value) {
+            _updateOption(value.option);
           },
           selectOption: (_SelectOption value) {
-            _cellService.addSelectOpiton(
-              gridId: state.gridId,
-              fieldId: state.field.id,
-              rowId: state.rowId,
-              optionId: value.optionId,
-            );
+            _makeOptionAsSelected(value.optionId);
           },
         );
       },
@@ -59,49 +59,97 @@ class SelectOptionEditorBloc extends Bloc<SelectOptionEditorEvent, SelectOptionE
 
   @override
   Future<void> close() async {
-    await _listener.stop();
+    await _fieldListener.stop();
+    await _cellListener.stop();
     return super.close();
   }
 
-  void _startListening() {
-    _listener.updateFieldNotifier.addPublishListener((result) {
-      result.fold(
-        (field) => add(SelectOptionEditorEvent.didReceiveFieldUpdate(field)),
-        (err) => Log.error(err),
-      );
-    });
+  void _createOption(String name) async {
+    final result = await _selectOptionService.create(
+      gridId: state.gridId,
+      fieldId: state.field.id,
+      rowId: state.rowId,
+      name: name,
+    );
+    result.fold((l) => _loadOptions(), (err) => Log.error(err));
+  }
+
+  void _deleteOption(SelectOption option) async {
+    final result = await _selectOptionService.delete(
+      gridId: state.gridId,
+      fieldId: state.field.id,
+      rowId: state.rowId,
+      option: option,
+    );
+
+    result.fold((l) => null, (err) => Log.error(err));
+  }
+
+  void _updateOption(SelectOption option) async {
+    final result = await _selectOptionService.insert(
+      gridId: state.gridId,
+      fieldId: state.field.id,
+      rowId: state.rowId,
+      option: option,
+    );
+
+    result.fold((l) => null, (err) => Log.error(err));
+  }
+
+  void _makeOptionAsSelected(String optionId) {
+    _selectOptionService.select(
+      gridId: state.gridId,
+      fieldId: state.field.id,
+      rowId: state.rowId,
+      optionId: optionId,
+    );
   }
 
   void _loadOptions() async {
-    final result = await FieldContextLoaderAdaptor(gridId: state.gridId, field: state.field).load();
+    final result = await _selectOptionService.getOpitonContext(
+      gridId: state.gridId,
+      fieldId: state.field.id,
+      rowId: state.rowId,
+    );
+
     result.fold(
-      (context) {
-        List<SelectOption> options = [];
-        switch (state.field.fieldType) {
-          case FieldType.MultiSelect:
-            options.addAll(MultiSelectTypeOption.fromBuffer(context.typeOptionData).options);
-            break;
-          case FieldType.SingleSelect:
-            options.addAll(SingleSelectTypeOption.fromBuffer(context.typeOptionData).options);
-            break;
-          default:
-            Log.error("Invalid field type, expect single select or multiple select");
-            break;
-        }
-        add(SelectOptionEditorEvent.didReceiveOptions(options));
-      },
+      (selectOptionContext) => add(SelectOptionEditorEvent.didReceiveOptions(
+        selectOptionContext.options,
+        selectOptionContext.selectOptions,
+      )),
       (err) => Log.error(err),
     );
   }
+
+  void _startListening() {
+    _cellListener.updateCellNotifier.addPublishListener((result) {
+      result.fold(
+        (notificationData) => _loadOptions(),
+        (err) => Log.error(err),
+      );
+    });
+    _cellListener.start();
+
+    _fieldListener.updateFieldNotifier.addPublishListener((result) {
+      result.fold(
+        (field) => add(SelectOptionEditorEvent.didReceiveFieldUpdate(field)),
+        (err) => Log.error(err),
+      );
+    });
+    _fieldListener.start();
+  }
 }
 
 @freezed
 class SelectOptionEditorEvent with _$SelectOptionEditorEvent {
   const factory SelectOptionEditorEvent.initial() = _Initial;
   const factory SelectOptionEditorEvent.didReceiveFieldUpdate(Field field) = _DidReceiveFieldUpdate;
-  const factory SelectOptionEditorEvent.didReceiveOptions(List<SelectOption> options) = _DidReceiveOptions;
+  const factory SelectOptionEditorEvent.didReceiveOptions(
+      List<SelectOption> options, List<SelectOption> selectedOptions) = _DidReceiveOptions;
   const factory SelectOptionEditorEvent.newOption(String optionName) = _NewOption;
   const factory SelectOptionEditorEvent.selectOption(String optionId) = _SelectOption;
+  const factory SelectOptionEditorEvent.updateOption(SelectOption option) = _UpdateOption;
+  const factory SelectOptionEditorEvent.deleteOption(SelectOption option) = _DeleteOption;
 }
 
 @freezed

+ 1 - 1
frontend/app_flowy/lib/workspace/application/grid/field/field_editor_bloc.dart

@@ -48,7 +48,7 @@ class FieldEditorBloc extends Bloc<FieldEditorEvent, FieldEditorState> {
       () async => null,
       (field) async {
         field.name = state.fieldName;
-        final result = await service.createField(
+        final result = await service.insertField(
           field: field,
           typeOptionData: state.typeOptionData,
         );

+ 2 - 1
frontend/app_flowy/lib/workspace/application/grid/field/field_service.dart

@@ -4,6 +4,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-data-model/grid.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid-data-model/meta.pb.dart';
+import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
 
 class FieldService {
   final String gridId;
@@ -60,7 +61,7 @@ class FieldService {
   }
 
   // Create the field if it does not exist. Otherwise, update the field.
-  Future<Either<Unit, FlowyError>> createField({
+  Future<Either<Unit, FlowyError>> insertField({
     required Field field,
     List<int>? typeOptionData,
     String? startFieldId,

+ 1 - 1
frontend/app_flowy/lib/workspace/application/grid/field/grid_listenr.dart

@@ -24,7 +24,7 @@ class GridFieldsListener {
 
   void _handler(GridNotification ty, Either<Uint8List, FlowyError> result) {
     switch (ty) {
-      case GridNotification.DidUpdateFields:
+      case GridNotification.DidUpdateGrid:
         result.fold(
           (payload) => updateFieldsNotifier.value = left(RepeatedField.fromBuffer(payload).items),
           (error) => updateFieldsNotifier.value = right(error),

+ 1 - 1
frontend/app_flowy/lib/workspace/application/grid/field/type_option/multi_select_bloc.dart

@@ -18,7 +18,7 @@ class MultiSelectTypeOptionBloc extends Bloc<MultiSelectTypeOptionEvent, MultiSe
       (event, emit) async {
         await event.map(
           createOption: (_CreateOption value) async {
-            final result = await service.createOption(value.optionName);
+            final result = await service.newOption(value.optionName);
             result.fold(
               (option) {
                 emit(state.copyWith(typeOption: _insertOption(option)));

+ 1 - 1
frontend/app_flowy/lib/workspace/application/grid/field/type_option/single_select_bloc.dart

@@ -22,7 +22,7 @@ class SingleSelectTypeOptionBloc extends Bloc<SingleSelectTypeOptionEvent, Singl
       (event, emit) async {
         await event.map(
           createOption: (_CreateOption value) async {
-            final result = await service.createOption(value.optionName);
+            final result = await service.newOption(value.optionName);
             result.fold(
               (option) {
                 emit(state.copyWith(typeOption: _insertOption(option)));

+ 5 - 7
frontend/app_flowy/lib/workspace/application/grid/field/type_option/type_option_service.dart

@@ -1,19 +1,17 @@
 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-data-model/grid.pb.dart';
+import 'package:flowy_sdk/protobuf/flowy-grid/cell_entities.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/selection_type_option.pb.dart';
 
 class TypeOptionService {
-  String fieldId;
+  final String fieldId;
   TypeOptionService({
     required this.fieldId,
   });
 
-  Future<Either<SelectOption, FlowyError>> createOption(String name, {bool selected = false}) {
-    final payload = CreateSelectOptionPayload.create()
-      ..optionName = name
-      ..selected = selected;
-    return GridEventCreateSelectOption(payload).send();
+  Future<Either<SelectOption, FlowyError>> newOption(String name, {bool selected = false}) {
+    final payload = SelectOptionName.create()..name = name;
+    return GridEventNewSelectOption(payload).send();
   }
 }

+ 2 - 1
frontend/app_flowy/lib/workspace/application/grid/row/row_service.dart

@@ -3,6 +3,7 @@ import 'package:equatable/equatable.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-data-model/grid.pb.dart';
+import 'package:flowy_sdk/protobuf/flowy-grid/row_entities.pb.dart';
 
 class RowService {
   final String gridId;
@@ -20,7 +21,7 @@ class RowService {
   }
 
   Future<Either<Row, FlowyError>> getRow() {
-    QueryRowPayload payload = QueryRowPayload.create()
+    final payload = RowIdentifierPayload.create()
       ..gridId = gridId
       ..rowId = rowId;
 

+ 11 - 6
frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/selection_cell/extension.dart

@@ -67,13 +67,16 @@ class SelectOptionTextField extends StatelessWidget {
   final FocusNode _focusNode;
   final TextEditingController _controller;
   final TextfieldTagsController tagController;
-  final LinkedHashMap<String, SelectOption> optionMap;
+  final List<SelectOption> options;
+  final LinkedHashMap<String, SelectOption> selectedOptionMap;
+
   final double distanceToText;
 
   final Function(String) onNewTag;
 
   SelectOptionTextField({
-    required this.optionMap,
+    required this.options,
+    required this.selectedOptionMap,
     required this.distanceToText,
     required this.tagController,
     required this.onNewTag,
@@ -91,12 +94,14 @@ class SelectOptionTextField extends StatelessWidget {
     return TextFieldTags(
       textEditingController: _controller,
       textfieldTagsController: tagController,
-      initialTags: optionMap.keys.toList(),
+      initialTags: selectedOptionMap.keys.toList(),
       focusNode: _focusNode,
       textSeparators: const [' ', ','],
       inputfieldBuilder: (BuildContext context, editController, focusNode, error, onChanged, onSubmitted) {
         return ((context, sc, tags, onTagDelegate) {
-          tags.retainWhere((name) => optionMap.containsKey(name) == false);
+          tags.retainWhere((name) {
+            return options.where((option) => option.name == name).isEmpty;
+          });
           if (tags.isNotEmpty) {
             assert(tags.length == 1);
             onNewTag(tags.first);
@@ -133,11 +138,11 @@ class SelectOptionTextField extends StatelessWidget {
   }
 
   Widget? _renderTags(ScrollController sc) {
-    if (optionMap.isEmpty) {
+    if (selectedOptionMap.isEmpty) {
       return null;
     }
 
-    final children = optionMap.values.map((option) => SelectOptionTag(option: option)).toList();
+    final children = selectedOptionMap.values.map((option) => SelectOptionTag(option: option)).toList();
     return Padding(
       padding: const EdgeInsets.all(8.0),
       child: SingleChildScrollView(

+ 0 - 2
frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/selection_cell/selection_cell.dart

@@ -1,7 +1,5 @@
 import 'package:app_flowy/startup/startup.dart';
 import 'package:app_flowy/workspace/application/grid/prelude.dart';
-import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/cell/cell_container.dart';
-import 'package:flowy_sdk/log.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 

+ 45 - 7
frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/selection_cell/selection_editor.dart

@@ -3,10 +3,12 @@ import 'dart:collection';
 import 'package:app_flowy/workspace/application/grid/cell_bloc/selection_editor_bloc.dart';
 import 'package:app_flowy/workspace/application/grid/row/row_service.dart';
 import 'package:app_flowy/workspace/presentation/plugins/grid/src/layout/sizes.dart';
+import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/header/type_option/edit_option_pannel.dart';
 import 'package:flowy_infra/image.dart';
 import 'package:flowy_infra/theme.dart';
 import 'package:flowy_infra_ui/flowy_infra_ui.dart';
 import 'package:flowy_infra_ui/style_widget/hover.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_sdk/protobuf/flowy-grid/selection_type_option.pb.dart';
@@ -21,7 +23,7 @@ import 'extension.dart';
 
 const double _editorPannelWidth = 300;
 
-class SelectOptionEditor extends StatelessWidget {
+class SelectOptionEditor extends StatelessWidget with FlowyOverlayDelegate {
   final CellData cellData;
   final List<SelectOption> options;
   final List<SelectOption> selectedOptions;
@@ -44,7 +46,7 @@ class SelectOptionEditor extends StatelessWidget {
         cellData: cellData,
         options: options,
         selectedOptions: selectedOptions,
-      ),
+      )..add(const SelectOptionEditorEvent.initial()),
       child: BlocBuilder<SelectOptionEditorBloc, SelectOptionEditorState>(
         builder: (context, state) {
           return CustomScrollView(
@@ -83,12 +85,16 @@ class SelectOptionEditor extends StatelessWidget {
       identifier: SelectOptionEditor.identifier(),
       anchorContext: context,
       anchorDirection: AnchorDirection.bottomWithCenterAligned,
+      delegate: editor,
     );
   }
 
   static void hide(BuildContext context) {
     FlowyOverlay.of(context).remove(identifier());
   }
+
+  @override
+  bool asBarrier() => true;
 }
 
 class _OptionList extends StatelessWidget {
@@ -126,18 +132,19 @@ class _TextField extends StatelessWidget {
   Widget build(BuildContext context) {
     return BlocConsumer<SelectOptionEditorBloc, SelectOptionEditorState>(
       listener: (context, state) {},
-      buildWhen: (previous, current) => previous.field.id != current.field.id,
       builder: (context, state) {
         final optionMap = LinkedHashMap<String, SelectOption>.fromIterable(state.selectedOptions,
             key: (option) => option.name, value: (option) => option);
+
         return SizedBox(
           height: 42,
           child: SelectOptionTextField(
-            optionMap: optionMap,
+            options: state.options,
+            selectedOptionMap: optionMap,
             distanceToText: _editorPannelWidth * 0.7,
             tagController: _tagController,
-            onNewTag: (newTagName) {
-              context.read<SelectOptionEditorBloc>().add(SelectOptionEditorEvent.newOption(newTagName));
+            onNewTag: (tagName) {
+              context.read<SelectOptionEditorBloc>().add(SelectOptionEditorEvent.newOption(tagName));
             },
           ),
         );
@@ -188,7 +195,12 @@ class _SelectOptionCell extends StatelessWidget {
             ];
 
             if (onHover) {
-              children.add(svgWidget("editor/details", color: theme.iconColor));
+              children.add(FlowyIconButton(
+                width: 28,
+                onPressed: () => _showEditOptionPannel(context),
+                iconPadding: const EdgeInsets.fromLTRB(4, 4, 4, 4),
+                icon: svgWidget("editor/details", color: theme.iconColor),
+              ));
             }
 
             return Padding(
@@ -200,4 +212,30 @@ class _SelectOptionCell extends StatelessWidget {
       ),
     );
   }
+
+  void _showEditOptionPannel(BuildContext context) {
+    final pannel = EditSelectOptionPannel(
+      option: option,
+      onDeleted: () {
+        context.read<SelectOptionEditorBloc>().add(SelectOptionEditorEvent.deleteOption(option));
+      },
+      onUpdated: (updatedOption) {
+        context.read<SelectOptionEditorBloc>().add(SelectOptionEditorEvent.updateOption(updatedOption));
+      },
+      // key: ValueKey(option.id),
+    );
+    final overlayIdentifier = pannel.toString();
+
+    FlowyOverlay.of(context).remove(overlayIdentifier);
+    FlowyOverlay.of(context).insertWithAnchor(
+      widget: OverlayContainer(
+        child: pannel,
+        constraints: BoxConstraints.loose(const Size(200, 300)),
+      ),
+      identifier: overlayIdentifier,
+      anchorContext: context,
+      anchorDirection: AnchorDirection.rightWithCenterAligned,
+      anchorOffset: Offset(2 * overlayContainerPadding.left, 0),
+    );
+  }
 }

+ 0 - 1
frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/option_pannel.dart

@@ -165,7 +165,6 @@ class _OptionList extends StatelessWidget {
             delegate.hideOverlay(context);
             context.read<OptionPannelBloc>().add(OptionPannelEvent.updateOption(updatedOption));
           },
-          key: ValueKey(option.id),
         );
         delegate.showOverlay(context, pannel);
       },

+ 3 - 1
frontend/app_flowy/packages/flowy_infra_ui/lib/src/flowy_overlay/overlay_container.dart

@@ -3,6 +3,8 @@ import 'package:flowy_infra_ui/style_widget/decoration.dart';
 import 'package:flutter/material.dart';
 import 'package:provider/provider.dart';
 
+const overlayContainerPadding = EdgeInsets.all(12);
+
 class OverlayContainer extends StatelessWidget {
   final Widget child;
   final BoxConstraints? constraints;
@@ -10,7 +12,7 @@ class OverlayContainer extends StatelessWidget {
   const OverlayContainer({
     required this.child,
     this.constraints,
-    this.padding = const EdgeInsets.all(12),
+    this.padding = overlayContainerPadding,
     Key? key,
   }) : super(key: key);
 

+ 29 - 12
frontend/app_flowy/packages/flowy_sdk/lib/dispatch/dart_event/flowy-grid/dart_event.dart

@@ -154,13 +154,13 @@ class GridEventGetEditFieldContext {
     }
 }
 
-class GridEventCreateSelectOption {
-     CreateSelectOptionPayload request;
-     GridEventCreateSelectOption(this.request);
+class GridEventNewSelectOption {
+     SelectOptionName request;
+     GridEventNewSelectOption(this.request);
 
     Future<Either<SelectOption, FlowyError>> send() {
     final request = FFIRequest.create()
-          ..event = GridEvent.CreateSelectOption.toString()
+          ..event = GridEvent.NewSelectOption.toString()
           ..payload = requestToBytes(this.request);
 
     return Dispatch.asyncRequest(request)
@@ -171,13 +171,13 @@ class GridEventCreateSelectOption {
     }
 }
 
-class GridEventGetSelectOptions {
+class GridEventGetSelectOptionContext {
      CellIdentifierPayload request;
-     GridEventGetSelectOptions(this.request);
+     GridEventGetSelectOptionContext(this.request);
 
     Future<Either<SelectOptionContext, FlowyError>> send() {
     final request = FFIRequest.create()
-          ..event = GridEvent.GetSelectOptions.toString()
+          ..event = GridEvent.GetSelectOptionContext.toString()
           ..payload = requestToBytes(this.request);
 
     return Dispatch.asyncRequest(request)
@@ -188,6 +188,23 @@ class GridEventGetSelectOptions {
     }
 }
 
+class GridEventApplySelectOptionChangeset {
+     SelectOptionChangesetPayload request;
+     GridEventApplySelectOptionChangeset(this.request);
+
+    Future<Either<Unit, FlowyError>> send() {
+    final request = FFIRequest.create()
+          ..event = GridEvent.ApplySelectOptionChangeset.toString()
+          ..payload = requestToBytes(this.request);
+
+    return Dispatch.asyncRequest(request)
+        .then((bytesResult) => bytesResult.fold(
+           (bytes) => left(unit),
+           (errBytes) => right(FlowyError.fromBuffer(errBytes)),
+        ));
+    }
+}
+
 class GridEventCreateRow {
      CreateRowPayload request;
      GridEventCreateRow(this.request);
@@ -206,7 +223,7 @@ class GridEventCreateRow {
 }
 
 class GridEventGetRow {
-     QueryRowPayload request;
+     RowIdentifierPayload request;
      GridEventGetRow(this.request);
 
     Future<Either<Row, FlowyError>> send() {
@@ -239,13 +256,13 @@ class GridEventUpdateCell {
     }
 }
 
-class GridEventApplySelectOptionChangeset {
-     SelectOptionChangesetPayload request;
-     GridEventApplySelectOptionChangeset(this.request);
+class GridEventApplySelectOptionCellChangeset {
+     SelectOptionCellChangesetPayload request;
+     GridEventApplySelectOptionCellChangeset(this.request);
 
     Future<Either<Unit, FlowyError>> send() {
     final request = FFIRequest.create()
-          ..event = GridEvent.ApplySelectOptionChangeset.toString()
+          ..event = GridEvent.ApplySelectOptionCellChangeset.toString()
           ..payload = requestToBytes(this.request);
 
     return Dispatch.asyncRequest(request)

+ 3 - 0
frontend/app_flowy/packages/flowy_sdk/lib/dispatch/dispatch.dart

@@ -4,6 +4,9 @@ import 'package:flowy_sdk/log.dart';
 // ignore: unnecessary_import
 import 'package:flowy_sdk/protobuf/dart-ffi/ffi_response.pb.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/field_entities.pb.dart';
+import 'package:flowy_sdk/protobuf/flowy-grid/row_entities.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/selection_type_option.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-net/event.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-net/network_state.pb.dart';

+ 4 - 0
frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-error-code/code.pbenum.dart

@@ -49,6 +49,8 @@ class ErrorCode extends $pb.ProtobufEnum {
   static const ErrorCode FieldIdIsEmpty = ErrorCode._(440, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'FieldIdIsEmpty');
   static const ErrorCode FieldDoesNotExist = ErrorCode._(441, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'FieldDoesNotExist');
   static const ErrorCode SelectOptionNameIsEmpty = ErrorCode._(442, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'SelectOptionNameIsEmpty');
+  static const ErrorCode FieldNotExists = ErrorCode._(443, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'FieldNotExists');
+  static const ErrorCode FieldInvalidOperation = ErrorCode._(444, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'FieldInvalidOperation');
   static const ErrorCode TypeOptionDataIsEmpty = ErrorCode._(450, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'TypeOptionDataIsEmpty');
   static const ErrorCode InvalidData = ErrorCode._(500, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'InvalidData');
 
@@ -92,6 +94,8 @@ class ErrorCode extends $pb.ProtobufEnum {
     FieldIdIsEmpty,
     FieldDoesNotExist,
     SelectOptionNameIsEmpty,
+    FieldNotExists,
+    FieldInvalidOperation,
     TypeOptionDataIsEmpty,
     InvalidData,
   ];

+ 3 - 1
frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-error-code/code.pbjson.dart

@@ -51,10 +51,12 @@ const ErrorCode$json = const {
     const {'1': 'FieldIdIsEmpty', '2': 440},
     const {'1': 'FieldDoesNotExist', '2': 441},
     const {'1': 'SelectOptionNameIsEmpty', '2': 442},
+    const {'1': 'FieldNotExists', '2': 443},
+    const {'1': 'FieldInvalidOperation', '2': 444},
     const {'1': 'TypeOptionDataIsEmpty', '2': 450},
     const {'1': 'InvalidData', '2': 500},
   ],
 };
 
 /// Descriptor for `ErrorCode`. Decode as a `google.protobuf.EnumDescriptorProto`.
-final $typed_data.Uint8List errorCodeDescriptor = $convert.base64Decode('CglFcnJvckNvZGUSDAoISW50ZXJuYWwQABIUChBVc2VyVW5hdXRob3JpemVkEAISEgoOUmVjb3JkTm90Rm91bmQQAxIRCg1Vc2VySWRJc0VtcHR5EAQSGAoUV29ya3NwYWNlTmFtZUludmFsaWQQZBIWChJXb3Jrc3BhY2VJZEludmFsaWQQZRIYChRBcHBDb2xvclN0eWxlSW52YWxpZBBmEhgKFFdvcmtzcGFjZURlc2NUb29Mb25nEGcSGAoUV29ya3NwYWNlTmFtZVRvb0xvbmcQaBIQCgxBcHBJZEludmFsaWQQbhISCg5BcHBOYW1lSW52YWxpZBBvEhMKD1ZpZXdOYW1lSW52YWxpZBB4EhgKFFZpZXdUaHVtYm5haWxJbnZhbGlkEHkSEQoNVmlld0lkSW52YWxpZBB6EhMKD1ZpZXdEZXNjVG9vTG9uZxB7EhMKD1ZpZXdEYXRhSW52YWxpZBB8EhMKD1ZpZXdOYW1lVG9vTG9uZxB9EhEKDENvbm5lY3RFcnJvchDIARIRCgxFbWFpbElzRW1wdHkQrAISFwoSRW1haWxGb3JtYXRJbnZhbGlkEK0CEhcKEkVtYWlsQWxyZWFkeUV4aXN0cxCuAhIUCg9QYXNzd29yZElzRW1wdHkQrwISFAoPUGFzc3dvcmRUb29Mb25nELACEiUKIFBhc3N3b3JkQ29udGFpbnNGb3JiaWRDaGFyYWN0ZXJzELECEhoKFVBhc3N3b3JkRm9ybWF0SW52YWxpZBCyAhIVChBQYXNzd29yZE5vdE1hdGNoELMCEhQKD1VzZXJOYW1lVG9vTG9uZxC0AhInCiJVc2VyTmFtZUNvbnRhaW5Gb3JiaWRkZW5DaGFyYWN0ZXJzELUCEhQKD1VzZXJOYW1lSXNFbXB0eRC2AhISCg1Vc2VySWRJbnZhbGlkELcCEhEKDFVzZXJOb3RFeGlzdBC4AhIQCgtUZXh0VG9vTG9uZxCQAxISCg1HcmlkSWRJc0VtcHR5EJoDEhMKDkJsb2NrSWRJc0VtcHR5EKQDEhEKDFJvd0lkSXNFbXB0eRCuAxIUCg9PcHRpb25JZElzRW1wdHkQrwMSEwoORmllbGRJZElzRW1wdHkQuAMSFgoRRmllbGREb2VzTm90RXhpc3QQuQMSHAoXU2VsZWN0T3B0aW9uTmFtZUlzRW1wdHkQugMSGgoVVHlwZU9wdGlvbkRhdGFJc0VtcHR5EMIDEhAKC0ludmFsaWREYXRhEPQD');
+final $typed_data.Uint8List errorCodeDescriptor = $convert.base64Decode('CglFcnJvckNvZGUSDAoISW50ZXJuYWwQABIUChBVc2VyVW5hdXRob3JpemVkEAISEgoOUmVjb3JkTm90Rm91bmQQAxIRCg1Vc2VySWRJc0VtcHR5EAQSGAoUV29ya3NwYWNlTmFtZUludmFsaWQQZBIWChJXb3Jrc3BhY2VJZEludmFsaWQQZRIYChRBcHBDb2xvclN0eWxlSW52YWxpZBBmEhgKFFdvcmtzcGFjZURlc2NUb29Mb25nEGcSGAoUV29ya3NwYWNlTmFtZVRvb0xvbmcQaBIQCgxBcHBJZEludmFsaWQQbhISCg5BcHBOYW1lSW52YWxpZBBvEhMKD1ZpZXdOYW1lSW52YWxpZBB4EhgKFFZpZXdUaHVtYm5haWxJbnZhbGlkEHkSEQoNVmlld0lkSW52YWxpZBB6EhMKD1ZpZXdEZXNjVG9vTG9uZxB7EhMKD1ZpZXdEYXRhSW52YWxpZBB8EhMKD1ZpZXdOYW1lVG9vTG9uZxB9EhEKDENvbm5lY3RFcnJvchDIARIRCgxFbWFpbElzRW1wdHkQrAISFwoSRW1haWxGb3JtYXRJbnZhbGlkEK0CEhcKEkVtYWlsQWxyZWFkeUV4aXN0cxCuAhIUCg9QYXNzd29yZElzRW1wdHkQrwISFAoPUGFzc3dvcmRUb29Mb25nELACEiUKIFBhc3N3b3JkQ29udGFpbnNGb3JiaWRDaGFyYWN0ZXJzELECEhoKFVBhc3N3b3JkRm9ybWF0SW52YWxpZBCyAhIVChBQYXNzd29yZE5vdE1hdGNoELMCEhQKD1VzZXJOYW1lVG9vTG9uZxC0AhInCiJVc2VyTmFtZUNvbnRhaW5Gb3JiaWRkZW5DaGFyYWN0ZXJzELUCEhQKD1VzZXJOYW1lSXNFbXB0eRC2AhISCg1Vc2VySWRJbnZhbGlkELcCEhEKDFVzZXJOb3RFeGlzdBC4AhIQCgtUZXh0VG9vTG9uZxCQAxISCg1HcmlkSWRJc0VtcHR5EJoDEhMKDkJsb2NrSWRJc0VtcHR5EKQDEhEKDFJvd0lkSXNFbXB0eRCuAxIUCg9PcHRpb25JZElzRW1wdHkQrwMSEwoORmllbGRJZElzRW1wdHkQuAMSFgoRRmllbGREb2VzTm90RXhpc3QQuQMSHAoXU2VsZWN0T3B0aW9uTmFtZUlzRW1wdHkQugMSEwoORmllbGROb3RFeGlzdHMQuwMSGgoVRmllbGRJbnZhbGlkT3BlcmF0aW9uELwDEhoKFVR5cGVPcHRpb25EYXRhSXNFbXB0eRDCAxIQCgtJbnZhbGlkRGF0YRD0Aw==');

+ 0 - 258
frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/grid.pb.dart

@@ -205,67 +205,6 @@ class Field extends $pb.GeneratedMessage {
   void clearWidth() => clearField(7);
 }
 
-class FieldIdentifierPayload extends $pb.GeneratedMessage {
-  static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'FieldIdentifierPayload', createEmptyInstance: create)
-    ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'fieldId')
-    ..aOS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'gridId')
-    ..hasRequiredFields = false
-  ;
-
-  FieldIdentifierPayload._() : super();
-  factory FieldIdentifierPayload({
-    $core.String? fieldId,
-    $core.String? gridId,
-  }) {
-    final _result = create();
-    if (fieldId != null) {
-      _result.fieldId = fieldId;
-    }
-    if (gridId != null) {
-      _result.gridId = gridId;
-    }
-    return _result;
-  }
-  factory FieldIdentifierPayload.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
-  factory FieldIdentifierPayload.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
-  @$core.Deprecated(
-  'Using this can add significant overhead to your binary. '
-  'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
-  'Will be removed in next major version')
-  FieldIdentifierPayload clone() => FieldIdentifierPayload()..mergeFromMessage(this);
-  @$core.Deprecated(
-  'Using this can add significant overhead to your binary. '
-  'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
-  'Will be removed in next major version')
-  FieldIdentifierPayload copyWith(void Function(FieldIdentifierPayload) updates) => super.copyWith((message) => updates(message as FieldIdentifierPayload)) as FieldIdentifierPayload; // ignore: deprecated_member_use
-  $pb.BuilderInfo get info_ => _i;
-  @$core.pragma('dart2js:noInline')
-  static FieldIdentifierPayload create() => FieldIdentifierPayload._();
-  FieldIdentifierPayload createEmptyInstance() => create();
-  static $pb.PbList<FieldIdentifierPayload> createRepeated() => $pb.PbList<FieldIdentifierPayload>();
-  @$core.pragma('dart2js:noInline')
-  static FieldIdentifierPayload getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<FieldIdentifierPayload>(create);
-  static FieldIdentifierPayload? _defaultInstance;
-
-  @$pb.TagNumber(1)
-  $core.String get fieldId => $_getSZ(0);
-  @$pb.TagNumber(1)
-  set fieldId($core.String v) { $_setString(0, v); }
-  @$pb.TagNumber(1)
-  $core.bool hasFieldId() => $_has(0);
-  @$pb.TagNumber(1)
-  void clearFieldId() => clearField(1);
-
-  @$pb.TagNumber(2)
-  $core.String get gridId => $_getSZ(1);
-  @$pb.TagNumber(2)
-  set gridId($core.String v) { $_setString(1, v); }
-  @$pb.TagNumber(2)
-  $core.bool hasGridId() => $_has(1);
-  @$pb.TagNumber(2)
-  void clearGridId() => clearField(2);
-}
-
 class FieldOrder extends $pb.GeneratedMessage {
   static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'FieldOrder', createEmptyInstance: create)
     ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'fieldId')
@@ -1024,81 +963,6 @@ class Cell extends $pb.GeneratedMessage {
   void clearContent() => clearField(2);
 }
 
-class CellIdentifierPayload extends $pb.GeneratedMessage {
-  static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'CellIdentifierPayload', createEmptyInstance: create)
-    ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'gridId')
-    ..aOS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'fieldId')
-    ..aOS(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'rowId')
-    ..hasRequiredFields = false
-  ;
-
-  CellIdentifierPayload._() : super();
-  factory CellIdentifierPayload({
-    $core.String? gridId,
-    $core.String? fieldId,
-    $core.String? rowId,
-  }) {
-    final _result = create();
-    if (gridId != null) {
-      _result.gridId = gridId;
-    }
-    if (fieldId != null) {
-      _result.fieldId = fieldId;
-    }
-    if (rowId != null) {
-      _result.rowId = rowId;
-    }
-    return _result;
-  }
-  factory CellIdentifierPayload.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
-  factory CellIdentifierPayload.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
-  @$core.Deprecated(
-  'Using this can add significant overhead to your binary. '
-  'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
-  'Will be removed in next major version')
-  CellIdentifierPayload clone() => CellIdentifierPayload()..mergeFromMessage(this);
-  @$core.Deprecated(
-  'Using this can add significant overhead to your binary. '
-  'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
-  'Will be removed in next major version')
-  CellIdentifierPayload copyWith(void Function(CellIdentifierPayload) updates) => super.copyWith((message) => updates(message as CellIdentifierPayload)) as CellIdentifierPayload; // ignore: deprecated_member_use
-  $pb.BuilderInfo get info_ => _i;
-  @$core.pragma('dart2js:noInline')
-  static CellIdentifierPayload create() => CellIdentifierPayload._();
-  CellIdentifierPayload createEmptyInstance() => create();
-  static $pb.PbList<CellIdentifierPayload> createRepeated() => $pb.PbList<CellIdentifierPayload>();
-  @$core.pragma('dart2js:noInline')
-  static CellIdentifierPayload getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<CellIdentifierPayload>(create);
-  static CellIdentifierPayload? _defaultInstance;
-
-  @$pb.TagNumber(1)
-  $core.String get gridId => $_getSZ(0);
-  @$pb.TagNumber(1)
-  set gridId($core.String v) { $_setString(0, v); }
-  @$pb.TagNumber(1)
-  $core.bool hasGridId() => $_has(0);
-  @$pb.TagNumber(1)
-  void clearGridId() => clearField(1);
-
-  @$pb.TagNumber(2)
-  $core.String get fieldId => $_getSZ(1);
-  @$pb.TagNumber(2)
-  set fieldId($core.String v) { $_setString(1, v); }
-  @$pb.TagNumber(2)
-  $core.bool hasFieldId() => $_has(1);
-  @$pb.TagNumber(2)
-  void clearFieldId() => clearField(2);
-
-  @$pb.TagNumber(3)
-  $core.String get rowId => $_getSZ(2);
-  @$pb.TagNumber(3)
-  set rowId($core.String v) { $_setString(2, v); }
-  @$pb.TagNumber(3)
-  $core.bool hasRowId() => $_has(2);
-  @$pb.TagNumber(3)
-  void clearRowId() => clearField(3);
-}
-
 enum CellNotificationData_OneOfContent {
   content, 
   notSet
@@ -1679,125 +1543,3 @@ class QueryGridBlocksPayload extends $pb.GeneratedMessage {
   $core.List<GridBlockOrder> get blockOrders => $_getList(1);
 }
 
-class QueryRowPayload extends $pb.GeneratedMessage {
-  static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'QueryRowPayload', createEmptyInstance: create)
-    ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'gridId')
-    ..aOS(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'rowId')
-    ..hasRequiredFields = false
-  ;
-
-  QueryRowPayload._() : super();
-  factory QueryRowPayload({
-    $core.String? gridId,
-    $core.String? rowId,
-  }) {
-    final _result = create();
-    if (gridId != null) {
-      _result.gridId = gridId;
-    }
-    if (rowId != null) {
-      _result.rowId = rowId;
-    }
-    return _result;
-  }
-  factory QueryRowPayload.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
-  factory QueryRowPayload.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
-  @$core.Deprecated(
-  'Using this can add significant overhead to your binary. '
-  'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
-  'Will be removed in next major version')
-  QueryRowPayload clone() => QueryRowPayload()..mergeFromMessage(this);
-  @$core.Deprecated(
-  'Using this can add significant overhead to your binary. '
-  'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
-  'Will be removed in next major version')
-  QueryRowPayload copyWith(void Function(QueryRowPayload) updates) => super.copyWith((message) => updates(message as QueryRowPayload)) as QueryRowPayload; // ignore: deprecated_member_use
-  $pb.BuilderInfo get info_ => _i;
-  @$core.pragma('dart2js:noInline')
-  static QueryRowPayload create() => QueryRowPayload._();
-  QueryRowPayload createEmptyInstance() => create();
-  static $pb.PbList<QueryRowPayload> createRepeated() => $pb.PbList<QueryRowPayload>();
-  @$core.pragma('dart2js:noInline')
-  static QueryRowPayload getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<QueryRowPayload>(create);
-  static QueryRowPayload? _defaultInstance;
-
-  @$pb.TagNumber(1)
-  $core.String get gridId => $_getSZ(0);
-  @$pb.TagNumber(1)
-  set gridId($core.String v) { $_setString(0, v); }
-  @$pb.TagNumber(1)
-  $core.bool hasGridId() => $_has(0);
-  @$pb.TagNumber(1)
-  void clearGridId() => clearField(1);
-
-  @$pb.TagNumber(3)
-  $core.String get rowId => $_getSZ(1);
-  @$pb.TagNumber(3)
-  set rowId($core.String v) { $_setString(1, v); }
-  @$pb.TagNumber(3)
-  $core.bool hasRowId() => $_has(1);
-  @$pb.TagNumber(3)
-  void clearRowId() => clearField(3);
-}
-
-class CreateSelectOptionPayload extends $pb.GeneratedMessage {
-  static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'CreateSelectOptionPayload', createEmptyInstance: create)
-    ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'optionName')
-    ..aOB(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'selected')
-    ..hasRequiredFields = false
-  ;
-
-  CreateSelectOptionPayload._() : super();
-  factory CreateSelectOptionPayload({
-    $core.String? optionName,
-    $core.bool? selected,
-  }) {
-    final _result = create();
-    if (optionName != null) {
-      _result.optionName = optionName;
-    }
-    if (selected != null) {
-      _result.selected = selected;
-    }
-    return _result;
-  }
-  factory CreateSelectOptionPayload.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
-  factory CreateSelectOptionPayload.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
-  @$core.Deprecated(
-  'Using this can add significant overhead to your binary. '
-  'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
-  'Will be removed in next major version')
-  CreateSelectOptionPayload clone() => CreateSelectOptionPayload()..mergeFromMessage(this);
-  @$core.Deprecated(
-  'Using this can add significant overhead to your binary. '
-  'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
-  'Will be removed in next major version')
-  CreateSelectOptionPayload copyWith(void Function(CreateSelectOptionPayload) updates) => super.copyWith((message) => updates(message as CreateSelectOptionPayload)) as CreateSelectOptionPayload; // ignore: deprecated_member_use
-  $pb.BuilderInfo get info_ => _i;
-  @$core.pragma('dart2js:noInline')
-  static CreateSelectOptionPayload create() => CreateSelectOptionPayload._();
-  CreateSelectOptionPayload createEmptyInstance() => create();
-  static $pb.PbList<CreateSelectOptionPayload> createRepeated() => $pb.PbList<CreateSelectOptionPayload>();
-  @$core.pragma('dart2js:noInline')
-  static CreateSelectOptionPayload getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<CreateSelectOptionPayload>(create);
-  static CreateSelectOptionPayload? _defaultInstance;
-
-  @$pb.TagNumber(1)
-  $core.String get optionName => $_getSZ(0);
-  @$pb.TagNumber(1)
-  set optionName($core.String v) { $_setString(0, v); }
-  @$pb.TagNumber(1)
-  $core.bool hasOptionName() => $_has(0);
-  @$pb.TagNumber(1)
-  void clearOptionName() => clearField(1);
-
-  @$pb.TagNumber(2)
-  $core.bool get selected => $_getBF(1);
-  @$pb.TagNumber(2)
-  set selected($core.bool v) { $_setBool(1, v); }
-  @$pb.TagNumber(2)
-  $core.bool hasSelected() => $_has(1);
-  @$pb.TagNumber(2)
-  void clearSelected() => clearField(2);
-}
-

+ 0 - 45
frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/grid.pbjson.dart

@@ -36,17 +36,6 @@ const Field$json = const {
 
 /// Descriptor for `Field`. Decode as a `google.protobuf.DescriptorProto`.
 final $typed_data.Uint8List fieldDescriptor = $convert.base64Decode('CgVGaWVsZBIOCgJpZBgBIAEoCVICaWQSEgoEbmFtZRgCIAEoCVIEbmFtZRISCgRkZXNjGAMgASgJUgRkZXNjEikKCmZpZWxkX3R5cGUYBCABKA4yCi5GaWVsZFR5cGVSCWZpZWxkVHlwZRIWCgZmcm96ZW4YBSABKAhSBmZyb3plbhIeCgp2aXNpYmlsaXR5GAYgASgIUgp2aXNpYmlsaXR5EhQKBXdpZHRoGAcgASgFUgV3aWR0aA==');
-@$core.Deprecated('Use fieldIdentifierPayloadDescriptor instead')
-const FieldIdentifierPayload$json = const {
-  '1': 'FieldIdentifierPayload',
-  '2': const [
-    const {'1': 'field_id', '3': 1, '4': 1, '5': 9, '10': 'fieldId'},
-    const {'1': 'grid_id', '3': 2, '4': 1, '5': 9, '10': 'gridId'},
-  ],
-};
-
-/// Descriptor for `FieldIdentifierPayload`. Decode as a `google.protobuf.DescriptorProto`.
-final $typed_data.Uint8List fieldIdentifierPayloadDescriptor = $convert.base64Decode('ChZGaWVsZElkZW50aWZpZXJQYXlsb2FkEhkKCGZpZWxkX2lkGAEgASgJUgdmaWVsZElkEhcKB2dyaWRfaWQYAiABKAlSBmdyaWRJZA==');
 @$core.Deprecated('Use fieldOrderDescriptor instead')
 const FieldOrder$json = const {
   '1': 'FieldOrder',
@@ -203,18 +192,6 @@ const Cell$json = const {
 
 /// Descriptor for `Cell`. Decode as a `google.protobuf.DescriptorProto`.
 final $typed_data.Uint8List cellDescriptor = $convert.base64Decode('CgRDZWxsEhkKCGZpZWxkX2lkGAEgASgJUgdmaWVsZElkEhgKB2NvbnRlbnQYAiABKAlSB2NvbnRlbnQ=');
-@$core.Deprecated('Use cellIdentifierPayloadDescriptor instead')
-const CellIdentifierPayload$json = const {
-  '1': 'CellIdentifierPayload',
-  '2': const [
-    const {'1': 'grid_id', '3': 1, '4': 1, '5': 9, '10': 'gridId'},
-    const {'1': 'field_id', '3': 2, '4': 1, '5': 9, '10': 'fieldId'},
-    const {'1': 'row_id', '3': 3, '4': 1, '5': 9, '10': 'rowId'},
-  ],
-};
-
-/// Descriptor for `CellIdentifierPayload`. Decode as a `google.protobuf.DescriptorProto`.
-final $typed_data.Uint8List cellIdentifierPayloadDescriptor = $convert.base64Decode('ChVDZWxsSWRlbnRpZmllclBheWxvYWQSFwoHZ3JpZF9pZBgBIAEoCVIGZ3JpZElkEhkKCGZpZWxkX2lkGAIgASgJUgdmaWVsZElkEhUKBnJvd19pZBgDIAEoCVIFcm93SWQ=');
 @$core.Deprecated('Use cellNotificationDataDescriptor instead')
 const CellNotificationData$json = const {
   '1': 'CellNotificationData',
@@ -323,25 +300,3 @@ const QueryGridBlocksPayload$json = const {
 
 /// Descriptor for `QueryGridBlocksPayload`. Decode as a `google.protobuf.DescriptorProto`.
 final $typed_data.Uint8List queryGridBlocksPayloadDescriptor = $convert.base64Decode('ChZRdWVyeUdyaWRCbG9ja3NQYXlsb2FkEhcKB2dyaWRfaWQYASABKAlSBmdyaWRJZBIyCgxibG9ja19vcmRlcnMYAiADKAsyDy5HcmlkQmxvY2tPcmRlclILYmxvY2tPcmRlcnM=');
-@$core.Deprecated('Use queryRowPayloadDescriptor instead')
-const QueryRowPayload$json = const {
-  '1': 'QueryRowPayload',
-  '2': const [
-    const {'1': 'grid_id', '3': 1, '4': 1, '5': 9, '10': 'gridId'},
-    const {'1': 'row_id', '3': 3, '4': 1, '5': 9, '10': 'rowId'},
-  ],
-};
-
-/// Descriptor for `QueryRowPayload`. Decode as a `google.protobuf.DescriptorProto`.
-final $typed_data.Uint8List queryRowPayloadDescriptor = $convert.base64Decode('Cg9RdWVyeVJvd1BheWxvYWQSFwoHZ3JpZF9pZBgBIAEoCVIGZ3JpZElkEhUKBnJvd19pZBgDIAEoCVIFcm93SWQ=');
-@$core.Deprecated('Use createSelectOptionPayloadDescriptor instead')
-const CreateSelectOptionPayload$json = const {
-  '1': 'CreateSelectOptionPayload',
-  '2': const [
-    const {'1': 'option_name', '3': 1, '4': 1, '5': 9, '10': 'optionName'},
-    const {'1': 'selected', '3': 2, '4': 1, '5': 8, '10': 'selected'},
-  ],
-};
-
-/// Descriptor for `CreateSelectOptionPayload`. Decode as a `google.protobuf.DescriptorProto`.
-final $typed_data.Uint8List createSelectOptionPayloadDescriptor = $convert.base64Decode('ChlDcmVhdGVTZWxlY3RPcHRpb25QYXlsb2FkEh8KC29wdGlvbl9uYW1lGAEgASgJUgpvcHRpb25OYW1lEhoKCHNlbGVjdGVkGAIgASgIUghzZWxlY3RlZA==');

+ 1 - 1
frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/protobuf.dart

@@ -1,3 +1,3 @@
-// Auto-generated, do not edit 
+// Auto-generated, do not edit
 export './grid.pb.dart';
 export './meta.pb.dart';

+ 196 - 0
frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/cell_entities.pb.dart

@@ -0,0 +1,196 @@
+///
+//  Generated code. Do not modify.
+//  source: cell_entities.proto
+//
+// @dart = 2.12
+// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields
+
+import 'dart:core' as $core;
+
+import 'package:protobuf/protobuf.dart' as $pb;
+
+class CreateSelectOptionPayload extends $pb.GeneratedMessage {
+  static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'CreateSelectOptionPayload', createEmptyInstance: create)
+    ..aOM<CellIdentifierPayload>(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'cellIdentifier', subBuilder: CellIdentifierPayload.create)
+    ..aOS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'optionName')
+    ..hasRequiredFields = false
+  ;
+
+  CreateSelectOptionPayload._() : super();
+  factory CreateSelectOptionPayload({
+    CellIdentifierPayload? cellIdentifier,
+    $core.String? optionName,
+  }) {
+    final _result = create();
+    if (cellIdentifier != null) {
+      _result.cellIdentifier = cellIdentifier;
+    }
+    if (optionName != null) {
+      _result.optionName = optionName;
+    }
+    return _result;
+  }
+  factory CreateSelectOptionPayload.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
+  factory CreateSelectOptionPayload.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
+  @$core.Deprecated(
+  'Using this can add significant overhead to your binary. '
+  'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
+  'Will be removed in next major version')
+  CreateSelectOptionPayload clone() => CreateSelectOptionPayload()..mergeFromMessage(this);
+  @$core.Deprecated(
+  'Using this can add significant overhead to your binary. '
+  'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
+  'Will be removed in next major version')
+  CreateSelectOptionPayload copyWith(void Function(CreateSelectOptionPayload) updates) => super.copyWith((message) => updates(message as CreateSelectOptionPayload)) as CreateSelectOptionPayload; // ignore: deprecated_member_use
+  $pb.BuilderInfo get info_ => _i;
+  @$core.pragma('dart2js:noInline')
+  static CreateSelectOptionPayload create() => CreateSelectOptionPayload._();
+  CreateSelectOptionPayload createEmptyInstance() => create();
+  static $pb.PbList<CreateSelectOptionPayload> createRepeated() => $pb.PbList<CreateSelectOptionPayload>();
+  @$core.pragma('dart2js:noInline')
+  static CreateSelectOptionPayload getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<CreateSelectOptionPayload>(create);
+  static CreateSelectOptionPayload? _defaultInstance;
+
+  @$pb.TagNumber(1)
+  CellIdentifierPayload get cellIdentifier => $_getN(0);
+  @$pb.TagNumber(1)
+  set cellIdentifier(CellIdentifierPayload v) { setField(1, v); }
+  @$pb.TagNumber(1)
+  $core.bool hasCellIdentifier() => $_has(0);
+  @$pb.TagNumber(1)
+  void clearCellIdentifier() => clearField(1);
+  @$pb.TagNumber(1)
+  CellIdentifierPayload ensureCellIdentifier() => $_ensure(0);
+
+  @$pb.TagNumber(2)
+  $core.String get optionName => $_getSZ(1);
+  @$pb.TagNumber(2)
+  set optionName($core.String v) { $_setString(1, v); }
+  @$pb.TagNumber(2)
+  $core.bool hasOptionName() => $_has(1);
+  @$pb.TagNumber(2)
+  void clearOptionName() => clearField(2);
+}
+
+class CellIdentifierPayload extends $pb.GeneratedMessage {
+  static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'CellIdentifierPayload', createEmptyInstance: create)
+    ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'gridId')
+    ..aOS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'fieldId')
+    ..aOS(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'rowId')
+    ..hasRequiredFields = false
+  ;
+
+  CellIdentifierPayload._() : super();
+  factory CellIdentifierPayload({
+    $core.String? gridId,
+    $core.String? fieldId,
+    $core.String? rowId,
+  }) {
+    final _result = create();
+    if (gridId != null) {
+      _result.gridId = gridId;
+    }
+    if (fieldId != null) {
+      _result.fieldId = fieldId;
+    }
+    if (rowId != null) {
+      _result.rowId = rowId;
+    }
+    return _result;
+  }
+  factory CellIdentifierPayload.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
+  factory CellIdentifierPayload.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
+  @$core.Deprecated(
+  'Using this can add significant overhead to your binary. '
+  'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
+  'Will be removed in next major version')
+  CellIdentifierPayload clone() => CellIdentifierPayload()..mergeFromMessage(this);
+  @$core.Deprecated(
+  'Using this can add significant overhead to your binary. '
+  'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
+  'Will be removed in next major version')
+  CellIdentifierPayload copyWith(void Function(CellIdentifierPayload) updates) => super.copyWith((message) => updates(message as CellIdentifierPayload)) as CellIdentifierPayload; // ignore: deprecated_member_use
+  $pb.BuilderInfo get info_ => _i;
+  @$core.pragma('dart2js:noInline')
+  static CellIdentifierPayload create() => CellIdentifierPayload._();
+  CellIdentifierPayload createEmptyInstance() => create();
+  static $pb.PbList<CellIdentifierPayload> createRepeated() => $pb.PbList<CellIdentifierPayload>();
+  @$core.pragma('dart2js:noInline')
+  static CellIdentifierPayload getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<CellIdentifierPayload>(create);
+  static CellIdentifierPayload? _defaultInstance;
+
+  @$pb.TagNumber(1)
+  $core.String get gridId => $_getSZ(0);
+  @$pb.TagNumber(1)
+  set gridId($core.String v) { $_setString(0, v); }
+  @$pb.TagNumber(1)
+  $core.bool hasGridId() => $_has(0);
+  @$pb.TagNumber(1)
+  void clearGridId() => clearField(1);
+
+  @$pb.TagNumber(2)
+  $core.String get fieldId => $_getSZ(1);
+  @$pb.TagNumber(2)
+  set fieldId($core.String v) { $_setString(1, v); }
+  @$pb.TagNumber(2)
+  $core.bool hasFieldId() => $_has(1);
+  @$pb.TagNumber(2)
+  void clearFieldId() => clearField(2);
+
+  @$pb.TagNumber(3)
+  $core.String get rowId => $_getSZ(2);
+  @$pb.TagNumber(3)
+  set rowId($core.String v) { $_setString(2, v); }
+  @$pb.TagNumber(3)
+  $core.bool hasRowId() => $_has(2);
+  @$pb.TagNumber(3)
+  void clearRowId() => clearField(3);
+}
+
+class SelectOptionName extends $pb.GeneratedMessage {
+  static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'SelectOptionName', createEmptyInstance: create)
+    ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'name')
+    ..hasRequiredFields = false
+  ;
+
+  SelectOptionName._() : super();
+  factory SelectOptionName({
+    $core.String? name,
+  }) {
+    final _result = create();
+    if (name != null) {
+      _result.name = name;
+    }
+    return _result;
+  }
+  factory SelectOptionName.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
+  factory SelectOptionName.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
+  @$core.Deprecated(
+  'Using this can add significant overhead to your binary. '
+  'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
+  'Will be removed in next major version')
+  SelectOptionName clone() => SelectOptionName()..mergeFromMessage(this);
+  @$core.Deprecated(
+  'Using this can add significant overhead to your binary. '
+  'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
+  'Will be removed in next major version')
+  SelectOptionName copyWith(void Function(SelectOptionName) updates) => super.copyWith((message) => updates(message as SelectOptionName)) as SelectOptionName; // ignore: deprecated_member_use
+  $pb.BuilderInfo get info_ => _i;
+  @$core.pragma('dart2js:noInline')
+  static SelectOptionName create() => SelectOptionName._();
+  SelectOptionName createEmptyInstance() => create();
+  static $pb.PbList<SelectOptionName> createRepeated() => $pb.PbList<SelectOptionName>();
+  @$core.pragma('dart2js:noInline')
+  static SelectOptionName getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<SelectOptionName>(create);
+  static SelectOptionName? _defaultInstance;
+
+  @$pb.TagNumber(1)
+  $core.String get name => $_getSZ(0);
+  @$pb.TagNumber(1)
+  set name($core.String v) { $_setString(0, v); }
+  @$pb.TagNumber(1)
+  $core.bool hasName() => $_has(0);
+  @$pb.TagNumber(1)
+  void clearName() => clearField(1);
+}
+

+ 7 - 0
frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/cell_entities.pbenum.dart

@@ -0,0 +1,7 @@
+///
+//  Generated code. Do not modify.
+//  source: cell_entities.proto
+//
+// @dart = 2.12
+// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields
+

+ 43 - 0
frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/cell_entities.pbjson.dart

@@ -0,0 +1,43 @@
+///
+//  Generated code. Do not modify.
+//  source: cell_entities.proto
+//
+// @dart = 2.12
+// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields,deprecated_member_use_from_same_package
+
+import 'dart:core' as $core;
+import 'dart:convert' as $convert;
+import 'dart:typed_data' as $typed_data;
+@$core.Deprecated('Use createSelectOptionPayloadDescriptor instead')
+const CreateSelectOptionPayload$json = const {
+  '1': 'CreateSelectOptionPayload',
+  '2': const [
+    const {'1': 'cell_identifier', '3': 1, '4': 1, '5': 11, '6': '.CellIdentifierPayload', '10': 'cellIdentifier'},
+    const {'1': 'option_name', '3': 2, '4': 1, '5': 9, '10': 'optionName'},
+  ],
+};
+
+/// Descriptor for `CreateSelectOptionPayload`. Decode as a `google.protobuf.DescriptorProto`.
+final $typed_data.Uint8List createSelectOptionPayloadDescriptor = $convert.base64Decode('ChlDcmVhdGVTZWxlY3RPcHRpb25QYXlsb2FkEj8KD2NlbGxfaWRlbnRpZmllchgBIAEoCzIWLkNlbGxJZGVudGlmaWVyUGF5bG9hZFIOY2VsbElkZW50aWZpZXISHwoLb3B0aW9uX25hbWUYAiABKAlSCm9wdGlvbk5hbWU=');
+@$core.Deprecated('Use cellIdentifierPayloadDescriptor instead')
+const CellIdentifierPayload$json = const {
+  '1': 'CellIdentifierPayload',
+  '2': const [
+    const {'1': 'grid_id', '3': 1, '4': 1, '5': 9, '10': 'gridId'},
+    const {'1': 'field_id', '3': 2, '4': 1, '5': 9, '10': 'fieldId'},
+    const {'1': 'row_id', '3': 3, '4': 1, '5': 9, '10': 'rowId'},
+  ],
+};
+
+/// Descriptor for `CellIdentifierPayload`. Decode as a `google.protobuf.DescriptorProto`.
+final $typed_data.Uint8List cellIdentifierPayloadDescriptor = $convert.base64Decode('ChVDZWxsSWRlbnRpZmllclBheWxvYWQSFwoHZ3JpZF9pZBgBIAEoCVIGZ3JpZElkEhkKCGZpZWxkX2lkGAIgASgJUgdmaWVsZElkEhUKBnJvd19pZBgDIAEoCVIFcm93SWQ=');
+@$core.Deprecated('Use selectOptionNameDescriptor instead')
+const SelectOptionName$json = const {
+  '1': 'SelectOptionName',
+  '2': const [
+    const {'1': 'name', '3': 1, '4': 1, '5': 9, '10': 'name'},
+  ],
+};
+
+/// Descriptor for `SelectOptionName`. Decode as a `google.protobuf.DescriptorProto`.
+final $typed_data.Uint8List selectOptionNameDescriptor = $convert.base64Decode('ChBTZWxlY3RPcHRpb25OYW1lEhIKBG5hbWUYASABKAlSBG5hbWU=');

+ 9 - 0
frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/cell_entities.pbserver.dart

@@ -0,0 +1,9 @@
+///
+//  Generated code. Do not modify.
+//  source: cell_entities.proto
+//
+// @dart = 2.12
+// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields,deprecated_member_use_from_same_package
+
+export 'cell_entities.pb.dart';
+

+ 2 - 2
frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/dart_notification.pbenum.dart

@@ -15,7 +15,7 @@ class GridNotification extends $pb.ProtobufEnum {
   static const GridNotification DidUpdateBlock = GridNotification._(20, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'DidUpdateBlock');
   static const GridNotification DidUpdateRow = GridNotification._(30, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'DidUpdateRow');
   static const GridNotification DidUpdateCell = GridNotification._(31, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'DidUpdateCell');
-  static const GridNotification DidUpdateFields = GridNotification._(40, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'DidUpdateFields');
+  static const GridNotification DidUpdateGrid = GridNotification._(40, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'DidUpdateGrid');
   static const GridNotification DidUpdateField = GridNotification._(41, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'DidUpdateField');
 
   static const $core.List<GridNotification> values = <GridNotification> [
@@ -24,7 +24,7 @@ class GridNotification extends $pb.ProtobufEnum {
     DidUpdateBlock,
     DidUpdateRow,
     DidUpdateCell,
-    DidUpdateFields,
+    DidUpdateGrid,
     DidUpdateField,
   ];
 

+ 2 - 2
frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/dart_notification.pbjson.dart

@@ -17,10 +17,10 @@ const GridNotification$json = const {
     const {'1': 'DidUpdateBlock', '2': 20},
     const {'1': 'DidUpdateRow', '2': 30},
     const {'1': 'DidUpdateCell', '2': 31},
-    const {'1': 'DidUpdateFields', '2': 40},
+    const {'1': 'DidUpdateGrid', '2': 40},
     const {'1': 'DidUpdateField', '2': 41},
   ],
 };
 
 /// Descriptor for `GridNotification`. Decode as a `google.protobuf.EnumDescriptorProto`.
-final $typed_data.Uint8List gridNotificationDescriptor = $convert.base64Decode('ChBHcmlkTm90aWZpY2F0aW9uEgsKB1Vua25vd24QABISCg5EaWRDcmVhdGVCbG9jaxALEhIKDkRpZFVwZGF0ZUJsb2NrEBQSEAoMRGlkVXBkYXRlUm93EB4SEQoNRGlkVXBkYXRlQ2VsbBAfEhMKD0RpZFVwZGF0ZUZpZWxkcxAoEhIKDkRpZFVwZGF0ZUZpZWxkECk=');
+final $typed_data.Uint8List gridNotificationDescriptor = $convert.base64Decode('ChBHcmlkTm90aWZpY2F0aW9uEgsKB1Vua25vd24QABISCg5EaWRDcmVhdGVCbG9jaxALEhIKDkRpZFVwZGF0ZUJsb2NrEBQSEAoMRGlkVXBkYXRlUm93EB4SEQoNRGlkVXBkYXRlQ2VsbBAfEhEKDURpZFVwZGF0ZUdyaWQQKBISCg5EaWRVcGRhdGVGaWVsZBAp');

+ 8 - 6
frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/event_map.pbenum.dart

@@ -19,12 +19,13 @@ class GridEvent extends $pb.ProtobufEnum {
   static const GridEvent SwitchToField = GridEvent._(14, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'SwitchToField');
   static const GridEvent DuplicateField = GridEvent._(15, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'DuplicateField');
   static const GridEvent GetEditFieldContext = GridEvent._(16, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'GetEditFieldContext');
-  static const GridEvent CreateSelectOption = GridEvent._(30, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'CreateSelectOption');
-  static const GridEvent GetSelectOptions = GridEvent._(31, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'GetSelectOptions');
+  static const GridEvent NewSelectOption = GridEvent._(30, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'NewSelectOption');
+  static const GridEvent GetSelectOptionContext = GridEvent._(31, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'GetSelectOptionContext');
+  static const GridEvent ApplySelectOptionChangeset = GridEvent._(32, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'ApplySelectOptionChangeset');
   static const GridEvent CreateRow = GridEvent._(50, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'CreateRow');
   static const GridEvent GetRow = GridEvent._(51, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'GetRow');
   static const GridEvent UpdateCell = GridEvent._(70, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'UpdateCell');
-  static const GridEvent ApplySelectOptionChangeset = GridEvent._(71, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'ApplySelectOptionChangeset');
+  static const GridEvent ApplySelectOptionCellChangeset = GridEvent._(71, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'ApplySelectOptionCellChangeset');
 
   static const $core.List<GridEvent> values = <GridEvent> [
     GetGridData,
@@ -36,12 +37,13 @@ class GridEvent extends $pb.ProtobufEnum {
     SwitchToField,
     DuplicateField,
     GetEditFieldContext,
-    CreateSelectOption,
-    GetSelectOptions,
+    NewSelectOption,
+    GetSelectOptionContext,
+    ApplySelectOptionChangeset,
     CreateRow,
     GetRow,
     UpdateCell,
-    ApplySelectOptionChangeset,
+    ApplySelectOptionCellChangeset,
   ];
 
   static final $core.Map<$core.int, GridEvent> _byValue = $pb.ProtobufEnum.initByValue(values);

+ 5 - 4
frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/event_map.pbjson.dart

@@ -21,14 +21,15 @@ const GridEvent$json = const {
     const {'1': 'SwitchToField', '2': 14},
     const {'1': 'DuplicateField', '2': 15},
     const {'1': 'GetEditFieldContext', '2': 16},
-    const {'1': 'CreateSelectOption', '2': 30},
-    const {'1': 'GetSelectOptions', '2': 31},
+    const {'1': 'NewSelectOption', '2': 30},
+    const {'1': 'GetSelectOptionContext', '2': 31},
+    const {'1': 'ApplySelectOptionChangeset', '2': 32},
     const {'1': 'CreateRow', '2': 50},
     const {'1': 'GetRow', '2': 51},
     const {'1': 'UpdateCell', '2': 70},
-    const {'1': 'ApplySelectOptionChangeset', '2': 71},
+    const {'1': 'ApplySelectOptionCellChangeset', '2': 71},
   ],
 };
 
 /// Descriptor for `GridEvent`. Decode as a `google.protobuf.EnumDescriptorProto`.
-final $typed_data.Uint8List gridEventDescriptor = $convert.base64Decode('CglHcmlkRXZlbnQSDwoLR2V0R3JpZERhdGEQABIRCg1HZXRHcmlkQmxvY2tzEAESDQoJR2V0RmllbGRzEAoSDwoLVXBkYXRlRmllbGQQCxIPCgtDcmVhdGVGaWVsZBAMEg8KC0RlbGV0ZUZpZWxkEA0SEQoNU3dpdGNoVG9GaWVsZBAOEhIKDkR1cGxpY2F0ZUZpZWxkEA8SFwoTR2V0RWRpdEZpZWxkQ29udGV4dBAQEhYKEkNyZWF0ZVNlbGVjdE9wdGlvbhAeEhQKEEdldFNlbGVjdE9wdGlvbnMQHxINCglDcmVhdGVSb3cQMhIKCgZHZXRSb3cQMxIOCgpVcGRhdGVDZWxsEEYSHgoaQXBwbHlTZWxlY3RPcHRpb25DaGFuZ2VzZXQQRw==');
+final $typed_data.Uint8List gridEventDescriptor = $convert.base64Decode('CglHcmlkRXZlbnQSDwoLR2V0R3JpZERhdGEQABIRCg1HZXRHcmlkQmxvY2tzEAESDQoJR2V0RmllbGRzEAoSDwoLVXBkYXRlRmllbGQQCxIPCgtDcmVhdGVGaWVsZBAMEg8KC0RlbGV0ZUZpZWxkEA0SEQoNU3dpdGNoVG9GaWVsZBAOEhIKDkR1cGxpY2F0ZUZpZWxkEA8SFwoTR2V0RWRpdEZpZWxkQ29udGV4dBAQEhMKD05ld1NlbGVjdE9wdGlvbhAeEhoKFkdldFNlbGVjdE9wdGlvbkNvbnRleHQQHxIeChpBcHBseVNlbGVjdE9wdGlvbkNoYW5nZXNldBAgEg0KCUNyZWF0ZVJvdxAyEgoKBkdldFJvdxAzEg4KClVwZGF0ZUNlbGwQRhIiCh5BcHBseVNlbGVjdE9wdGlvbkNlbGxDaGFuZ2VzZXQQRw==');

+ 72 - 0
frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/field_entities.pb.dart

@@ -0,0 +1,72 @@
+///
+//  Generated code. Do not modify.
+//  source: field_entities.proto
+//
+// @dart = 2.12
+// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields
+
+import 'dart:core' as $core;
+
+import 'package:protobuf/protobuf.dart' as $pb;
+
+class FieldIdentifierPayload extends $pb.GeneratedMessage {
+  static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'FieldIdentifierPayload', createEmptyInstance: create)
+    ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'fieldId')
+    ..aOS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'gridId')
+    ..hasRequiredFields = false
+  ;
+
+  FieldIdentifierPayload._() : super();
+  factory FieldIdentifierPayload({
+    $core.String? fieldId,
+    $core.String? gridId,
+  }) {
+    final _result = create();
+    if (fieldId != null) {
+      _result.fieldId = fieldId;
+    }
+    if (gridId != null) {
+      _result.gridId = gridId;
+    }
+    return _result;
+  }
+  factory FieldIdentifierPayload.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
+  factory FieldIdentifierPayload.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
+  @$core.Deprecated(
+  'Using this can add significant overhead to your binary. '
+  'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
+  'Will be removed in next major version')
+  FieldIdentifierPayload clone() => FieldIdentifierPayload()..mergeFromMessage(this);
+  @$core.Deprecated(
+  'Using this can add significant overhead to your binary. '
+  'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
+  'Will be removed in next major version')
+  FieldIdentifierPayload copyWith(void Function(FieldIdentifierPayload) updates) => super.copyWith((message) => updates(message as FieldIdentifierPayload)) as FieldIdentifierPayload; // ignore: deprecated_member_use
+  $pb.BuilderInfo get info_ => _i;
+  @$core.pragma('dart2js:noInline')
+  static FieldIdentifierPayload create() => FieldIdentifierPayload._();
+  FieldIdentifierPayload createEmptyInstance() => create();
+  static $pb.PbList<FieldIdentifierPayload> createRepeated() => $pb.PbList<FieldIdentifierPayload>();
+  @$core.pragma('dart2js:noInline')
+  static FieldIdentifierPayload getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<FieldIdentifierPayload>(create);
+  static FieldIdentifierPayload? _defaultInstance;
+
+  @$pb.TagNumber(1)
+  $core.String get fieldId => $_getSZ(0);
+  @$pb.TagNumber(1)
+  set fieldId($core.String v) { $_setString(0, v); }
+  @$pb.TagNumber(1)
+  $core.bool hasFieldId() => $_has(0);
+  @$pb.TagNumber(1)
+  void clearFieldId() => clearField(1);
+
+  @$pb.TagNumber(2)
+  $core.String get gridId => $_getSZ(1);
+  @$pb.TagNumber(2)
+  set gridId($core.String v) { $_setString(1, v); }
+  @$pb.TagNumber(2)
+  $core.bool hasGridId() => $_has(1);
+  @$pb.TagNumber(2)
+  void clearGridId() => clearField(2);
+}
+

+ 7 - 0
frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/field_entities.pbenum.dart

@@ -0,0 +1,7 @@
+///
+//  Generated code. Do not modify.
+//  source: field_entities.proto
+//
+// @dart = 2.12
+// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields
+

+ 21 - 0
frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/field_entities.pbjson.dart

@@ -0,0 +1,21 @@
+///
+//  Generated code. Do not modify.
+//  source: field_entities.proto
+//
+// @dart = 2.12
+// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields,deprecated_member_use_from_same_package
+
+import 'dart:core' as $core;
+import 'dart:convert' as $convert;
+import 'dart:typed_data' as $typed_data;
+@$core.Deprecated('Use fieldIdentifierPayloadDescriptor instead')
+const FieldIdentifierPayload$json = const {
+  '1': 'FieldIdentifierPayload',
+  '2': const [
+    const {'1': 'field_id', '3': 1, '4': 1, '5': 9, '10': 'fieldId'},
+    const {'1': 'grid_id', '3': 2, '4': 1, '5': 9, '10': 'gridId'},
+  ],
+};
+
+/// Descriptor for `FieldIdentifierPayload`. Decode as a `google.protobuf.DescriptorProto`.
+final $typed_data.Uint8List fieldIdentifierPayloadDescriptor = $convert.base64Decode('ChZGaWVsZElkZW50aWZpZXJQYXlsb2FkEhkKCGZpZWxkX2lkGAEgASgJUgdmaWVsZElkEhcKB2dyaWRfaWQYAiABKAlSBmdyaWRJZA==');

+ 9 - 0
frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/field_entities.pbserver.dart

@@ -0,0 +1,9 @@
+///
+//  Generated code. Do not modify.
+//  source: field_entities.proto
+//
+// @dart = 2.12
+// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields,deprecated_member_use_from_same_package
+
+export 'field_entities.pb.dart';
+

+ 3 - 0
frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/protobuf.dart

@@ -1,7 +1,10 @@
 // Auto-generated, do not edit 
+export './field_entities.pb.dart';
 export './number_type_option.pb.dart';
 export './dart_notification.pb.dart';
 export './selection_type_option.pb.dart';
+export './row_entities.pb.dart';
+export './cell_entities.pb.dart';
 export './checkbox_type_option.pb.dart';
 export './event_map.pb.dart';
 export './text_type_option.pb.dart';

+ 72 - 0
frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/row_entities.pb.dart

@@ -0,0 +1,72 @@
+///
+//  Generated code. Do not modify.
+//  source: row_entities.proto
+//
+// @dart = 2.12
+// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields
+
+import 'dart:core' as $core;
+
+import 'package:protobuf/protobuf.dart' as $pb;
+
+class RowIdentifierPayload extends $pb.GeneratedMessage {
+  static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'RowIdentifierPayload', createEmptyInstance: create)
+    ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'gridId')
+    ..aOS(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'rowId')
+    ..hasRequiredFields = false
+  ;
+
+  RowIdentifierPayload._() : super();
+  factory RowIdentifierPayload({
+    $core.String? gridId,
+    $core.String? rowId,
+  }) {
+    final _result = create();
+    if (gridId != null) {
+      _result.gridId = gridId;
+    }
+    if (rowId != null) {
+      _result.rowId = rowId;
+    }
+    return _result;
+  }
+  factory RowIdentifierPayload.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
+  factory RowIdentifierPayload.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
+  @$core.Deprecated(
+  'Using this can add significant overhead to your binary. '
+  'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
+  'Will be removed in next major version')
+  RowIdentifierPayload clone() => RowIdentifierPayload()..mergeFromMessage(this);
+  @$core.Deprecated(
+  'Using this can add significant overhead to your binary. '
+  'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
+  'Will be removed in next major version')
+  RowIdentifierPayload copyWith(void Function(RowIdentifierPayload) updates) => super.copyWith((message) => updates(message as RowIdentifierPayload)) as RowIdentifierPayload; // ignore: deprecated_member_use
+  $pb.BuilderInfo get info_ => _i;
+  @$core.pragma('dart2js:noInline')
+  static RowIdentifierPayload create() => RowIdentifierPayload._();
+  RowIdentifierPayload createEmptyInstance() => create();
+  static $pb.PbList<RowIdentifierPayload> createRepeated() => $pb.PbList<RowIdentifierPayload>();
+  @$core.pragma('dart2js:noInline')
+  static RowIdentifierPayload getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<RowIdentifierPayload>(create);
+  static RowIdentifierPayload? _defaultInstance;
+
+  @$pb.TagNumber(1)
+  $core.String get gridId => $_getSZ(0);
+  @$pb.TagNumber(1)
+  set gridId($core.String v) { $_setString(0, v); }
+  @$pb.TagNumber(1)
+  $core.bool hasGridId() => $_has(0);
+  @$pb.TagNumber(1)
+  void clearGridId() => clearField(1);
+
+  @$pb.TagNumber(3)
+  $core.String get rowId => $_getSZ(1);
+  @$pb.TagNumber(3)
+  set rowId($core.String v) { $_setString(1, v); }
+  @$pb.TagNumber(3)
+  $core.bool hasRowId() => $_has(1);
+  @$pb.TagNumber(3)
+  void clearRowId() => clearField(3);
+}
+

+ 7 - 0
frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/row_entities.pbenum.dart

@@ -0,0 +1,7 @@
+///
+//  Generated code. Do not modify.
+//  source: row_entities.proto
+//
+// @dart = 2.12
+// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields
+

+ 21 - 0
frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/row_entities.pbjson.dart

@@ -0,0 +1,21 @@
+///
+//  Generated code. Do not modify.
+//  source: row_entities.proto
+//
+// @dart = 2.12
+// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields,deprecated_member_use_from_same_package
+
+import 'dart:core' as $core;
+import 'dart:convert' as $convert;
+import 'dart:typed_data' as $typed_data;
+@$core.Deprecated('Use rowIdentifierPayloadDescriptor instead')
+const RowIdentifierPayload$json = const {
+  '1': 'RowIdentifierPayload',
+  '2': const [
+    const {'1': 'grid_id', '3': 1, '4': 1, '5': 9, '10': 'gridId'},
+    const {'1': 'row_id', '3': 3, '4': 1, '5': 9, '10': 'rowId'},
+  ],
+};
+
+/// Descriptor for `RowIdentifierPayload`. Decode as a `google.protobuf.DescriptorProto`.
+final $typed_data.Uint8List rowIdentifierPayloadDescriptor = $convert.base64Decode('ChRSb3dJZGVudGlmaWVyUGF5bG9hZBIXCgdncmlkX2lkGAEgASgJUgZncmlkSWQSFQoGcm93X2lkGAMgASgJUgVyb3dJZA==');

+ 9 - 0
frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/row_entities.pbserver.dart

@@ -0,0 +1,9 @@
+///
+//  Generated code. Do not modify.
+//  source: row_entities.proto
+//
+// @dart = 2.12
+// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields,deprecated_member_use_from_same_package
+
+export 'row_entities.pb.dart';
+

+ 132 - 23
frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/selection_type_option.pb.dart

@@ -9,6 +9,8 @@ import 'dart:core' as $core;
 
 import 'package:protobuf/protobuf.dart' as $pb;
 
+import 'cell_entities.pb.dart' as $0;
+
 import 'selection_type_option.pbenum.dart';
 
 export 'selection_type_option.pbenum.dart';
@@ -198,26 +200,133 @@ class SelectOption extends $pb.GeneratedMessage {
   void clearColor() => clearField(3);
 }
 
-enum SelectOptionChangesetPayload_OneOfInsertOptionId {
-  insertOptionId, 
+enum SelectOptionChangesetPayload_OneOfInsertOption {
+  insertOption, 
   notSet
 }
 
-enum SelectOptionChangesetPayload_OneOfDeleteOptionId {
-  deleteOptionId, 
+enum SelectOptionChangesetPayload_OneOfDeleteOption {
+  deleteOption, 
   notSet
 }
 
 class SelectOptionChangesetPayload extends $pb.GeneratedMessage {
-  static const $core.Map<$core.int, SelectOptionChangesetPayload_OneOfInsertOptionId> _SelectOptionChangesetPayload_OneOfInsertOptionIdByTag = {
-    4 : SelectOptionChangesetPayload_OneOfInsertOptionId.insertOptionId,
-    0 : SelectOptionChangesetPayload_OneOfInsertOptionId.notSet
+  static const $core.Map<$core.int, SelectOptionChangesetPayload_OneOfInsertOption> _SelectOptionChangesetPayload_OneOfInsertOptionByTag = {
+    2 : SelectOptionChangesetPayload_OneOfInsertOption.insertOption,
+    0 : SelectOptionChangesetPayload_OneOfInsertOption.notSet
   };
-  static const $core.Map<$core.int, SelectOptionChangesetPayload_OneOfDeleteOptionId> _SelectOptionChangesetPayload_OneOfDeleteOptionIdByTag = {
-    5 : SelectOptionChangesetPayload_OneOfDeleteOptionId.deleteOptionId,
-    0 : SelectOptionChangesetPayload_OneOfDeleteOptionId.notSet
+  static const $core.Map<$core.int, SelectOptionChangesetPayload_OneOfDeleteOption> _SelectOptionChangesetPayload_OneOfDeleteOptionByTag = {
+    3 : SelectOptionChangesetPayload_OneOfDeleteOption.deleteOption,
+    0 : SelectOptionChangesetPayload_OneOfDeleteOption.notSet
   };
   static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'SelectOptionChangesetPayload', createEmptyInstance: create)
+    ..oo(0, [2])
+    ..oo(1, [3])
+    ..aOM<$0.CellIdentifierPayload>(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'cellIdentifier', subBuilder: $0.CellIdentifierPayload.create)
+    ..aOM<SelectOption>(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'insertOption', subBuilder: SelectOption.create)
+    ..aOM<SelectOption>(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'deleteOption', subBuilder: SelectOption.create)
+    ..hasRequiredFields = false
+  ;
+
+  SelectOptionChangesetPayload._() : super();
+  factory SelectOptionChangesetPayload({
+    $0.CellIdentifierPayload? cellIdentifier,
+    SelectOption? insertOption,
+    SelectOption? deleteOption,
+  }) {
+    final _result = create();
+    if (cellIdentifier != null) {
+      _result.cellIdentifier = cellIdentifier;
+    }
+    if (insertOption != null) {
+      _result.insertOption = insertOption;
+    }
+    if (deleteOption != null) {
+      _result.deleteOption = deleteOption;
+    }
+    return _result;
+  }
+  factory SelectOptionChangesetPayload.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
+  factory SelectOptionChangesetPayload.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
+  @$core.Deprecated(
+  'Using this can add significant overhead to your binary. '
+  'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
+  'Will be removed in next major version')
+  SelectOptionChangesetPayload clone() => SelectOptionChangesetPayload()..mergeFromMessage(this);
+  @$core.Deprecated(
+  'Using this can add significant overhead to your binary. '
+  'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
+  'Will be removed in next major version')
+  SelectOptionChangesetPayload copyWith(void Function(SelectOptionChangesetPayload) updates) => super.copyWith((message) => updates(message as SelectOptionChangesetPayload)) as SelectOptionChangesetPayload; // ignore: deprecated_member_use
+  $pb.BuilderInfo get info_ => _i;
+  @$core.pragma('dart2js:noInline')
+  static SelectOptionChangesetPayload create() => SelectOptionChangesetPayload._();
+  SelectOptionChangesetPayload createEmptyInstance() => create();
+  static $pb.PbList<SelectOptionChangesetPayload> createRepeated() => $pb.PbList<SelectOptionChangesetPayload>();
+  @$core.pragma('dart2js:noInline')
+  static SelectOptionChangesetPayload getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<SelectOptionChangesetPayload>(create);
+  static SelectOptionChangesetPayload? _defaultInstance;
+
+  SelectOptionChangesetPayload_OneOfInsertOption whichOneOfInsertOption() => _SelectOptionChangesetPayload_OneOfInsertOptionByTag[$_whichOneof(0)]!;
+  void clearOneOfInsertOption() => clearField($_whichOneof(0));
+
+  SelectOptionChangesetPayload_OneOfDeleteOption whichOneOfDeleteOption() => _SelectOptionChangesetPayload_OneOfDeleteOptionByTag[$_whichOneof(1)]!;
+  void clearOneOfDeleteOption() => clearField($_whichOneof(1));
+
+  @$pb.TagNumber(1)
+  $0.CellIdentifierPayload get cellIdentifier => $_getN(0);
+  @$pb.TagNumber(1)
+  set cellIdentifier($0.CellIdentifierPayload v) { setField(1, v); }
+  @$pb.TagNumber(1)
+  $core.bool hasCellIdentifier() => $_has(0);
+  @$pb.TagNumber(1)
+  void clearCellIdentifier() => clearField(1);
+  @$pb.TagNumber(1)
+  $0.CellIdentifierPayload ensureCellIdentifier() => $_ensure(0);
+
+  @$pb.TagNumber(2)
+  SelectOption get insertOption => $_getN(1);
+  @$pb.TagNumber(2)
+  set insertOption(SelectOption v) { setField(2, v); }
+  @$pb.TagNumber(2)
+  $core.bool hasInsertOption() => $_has(1);
+  @$pb.TagNumber(2)
+  void clearInsertOption() => clearField(2);
+  @$pb.TagNumber(2)
+  SelectOption ensureInsertOption() => $_ensure(1);
+
+  @$pb.TagNumber(3)
+  SelectOption get deleteOption => $_getN(2);
+  @$pb.TagNumber(3)
+  set deleteOption(SelectOption v) { setField(3, v); }
+  @$pb.TagNumber(3)
+  $core.bool hasDeleteOption() => $_has(2);
+  @$pb.TagNumber(3)
+  void clearDeleteOption() => clearField(3);
+  @$pb.TagNumber(3)
+  SelectOption ensureDeleteOption() => $_ensure(2);
+}
+
+enum SelectOptionCellChangesetPayload_OneOfInsertOptionId {
+  insertOptionId, 
+  notSet
+}
+
+enum SelectOptionCellChangesetPayload_OneOfDeleteOptionId {
+  deleteOptionId, 
+  notSet
+}
+
+class SelectOptionCellChangesetPayload extends $pb.GeneratedMessage {
+  static const $core.Map<$core.int, SelectOptionCellChangesetPayload_OneOfInsertOptionId> _SelectOptionCellChangesetPayload_OneOfInsertOptionIdByTag = {
+    4 : SelectOptionCellChangesetPayload_OneOfInsertOptionId.insertOptionId,
+    0 : SelectOptionCellChangesetPayload_OneOfInsertOptionId.notSet
+  };
+  static const $core.Map<$core.int, SelectOptionCellChangesetPayload_OneOfDeleteOptionId> _SelectOptionCellChangesetPayload_OneOfDeleteOptionIdByTag = {
+    5 : SelectOptionCellChangesetPayload_OneOfDeleteOptionId.deleteOptionId,
+    0 : SelectOptionCellChangesetPayload_OneOfDeleteOptionId.notSet
+  };
+  static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'SelectOptionCellChangesetPayload', createEmptyInstance: create)
     ..oo(0, [4])
     ..oo(1, [5])
     ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'gridId')
@@ -228,8 +337,8 @@ class SelectOptionChangesetPayload extends $pb.GeneratedMessage {
     ..hasRequiredFields = false
   ;
 
-  SelectOptionChangesetPayload._() : super();
-  factory SelectOptionChangesetPayload({
+  SelectOptionCellChangesetPayload._() : super();
+  factory SelectOptionCellChangesetPayload({
     $core.String? gridId,
     $core.String? rowId,
     $core.String? fieldId,
@@ -254,31 +363,31 @@ class SelectOptionChangesetPayload extends $pb.GeneratedMessage {
     }
     return _result;
   }
-  factory SelectOptionChangesetPayload.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
-  factory SelectOptionChangesetPayload.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
+  factory SelectOptionCellChangesetPayload.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
+  factory SelectOptionCellChangesetPayload.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
   @$core.Deprecated(
   'Using this can add significant overhead to your binary. '
   'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
   'Will be removed in next major version')
-  SelectOptionChangesetPayload clone() => SelectOptionChangesetPayload()..mergeFromMessage(this);
+  SelectOptionCellChangesetPayload clone() => SelectOptionCellChangesetPayload()..mergeFromMessage(this);
   @$core.Deprecated(
   'Using this can add significant overhead to your binary. '
   'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
   'Will be removed in next major version')
-  SelectOptionChangesetPayload copyWith(void Function(SelectOptionChangesetPayload) updates) => super.copyWith((message) => updates(message as SelectOptionChangesetPayload)) as SelectOptionChangesetPayload; // ignore: deprecated_member_use
+  SelectOptionCellChangesetPayload copyWith(void Function(SelectOptionCellChangesetPayload) updates) => super.copyWith((message) => updates(message as SelectOptionCellChangesetPayload)) as SelectOptionCellChangesetPayload; // ignore: deprecated_member_use
   $pb.BuilderInfo get info_ => _i;
   @$core.pragma('dart2js:noInline')
-  static SelectOptionChangesetPayload create() => SelectOptionChangesetPayload._();
-  SelectOptionChangesetPayload createEmptyInstance() => create();
-  static $pb.PbList<SelectOptionChangesetPayload> createRepeated() => $pb.PbList<SelectOptionChangesetPayload>();
+  static SelectOptionCellChangesetPayload create() => SelectOptionCellChangesetPayload._();
+  SelectOptionCellChangesetPayload createEmptyInstance() => create();
+  static $pb.PbList<SelectOptionCellChangesetPayload> createRepeated() => $pb.PbList<SelectOptionCellChangesetPayload>();
   @$core.pragma('dart2js:noInline')
-  static SelectOptionChangesetPayload getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<SelectOptionChangesetPayload>(create);
-  static SelectOptionChangesetPayload? _defaultInstance;
+  static SelectOptionCellChangesetPayload getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<SelectOptionCellChangesetPayload>(create);
+  static SelectOptionCellChangesetPayload? _defaultInstance;
 
-  SelectOptionChangesetPayload_OneOfInsertOptionId whichOneOfInsertOptionId() => _SelectOptionChangesetPayload_OneOfInsertOptionIdByTag[$_whichOneof(0)]!;
+  SelectOptionCellChangesetPayload_OneOfInsertOptionId whichOneOfInsertOptionId() => _SelectOptionCellChangesetPayload_OneOfInsertOptionIdByTag[$_whichOneof(0)]!;
   void clearOneOfInsertOptionId() => clearField($_whichOneof(0));
 
-  SelectOptionChangesetPayload_OneOfDeleteOptionId whichOneOfDeleteOptionId() => _SelectOptionChangesetPayload_OneOfDeleteOptionIdByTag[$_whichOneof(1)]!;
+  SelectOptionCellChangesetPayload_OneOfDeleteOptionId whichOneOfDeleteOptionId() => _SelectOptionCellChangesetPayload_OneOfDeleteOptionIdByTag[$_whichOneof(1)]!;
   void clearOneOfDeleteOptionId() => clearField($_whichOneof(1));
 
   @$pb.TagNumber(1)

+ 18 - 2
frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/selection_type_option.pbjson.dart

@@ -63,6 +63,22 @@ final $typed_data.Uint8List selectOptionDescriptor = $convert.base64Decode('CgxT
 @$core.Deprecated('Use selectOptionChangesetPayloadDescriptor instead')
 const SelectOptionChangesetPayload$json = const {
   '1': 'SelectOptionChangesetPayload',
+  '2': const [
+    const {'1': 'cell_identifier', '3': 1, '4': 1, '5': 11, '6': '.CellIdentifierPayload', '10': 'cellIdentifier'},
+    const {'1': 'insert_option', '3': 2, '4': 1, '5': 11, '6': '.SelectOption', '9': 0, '10': 'insertOption'},
+    const {'1': 'delete_option', '3': 3, '4': 1, '5': 11, '6': '.SelectOption', '9': 1, '10': 'deleteOption'},
+  ],
+  '8': const [
+    const {'1': 'one_of_insert_option'},
+    const {'1': 'one_of_delete_option'},
+  ],
+};
+
+/// Descriptor for `SelectOptionChangesetPayload`. Decode as a `google.protobuf.DescriptorProto`.
+final $typed_data.Uint8List selectOptionChangesetPayloadDescriptor = $convert.base64Decode('ChxTZWxlY3RPcHRpb25DaGFuZ2VzZXRQYXlsb2FkEj8KD2NlbGxfaWRlbnRpZmllchgBIAEoCzIWLkNlbGxJZGVudGlmaWVyUGF5bG9hZFIOY2VsbElkZW50aWZpZXISNAoNaW5zZXJ0X29wdGlvbhgCIAEoCzINLlNlbGVjdE9wdGlvbkgAUgxpbnNlcnRPcHRpb24SNAoNZGVsZXRlX29wdGlvbhgDIAEoCzINLlNlbGVjdE9wdGlvbkgBUgxkZWxldGVPcHRpb25CFgoUb25lX29mX2luc2VydF9vcHRpb25CFgoUb25lX29mX2RlbGV0ZV9vcHRpb24=');
+@$core.Deprecated('Use selectOptionCellChangesetPayloadDescriptor instead')
+const SelectOptionCellChangesetPayload$json = const {
+  '1': 'SelectOptionCellChangesetPayload',
   '2': const [
     const {'1': 'grid_id', '3': 1, '4': 1, '5': 9, '10': 'gridId'},
     const {'1': 'row_id', '3': 2, '4': 1, '5': 9, '10': 'rowId'},
@@ -76,8 +92,8 @@ const SelectOptionChangesetPayload$json = const {
   ],
 };
 
-/// Descriptor for `SelectOptionChangesetPayload`. Decode as a `google.protobuf.DescriptorProto`.
-final $typed_data.Uint8List selectOptionChangesetPayloadDescriptor = $convert.base64Decode('ChxTZWxlY3RPcHRpb25DaGFuZ2VzZXRQYXlsb2FkEhcKB2dyaWRfaWQYASABKAlSBmdyaWRJZBIVCgZyb3dfaWQYAiABKAlSBXJvd0lkEhkKCGZpZWxkX2lkGAMgASgJUgdmaWVsZElkEioKEGluc2VydF9vcHRpb25faWQYBCABKAlIAFIOaW5zZXJ0T3B0aW9uSWQSKgoQZGVsZXRlX29wdGlvbl9pZBgFIAEoCUgBUg5kZWxldGVPcHRpb25JZEIZChdvbmVfb2ZfaW5zZXJ0X29wdGlvbl9pZEIZChdvbmVfb2ZfZGVsZXRlX29wdGlvbl9pZA==');
+/// Descriptor for `SelectOptionCellChangesetPayload`. Decode as a `google.protobuf.DescriptorProto`.
+final $typed_data.Uint8List selectOptionCellChangesetPayloadDescriptor = $convert.base64Decode('CiBTZWxlY3RPcHRpb25DZWxsQ2hhbmdlc2V0UGF5bG9hZBIXCgdncmlkX2lkGAEgASgJUgZncmlkSWQSFQoGcm93X2lkGAIgASgJUgVyb3dJZBIZCghmaWVsZF9pZBgDIAEoCVIHZmllbGRJZBIqChBpbnNlcnRfb3B0aW9uX2lkGAQgASgJSABSDmluc2VydE9wdGlvbklkEioKEGRlbGV0ZV9vcHRpb25faWQYBSABKAlIAVIOZGVsZXRlT3B0aW9uSWRCGQoXb25lX29mX2luc2VydF9vcHRpb25faWRCGQoXb25lX29mX2RlbGV0ZV9vcHRpb25faWQ=');
 @$core.Deprecated('Use selectOptionContextDescriptor instead')
 const SelectOptionContext$json = const {
   '1': 'SelectOptionContext',

+ 1 - 1
frontend/rust-lib/dart-ffi/Cargo.toml

@@ -8,7 +8,7 @@ edition = "2018"
 name = "dart_ffi"
 # this value will change depending on the target os
 # default static lib
-crate-type = ["cdylib"]
+crate-type = ["staticlib"]
 
 
 [dependencies]

+ 8 - 1
frontend/rust-lib/flowy-grid/Flowy.toml

@@ -1,3 +1,10 @@
 
-proto_crates = ["src/event_map.rs", "src/services/field/type_options", "src/dart_notification.rs"]
+proto_crates = [
+    "src/event_map.rs",
+    "src/services/field/type_options",
+    "src/services/field/field_entities.rs",
+    "src/services/cell/cell_entities.rs",
+    "src/services/row/row_entities.rs",
+    "src/dart_notification.rs"
+]
 event_files = ["src/event_map.rs"]

+ 1 - 1
frontend/rust-lib/flowy-grid/src/dart_notification.rs

@@ -9,7 +9,7 @@ pub enum GridNotification {
     DidUpdateBlock = 20,
     DidUpdateRow = 30,
     DidUpdateCell = 31,
-    DidUpdateFields = 40,
+    DidUpdateGrid = 40,
     DidUpdateField = 41,
 }
 

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

@@ -1,10 +1,11 @@
 use crate::manager::GridManager;
-use crate::services::field::{
-    default_type_option_builder_from_type, type_option_builder_from_json_str, MultiSelectTypeOption, SelectOption,
-    SelectOptionChangesetParams, SelectOptionChangesetPayload, SelectOptionContext, SingleSelectTypeOption,
-};
+use crate::services::cell::cell_entities::*;
+use crate::services::field::field_entities::*;
+use crate::services::field::type_options::*;
+use crate::services::field::{default_type_option_builder_from_type, type_option_builder_from_json_str};
 use crate::services::grid_editor::ClientGridEditor;
-use flowy_error::{FlowyError, FlowyResult};
+use crate::services::row::row_entities::*;
+use flowy_error::{ErrorCode, FlowyError, FlowyResult};
 use flowy_grid_data_model::entities::*;
 use lib_dispatch::prelude::{data_result, AppData, Data, DataResult};
 use std::sync::Arc;
@@ -76,7 +77,7 @@ pub(crate) async fn delete_field_handler(
     data: Data<FieldIdentifierPayload>,
     manager: AppData<Arc<GridManager>>,
 ) -> Result<(), FlowyError> {
-    let params: FieldIdentifierParams = data.into_inner().try_into()?;
+    let params: FieldIdentifier = data.into_inner().try_into()?;
     let editor = manager.get_grid_editor(&params.grid_id)?;
     let _ = editor.delete_field(&params.field_id).await?;
     Ok(())
@@ -93,10 +94,7 @@ pub(crate) async fn switch_to_field_handler(
         .switch_to_field_type(&params.field_id, &params.field_type)
         .await?;
 
-    let field_meta = editor
-        .get_field_metas(Some(vec![params.field_id.as_str()]))
-        .await?
-        .pop();
+    let field_meta = editor.get_field_meta(&params.field_id).await;
     let edit_context = make_field_edit_context(
         &params.grid_id,
         Some(params.field_id),
@@ -113,58 +111,12 @@ pub(crate) async fn duplicate_field_handler(
     data: Data<FieldIdentifierPayload>,
     manager: AppData<Arc<GridManager>>,
 ) -> Result<(), FlowyError> {
-    let params: FieldIdentifierParams = data.into_inner().try_into()?;
+    let params: FieldIdentifier = data.into_inner().try_into()?;
     let editor = manager.get_grid_editor(&params.grid_id)?;
     let _ = editor.duplicate_field(&params.field_id).await?;
     Ok(())
 }
 
-#[tracing::instrument(level = "debug", skip(data), err)]
-pub(crate) async fn create_select_option_handler(
-    data: Data<CreateSelectOptionPayload>,
-) -> DataResult<SelectOption, FlowyError> {
-    let params: CreateSelectOptionParams = data.into_inner().try_into()?;
-    data_result(SelectOption::new(&params.option_name))
-}
-
-#[tracing::instrument(level = "debug", skip(data, manager), err)]
-pub(crate) async fn get_select_option_handler(
-    data: Data<CellIdentifierPayload>,
-    manager: AppData<Arc<GridManager>>,
-) -> DataResult<SelectOptionContext, FlowyError> {
-    let params: CellIdentifier = data.into_inner().try_into()?;
-    let editor = manager.get_grid_editor(&params.grid_id)?;
-    match editor
-        .get_field_metas(Some(vec![params.field_id.as_str()]))
-        .await?
-        .pop()
-    {
-        None => {
-            tracing::error!("Can't find the corresponding field with id: {}", params.field_id);
-            data_result(SelectOptionContext::default())
-        }
-        Some(field_meta) => {
-            let cell_meta = editor.get_cell_meta(&params.row_id, &params.field_id).await?;
-            match field_meta.field_type {
-                FieldType::SingleSelect => {
-                    let type_option = SingleSelectTypeOption::from(&field_meta);
-                    let select_option_context = type_option.select_option_context(&cell_meta);
-                    data_result(select_option_context)
-                }
-                FieldType::MultiSelect => {
-                    let type_option = MultiSelectTypeOption::from(&field_meta);
-                    let select_option_context = type_option.select_option_context(&cell_meta);
-                    data_result(select_option_context)
-                }
-                ty => {
-                    tracing::error!("Unsupported field type: {:?} for this handler", ty);
-                    data_result(SelectOptionContext::default())
-                }
-            }
-        }
-    }
-}
-
 #[tracing::instrument(level = "debug", skip(data, manager), err)]
 pub(crate) async fn get_field_context_handler(
     data: Data<GetEditFieldContextPayload>,
@@ -207,7 +159,7 @@ async fn get_or_create_field_meta(
 ) -> FlowyResult<FieldMeta> {
     match field_id {
         None => editor.create_next_field_meta(field_type).await,
-        Some(field_id) => match editor.get_field_metas(Some(vec![field_id.as_str()])).await?.pop() {
+        Some(field_id) => match editor.get_field_meta(&field_id).await {
             None => editor.create_next_field_meta(field_type).await,
             Some(field_meta) => Ok(field_meta),
         },
@@ -216,10 +168,10 @@ async fn get_or_create_field_meta(
 
 #[tracing::instrument(level = "debug", skip(data, manager), err)]
 pub(crate) async fn get_row_handler(
-    data: Data<QueryRowPayload>,
+    data: Data<RowIdentifierPayload>,
     manager: AppData<Arc<GridManager>>,
 ) -> DataResult<Row, FlowyError> {
-    let params: QueryRowParams = data.into_inner().try_into()?;
+    let params: RowIdentifier = data.into_inner().try_into()?;
     let editor = manager.get_grid_editor(&params.grid_id)?;
     match editor.get_row(&params.row_id).await? {
         None => Err(FlowyError::record_not_found().context("Can not find the row")),
@@ -250,13 +202,92 @@ pub(crate) async fn update_cell_handler(
 }
 
 #[tracing::instrument(level = "debug", skip_all, err)]
-pub(crate) async fn apply_select_option_changeset_handler(
+pub(crate) async fn new_select_option_handler(data: Data<SelectOptionName>) -> DataResult<SelectOption, FlowyError> {
+    let params = data.into_inner();
+    data_result(SelectOption::new(&params.name))
+}
+
+#[tracing::instrument(level = "debug", skip_all, err)]
+pub(crate) async fn select_option_changeset_handler(
     data: Data<SelectOptionChangesetPayload>,
     manager: AppData<Arc<GridManager>>,
 ) -> Result<(), FlowyError> {
-    let params: SelectOptionChangesetParams = data.into_inner().try_into()?;
+    let changeset: SelectOptionChangeset = data.into_inner().try_into()?;
+    let editor = manager.get_grid_editor(&changeset.cell_identifier.grid_id)?;
+
+    if let Some(mut field_meta) = editor.get_field_meta(&changeset.cell_identifier.field_id).await {
+        let mut type_option = select_option_operation(&field_meta)?;
+        let mut cell_data = None;
+        if let Some(option) = changeset.insert_option {
+            cell_data = Some(SelectOptionCellChangeset::from_insert(&option.id).cell_data());
+            type_option.insert_option(option);
+        }
+
+        if let Some(option) = changeset.delete_option {
+            cell_data = Some(SelectOptionCellChangeset::from_delete(&option.id).cell_data());
+            type_option.delete_option(option);
+        }
+
+        field_meta.insert_type_option_entry(&*type_option);
+        let _ = editor.replace_field(field_meta).await?;
+
+        let changeset = CellMetaChangeset {
+            grid_id: changeset.cell_identifier.grid_id,
+            row_id: changeset.cell_identifier.row_id,
+            field_id: changeset.cell_identifier.field_id,
+            data: cell_data,
+        };
+        let _ = editor.update_cell(changeset).await?;
+    }
+    Ok(())
+}
+
+#[tracing::instrument(level = "debug", skip(data, manager), err)]
+pub(crate) async fn get_select_option_handler(
+    data: Data<CellIdentifierPayload>,
+    manager: AppData<Arc<GridManager>>,
+) -> DataResult<SelectOptionContext, FlowyError> {
+    let params: CellIdentifier = data.into_inner().try_into()?;
+    let editor = manager.get_grid_editor(&params.grid_id)?;
+    match editor.get_field_meta(&params.field_id).await {
+        None => {
+            tracing::error!("Can't find the corresponding field with id: {}", params.field_id);
+            data_result(SelectOptionContext::default())
+        }
+        Some(field_meta) => {
+            let cell_meta = editor.get_cell_meta(&params.row_id, &params.field_id).await?;
+            let type_option = select_option_operation(&field_meta)?;
+            let option_context = type_option.option_context(&cell_meta);
+            data_result(option_context)
+        }
+    }
+}
+
+#[tracing::instrument(level = "debug", skip_all, err)]
+pub(crate) async fn select_option_cell_changeset_handler(
+    data: Data<SelectOptionCellChangesetPayload>,
+    manager: AppData<Arc<GridManager>>,
+) -> Result<(), FlowyError> {
+    let params: SelectOptionCellChangesetParams = data.into_inner().try_into()?;
     let editor = manager.get_grid_editor(&params.grid_id)?;
     let changeset: CellMetaChangeset = params.into();
     let _ = editor.update_cell(changeset).await?;
     Ok(())
 }
+
+fn select_option_operation(field_meta: &FieldMeta) -> FlowyResult<Box<dyn SelectOptionOperation>> {
+    match &field_meta.field_type {
+        FieldType::SingleSelect => {
+            let type_option = SingleSelectTypeOption::from(field_meta);
+            Ok(Box::new(type_option))
+        }
+        FieldType::MultiSelect => {
+            let type_option = MultiSelectTypeOption::from(field_meta);
+            Ok(Box::new(type_option))
+        }
+        ty => {
+            tracing::error!("Unsupported field type: {:?} for this handler", ty);
+            Err(ErrorCode::FieldInvalidOperation.into())
+        }
+    }
+}

+ 14 - 10
frontend/rust-lib/flowy-grid/src/event_map.rs

@@ -23,11 +23,12 @@ pub fn create(grid_manager: Arc<GridManager>) -> Module {
         // Cell
         .event(GridEvent::UpdateCell, update_cell_handler)
         // SelectOption
-        .event(GridEvent::CreateSelectOption, create_select_option_handler)
-        .event(GridEvent::GetSelectOptions, get_select_option_handler)
+        .event(GridEvent::NewSelectOption, new_select_option_handler)
+        .event(GridEvent::ApplySelectOptionChangeset, select_option_changeset_handler)
+        .event(GridEvent::GetSelectOptionContext, get_select_option_handler)
         .event(
-            GridEvent::ApplySelectOptionChangeset,
-            apply_select_option_changeset_handler,
+            GridEvent::ApplySelectOptionCellChangeset,
+            select_option_cell_changeset_handler,
         )
         .event(GridEvent::GetEditFieldContext, get_field_context_handler);
 
@@ -64,21 +65,24 @@ pub enum GridEvent {
     #[event(input = "GetEditFieldContextPayload", output = "EditFieldContext")]
     GetEditFieldContext = 16,
 
-    #[event(input = "CreateSelectOptionPayload", output = "SelectOption")]
-    CreateSelectOption = 30,
+    #[event(input = "SelectOptionName", output = "SelectOption")]
+    NewSelectOption = 30,
 
     #[event(input = "CellIdentifierPayload", output = "SelectOptionContext")]
-    GetSelectOptions = 31,
+    GetSelectOptionContext = 31,
+
+    #[event(input = "SelectOptionChangesetPayload")]
+    ApplySelectOptionChangeset = 32,
 
     #[event(input = "CreateRowPayload", output = "Row")]
     CreateRow = 50,
 
-    #[event(input = "QueryRowPayload", output = "Row")]
+    #[event(input = "RowIdentifierPayload", output = "Row")]
     GetRow = 51,
 
     #[event(input = "CellMetaChangeset")]
     UpdateCell = 70,
 
-    #[event(input = "SelectOptionChangesetPayload")]
-    ApplySelectOptionChangeset = 71,
+    #[event(input = "SelectOptionCellChangesetPayload")]
+    ApplySelectOptionCellChangeset = 71,
 }

+ 664 - 0
frontend/rust-lib/flowy-grid/src/protobuf/model/cell_entities.rs

@@ -0,0 +1,664 @@
+// This file is generated by rust-protobuf 2.25.2. Do not edit
+// @generated
+
+// https://github.com/rust-lang/rust-clippy/issues/702
+#![allow(unknown_lints)]
+#![allow(clippy::all)]
+
+#![allow(unused_attributes)]
+#![cfg_attr(rustfmt, rustfmt::skip)]
+
+#![allow(box_pointers)]
+#![allow(dead_code)]
+#![allow(missing_docs)]
+#![allow(non_camel_case_types)]
+#![allow(non_snake_case)]
+#![allow(non_upper_case_globals)]
+#![allow(trivial_casts)]
+#![allow(unused_imports)]
+#![allow(unused_results)]
+//! Generated file from `cell_entities.proto`
+
+/// Generated files are compatible only with the same version
+/// of protobuf runtime.
+// const _PROTOBUF_VERSION_CHECK: () = ::protobuf::VERSION_2_25_2;
+
+#[derive(PartialEq,Clone,Default)]
+pub struct CreateSelectOptionPayload {
+    // message fields
+    pub cell_identifier: ::protobuf::SingularPtrField<CellIdentifierPayload>,
+    pub option_name: ::std::string::String,
+    // special fields
+    pub unknown_fields: ::protobuf::UnknownFields,
+    pub cached_size: ::protobuf::CachedSize,
+}
+
+impl<'a> ::std::default::Default for &'a CreateSelectOptionPayload {
+    fn default() -> &'a CreateSelectOptionPayload {
+        <CreateSelectOptionPayload as ::protobuf::Message>::default_instance()
+    }
+}
+
+impl CreateSelectOptionPayload {
+    pub fn new() -> CreateSelectOptionPayload {
+        ::std::default::Default::default()
+    }
+
+    // .CellIdentifierPayload cell_identifier = 1;
+
+
+    pub fn get_cell_identifier(&self) -> &CellIdentifierPayload {
+        self.cell_identifier.as_ref().unwrap_or_else(|| <CellIdentifierPayload as ::protobuf::Message>::default_instance())
+    }
+    pub fn clear_cell_identifier(&mut self) {
+        self.cell_identifier.clear();
+    }
+
+    pub fn has_cell_identifier(&self) -> bool {
+        self.cell_identifier.is_some()
+    }
+
+    // Param is passed by value, moved
+    pub fn set_cell_identifier(&mut self, v: CellIdentifierPayload) {
+        self.cell_identifier = ::protobuf::SingularPtrField::some(v);
+    }
+
+    // Mutable pointer to the field.
+    // If field is not initialized, it is initialized with default value first.
+    pub fn mut_cell_identifier(&mut self) -> &mut CellIdentifierPayload {
+        if self.cell_identifier.is_none() {
+            self.cell_identifier.set_default();
+        }
+        self.cell_identifier.as_mut().unwrap()
+    }
+
+    // Take field
+    pub fn take_cell_identifier(&mut self) -> CellIdentifierPayload {
+        self.cell_identifier.take().unwrap_or_else(|| CellIdentifierPayload::new())
+    }
+
+    // string option_name = 2;
+
+
+    pub fn get_option_name(&self) -> &str {
+        &self.option_name
+    }
+    pub fn clear_option_name(&mut self) {
+        self.option_name.clear();
+    }
+
+    // Param is passed by value, moved
+    pub fn set_option_name(&mut self, v: ::std::string::String) {
+        self.option_name = v;
+    }
+
+    // Mutable pointer to the field.
+    // If field is not initialized, it is initialized with default value first.
+    pub fn mut_option_name(&mut self) -> &mut ::std::string::String {
+        &mut self.option_name
+    }
+
+    // Take field
+    pub fn take_option_name(&mut self) -> ::std::string::String {
+        ::std::mem::replace(&mut self.option_name, ::std::string::String::new())
+    }
+}
+
+impl ::protobuf::Message for CreateSelectOptionPayload {
+    fn is_initialized(&self) -> bool {
+        for v in &self.cell_identifier {
+            if !v.is_initialized() {
+                return false;
+            }
+        };
+        true
+    }
+
+    fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::ProtobufResult<()> {
+        while !is.eof()? {
+            let (field_number, wire_type) = is.read_tag_unpack()?;
+            match field_number {
+                1 => {
+                    ::protobuf::rt::read_singular_message_into(wire_type, is, &mut self.cell_identifier)?;
+                },
+                2 => {
+                    ::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.option_name)?;
+                },
+                _ => {
+                    ::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?;
+                },
+            };
+        }
+        ::std::result::Result::Ok(())
+    }
+
+    // Compute sizes of nested messages
+    #[allow(unused_variables)]
+    fn compute_size(&self) -> u32 {
+        let mut my_size = 0;
+        if let Some(ref v) = self.cell_identifier.as_ref() {
+            let len = v.compute_size();
+            my_size += 1 + ::protobuf::rt::compute_raw_varint32_size(len) + len;
+        }
+        if !self.option_name.is_empty() {
+            my_size += ::protobuf::rt::string_size(2, &self.option_name);
+        }
+        my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields());
+        self.cached_size.set(my_size);
+        my_size
+    }
+
+    fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::ProtobufResult<()> {
+        if let Some(ref v) = self.cell_identifier.as_ref() {
+            os.write_tag(1, ::protobuf::wire_format::WireTypeLengthDelimited)?;
+            os.write_raw_varint32(v.get_cached_size())?;
+            v.write_to_with_cached_sizes(os)?;
+        }
+        if !self.option_name.is_empty() {
+            os.write_string(2, &self.option_name)?;
+        }
+        os.write_unknown_fields(self.get_unknown_fields())?;
+        ::std::result::Result::Ok(())
+    }
+
+    fn get_cached_size(&self) -> u32 {
+        self.cached_size.get()
+    }
+
+    fn get_unknown_fields(&self) -> &::protobuf::UnknownFields {
+        &self.unknown_fields
+    }
+
+    fn mut_unknown_fields(&mut self) -> &mut ::protobuf::UnknownFields {
+        &mut self.unknown_fields
+    }
+
+    fn as_any(&self) -> &dyn (::std::any::Any) {
+        self as &dyn (::std::any::Any)
+    }
+    fn as_any_mut(&mut self) -> &mut dyn (::std::any::Any) {
+        self as &mut dyn (::std::any::Any)
+    }
+    fn into_any(self: ::std::boxed::Box<Self>) -> ::std::boxed::Box<dyn (::std::any::Any)> {
+        self
+    }
+
+    fn descriptor(&self) -> &'static ::protobuf::reflect::MessageDescriptor {
+        Self::descriptor_static()
+    }
+
+    fn new() -> CreateSelectOptionPayload {
+        CreateSelectOptionPayload::new()
+    }
+
+    fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor {
+        static descriptor: ::protobuf::rt::LazyV2<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::LazyV2::INIT;
+        descriptor.get(|| {
+            let mut fields = ::std::vec::Vec::new();
+            fields.push(::protobuf::reflect::accessor::make_singular_ptr_field_accessor::<_, ::protobuf::types::ProtobufTypeMessage<CellIdentifierPayload>>(
+                "cell_identifier",
+                |m: &CreateSelectOptionPayload| { &m.cell_identifier },
+                |m: &mut CreateSelectOptionPayload| { &mut m.cell_identifier },
+            ));
+            fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
+                "option_name",
+                |m: &CreateSelectOptionPayload| { &m.option_name },
+                |m: &mut CreateSelectOptionPayload| { &mut m.option_name },
+            ));
+            ::protobuf::reflect::MessageDescriptor::new_pb_name::<CreateSelectOptionPayload>(
+                "CreateSelectOptionPayload",
+                fields,
+                file_descriptor_proto()
+            )
+        })
+    }
+
+    fn default_instance() -> &'static CreateSelectOptionPayload {
+        static instance: ::protobuf::rt::LazyV2<CreateSelectOptionPayload> = ::protobuf::rt::LazyV2::INIT;
+        instance.get(CreateSelectOptionPayload::new)
+    }
+}
+
+impl ::protobuf::Clear for CreateSelectOptionPayload {
+    fn clear(&mut self) {
+        self.cell_identifier.clear();
+        self.option_name.clear();
+        self.unknown_fields.clear();
+    }
+}
+
+impl ::std::fmt::Debug for CreateSelectOptionPayload {
+    fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
+        ::protobuf::text_format::fmt(self, f)
+    }
+}
+
+impl ::protobuf::reflect::ProtobufValue for CreateSelectOptionPayload {
+    fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
+        ::protobuf::reflect::ReflectValueRef::Message(self)
+    }
+}
+
+#[derive(PartialEq,Clone,Default)]
+pub struct CellIdentifierPayload {
+    // message fields
+    pub grid_id: ::std::string::String,
+    pub field_id: ::std::string::String,
+    pub row_id: ::std::string::String,
+    // special fields
+    pub unknown_fields: ::protobuf::UnknownFields,
+    pub cached_size: ::protobuf::CachedSize,
+}
+
+impl<'a> ::std::default::Default for &'a CellIdentifierPayload {
+    fn default() -> &'a CellIdentifierPayload {
+        <CellIdentifierPayload as ::protobuf::Message>::default_instance()
+    }
+}
+
+impl CellIdentifierPayload {
+    pub fn new() -> CellIdentifierPayload {
+        ::std::default::Default::default()
+    }
+
+    // string grid_id = 1;
+
+
+    pub fn get_grid_id(&self) -> &str {
+        &self.grid_id
+    }
+    pub fn clear_grid_id(&mut self) {
+        self.grid_id.clear();
+    }
+
+    // Param is passed by value, moved
+    pub fn set_grid_id(&mut self, v: ::std::string::String) {
+        self.grid_id = v;
+    }
+
+    // Mutable pointer to the field.
+    // If field is not initialized, it is initialized with default value first.
+    pub fn mut_grid_id(&mut self) -> &mut ::std::string::String {
+        &mut self.grid_id
+    }
+
+    // Take field
+    pub fn take_grid_id(&mut self) -> ::std::string::String {
+        ::std::mem::replace(&mut self.grid_id, ::std::string::String::new())
+    }
+
+    // string field_id = 2;
+
+
+    pub fn get_field_id(&self) -> &str {
+        &self.field_id
+    }
+    pub fn clear_field_id(&mut self) {
+        self.field_id.clear();
+    }
+
+    // Param is passed by value, moved
+    pub fn set_field_id(&mut self, v: ::std::string::String) {
+        self.field_id = v;
+    }
+
+    // Mutable pointer to the field.
+    // If field is not initialized, it is initialized with default value first.
+    pub fn mut_field_id(&mut self) -> &mut ::std::string::String {
+        &mut self.field_id
+    }
+
+    // Take field
+    pub fn take_field_id(&mut self) -> ::std::string::String {
+        ::std::mem::replace(&mut self.field_id, ::std::string::String::new())
+    }
+
+    // string row_id = 3;
+
+
+    pub fn get_row_id(&self) -> &str {
+        &self.row_id
+    }
+    pub fn clear_row_id(&mut self) {
+        self.row_id.clear();
+    }
+
+    // Param is passed by value, moved
+    pub fn set_row_id(&mut self, v: ::std::string::String) {
+        self.row_id = v;
+    }
+
+    // Mutable pointer to the field.
+    // If field is not initialized, it is initialized with default value first.
+    pub fn mut_row_id(&mut self) -> &mut ::std::string::String {
+        &mut self.row_id
+    }
+
+    // Take field
+    pub fn take_row_id(&mut self) -> ::std::string::String {
+        ::std::mem::replace(&mut self.row_id, ::std::string::String::new())
+    }
+}
+
+impl ::protobuf::Message for CellIdentifierPayload {
+    fn is_initialized(&self) -> bool {
+        true
+    }
+
+    fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::ProtobufResult<()> {
+        while !is.eof()? {
+            let (field_number, wire_type) = is.read_tag_unpack()?;
+            match field_number {
+                1 => {
+                    ::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.grid_id)?;
+                },
+                2 => {
+                    ::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.field_id)?;
+                },
+                3 => {
+                    ::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.row_id)?;
+                },
+                _ => {
+                    ::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?;
+                },
+            };
+        }
+        ::std::result::Result::Ok(())
+    }
+
+    // Compute sizes of nested messages
+    #[allow(unused_variables)]
+    fn compute_size(&self) -> u32 {
+        let mut my_size = 0;
+        if !self.grid_id.is_empty() {
+            my_size += ::protobuf::rt::string_size(1, &self.grid_id);
+        }
+        if !self.field_id.is_empty() {
+            my_size += ::protobuf::rt::string_size(2, &self.field_id);
+        }
+        if !self.row_id.is_empty() {
+            my_size += ::protobuf::rt::string_size(3, &self.row_id);
+        }
+        my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields());
+        self.cached_size.set(my_size);
+        my_size
+    }
+
+    fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::ProtobufResult<()> {
+        if !self.grid_id.is_empty() {
+            os.write_string(1, &self.grid_id)?;
+        }
+        if !self.field_id.is_empty() {
+            os.write_string(2, &self.field_id)?;
+        }
+        if !self.row_id.is_empty() {
+            os.write_string(3, &self.row_id)?;
+        }
+        os.write_unknown_fields(self.get_unknown_fields())?;
+        ::std::result::Result::Ok(())
+    }
+
+    fn get_cached_size(&self) -> u32 {
+        self.cached_size.get()
+    }
+
+    fn get_unknown_fields(&self) -> &::protobuf::UnknownFields {
+        &self.unknown_fields
+    }
+
+    fn mut_unknown_fields(&mut self) -> &mut ::protobuf::UnknownFields {
+        &mut self.unknown_fields
+    }
+
+    fn as_any(&self) -> &dyn (::std::any::Any) {
+        self as &dyn (::std::any::Any)
+    }
+    fn as_any_mut(&mut self) -> &mut dyn (::std::any::Any) {
+        self as &mut dyn (::std::any::Any)
+    }
+    fn into_any(self: ::std::boxed::Box<Self>) -> ::std::boxed::Box<dyn (::std::any::Any)> {
+        self
+    }
+
+    fn descriptor(&self) -> &'static ::protobuf::reflect::MessageDescriptor {
+        Self::descriptor_static()
+    }
+
+    fn new() -> CellIdentifierPayload {
+        CellIdentifierPayload::new()
+    }
+
+    fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor {
+        static descriptor: ::protobuf::rt::LazyV2<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::LazyV2::INIT;
+        descriptor.get(|| {
+            let mut fields = ::std::vec::Vec::new();
+            fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
+                "grid_id",
+                |m: &CellIdentifierPayload| { &m.grid_id },
+                |m: &mut CellIdentifierPayload| { &mut m.grid_id },
+            ));
+            fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
+                "field_id",
+                |m: &CellIdentifierPayload| { &m.field_id },
+                |m: &mut CellIdentifierPayload| { &mut m.field_id },
+            ));
+            fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
+                "row_id",
+                |m: &CellIdentifierPayload| { &m.row_id },
+                |m: &mut CellIdentifierPayload| { &mut m.row_id },
+            ));
+            ::protobuf::reflect::MessageDescriptor::new_pb_name::<CellIdentifierPayload>(
+                "CellIdentifierPayload",
+                fields,
+                file_descriptor_proto()
+            )
+        })
+    }
+
+    fn default_instance() -> &'static CellIdentifierPayload {
+        static instance: ::protobuf::rt::LazyV2<CellIdentifierPayload> = ::protobuf::rt::LazyV2::INIT;
+        instance.get(CellIdentifierPayload::new)
+    }
+}
+
+impl ::protobuf::Clear for CellIdentifierPayload {
+    fn clear(&mut self) {
+        self.grid_id.clear();
+        self.field_id.clear();
+        self.row_id.clear();
+        self.unknown_fields.clear();
+    }
+}
+
+impl ::std::fmt::Debug for CellIdentifierPayload {
+    fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
+        ::protobuf::text_format::fmt(self, f)
+    }
+}
+
+impl ::protobuf::reflect::ProtobufValue for CellIdentifierPayload {
+    fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
+        ::protobuf::reflect::ReflectValueRef::Message(self)
+    }
+}
+
+#[derive(PartialEq,Clone,Default)]
+pub struct SelectOptionName {
+    // message fields
+    pub name: ::std::string::String,
+    // special fields
+    pub unknown_fields: ::protobuf::UnknownFields,
+    pub cached_size: ::protobuf::CachedSize,
+}
+
+impl<'a> ::std::default::Default for &'a SelectOptionName {
+    fn default() -> &'a SelectOptionName {
+        <SelectOptionName as ::protobuf::Message>::default_instance()
+    }
+}
+
+impl SelectOptionName {
+    pub fn new() -> SelectOptionName {
+        ::std::default::Default::default()
+    }
+
+    // string name = 1;
+
+
+    pub fn get_name(&self) -> &str {
+        &self.name
+    }
+    pub fn clear_name(&mut self) {
+        self.name.clear();
+    }
+
+    // Param is passed by value, moved
+    pub fn set_name(&mut self, v: ::std::string::String) {
+        self.name = v;
+    }
+
+    // Mutable pointer to the field.
+    // If field is not initialized, it is initialized with default value first.
+    pub fn mut_name(&mut self) -> &mut ::std::string::String {
+        &mut self.name
+    }
+
+    // Take field
+    pub fn take_name(&mut self) -> ::std::string::String {
+        ::std::mem::replace(&mut self.name, ::std::string::String::new())
+    }
+}
+
+impl ::protobuf::Message for SelectOptionName {
+    fn is_initialized(&self) -> bool {
+        true
+    }
+
+    fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::ProtobufResult<()> {
+        while !is.eof()? {
+            let (field_number, wire_type) = is.read_tag_unpack()?;
+            match field_number {
+                1 => {
+                    ::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.name)?;
+                },
+                _ => {
+                    ::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?;
+                },
+            };
+        }
+        ::std::result::Result::Ok(())
+    }
+
+    // Compute sizes of nested messages
+    #[allow(unused_variables)]
+    fn compute_size(&self) -> u32 {
+        let mut my_size = 0;
+        if !self.name.is_empty() {
+            my_size += ::protobuf::rt::string_size(1, &self.name);
+        }
+        my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields());
+        self.cached_size.set(my_size);
+        my_size
+    }
+
+    fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::ProtobufResult<()> {
+        if !self.name.is_empty() {
+            os.write_string(1, &self.name)?;
+        }
+        os.write_unknown_fields(self.get_unknown_fields())?;
+        ::std::result::Result::Ok(())
+    }
+
+    fn get_cached_size(&self) -> u32 {
+        self.cached_size.get()
+    }
+
+    fn get_unknown_fields(&self) -> &::protobuf::UnknownFields {
+        &self.unknown_fields
+    }
+
+    fn mut_unknown_fields(&mut self) -> &mut ::protobuf::UnknownFields {
+        &mut self.unknown_fields
+    }
+
+    fn as_any(&self) -> &dyn (::std::any::Any) {
+        self as &dyn (::std::any::Any)
+    }
+    fn as_any_mut(&mut self) -> &mut dyn (::std::any::Any) {
+        self as &mut dyn (::std::any::Any)
+    }
+    fn into_any(self: ::std::boxed::Box<Self>) -> ::std::boxed::Box<dyn (::std::any::Any)> {
+        self
+    }
+
+    fn descriptor(&self) -> &'static ::protobuf::reflect::MessageDescriptor {
+        Self::descriptor_static()
+    }
+
+    fn new() -> SelectOptionName {
+        SelectOptionName::new()
+    }
+
+    fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor {
+        static descriptor: ::protobuf::rt::LazyV2<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::LazyV2::INIT;
+        descriptor.get(|| {
+            let mut fields = ::std::vec::Vec::new();
+            fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
+                "name",
+                |m: &SelectOptionName| { &m.name },
+                |m: &mut SelectOptionName| { &mut m.name },
+            ));
+            ::protobuf::reflect::MessageDescriptor::new_pb_name::<SelectOptionName>(
+                "SelectOptionName",
+                fields,
+                file_descriptor_proto()
+            )
+        })
+    }
+
+    fn default_instance() -> &'static SelectOptionName {
+        static instance: ::protobuf::rt::LazyV2<SelectOptionName> = ::protobuf::rt::LazyV2::INIT;
+        instance.get(SelectOptionName::new)
+    }
+}
+
+impl ::protobuf::Clear for SelectOptionName {
+    fn clear(&mut self) {
+        self.name.clear();
+        self.unknown_fields.clear();
+    }
+}
+
+impl ::std::fmt::Debug for SelectOptionName {
+    fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
+        ::protobuf::text_format::fmt(self, f)
+    }
+}
+
+impl ::protobuf::reflect::ProtobufValue for SelectOptionName {
+    fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
+        ::protobuf::reflect::ReflectValueRef::Message(self)
+    }
+}
+
+static file_descriptor_proto_data: &'static [u8] = b"\
+    \n\x13cell_entities.proto\"}\n\x19CreateSelectOptionPayload\x12?\n\x0fce\
+    ll_identifier\x18\x01\x20\x01(\x0b2\x16.CellIdentifierPayloadR\x0ecellId\
+    entifier\x12\x1f\n\x0boption_name\x18\x02\x20\x01(\tR\noptionName\"b\n\
+    \x15CellIdentifierPayload\x12\x17\n\x07grid_id\x18\x01\x20\x01(\tR\x06gr\
+    idId\x12\x19\n\x08field_id\x18\x02\x20\x01(\tR\x07fieldId\x12\x15\n\x06r\
+    ow_id\x18\x03\x20\x01(\tR\x05rowId\"&\n\x10SelectOptionName\x12\x12\n\
+    \x04name\x18\x01\x20\x01(\tR\x04nameb\x06proto3\
+";
+
+static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;
+
+fn parse_descriptor_proto() -> ::protobuf::descriptor::FileDescriptorProto {
+    ::protobuf::Message::parse_from_bytes(file_descriptor_proto_data).unwrap()
+}
+
+pub fn file_descriptor_proto() -> &'static ::protobuf::descriptor::FileDescriptorProto {
+    file_descriptor_proto_lazy.get(|| {
+        parse_descriptor_proto()
+    })
+}

+ 6 - 6
frontend/rust-lib/flowy-grid/src/protobuf/model/dart_notification.rs

@@ -30,7 +30,7 @@ pub enum GridNotification {
     DidUpdateBlock = 20,
     DidUpdateRow = 30,
     DidUpdateCell = 31,
-    DidUpdateFields = 40,
+    DidUpdateGrid = 40,
     DidUpdateField = 41,
 }
 
@@ -46,7 +46,7 @@ impl ::protobuf::ProtobufEnum for GridNotification {
             20 => ::std::option::Option::Some(GridNotification::DidUpdateBlock),
             30 => ::std::option::Option::Some(GridNotification::DidUpdateRow),
             31 => ::std::option::Option::Some(GridNotification::DidUpdateCell),
-            40 => ::std::option::Option::Some(GridNotification::DidUpdateFields),
+            40 => ::std::option::Option::Some(GridNotification::DidUpdateGrid),
             41 => ::std::option::Option::Some(GridNotification::DidUpdateField),
             _ => ::std::option::Option::None
         }
@@ -59,7 +59,7 @@ impl ::protobuf::ProtobufEnum for GridNotification {
             GridNotification::DidUpdateBlock,
             GridNotification::DidUpdateRow,
             GridNotification::DidUpdateCell,
-            GridNotification::DidUpdateFields,
+            GridNotification::DidUpdateGrid,
             GridNotification::DidUpdateField,
         ];
         values
@@ -89,11 +89,11 @@ impl ::protobuf::reflect::ProtobufValue for GridNotification {
 }
 
 static file_descriptor_proto_data: &'static [u8] = b"\
-    \n\x17dart_notification.proto*\x95\x01\n\x10GridNotification\x12\x0b\n\
+    \n\x17dart_notification.proto*\x93\x01\n\x10GridNotification\x12\x0b\n\
     \x07Unknown\x10\0\x12\x12\n\x0eDidCreateBlock\x10\x0b\x12\x12\n\x0eDidUp\
     dateBlock\x10\x14\x12\x10\n\x0cDidUpdateRow\x10\x1e\x12\x11\n\rDidUpdate\
-    Cell\x10\x1f\x12\x13\n\x0fDidUpdateFields\x10(\x12\x12\n\x0eDidUpdateFie\
-    ld\x10)b\x06proto3\
+    Cell\x10\x1f\x12\x11\n\rDidUpdateGrid\x10(\x12\x12\n\x0eDidUpdateField\
+    \x10)b\x06proto3\
 ";
 
 static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;

+ 18 - 14
frontend/rust-lib/flowy-grid/src/protobuf/model/event_map.rs

@@ -34,12 +34,13 @@ pub enum GridEvent {
     SwitchToField = 14,
     DuplicateField = 15,
     GetEditFieldContext = 16,
-    CreateSelectOption = 30,
-    GetSelectOptions = 31,
+    NewSelectOption = 30,
+    GetSelectOptionContext = 31,
+    ApplySelectOptionChangeset = 32,
     CreateRow = 50,
     GetRow = 51,
     UpdateCell = 70,
-    ApplySelectOptionChangeset = 71,
+    ApplySelectOptionCellChangeset = 71,
 }
 
 impl ::protobuf::ProtobufEnum for GridEvent {
@@ -58,12 +59,13 @@ impl ::protobuf::ProtobufEnum for GridEvent {
             14 => ::std::option::Option::Some(GridEvent::SwitchToField),
             15 => ::std::option::Option::Some(GridEvent::DuplicateField),
             16 => ::std::option::Option::Some(GridEvent::GetEditFieldContext),
-            30 => ::std::option::Option::Some(GridEvent::CreateSelectOption),
-            31 => ::std::option::Option::Some(GridEvent::GetSelectOptions),
+            30 => ::std::option::Option::Some(GridEvent::NewSelectOption),
+            31 => ::std::option::Option::Some(GridEvent::GetSelectOptionContext),
+            32 => ::std::option::Option::Some(GridEvent::ApplySelectOptionChangeset),
             50 => ::std::option::Option::Some(GridEvent::CreateRow),
             51 => ::std::option::Option::Some(GridEvent::GetRow),
             70 => ::std::option::Option::Some(GridEvent::UpdateCell),
-            71 => ::std::option::Option::Some(GridEvent::ApplySelectOptionChangeset),
+            71 => ::std::option::Option::Some(GridEvent::ApplySelectOptionCellChangeset),
             _ => ::std::option::Option::None
         }
     }
@@ -79,12 +81,13 @@ impl ::protobuf::ProtobufEnum for GridEvent {
             GridEvent::SwitchToField,
             GridEvent::DuplicateField,
             GridEvent::GetEditFieldContext,
-            GridEvent::CreateSelectOption,
-            GridEvent::GetSelectOptions,
+            GridEvent::NewSelectOption,
+            GridEvent::GetSelectOptionContext,
+            GridEvent::ApplySelectOptionChangeset,
             GridEvent::CreateRow,
             GridEvent::GetRow,
             GridEvent::UpdateCell,
-            GridEvent::ApplySelectOptionChangeset,
+            GridEvent::ApplySelectOptionCellChangeset,
         ];
         values
     }
@@ -113,14 +116,15 @@ impl ::protobuf::reflect::ProtobufValue for GridEvent {
 }
 
 static file_descriptor_proto_data: &'static [u8] = b"\
-    \n\x0fevent_map.proto*\xaa\x02\n\tGridEvent\x12\x0f\n\x0bGetGridData\x10\
+    \n\x0fevent_map.proto*\xd1\x02\n\tGridEvent\x12\x0f\n\x0bGetGridData\x10\
     \0\x12\x11\n\rGetGridBlocks\x10\x01\x12\r\n\tGetFields\x10\n\x12\x0f\n\
     \x0bUpdateField\x10\x0b\x12\x0f\n\x0bCreateField\x10\x0c\x12\x0f\n\x0bDe\
     leteField\x10\r\x12\x11\n\rSwitchToField\x10\x0e\x12\x12\n\x0eDuplicateF\
-    ield\x10\x0f\x12\x17\n\x13GetEditFieldContext\x10\x10\x12\x16\n\x12Creat\
-    eSelectOption\x10\x1e\x12\x14\n\x10GetSelectOptions\x10\x1f\x12\r\n\tCre\
-    ateRow\x102\x12\n\n\x06GetRow\x103\x12\x0e\n\nUpdateCell\x10F\x12\x1e\n\
-    \x1aApplySelectOptionChangeset\x10Gb\x06proto3\
+    ield\x10\x0f\x12\x17\n\x13GetEditFieldContext\x10\x10\x12\x13\n\x0fNewSe\
+    lectOption\x10\x1e\x12\x1a\n\x16GetSelectOptionContext\x10\x1f\x12\x1e\n\
+    \x1aApplySelectOptionChangeset\x10\x20\x12\r\n\tCreateRow\x102\x12\n\n\
+    \x06GetRow\x103\x12\x0e\n\nUpdateCell\x10F\x12\"\n\x1eApplySelectOptionC\
+    ellChangeset\x10Gb\x06proto3\
 ";
 
 static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;

+ 243 - 0
frontend/rust-lib/flowy-grid/src/protobuf/model/field_entities.rs

@@ -0,0 +1,243 @@
+// This file is generated by rust-protobuf 2.25.2. Do not edit
+// @generated
+
+// https://github.com/rust-lang/rust-clippy/issues/702
+#![allow(unknown_lints)]
+#![allow(clippy::all)]
+
+#![allow(unused_attributes)]
+#![cfg_attr(rustfmt, rustfmt::skip)]
+
+#![allow(box_pointers)]
+#![allow(dead_code)]
+#![allow(missing_docs)]
+#![allow(non_camel_case_types)]
+#![allow(non_snake_case)]
+#![allow(non_upper_case_globals)]
+#![allow(trivial_casts)]
+#![allow(unused_imports)]
+#![allow(unused_results)]
+//! Generated file from `field_entities.proto`
+
+/// Generated files are compatible only with the same version
+/// of protobuf runtime.
+// const _PROTOBUF_VERSION_CHECK: () = ::protobuf::VERSION_2_25_2;
+
+#[derive(PartialEq,Clone,Default)]
+pub struct FieldIdentifierPayload {
+    // message fields
+    pub field_id: ::std::string::String,
+    pub grid_id: ::std::string::String,
+    // special fields
+    pub unknown_fields: ::protobuf::UnknownFields,
+    pub cached_size: ::protobuf::CachedSize,
+}
+
+impl<'a> ::std::default::Default for &'a FieldIdentifierPayload {
+    fn default() -> &'a FieldIdentifierPayload {
+        <FieldIdentifierPayload as ::protobuf::Message>::default_instance()
+    }
+}
+
+impl FieldIdentifierPayload {
+    pub fn new() -> FieldIdentifierPayload {
+        ::std::default::Default::default()
+    }
+
+    // string field_id = 1;
+
+
+    pub fn get_field_id(&self) -> &str {
+        &self.field_id
+    }
+    pub fn clear_field_id(&mut self) {
+        self.field_id.clear();
+    }
+
+    // Param is passed by value, moved
+    pub fn set_field_id(&mut self, v: ::std::string::String) {
+        self.field_id = v;
+    }
+
+    // Mutable pointer to the field.
+    // If field is not initialized, it is initialized with default value first.
+    pub fn mut_field_id(&mut self) -> &mut ::std::string::String {
+        &mut self.field_id
+    }
+
+    // Take field
+    pub fn take_field_id(&mut self) -> ::std::string::String {
+        ::std::mem::replace(&mut self.field_id, ::std::string::String::new())
+    }
+
+    // string grid_id = 2;
+
+
+    pub fn get_grid_id(&self) -> &str {
+        &self.grid_id
+    }
+    pub fn clear_grid_id(&mut self) {
+        self.grid_id.clear();
+    }
+
+    // Param is passed by value, moved
+    pub fn set_grid_id(&mut self, v: ::std::string::String) {
+        self.grid_id = v;
+    }
+
+    // Mutable pointer to the field.
+    // If field is not initialized, it is initialized with default value first.
+    pub fn mut_grid_id(&mut self) -> &mut ::std::string::String {
+        &mut self.grid_id
+    }
+
+    // Take field
+    pub fn take_grid_id(&mut self) -> ::std::string::String {
+        ::std::mem::replace(&mut self.grid_id, ::std::string::String::new())
+    }
+}
+
+impl ::protobuf::Message for FieldIdentifierPayload {
+    fn is_initialized(&self) -> bool {
+        true
+    }
+
+    fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::ProtobufResult<()> {
+        while !is.eof()? {
+            let (field_number, wire_type) = is.read_tag_unpack()?;
+            match field_number {
+                1 => {
+                    ::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.field_id)?;
+                },
+                2 => {
+                    ::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.grid_id)?;
+                },
+                _ => {
+                    ::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?;
+                },
+            };
+        }
+        ::std::result::Result::Ok(())
+    }
+
+    // Compute sizes of nested messages
+    #[allow(unused_variables)]
+    fn compute_size(&self) -> u32 {
+        let mut my_size = 0;
+        if !self.field_id.is_empty() {
+            my_size += ::protobuf::rt::string_size(1, &self.field_id);
+        }
+        if !self.grid_id.is_empty() {
+            my_size += ::protobuf::rt::string_size(2, &self.grid_id);
+        }
+        my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields());
+        self.cached_size.set(my_size);
+        my_size
+    }
+
+    fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::ProtobufResult<()> {
+        if !self.field_id.is_empty() {
+            os.write_string(1, &self.field_id)?;
+        }
+        if !self.grid_id.is_empty() {
+            os.write_string(2, &self.grid_id)?;
+        }
+        os.write_unknown_fields(self.get_unknown_fields())?;
+        ::std::result::Result::Ok(())
+    }
+
+    fn get_cached_size(&self) -> u32 {
+        self.cached_size.get()
+    }
+
+    fn get_unknown_fields(&self) -> &::protobuf::UnknownFields {
+        &self.unknown_fields
+    }
+
+    fn mut_unknown_fields(&mut self) -> &mut ::protobuf::UnknownFields {
+        &mut self.unknown_fields
+    }
+
+    fn as_any(&self) -> &dyn (::std::any::Any) {
+        self as &dyn (::std::any::Any)
+    }
+    fn as_any_mut(&mut self) -> &mut dyn (::std::any::Any) {
+        self as &mut dyn (::std::any::Any)
+    }
+    fn into_any(self: ::std::boxed::Box<Self>) -> ::std::boxed::Box<dyn (::std::any::Any)> {
+        self
+    }
+
+    fn descriptor(&self) -> &'static ::protobuf::reflect::MessageDescriptor {
+        Self::descriptor_static()
+    }
+
+    fn new() -> FieldIdentifierPayload {
+        FieldIdentifierPayload::new()
+    }
+
+    fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor {
+        static descriptor: ::protobuf::rt::LazyV2<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::LazyV2::INIT;
+        descriptor.get(|| {
+            let mut fields = ::std::vec::Vec::new();
+            fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
+                "field_id",
+                |m: &FieldIdentifierPayload| { &m.field_id },
+                |m: &mut FieldIdentifierPayload| { &mut m.field_id },
+            ));
+            fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
+                "grid_id",
+                |m: &FieldIdentifierPayload| { &m.grid_id },
+                |m: &mut FieldIdentifierPayload| { &mut m.grid_id },
+            ));
+            ::protobuf::reflect::MessageDescriptor::new_pb_name::<FieldIdentifierPayload>(
+                "FieldIdentifierPayload",
+                fields,
+                file_descriptor_proto()
+            )
+        })
+    }
+
+    fn default_instance() -> &'static FieldIdentifierPayload {
+        static instance: ::protobuf::rt::LazyV2<FieldIdentifierPayload> = ::protobuf::rt::LazyV2::INIT;
+        instance.get(FieldIdentifierPayload::new)
+    }
+}
+
+impl ::protobuf::Clear for FieldIdentifierPayload {
+    fn clear(&mut self) {
+        self.field_id.clear();
+        self.grid_id.clear();
+        self.unknown_fields.clear();
+    }
+}
+
+impl ::std::fmt::Debug for FieldIdentifierPayload {
+    fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
+        ::protobuf::text_format::fmt(self, f)
+    }
+}
+
+impl ::protobuf::reflect::ProtobufValue for FieldIdentifierPayload {
+    fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
+        ::protobuf::reflect::ReflectValueRef::Message(self)
+    }
+}
+
+static file_descriptor_proto_data: &'static [u8] = b"\
+    \n\x14field_entities.proto\"L\n\x16FieldIdentifierPayload\x12\x19\n\x08f\
+    ield_id\x18\x01\x20\x01(\tR\x07fieldId\x12\x17\n\x07grid_id\x18\x02\x20\
+    \x01(\tR\x06gridIdb\x06proto3\
+";
+
+static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;
+
+fn parse_descriptor_proto() -> ::protobuf::descriptor::FileDescriptorProto {
+    ::protobuf::Message::parse_from_bytes(file_descriptor_proto_data).unwrap()
+}
+
+pub fn file_descriptor_proto() -> &'static ::protobuf::descriptor::FileDescriptorProto {
+    file_descriptor_proto_lazy.get(|| {
+        parse_descriptor_proto()
+    })
+}

+ 9 - 0
frontend/rust-lib/flowy-grid/src/protobuf/model/mod.rs

@@ -1,6 +1,9 @@
 #![cfg_attr(rustfmt, rustfmt::skip)]
 // Auto-generated, do not edit
 
+mod field_entities;
+pub use field_entities::*;
+
 mod number_type_option;
 pub use number_type_option::*;
 
@@ -10,6 +13,12 @@ pub use dart_notification::*;
 mod selection_type_option;
 pub use selection_type_option::*;
 
+mod row_entities;
+pub use row_entities::*;
+
+mod cell_entities;
+pub use cell_entities::*;
+
 mod checkbox_type_option;
 pub use checkbox_type_option::*;
 

+ 243 - 0
frontend/rust-lib/flowy-grid/src/protobuf/model/row_entities.rs

@@ -0,0 +1,243 @@
+// This file is generated by rust-protobuf 2.25.2. Do not edit
+// @generated
+
+// https://github.com/rust-lang/rust-clippy/issues/702
+#![allow(unknown_lints)]
+#![allow(clippy::all)]
+
+#![allow(unused_attributes)]
+#![cfg_attr(rustfmt, rustfmt::skip)]
+
+#![allow(box_pointers)]
+#![allow(dead_code)]
+#![allow(missing_docs)]
+#![allow(non_camel_case_types)]
+#![allow(non_snake_case)]
+#![allow(non_upper_case_globals)]
+#![allow(trivial_casts)]
+#![allow(unused_imports)]
+#![allow(unused_results)]
+//! Generated file from `row_entities.proto`
+
+/// Generated files are compatible only with the same version
+/// of protobuf runtime.
+// const _PROTOBUF_VERSION_CHECK: () = ::protobuf::VERSION_2_25_2;
+
+#[derive(PartialEq,Clone,Default)]
+pub struct RowIdentifierPayload {
+    // message fields
+    pub grid_id: ::std::string::String,
+    pub row_id: ::std::string::String,
+    // special fields
+    pub unknown_fields: ::protobuf::UnknownFields,
+    pub cached_size: ::protobuf::CachedSize,
+}
+
+impl<'a> ::std::default::Default for &'a RowIdentifierPayload {
+    fn default() -> &'a RowIdentifierPayload {
+        <RowIdentifierPayload as ::protobuf::Message>::default_instance()
+    }
+}
+
+impl RowIdentifierPayload {
+    pub fn new() -> RowIdentifierPayload {
+        ::std::default::Default::default()
+    }
+
+    // string grid_id = 1;
+
+
+    pub fn get_grid_id(&self) -> &str {
+        &self.grid_id
+    }
+    pub fn clear_grid_id(&mut self) {
+        self.grid_id.clear();
+    }
+
+    // Param is passed by value, moved
+    pub fn set_grid_id(&mut self, v: ::std::string::String) {
+        self.grid_id = v;
+    }
+
+    // Mutable pointer to the field.
+    // If field is not initialized, it is initialized with default value first.
+    pub fn mut_grid_id(&mut self) -> &mut ::std::string::String {
+        &mut self.grid_id
+    }
+
+    // Take field
+    pub fn take_grid_id(&mut self) -> ::std::string::String {
+        ::std::mem::replace(&mut self.grid_id, ::std::string::String::new())
+    }
+
+    // string row_id = 3;
+
+
+    pub fn get_row_id(&self) -> &str {
+        &self.row_id
+    }
+    pub fn clear_row_id(&mut self) {
+        self.row_id.clear();
+    }
+
+    // Param is passed by value, moved
+    pub fn set_row_id(&mut self, v: ::std::string::String) {
+        self.row_id = v;
+    }
+
+    // Mutable pointer to the field.
+    // If field is not initialized, it is initialized with default value first.
+    pub fn mut_row_id(&mut self) -> &mut ::std::string::String {
+        &mut self.row_id
+    }
+
+    // Take field
+    pub fn take_row_id(&mut self) -> ::std::string::String {
+        ::std::mem::replace(&mut self.row_id, ::std::string::String::new())
+    }
+}
+
+impl ::protobuf::Message for RowIdentifierPayload {
+    fn is_initialized(&self) -> bool {
+        true
+    }
+
+    fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::ProtobufResult<()> {
+        while !is.eof()? {
+            let (field_number, wire_type) = is.read_tag_unpack()?;
+            match field_number {
+                1 => {
+                    ::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.grid_id)?;
+                },
+                3 => {
+                    ::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.row_id)?;
+                },
+                _ => {
+                    ::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?;
+                },
+            };
+        }
+        ::std::result::Result::Ok(())
+    }
+
+    // Compute sizes of nested messages
+    #[allow(unused_variables)]
+    fn compute_size(&self) -> u32 {
+        let mut my_size = 0;
+        if !self.grid_id.is_empty() {
+            my_size += ::protobuf::rt::string_size(1, &self.grid_id);
+        }
+        if !self.row_id.is_empty() {
+            my_size += ::protobuf::rt::string_size(3, &self.row_id);
+        }
+        my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields());
+        self.cached_size.set(my_size);
+        my_size
+    }
+
+    fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::ProtobufResult<()> {
+        if !self.grid_id.is_empty() {
+            os.write_string(1, &self.grid_id)?;
+        }
+        if !self.row_id.is_empty() {
+            os.write_string(3, &self.row_id)?;
+        }
+        os.write_unknown_fields(self.get_unknown_fields())?;
+        ::std::result::Result::Ok(())
+    }
+
+    fn get_cached_size(&self) -> u32 {
+        self.cached_size.get()
+    }
+
+    fn get_unknown_fields(&self) -> &::protobuf::UnknownFields {
+        &self.unknown_fields
+    }
+
+    fn mut_unknown_fields(&mut self) -> &mut ::protobuf::UnknownFields {
+        &mut self.unknown_fields
+    }
+
+    fn as_any(&self) -> &dyn (::std::any::Any) {
+        self as &dyn (::std::any::Any)
+    }
+    fn as_any_mut(&mut self) -> &mut dyn (::std::any::Any) {
+        self as &mut dyn (::std::any::Any)
+    }
+    fn into_any(self: ::std::boxed::Box<Self>) -> ::std::boxed::Box<dyn (::std::any::Any)> {
+        self
+    }
+
+    fn descriptor(&self) -> &'static ::protobuf::reflect::MessageDescriptor {
+        Self::descriptor_static()
+    }
+
+    fn new() -> RowIdentifierPayload {
+        RowIdentifierPayload::new()
+    }
+
+    fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor {
+        static descriptor: ::protobuf::rt::LazyV2<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::LazyV2::INIT;
+        descriptor.get(|| {
+            let mut fields = ::std::vec::Vec::new();
+            fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
+                "grid_id",
+                |m: &RowIdentifierPayload| { &m.grid_id },
+                |m: &mut RowIdentifierPayload| { &mut m.grid_id },
+            ));
+            fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
+                "row_id",
+                |m: &RowIdentifierPayload| { &m.row_id },
+                |m: &mut RowIdentifierPayload| { &mut m.row_id },
+            ));
+            ::protobuf::reflect::MessageDescriptor::new_pb_name::<RowIdentifierPayload>(
+                "RowIdentifierPayload",
+                fields,
+                file_descriptor_proto()
+            )
+        })
+    }
+
+    fn default_instance() -> &'static RowIdentifierPayload {
+        static instance: ::protobuf::rt::LazyV2<RowIdentifierPayload> = ::protobuf::rt::LazyV2::INIT;
+        instance.get(RowIdentifierPayload::new)
+    }
+}
+
+impl ::protobuf::Clear for RowIdentifierPayload {
+    fn clear(&mut self) {
+        self.grid_id.clear();
+        self.row_id.clear();
+        self.unknown_fields.clear();
+    }
+}
+
+impl ::std::fmt::Debug for RowIdentifierPayload {
+    fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
+        ::protobuf::text_format::fmt(self, f)
+    }
+}
+
+impl ::protobuf::reflect::ProtobufValue for RowIdentifierPayload {
+    fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
+        ::protobuf::reflect::ReflectValueRef::Message(self)
+    }
+}
+
+static file_descriptor_proto_data: &'static [u8] = b"\
+    \n\x12row_entities.proto\"F\n\x14RowIdentifierPayload\x12\x17\n\x07grid_\
+    id\x18\x01\x20\x01(\tR\x06gridId\x12\x15\n\x06row_id\x18\x03\x20\x01(\tR\
+    \x05rowIdb\x06proto3\
+";
+
+static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;
+
+fn parse_descriptor_proto() -> ::protobuf::descriptor::FileDescriptorProto {
+    ::protobuf::Message::parse_from_bytes(file_descriptor_proto_data).unwrap()
+}
+
+pub fn file_descriptor_proto() -> &'static ::protobuf::descriptor::FileDescriptorProto {
+    file_descriptor_proto_lazy.get(|| {
+        parse_descriptor_proto()
+    })
+}

+ 428 - 70
frontend/rust-lib/flowy-grid/src/protobuf/model/selection_type_option.rs

@@ -660,12 +660,10 @@ impl ::protobuf::reflect::ProtobufValue for SelectOption {
 #[derive(PartialEq,Clone,Default)]
 pub struct SelectOptionChangesetPayload {
     // message fields
-    pub grid_id: ::std::string::String,
-    pub row_id: ::std::string::String,
-    pub field_id: ::std::string::String,
+    pub cell_identifier: ::protobuf::SingularPtrField<super::cell_entities::CellIdentifierPayload>,
     // message oneof groups
-    pub one_of_insert_option_id: ::std::option::Option<SelectOptionChangesetPayload_oneof_one_of_insert_option_id>,
-    pub one_of_delete_option_id: ::std::option::Option<SelectOptionChangesetPayload_oneof_one_of_delete_option_id>,
+    pub one_of_insert_option: ::std::option::Option<SelectOptionChangesetPayload_oneof_one_of_insert_option>,
+    pub one_of_delete_option: ::std::option::Option<SelectOptionChangesetPayload_oneof_one_of_delete_option>,
     // special fields
     pub unknown_fields: ::protobuf::UnknownFields,
     pub cached_size: ::protobuf::CachedSize,
@@ -678,13 +676,13 @@ impl<'a> ::std::default::Default for &'a SelectOptionChangesetPayload {
 }
 
 #[derive(Clone,PartialEq,Debug)]
-pub enum SelectOptionChangesetPayload_oneof_one_of_insert_option_id {
-    insert_option_id(::std::string::String),
+pub enum SelectOptionChangesetPayload_oneof_one_of_insert_option {
+    insert_option(SelectOption),
 }
 
 #[derive(Clone,PartialEq,Debug)]
-pub enum SelectOptionChangesetPayload_oneof_one_of_delete_option_id {
-    delete_option_id(::std::string::String),
+pub enum SelectOptionChangesetPayload_oneof_one_of_delete_option {
+    delete_option(SelectOption),
 }
 
 impl SelectOptionChangesetPayload {
@@ -692,6 +690,361 @@ impl SelectOptionChangesetPayload {
         ::std::default::Default::default()
     }
 
+    // .CellIdentifierPayload cell_identifier = 1;
+
+
+    pub fn get_cell_identifier(&self) -> &super::cell_entities::CellIdentifierPayload {
+        self.cell_identifier.as_ref().unwrap_or_else(|| <super::cell_entities::CellIdentifierPayload as ::protobuf::Message>::default_instance())
+    }
+    pub fn clear_cell_identifier(&mut self) {
+        self.cell_identifier.clear();
+    }
+
+    pub fn has_cell_identifier(&self) -> bool {
+        self.cell_identifier.is_some()
+    }
+
+    // Param is passed by value, moved
+    pub fn set_cell_identifier(&mut self, v: super::cell_entities::CellIdentifierPayload) {
+        self.cell_identifier = ::protobuf::SingularPtrField::some(v);
+    }
+
+    // Mutable pointer to the field.
+    // If field is not initialized, it is initialized with default value first.
+    pub fn mut_cell_identifier(&mut self) -> &mut super::cell_entities::CellIdentifierPayload {
+        if self.cell_identifier.is_none() {
+            self.cell_identifier.set_default();
+        }
+        self.cell_identifier.as_mut().unwrap()
+    }
+
+    // Take field
+    pub fn take_cell_identifier(&mut self) -> super::cell_entities::CellIdentifierPayload {
+        self.cell_identifier.take().unwrap_or_else(|| super::cell_entities::CellIdentifierPayload::new())
+    }
+
+    // .SelectOption insert_option = 2;
+
+
+    pub fn get_insert_option(&self) -> &SelectOption {
+        match self.one_of_insert_option {
+            ::std::option::Option::Some(SelectOptionChangesetPayload_oneof_one_of_insert_option::insert_option(ref v)) => v,
+            _ => <SelectOption as ::protobuf::Message>::default_instance(),
+        }
+    }
+    pub fn clear_insert_option(&mut self) {
+        self.one_of_insert_option = ::std::option::Option::None;
+    }
+
+    pub fn has_insert_option(&self) -> bool {
+        match self.one_of_insert_option {
+            ::std::option::Option::Some(SelectOptionChangesetPayload_oneof_one_of_insert_option::insert_option(..)) => true,
+            _ => false,
+        }
+    }
+
+    // Param is passed by value, moved
+    pub fn set_insert_option(&mut self, v: SelectOption) {
+        self.one_of_insert_option = ::std::option::Option::Some(SelectOptionChangesetPayload_oneof_one_of_insert_option::insert_option(v))
+    }
+
+    // Mutable pointer to the field.
+    pub fn mut_insert_option(&mut self) -> &mut SelectOption {
+        if let ::std::option::Option::Some(SelectOptionChangesetPayload_oneof_one_of_insert_option::insert_option(_)) = self.one_of_insert_option {
+        } else {
+            self.one_of_insert_option = ::std::option::Option::Some(SelectOptionChangesetPayload_oneof_one_of_insert_option::insert_option(SelectOption::new()));
+        }
+        match self.one_of_insert_option {
+            ::std::option::Option::Some(SelectOptionChangesetPayload_oneof_one_of_insert_option::insert_option(ref mut v)) => v,
+            _ => panic!(),
+        }
+    }
+
+    // Take field
+    pub fn take_insert_option(&mut self) -> SelectOption {
+        if self.has_insert_option() {
+            match self.one_of_insert_option.take() {
+                ::std::option::Option::Some(SelectOptionChangesetPayload_oneof_one_of_insert_option::insert_option(v)) => v,
+                _ => panic!(),
+            }
+        } else {
+            SelectOption::new()
+        }
+    }
+
+    // .SelectOption delete_option = 3;
+
+
+    pub fn get_delete_option(&self) -> &SelectOption {
+        match self.one_of_delete_option {
+            ::std::option::Option::Some(SelectOptionChangesetPayload_oneof_one_of_delete_option::delete_option(ref v)) => v,
+            _ => <SelectOption as ::protobuf::Message>::default_instance(),
+        }
+    }
+    pub fn clear_delete_option(&mut self) {
+        self.one_of_delete_option = ::std::option::Option::None;
+    }
+
+    pub fn has_delete_option(&self) -> bool {
+        match self.one_of_delete_option {
+            ::std::option::Option::Some(SelectOptionChangesetPayload_oneof_one_of_delete_option::delete_option(..)) => true,
+            _ => false,
+        }
+    }
+
+    // Param is passed by value, moved
+    pub fn set_delete_option(&mut self, v: SelectOption) {
+        self.one_of_delete_option = ::std::option::Option::Some(SelectOptionChangesetPayload_oneof_one_of_delete_option::delete_option(v))
+    }
+
+    // Mutable pointer to the field.
+    pub fn mut_delete_option(&mut self) -> &mut SelectOption {
+        if let ::std::option::Option::Some(SelectOptionChangesetPayload_oneof_one_of_delete_option::delete_option(_)) = self.one_of_delete_option {
+        } else {
+            self.one_of_delete_option = ::std::option::Option::Some(SelectOptionChangesetPayload_oneof_one_of_delete_option::delete_option(SelectOption::new()));
+        }
+        match self.one_of_delete_option {
+            ::std::option::Option::Some(SelectOptionChangesetPayload_oneof_one_of_delete_option::delete_option(ref mut v)) => v,
+            _ => panic!(),
+        }
+    }
+
+    // Take field
+    pub fn take_delete_option(&mut self) -> SelectOption {
+        if self.has_delete_option() {
+            match self.one_of_delete_option.take() {
+                ::std::option::Option::Some(SelectOptionChangesetPayload_oneof_one_of_delete_option::delete_option(v)) => v,
+                _ => panic!(),
+            }
+        } else {
+            SelectOption::new()
+        }
+    }
+}
+
+impl ::protobuf::Message for SelectOptionChangesetPayload {
+    fn is_initialized(&self) -> bool {
+        for v in &self.cell_identifier {
+            if !v.is_initialized() {
+                return false;
+            }
+        };
+        if let Some(SelectOptionChangesetPayload_oneof_one_of_insert_option::insert_option(ref v)) = self.one_of_insert_option {
+            if !v.is_initialized() {
+                return false;
+            }
+        }
+        if let Some(SelectOptionChangesetPayload_oneof_one_of_delete_option::delete_option(ref v)) = self.one_of_delete_option {
+            if !v.is_initialized() {
+                return false;
+            }
+        }
+        true
+    }
+
+    fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::ProtobufResult<()> {
+        while !is.eof()? {
+            let (field_number, wire_type) = is.read_tag_unpack()?;
+            match field_number {
+                1 => {
+                    ::protobuf::rt::read_singular_message_into(wire_type, is, &mut self.cell_identifier)?;
+                },
+                2 => {
+                    if wire_type != ::protobuf::wire_format::WireTypeLengthDelimited {
+                        return ::std::result::Result::Err(::protobuf::rt::unexpected_wire_type(wire_type));
+                    }
+                    self.one_of_insert_option = ::std::option::Option::Some(SelectOptionChangesetPayload_oneof_one_of_insert_option::insert_option(is.read_message()?));
+                },
+                3 => {
+                    if wire_type != ::protobuf::wire_format::WireTypeLengthDelimited {
+                        return ::std::result::Result::Err(::protobuf::rt::unexpected_wire_type(wire_type));
+                    }
+                    self.one_of_delete_option = ::std::option::Option::Some(SelectOptionChangesetPayload_oneof_one_of_delete_option::delete_option(is.read_message()?));
+                },
+                _ => {
+                    ::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?;
+                },
+            };
+        }
+        ::std::result::Result::Ok(())
+    }
+
+    // Compute sizes of nested messages
+    #[allow(unused_variables)]
+    fn compute_size(&self) -> u32 {
+        let mut my_size = 0;
+        if let Some(ref v) = self.cell_identifier.as_ref() {
+            let len = v.compute_size();
+            my_size += 1 + ::protobuf::rt::compute_raw_varint32_size(len) + len;
+        }
+        if let ::std::option::Option::Some(ref v) = self.one_of_insert_option {
+            match v {
+                &SelectOptionChangesetPayload_oneof_one_of_insert_option::insert_option(ref v) => {
+                    let len = v.compute_size();
+                    my_size += 1 + ::protobuf::rt::compute_raw_varint32_size(len) + len;
+                },
+            };
+        }
+        if let ::std::option::Option::Some(ref v) = self.one_of_delete_option {
+            match v {
+                &SelectOptionChangesetPayload_oneof_one_of_delete_option::delete_option(ref v) => {
+                    let len = v.compute_size();
+                    my_size += 1 + ::protobuf::rt::compute_raw_varint32_size(len) + len;
+                },
+            };
+        }
+        my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields());
+        self.cached_size.set(my_size);
+        my_size
+    }
+
+    fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::ProtobufResult<()> {
+        if let Some(ref v) = self.cell_identifier.as_ref() {
+            os.write_tag(1, ::protobuf::wire_format::WireTypeLengthDelimited)?;
+            os.write_raw_varint32(v.get_cached_size())?;
+            v.write_to_with_cached_sizes(os)?;
+        }
+        if let ::std::option::Option::Some(ref v) = self.one_of_insert_option {
+            match v {
+                &SelectOptionChangesetPayload_oneof_one_of_insert_option::insert_option(ref v) => {
+                    os.write_tag(2, ::protobuf::wire_format::WireTypeLengthDelimited)?;
+                    os.write_raw_varint32(v.get_cached_size())?;
+                    v.write_to_with_cached_sizes(os)?;
+                },
+            };
+        }
+        if let ::std::option::Option::Some(ref v) = self.one_of_delete_option {
+            match v {
+                &SelectOptionChangesetPayload_oneof_one_of_delete_option::delete_option(ref v) => {
+                    os.write_tag(3, ::protobuf::wire_format::WireTypeLengthDelimited)?;
+                    os.write_raw_varint32(v.get_cached_size())?;
+                    v.write_to_with_cached_sizes(os)?;
+                },
+            };
+        }
+        os.write_unknown_fields(self.get_unknown_fields())?;
+        ::std::result::Result::Ok(())
+    }
+
+    fn get_cached_size(&self) -> u32 {
+        self.cached_size.get()
+    }
+
+    fn get_unknown_fields(&self) -> &::protobuf::UnknownFields {
+        &self.unknown_fields
+    }
+
+    fn mut_unknown_fields(&mut self) -> &mut ::protobuf::UnknownFields {
+        &mut self.unknown_fields
+    }
+
+    fn as_any(&self) -> &dyn (::std::any::Any) {
+        self as &dyn (::std::any::Any)
+    }
+    fn as_any_mut(&mut self) -> &mut dyn (::std::any::Any) {
+        self as &mut dyn (::std::any::Any)
+    }
+    fn into_any(self: ::std::boxed::Box<Self>) -> ::std::boxed::Box<dyn (::std::any::Any)> {
+        self
+    }
+
+    fn descriptor(&self) -> &'static ::protobuf::reflect::MessageDescriptor {
+        Self::descriptor_static()
+    }
+
+    fn new() -> SelectOptionChangesetPayload {
+        SelectOptionChangesetPayload::new()
+    }
+
+    fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor {
+        static descriptor: ::protobuf::rt::LazyV2<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::LazyV2::INIT;
+        descriptor.get(|| {
+            let mut fields = ::std::vec::Vec::new();
+            fields.push(::protobuf::reflect::accessor::make_singular_ptr_field_accessor::<_, ::protobuf::types::ProtobufTypeMessage<super::cell_entities::CellIdentifierPayload>>(
+                "cell_identifier",
+                |m: &SelectOptionChangesetPayload| { &m.cell_identifier },
+                |m: &mut SelectOptionChangesetPayload| { &mut m.cell_identifier },
+            ));
+            fields.push(::protobuf::reflect::accessor::make_singular_message_accessor::<_, SelectOption>(
+                "insert_option",
+                SelectOptionChangesetPayload::has_insert_option,
+                SelectOptionChangesetPayload::get_insert_option,
+            ));
+            fields.push(::protobuf::reflect::accessor::make_singular_message_accessor::<_, SelectOption>(
+                "delete_option",
+                SelectOptionChangesetPayload::has_delete_option,
+                SelectOptionChangesetPayload::get_delete_option,
+            ));
+            ::protobuf::reflect::MessageDescriptor::new_pb_name::<SelectOptionChangesetPayload>(
+                "SelectOptionChangesetPayload",
+                fields,
+                file_descriptor_proto()
+            )
+        })
+    }
+
+    fn default_instance() -> &'static SelectOptionChangesetPayload {
+        static instance: ::protobuf::rt::LazyV2<SelectOptionChangesetPayload> = ::protobuf::rt::LazyV2::INIT;
+        instance.get(SelectOptionChangesetPayload::new)
+    }
+}
+
+impl ::protobuf::Clear for SelectOptionChangesetPayload {
+    fn clear(&mut self) {
+        self.cell_identifier.clear();
+        self.one_of_insert_option = ::std::option::Option::None;
+        self.one_of_delete_option = ::std::option::Option::None;
+        self.unknown_fields.clear();
+    }
+}
+
+impl ::std::fmt::Debug for SelectOptionChangesetPayload {
+    fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
+        ::protobuf::text_format::fmt(self, f)
+    }
+}
+
+impl ::protobuf::reflect::ProtobufValue for SelectOptionChangesetPayload {
+    fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
+        ::protobuf::reflect::ReflectValueRef::Message(self)
+    }
+}
+
+#[derive(PartialEq,Clone,Default)]
+pub struct SelectOptionCellChangesetPayload {
+    // message fields
+    pub grid_id: ::std::string::String,
+    pub row_id: ::std::string::String,
+    pub field_id: ::std::string::String,
+    // message oneof groups
+    pub one_of_insert_option_id: ::std::option::Option<SelectOptionCellChangesetPayload_oneof_one_of_insert_option_id>,
+    pub one_of_delete_option_id: ::std::option::Option<SelectOptionCellChangesetPayload_oneof_one_of_delete_option_id>,
+    // special fields
+    pub unknown_fields: ::protobuf::UnknownFields,
+    pub cached_size: ::protobuf::CachedSize,
+}
+
+impl<'a> ::std::default::Default for &'a SelectOptionCellChangesetPayload {
+    fn default() -> &'a SelectOptionCellChangesetPayload {
+        <SelectOptionCellChangesetPayload as ::protobuf::Message>::default_instance()
+    }
+}
+
+#[derive(Clone,PartialEq,Debug)]
+pub enum SelectOptionCellChangesetPayload_oneof_one_of_insert_option_id {
+    insert_option_id(::std::string::String),
+}
+
+#[derive(Clone,PartialEq,Debug)]
+pub enum SelectOptionCellChangesetPayload_oneof_one_of_delete_option_id {
+    delete_option_id(::std::string::String),
+}
+
+impl SelectOptionCellChangesetPayload {
+    pub fn new() -> SelectOptionCellChangesetPayload {
+        ::std::default::Default::default()
+    }
+
     // string grid_id = 1;
 
 
@@ -775,7 +1128,7 @@ impl SelectOptionChangesetPayload {
 
     pub fn get_insert_option_id(&self) -> &str {
         match self.one_of_insert_option_id {
-            ::std::option::Option::Some(SelectOptionChangesetPayload_oneof_one_of_insert_option_id::insert_option_id(ref v)) => v,
+            ::std::option::Option::Some(SelectOptionCellChangesetPayload_oneof_one_of_insert_option_id::insert_option_id(ref v)) => v,
             _ => "",
         }
     }
@@ -785,24 +1138,24 @@ impl SelectOptionChangesetPayload {
 
     pub fn has_insert_option_id(&self) -> bool {
         match self.one_of_insert_option_id {
-            ::std::option::Option::Some(SelectOptionChangesetPayload_oneof_one_of_insert_option_id::insert_option_id(..)) => true,
+            ::std::option::Option::Some(SelectOptionCellChangesetPayload_oneof_one_of_insert_option_id::insert_option_id(..)) => true,
             _ => false,
         }
     }
 
     // Param is passed by value, moved
     pub fn set_insert_option_id(&mut self, v: ::std::string::String) {
-        self.one_of_insert_option_id = ::std::option::Option::Some(SelectOptionChangesetPayload_oneof_one_of_insert_option_id::insert_option_id(v))
+        self.one_of_insert_option_id = ::std::option::Option::Some(SelectOptionCellChangesetPayload_oneof_one_of_insert_option_id::insert_option_id(v))
     }
 
     // Mutable pointer to the field.
     pub fn mut_insert_option_id(&mut self) -> &mut ::std::string::String {
-        if let ::std::option::Option::Some(SelectOptionChangesetPayload_oneof_one_of_insert_option_id::insert_option_id(_)) = self.one_of_insert_option_id {
+        if let ::std::option::Option::Some(SelectOptionCellChangesetPayload_oneof_one_of_insert_option_id::insert_option_id(_)) = self.one_of_insert_option_id {
         } else {
-            self.one_of_insert_option_id = ::std::option::Option::Some(SelectOptionChangesetPayload_oneof_one_of_insert_option_id::insert_option_id(::std::string::String::new()));
+            self.one_of_insert_option_id = ::std::option::Option::Some(SelectOptionCellChangesetPayload_oneof_one_of_insert_option_id::insert_option_id(::std::string::String::new()));
         }
         match self.one_of_insert_option_id {
-            ::std::option::Option::Some(SelectOptionChangesetPayload_oneof_one_of_insert_option_id::insert_option_id(ref mut v)) => v,
+            ::std::option::Option::Some(SelectOptionCellChangesetPayload_oneof_one_of_insert_option_id::insert_option_id(ref mut v)) => v,
             _ => panic!(),
         }
     }
@@ -811,7 +1164,7 @@ impl SelectOptionChangesetPayload {
     pub fn take_insert_option_id(&mut self) -> ::std::string::String {
         if self.has_insert_option_id() {
             match self.one_of_insert_option_id.take() {
-                ::std::option::Option::Some(SelectOptionChangesetPayload_oneof_one_of_insert_option_id::insert_option_id(v)) => v,
+                ::std::option::Option::Some(SelectOptionCellChangesetPayload_oneof_one_of_insert_option_id::insert_option_id(v)) => v,
                 _ => panic!(),
             }
         } else {
@@ -824,7 +1177,7 @@ impl SelectOptionChangesetPayload {
 
     pub fn get_delete_option_id(&self) -> &str {
         match self.one_of_delete_option_id {
-            ::std::option::Option::Some(SelectOptionChangesetPayload_oneof_one_of_delete_option_id::delete_option_id(ref v)) => v,
+            ::std::option::Option::Some(SelectOptionCellChangesetPayload_oneof_one_of_delete_option_id::delete_option_id(ref v)) => v,
             _ => "",
         }
     }
@@ -834,24 +1187,24 @@ impl SelectOptionChangesetPayload {
 
     pub fn has_delete_option_id(&self) -> bool {
         match self.one_of_delete_option_id {
-            ::std::option::Option::Some(SelectOptionChangesetPayload_oneof_one_of_delete_option_id::delete_option_id(..)) => true,
+            ::std::option::Option::Some(SelectOptionCellChangesetPayload_oneof_one_of_delete_option_id::delete_option_id(..)) => true,
             _ => false,
         }
     }
 
     // Param is passed by value, moved
     pub fn set_delete_option_id(&mut self, v: ::std::string::String) {
-        self.one_of_delete_option_id = ::std::option::Option::Some(SelectOptionChangesetPayload_oneof_one_of_delete_option_id::delete_option_id(v))
+        self.one_of_delete_option_id = ::std::option::Option::Some(SelectOptionCellChangesetPayload_oneof_one_of_delete_option_id::delete_option_id(v))
     }
 
     // Mutable pointer to the field.
     pub fn mut_delete_option_id(&mut self) -> &mut ::std::string::String {
-        if let ::std::option::Option::Some(SelectOptionChangesetPayload_oneof_one_of_delete_option_id::delete_option_id(_)) = self.one_of_delete_option_id {
+        if let ::std::option::Option::Some(SelectOptionCellChangesetPayload_oneof_one_of_delete_option_id::delete_option_id(_)) = self.one_of_delete_option_id {
         } else {
-            self.one_of_delete_option_id = ::std::option::Option::Some(SelectOptionChangesetPayload_oneof_one_of_delete_option_id::delete_option_id(::std::string::String::new()));
+            self.one_of_delete_option_id = ::std::option::Option::Some(SelectOptionCellChangesetPayload_oneof_one_of_delete_option_id::delete_option_id(::std::string::String::new()));
         }
         match self.one_of_delete_option_id {
-            ::std::option::Option::Some(SelectOptionChangesetPayload_oneof_one_of_delete_option_id::delete_option_id(ref mut v)) => v,
+            ::std::option::Option::Some(SelectOptionCellChangesetPayload_oneof_one_of_delete_option_id::delete_option_id(ref mut v)) => v,
             _ => panic!(),
         }
     }
@@ -860,7 +1213,7 @@ impl SelectOptionChangesetPayload {
     pub fn take_delete_option_id(&mut self) -> ::std::string::String {
         if self.has_delete_option_id() {
             match self.one_of_delete_option_id.take() {
-                ::std::option::Option::Some(SelectOptionChangesetPayload_oneof_one_of_delete_option_id::delete_option_id(v)) => v,
+                ::std::option::Option::Some(SelectOptionCellChangesetPayload_oneof_one_of_delete_option_id::delete_option_id(v)) => v,
                 _ => panic!(),
             }
         } else {
@@ -869,7 +1222,7 @@ impl SelectOptionChangesetPayload {
     }
 }
 
-impl ::protobuf::Message for SelectOptionChangesetPayload {
+impl ::protobuf::Message for SelectOptionCellChangesetPayload {
     fn is_initialized(&self) -> bool {
         true
     }
@@ -891,13 +1244,13 @@ impl ::protobuf::Message for SelectOptionChangesetPayload {
                     if wire_type != ::protobuf::wire_format::WireTypeLengthDelimited {
                         return ::std::result::Result::Err(::protobuf::rt::unexpected_wire_type(wire_type));
                     }
-                    self.one_of_insert_option_id = ::std::option::Option::Some(SelectOptionChangesetPayload_oneof_one_of_insert_option_id::insert_option_id(is.read_string()?));
+                    self.one_of_insert_option_id = ::std::option::Option::Some(SelectOptionCellChangesetPayload_oneof_one_of_insert_option_id::insert_option_id(is.read_string()?));
                 },
                 5 => {
                     if wire_type != ::protobuf::wire_format::WireTypeLengthDelimited {
                         return ::std::result::Result::Err(::protobuf::rt::unexpected_wire_type(wire_type));
                     }
-                    self.one_of_delete_option_id = ::std::option::Option::Some(SelectOptionChangesetPayload_oneof_one_of_delete_option_id::delete_option_id(is.read_string()?));
+                    self.one_of_delete_option_id = ::std::option::Option::Some(SelectOptionCellChangesetPayload_oneof_one_of_delete_option_id::delete_option_id(is.read_string()?));
                 },
                 _ => {
                     ::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?;
@@ -922,14 +1275,14 @@ impl ::protobuf::Message for SelectOptionChangesetPayload {
         }
         if let ::std::option::Option::Some(ref v) = self.one_of_insert_option_id {
             match v {
-                &SelectOptionChangesetPayload_oneof_one_of_insert_option_id::insert_option_id(ref v) => {
+                &SelectOptionCellChangesetPayload_oneof_one_of_insert_option_id::insert_option_id(ref v) => {
                     my_size += ::protobuf::rt::string_size(4, &v);
                 },
             };
         }
         if let ::std::option::Option::Some(ref v) = self.one_of_delete_option_id {
             match v {
-                &SelectOptionChangesetPayload_oneof_one_of_delete_option_id::delete_option_id(ref v) => {
+                &SelectOptionCellChangesetPayload_oneof_one_of_delete_option_id::delete_option_id(ref v) => {
                     my_size += ::protobuf::rt::string_size(5, &v);
                 },
             };
@@ -951,14 +1304,14 @@ impl ::protobuf::Message for SelectOptionChangesetPayload {
         }
         if let ::std::option::Option::Some(ref v) = self.one_of_insert_option_id {
             match v {
-                &SelectOptionChangesetPayload_oneof_one_of_insert_option_id::insert_option_id(ref v) => {
+                &SelectOptionCellChangesetPayload_oneof_one_of_insert_option_id::insert_option_id(ref v) => {
                     os.write_string(4, v)?;
                 },
             };
         }
         if let ::std::option::Option::Some(ref v) = self.one_of_delete_option_id {
             match v {
-                &SelectOptionChangesetPayload_oneof_one_of_delete_option_id::delete_option_id(ref v) => {
+                &SelectOptionCellChangesetPayload_oneof_one_of_delete_option_id::delete_option_id(ref v) => {
                     os.write_string(5, v)?;
                 },
             };
@@ -993,8 +1346,8 @@ impl ::protobuf::Message for SelectOptionChangesetPayload {
         Self::descriptor_static()
     }
 
-    fn new() -> SelectOptionChangesetPayload {
-        SelectOptionChangesetPayload::new()
+    fn new() -> SelectOptionCellChangesetPayload {
+        SelectOptionCellChangesetPayload::new()
     }
 
     fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor {
@@ -1003,44 +1356,44 @@ impl ::protobuf::Message for SelectOptionChangesetPayload {
             let mut fields = ::std::vec::Vec::new();
             fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
                 "grid_id",
-                |m: &SelectOptionChangesetPayload| { &m.grid_id },
-                |m: &mut SelectOptionChangesetPayload| { &mut m.grid_id },
+                |m: &SelectOptionCellChangesetPayload| { &m.grid_id },
+                |m: &mut SelectOptionCellChangesetPayload| { &mut m.grid_id },
             ));
             fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
                 "row_id",
-                |m: &SelectOptionChangesetPayload| { &m.row_id },
-                |m: &mut SelectOptionChangesetPayload| { &mut m.row_id },
+                |m: &SelectOptionCellChangesetPayload| { &m.row_id },
+                |m: &mut SelectOptionCellChangesetPayload| { &mut m.row_id },
             ));
             fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
                 "field_id",
-                |m: &SelectOptionChangesetPayload| { &m.field_id },
-                |m: &mut SelectOptionChangesetPayload| { &mut m.field_id },
+                |m: &SelectOptionCellChangesetPayload| { &m.field_id },
+                |m: &mut SelectOptionCellChangesetPayload| { &mut m.field_id },
             ));
             fields.push(::protobuf::reflect::accessor::make_singular_string_accessor::<_>(
                 "insert_option_id",
-                SelectOptionChangesetPayload::has_insert_option_id,
-                SelectOptionChangesetPayload::get_insert_option_id,
+                SelectOptionCellChangesetPayload::has_insert_option_id,
+                SelectOptionCellChangesetPayload::get_insert_option_id,
             ));
             fields.push(::protobuf::reflect::accessor::make_singular_string_accessor::<_>(
                 "delete_option_id",
-                SelectOptionChangesetPayload::has_delete_option_id,
-                SelectOptionChangesetPayload::get_delete_option_id,
+                SelectOptionCellChangesetPayload::has_delete_option_id,
+                SelectOptionCellChangesetPayload::get_delete_option_id,
             ));
-            ::protobuf::reflect::MessageDescriptor::new_pb_name::<SelectOptionChangesetPayload>(
-                "SelectOptionChangesetPayload",
+            ::protobuf::reflect::MessageDescriptor::new_pb_name::<SelectOptionCellChangesetPayload>(
+                "SelectOptionCellChangesetPayload",
                 fields,
                 file_descriptor_proto()
             )
         })
     }
 
-    fn default_instance() -> &'static SelectOptionChangesetPayload {
-        static instance: ::protobuf::rt::LazyV2<SelectOptionChangesetPayload> = ::protobuf::rt::LazyV2::INIT;
-        instance.get(SelectOptionChangesetPayload::new)
+    fn default_instance() -> &'static SelectOptionCellChangesetPayload {
+        static instance: ::protobuf::rt::LazyV2<SelectOptionCellChangesetPayload> = ::protobuf::rt::LazyV2::INIT;
+        instance.get(SelectOptionCellChangesetPayload::new)
     }
 }
 
-impl ::protobuf::Clear for SelectOptionChangesetPayload {
+impl ::protobuf::Clear for SelectOptionCellChangesetPayload {
     fn clear(&mut self) {
         self.grid_id.clear();
         self.row_id.clear();
@@ -1051,13 +1404,13 @@ impl ::protobuf::Clear for SelectOptionChangesetPayload {
     }
 }
 
-impl ::std::fmt::Debug for SelectOptionChangesetPayload {
+impl ::std::fmt::Debug for SelectOptionCellChangesetPayload {
     fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
         ::protobuf::text_format::fmt(self, f)
     }
 }
 
-impl ::protobuf::reflect::ProtobufValue for SelectOptionChangesetPayload {
+impl ::protobuf::reflect::ProtobufValue for SelectOptionCellChangesetPayload {
     fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
         ::protobuf::reflect::ReflectValueRef::Message(self)
     }
@@ -1350,26 +1703,31 @@ impl ::protobuf::reflect::ProtobufValue for SelectOptionColor {
 }
 
 static file_descriptor_proto_data: &'static [u8] = b"\
-    \n\x1bselection_type_option.proto\"f\n\x16SingleSelectTypeOption\x12'\n\
-    \x07options\x18\x01\x20\x03(\x0b2\r.SelectOptionR\x07options\x12#\n\rdis\
-    able_color\x18\x02\x20\x01(\x08R\x0cdisableColor\"e\n\x15MultiSelectType\
-    Option\x12'\n\x07options\x18\x01\x20\x03(\x0b2\r.SelectOptionR\x07option\
-    s\x12#\n\rdisable_color\x18\x02\x20\x01(\x08R\x0cdisableColor\"\\\n\x0cS\
-    electOption\x12\x0e\n\x02id\x18\x01\x20\x01(\tR\x02id\x12\x12\n\x04name\
-    \x18\x02\x20\x01(\tR\x04name\x12(\n\x05color\x18\x03\x20\x01(\x0e2\x12.S\
-    electOptionColorR\x05color\"\xf7\x01\n\x1cSelectOptionChangesetPayload\
-    \x12\x17\n\x07grid_id\x18\x01\x20\x01(\tR\x06gridId\x12\x15\n\x06row_id\
-    \x18\x02\x20\x01(\tR\x05rowId\x12\x19\n\x08field_id\x18\x03\x20\x01(\tR\
-    \x07fieldId\x12*\n\x10insert_option_id\x18\x04\x20\x01(\tH\0R\x0einsertO\
-    ptionId\x12*\n\x10delete_option_id\x18\x05\x20\x01(\tH\x01R\x0edeleteOpt\
-    ionIdB\x19\n\x17one_of_insert_option_idB\x19\n\x17one_of_delete_option_i\
-    d\"t\n\x13SelectOptionContext\x12'\n\x07options\x18\x01\x20\x03(\x0b2\r.\
-    SelectOptionR\x07options\x124\n\x0eselect_options\x18\x02\x20\x03(\x0b2\
-    \r.SelectOptionR\rselectOptions*y\n\x11SelectOptionColor\x12\n\n\x06Purp\
-    le\x10\0\x12\x08\n\x04Pink\x10\x01\x12\r\n\tLightPink\x10\x02\x12\n\n\
-    \x06Orange\x10\x03\x12\n\n\x06Yellow\x10\x04\x12\x08\n\x04Lime\x10\x05\
-    \x12\t\n\x05Green\x10\x06\x12\x08\n\x04Aqua\x10\x07\x12\x08\n\x04Blue\
-    \x10\x08b\x06proto3\
+    \n\x1bselection_type_option.proto\x1a\x13cell_entities.proto\"f\n\x16Sin\
+    gleSelectTypeOption\x12'\n\x07options\x18\x01\x20\x03(\x0b2\r.SelectOpti\
+    onR\x07options\x12#\n\rdisable_color\x18\x02\x20\x01(\x08R\x0cdisableCol\
+    or\"e\n\x15MultiSelectTypeOption\x12'\n\x07options\x18\x01\x20\x03(\x0b2\
+    \r.SelectOptionR\x07options\x12#\n\rdisable_color\x18\x02\x20\x01(\x08R\
+    \x0cdisableColor\"\\\n\x0cSelectOption\x12\x0e\n\x02id\x18\x01\x20\x01(\
+    \tR\x02id\x12\x12\n\x04name\x18\x02\x20\x01(\tR\x04name\x12(\n\x05color\
+    \x18\x03\x20\x01(\x0e2\x12.SelectOptionColorR\x05color\"\xfb\x01\n\x1cSe\
+    lectOptionChangesetPayload\x12?\n\x0fcell_identifier\x18\x01\x20\x01(\
+    \x0b2\x16.CellIdentifierPayloadR\x0ecellIdentifier\x124\n\rinsert_option\
+    \x18\x02\x20\x01(\x0b2\r.SelectOptionH\0R\x0cinsertOption\x124\n\rdelete\
+    _option\x18\x03\x20\x01(\x0b2\r.SelectOptionH\x01R\x0cdeleteOptionB\x16\
+    \n\x14one_of_insert_optionB\x16\n\x14one_of_delete_option\"\xfb\x01\n\
+    \x20SelectOptionCellChangesetPayload\x12\x17\n\x07grid_id\x18\x01\x20\
+    \x01(\tR\x06gridId\x12\x15\n\x06row_id\x18\x02\x20\x01(\tR\x05rowId\x12\
+    \x19\n\x08field_id\x18\x03\x20\x01(\tR\x07fieldId\x12*\n\x10insert_optio\
+    n_id\x18\x04\x20\x01(\tH\0R\x0einsertOptionId\x12*\n\x10delete_option_id\
+    \x18\x05\x20\x01(\tH\x01R\x0edeleteOptionIdB\x19\n\x17one_of_insert_opti\
+    on_idB\x19\n\x17one_of_delete_option_id\"t\n\x13SelectOptionContext\x12'\
+    \n\x07options\x18\x01\x20\x03(\x0b2\r.SelectOptionR\x07options\x124\n\
+    \x0eselect_options\x18\x02\x20\x03(\x0b2\r.SelectOptionR\rselectOptions*\
+    y\n\x11SelectOptionColor\x12\n\n\x06Purple\x10\0\x12\x08\n\x04Pink\x10\
+    \x01\x12\r\n\tLightPink\x10\x02\x12\n\n\x06Orange\x10\x03\x12\n\n\x06Yel\
+    low\x10\x04\x12\x08\n\x04Lime\x10\x05\x12\t\n\x05Green\x10\x06\x12\x08\n\
+    \x04Aqua\x10\x07\x12\x08\n\x04Blue\x10\x08b\x06proto3\
 ";
 
 static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;

+ 14 - 0
frontend/rust-lib/flowy-grid/src/protobuf/proto/cell_entities.proto

@@ -0,0 +1,14 @@
+syntax = "proto3";
+
+message CreateSelectOptionPayload {
+    CellIdentifierPayload cell_identifier = 1;
+    string option_name = 2;
+}
+message CellIdentifierPayload {
+    string grid_id = 1;
+    string field_id = 2;
+    string row_id = 3;
+}
+message SelectOptionName {
+    string name = 1;
+}

+ 1 - 1
frontend/rust-lib/flowy-grid/src/protobuf/proto/dart_notification.proto

@@ -6,6 +6,6 @@ enum GridNotification {
     DidUpdateBlock = 20;
     DidUpdateRow = 30;
     DidUpdateCell = 31;
-    DidUpdateFields = 40;
+    DidUpdateGrid = 40;
     DidUpdateField = 41;
 }

+ 4 - 3
frontend/rust-lib/flowy-grid/src/protobuf/proto/event_map.proto

@@ -10,10 +10,11 @@ enum GridEvent {
     SwitchToField = 14;
     DuplicateField = 15;
     GetEditFieldContext = 16;
-    CreateSelectOption = 30;
-    GetSelectOptions = 31;
+    NewSelectOption = 30;
+    GetSelectOptionContext = 31;
+    ApplySelectOptionChangeset = 32;
     CreateRow = 50;
     GetRow = 51;
     UpdateCell = 70;
-    ApplySelectOptionChangeset = 71;
+    ApplySelectOptionCellChangeset = 71;
 }

+ 6 - 0
frontend/rust-lib/flowy-grid/src/protobuf/proto/field_entities.proto

@@ -0,0 +1,6 @@
+syntax = "proto3";
+
+message FieldIdentifierPayload {
+    string field_id = 1;
+    string grid_id = 2;
+}

+ 6 - 0
frontend/rust-lib/flowy-grid/src/protobuf/proto/row_entities.proto

@@ -0,0 +1,6 @@
+syntax = "proto3";
+
+message RowIdentifierPayload {
+    string grid_id = 1;
+    string row_id = 3;
+}

+ 6 - 0
frontend/rust-lib/flowy-grid/src/protobuf/proto/selection_type_option.proto

@@ -1,4 +1,5 @@
 syntax = "proto3";
+import "cell_entities.proto";
 
 message SingleSelectTypeOption {
     repeated SelectOption options = 1;
@@ -14,6 +15,11 @@ message SelectOption {
     SelectOptionColor color = 3;
 }
 message SelectOptionChangesetPayload {
+    CellIdentifierPayload cell_identifier = 1;
+    oneof one_of_insert_option { SelectOption insert_option = 2; };
+    oneof one_of_delete_option { SelectOption delete_option = 3; };
+}
+message SelectOptionCellChangesetPayload {
     string grid_id = 1;
     string row_id = 2;
     string field_id = 3;

+ 2 - 2
frontend/rust-lib/flowy-grid/src/services/block_meta_editor.rs

@@ -6,8 +6,8 @@ use bytes::Bytes;
 use dashmap::DashMap;
 use flowy_error::{FlowyError, FlowyResult};
 use flowy_grid_data_model::entities::{
-    CellIdentifier, CellMeta, CellMetaChangeset, CellNotificationData, FieldMeta, GridBlockMeta,
-    GridBlockMetaChangeset, GridBlockOrder, RepeatedCell, Row, RowMeta, RowMetaChangeset, RowOrder,
+    CellMeta, CellMetaChangeset, CellNotificationData, FieldMeta, GridBlockMeta, GridBlockMetaChangeset,
+    GridBlockOrder, RepeatedCell, Row, RowMeta, RowMetaChangeset, RowOrder,
 };
 use flowy_revision::disk::SQLiteGridBlockMetaRevisionPersistence;
 use flowy_revision::{

+ 69 - 0
frontend/rust-lib/flowy-grid/src/services/cell/cell_entities.rs

@@ -0,0 +1,69 @@
+use flowy_derive::ProtoBuf;
+use flowy_error::ErrorCode;
+use flowy_grid_data_model::parser::{NotEmptyStr, NotEmptyUuid};
+
+#[derive(ProtoBuf, Default)]
+pub struct CreateSelectOptionPayload {
+    #[pb(index = 1)]
+    pub cell_identifier: CellIdentifierPayload,
+
+    #[pb(index = 2)]
+    pub option_name: String,
+}
+
+pub struct CreateSelectOptionParams {
+    pub cell_identifier: CellIdentifier,
+    pub option_name: String,
+}
+
+impl TryInto<CreateSelectOptionParams> for CreateSelectOptionPayload {
+    type Error = ErrorCode;
+
+    fn try_into(self) -> Result<CreateSelectOptionParams, Self::Error> {
+        let option_name = NotEmptyStr::parse(self.option_name).map_err(|_| ErrorCode::SelectOptionNameIsEmpty)?;
+        let cell_identifier = self.cell_identifier.try_into()?;
+        Ok(CreateSelectOptionParams {
+            cell_identifier,
+            option_name: option_name.0,
+        })
+    }
+}
+
+#[derive(Debug, Clone, Default, ProtoBuf)]
+pub struct CellIdentifierPayload {
+    #[pb(index = 1)]
+    pub grid_id: String,
+
+    #[pb(index = 2)]
+    pub field_id: String,
+
+    #[pb(index = 3)]
+    pub row_id: String,
+}
+
+pub struct CellIdentifier {
+    pub grid_id: String,
+    pub field_id: String,
+    pub row_id: String,
+}
+
+impl TryInto<CellIdentifier> for CellIdentifierPayload {
+    type Error = ErrorCode;
+
+    fn try_into(self) -> Result<CellIdentifier, Self::Error> {
+        let grid_id = NotEmptyUuid::parse(self.grid_id).map_err(|_| ErrorCode::GridIdIsEmpty)?;
+        let field_id = NotEmptyUuid::parse(self.field_id).map_err(|_| ErrorCode::FieldIdIsEmpty)?;
+        let row_id = NotEmptyUuid::parse(self.row_id).map_err(|_| ErrorCode::RowIdIsEmpty)?;
+        Ok(CellIdentifier {
+            grid_id: grid_id.0,
+            field_id: field_id.0,
+            row_id: row_id.0,
+        })
+    }
+}
+
+#[derive(ProtoBuf, Default)]
+pub struct SelectOptionName {
+    #[pb(index = 1)]
+    pub name: String,
+}

+ 3 - 0
frontend/rust-lib/flowy-grid/src/services/cell/mod.rs

@@ -0,0 +1,3 @@
+pub(crate) mod cell_entities;
+
+pub use cell_entities::*;

+ 30 - 0
frontend/rust-lib/flowy-grid/src/services/field/field_entities.rs

@@ -0,0 +1,30 @@
+use flowy_derive::ProtoBuf;
+use flowy_error::ErrorCode;
+use flowy_grid_data_model::parser::NotEmptyUuid;
+
+#[derive(Debug, Clone, Default, ProtoBuf)]
+pub struct FieldIdentifierPayload {
+    #[pb(index = 1)]
+    pub field_id: String,
+
+    #[pb(index = 2)]
+    pub grid_id: String,
+}
+
+pub struct FieldIdentifier {
+    pub field_id: String,
+    pub grid_id: String,
+}
+
+impl TryInto<FieldIdentifier> for FieldIdentifierPayload {
+    type Error = ErrorCode;
+
+    fn try_into(self) -> Result<FieldIdentifier, Self::Error> {
+        let grid_id = NotEmptyUuid::parse(self.grid_id).map_err(|_| ErrorCode::GridIdIsEmpty)?;
+        let field_id = NotEmptyUuid::parse(self.field_id).map_err(|_| ErrorCode::FieldIdIsEmpty)?;
+        Ok(FieldIdentifier {
+            grid_id: grid_id.0,
+            field_id: field_id.0,
+        })
+    }
+}

+ 3 - 1
frontend/rust-lib/flowy-grid/src/services/field/mod.rs

@@ -1,5 +1,7 @@
 mod field_builder;
-mod type_options;
+pub(crate) mod field_entities;
+pub(crate) mod type_options;
 
 pub use field_builder::*;
+pub use field_entities::*;
 pub use type_options::*;

+ 123 - 31
frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option.rs

@@ -1,4 +1,5 @@
 use crate::impl_type_option;
+use crate::services::cell::{CellIdentifier, CellIdentifierPayload};
 use crate::services::field::{BoxTypeOptionBuilder, TypeOptionBuilder};
 use crate::services::row::{CellDataChangeset, CellDataOperation, TypeOptionCellData};
 use crate::services::util::*;
@@ -16,6 +17,12 @@ use uuid::Uuid;
 
 pub const SELECTION_IDS_SEPARATOR: &str = ",";
 
+pub trait SelectOptionOperation: TypeOptionDataEntry + Send + Sync {
+    fn insert_option(&mut self, new_option: SelectOption);
+    fn delete_option(&mut self, delete_option: SelectOption);
+    fn option_context(&self, cell_meta: &Option<CellMeta>) -> SelectOptionContext;
+}
+
 // Single select
 #[derive(Clone, Debug, Default, Serialize, Deserialize, ProtoBuf)]
 pub struct SingleSelectTypeOption {
@@ -27,8 +34,23 @@ pub struct SingleSelectTypeOption {
 }
 impl_type_option!(SingleSelectTypeOption, FieldType::SingleSelect);
 
-impl SingleSelectTypeOption {
-    pub fn select_option_context(&self, cell_meta: &Option<CellMeta>) -> SelectOptionContext {
+impl SelectOptionOperation for SingleSelectTypeOption {
+    fn insert_option(&mut self, new_option: SelectOption) {
+        if let Some(index) = self.options.iter().position(|option| option.id == new_option.id) {
+            self.options.remove(index);
+            self.options.insert(index, new_option);
+        } else {
+            self.options.insert(0, new_option);
+        }
+    }
+
+    fn delete_option(&mut self, delete_option: SelectOption) {
+        if let Some(index) = self.options.iter().position(|option| option.id == delete_option.id) {
+            self.options.remove(index);
+        }
+    }
+
+    fn option_context(&self, cell_meta: &Option<CellMeta>) -> SelectOptionContext {
         let select_options = make_select_context_from(cell_meta, &self.options);
         SelectOptionContext {
             options: self.options.clone(),
@@ -37,22 +59,6 @@ impl SingleSelectTypeOption {
     }
 }
 
-fn make_select_context_from(cell_meta: &Option<CellMeta>, options: &Vec<SelectOption>) -> Vec<SelectOption> {
-    match cell_meta {
-        None => vec![],
-        Some(cell_meta) => {
-            if let Ok(type_option_cell_data) = TypeOptionCellData::from_str(&cell_meta.data) {
-                select_option_ids(type_option_cell_data.data)
-                    .into_iter()
-                    .flat_map(|option_id| options.iter().find(|option| option.id == option_id).cloned())
-                    .collect()
-            } else {
-                vec![]
-            }
-        }
-    }
-}
-
 impl CellDataOperation for SingleSelectTypeOption {
     fn decode_cell_data(&self, data: String, _field_meta: &FieldMeta) -> String {
         if let Ok(type_option_cell_data) = TypeOptionCellData::from_str(&data) {
@@ -78,7 +84,7 @@ impl CellDataOperation for SingleSelectTypeOption {
         _cell_meta: Option<CellMeta>,
     ) -> Result<String, FlowyError> {
         let changeset = changeset.into();
-        let select_option_changeset: SelectOptionChangeset = serde_json::from_str(&changeset)?;
+        let select_option_changeset: SelectOptionCellChangeset = serde_json::from_str(&changeset)?;
         let new_cell_data: String;
         if let Some(insert_option_id) = select_option_changeset.insert_option_id {
             new_cell_data = insert_option_id;
@@ -123,9 +129,28 @@ pub struct MultiSelectTypeOption {
 }
 impl_type_option!(MultiSelectTypeOption, FieldType::MultiSelect);
 
-impl MultiSelectTypeOption {
-    pub fn select_option_context(&self, cell_meta: &Option<CellMeta>) -> SelectOptionContext {
-        todo!()
+impl SelectOptionOperation for MultiSelectTypeOption {
+    fn insert_option(&mut self, new_option: SelectOption) {
+        if let Some(index) = self.options.iter().position(|option| option.id == new_option.id) {
+            self.options.remove(index);
+            self.options.insert(index, new_option);
+        } else {
+            self.options.insert(0, new_option);
+        }
+    }
+
+    fn delete_option(&mut self, delete_option: SelectOption) {
+        if let Some(index) = self.options.iter().position(|option| option.id == delete_option.id) {
+            self.options.remove(index);
+        }
+    }
+
+    fn option_context(&self, cell_meta: &Option<CellMeta>) -> SelectOptionContext {
+        let select_options = make_select_context_from(cell_meta, &self.options);
+        SelectOptionContext {
+            options: self.options.clone(),
+            select_options,
+        }
     }
 }
 
@@ -153,7 +178,7 @@ impl CellDataOperation for MultiSelectTypeOption {
         cell_meta: Option<CellMeta>,
     ) -> Result<String, FlowyError> {
         let changeset = changeset.into();
-        let select_option_changeset: SelectOptionChangeset = serde_json::from_str(&changeset)?;
+        let select_option_changeset: SelectOptionCellChangeset = serde_json::from_str(&changeset)?;
         let new_cell_data: String;
         match cell_meta {
             None => {
@@ -230,6 +255,37 @@ impl SelectOption {
 
 #[derive(Clone, Debug, Default, ProtoBuf)]
 pub struct SelectOptionChangesetPayload {
+    #[pb(index = 1)]
+    pub cell_identifier: CellIdentifierPayload,
+
+    #[pb(index = 2, one_of)]
+    pub insert_option: Option<SelectOption>,
+
+    #[pb(index = 3, one_of)]
+    pub delete_option: Option<SelectOption>,
+}
+
+pub struct SelectOptionChangeset {
+    pub cell_identifier: CellIdentifier,
+    pub insert_option: Option<SelectOption>,
+    pub delete_option: Option<SelectOption>,
+}
+
+impl TryInto<SelectOptionChangeset> for SelectOptionChangesetPayload {
+    type Error = ErrorCode;
+
+    fn try_into(self) -> Result<SelectOptionChangeset, Self::Error> {
+        let cell_identifier = self.cell_identifier.try_into()?;
+        Ok(SelectOptionChangeset {
+            cell_identifier,
+            insert_option: self.insert_option,
+            delete_option: self.delete_option,
+        })
+    }
+}
+
+#[derive(Clone, Debug, Default, ProtoBuf)]
+pub struct SelectOptionCellChangesetPayload {
     #[pb(index = 1)]
     pub grid_id: String,
 
@@ -246,7 +302,7 @@ pub struct SelectOptionChangesetPayload {
     pub delete_option_id: Option<String>,
 }
 
-pub struct SelectOptionChangesetParams {
+pub struct SelectOptionCellChangesetParams {
     pub grid_id: String,
     pub field_id: String,
     pub row_id: String,
@@ -255,14 +311,34 @@ pub struct SelectOptionChangesetParams {
 }
 
 #[derive(Clone, Serialize, Deserialize)]
-pub struct SelectOptionChangeset {
+pub struct SelectOptionCellChangeset {
     pub insert_option_id: Option<String>,
     pub delete_option_id: Option<String>,
 }
 
-impl std::convert::From<SelectOptionChangesetParams> for CellMetaChangeset {
-    fn from(params: SelectOptionChangesetParams) -> Self {
-        let changeset = SelectOptionChangeset {
+impl SelectOptionCellChangeset {
+    pub fn from_insert(option_id: &str) -> Self {
+        SelectOptionCellChangeset {
+            insert_option_id: Some(option_id.to_string()),
+            delete_option_id: None,
+        }
+    }
+
+    pub fn from_delete(option_id: &str) -> Self {
+        SelectOptionCellChangeset {
+            insert_option_id: None,
+            delete_option_id: Some(option_id.to_string()),
+        }
+    }
+
+    pub fn cell_data(&self) -> String {
+        serde_json::to_string(self).unwrap()
+    }
+}
+
+impl std::convert::From<SelectOptionCellChangesetParams> for CellMetaChangeset {
+    fn from(params: SelectOptionCellChangesetParams) -> Self {
+        let changeset = SelectOptionCellChangeset {
             insert_option_id: params.insert_option_id,
             delete_option_id: params.delete_option_id,
         };
@@ -276,10 +352,10 @@ impl std::convert::From<SelectOptionChangesetParams> for CellMetaChangeset {
     }
 }
 
-impl TryInto<SelectOptionChangesetParams> for SelectOptionChangesetPayload {
+impl TryInto<SelectOptionCellChangesetParams> for SelectOptionCellChangesetPayload {
     type Error = ErrorCode;
 
-    fn try_into(self) -> Result<SelectOptionChangesetParams, Self::Error> {
+    fn try_into(self) -> Result<SelectOptionCellChangesetParams, Self::Error> {
         let grid_id = NotEmptyUuid::parse(self.grid_id).map_err(|_| ErrorCode::GridIdIsEmpty)?;
         let row_id = NotEmptyUuid::parse(self.row_id).map_err(|_| ErrorCode::RowIdIsEmpty)?;
         let field_id = NotEmptyUuid::parse(self.field_id).map_err(|_| ErrorCode::FieldIdIsEmpty)?;
@@ -301,7 +377,7 @@ impl TryInto<SelectOptionChangesetParams> for SelectOptionChangesetPayload {
             ),
         };
 
-        Ok(SelectOptionChangesetParams {
+        Ok(SelectOptionCellChangesetParams {
             grid_id: grid_id.0,
             row_id: row_id.0,
             field_id: field_id.0,
@@ -340,6 +416,22 @@ impl std::default::Default for SelectOptionColor {
     }
 }
 
+fn make_select_context_from(cell_meta: &Option<CellMeta>, options: &Vec<SelectOption>) -> Vec<SelectOption> {
+    match cell_meta {
+        None => vec![],
+        Some(cell_meta) => {
+            if let Ok(type_option_cell_data) = TypeOptionCellData::from_str(&cell_meta.data) {
+                select_option_ids(type_option_cell_data.data)
+                    .into_iter()
+                    .flat_map(|option_id| options.iter().find(|option| option.id == option_id).cloned())
+                    .collect()
+            } else {
+                vec![]
+            }
+        }
+    }
+}
+
 #[cfg(test)]
 mod tests {
     use crate::services::field::{MultiSelectTypeOption, SingleSelectTypeOption};

+ 30 - 20
frontend/rust-lib/flowy-grid/src/services/grid_editor.rs

@@ -8,7 +8,7 @@ use bytes::Bytes;
 use flowy_error::{ErrorCode, FlowyError, FlowyResult};
 use flowy_grid_data_model::entities::*;
 use flowy_revision::{RevisionCloudService, RevisionCompactor, RevisionManager, RevisionObjectBuilder};
-use flowy_sync::client_grid::{GridChangeset, GridMetaPad, TypeOptionDataDeserializer};
+use flowy_sync::client_grid::{GridChangeset, GridMetaPad, JsonDeserializer};
 use flowy_sync::entities::revision::Revision;
 use flowy_sync::errors::CollaborateResult;
 use flowy_sync::util::make_delta_from_revisions;
@@ -61,7 +61,7 @@ impl ClientGridEditor {
         let _ = self
             .modify(|grid| {
                 if grid.contain_field(&field.id) {
-                    let deserializer = TypeOptionChangesetDeserializer(field.field_type.clone());
+                    let deserializer = TypeOptionJsonDeserializer(field.field_type.clone());
                     let changeset = FieldChangesetParams {
                         field_id: field.id,
                         grid_id,
@@ -82,7 +82,7 @@ impl ClientGridEditor {
                 }
             })
             .await?;
-        let _ = self.notify_did_update_fields().await?;
+        let _ = self.notify_did_update_grid().await?;
         Ok(())
     }
 
@@ -98,20 +98,27 @@ impl ClientGridEditor {
 
     pub async fn update_field(&self, params: FieldChangesetParams) -> FlowyResult<()> {
         let field_id = params.field_id.clone();
-        let deserializer = match self.pad.read().await.get_field(params.field_id.as_str()) {
+        let json_deserializer = match self.pad.read().await.get_field(params.field_id.as_str()) {
             None => return Err(ErrorCode::FieldDoesNotExist.into()),
-            Some(field_meta) => TypeOptionChangesetDeserializer(field_meta.field_type.clone()),
+            Some(field_meta) => TypeOptionJsonDeserializer(field_meta.field_type.clone()),
         };
 
-        let _ = self.modify(|grid| Ok(grid.update_field(params, deserializer)?)).await?;
-        let _ = self.notify_did_update_fields().await?;
+        let _ = self
+            .modify(|grid| Ok(grid.update_field(params, json_deserializer)?))
+            .await?;
+        let _ = self.notify_did_update_grid().await?;
         let _ = self.notify_did_update_field(&field_id).await?;
         Ok(())
     }
 
+    pub async fn replace_field(&self, field_meta: FieldMeta) -> FlowyResult<()> {
+        let _ = self.modify(|pad| Ok(pad.replace_field(field_meta)?)).await?;
+        Ok(())
+    }
+
     pub async fn delete_field(&self, field_id: &str) -> FlowyResult<()> {
         let _ = self.modify(|grid| Ok(grid.delete_field(field_id)?)).await?;
-        let _ = self.notify_did_update_fields().await?;
+        let _ = self.notify_did_update_grid().await?;
         Ok(())
     }
 
@@ -134,28 +141,33 @@ impl ClientGridEditor {
         let _ = self
             .modify(|grid| Ok(grid.switch_to_field(field_id, field_type.clone(), type_option_json_builder)?))
             .await?;
-        let _ = self.notify_did_update_fields().await?;
+        let _ = self.notify_did_update_grid().await?;
         let _ = self.notify_did_update_field(field_id).await?;
         Ok(())
     }
 
     pub async fn duplicate_field(&self, field_id: &str) -> FlowyResult<()> {
         let _ = self.modify(|grid| Ok(grid.duplicate_field(field_id)?)).await?;
-        let _ = self.notify_did_update_fields().await?;
+        let _ = self.notify_did_update_grid().await?;
         Ok(())
     }
 
-    pub async fn get_field_metas<T>(&self, field_orders: Option<Vec<T>>) -> FlowyResult<Vec<FieldMeta>>
+    pub async fn get_field_meta(&self, field_id: &str) -> Option<FieldMeta> {
+        let field_meta = self.pad.read().await.get_field(field_id)?.clone();
+        return Some(field_meta);
+    }
+
+    pub async fn get_field_metas<T>(&self, field_ids: Option<Vec<T>>) -> FlowyResult<Vec<FieldMeta>>
     where
         T: Into<FieldOrder>,
     {
-        if field_orders.is_none() {
+        if field_ids.is_none() {
             let field_metas = self.pad.read().await.get_field_metas(None)?;
             return Ok(field_metas);
         }
 
         let to_field_orders = |item: Vec<T>| item.into_iter().map(|data| data.into()).collect();
-        let field_orders = field_orders.map_or(vec![], to_field_orders);
+        let field_orders = field_ids.map_or(vec![], to_field_orders);
         let expected_len = field_orders.len();
         let field_metas = self.pad.read().await.get_field_metas(Some(field_orders))?;
         if expected_len != 0 && field_metas.len() != expected_len {
@@ -382,10 +394,10 @@ impl ClientGridEditor {
         }
     }
 
-    async fn notify_did_update_fields(&self) -> FlowyResult<()> {
+    async fn notify_did_update_grid(&self) -> FlowyResult<()> {
         let field_metas = self.get_field_metas::<FieldOrder>(None).await?;
         let repeated_field: RepeatedField = field_metas.into_iter().map(Field::from).collect::<Vec<_>>().into();
-        send_dart_notification(&self.grid_id, GridNotification::DidUpdateFields)
+        send_dart_notification(&self.grid_id, GridNotification::DidUpdateGrid)
             .payload(repeated_field)
             .send();
         Ok(())
@@ -442,12 +454,10 @@ impl RevisionCompactor for GridRevisionCompactor {
     }
 }
 
-struct TypeOptionChangesetDeserializer(FieldType);
-impl TypeOptionDataDeserializer for TypeOptionChangesetDeserializer {
+struct TypeOptionJsonDeserializer(FieldType);
+impl JsonDeserializer for TypeOptionJsonDeserializer {
     fn deserialize(&self, type_option_data: Vec<u8>) -> CollaborateResult<String> {
-        // The type_option_data is serialized by protobuf. But the type_option_data should be
-        // serialized by utf-8. So we must transform the data here.
-
+        // The type_option_data sent from Dart is serialized by protobuf.
         let builder = type_option_builder_from_bytes(type_option_data, &self.0);
         Ok(builder.entry().json_str())
     }

+ 1 - 0
frontend/rust-lib/flowy-grid/src/services/mod.rs

@@ -1,6 +1,7 @@
 mod util;
 
 pub mod block_meta_editor;
+pub mod cell;
 pub mod field;
 pub mod grid_editor;
 pub mod persistence;

+ 2 - 3
frontend/rust-lib/flowy-grid/src/services/row/cell_data_operation.rs

@@ -90,9 +90,8 @@ impl TypeOptionCellData {
     }
 }
 
-/// The function,apply_cell_data_changeset, will apply the cell_data_changeset.
-///
-/// The cell_data_changeset will be deserialized into specific data base on the FieldType.
+/// The changeset will be deserialized into specific data base on the FieldType.
+/// For example, it's String on FieldType::RichText, and SelectOptionChangeset on FieldType::SingleSelect
 pub fn apply_cell_data_changeset<T: Into<CellDataChangeset>>(
     changeset: T,
     cell_meta: Option<CellMeta>,

+ 1 - 0
frontend/rust-lib/flowy-grid/src/services/row/mod.rs

@@ -1,5 +1,6 @@
 mod cell_data_operation;
 mod row_builder;
+pub mod row_entities;
 mod row_loader;
 
 pub use cell_data_operation::*;

+ 31 - 0
frontend/rust-lib/flowy-grid/src/services/row/row_entities.rs

@@ -0,0 +1,31 @@
+use flowy_derive::ProtoBuf;
+use flowy_error::ErrorCode;
+use flowy_grid_data_model::parser::{NotEmptyStr, NotEmptyUuid};
+
+#[derive(ProtoBuf, Default)]
+pub struct RowIdentifierPayload {
+    #[pb(index = 1)]
+    pub grid_id: String,
+
+    #[pb(index = 3)]
+    pub row_id: String,
+}
+
+pub struct RowIdentifier {
+    pub grid_id: String,
+    pub row_id: String,
+}
+
+impl TryInto<RowIdentifier> for RowIdentifierPayload {
+    type Error = ErrorCode;
+
+    fn try_into(self) -> Result<RowIdentifier, Self::Error> {
+        let grid_id = NotEmptyUuid::parse(self.grid_id).map_err(|_| ErrorCode::GridIdIsEmpty)?;
+        let row_id = NotEmptyUuid::parse(self.row_id).map_err(|_| ErrorCode::RowIdIsEmpty)?;
+
+        Ok(RowIdentifier {
+            grid_id: grid_id.0,
+            row_id: row_id.0,
+        })
+    }
+}

+ 2 - 11
frontend/rust-lib/flowy-grid/tests/grid/grid_test.rs

@@ -59,13 +59,7 @@ async fn grid_update_field_with_empty_change() {
     let changeset = FieldChangesetParams {
         field_id: field_meta.id.clone(),
         grid_id: test.grid_id.clone(),
-        name: None,
-        desc: None,
-        field_type: None,
-        frozen: None,
-        visibility: None,
-        width: None,
-        type_option_data: None,
+        ..Default::default()
     };
 
     let scripts = vec![
@@ -90,13 +84,10 @@ async fn grid_update_field() {
     let changeset = FieldChangesetParams {
         field_id: single_select_field.id.clone(),
         grid_id: test.grid_id.clone(),
-        name: None,
-        desc: None,
-        field_type: None,
         frozen: Some(true),
-        visibility: None,
         width: Some(1000),
         type_option_data: Some(single_select_type_option.protobuf_bytes().to_vec()),
+        ..Default::default()
     };
 
     cloned_field.frozen = true;

+ 4 - 0
shared-lib/flowy-error-code/src/code.rs

@@ -103,6 +103,10 @@ pub enum ErrorCode {
     FieldDoesNotExist = 441,
     #[display(fmt = "The name of the option should not be empty")]
     SelectOptionNameIsEmpty = 442,
+    #[display(fmt = "Field not exists")]
+    FieldNotExists = 443,
+    #[display(fmt = "The operation in this field is invalid")]
+    FieldInvalidOperation = 444,
 
     #[display(fmt = "Field's type option data should not be empty")]
     TypeOptionDataIsEmpty = 450,

+ 11 - 4
shared-lib/flowy-error-code/src/protobuf/model/code.rs

@@ -64,6 +64,8 @@ pub enum ErrorCode {
     FieldIdIsEmpty = 440,
     FieldDoesNotExist = 441,
     SelectOptionNameIsEmpty = 442,
+    FieldNotExists = 443,
+    FieldInvalidOperation = 444,
     TypeOptionDataIsEmpty = 450,
     InvalidData = 500,
 }
@@ -114,6 +116,8 @@ impl ::protobuf::ProtobufEnum for ErrorCode {
             440 => ::std::option::Option::Some(ErrorCode::FieldIdIsEmpty),
             441 => ::std::option::Option::Some(ErrorCode::FieldDoesNotExist),
             442 => ::std::option::Option::Some(ErrorCode::SelectOptionNameIsEmpty),
+            443 => ::std::option::Option::Some(ErrorCode::FieldNotExists),
+            444 => ::std::option::Option::Some(ErrorCode::FieldInvalidOperation),
             450 => ::std::option::Option::Some(ErrorCode::TypeOptionDataIsEmpty),
             500 => ::std::option::Option::Some(ErrorCode::InvalidData),
             _ => ::std::option::Option::None
@@ -161,6 +165,8 @@ impl ::protobuf::ProtobufEnum for ErrorCode {
             ErrorCode::FieldIdIsEmpty,
             ErrorCode::FieldDoesNotExist,
             ErrorCode::SelectOptionNameIsEmpty,
+            ErrorCode::FieldNotExists,
+            ErrorCode::FieldInvalidOperation,
             ErrorCode::TypeOptionDataIsEmpty,
             ErrorCode::InvalidData,
         ];
@@ -191,7 +197,7 @@ impl ::protobuf::reflect::ProtobufValue for ErrorCode {
 }
 
 static file_descriptor_proto_data: &'static [u8] = b"\
-    \n\ncode.proto*\xb4\x07\n\tErrorCode\x12\x0c\n\x08Internal\x10\0\x12\x14\
+    \n\ncode.proto*\xe5\x07\n\tErrorCode\x12\x0c\n\x08Internal\x10\0\x12\x14\
     \n\x10UserUnauthorized\x10\x02\x12\x12\n\x0eRecordNotFound\x10\x03\x12\
     \x11\n\rUserIdIsEmpty\x10\x04\x12\x18\n\x14WorkspaceNameInvalid\x10d\x12\
     \x16\n\x12WorkspaceIdInvalid\x10e\x12\x18\n\x14AppColorStyleInvalid\x10f\
@@ -212,9 +218,10 @@ static file_descriptor_proto_data: &'static [u8] = b"\
     ridIdIsEmpty\x10\x9a\x03\x12\x13\n\x0eBlockIdIsEmpty\x10\xa4\x03\x12\x11\
     \n\x0cRowIdIsEmpty\x10\xae\x03\x12\x14\n\x0fOptionIdIsEmpty\x10\xaf\x03\
     \x12\x13\n\x0eFieldIdIsEmpty\x10\xb8\x03\x12\x16\n\x11FieldDoesNotExist\
-    \x10\xb9\x03\x12\x1c\n\x17SelectOptionNameIsEmpty\x10\xba\x03\x12\x1a\n\
-    \x15TypeOptionDataIsEmpty\x10\xc2\x03\x12\x10\n\x0bInvalidData\x10\xf4\
-    \x03b\x06proto3\
+    \x10\xb9\x03\x12\x1c\n\x17SelectOptionNameIsEmpty\x10\xba\x03\x12\x13\n\
+    \x0eFieldNotExists\x10\xbb\x03\x12\x1a\n\x15FieldInvalidOperation\x10\
+    \xbc\x03\x12\x1a\n\x15TypeOptionDataIsEmpty\x10\xc2\x03\x12\x10\n\x0bInv\
+    alidData\x10\xf4\x03b\x06proto3\
 ";
 
 static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;

+ 2 - 0
shared-lib/flowy-error-code/src/protobuf/proto/code.proto

@@ -40,6 +40,8 @@ enum ErrorCode {
     FieldIdIsEmpty = 440;
     FieldDoesNotExist = 441;
     SelectOptionNameIsEmpty = 442;
+    FieldNotExists = 443;
+    FieldInvalidOperation = 444;
     TypeOptionDataIsEmpty = 450;
     InvalidData = 500;
 }

+ 0 - 114
shared-lib/flowy-grid-data-model/src/entities/grid.rs

@@ -55,33 +55,6 @@ impl std::convert::From<FieldMeta> for Field {
     }
 }
 
-#[derive(Debug, Clone, Default, ProtoBuf)]
-pub struct FieldIdentifierPayload {
-    #[pb(index = 1)]
-    pub field_id: String,
-
-    #[pb(index = 2)]
-    pub grid_id: String,
-}
-
-pub struct FieldIdentifierParams {
-    pub field_id: String,
-    pub grid_id: String,
-}
-
-impl TryInto<FieldIdentifierParams> for FieldIdentifierPayload {
-    type Error = ErrorCode;
-
-    fn try_into(self) -> Result<FieldIdentifierParams, Self::Error> {
-        let grid_id = NotEmptyUuid::parse(self.grid_id).map_err(|_| ErrorCode::GridIdIsEmpty)?;
-        let field_id = NotEmptyUuid::parse(self.field_id).map_err(|_| ErrorCode::FieldIdIsEmpty)?;
-        Ok(FieldIdentifierParams {
-            grid_id: grid_id.0,
-            field_id: field_id.0,
-        })
-    }
-}
-
 #[derive(Debug, Clone, Default, ProtoBuf)]
 pub struct FieldOrder {
     #[pb(index = 1)]
@@ -331,39 +304,6 @@ impl Cell {
     }
 }
 
-#[derive(Debug, Clone, Default, ProtoBuf)]
-pub struct CellIdentifierPayload {
-    #[pb(index = 1)]
-    pub grid_id: String,
-
-    #[pb(index = 2)]
-    pub field_id: String,
-
-    #[pb(index = 3)]
-    pub row_id: String,
-}
-
-pub struct CellIdentifier {
-    pub grid_id: String,
-    pub field_id: String,
-    pub row_id: String,
-}
-
-impl TryInto<CellIdentifier> for CellIdentifierPayload {
-    type Error = ErrorCode;
-
-    fn try_into(self) -> Result<CellIdentifier, Self::Error> {
-        let grid_id = NotEmptyUuid::parse(self.grid_id).map_err(|_| ErrorCode::GridIdIsEmpty)?;
-        let field_id = NotEmptyUuid::parse(self.field_id).map_err(|_| ErrorCode::FieldIdIsEmpty)?;
-        let row_id = NotEmptyUuid::parse(self.row_id).map_err(|_| ErrorCode::RowIdIsEmpty)?;
-        Ok(CellIdentifier {
-            grid_id: grid_id.0,
-            field_id: field_id.0,
-            row_id: row_id.0,
-        })
-    }
-}
-
 #[derive(Debug, Clone, Default, ProtoBuf)]
 pub struct CellNotificationData {
     #[pb(index = 1)]
@@ -561,57 +501,3 @@ impl TryInto<QueryGridBlocksParams> for QueryGridBlocksPayload {
         })
     }
 }
-
-#[derive(ProtoBuf, Default)]
-pub struct QueryRowPayload {
-    #[pb(index = 1)]
-    pub grid_id: String,
-
-    #[pb(index = 3)]
-    pub row_id: String,
-}
-
-pub struct QueryRowParams {
-    pub grid_id: String,
-    pub row_id: String,
-}
-
-impl TryInto<QueryRowParams> for QueryRowPayload {
-    type Error = ErrorCode;
-
-    fn try_into(self) -> Result<QueryRowParams, Self::Error> {
-        let grid_id = NotEmptyUuid::parse(self.grid_id).map_err(|_| ErrorCode::GridIdIsEmpty)?;
-        let row_id = NotEmptyUuid::parse(self.row_id).map_err(|_| ErrorCode::RowIdIsEmpty)?;
-
-        Ok(QueryRowParams {
-            grid_id: grid_id.0,
-            row_id: row_id.0,
-        })
-    }
-}
-
-#[derive(ProtoBuf, Default)]
-pub struct CreateSelectOptionPayload {
-    #[pb(index = 1)]
-    pub option_name: String,
-
-    #[pb(index = 2)]
-    pub selected: bool,
-}
-
-pub struct CreateSelectOptionParams {
-    pub option_name: String,
-    pub selected: bool,
-}
-
-impl TryInto<CreateSelectOptionParams> for CreateSelectOptionPayload {
-    type Error = ErrorCode;
-
-    fn try_into(self) -> Result<CreateSelectOptionParams, Self::Error> {
-        let option_name = NotEmptyStr::parse(self.option_name).map_err(|_| ErrorCode::SelectOptionNameIsEmpty)?;
-        Ok(CreateSelectOptionParams {
-            option_name: option_name.0,
-            selected: self.selected,
-        })
-    }
-}

文件差異過大導致無法顯示
+ 161 - 1180
shared-lib/flowy-grid-data-model/src/protobuf/model/grid.rs


+ 3 - 0
shared-lib/flowy-grid-data-model/src/protobuf/model/mod.rs

@@ -4,5 +4,8 @@
 mod grid;
 pub use grid::*;
 
+mod type_option;
+pub use type_option::*;
+
 mod meta;
 pub use meta::*;

+ 978 - 0
shared-lib/flowy-grid-data-model/src/protobuf/model/type_option.rs

@@ -0,0 +1,978 @@
+// This file is generated by rust-protobuf 2.25.2. Do not edit
+// @generated
+
+// https://github.com/rust-lang/rust-clippy/issues/702
+#![allow(unknown_lints)]
+#![allow(clippy::all)]
+
+#![allow(unused_attributes)]
+#![cfg_attr(rustfmt, rustfmt::skip)]
+
+#![allow(box_pointers)]
+#![allow(dead_code)]
+#![allow(missing_docs)]
+#![allow(non_camel_case_types)]
+#![allow(non_snake_case)]
+#![allow(non_upper_case_globals)]
+#![allow(trivial_casts)]
+#![allow(unused_imports)]
+#![allow(unused_results)]
+//! Generated file from `type_option.proto`
+
+/// Generated files are compatible only with the same version
+/// of protobuf runtime.
+// const _PROTOBUF_VERSION_CHECK: () = ::protobuf::VERSION_2_25_2;
+
+#[derive(PartialEq,Clone,Default)]
+pub struct SelectOption {
+    // message fields
+    pub id: ::std::string::String,
+    pub name: ::std::string::String,
+    pub color: SelectOptionColor,
+    // special fields
+    pub unknown_fields: ::protobuf::UnknownFields,
+    pub cached_size: ::protobuf::CachedSize,
+}
+
+impl<'a> ::std::default::Default for &'a SelectOption {
+    fn default() -> &'a SelectOption {
+        <SelectOption as ::protobuf::Message>::default_instance()
+    }
+}
+
+impl SelectOption {
+    pub fn new() -> SelectOption {
+        ::std::default::Default::default()
+    }
+
+    // string id = 1;
+
+
+    pub fn get_id(&self) -> &str {
+        &self.id
+    }
+    pub fn clear_id(&mut self) {
+        self.id.clear();
+    }
+
+    // Param is passed by value, moved
+    pub fn set_id(&mut self, v: ::std::string::String) {
+        self.id = v;
+    }
+
+    // Mutable pointer to the field.
+    // If field is not initialized, it is initialized with default value first.
+    pub fn mut_id(&mut self) -> &mut ::std::string::String {
+        &mut self.id
+    }
+
+    // Take field
+    pub fn take_id(&mut self) -> ::std::string::String {
+        ::std::mem::replace(&mut self.id, ::std::string::String::new())
+    }
+
+    // string name = 2;
+
+
+    pub fn get_name(&self) -> &str {
+        &self.name
+    }
+    pub fn clear_name(&mut self) {
+        self.name.clear();
+    }
+
+    // Param is passed by value, moved
+    pub fn set_name(&mut self, v: ::std::string::String) {
+        self.name = v;
+    }
+
+    // Mutable pointer to the field.
+    // If field is not initialized, it is initialized with default value first.
+    pub fn mut_name(&mut self) -> &mut ::std::string::String {
+        &mut self.name
+    }
+
+    // Take field
+    pub fn take_name(&mut self) -> ::std::string::String {
+        ::std::mem::replace(&mut self.name, ::std::string::String::new())
+    }
+
+    // .SelectOptionColor color = 3;
+
+
+    pub fn get_color(&self) -> SelectOptionColor {
+        self.color
+    }
+    pub fn clear_color(&mut self) {
+        self.color = SelectOptionColor::Purple;
+    }
+
+    // Param is passed by value, moved
+    pub fn set_color(&mut self, v: SelectOptionColor) {
+        self.color = v;
+    }
+}
+
+impl ::protobuf::Message for SelectOption {
+    fn is_initialized(&self) -> bool {
+        true
+    }
+
+    fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::ProtobufResult<()> {
+        while !is.eof()? {
+            let (field_number, wire_type) = is.read_tag_unpack()?;
+            match field_number {
+                1 => {
+                    ::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.id)?;
+                },
+                2 => {
+                    ::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.name)?;
+                },
+                3 => {
+                    ::protobuf::rt::read_proto3_enum_with_unknown_fields_into(wire_type, is, &mut self.color, 3, &mut self.unknown_fields)?
+                },
+                _ => {
+                    ::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?;
+                },
+            };
+        }
+        ::std::result::Result::Ok(())
+    }
+
+    // Compute sizes of nested messages
+    #[allow(unused_variables)]
+    fn compute_size(&self) -> u32 {
+        let mut my_size = 0;
+        if !self.id.is_empty() {
+            my_size += ::protobuf::rt::string_size(1, &self.id);
+        }
+        if !self.name.is_empty() {
+            my_size += ::protobuf::rt::string_size(2, &self.name);
+        }
+        if self.color != SelectOptionColor::Purple {
+            my_size += ::protobuf::rt::enum_size(3, self.color);
+        }
+        my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields());
+        self.cached_size.set(my_size);
+        my_size
+    }
+
+    fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::ProtobufResult<()> {
+        if !self.id.is_empty() {
+            os.write_string(1, &self.id)?;
+        }
+        if !self.name.is_empty() {
+            os.write_string(2, &self.name)?;
+        }
+        if self.color != SelectOptionColor::Purple {
+            os.write_enum(3, ::protobuf::ProtobufEnum::value(&self.color))?;
+        }
+        os.write_unknown_fields(self.get_unknown_fields())?;
+        ::std::result::Result::Ok(())
+    }
+
+    fn get_cached_size(&self) -> u32 {
+        self.cached_size.get()
+    }
+
+    fn get_unknown_fields(&self) -> &::protobuf::UnknownFields {
+        &self.unknown_fields
+    }
+
+    fn mut_unknown_fields(&mut self) -> &mut ::protobuf::UnknownFields {
+        &mut self.unknown_fields
+    }
+
+    fn as_any(&self) -> &dyn (::std::any::Any) {
+        self as &dyn (::std::any::Any)
+    }
+    fn as_any_mut(&mut self) -> &mut dyn (::std::any::Any) {
+        self as &mut dyn (::std::any::Any)
+    }
+    fn into_any(self: ::std::boxed::Box<Self>) -> ::std::boxed::Box<dyn (::std::any::Any)> {
+        self
+    }
+
+    fn descriptor(&self) -> &'static ::protobuf::reflect::MessageDescriptor {
+        Self::descriptor_static()
+    }
+
+    fn new() -> SelectOption {
+        SelectOption::new()
+    }
+
+    fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor {
+        static descriptor: ::protobuf::rt::LazyV2<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::LazyV2::INIT;
+        descriptor.get(|| {
+            let mut fields = ::std::vec::Vec::new();
+            fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
+                "id",
+                |m: &SelectOption| { &m.id },
+                |m: &mut SelectOption| { &mut m.id },
+            ));
+            fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
+                "name",
+                |m: &SelectOption| { &m.name },
+                |m: &mut SelectOption| { &mut m.name },
+            ));
+            fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeEnum<SelectOptionColor>>(
+                "color",
+                |m: &SelectOption| { &m.color },
+                |m: &mut SelectOption| { &mut m.color },
+            ));
+            ::protobuf::reflect::MessageDescriptor::new_pb_name::<SelectOption>(
+                "SelectOption",
+                fields,
+                file_descriptor_proto()
+            )
+        })
+    }
+
+    fn default_instance() -> &'static SelectOption {
+        static instance: ::protobuf::rt::LazyV2<SelectOption> = ::protobuf::rt::LazyV2::INIT;
+        instance.get(SelectOption::new)
+    }
+}
+
+impl ::protobuf::Clear for SelectOption {
+    fn clear(&mut self) {
+        self.id.clear();
+        self.name.clear();
+        self.color = SelectOptionColor::Purple;
+        self.unknown_fields.clear();
+    }
+}
+
+impl ::std::fmt::Debug for SelectOption {
+    fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
+        ::protobuf::text_format::fmt(self, f)
+    }
+}
+
+impl ::protobuf::reflect::ProtobufValue for SelectOption {
+    fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
+        ::protobuf::reflect::ReflectValueRef::Message(self)
+    }
+}
+
+#[derive(PartialEq,Clone,Default)]
+pub struct SelectOptionCellChangesetPayload {
+    // message fields
+    pub grid_id: ::std::string::String,
+    pub row_id: ::std::string::String,
+    pub field_id: ::std::string::String,
+    // message oneof groups
+    pub one_of_insert_option_id: ::std::option::Option<SelectOptionCellChangesetPayload_oneof_one_of_insert_option_id>,
+    pub one_of_delete_option_id: ::std::option::Option<SelectOptionCellChangesetPayload_oneof_one_of_delete_option_id>,
+    // special fields
+    pub unknown_fields: ::protobuf::UnknownFields,
+    pub cached_size: ::protobuf::CachedSize,
+}
+
+impl<'a> ::std::default::Default for &'a SelectOptionCellChangesetPayload {
+    fn default() -> &'a SelectOptionCellChangesetPayload {
+        <SelectOptionCellChangesetPayload as ::protobuf::Message>::default_instance()
+    }
+}
+
+#[derive(Clone,PartialEq,Debug)]
+pub enum SelectOptionCellChangesetPayload_oneof_one_of_insert_option_id {
+    insert_option_id(::std::string::String),
+}
+
+#[derive(Clone,PartialEq,Debug)]
+pub enum SelectOptionCellChangesetPayload_oneof_one_of_delete_option_id {
+    delete_option_id(::std::string::String),
+}
+
+impl SelectOptionCellChangesetPayload {
+    pub fn new() -> SelectOptionCellChangesetPayload {
+        ::std::default::Default::default()
+    }
+
+    // string grid_id = 1;
+
+
+    pub fn get_grid_id(&self) -> &str {
+        &self.grid_id
+    }
+    pub fn clear_grid_id(&mut self) {
+        self.grid_id.clear();
+    }
+
+    // Param is passed by value, moved
+    pub fn set_grid_id(&mut self, v: ::std::string::String) {
+        self.grid_id = v;
+    }
+
+    // Mutable pointer to the field.
+    // If field is not initialized, it is initialized with default value first.
+    pub fn mut_grid_id(&mut self) -> &mut ::std::string::String {
+        &mut self.grid_id
+    }
+
+    // Take field
+    pub fn take_grid_id(&mut self) -> ::std::string::String {
+        ::std::mem::replace(&mut self.grid_id, ::std::string::String::new())
+    }
+
+    // string row_id = 2;
+
+
+    pub fn get_row_id(&self) -> &str {
+        &self.row_id
+    }
+    pub fn clear_row_id(&mut self) {
+        self.row_id.clear();
+    }
+
+    // Param is passed by value, moved
+    pub fn set_row_id(&mut self, v: ::std::string::String) {
+        self.row_id = v;
+    }
+
+    // Mutable pointer to the field.
+    // If field is not initialized, it is initialized with default value first.
+    pub fn mut_row_id(&mut self) -> &mut ::std::string::String {
+        &mut self.row_id
+    }
+
+    // Take field
+    pub fn take_row_id(&mut self) -> ::std::string::String {
+        ::std::mem::replace(&mut self.row_id, ::std::string::String::new())
+    }
+
+    // string field_id = 3;
+
+
+    pub fn get_field_id(&self) -> &str {
+        &self.field_id
+    }
+    pub fn clear_field_id(&mut self) {
+        self.field_id.clear();
+    }
+
+    // Param is passed by value, moved
+    pub fn set_field_id(&mut self, v: ::std::string::String) {
+        self.field_id = v;
+    }
+
+    // Mutable pointer to the field.
+    // If field is not initialized, it is initialized with default value first.
+    pub fn mut_field_id(&mut self) -> &mut ::std::string::String {
+        &mut self.field_id
+    }
+
+    // Take field
+    pub fn take_field_id(&mut self) -> ::std::string::String {
+        ::std::mem::replace(&mut self.field_id, ::std::string::String::new())
+    }
+
+    // string insert_option_id = 4;
+
+
+    pub fn get_insert_option_id(&self) -> &str {
+        match self.one_of_insert_option_id {
+            ::std::option::Option::Some(SelectOptionCellChangesetPayload_oneof_one_of_insert_option_id::insert_option_id(ref v)) => v,
+            _ => "",
+        }
+    }
+    pub fn clear_insert_option_id(&mut self) {
+        self.one_of_insert_option_id = ::std::option::Option::None;
+    }
+
+    pub fn has_insert_option_id(&self) -> bool {
+        match self.one_of_insert_option_id {
+            ::std::option::Option::Some(SelectOptionCellChangesetPayload_oneof_one_of_insert_option_id::insert_option_id(..)) => true,
+            _ => false,
+        }
+    }
+
+    // Param is passed by value, moved
+    pub fn set_insert_option_id(&mut self, v: ::std::string::String) {
+        self.one_of_insert_option_id = ::std::option::Option::Some(SelectOptionCellChangesetPayload_oneof_one_of_insert_option_id::insert_option_id(v))
+    }
+
+    // Mutable pointer to the field.
+    pub fn mut_insert_option_id(&mut self) -> &mut ::std::string::String {
+        if let ::std::option::Option::Some(SelectOptionCellChangesetPayload_oneof_one_of_insert_option_id::insert_option_id(_)) = self.one_of_insert_option_id {
+        } else {
+            self.one_of_insert_option_id = ::std::option::Option::Some(SelectOptionCellChangesetPayload_oneof_one_of_insert_option_id::insert_option_id(::std::string::String::new()));
+        }
+        match self.one_of_insert_option_id {
+            ::std::option::Option::Some(SelectOptionCellChangesetPayload_oneof_one_of_insert_option_id::insert_option_id(ref mut v)) => v,
+            _ => panic!(),
+        }
+    }
+
+    // Take field
+    pub fn take_insert_option_id(&mut self) -> ::std::string::String {
+        if self.has_insert_option_id() {
+            match self.one_of_insert_option_id.take() {
+                ::std::option::Option::Some(SelectOptionCellChangesetPayload_oneof_one_of_insert_option_id::insert_option_id(v)) => v,
+                _ => panic!(),
+            }
+        } else {
+            ::std::string::String::new()
+        }
+    }
+
+    // string delete_option_id = 5;
+
+
+    pub fn get_delete_option_id(&self) -> &str {
+        match self.one_of_delete_option_id {
+            ::std::option::Option::Some(SelectOptionCellChangesetPayload_oneof_one_of_delete_option_id::delete_option_id(ref v)) => v,
+            _ => "",
+        }
+    }
+    pub fn clear_delete_option_id(&mut self) {
+        self.one_of_delete_option_id = ::std::option::Option::None;
+    }
+
+    pub fn has_delete_option_id(&self) -> bool {
+        match self.one_of_delete_option_id {
+            ::std::option::Option::Some(SelectOptionCellChangesetPayload_oneof_one_of_delete_option_id::delete_option_id(..)) => true,
+            _ => false,
+        }
+    }
+
+    // Param is passed by value, moved
+    pub fn set_delete_option_id(&mut self, v: ::std::string::String) {
+        self.one_of_delete_option_id = ::std::option::Option::Some(SelectOptionCellChangesetPayload_oneof_one_of_delete_option_id::delete_option_id(v))
+    }
+
+    // Mutable pointer to the field.
+    pub fn mut_delete_option_id(&mut self) -> &mut ::std::string::String {
+        if let ::std::option::Option::Some(SelectOptionCellChangesetPayload_oneof_one_of_delete_option_id::delete_option_id(_)) = self.one_of_delete_option_id {
+        } else {
+            self.one_of_delete_option_id = ::std::option::Option::Some(SelectOptionCellChangesetPayload_oneof_one_of_delete_option_id::delete_option_id(::std::string::String::new()));
+        }
+        match self.one_of_delete_option_id {
+            ::std::option::Option::Some(SelectOptionCellChangesetPayload_oneof_one_of_delete_option_id::delete_option_id(ref mut v)) => v,
+            _ => panic!(),
+        }
+    }
+
+    // Take field
+    pub fn take_delete_option_id(&mut self) -> ::std::string::String {
+        if self.has_delete_option_id() {
+            match self.one_of_delete_option_id.take() {
+                ::std::option::Option::Some(SelectOptionCellChangesetPayload_oneof_one_of_delete_option_id::delete_option_id(v)) => v,
+                _ => panic!(),
+            }
+        } else {
+            ::std::string::String::new()
+        }
+    }
+}
+
+impl ::protobuf::Message for SelectOptionCellChangesetPayload {
+    fn is_initialized(&self) -> bool {
+        true
+    }
+
+    fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::ProtobufResult<()> {
+        while !is.eof()? {
+            let (field_number, wire_type) = is.read_tag_unpack()?;
+            match field_number {
+                1 => {
+                    ::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.grid_id)?;
+                },
+                2 => {
+                    ::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.row_id)?;
+                },
+                3 => {
+                    ::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.field_id)?;
+                },
+                4 => {
+                    if wire_type != ::protobuf::wire_format::WireTypeLengthDelimited {
+                        return ::std::result::Result::Err(::protobuf::rt::unexpected_wire_type(wire_type));
+                    }
+                    self.one_of_insert_option_id = ::std::option::Option::Some(SelectOptionCellChangesetPayload_oneof_one_of_insert_option_id::insert_option_id(is.read_string()?));
+                },
+                5 => {
+                    if wire_type != ::protobuf::wire_format::WireTypeLengthDelimited {
+                        return ::std::result::Result::Err(::protobuf::rt::unexpected_wire_type(wire_type));
+                    }
+                    self.one_of_delete_option_id = ::std::option::Option::Some(SelectOptionCellChangesetPayload_oneof_one_of_delete_option_id::delete_option_id(is.read_string()?));
+                },
+                _ => {
+                    ::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?;
+                },
+            };
+        }
+        ::std::result::Result::Ok(())
+    }
+
+    // Compute sizes of nested messages
+    #[allow(unused_variables)]
+    fn compute_size(&self) -> u32 {
+        let mut my_size = 0;
+        if !self.grid_id.is_empty() {
+            my_size += ::protobuf::rt::string_size(1, &self.grid_id);
+        }
+        if !self.row_id.is_empty() {
+            my_size += ::protobuf::rt::string_size(2, &self.row_id);
+        }
+        if !self.field_id.is_empty() {
+            my_size += ::protobuf::rt::string_size(3, &self.field_id);
+        }
+        if let ::std::option::Option::Some(ref v) = self.one_of_insert_option_id {
+            match v {
+                &SelectOptionCellChangesetPayload_oneof_one_of_insert_option_id::insert_option_id(ref v) => {
+                    my_size += ::protobuf::rt::string_size(4, &v);
+                },
+            };
+        }
+        if let ::std::option::Option::Some(ref v) = self.one_of_delete_option_id {
+            match v {
+                &SelectOptionCellChangesetPayload_oneof_one_of_delete_option_id::delete_option_id(ref v) => {
+                    my_size += ::protobuf::rt::string_size(5, &v);
+                },
+            };
+        }
+        my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields());
+        self.cached_size.set(my_size);
+        my_size
+    }
+
+    fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::ProtobufResult<()> {
+        if !self.grid_id.is_empty() {
+            os.write_string(1, &self.grid_id)?;
+        }
+        if !self.row_id.is_empty() {
+            os.write_string(2, &self.row_id)?;
+        }
+        if !self.field_id.is_empty() {
+            os.write_string(3, &self.field_id)?;
+        }
+        if let ::std::option::Option::Some(ref v) = self.one_of_insert_option_id {
+            match v {
+                &SelectOptionCellChangesetPayload_oneof_one_of_insert_option_id::insert_option_id(ref v) => {
+                    os.write_string(4, v)?;
+                },
+            };
+        }
+        if let ::std::option::Option::Some(ref v) = self.one_of_delete_option_id {
+            match v {
+                &SelectOptionCellChangesetPayload_oneof_one_of_delete_option_id::delete_option_id(ref v) => {
+                    os.write_string(5, v)?;
+                },
+            };
+        }
+        os.write_unknown_fields(self.get_unknown_fields())?;
+        ::std::result::Result::Ok(())
+    }
+
+    fn get_cached_size(&self) -> u32 {
+        self.cached_size.get()
+    }
+
+    fn get_unknown_fields(&self) -> &::protobuf::UnknownFields {
+        &self.unknown_fields
+    }
+
+    fn mut_unknown_fields(&mut self) -> &mut ::protobuf::UnknownFields {
+        &mut self.unknown_fields
+    }
+
+    fn as_any(&self) -> &dyn (::std::any::Any) {
+        self as &dyn (::std::any::Any)
+    }
+    fn as_any_mut(&mut self) -> &mut dyn (::std::any::Any) {
+        self as &mut dyn (::std::any::Any)
+    }
+    fn into_any(self: ::std::boxed::Box<Self>) -> ::std::boxed::Box<dyn (::std::any::Any)> {
+        self
+    }
+
+    fn descriptor(&self) -> &'static ::protobuf::reflect::MessageDescriptor {
+        Self::descriptor_static()
+    }
+
+    fn new() -> SelectOptionCellChangesetPayload {
+        SelectOptionCellChangesetPayload::new()
+    }
+
+    fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor {
+        static descriptor: ::protobuf::rt::LazyV2<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::LazyV2::INIT;
+        descriptor.get(|| {
+            let mut fields = ::std::vec::Vec::new();
+            fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
+                "grid_id",
+                |m: &SelectOptionCellChangesetPayload| { &m.grid_id },
+                |m: &mut SelectOptionCellChangesetPayload| { &mut m.grid_id },
+            ));
+            fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
+                "row_id",
+                |m: &SelectOptionCellChangesetPayload| { &m.row_id },
+                |m: &mut SelectOptionCellChangesetPayload| { &mut m.row_id },
+            ));
+            fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
+                "field_id",
+                |m: &SelectOptionCellChangesetPayload| { &m.field_id },
+                |m: &mut SelectOptionCellChangesetPayload| { &mut m.field_id },
+            ));
+            fields.push(::protobuf::reflect::accessor::make_singular_string_accessor::<_>(
+                "insert_option_id",
+                SelectOptionCellChangesetPayload::has_insert_option_id,
+                SelectOptionCellChangesetPayload::get_insert_option_id,
+            ));
+            fields.push(::protobuf::reflect::accessor::make_singular_string_accessor::<_>(
+                "delete_option_id",
+                SelectOptionCellChangesetPayload::has_delete_option_id,
+                SelectOptionCellChangesetPayload::get_delete_option_id,
+            ));
+            ::protobuf::reflect::MessageDescriptor::new_pb_name::<SelectOptionCellChangesetPayload>(
+                "SelectOptionCellChangesetPayload",
+                fields,
+                file_descriptor_proto()
+            )
+        })
+    }
+
+    fn default_instance() -> &'static SelectOptionCellChangesetPayload {
+        static instance: ::protobuf::rt::LazyV2<SelectOptionCellChangesetPayload> = ::protobuf::rt::LazyV2::INIT;
+        instance.get(SelectOptionCellChangesetPayload::new)
+    }
+}
+
+impl ::protobuf::Clear for SelectOptionCellChangesetPayload {
+    fn clear(&mut self) {
+        self.grid_id.clear();
+        self.row_id.clear();
+        self.field_id.clear();
+        self.one_of_insert_option_id = ::std::option::Option::None;
+        self.one_of_delete_option_id = ::std::option::Option::None;
+        self.unknown_fields.clear();
+    }
+}
+
+impl ::std::fmt::Debug for SelectOptionCellChangesetPayload {
+    fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
+        ::protobuf::text_format::fmt(self, f)
+    }
+}
+
+impl ::protobuf::reflect::ProtobufValue for SelectOptionCellChangesetPayload {
+    fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
+        ::protobuf::reflect::ReflectValueRef::Message(self)
+    }
+}
+
+#[derive(PartialEq,Clone,Default)]
+pub struct SelectOptionContext {
+    // message fields
+    pub options: ::protobuf::RepeatedField<SelectOption>,
+    pub select_options: ::protobuf::RepeatedField<SelectOption>,
+    // special fields
+    pub unknown_fields: ::protobuf::UnknownFields,
+    pub cached_size: ::protobuf::CachedSize,
+}
+
+impl<'a> ::std::default::Default for &'a SelectOptionContext {
+    fn default() -> &'a SelectOptionContext {
+        <SelectOptionContext as ::protobuf::Message>::default_instance()
+    }
+}
+
+impl SelectOptionContext {
+    pub fn new() -> SelectOptionContext {
+        ::std::default::Default::default()
+    }
+
+    // repeated .SelectOption options = 1;
+
+
+    pub fn get_options(&self) -> &[SelectOption] {
+        &self.options
+    }
+    pub fn clear_options(&mut self) {
+        self.options.clear();
+    }
+
+    // Param is passed by value, moved
+    pub fn set_options(&mut self, v: ::protobuf::RepeatedField<SelectOption>) {
+        self.options = v;
+    }
+
+    // Mutable pointer to the field.
+    pub fn mut_options(&mut self) -> &mut ::protobuf::RepeatedField<SelectOption> {
+        &mut self.options
+    }
+
+    // Take field
+    pub fn take_options(&mut self) -> ::protobuf::RepeatedField<SelectOption> {
+        ::std::mem::replace(&mut self.options, ::protobuf::RepeatedField::new())
+    }
+
+    // repeated .SelectOption select_options = 2;
+
+
+    pub fn get_select_options(&self) -> &[SelectOption] {
+        &self.select_options
+    }
+    pub fn clear_select_options(&mut self) {
+        self.select_options.clear();
+    }
+
+    // Param is passed by value, moved
+    pub fn set_select_options(&mut self, v: ::protobuf::RepeatedField<SelectOption>) {
+        self.select_options = v;
+    }
+
+    // Mutable pointer to the field.
+    pub fn mut_select_options(&mut self) -> &mut ::protobuf::RepeatedField<SelectOption> {
+        &mut self.select_options
+    }
+
+    // Take field
+    pub fn take_select_options(&mut self) -> ::protobuf::RepeatedField<SelectOption> {
+        ::std::mem::replace(&mut self.select_options, ::protobuf::RepeatedField::new())
+    }
+}
+
+impl ::protobuf::Message for SelectOptionContext {
+    fn is_initialized(&self) -> bool {
+        for v in &self.options {
+            if !v.is_initialized() {
+                return false;
+            }
+        };
+        for v in &self.select_options {
+            if !v.is_initialized() {
+                return false;
+            }
+        };
+        true
+    }
+
+    fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::ProtobufResult<()> {
+        while !is.eof()? {
+            let (field_number, wire_type) = is.read_tag_unpack()?;
+            match field_number {
+                1 => {
+                    ::protobuf::rt::read_repeated_message_into(wire_type, is, &mut self.options)?;
+                },
+                2 => {
+                    ::protobuf::rt::read_repeated_message_into(wire_type, is, &mut self.select_options)?;
+                },
+                _ => {
+                    ::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?;
+                },
+            };
+        }
+        ::std::result::Result::Ok(())
+    }
+
+    // Compute sizes of nested messages
+    #[allow(unused_variables)]
+    fn compute_size(&self) -> u32 {
+        let mut my_size = 0;
+        for value in &self.options {
+            let len = value.compute_size();
+            my_size += 1 + ::protobuf::rt::compute_raw_varint32_size(len) + len;
+        };
+        for value in &self.select_options {
+            let len = value.compute_size();
+            my_size += 1 + ::protobuf::rt::compute_raw_varint32_size(len) + len;
+        };
+        my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields());
+        self.cached_size.set(my_size);
+        my_size
+    }
+
+    fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::ProtobufResult<()> {
+        for v in &self.options {
+            os.write_tag(1, ::protobuf::wire_format::WireTypeLengthDelimited)?;
+            os.write_raw_varint32(v.get_cached_size())?;
+            v.write_to_with_cached_sizes(os)?;
+        };
+        for v in &self.select_options {
+            os.write_tag(2, ::protobuf::wire_format::WireTypeLengthDelimited)?;
+            os.write_raw_varint32(v.get_cached_size())?;
+            v.write_to_with_cached_sizes(os)?;
+        };
+        os.write_unknown_fields(self.get_unknown_fields())?;
+        ::std::result::Result::Ok(())
+    }
+
+    fn get_cached_size(&self) -> u32 {
+        self.cached_size.get()
+    }
+
+    fn get_unknown_fields(&self) -> &::protobuf::UnknownFields {
+        &self.unknown_fields
+    }
+
+    fn mut_unknown_fields(&mut self) -> &mut ::protobuf::UnknownFields {
+        &mut self.unknown_fields
+    }
+
+    fn as_any(&self) -> &dyn (::std::any::Any) {
+        self as &dyn (::std::any::Any)
+    }
+    fn as_any_mut(&mut self) -> &mut dyn (::std::any::Any) {
+        self as &mut dyn (::std::any::Any)
+    }
+    fn into_any(self: ::std::boxed::Box<Self>) -> ::std::boxed::Box<dyn (::std::any::Any)> {
+        self
+    }
+
+    fn descriptor(&self) -> &'static ::protobuf::reflect::MessageDescriptor {
+        Self::descriptor_static()
+    }
+
+    fn new() -> SelectOptionContext {
+        SelectOptionContext::new()
+    }
+
+    fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor {
+        static descriptor: ::protobuf::rt::LazyV2<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::LazyV2::INIT;
+        descriptor.get(|| {
+            let mut fields = ::std::vec::Vec::new();
+            fields.push(::protobuf::reflect::accessor::make_repeated_field_accessor::<_, ::protobuf::types::ProtobufTypeMessage<SelectOption>>(
+                "options",
+                |m: &SelectOptionContext| { &m.options },
+                |m: &mut SelectOptionContext| { &mut m.options },
+            ));
+            fields.push(::protobuf::reflect::accessor::make_repeated_field_accessor::<_, ::protobuf::types::ProtobufTypeMessage<SelectOption>>(
+                "select_options",
+                |m: &SelectOptionContext| { &m.select_options },
+                |m: &mut SelectOptionContext| { &mut m.select_options },
+            ));
+            ::protobuf::reflect::MessageDescriptor::new_pb_name::<SelectOptionContext>(
+                "SelectOptionContext",
+                fields,
+                file_descriptor_proto()
+            )
+        })
+    }
+
+    fn default_instance() -> &'static SelectOptionContext {
+        static instance: ::protobuf::rt::LazyV2<SelectOptionContext> = ::protobuf::rt::LazyV2::INIT;
+        instance.get(SelectOptionContext::new)
+    }
+}
+
+impl ::protobuf::Clear for SelectOptionContext {
+    fn clear(&mut self) {
+        self.options.clear();
+        self.select_options.clear();
+        self.unknown_fields.clear();
+    }
+}
+
+impl ::std::fmt::Debug for SelectOptionContext {
+    fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
+        ::protobuf::text_format::fmt(self, f)
+    }
+}
+
+impl ::protobuf::reflect::ProtobufValue for SelectOptionContext {
+    fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
+        ::protobuf::reflect::ReflectValueRef::Message(self)
+    }
+}
+
+#[derive(Clone,PartialEq,Eq,Debug,Hash)]
+pub enum SelectOptionColor {
+    Purple = 0,
+    Pink = 1,
+    LightPink = 2,
+    Orange = 3,
+    Yellow = 4,
+    Lime = 5,
+    Green = 6,
+    Aqua = 7,
+    Blue = 8,
+}
+
+impl ::protobuf::ProtobufEnum for SelectOptionColor {
+    fn value(&self) -> i32 {
+        *self as i32
+    }
+
+    fn from_i32(value: i32) -> ::std::option::Option<SelectOptionColor> {
+        match value {
+            0 => ::std::option::Option::Some(SelectOptionColor::Purple),
+            1 => ::std::option::Option::Some(SelectOptionColor::Pink),
+            2 => ::std::option::Option::Some(SelectOptionColor::LightPink),
+            3 => ::std::option::Option::Some(SelectOptionColor::Orange),
+            4 => ::std::option::Option::Some(SelectOptionColor::Yellow),
+            5 => ::std::option::Option::Some(SelectOptionColor::Lime),
+            6 => ::std::option::Option::Some(SelectOptionColor::Green),
+            7 => ::std::option::Option::Some(SelectOptionColor::Aqua),
+            8 => ::std::option::Option::Some(SelectOptionColor::Blue),
+            _ => ::std::option::Option::None
+        }
+    }
+
+    fn values() -> &'static [Self] {
+        static values: &'static [SelectOptionColor] = &[
+            SelectOptionColor::Purple,
+            SelectOptionColor::Pink,
+            SelectOptionColor::LightPink,
+            SelectOptionColor::Orange,
+            SelectOptionColor::Yellow,
+            SelectOptionColor::Lime,
+            SelectOptionColor::Green,
+            SelectOptionColor::Aqua,
+            SelectOptionColor::Blue,
+        ];
+        values
+    }
+
+    fn enum_descriptor_static() -> &'static ::protobuf::reflect::EnumDescriptor {
+        static descriptor: ::protobuf::rt::LazyV2<::protobuf::reflect::EnumDescriptor> = ::protobuf::rt::LazyV2::INIT;
+        descriptor.get(|| {
+            ::protobuf::reflect::EnumDescriptor::new_pb_name::<SelectOptionColor>("SelectOptionColor", file_descriptor_proto())
+        })
+    }
+}
+
+impl ::std::marker::Copy for SelectOptionColor {
+}
+
+impl ::std::default::Default for SelectOptionColor {
+    fn default() -> Self {
+        SelectOptionColor::Purple
+    }
+}
+
+impl ::protobuf::reflect::ProtobufValue for SelectOptionColor {
+    fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
+        ::protobuf::reflect::ReflectValueRef::Enum(::protobuf::ProtobufEnum::descriptor(self))
+    }
+}
+
+static file_descriptor_proto_data: &'static [u8] = b"\
+    \n\x11type_option.proto\"\\\n\x0cSelectOption\x12\x0e\n\x02id\x18\x01\
+    \x20\x01(\tR\x02id\x12\x12\n\x04name\x18\x02\x20\x01(\tR\x04name\x12(\n\
+    \x05color\x18\x03\x20\x01(\x0e2\x12.SelectOptionColorR\x05color\"\xfb\
+    \x01\n\x20SelectOptionCellChangesetPayload\x12\x17\n\x07grid_id\x18\x01\
+    \x20\x01(\tR\x06gridId\x12\x15\n\x06row_id\x18\x02\x20\x01(\tR\x05rowId\
+    \x12\x19\n\x08field_id\x18\x03\x20\x01(\tR\x07fieldId\x12*\n\x10insert_o\
+    ption_id\x18\x04\x20\x01(\tH\0R\x0einsertOptionId\x12*\n\x10delete_optio\
+    n_id\x18\x05\x20\x01(\tH\x01R\x0edeleteOptionIdB\x19\n\x17one_of_insert_\
+    option_idB\x19\n\x17one_of_delete_option_id\"t\n\x13SelectOptionContext\
+    \x12'\n\x07options\x18\x01\x20\x03(\x0b2\r.SelectOptionR\x07options\x124\
+    \n\x0eselect_options\x18\x02\x20\x03(\x0b2\r.SelectOptionR\rselectOption\
+    s*y\n\x11SelectOptionColor\x12\n\n\x06Purple\x10\0\x12\x08\n\x04Pink\x10\
+    \x01\x12\r\n\tLightPink\x10\x02\x12\n\n\x06Orange\x10\x03\x12\n\n\x06Yel\
+    low\x10\x04\x12\x08\n\x04Lime\x10\x05\x12\t\n\x05Green\x10\x06\x12\x08\n\
+    \x04Aqua\x10\x07\x12\x08\n\x04Blue\x10\x08b\x06proto3\
+";
+
+static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;
+
+fn parse_descriptor_proto() -> ::protobuf::descriptor::FileDescriptorProto {
+    ::protobuf::Message::parse_from_bytes(file_descriptor_proto_data).unwrap()
+}
+
+pub fn file_descriptor_proto() -> &'static ::protobuf::descriptor::FileDescriptorProto {
+    file_descriptor_proto_lazy.get(|| {
+        parse_descriptor_proto()
+    })
+}

+ 0 - 17
shared-lib/flowy-grid-data-model/src/protobuf/proto/grid.proto

@@ -15,10 +15,6 @@ message Field {
     bool visibility = 6;
     int32 width = 7;
 }
-message FieldIdentifierPayload {
-    string field_id = 1;
-    string grid_id = 2;
-}
 message FieldOrder {
     string field_id = 1;
 }
@@ -70,11 +66,6 @@ message Cell {
     string field_id = 1;
     string content = 2;
 }
-message CellIdentifierPayload {
-    string grid_id = 1;
-    string field_id = 2;
-    string row_id = 3;
-}
 message CellNotificationData {
     string grid_id = 1;
     string field_id = 2;
@@ -111,11 +102,3 @@ message QueryGridBlocksPayload {
     string grid_id = 1;
     repeated GridBlockOrder block_orders = 2;
 }
-message QueryRowPayload {
-    string grid_id = 1;
-    string row_id = 3;
-}
-message CreateSelectOptionPayload {
-    string option_name = 1;
-    bool selected = 2;
-}

+ 29 - 0
shared-lib/flowy-grid-data-model/src/protobuf/proto/type_option.proto

@@ -0,0 +1,29 @@
+syntax = "proto3";
+
+message SelectOption {
+    string id = 1;
+    string name = 2;
+    SelectOptionColor color = 3;
+}
+message SelectOptionCellChangesetPayload {
+    string grid_id = 1;
+    string row_id = 2;
+    string field_id = 3;
+    oneof one_of_insert_option_id { string insert_option_id = 4; };
+    oneof one_of_delete_option_id { string delete_option_id = 5; };
+}
+message SelectOptionContext {
+    repeated SelectOption options = 1;
+    repeated SelectOption select_options = 2;
+}
+enum SelectOptionColor {
+    Purple = 0;
+    Pink = 1;
+    LightPink = 2;
+    Orange = 3;
+    Yellow = 4;
+    Lime = 5;
+    Green = 6;
+    Aqua = 7;
+    Blue = 8;
+}

+ 70 - 43
shared-lib/flowy-sync/src/client_grid/grid_meta_pad.rs

@@ -20,7 +20,7 @@ pub struct GridMetaPad {
     pub(crate) delta: GridMetaDelta,
 }
 
-pub trait TypeOptionDataDeserializer {
+pub trait JsonDeserializer {
     fn deserialize(&self, type_option_data: Vec<u8>) -> CollaborateResult<String>;
 }
 
@@ -47,47 +47,55 @@ impl GridMetaPad {
         new_field_meta: FieldMeta,
         start_field_id: Option<String>,
     ) -> CollaborateResult<Option<GridChangeset>> {
-        self.modify_grid(|grid| {
+        self.modify_grid(|grid_meta| {
             // Check if the field exists or not
-            if grid.fields.iter().any(|field_meta| field_meta.id == new_field_meta.id) {
+            if grid_meta
+                .fields
+                .iter()
+                .any(|field_meta| field_meta.id == new_field_meta.id)
+            {
                 tracing::error!("Duplicate grid field");
                 return Ok(None);
             }
 
             let insert_index = match start_field_id {
                 None => None,
-                Some(start_field_id) => grid.fields.iter().position(|field| field.id == start_field_id),
+                Some(start_field_id) => grid_meta.fields.iter().position(|field| field.id == start_field_id),
             };
 
             match insert_index {
-                None => grid.fields.push(new_field_meta),
-                Some(index) => grid.fields.insert(index, new_field_meta),
+                None => grid_meta.fields.push(new_field_meta),
+                Some(index) => grid_meta.fields.insert(index, new_field_meta),
             }
             Ok(Some(()))
         })
     }
 
     pub fn delete_field(&mut self, field_id: &str) -> CollaborateResult<Option<GridChangeset>> {
-        self.modify_grid(|grid| match grid.fields.iter().position(|field| field.id == field_id) {
-            None => Ok(None),
-            Some(index) => {
-                grid.fields.remove(index);
-                Ok(Some(()))
-            }
-        })
+        self.modify_grid(
+            |grid_meta| match grid_meta.fields.iter().position(|field| field.id == field_id) {
+                None => Ok(None),
+                Some(index) => {
+                    grid_meta.fields.remove(index);
+                    Ok(Some(()))
+                }
+            },
+        )
     }
 
     pub fn duplicate_field(&mut self, field_id: &str) -> CollaborateResult<Option<GridChangeset>> {
-        self.modify_grid(|grid| match grid.fields.iter().position(|field| field.id == field_id) {
-            None => Ok(None),
-            Some(index) => {
-                let mut duplicate_field_meta = grid.fields[index].clone();
-                duplicate_field_meta.id = uuid();
-                duplicate_field_meta.name = format!("{} (copy)", duplicate_field_meta.name);
-                grid.fields.insert(index + 1, duplicate_field_meta);
-                Ok(Some(()))
-            }
-        })
+        self.modify_grid(
+            |grid_meta| match grid_meta.fields.iter().position(|field| field.id == field_id) {
+                None => Ok(None),
+                Some(index) => {
+                    let mut duplicate_field_meta = grid_meta.fields[index].clone();
+                    duplicate_field_meta.id = uuid();
+                    duplicate_field_meta.name = format!("{} (copy)", duplicate_field_meta.name);
+                    grid_meta.fields.insert(index + 1, duplicate_field_meta);
+                    Ok(Some(()))
+                }
+            },
+        )
     }
 
     pub fn switch_to_field<B>(
@@ -99,9 +107,9 @@ impl GridMetaPad {
     where
         B: FnOnce(&FieldType) -> String,
     {
-        self.modify_grid(|grid| {
+        self.modify_grid(|grid_meta| {
             //
-            match grid.fields.iter_mut().find(|field_meta| field_meta.id == field_id) {
+            match grid_meta.fields.iter_mut().find(|field_meta| field_meta.id == field_id) {
                 None => {
                     tracing::warn!("Can not find the field with id: {}", field_id);
                     Ok(None)
@@ -119,7 +127,7 @@ impl GridMetaPad {
         })
     }
 
-    pub fn update_field<T: TypeOptionDataDeserializer>(
+    pub fn update_field<T: JsonDeserializer>(
         &mut self,
         changeset: FieldChangesetParams,
         deserializer: T,
@@ -178,6 +186,19 @@ impl GridMetaPad {
         self.grid_meta.fields.iter().find(|field| field.id == field_id)
     }
 
+    pub fn replace_field(&mut self, field_meta: FieldMeta) -> CollaborateResult<Option<GridChangeset>> {
+        self.modify_grid(
+            |grid_meta| match grid_meta.fields.iter().position(|field| field.id == field_meta.id) {
+                None => Ok(None),
+                Some(index) => {
+                    grid_meta.fields.remove(index);
+                    grid_meta.fields.insert(index, field_meta);
+                    Ok(Some(()))
+                }
+            },
+        )
+    }
+
     pub fn contain_field(&self, field_id: &str) -> bool {
         self.grid_meta.fields.iter().any(|field| field.id == field_id)
     }
@@ -213,13 +234,13 @@ impl GridMetaPad {
     }
 
     pub fn create_block_meta(&mut self, block: GridBlockMeta) -> CollaborateResult<Option<GridChangeset>> {
-        self.modify_grid(|grid| {
-            if grid.block_metas.iter().any(|b| b.block_id == block.block_id) {
+        self.modify_grid(|grid_meta| {
+            if grid_meta.block_metas.iter().any(|b| b.block_id == block.block_id) {
                 tracing::warn!("Duplicate grid block");
                 Ok(None)
             } else {
-                match grid.block_metas.last() {
-                    None => grid.block_metas.push(block),
+                match grid_meta.block_metas.last() {
+                    None => grid_meta.block_metas.push(block),
                     Some(last_block) => {
                         if last_block.start_row_index > block.start_row_index
                             && last_block.len() > block.start_row_index
@@ -227,7 +248,7 @@ impl GridMetaPad {
                             let msg = "GridBlock's start_row_index should be greater than the last_block's start_row_index and its len".to_string();
                             return Err(CollaborateError::internal().context(msg))
                         }
-                        grid.block_metas.push(block);
+                        grid_meta.block_metas.push(block);
                     }
                 }
                 Ok(Some(()))
@@ -299,28 +320,34 @@ impl GridMetaPad {
     where
         F: FnOnce(&mut GridBlockMeta) -> CollaborateResult<Option<()>>,
     {
-        self.modify_grid(
-            |grid| match grid.block_metas.iter().position(|block| block.block_id == block_id) {
+        self.modify_grid(|grid_meta| {
+            match grid_meta
+                .block_metas
+                .iter()
+                .position(|block| block.block_id == block_id)
+            {
                 None => {
                     tracing::warn!("[GridMetaPad]: Can't find any block with id: {}", block_id);
                     Ok(None)
                 }
-                Some(index) => f(&mut grid.block_metas[index]),
-            },
-        )
+                Some(index) => f(&mut grid_meta.block_metas[index]),
+            }
+        })
     }
 
     pub fn modify_field<F>(&mut self, field_id: &str, f: F) -> CollaborateResult<Option<GridChangeset>>
     where
         F: FnOnce(&mut FieldMeta) -> CollaborateResult<Option<()>>,
     {
-        self.modify_grid(|grid| match grid.fields.iter().position(|field| field.id == field_id) {
-            None => {
-                tracing::warn!("[GridMetaPad]: Can't find any field with id: {}", field_id);
-                Ok(None)
-            }
-            Some(index) => f(&mut grid.fields[index]),
-        })
+        self.modify_grid(
+            |grid_meta| match grid_meta.fields.iter().position(|field| field.id == field_id) {
+                None => {
+                    tracing::warn!("[GridMetaPad]: Can't find any field with id: {}", field_id);
+                    Ok(None)
+                }
+                Some(index) => f(&mut grid_meta.fields[index]),
+            },
+        )
     }
 }
 

部分文件因文件數量過多而無法顯示