瀏覽代碼

fix: fix some bugs

appflowy 3 年之前
父節點
當前提交
3749168861
共有 35 個文件被更改,包括 666 次插入355 次删除
  1. 2 1
      frontend/app_flowy/assets/translations/en.json
  2. 7 6
      frontend/app_flowy/lib/startup/deps_resolver.dart
  3. 0 98
      frontend/app_flowy/lib/workspace/application/grid/field/create_field_bloc.dart
  4. 63 45
      frontend/app_flowy/lib/workspace/application/grid/field/edit_field_bloc.dart
  5. 38 2
      frontend/app_flowy/lib/workspace/application/grid/field/field_service.dart
  6. 80 0
      frontend/app_flowy/lib/workspace/application/grid/field/grid_field_bloc.dart
  7. 12 16
      frontend/app_flowy/lib/workspace/application/grid/field/switch_field_type_bloc.dart
  8. 1 1
      frontend/app_flowy/lib/workspace/application/grid/prelude.dart
  9. 0 0
      frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_list.dart
  10. 4 4
      frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_operation_list.dart
  11. 15 13
      frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_switcher.dart
  12. 103 0
      frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/grid_field_action_sheet.dart
  13. 29 23
      frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/grid_field_editor.dart
  14. 6 2
      frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/grid_header.dart
  15. 8 2
      frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/grid_header_cell.dart
  16. 1 1
      frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/date.dart
  17. 1 1
      frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/multi_select.dart
  18. 1 1
      frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/number.dart
  19. 1 1
      frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/option_pannel.dart
  20. 1 1
      frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/single_select.dart
  21. 1 1
      frontend/app_flowy/packages/flowy_infra_ui/lib/src/flowy_overlay/flowy_overlay.dart
  22. 4 4
      frontend/app_flowy/packages/flowy_sdk/lib/dispatch/dart_event/flowy-grid/dart_event.dart
  23. 45 18
      frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/grid.pb.dart
  24. 10 6
      frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/grid.pbjson.dart
  25. 2 2
      frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/event_map.pbenum.dart
  26. 2 2
      frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/event_map.pbjson.dart
  27. 20 5
      frontend/rust-lib/flowy-grid/src/event_handler.rs
  28. 3 3
      frontend/rust-lib/flowy-grid/src/event_map.rs
  29. 8 8
      frontend/rust-lib/flowy-grid/src/protobuf/model/event_map.rs
  30. 1 1
      frontend/rust-lib/flowy-grid/src/protobuf/proto/event_map.proto
  31. 38 15
      frontend/rust-lib/flowy-grid/src/services/grid_editor.rs
  32. 5 2
      shared-lib/flowy-grid-data-model/src/entities/grid.rs
  33. 150 67
      shared-lib/flowy-grid-data-model/src/protobuf/model/grid.rs
  34. 3 2
      shared-lib/flowy-grid-data-model/src/protobuf/proto/grid.proto
  35. 1 1
      shared-lib/flowy-sync/src/client_grid/grid_meta_pad.rs

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

@@ -166,7 +166,8 @@
       "timeFormatTwentyFourHour": "24 hour",
       "addSelectOption": "Add an option",
       "optionTitle": "Options",
-      "addOption": "Add option"
+      "addOption": "Add option",
+      "editProperty": "Edit property"
     },
     "selectOption": {
       "purpleColor": "Purple",

+ 7 - 6
frontend/app_flowy/lib/startup/deps_resolver.dart

@@ -165,16 +165,17 @@ void _resolveGridDeps(GetIt getIt) {
     ),
   );
 
-  getIt.registerFactoryParam<EditFieldBloc, GridFieldData, void>(
-    (data, _) => EditFieldBloc(
+  getIt.registerFactoryParam<GridFieldBloc, GridFieldData, void>(
+    (data, _) => GridFieldBloc(
       field: data.field,
       service: FieldService(gridId: data.gridId),
     ),
   );
 
-  getIt.registerFactoryParam<CreateFieldBloc, String, void>(
-    (gridId, _) => CreateFieldBloc(
+  getIt.registerFactoryParam<FieldEditorBloc, String, FieldContextLoader>(
+    (gridId, fieldLoader) => FieldEditorBloc(
       service: FieldService(gridId: gridId),
+      fieldLoader: fieldLoader,
     ),
   );
 
@@ -213,8 +214,8 @@ void _resolveGridDeps(GetIt getIt) {
     ),
   );
 
-  getIt.registerFactoryParam<FieldTypeSwitchBloc, SwitchFieldContext, void>(
-    (context, _) => FieldTypeSwitchBloc(context),
+  getIt.registerFactoryParam<FieldSwitchBloc, SwitchFieldContext, void>(
+    (context, _) => FieldSwitchBloc(context),
   );
 
   getIt.registerFactoryParam<SingleSelectTypeOptionBloc, SingleSelectTypeOption, String>(

+ 0 - 98
frontend/app_flowy/lib/workspace/application/grid/field/create_field_bloc.dart

@@ -1,98 +0,0 @@
-import 'dart:typed_data';
-
-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:flutter_bloc/flutter_bloc.dart';
-import 'package:freezed_annotation/freezed_annotation.dart';
-import 'dart:async';
-import 'field_service.dart';
-import 'package:dartz/dartz.dart';
-
-part 'create_field_bloc.freezed.dart';
-
-class CreateFieldBloc extends Bloc<CreateFieldEvent, CreateFieldState> {
-  final FieldService service;
-
-  CreateFieldBloc({required this.service}) : super(CreateFieldState.initial(service.gridId)) {
-    on<CreateFieldEvent>(
-      (event, emit) async {
-        await event.map(
-          initial: (_InitialField value) async {
-            await _getEditFieldContext(emit);
-          },
-          updateName: (_UpdateName value) {
-            emit(state.copyWith(fieldName: value.name));
-          },
-          switchField: (_SwitchField value) {
-            emit(state.copyWith(field: Some(value.field), typeOptionData: value.typeOptionData));
-          },
-          done: (_Done value) async {
-            await _saveField(emit);
-          },
-        );
-      },
-    );
-  }
-
-  @override
-  Future<void> close() async {
-    return super.close();
-  }
-
-  Future<void> _saveField(Emitter<CreateFieldState> emit) async {
-    await state.field.fold(
-      () async => null,
-      (field) async {
-        field.name = state.fieldName;
-
-        final result = await service.createField(
-          field: field,
-          typeOptionData: state.typeOptionData,
-        );
-        result.fold((l) => null, (r) => null);
-      },
-    );
-  }
-
-  Future<void> _getEditFieldContext(Emitter<CreateFieldState> emit) async {
-    final result = await service.getEditFieldContext(FieldType.RichText);
-    result.fold(
-      (editContext) {
-        emit(state.copyWith(
-          field: Some(editContext.gridField),
-          typeOptionData: editContext.typeOptionData,
-          fieldName: editContext.gridField.name,
-        ));
-      },
-      (err) => Log.error(err),
-    );
-  }
-}
-
-@freezed
-class CreateFieldEvent with _$CreateFieldEvent {
-  const factory CreateFieldEvent.initial() = _InitialField;
-  const factory CreateFieldEvent.updateName(String name) = _UpdateName;
-  const factory CreateFieldEvent.switchField(Field field, Uint8List typeOptionData) = _SwitchField;
-  const factory CreateFieldEvent.done() = _Done;
-}
-
-@freezed
-class CreateFieldState with _$CreateFieldState {
-  const factory CreateFieldState({
-    required String fieldName,
-    required String gridId,
-    required String errorText,
-    required Option<Field> field,
-    required List<int> typeOptionData,
-  }) = _CreateFieldState;
-
-  factory CreateFieldState.initial(String gridId) => CreateFieldState(
-        gridId: gridId,
-        fieldName: '',
-        field: none(),
-        errorText: '',
-        typeOptionData: List<int>.empty(),
-      );
-}

+ 63 - 45
frontend/app_flowy/lib/workspace/application/grid/field/edit_field_bloc.dart

@@ -1,50 +1,38 @@
+import 'dart:typed_data';
 import 'package:flowy_sdk/log.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:freezed_annotation/freezed_annotation.dart';
 import 'dart:async';
 import 'field_service.dart';
+import 'package:dartz/dartz.dart';
 
 part 'edit_field_bloc.freezed.dart';
 
-class EditFieldBloc extends Bloc<EditFieldEvent, EditFieldState> {
+class FieldEditorBloc extends Bloc<FieldEditorEvent, FieldEditorState> {
   final FieldService service;
+  final FieldContextLoader _loader;
 
-  EditFieldBloc({required Field field, required this.service})
-      : super(EditFieldState.initial(EditFieldContext.create()..gridField = field)) {
-    on<EditFieldEvent>(
+  FieldEditorBloc({
+    required this.service,
+    required FieldContextLoader fieldLoader,
+  })  : _loader = fieldLoader,
+        super(FieldEditorState.initial(service.gridId)) {
+    on<FieldEditorEvent>(
       (event, emit) async {
         await event.map(
-          initial: (_InitialField value) {},
-          updateFieldName: (_UpdateFieldName value) async {
-            final result = await service.updateField(fieldId: field.id, name: value.name);
-            result.fold(
-              (l) => null,
-              (err) => Log.error(err),
-            );
+          initial: (_InitialField value) async {
+            await _getEditFieldContext(emit);
           },
-          hideField: (_HideField value) async {
-            final result = await service.updateField(fieldId: field.id, visibility: false);
-            result.fold(
-              (l) => null,
-              (err) => Log.error(err),
-            );
+          updateName: (_UpdateName value) {
+            emit(state.copyWith(fieldName: value.name));
           },
-          deleteField: (_DeleteField value) async {
-            final result = await service.deleteField(fieldId: field.id);
-            result.fold(
-              (l) => null,
-              (err) => Log.error(err),
-            );
+          switchField: (_SwitchField value) {
+            emit(state.copyWith(field: Some(value.field), typeOptionData: value.typeOptionData));
           },
-          duplicateField: (_DuplicateField value) async {
-            final result = await service.duplicateField(fieldId: field.id);
-            result.fold(
-              (l) => null,
-              (err) => Log.error(err),
-            );
+          done: (_Done value) async {
+            await _saveField(emit);
           },
-          saveField: (_SaveField value) {},
         );
       },
     );
@@ -54,29 +42,59 @@ class EditFieldBloc extends Bloc<EditFieldEvent, EditFieldState> {
   Future<void> close() async {
     return super.close();
   }
+
+  Future<void> _saveField(Emitter<FieldEditorState> emit) async {
+    await state.field.fold(
+      () async => null,
+      (field) async {
+        field.name = state.fieldName;
+        final result = await service.createField(
+          field: field,
+          typeOptionData: state.typeOptionData,
+        );
+        result.fold((l) => null, (r) => null);
+      },
+    );
+  }
+
+  Future<void> _getEditFieldContext(Emitter<FieldEditorState> emit) async {
+    final result = await _loader.load();
+    result.fold(
+      (editContext) {
+        emit(state.copyWith(
+          field: Some(editContext.gridField),
+          typeOptionData: editContext.typeOptionData,
+          fieldName: editContext.gridField.name,
+        ));
+      },
+      (err) => Log.error(err),
+    );
+  }
 }
 
 @freezed
-class EditFieldEvent with _$EditFieldEvent {
-  const factory EditFieldEvent.initial() = _InitialField;
-  const factory EditFieldEvent.updateFieldName(String name) = _UpdateFieldName;
-  const factory EditFieldEvent.hideField() = _HideField;
-  const factory EditFieldEvent.duplicateField() = _DuplicateField;
-  const factory EditFieldEvent.deleteField() = _DeleteField;
-  const factory EditFieldEvent.saveField() = _SaveField;
+class FieldEditorEvent with _$FieldEditorEvent {
+  const factory FieldEditorEvent.initial() = _InitialField;
+  const factory FieldEditorEvent.updateName(String name) = _UpdateName;
+  const factory FieldEditorEvent.switchField(Field field, Uint8List typeOptionData) = _SwitchField;
+  const factory FieldEditorEvent.done() = _Done;
 }
 
 @freezed
-class EditFieldState with _$EditFieldState {
-  const factory EditFieldState({
-    required EditFieldContext editContext,
-    required String errorText,
+class FieldEditorState with _$FieldEditorState {
+  const factory FieldEditorState({
     required String fieldName,
-  }) = _EditFieldState;
+    required String gridId,
+    required String errorText,
+    required Option<Field> field,
+    required List<int> typeOptionData,
+  }) = _FieldEditorState;
 
-  factory EditFieldState.initial(EditFieldContext editContext) => EditFieldState(
-        editContext: editContext,
+  factory FieldEditorState.initial(String gridId) => FieldEditorState(
+        gridId: gridId,
+        fieldName: '',
+        field: none(),
         errorText: '',
-        fieldName: editContext.gridField.name,
+        typeOptionData: List<int>.empty(),
       );
 }

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

@@ -11,11 +11,11 @@ class FieldService {
   FieldService({required this.gridId});
 
   Future<Either<EditFieldContext, FlowyError>> getEditFieldContext(FieldType fieldType) {
-    final payload = CreateEditFieldContextParams.create()
+    final payload = GetEditFieldContextParams.create()
       ..gridId = gridId
       ..fieldType = fieldType;
 
-    return GridEventCreateEditFieldContext(payload).send();
+    return GridEventGetEditFieldContext(payload).send();
   }
 
   Future<Either<Unit, FlowyError>> updateField({
@@ -108,3 +108,39 @@ class GridFieldData extends Equatable {
   @override
   List<Object> get props => [field.id];
 }
+
+abstract class FieldContextLoader {
+  Future<Either<EditFieldContext, FlowyError>> load();
+}
+
+class NewFieldContextLoader extends FieldContextLoader {
+  final String gridId;
+  NewFieldContextLoader({
+    required this.gridId,
+  });
+
+  @override
+  Future<Either<EditFieldContext, FlowyError>> load() {
+    final payload = GetEditFieldContextParams.create()
+      ..gridId = gridId
+      ..fieldType = FieldType.RichText;
+
+    return GridEventGetEditFieldContext(payload).send();
+  }
+}
+
+class FieldContextLoaderAdaptor extends FieldContextLoader {
+  final GridFieldData data;
+
+  FieldContextLoaderAdaptor(this.data);
+
+  @override
+  Future<Either<EditFieldContext, FlowyError>> load() {
+    final payload = GetEditFieldContextParams.create()
+      ..gridId = data.gridId
+      ..fieldId = data.field.id
+      ..fieldType = data.field.fieldType;
+
+    return GridEventGetEditFieldContext(payload).send();
+  }
+}

+ 80 - 0
frontend/app_flowy/lib/workspace/application/grid/field/grid_field_bloc.dart

@@ -0,0 +1,80 @@
+import 'package:flowy_sdk/log.dart';
+import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:freezed_annotation/freezed_annotation.dart';
+import 'dart:async';
+import 'field_service.dart';
+
+part 'grid_field_bloc.freezed.dart';
+
+class GridFieldBloc extends Bloc<GridFieldEvent, GridFieldState> {
+  final FieldService service;
+
+  GridFieldBloc({required Field field, required this.service})
+      : super(GridFieldState.initial(EditFieldContext.create()..gridField = field)) {
+    on<GridFieldEvent>(
+      (event, emit) async {
+        await event.map(
+          updateFieldName: (_UpdateFieldName value) async {
+            final result = await service.updateField(fieldId: field.id, name: value.name);
+            result.fold(
+              (l) => null,
+              (err) => Log.error(err),
+            );
+          },
+          hideField: (_HideField value) async {
+            final result = await service.updateField(fieldId: field.id, visibility: false);
+            result.fold(
+              (l) => null,
+              (err) => Log.error(err),
+            );
+          },
+          deleteField: (_DeleteField value) async {
+            final result = await service.deleteField(fieldId: field.id);
+            result.fold(
+              (l) => null,
+              (err) => Log.error(err),
+            );
+          },
+          duplicateField: (_DuplicateField value) async {
+            final result = await service.duplicateField(fieldId: field.id);
+            result.fold(
+              (l) => null,
+              (err) => Log.error(err),
+            );
+          },
+          saveField: (_SaveField value) {},
+        );
+      },
+    );
+  }
+
+  @override
+  Future<void> close() async {
+    return super.close();
+  }
+}
+
+@freezed
+class GridFieldEvent with _$GridFieldEvent {
+  const factory GridFieldEvent.updateFieldName(String name) = _UpdateFieldName;
+  const factory GridFieldEvent.hideField() = _HideField;
+  const factory GridFieldEvent.duplicateField() = _DuplicateField;
+  const factory GridFieldEvent.deleteField() = _DeleteField;
+  const factory GridFieldEvent.saveField() = _SaveField;
+}
+
+@freezed
+class GridFieldState with _$GridFieldState {
+  const factory GridFieldState({
+    required EditFieldContext editContext,
+    required String errorText,
+    required String fieldName,
+  }) = _GridFieldState;
+
+  factory GridFieldState.initial(EditFieldContext editContext) => GridFieldState(
+        editContext: editContext,
+        errorText: '',
+        fieldName: editContext.gridField.name,
+      );
+}

+ 12 - 16
frontend/app_flowy/lib/workspace/application/grid/field/switch_field_type_bloc.dart

@@ -9,9 +9,9 @@ import 'field_service.dart';
 
 part 'switch_field_type_bloc.freezed.dart';
 
-class FieldTypeSwitchBloc extends Bloc<FieldTypeSwitchEvent, FieldTypeSwitchState> {
-  FieldTypeSwitchBloc(SwitchFieldContext editContext) : super(FieldTypeSwitchState.initial(editContext)) {
-    on<FieldTypeSwitchEvent>(
+class FieldSwitchBloc extends Bloc<FieldSwitchEvent, FieldSwitchState> {
+  FieldSwitchBloc(SwitchFieldContext editContext) : super(FieldSwitchState.initial(editContext)) {
+    on<FieldSwitchEvent>(
       (event, emit) async {
         await event.map(
           toFieldType: (_ToFieldType value) async {
@@ -19,12 +19,8 @@ class FieldTypeSwitchBloc extends Bloc<FieldTypeSwitchEvent, FieldTypeSwitchStat
             final result = await fieldService.getEditFieldContext(value.fieldType);
             result.fold(
               (newEditContext) {
-                emit(
-                  state.copyWith(
-                    field: newEditContext.gridField,
-                    typeOptionData: Uint8List.fromList(newEditContext.typeOptionData),
-                  ),
-                );
+                final typeOptionData = Uint8List.fromList(newEditContext.typeOptionData);
+                emit(state.copyWith(field: newEditContext.gridField, typeOptionData: typeOptionData));
               },
               (err) => Log.error(err),
             );
@@ -44,20 +40,20 @@ class FieldTypeSwitchBloc extends Bloc<FieldTypeSwitchEvent, FieldTypeSwitchStat
 }
 
 @freezed
-class FieldTypeSwitchEvent with _$FieldTypeSwitchEvent {
-  const factory FieldTypeSwitchEvent.toFieldType(FieldType fieldType) = _ToFieldType;
-  const factory FieldTypeSwitchEvent.didUpdateTypeOptionData(Uint8List typeOptionData) = _DidUpdateTypeOptionData;
+class FieldSwitchEvent with _$FieldSwitchEvent {
+  const factory FieldSwitchEvent.toFieldType(FieldType fieldType) = _ToFieldType;
+  const factory FieldSwitchEvent.didUpdateTypeOptionData(Uint8List typeOptionData) = _DidUpdateTypeOptionData;
 }
 
 @freezed
-class FieldTypeSwitchState with _$FieldTypeSwitchState {
-  const factory FieldTypeSwitchState({
+class FieldSwitchState with _$FieldSwitchState {
+  const factory FieldSwitchState({
     required String gridId,
     required Field field,
     required Uint8List typeOptionData,
-  }) = _FieldTypeSwitchState;
+  }) = _FieldSwitchState;
 
-  factory FieldTypeSwitchState.initial(SwitchFieldContext switchContext) => FieldTypeSwitchState(
+  factory FieldSwitchState.initial(SwitchFieldContext switchContext) => FieldSwitchState(
         gridId: switchContext.gridId,
         field: switchContext.field,
         typeOptionData: Uint8List.fromList(switchContext.typeOptionData),

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

@@ -7,8 +7,8 @@ export 'data.dart';
 // Field
 export 'field/field_service.dart';
 export 'field/grid_header_bloc.dart';
+export 'field/grid_field_bloc.dart';
 export 'field/edit_field_bloc.dart';
-export 'field/create_field_bloc.dart';
 export 'field/switch_field_type_bloc.dart';
 
 // Field Type Option

+ 0 - 0
frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_type_list.dart → frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_list.dart


+ 4 - 4
frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_operation_list.dart

@@ -1,4 +1,4 @@
-import 'package:app_flowy/workspace/application/grid/field/edit_field_bloc.dart';
+import 'package:app_flowy/workspace/application/grid/field/grid_field_bloc.dart';
 import 'package:flowy_infra/image.dart';
 import 'package:flowy_infra/theme.dart';
 import 'package:flowy_infra_ui/style_widget/button.dart';
@@ -86,13 +86,13 @@ extension _FieldActionExtension on FieldAction {
   void run(BuildContext context) {
     switch (this) {
       case FieldAction.hide:
-        context.read<EditFieldBloc>().add(const EditFieldEvent.hideField());
+        context.read<GridFieldBloc>().add(const GridFieldEvent.hideField());
         break;
       case FieldAction.duplicate:
-        context.read<EditFieldBloc>().add(const EditFieldEvent.duplicateField());
+        context.read<GridFieldBloc>().add(const GridFieldEvent.duplicateField());
         break;
       case FieldAction.delete:
-        context.read<EditFieldBloc>().add(const EditFieldEvent.deleteField());
+        context.read<GridFieldBloc>().add(const GridFieldEvent.deleteField());
         break;
     }
   }

+ 15 - 13
frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_tyep_switcher.dart → frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_switcher.dart

@@ -15,39 +15,41 @@ import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 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/header/field_type_list.dart';
+import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/header/field_list.dart';
 
 import 'type_option/multi_select.dart';
 import 'type_option/number.dart';
 import 'type_option/single_select.dart';
 
-typedef SelectFieldCallback = void Function(Field, Uint8List);
+typedef UpdateFieldCallback = void Function(Field, Uint8List);
 
-class FieldTypeSwitcher extends StatefulWidget {
+class FieldSwitcher extends StatefulWidget {
   final SwitchFieldContext switchContext;
-  final SelectFieldCallback onSelected;
+  final UpdateFieldCallback onUpdated;
 
-  const FieldTypeSwitcher({
+  const FieldSwitcher({
     required this.switchContext,
-    required this.onSelected,
+    required this.onUpdated,
     Key? key,
   }) : super(key: key);
 
   @override
-  State<FieldTypeSwitcher> createState() => _FieldTypeSwitcherState();
+  State<FieldSwitcher> createState() => _FieldSwitcherState();
 }
 
-class _FieldTypeSwitcherState extends State<FieldTypeSwitcher> {
+class _FieldSwitcherState extends State<FieldSwitcher> {
   String? currentOverlayIdentifier;
 
   @override
   Widget build(BuildContext context) {
     return BlocProvider(
-      create: (context) => getIt<FieldTypeSwitchBloc>(param1: widget.switchContext),
-      child: BlocBuilder<FieldTypeSwitchBloc, FieldTypeSwitchState>(
+      create: (context) => getIt<FieldSwitchBloc>(param1: widget.switchContext),
+      child: BlocConsumer<FieldSwitchBloc, FieldSwitchState>(
+        listener: (context, state) {
+          widget.onUpdated(state.field, state.typeOptionData);
+        },
         builder: (context, state) {
           List<Widget> children = [_switchFieldTypeButton(context, state.field)];
-
           final typeOptionWidget = _typeOptionWidget(
             context: context,
             field: state.field,
@@ -77,7 +79,7 @@ class _FieldTypeSwitcherState extends State<FieldTypeSwitcher> {
         hoverColor: theme.hover,
         onTap: () {
           final list = FieldTypeList(onSelectField: (fieldType) {
-            context.read<FieldTypeSwitchBloc>().add(FieldTypeSwitchEvent.toFieldType(fieldType));
+            context.read<FieldSwitchBloc>().add(FieldSwitchEvent.toFieldType(fieldType));
           });
           _showOverlay(context, list);
         },
@@ -98,7 +100,7 @@ class _FieldTypeSwitcherState extends State<FieldTypeSwitcher> {
     );
 
     final dataDelegate = TypeOptionDataDelegate(didUpdateTypeOptionData: (data) {
-      context.read<FieldTypeSwitchBloc>().add(FieldTypeSwitchEvent.didUpdateTypeOptionData(data));
+      context.read<FieldSwitchBloc>().add(FieldSwitchEvent.didUpdateTypeOptionData(data));
     });
 
     final builder = _makeTypeOptionBuild(

+ 103 - 0
frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/grid_field_action_sheet.dart

@@ -0,0 +1,103 @@
+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/layout/sizes.dart';
+import 'package:flowy_infra/theme.dart';
+import 'package:flowy_infra_ui/flowy_infra_ui.dart';
+import 'package:flowy_infra_ui/style_widget/button.dart';
+import 'package:flowy_infra_ui/style_widget/text.dart';
+import 'package:flowy_infra_ui/widget/spacing.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+import 'field_operation_list.dart';
+import 'package:easy_localization/easy_localization.dart';
+import 'package:app_flowy/generated/locale_keys.g.dart';
+
+class GridFieldActionSheet extends StatelessWidget with FlowyOverlayDelegate {
+  final GridFieldData fieldData;
+  final VoidCallback onEdited;
+  const GridFieldActionSheet({required this.fieldData, required this.onEdited, Key? key}) : super(key: key);
+
+  static void show(BuildContext overlayContext, GridFieldData fieldData, final VoidCallback onEdited) {
+    final editor = GridFieldActionSheet(fieldData: fieldData, onEdited: onEdited);
+    FlowyOverlay.of(overlayContext).insertWithAnchor(
+      widget: OverlayContainer(
+        child: editor,
+        constraints: BoxConstraints.loose(const Size(240, 200)),
+      ),
+      identifier: editor.identifier(),
+      anchorContext: overlayContext,
+      anchorDirection: AnchorDirection.bottomWithLeftAligned,
+    );
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return BlocProvider(
+      create: (context) => getIt<GridFieldBloc>(param1: fieldData),
+      child: SingleChildScrollView(
+        child: Column(
+          children: [
+            _EditFieldButton(
+              onEdited: () {
+                FlowyOverlay.of(context).remove(identifier());
+                onEdited();
+              },
+            ),
+            const VSpace(6),
+            _FieldOperationList(fieldData, () => FlowyOverlay.of(context).remove(identifier())),
+          ],
+        ),
+      ),
+    );
+  }
+
+  String identifier() {
+    return toString();
+  }
+
+  @override
+  bool asBarrier() => true;
+}
+
+class _EditFieldButton extends StatelessWidget {
+  final Function() onEdited;
+  const _EditFieldButton({required this.onEdited, Key? key}) : super(key: key);
+
+  @override
+  Widget build(BuildContext context) {
+    final theme = context.watch<AppTheme>();
+    return BlocBuilder<GridFieldBloc, GridFieldState>(
+      builder: (context, state) {
+        return SizedBox(
+          height: GridSize.typeOptionItemHeight,
+          child: FlowyButton(
+            text: FlowyText.medium(LocaleKeys.grid_field_editProperty.tr(), fontSize: 12),
+            hoverColor: theme.hover,
+            onTap: onEdited,
+          ),
+        );
+      },
+    );
+  }
+}
+
+class _FieldOperationList extends StatelessWidget {
+  final GridFieldData fieldData;
+  final VoidCallback onDismissed;
+  const _FieldOperationList(this.fieldData, this.onDismissed, {Key? key}) : super(key: key);
+
+  @override
+  Widget build(BuildContext context) {
+    final actions = FieldAction.values
+        .map(
+          (action) => FieldActionItem(
+            fieldId: fieldData.field.id,
+            action: action,
+            onTap: onDismissed,
+          ),
+        )
+        .toList();
+
+    return FieldOperationList(actions: actions);
+  }
+}

+ 29 - 23
frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/create_field_pannel.dart → frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/grid_field_editor.dart

@@ -1,5 +1,6 @@
 import 'package:app_flowy/startup/startup.dart';
-import 'package:app_flowy/workspace/application/grid/field/create_field_bloc.dart';
+import 'package:app_flowy/workspace/application/grid/field/edit_field_bloc.dart';
+import 'package:app_flowy/workspace/application/grid/field/field_service.dart';
 import 'package:app_flowy/workspace/application/grid/field/switch_field_type_bloc.dart';
 import 'package:flowy_infra_ui/flowy_infra_ui.dart';
 import 'package:flowy_infra_ui/style_widget/text.dart';
@@ -7,19 +8,24 @@ import 'package:flowy_infra_ui/widget/spacing.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 import 'field_name_input.dart';
-import 'field_tyep_switcher.dart';
+import 'field_switcher.dart';
 
-class CreateFieldPannel extends FlowyOverlayDelegate {
+class FieldEditor extends FlowyOverlayDelegate {
   final String gridId;
-  final CreateFieldBloc _createFieldBloc;
-  CreateFieldPannel({required this.gridId, Key? key}) : _createFieldBloc = getIt<CreateFieldBloc>(param1: gridId) {
-    _createFieldBloc.add(const CreateFieldEvent.initial());
+  final FieldEditorBloc _fieldEditorBloc;
+  final FieldContextLoader? fieldContextLoader;
+  FieldEditor({
+    required this.gridId,
+    required this.fieldContextLoader,
+    Key? key,
+  }) : _fieldEditorBloc = getIt<FieldEditorBloc>(param1: gridId, param2: fieldContextLoader) {
+    _fieldEditorBloc.add(const FieldEditorEvent.initial());
   }
 
-  void show(BuildContext context, String gridId) {
+  void show(BuildContext context) {
     FlowyOverlay.of(context).insertWithAnchor(
       widget: OverlayContainer(
-        child: _CreateFieldPannelWidget(_createFieldBloc),
+        child: _EditFieldPannelWidget(_fieldEditorBloc),
         constraints: BoxConstraints.loose(const Size(220, 400)),
       ),
       identifier: identifier(),
@@ -36,22 +42,22 @@ class CreateFieldPannel extends FlowyOverlayDelegate {
 
   @override
   void didRemove() {
-    _createFieldBloc.add(const CreateFieldEvent.done());
+    _fieldEditorBloc.add(const FieldEditorEvent.done());
   }
 
   @override
   bool asBarrier() => true;
 }
 
-class _CreateFieldPannelWidget extends StatelessWidget {
-  final CreateFieldBloc createFieldBloc;
-  const _CreateFieldPannelWidget(this.createFieldBloc, {Key? key}) : super(key: key);
+class _EditFieldPannelWidget extends StatelessWidget {
+  final FieldEditorBloc editorBloc;
+  const _EditFieldPannelWidget(this.editorBloc, {Key? key}) : super(key: key);
 
   @override
   Widget build(BuildContext context) {
     return BlocProvider.value(
-      value: createFieldBloc,
-      child: BlocBuilder<CreateFieldBloc, CreateFieldState>(
+      value: editorBloc,
+      child: BlocBuilder<FieldEditorBloc, FieldEditorState>(
         builder: (context, state) {
           return state.field.fold(
             () => const SizedBox(width: 200),
@@ -62,7 +68,7 @@ class _CreateFieldPannelWidget extends StatelessWidget {
                 const VSpace(10),
                 const _FieldNameTextField(),
                 const VSpace(10),
-                _FieldTypeSwitcher(SwitchFieldContext(state.gridId, field, state.typeOptionData)),
+                _FieldSwitcher(SwitchFieldContext(state.gridId, field, state.typeOptionData)),
               ],
             ),
           );
@@ -72,16 +78,16 @@ class _CreateFieldPannelWidget extends StatelessWidget {
   }
 }
 
-class _FieldTypeSwitcher extends StatelessWidget {
+class _FieldSwitcher extends StatelessWidget {
   final SwitchFieldContext switchContext;
-  const _FieldTypeSwitcher(this.switchContext, {Key? key}) : super(key: key);
+  const _FieldSwitcher(this.switchContext, {Key? key}) : super(key: key);
 
   @override
   Widget build(BuildContext context) {
-    return FieldTypeSwitcher(
+    return FieldSwitcher(
       switchContext: switchContext,
-      onSelected: (field, typeOptionData) {
-        context.read<CreateFieldBloc>().add(CreateFieldEvent.switchField(field, typeOptionData));
+      onUpdated: (field, typeOptionData) {
+        context.read<FieldEditorBloc>().add(FieldEditorEvent.switchField(field, typeOptionData));
       },
     );
   }
@@ -92,14 +98,14 @@ class _FieldNameTextField extends StatelessWidget {
 
   @override
   Widget build(BuildContext context) {
-    return BlocBuilder<CreateFieldBloc, CreateFieldState>(
+    return BlocBuilder<FieldEditorBloc, FieldEditorState>(
       buildWhen: (previous, current) => previous.fieldName != current.fieldName,
       builder: (context, state) {
         return FieldNameTextField(
           name: state.fieldName,
-          errorText: context.read<CreateFieldBloc>().state.errorText,
+          errorText: context.read<FieldEditorBloc>().state.errorText,
           onNameChanged: (newName) {
-            context.read<CreateFieldBloc>().add(CreateFieldEvent.updateName(newName));
+            context.read<FieldEditorBloc>().add(FieldEditorEvent.updateName(newName));
           },
         );
       },

+ 6 - 2
frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/grid_header.dart

@@ -9,7 +9,7 @@ import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart' hide Row;
 import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 
-import 'create_field_pannel.dart';
+import 'grid_field_editor.dart';
 import 'grid_header_cell.dart';
 
 class GridHeaderDelegate extends SliverPersistentHeaderDelegate {
@@ -113,10 +113,14 @@ class CreateFieldButton extends StatelessWidget {
   @override
   Widget build(BuildContext context) {
     final theme = context.watch<AppTheme>();
+
     return FlowyButton(
       text: const FlowyText.medium('New column', fontSize: 12),
       hoverColor: theme.hover,
-      onTap: () => CreateFieldPannel(gridId: gridId).show(context, gridId),
+      onTap: () => FieldEditor(
+        gridId: gridId,
+        fieldContextLoader: NewFieldContextLoader(gridId: gridId),
+      ).show(context),
       leftIcon: svg("home/add"),
     );
   }

+ 8 - 2
frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/grid_header_cell.dart

@@ -7,7 +7,8 @@ import 'package:flowy_infra_ui/style_widget/text.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 
-import 'field_detail_pannel.dart';
+import 'grid_field_editor.dart';
+import 'grid_field_action_sheet.dart';
 
 class GridHeaderCell extends StatelessWidget {
   final GridFieldData fieldData;
@@ -18,7 +19,12 @@ class GridHeaderCell extends StatelessWidget {
     final theme = context.watch<AppTheme>();
     final button = FlowyButton(
       hoverColor: theme.hover,
-      onTap: () => FieldDetailPannel.show(context, fieldData),
+      onTap: () => GridFieldActionSheet.show(context, fieldData, () {
+        FieldEditor(
+          gridId: fieldData.gridId,
+          fieldContextLoader: FieldContextLoaderAdaptor(fieldData),
+        ).show(context);
+      }),
       rightIcon: svg("editor/details", color: theme.iconColor),
       text: Padding(padding: GridSize.cellContentInsets, child: FlowyText.medium(fieldData.field.name, fontSize: 12)),
     );

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

@@ -1,7 +1,7 @@
 import 'package:app_flowy/startup/startup.dart';
 import 'package:app_flowy/workspace/application/grid/field/type_option/date_bloc.dart';
 import 'package:app_flowy/workspace/presentation/plugins/grid/src/layout/sizes.dart';
-import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/header/field_tyep_switcher.dart';
+import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/header/field_switcher.dart';
 import 'package:easy_localization/easy_localization.dart' hide DateFormat;
 import 'package:app_flowy/generated/locale_keys.g.dart';
 import 'package:flowy_infra/image.dart';

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

@@ -1,6 +1,6 @@
 import 'package:app_flowy/startup/startup.dart';
 import 'package:app_flowy/workspace/application/grid/field/type_option/multi_select_bloc.dart';
-import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/header/field_tyep_switcher.dart';
+import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/header/field_switcher.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/selection_type_option.pb.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';

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

@@ -1,7 +1,7 @@
 import 'package:app_flowy/startup/startup.dart';
 import 'package:app_flowy/workspace/application/grid/field/type_option/number_bloc.dart';
 import 'package:app_flowy/workspace/presentation/plugins/grid/src/layout/sizes.dart';
-import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/header/field_tyep_switcher.dart';
+import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/header/field_switcher.dart';
 import 'package:flowy_infra/image.dart';
 import 'package:flowy_infra/theme.dart';
 import 'package:flowy_infra_ui/flowy_infra_ui.dart';

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

@@ -1,6 +1,6 @@
 import 'package:app_flowy/workspace/application/grid/field/type_option/option_pannel_bloc.dart';
 import 'package:app_flowy/workspace/presentation/plugins/grid/src/layout/sizes.dart';
-import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/header/field_tyep_switcher.dart';
+import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/header/field_switcher.dart';
 import 'package:flowy_infra/image.dart';
 import 'package:flowy_infra/theme.dart';
 import 'package:flowy_infra_ui/style_widget/button.dart';

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

@@ -1,6 +1,6 @@
 import 'package:app_flowy/startup/startup.dart';
 import 'package:app_flowy/workspace/application/grid/field/type_option/single_select_bloc.dart';
-import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/header/field_tyep_switcher.dart';
+import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/header/field_switcher.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/selection_type_option.pb.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';

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

@@ -74,7 +74,7 @@ TransitionBuilder overlayManagerBuilder() {
 
 abstract class FlowyOverlayDelegate {
   bool asBarrier() => false;
-  void didRemove();
+  void didRemove() => {};
 }
 
 class FlowyOverlay extends StatefulWidget {

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

@@ -120,13 +120,13 @@ class GridEventDuplicateField {
     }
 }
 
-class GridEventCreateEditFieldContext {
-     CreateEditFieldContextParams request;
-     GridEventCreateEditFieldContext(this.request);
+class GridEventGetEditFieldContext {
+     GetEditFieldContextParams request;
+     GridEventGetEditFieldContext(this.request);
 
     Future<Either<EditFieldContext, FlowyError>> send() {
     final request = FFIRequest.create()
-          ..event = GridEvent.CreateEditFieldContext.toString()
+          ..event = GridEvent.GetEditFieldContext.toString()
           ..payload = requestToBytes(this.request);
 
     return Dispatch.asyncRequest(request)

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

@@ -374,47 +374,65 @@ class FieldOrder extends $pb.GeneratedMessage {
   void clearFieldId() => clearField(1);
 }
 
-class CreateEditFieldContextParams extends $pb.GeneratedMessage {
-  static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'CreateEditFieldContextParams', createEmptyInstance: create)
+enum GetEditFieldContextParams_OneOfFieldId {
+  fieldId, 
+  notSet
+}
+
+class GetEditFieldContextParams extends $pb.GeneratedMessage {
+  static const $core.Map<$core.int, GetEditFieldContextParams_OneOfFieldId> _GetEditFieldContextParams_OneOfFieldIdByTag = {
+    2 : GetEditFieldContextParams_OneOfFieldId.fieldId,
+    0 : GetEditFieldContextParams_OneOfFieldId.notSet
+  };
+  static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'GetEditFieldContextParams', createEmptyInstance: create)
+    ..oo(0, [2])
     ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'gridId')
-    ..e<$0.FieldType>(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'fieldType', $pb.PbFieldType.OE, defaultOrMaker: $0.FieldType.RichText, valueOf: $0.FieldType.valueOf, enumValues: $0.FieldType.values)
+    ..aOS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'fieldId')
+    ..e<$0.FieldType>(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'fieldType', $pb.PbFieldType.OE, defaultOrMaker: $0.FieldType.RichText, valueOf: $0.FieldType.valueOf, enumValues: $0.FieldType.values)
     ..hasRequiredFields = false
   ;
 
-  CreateEditFieldContextParams._() : super();
-  factory CreateEditFieldContextParams({
+  GetEditFieldContextParams._() : super();
+  factory GetEditFieldContextParams({
     $core.String? gridId,
+    $core.String? fieldId,
     $0.FieldType? fieldType,
   }) {
     final _result = create();
     if (gridId != null) {
       _result.gridId = gridId;
     }
+    if (fieldId != null) {
+      _result.fieldId = fieldId;
+    }
     if (fieldType != null) {
       _result.fieldType = fieldType;
     }
     return _result;
   }
-  factory CreateEditFieldContextParams.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
-  factory CreateEditFieldContextParams.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
+  factory GetEditFieldContextParams.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
+  factory GetEditFieldContextParams.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')
-  CreateEditFieldContextParams clone() => CreateEditFieldContextParams()..mergeFromMessage(this);
+  GetEditFieldContextParams clone() => GetEditFieldContextParams()..mergeFromMessage(this);
   @$core.Deprecated(
   'Using this can add significant overhead to your binary. '
   'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
   'Will be removed in next major version')
-  CreateEditFieldContextParams copyWith(void Function(CreateEditFieldContextParams) updates) => super.copyWith((message) => updates(message as CreateEditFieldContextParams)) as CreateEditFieldContextParams; // ignore: deprecated_member_use
+  GetEditFieldContextParams copyWith(void Function(GetEditFieldContextParams) updates) => super.copyWith((message) => updates(message as GetEditFieldContextParams)) as GetEditFieldContextParams; // ignore: deprecated_member_use
   $pb.BuilderInfo get info_ => _i;
   @$core.pragma('dart2js:noInline')
-  static CreateEditFieldContextParams create() => CreateEditFieldContextParams._();
-  CreateEditFieldContextParams createEmptyInstance() => create();
-  static $pb.PbList<CreateEditFieldContextParams> createRepeated() => $pb.PbList<CreateEditFieldContextParams>();
+  static GetEditFieldContextParams create() => GetEditFieldContextParams._();
+  GetEditFieldContextParams createEmptyInstance() => create();
+  static $pb.PbList<GetEditFieldContextParams> createRepeated() => $pb.PbList<GetEditFieldContextParams>();
   @$core.pragma('dart2js:noInline')
-  static CreateEditFieldContextParams getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<CreateEditFieldContextParams>(create);
-  static CreateEditFieldContextParams? _defaultInstance;
+  static GetEditFieldContextParams getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<GetEditFieldContextParams>(create);
+  static GetEditFieldContextParams? _defaultInstance;
+
+  GetEditFieldContextParams_OneOfFieldId whichOneOfFieldId() => _GetEditFieldContextParams_OneOfFieldIdByTag[$_whichOneof(0)]!;
+  void clearOneOfFieldId() => clearField($_whichOneof(0));
 
   @$pb.TagNumber(1)
   $core.String get gridId => $_getSZ(0);
@@ -426,13 +444,22 @@ class CreateEditFieldContextParams extends $pb.GeneratedMessage {
   void clearGridId() => clearField(1);
 
   @$pb.TagNumber(2)
-  $0.FieldType get fieldType => $_getN(1);
+  $core.String get fieldId => $_getSZ(1);
   @$pb.TagNumber(2)
-  set fieldType($0.FieldType v) { setField(2, v); }
+  set fieldId($core.String v) { $_setString(1, v); }
   @$pb.TagNumber(2)
-  $core.bool hasFieldType() => $_has(1);
+  $core.bool hasFieldId() => $_has(1);
   @$pb.TagNumber(2)
-  void clearFieldType() => clearField(2);
+  void clearFieldId() => clearField(2);
+
+  @$pb.TagNumber(3)
+  $0.FieldType get fieldType => $_getN(2);
+  @$pb.TagNumber(3)
+  set fieldType($0.FieldType v) { setField(3, v); }
+  @$pb.TagNumber(3)
+  $core.bool hasFieldType() => $_has(2);
+  @$pb.TagNumber(3)
+  void clearFieldType() => clearField(3);
 }
 
 class EditFieldContext extends $pb.GeneratedMessage {

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

@@ -68,17 +68,21 @@ const FieldOrder$json = const {
 
 /// Descriptor for `FieldOrder`. Decode as a `google.protobuf.DescriptorProto`.
 final $typed_data.Uint8List fieldOrderDescriptor = $convert.base64Decode('CgpGaWVsZE9yZGVyEhkKCGZpZWxkX2lkGAEgASgJUgdmaWVsZElk');
-@$core.Deprecated('Use createEditFieldContextParamsDescriptor instead')
-const CreateEditFieldContextParams$json = const {
-  '1': 'CreateEditFieldContextParams',
+@$core.Deprecated('Use getEditFieldContextParamsDescriptor instead')
+const GetEditFieldContextParams$json = const {
+  '1': 'GetEditFieldContextParams',
   '2': const [
     const {'1': 'grid_id', '3': 1, '4': 1, '5': 9, '10': 'gridId'},
-    const {'1': 'field_type', '3': 2, '4': 1, '5': 14, '6': '.FieldType', '10': 'fieldType'},
+    const {'1': 'field_id', '3': 2, '4': 1, '5': 9, '9': 0, '10': 'fieldId'},
+    const {'1': 'field_type', '3': 3, '4': 1, '5': 14, '6': '.FieldType', '10': 'fieldType'},
+  ],
+  '8': const [
+    const {'1': 'one_of_field_id'},
   ],
 };
 
-/// Descriptor for `CreateEditFieldContextParams`. Decode as a `google.protobuf.DescriptorProto`.
-final $typed_data.Uint8List createEditFieldContextParamsDescriptor = $convert.base64Decode('ChxDcmVhdGVFZGl0RmllbGRDb250ZXh0UGFyYW1zEhcKB2dyaWRfaWQYASABKAlSBmdyaWRJZBIpCgpmaWVsZF90eXBlGAIgASgOMgouRmllbGRUeXBlUglmaWVsZFR5cGU=');
+/// Descriptor for `GetEditFieldContextParams`. Decode as a `google.protobuf.DescriptorProto`.
+final $typed_data.Uint8List getEditFieldContextParamsDescriptor = $convert.base64Decode('ChlHZXRFZGl0RmllbGRDb250ZXh0UGFyYW1zEhcKB2dyaWRfaWQYASABKAlSBmdyaWRJZBIbCghmaWVsZF9pZBgCIAEoCUgAUgdmaWVsZElkEikKCmZpZWxkX3R5cGUYAyABKA4yCi5GaWVsZFR5cGVSCWZpZWxkVHlwZUIRCg9vbmVfb2ZfZmllbGRfaWQ=');
 @$core.Deprecated('Use editFieldContextDescriptor instead')
 const EditFieldContext$json = const {
   '1': 'EditFieldContext',

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

@@ -17,7 +17,7 @@ class GridEvent extends $pb.ProtobufEnum {
   static const GridEvent CreateField = GridEvent._(12, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'CreateField');
   static const GridEvent DeleteField = GridEvent._(13, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'DeleteField');
   static const GridEvent DuplicateField = GridEvent._(15, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'DuplicateField');
-  static const GridEvent CreateEditFieldContext = GridEvent._(16, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'CreateEditFieldContext');
+  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 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');
@@ -31,7 +31,7 @@ class GridEvent extends $pb.ProtobufEnum {
     CreateField,
     DeleteField,
     DuplicateField,
-    CreateEditFieldContext,
+    GetEditFieldContext,
     CreateSelectOption,
     CreateRow,
     GetRow,

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

@@ -19,7 +19,7 @@ const GridEvent$json = const {
     const {'1': 'CreateField', '2': 12},
     const {'1': 'DeleteField', '2': 13},
     const {'1': 'DuplicateField', '2': 15},
-    const {'1': 'CreateEditFieldContext', '2': 16},
+    const {'1': 'GetEditFieldContext', '2': 16},
     const {'1': 'CreateSelectOption', '2': 30},
     const {'1': 'CreateRow', '2': 50},
     const {'1': 'GetRow', '2': 51},
@@ -28,4 +28,4 @@ const GridEvent$json = const {
 };
 
 /// Descriptor for `GridEvent`. Decode as a `google.protobuf.EnumDescriptorProto`.
-final $typed_data.Uint8List gridEventDescriptor = $convert.base64Decode('CglHcmlkRXZlbnQSDwoLR2V0R3JpZERhdGEQABIRCg1HZXRHcmlkQmxvY2tzEAESDQoJR2V0RmllbGRzEAoSDwoLVXBkYXRlRmllbGQQCxIPCgtDcmVhdGVGaWVsZBAMEg8KC0RlbGV0ZUZpZWxkEA0SEgoORHVwbGljYXRlRmllbGQQDxIaChZDcmVhdGVFZGl0RmllbGRDb250ZXh0EBASFgoSQ3JlYXRlU2VsZWN0T3B0aW9uEB4SDQoJQ3JlYXRlUm93EDISCgoGR2V0Um93EDMSDgoKVXBkYXRlQ2VsbBBG');
+final $typed_data.Uint8List gridEventDescriptor = $convert.base64Decode('CglHcmlkRXZlbnQSDwoLR2V0R3JpZERhdGEQABIRCg1HZXRHcmlkQmxvY2tzEAESDQoJR2V0RmllbGRzEAoSDwoLVXBkYXRlRmllbGQQCxIPCgtDcmVhdGVGaWVsZBAMEg8KC0RlbGV0ZUZpZWxkEA0SEgoORHVwbGljYXRlRmllbGQQDxIXChNHZXRFZGl0RmllbGRDb250ZXh0EBASFgoSQ3JlYXRlU2VsZWN0T3B0aW9uEB4SDQoJQ3JlYXRlUm93EDISCgoGR2V0Um93EDMSDgoKVXBkYXRlQ2VsbBBG');

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

@@ -1,6 +1,7 @@
 use crate::manager::GridManager;
 use crate::services::field::{type_option_data_from_str, SelectOption};
-use flowy_error::FlowyError;
+use crate::services::grid_editor::ClientGridEditor;
+use flowy_error::{FlowyError, FlowyResult};
 use flowy_grid_data_model::entities::*;
 use lib_dispatch::prelude::{data_result, AppData, Data, DataResult};
 use std::sync::Arc;
@@ -97,13 +98,14 @@ pub(crate) async fn create_select_option_handler(
 }
 
 #[tracing::instrument(level = "debug", skip(data, manager), err)]
-pub(crate) async fn create_edit_field_context_handler(
-    data: Data<CreateEditFieldContextParams>,
+pub(crate) async fn edit_edit_field_context_handler(
+    data: Data<GetEditFieldContextParams>,
     manager: AppData<Arc<GridManager>>,
 ) -> DataResult<EditFieldContext, FlowyError> {
-    let params: CreateEditFieldContextParams = data.into_inner();
+    let params: GetEditFieldContextParams = data.into_inner();
     let editor = manager.get_grid_editor(&params.grid_id)?;
-    let field_meta = editor.default_field_meta(&params.field_type).await?;
+
+    let mut field_meta = get_or_create_field_meta(&params, editor).await?;
     let type_option_data = type_option_data_from_str(&field_meta.type_option_json, &field_meta.field_type);
     let field: Field = field_meta.into();
     let edit_context = EditFieldContext {
@@ -114,6 +116,19 @@ pub(crate) async fn create_edit_field_context_handler(
     data_result(edit_context)
 }
 
+async fn get_or_create_field_meta(
+    params: &GetEditFieldContextParams,
+    editor: Arc<ClientGridEditor>,
+) -> FlowyResult<FieldMeta> {
+    if params.field_id.is_some() {
+        if let Some(field_meta) = editor.get_field(params.field_id.as_ref().unwrap()).await? {
+            return Ok(field_meta);
+        }
+    }
+
+    editor.default_field_meta(&params.field_type).await
+}
+
 #[tracing::instrument(level = "debug", skip(data, manager), err)]
 pub(crate) async fn get_row_handler(
     data: Data<QueryRowPayload>,

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

@@ -15,7 +15,7 @@ pub fn create(grid_manager: Arc<GridManager>) -> Module {
         .event(GridEvent::CreateField, create_field_handler)
         .event(GridEvent::DeleteField, delete_field_handler)
         .event(GridEvent::DuplicateField, duplicate_field_handler)
-        .event(GridEvent::CreateEditFieldContext, create_edit_field_context_handler)
+        .event(GridEvent::GetEditFieldContext, edit_edit_field_context_handler)
         .event(GridEvent::CreateSelectOption, create_select_option_handler)
         .event(GridEvent::CreateRow, create_row_handler)
         .event(GridEvent::GetRow, get_row_handler)
@@ -48,8 +48,8 @@ pub enum GridEvent {
     #[event(input = "FieldIdentifierPayload")]
     DuplicateField = 15,
 
-    #[event(input = "CreateEditFieldContextParams", output = "EditFieldContext")]
-    CreateEditFieldContext = 16,
+    #[event(input = "GetEditFieldContextParams", output = "EditFieldContext")]
+    GetEditFieldContext = 16,
 
     #[event(input = "CreateSelectOptionPayload", output = "SelectOption")]
     CreateSelectOption = 30,

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

@@ -32,7 +32,7 @@ pub enum GridEvent {
     CreateField = 12,
     DeleteField = 13,
     DuplicateField = 15,
-    CreateEditFieldContext = 16,
+    GetEditFieldContext = 16,
     CreateSelectOption = 30,
     CreateRow = 50,
     GetRow = 51,
@@ -53,7 +53,7 @@ impl ::protobuf::ProtobufEnum for GridEvent {
             12 => ::std::option::Option::Some(GridEvent::CreateField),
             13 => ::std::option::Option::Some(GridEvent::DeleteField),
             15 => ::std::option::Option::Some(GridEvent::DuplicateField),
-            16 => ::std::option::Option::Some(GridEvent::CreateEditFieldContext),
+            16 => ::std::option::Option::Some(GridEvent::GetEditFieldContext),
             30 => ::std::option::Option::Some(GridEvent::CreateSelectOption),
             50 => ::std::option::Option::Some(GridEvent::CreateRow),
             51 => ::std::option::Option::Some(GridEvent::GetRow),
@@ -71,7 +71,7 @@ impl ::protobuf::ProtobufEnum for GridEvent {
             GridEvent::CreateField,
             GridEvent::DeleteField,
             GridEvent::DuplicateField,
-            GridEvent::CreateEditFieldContext,
+            GridEvent::GetEditFieldContext,
             GridEvent::CreateSelectOption,
             GridEvent::CreateRow,
             GridEvent::GetRow,
@@ -104,13 +104,13 @@ impl ::protobuf::reflect::ProtobufValue for GridEvent {
 }
 
 static file_descriptor_proto_data: &'static [u8] = b"\
-    \n\x0fevent_map.proto*\xe4\x01\n\tGridEvent\x12\x0f\n\x0bGetGridData\x10\
+    \n\x0fevent_map.proto*\xe1\x01\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\x12\n\x0eDuplicateField\x10\x0f\x12\x1a\n\x16CreateE\
-    ditFieldContext\x10\x10\x12\x16\n\x12CreateSelectOption\x10\x1e\x12\r\n\
-    \tCreateRow\x102\x12\n\n\x06GetRow\x103\x12\x0e\n\nUpdateCell\x10Fb\x06p\
-    roto3\
+    leteField\x10\r\x12\x12\n\x0eDuplicateField\x10\x0f\x12\x17\n\x13GetEdit\
+    FieldContext\x10\x10\x12\x16\n\x12CreateSelectOption\x10\x1e\x12\r\n\tCr\
+    eateRow\x102\x12\n\n\x06GetRow\x103\x12\x0e\n\nUpdateCell\x10Fb\x06proto\
+    3\
 ";
 
 static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;

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

@@ -8,7 +8,7 @@ enum GridEvent {
     CreateField = 12;
     DeleteField = 13;
     DuplicateField = 15;
-    CreateEditFieldContext = 16;
+    GetEditFieldContext = 16;
     CreateSelectOption = 30;
     CreateRow = 50;
     GetRow = 51;

+ 38 - 15
frontend/rust-lib/flowy-grid/src/services/grid_editor.rs

@@ -53,24 +53,40 @@ impl ClientGridEditor {
             field,
             type_option_data,
             start_field_id,
-            ..
+            grid_id,
         } = params;
 
-        let type_option_json = type_option_json_str_from_bytes(type_option_data, &field.field_type);
-
-        let field_meta = FieldMeta {
-            id: field.id,
-            name: field.name,
-            desc: field.desc,
-            field_type: field.field_type,
-            frozen: field.frozen,
-            visibility: field.visibility,
-            width: field.width,
-            type_option_json,
-        };
-
         let _ = self
-            .modify(|grid| Ok(grid.create_field(field_meta, start_field_id)?))
+            .modify(|grid| {
+                if grid.contain_field(&field.id) {
+                    let changeset = FieldChangesetParams {
+                        field_id: field.id,
+                        grid_id,
+                        name: Some(field.name),
+                        desc: Some(field.desc),
+                        field_type: Some(field.field_type),
+                        frozen: Some(field.frozen),
+                        visibility: Some(field.visibility),
+                        width: Some(field.width),
+                        type_option_data: Some(type_option_data),
+                    };
+
+                    Ok(grid.update_field(changeset)?)
+                } else {
+                    let type_option_json = type_option_json_str_from_bytes(type_option_data, &field.field_type);
+                    let field_meta = FieldMeta {
+                        id: field.id,
+                        name: field.name,
+                        desc: field.desc,
+                        field_type: field.field_type,
+                        frozen: field.frozen,
+                        visibility: field.visibility,
+                        width: field.width,
+                        type_option_json,
+                    };
+                    Ok(grid.create_field(field_meta, start_field_id)?)
+                }
+            })
             .await?;
         let _ = self.notify_did_update_fields().await?;
         Ok(())
@@ -116,6 +132,13 @@ impl ClientGridEditor {
         Ok(())
     }
 
+    pub async fn get_field(&self, field_id: &str) -> FlowyResult<Option<FieldMeta>> {
+        match self.pad.read().await.get_field(field_id) {
+            None => Ok(None),
+            Some(field_meta) => Ok(Some(field_meta.clone())),
+        }
+    }
+
     pub async fn create_block(&self, grid_block: GridBlockMeta) -> FlowyResult<()> {
         let _ = self.modify(|grid| Ok(grid.create_block(grid_block)?)).await?;
         Ok(())

+ 5 - 2
shared-lib/flowy-grid-data-model/src/entities/grid.rs

@@ -101,11 +101,14 @@ impl std::convert::From<&FieldMeta> for FieldOrder {
 }
 
 #[derive(Debug, Default, ProtoBuf)]
-pub struct CreateEditFieldContextParams {
+pub struct GetEditFieldContextParams {
     #[pb(index = 1)]
     pub grid_id: String,
 
-    #[pb(index = 2)]
+    #[pb(index = 2, one_of)]
+    pub field_id: Option<String>,
+
+    #[pb(index = 3)]
     pub field_type: FieldType,
 }
 

+ 150 - 67
shared-lib/flowy-grid-data-model/src/protobuf/model/grid.rs

@@ -1221,23 +1221,30 @@ impl ::protobuf::reflect::ProtobufValue for FieldOrder {
 }
 
 #[derive(PartialEq,Clone,Default)]
-pub struct CreateEditFieldContextParams {
+pub struct GetEditFieldContextParams {
     // message fields
     pub grid_id: ::std::string::String,
     pub field_type: super::meta::FieldType,
+    // message oneof groups
+    pub one_of_field_id: ::std::option::Option<GetEditFieldContextParams_oneof_one_of_field_id>,
     // special fields
     pub unknown_fields: ::protobuf::UnknownFields,
     pub cached_size: ::protobuf::CachedSize,
 }
 
-impl<'a> ::std::default::Default for &'a CreateEditFieldContextParams {
-    fn default() -> &'a CreateEditFieldContextParams {
-        <CreateEditFieldContextParams as ::protobuf::Message>::default_instance()
+impl<'a> ::std::default::Default for &'a GetEditFieldContextParams {
+    fn default() -> &'a GetEditFieldContextParams {
+        <GetEditFieldContextParams as ::protobuf::Message>::default_instance()
     }
 }
 
-impl CreateEditFieldContextParams {
-    pub fn new() -> CreateEditFieldContextParams {
+#[derive(Clone,PartialEq,Debug)]
+pub enum GetEditFieldContextParams_oneof_one_of_field_id {
+    field_id(::std::string::String),
+}
+
+impl GetEditFieldContextParams {
+    pub fn new() -> GetEditFieldContextParams {
         ::std::default::Default::default()
     }
 
@@ -1267,7 +1274,56 @@ impl CreateEditFieldContextParams {
         ::std::mem::replace(&mut self.grid_id, ::std::string::String::new())
     }
 
-    // .FieldType field_type = 2;
+    // string field_id = 2;
+
+
+    pub fn get_field_id(&self) -> &str {
+        match self.one_of_field_id {
+            ::std::option::Option::Some(GetEditFieldContextParams_oneof_one_of_field_id::field_id(ref v)) => v,
+            _ => "",
+        }
+    }
+    pub fn clear_field_id(&mut self) {
+        self.one_of_field_id = ::std::option::Option::None;
+    }
+
+    pub fn has_field_id(&self) -> bool {
+        match self.one_of_field_id {
+            ::std::option::Option::Some(GetEditFieldContextParams_oneof_one_of_field_id::field_id(..)) => true,
+            _ => false,
+        }
+    }
+
+    // Param is passed by value, moved
+    pub fn set_field_id(&mut self, v: ::std::string::String) {
+        self.one_of_field_id = ::std::option::Option::Some(GetEditFieldContextParams_oneof_one_of_field_id::field_id(v))
+    }
+
+    // Mutable pointer to the field.
+    pub fn mut_field_id(&mut self) -> &mut ::std::string::String {
+        if let ::std::option::Option::Some(GetEditFieldContextParams_oneof_one_of_field_id::field_id(_)) = self.one_of_field_id {
+        } else {
+            self.one_of_field_id = ::std::option::Option::Some(GetEditFieldContextParams_oneof_one_of_field_id::field_id(::std::string::String::new()));
+        }
+        match self.one_of_field_id {
+            ::std::option::Option::Some(GetEditFieldContextParams_oneof_one_of_field_id::field_id(ref mut v)) => v,
+            _ => panic!(),
+        }
+    }
+
+    // Take field
+    pub fn take_field_id(&mut self) -> ::std::string::String {
+        if self.has_field_id() {
+            match self.one_of_field_id.take() {
+                ::std::option::Option::Some(GetEditFieldContextParams_oneof_one_of_field_id::field_id(v)) => v,
+                _ => panic!(),
+            }
+        } else {
+            ::std::string::String::new()
+        }
+    }
+
+    // .FieldType field_type = 3;
 
 
     pub fn get_field_type(&self) -> super::meta::FieldType {
@@ -1283,7 +1339,7 @@ impl CreateEditFieldContextParams {
     }
 }
 
-impl ::protobuf::Message for CreateEditFieldContextParams {
+impl ::protobuf::Message for GetEditFieldContextParams {
     fn is_initialized(&self) -> bool {
         true
     }
@@ -1296,7 +1352,13 @@ impl ::protobuf::Message for CreateEditFieldContextParams {
                     ::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.grid_id)?;
                 },
                 2 => {
-                    ::protobuf::rt::read_proto3_enum_with_unknown_fields_into(wire_type, is, &mut self.field_type, 2, &mut self.unknown_fields)?
+                    if wire_type != ::protobuf::wire_format::WireTypeLengthDelimited {
+                        return ::std::result::Result::Err(::protobuf::rt::unexpected_wire_type(wire_type));
+                    }
+                    self.one_of_field_id = ::std::option::Option::Some(GetEditFieldContextParams_oneof_one_of_field_id::field_id(is.read_string()?));
+                },
+                3 => {
+                    ::protobuf::rt::read_proto3_enum_with_unknown_fields_into(wire_type, is, &mut self.field_type, 3, &mut self.unknown_fields)?
                 },
                 _ => {
                     ::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?;
@@ -1314,7 +1376,14 @@ impl ::protobuf::Message for CreateEditFieldContextParams {
             my_size += ::protobuf::rt::string_size(1, &self.grid_id);
         }
         if self.field_type != super::meta::FieldType::RichText {
-            my_size += ::protobuf::rt::enum_size(2, self.field_type);
+            my_size += ::protobuf::rt::enum_size(3, self.field_type);
+        }
+        if let ::std::option::Option::Some(ref v) = self.one_of_field_id {
+            match v {
+                &GetEditFieldContextParams_oneof_one_of_field_id::field_id(ref v) => {
+                    my_size += ::protobuf::rt::string_size(2, &v);
+                },
+            };
         }
         my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields());
         self.cached_size.set(my_size);
@@ -1326,7 +1395,14 @@ impl ::protobuf::Message for CreateEditFieldContextParams {
             os.write_string(1, &self.grid_id)?;
         }
         if self.field_type != super::meta::FieldType::RichText {
-            os.write_enum(2, ::protobuf::ProtobufEnum::value(&self.field_type))?;
+            os.write_enum(3, ::protobuf::ProtobufEnum::value(&self.field_type))?;
+        }
+        if let ::std::option::Option::Some(ref v) = self.one_of_field_id {
+            match v {
+                &GetEditFieldContextParams_oneof_one_of_field_id::field_id(ref v) => {
+                    os.write_string(2, v)?;
+                },
+            };
         }
         os.write_unknown_fields(self.get_unknown_fields())?;
         ::std::result::Result::Ok(())
@@ -1358,8 +1434,8 @@ impl ::protobuf::Message for CreateEditFieldContextParams {
         Self::descriptor_static()
     }
 
-    fn new() -> CreateEditFieldContextParams {
-        CreateEditFieldContextParams::new()
+    fn new() -> GetEditFieldContextParams {
+        GetEditFieldContextParams::new()
     }
 
     fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor {
@@ -1368,43 +1444,49 @@ impl ::protobuf::Message for CreateEditFieldContextParams {
             let mut fields = ::std::vec::Vec::new();
             fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
                 "grid_id",
-                |m: &CreateEditFieldContextParams| { &m.grid_id },
-                |m: &mut CreateEditFieldContextParams| { &mut m.grid_id },
+                |m: &GetEditFieldContextParams| { &m.grid_id },
+                |m: &mut GetEditFieldContextParams| { &mut m.grid_id },
+            ));
+            fields.push(::protobuf::reflect::accessor::make_singular_string_accessor::<_>(
+                "field_id",
+                GetEditFieldContextParams::has_field_id,
+                GetEditFieldContextParams::get_field_id,
             ));
             fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeEnum<super::meta::FieldType>>(
                 "field_type",
-                |m: &CreateEditFieldContextParams| { &m.field_type },
-                |m: &mut CreateEditFieldContextParams| { &mut m.field_type },
+                |m: &GetEditFieldContextParams| { &m.field_type },
+                |m: &mut GetEditFieldContextParams| { &mut m.field_type },
             ));
-            ::protobuf::reflect::MessageDescriptor::new_pb_name::<CreateEditFieldContextParams>(
-                "CreateEditFieldContextParams",
+            ::protobuf::reflect::MessageDescriptor::new_pb_name::<GetEditFieldContextParams>(
+                "GetEditFieldContextParams",
                 fields,
                 file_descriptor_proto()
             )
         })
     }
 
-    fn default_instance() -> &'static CreateEditFieldContextParams {
-        static instance: ::protobuf::rt::LazyV2<CreateEditFieldContextParams> = ::protobuf::rt::LazyV2::INIT;
-        instance.get(CreateEditFieldContextParams::new)
+    fn default_instance() -> &'static GetEditFieldContextParams {
+        static instance: ::protobuf::rt::LazyV2<GetEditFieldContextParams> = ::protobuf::rt::LazyV2::INIT;
+        instance.get(GetEditFieldContextParams::new)
     }
 }
 
-impl ::protobuf::Clear for CreateEditFieldContextParams {
+impl ::protobuf::Clear for GetEditFieldContextParams {
     fn clear(&mut self) {
         self.grid_id.clear();
+        self.one_of_field_id = ::std::option::Option::None;
         self.field_type = super::meta::FieldType::RichText;
         self.unknown_fields.clear();
     }
 }
 
-impl ::std::fmt::Debug for CreateEditFieldContextParams {
+impl ::std::fmt::Debug for GetEditFieldContextParams {
     fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
         ::protobuf::text_format::fmt(self, f)
     }
 }
 
-impl ::protobuf::reflect::ProtobufValue for CreateEditFieldContextParams {
+impl ::protobuf::reflect::ProtobufValue for GetEditFieldContextParams {
     fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
         ::protobuf::reflect::ReflectValueRef::Message(self)
     }
@@ -5431,48 +5513,49 @@ static file_descriptor_proto_data: &'static [u8] = b"\
     \x07grid_id\x18\x02\x20\x01(\tR\x06gridId\"K\n\x15FieldIdentifierParams\
     \x12\x19\n\x08field_id\x18\x01\x20\x01(\tR\x07fieldId\x12\x17\n\x07grid_\
     id\x18\x02\x20\x01(\tR\x06gridId\"'\n\nFieldOrder\x12\x19\n\x08field_id\
-    \x18\x01\x20\x01(\tR\x07fieldId\"b\n\x1cCreateEditFieldContextParams\x12\
-    \x17\n\x07grid_id\x18\x01\x20\x01(\tR\x06gridId\x12)\n\nfield_type\x18\
-    \x02\x20\x01(\x0e2\n.FieldTypeR\tfieldType\"|\n\x10EditFieldContext\x12\
-    \x17\n\x07grid_id\x18\x01\x20\x01(\tR\x06gridId\x12%\n\ngrid_field\x18\
-    \x02\x20\x01(\x0b2\x06.FieldR\tgridField\x12(\n\x10type_option_data\x18\
-    \x03\x20\x01(\x0cR\x0etypeOptionData\"-\n\rRepeatedField\x12\x1c\n\x05it\
-    ems\x18\x01\x20\x03(\x0b2\x06.FieldR\x05items\"7\n\x12RepeatedFieldOrder\
-    \x12!\n\x05items\x18\x01\x20\x03(\x0b2\x0b.FieldOrderR\x05items\"T\n\x08\
-    RowOrder\x12\x15\n\x06row_id\x18\x01\x20\x01(\tR\x05rowId\x12\x19\n\x08b\
-    lock_id\x18\x02\x20\x01(\tR\x07blockId\x12\x16\n\x06height\x18\x03\x20\
-    \x01(\x05R\x06height\"\xb8\x01\n\x03Row\x12\x0e\n\x02id\x18\x01\x20\x01(\
-    \tR\x02id\x12@\n\x10cell_by_field_id\x18\x02\x20\x03(\x0b2\x17.Row.CellB\
-    yFieldIdEntryR\rcellByFieldId\x12\x16\n\x06height\x18\x03\x20\x01(\x05R\
-    \x06height\x1aG\n\x12CellByFieldIdEntry\x12\x10\n\x03key\x18\x01\x20\x01\
-    (\tR\x03key\x12\x1b\n\x05value\x18\x02\x20\x01(\x0b2\x05.CellR\x05value:\
-    \x028\x01\")\n\x0bRepeatedRow\x12\x1a\n\x05items\x18\x01\x20\x03(\x0b2\
-    \x04.RowR\x05items\"5\n\x11RepeatedGridBlock\x12\x20\n\x05items\x18\x01\
-    \x20\x03(\x0b2\n.GridBlockR\x05items\"+\n\x0eGridBlockOrder\x12\x19\n\
-    \x08block_id\x18\x01\x20\x01(\tR\x07blockId\"E\n\tGridBlock\x12\x0e\n\
-    \x02id\x18\x01\x20\x01(\tR\x02id\x12(\n\nrow_orders\x18\x02\x20\x03(\x0b\
-    2\t.RowOrderR\trowOrders\";\n\x04Cell\x12\x19\n\x08field_id\x18\x01\x20\
-    \x01(\tR\x07fieldId\x12\x18\n\x07content\x18\x02\x20\x01(\tR\x07content\
-    \"+\n\x0cRepeatedCell\x12\x1b\n\x05items\x18\x01\x20\x03(\x0b2\x05.CellR\
-    \x05items\"'\n\x11CreateGridPayload\x12\x12\n\x04name\x18\x01\x20\x01(\t\
-    R\x04name\"\x1e\n\x06GridId\x12\x14\n\x05value\x18\x01\x20\x01(\tR\x05va\
-    lue\"#\n\x0bGridBlockId\x12\x14\n\x05value\x18\x01\x20\x01(\tR\x05value\
-    \"f\n\x10CreateRowPayload\x12\x17\n\x07grid_id\x18\x01\x20\x01(\tR\x06gr\
-    idId\x12\"\n\x0cstart_row_id\x18\x02\x20\x01(\tH\0R\nstartRowIdB\x15\n\
-    \x13one_of_start_row_id\"\xb6\x01\n\x12CreateFieldPayload\x12\x17\n\x07g\
-    rid_id\x18\x01\x20\x01(\tR\x06gridId\x12\x1c\n\x05field\x18\x02\x20\x01(\
-    \x0b2\x06.FieldR\x05field\x12(\n\x10type_option_data\x18\x03\x20\x01(\
-    \x0cR\x0etypeOptionData\x12&\n\x0estart_field_id\x18\x04\x20\x01(\tH\0R\
-    \x0cstartFieldIdB\x17\n\x15one_of_start_field_id\"d\n\x11QueryFieldPaylo\
-    ad\x12\x17\n\x07grid_id\x18\x01\x20\x01(\tR\x06gridId\x126\n\x0cfield_or\
-    ders\x18\x02\x20\x01(\x0b2\x13.RepeatedFieldOrderR\x0bfieldOrders\"e\n\
-    \x16QueryGridBlocksPayload\x12\x17\n\x07grid_id\x18\x01\x20\x01(\tR\x06g\
-    ridId\x122\n\x0cblock_orders\x18\x02\x20\x03(\x0b2\x0f.GridBlockOrderR\
-    \x0bblockOrders\"\\\n\x0fQueryRowPayload\x12\x17\n\x07grid_id\x18\x01\
-    \x20\x01(\tR\x06gridId\x12\x19\n\x08block_id\x18\x02\x20\x01(\tR\x07bloc\
-    kId\x12\x15\n\x06row_id\x18\x03\x20\x01(\tR\x05rowId\"<\n\x19CreateSelec\
-    tOptionPayload\x12\x1f\n\x0boption_name\x18\x01\x20\x01(\tR\noptionNameb\
-    \x06proto3\
+    \x18\x01\x20\x01(\tR\x07fieldId\"\x8f\x01\n\x19GetEditFieldContextParams\
+    \x12\x17\n\x07grid_id\x18\x01\x20\x01(\tR\x06gridId\x12\x1b\n\x08field_i\
+    d\x18\x02\x20\x01(\tH\0R\x07fieldId\x12)\n\nfield_type\x18\x03\x20\x01(\
+    \x0e2\n.FieldTypeR\tfieldTypeB\x11\n\x0fone_of_field_id\"|\n\x10EditFiel\
+    dContext\x12\x17\n\x07grid_id\x18\x01\x20\x01(\tR\x06gridId\x12%\n\ngrid\
+    _field\x18\x02\x20\x01(\x0b2\x06.FieldR\tgridField\x12(\n\x10type_option\
+    _data\x18\x03\x20\x01(\x0cR\x0etypeOptionData\"-\n\rRepeatedField\x12\
+    \x1c\n\x05items\x18\x01\x20\x03(\x0b2\x06.FieldR\x05items\"7\n\x12Repeat\
+    edFieldOrder\x12!\n\x05items\x18\x01\x20\x03(\x0b2\x0b.FieldOrderR\x05it\
+    ems\"T\n\x08RowOrder\x12\x15\n\x06row_id\x18\x01\x20\x01(\tR\x05rowId\
+    \x12\x19\n\x08block_id\x18\x02\x20\x01(\tR\x07blockId\x12\x16\n\x06heigh\
+    t\x18\x03\x20\x01(\x05R\x06height\"\xb8\x01\n\x03Row\x12\x0e\n\x02id\x18\
+    \x01\x20\x01(\tR\x02id\x12@\n\x10cell_by_field_id\x18\x02\x20\x03(\x0b2\
+    \x17.Row.CellByFieldIdEntryR\rcellByFieldId\x12\x16\n\x06height\x18\x03\
+    \x20\x01(\x05R\x06height\x1aG\n\x12CellByFieldIdEntry\x12\x10\n\x03key\
+    \x18\x01\x20\x01(\tR\x03key\x12\x1b\n\x05value\x18\x02\x20\x01(\x0b2\x05\
+    .CellR\x05value:\x028\x01\")\n\x0bRepeatedRow\x12\x1a\n\x05items\x18\x01\
+    \x20\x03(\x0b2\x04.RowR\x05items\"5\n\x11RepeatedGridBlock\x12\x20\n\x05\
+    items\x18\x01\x20\x03(\x0b2\n.GridBlockR\x05items\"+\n\x0eGridBlockOrder\
+    \x12\x19\n\x08block_id\x18\x01\x20\x01(\tR\x07blockId\"E\n\tGridBlock\
+    \x12\x0e\n\x02id\x18\x01\x20\x01(\tR\x02id\x12(\n\nrow_orders\x18\x02\
+    \x20\x03(\x0b2\t.RowOrderR\trowOrders\";\n\x04Cell\x12\x19\n\x08field_id\
+    \x18\x01\x20\x01(\tR\x07fieldId\x12\x18\n\x07content\x18\x02\x20\x01(\tR\
+    \x07content\"+\n\x0cRepeatedCell\x12\x1b\n\x05items\x18\x01\x20\x03(\x0b\
+    2\x05.CellR\x05items\"'\n\x11CreateGridPayload\x12\x12\n\x04name\x18\x01\
+    \x20\x01(\tR\x04name\"\x1e\n\x06GridId\x12\x14\n\x05value\x18\x01\x20\
+    \x01(\tR\x05value\"#\n\x0bGridBlockId\x12\x14\n\x05value\x18\x01\x20\x01\
+    (\tR\x05value\"f\n\x10CreateRowPayload\x12\x17\n\x07grid_id\x18\x01\x20\
+    \x01(\tR\x06gridId\x12\"\n\x0cstart_row_id\x18\x02\x20\x01(\tH\0R\nstart\
+    RowIdB\x15\n\x13one_of_start_row_id\"\xb6\x01\n\x12CreateFieldPayload\
+    \x12\x17\n\x07grid_id\x18\x01\x20\x01(\tR\x06gridId\x12\x1c\n\x05field\
+    \x18\x02\x20\x01(\x0b2\x06.FieldR\x05field\x12(\n\x10type_option_data\
+    \x18\x03\x20\x01(\x0cR\x0etypeOptionData\x12&\n\x0estart_field_id\x18\
+    \x04\x20\x01(\tH\0R\x0cstartFieldIdB\x17\n\x15one_of_start_field_id\"d\n\
+    \x11QueryFieldPayload\x12\x17\n\x07grid_id\x18\x01\x20\x01(\tR\x06gridId\
+    \x126\n\x0cfield_orders\x18\x02\x20\x01(\x0b2\x13.RepeatedFieldOrderR\
+    \x0bfieldOrders\"e\n\x16QueryGridBlocksPayload\x12\x17\n\x07grid_id\x18\
+    \x01\x20\x01(\tR\x06gridId\x122\n\x0cblock_orders\x18\x02\x20\x03(\x0b2\
+    \x0f.GridBlockOrderR\x0bblockOrders\"\\\n\x0fQueryRowPayload\x12\x17\n\
+    \x07grid_id\x18\x01\x20\x01(\tR\x06gridId\x12\x19\n\x08block_id\x18\x02\
+    \x20\x01(\tR\x07blockId\x12\x15\n\x06row_id\x18\x03\x20\x01(\tR\x05rowId\
+    \"<\n\x19CreateSelectOptionPayload\x12\x1f\n\x0boption_name\x18\x01\x20\
+    \x01(\tR\noptionNameb\x06proto3\
 ";
 
 static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;

+ 3 - 2
shared-lib/flowy-grid-data-model/src/protobuf/proto/grid.proto

@@ -26,9 +26,10 @@ message FieldIdentifierParams {
 message FieldOrder {
     string field_id = 1;
 }
-message CreateEditFieldContextParams {
+message GetEditFieldContextParams {
     string grid_id = 1;
-    FieldType field_type = 2;
+    oneof one_of_field_id { string field_id = 2; };
+    FieldType field_type = 3;
 }
 message EditFieldContext {
     string grid_id = 1;

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

@@ -45,7 +45,7 @@ impl GridMetaPad {
         self.modify_grid(|grid| {
             // Check if the field exists or not
             if grid.fields.iter().any(|field_meta| field_meta.id == new_field_meta.id) {
-                tracing::warn!("Duplicate grid field");
+                tracing::error!("Duplicate grid field");
                 return Ok(None);
             }