소스 검색

chore: add GridFieldContext

appflowy 3 년 전
부모
커밋
4840d23fff
18개의 변경된 파일351개의 추가작업 그리고 323개의 파일을 삭제
  1. 0 12
      frontend/app_flowy/lib/startup/deps_resolver.dart
  2. 11 4
      frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/context_builder.dart
  3. 18 19
      frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/data_loader.dart
  4. 21 94
      frontend/app_flowy/lib/workspace/application/grid/field/field_editor_bloc.dart
  5. 22 18
      frontend/app_flowy/lib/workspace/application/grid/field/field_editor_pannel_bloc.dart
  6. 111 0
      frontend/app_flowy/lib/workspace/application/grid/field/field_service.dart
  7. 9 7
      frontend/app_flowy/lib/workspace/application/grid/field/type_option/type_option_service.dart
  8. 34 36
      frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/select_option_cell/select_option_editor.dart
  9. 1 0
      frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_cell.dart
  10. 46 53
      frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_editor.dart
  11. 8 33
      frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_editor_pannel.dart
  12. 1 0
      frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/grid_header.dart
  13. 1 0
      frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/row/row_detail.dart
  14. 1 0
      frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/toolbar/grid_property.dart
  15. 9 4
      frontend/app_flowy/packages/flowy_infra_ui/lib/style_widget/hover.dart
  16. 1 1
      shared-lib/flowy-grid-data-model/src/entities/grid.rs
  17. 56 41
      shared-lib/flowy-grid-data-model/src/protobuf/model/grid.rs
  18. 1 1
      shared-lib/flowy-grid-data-model/src/protobuf/proto/grid.proto

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

@@ -15,7 +15,6 @@ import 'package:app_flowy/workspace/presentation/home/home_stack.dart';
 import 'package:app_flowy/workspace/presentation/home/menu/menu.dart';
 import 'package:flowy_sdk/protobuf/flowy-folder-data-model/app.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-folder-data-model/view.pb.dart';
-import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart' show FieldTypeOptionData;
 import 'package:flowy_sdk/protobuf/flowy-grid/date_type_option.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/number_type_option.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-user-data-model/user_profile.pb.dart';
@@ -157,13 +156,6 @@ void _resolveGridDeps(GetIt getIt) {
     ),
   );
 
-  getIt.registerFactoryParam<FieldEditorBloc, String, FieldContextLoader>(
-    (gridId, fieldLoader) => FieldEditorBloc(
-      gridId: gridId,
-      fieldLoader: fieldLoader,
-    ),
-  );
-
   getIt.registerFactoryParam<TextCellBloc, GridCellContext, void>(
     (context, _) => TextCellBloc(
       cellContext: context,
@@ -195,10 +187,6 @@ void _resolveGridDeps(GetIt getIt) {
     ),
   );
 
-  getIt.registerFactoryParam<FieldEditorPannelBloc, FieldTypeOptionData, void>(
-    (context, _) => FieldEditorPannelBloc(context),
-  );
-
   getIt.registerFactoryParam<DateTypeOptionBloc, DateTypeOption, void>(
     (typeOption, _) => DateTypeOptionBloc(typeOption: typeOption),
   );

+ 11 - 4
frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/context_builder.dart

@@ -19,7 +19,7 @@ class GridCellContextBuilder {
         return GridCellContext(
           gridCell: _gridCell,
           cellCache: _cellCache,
-          cellDataLoader: CellDataLoader(gridCell: _gridCell),
+          cellDataLoader: GridCellDataLoader(gridCell: _gridCell),
           cellDataPersistence: CellDataPersistence(gridCell: _gridCell),
         );
       case FieldType.DateTime:
@@ -30,17 +30,24 @@ class GridCellContextBuilder {
           cellDataPersistence: DateCellDataPersistence(gridCell: _gridCell),
         );
       case FieldType.Number:
+        final cellDataLoader = GridCellDataLoader(
+          gridCell: _gridCell,
+          config: const GridCellDataConfig(
+            reloadOnCellChanged: true,
+            reloadOnFieldChanged: true,
+          ),
+        );
         return GridCellContext(
           gridCell: _gridCell,
           cellCache: _cellCache,
-          cellDataLoader: CellDataLoader(gridCell: _gridCell, reloadOnCellChanged: true),
+          cellDataLoader: cellDataLoader,
           cellDataPersistence: CellDataPersistence(gridCell: _gridCell),
         );
       case FieldType.RichText:
         return GridCellContext(
           gridCell: _gridCell,
           cellCache: _cellCache,
-          cellDataLoader: CellDataLoader(gridCell: _gridCell),
+          cellDataLoader: GridCellDataLoader(gridCell: _gridCell),
           cellDataPersistence: CellDataPersistence(gridCell: _gridCell),
         );
       case FieldType.MultiSelect:
@@ -62,7 +69,7 @@ class _GridCellContext<T, D> extends Equatable {
   final GridCell gridCell;
   final GridCellCache cellCache;
   final GridCellCacheKey _cacheKey;
-  final _GridCellDataLoader<T> cellDataLoader;
+  final IGridCellDataLoader<T> cellDataLoader;
   final _GridCellDataPersistence<D> cellDataPersistence;
   final FieldService _fieldService;
 

+ 18 - 19
frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/data_loader.dart

@@ -1,6 +1,6 @@
 part of 'cell_service.dart';
 
-abstract class GridCellDataConfig {
+abstract class IGridCellDataConfig {
   // The cell data will reload if it receives the field's change notification.
   bool get reloadOnFieldChanged;
 
@@ -11,34 +11,36 @@ abstract class GridCellDataConfig {
   bool get reloadOnCellChanged;
 }
 
-class DefaultCellDataConfig implements GridCellDataConfig {
+class GridCellDataConfig implements IGridCellDataConfig {
   @override
   final bool reloadOnCellChanged;
 
   @override
   final bool reloadOnFieldChanged;
 
-  DefaultCellDataConfig({
+  const GridCellDataConfig({
     this.reloadOnCellChanged = false,
     this.reloadOnFieldChanged = false,
   });
 }
 
-abstract class _GridCellDataLoader<T> {
+abstract class IGridCellDataLoader<T> {
   Future<T?> loadData();
 
-  GridCellDataConfig get config;
+  IGridCellDataConfig get config;
 }
 
-class CellDataLoader extends _GridCellDataLoader<Cell> {
+class GridCellDataLoader extends IGridCellDataLoader<Cell> {
   final CellService service = CellService();
   final GridCell gridCell;
-  final GridCellDataConfig _config;
 
-  CellDataLoader({
+  @override
+  final IGridCellDataConfig config;
+
+  GridCellDataLoader({
     required this.gridCell,
-    bool reloadOnCellChanged = false,
-  }) : _config = DefaultCellDataConfig(reloadOnCellChanged: reloadOnCellChanged);
+    this.config = const GridCellDataConfig(),
+  });
 
   @override
   Future<Cell?> loadData() {
@@ -54,20 +56,17 @@ class CellDataLoader extends _GridCellDataLoader<Cell> {
       });
     });
   }
-
-  @override
-  GridCellDataConfig get config => _config;
 }
 
-class DateCellDataLoader extends _GridCellDataLoader<DateCellData> {
+class DateCellDataLoader extends IGridCellDataLoader<DateCellData> {
   final GridCell gridCell;
-  final GridCellDataConfig _config;
+  final IGridCellDataConfig _config;
   DateCellDataLoader({
     required this.gridCell,
-  }) : _config = DefaultCellDataConfig(reloadOnFieldChanged: true);
+  }) : _config = const GridCellDataConfig(reloadOnFieldChanged: true);
 
   @override
-  GridCellDataConfig get config => _config;
+  IGridCellDataConfig get config => _config;
 
   @override
   Future<DateCellData?> loadData() {
@@ -88,7 +87,7 @@ class DateCellDataLoader extends _GridCellDataLoader<DateCellData> {
   }
 }
 
-class SelectOptionCellDataLoader extends _GridCellDataLoader<SelectOptionCellData> {
+class SelectOptionCellDataLoader extends IGridCellDataLoader<SelectOptionCellData> {
   final SelectOptionService service;
   final GridCell gridCell;
   SelectOptionCellDataLoader({
@@ -108,5 +107,5 @@ class SelectOptionCellDataLoader extends _GridCellDataLoader<SelectOptionCellDat
   }
 
   @override
-  GridCellDataConfig get config => DefaultCellDataConfig();
+  IGridCellDataConfig get config => const GridCellDataConfig(reloadOnFieldChanged: true);
 }

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

@@ -1,40 +1,31 @@
-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';
-import 'package:protobuf/protobuf.dart';
-
 part 'field_editor_bloc.freezed.dart';
 
 class FieldEditorBloc extends Bloc<FieldEditorEvent, FieldEditorState> {
-  final String gridId;
-  final FieldContextLoader _loader;
-
   FieldEditorBloc({
-    required this.gridId,
-    required FieldContextLoader fieldLoader,
-  })  : _loader = fieldLoader,
-        super(FieldEditorState.initial(gridId)) {
+    required String gridId,
+    required String fieldName,
+    required FieldContextLoader fieldContextLoader,
+  }) : super(FieldEditorState.initial(gridId, fieldName, fieldContextLoader)) {
     on<FieldEditorEvent>(
       (event, emit) async {
-        await event.map(
-          initial: (_InitialField value) async {
-            await _getFieldTypeOptionContext(emit);
+        await event.when(
+          initial: () async {
+            final fieldContext = GridFieldContext(gridId: gridId, loader: fieldContextLoader);
+            await fieldContext.loadData().then((result) {
+              result.fold(
+                (l) => emit(state.copyWith(fieldContext: Some(fieldContext))),
+                (r) => null,
+              );
+            });
           },
-          updateName: (_UpdateName value) {
-            final newContext = _updateEditContext(name: value.name);
-            emit(state.copyWith(fieldTypeOptionData: newContext));
-          },
-          updateField: (_UpdateField value) {
-            final data = _updateEditContext(field: value.field, typeOptionData: value.typeOptionData);
-            emit(state.copyWith(fieldTypeOptionData: data));
-          },
-          done: (_Done value) async {
-            await _saveField(emit);
+          updateName: (name) {
+            state.fieldContext.fold(() => null, (fieldContext) => fieldContext.fieldName = name);
+            emit(state.copyWith(name: name));
           },
         );
       },
@@ -45,78 +36,12 @@ class FieldEditorBloc extends Bloc<FieldEditorEvent, FieldEditorState> {
   Future<void> close() async {
     return super.close();
   }
-
-  Option<FieldTypeOptionData> _updateEditContext({
-    String? name,
-    Field? field,
-    List<int>? typeOptionData,
-  }) {
-    return state.fieldTypeOptionData.fold(
-      () => none(),
-      (context) {
-        context.freeze();
-        final newFieldTypeOptionData = context.rebuild((newContext) {
-          newContext.field_2.rebuild((newField) {
-            if (name != null) {
-              newField.name = name;
-            }
-
-            newContext.field_2 = newField;
-          });
-
-          if (field != null) {
-            newContext.field_2 = field;
-          }
-
-          if (typeOptionData != null) {
-            newContext.typeOptionData = typeOptionData;
-          }
-        });
-
-        FieldService.insertField(
-          gridId: gridId,
-          field: newFieldTypeOptionData.field_2,
-          typeOptionData: newFieldTypeOptionData.typeOptionData,
-        );
-
-        return Some(newFieldTypeOptionData);
-      },
-    );
-  }
-
-  Future<void> _saveField(Emitter<FieldEditorState> emit) async {
-    await state.fieldTypeOptionData.fold(
-      () async => null,
-      (data) async {
-        final result = await FieldService.insertField(
-          gridId: gridId,
-          field: data.field_2,
-          typeOptionData: data.typeOptionData,
-        );
-        result.fold((l) => null, (r) => null);
-      },
-    );
-  }
-
-  Future<void> _getFieldTypeOptionContext(Emitter<FieldEditorState> emit) async {
-    final result = await _loader.load();
-    result.fold(
-      (context) {
-        emit(state.copyWith(
-          fieldTypeOptionData: Some(context),
-        ));
-      },
-      (err) => Log.error(err),
-    );
-  }
 }
 
 @freezed
 class FieldEditorEvent with _$FieldEditorEvent {
   const factory FieldEditorEvent.initial() = _InitialField;
   const factory FieldEditorEvent.updateName(String name) = _UpdateName;
-  const factory FieldEditorEvent.updateField(Field field, Uint8List typeOptionData) = _UpdateField;
-  const factory FieldEditorEvent.done() = _Done;
 }
 
 @freezed
@@ -124,12 +49,14 @@ class FieldEditorState with _$FieldEditorState {
   const factory FieldEditorState({
     required String gridId,
     required String errorText,
-    required Option<FieldTypeOptionData> fieldTypeOptionData,
+    required String name,
+    required Option<GridFieldContext> fieldContext,
   }) = _FieldEditorState;
 
-  factory FieldEditorState.initial(String gridId) => FieldEditorState(
+  factory FieldEditorState.initial(String gridId, String fieldName, FieldContextLoader loader) => FieldEditorState(
         gridId: gridId,
-        fieldTypeOptionData: none(),
+        fieldContext: none(),
         errorText: '',
+        name: fieldName,
       );
 }

+ 22 - 18
frontend/app_flowy/lib/workspace/application/grid/field/field_editor_pannel_bloc.dart

@@ -1,24 +1,29 @@
-import 'dart:typed_data';
 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 'field_editor_pannel_bloc.freezed.dart';
 
 class FieldEditorPannelBloc extends Bloc<FieldEditorPannelEvent, FieldEditorPannelState> {
-  FieldEditorPannelBloc(FieldTypeOptionData editContext) : super(FieldEditorPannelState.initial(editContext)) {
+  final GridFieldContext _fieldContext;
+  void Function()? _fieldListenFn;
+
+  FieldEditorPannelBloc(GridFieldContext fieldContext)
+      : _fieldContext = fieldContext,
+        super(FieldEditorPannelState.initial(fieldContext)) {
     on<FieldEditorPannelEvent>(
       (event, emit) async {
-        await event.map(
-          toFieldType: (_ToFieldType value) async {
-            emit(state.copyWith(
-              field: value.field,
-              typeOptionData: Uint8List.fromList(value.typeOptionData),
-            ));
+        event.when(
+          initial: () {
+            _fieldListenFn = fieldContext.addFieldListener((field) {
+              add(FieldEditorPannelEvent.didReceiveFieldUpdated(field));
+            });
           },
-          didUpdateTypeOptionData: (_DidUpdateTypeOptionData value) {
-            emit(state.copyWith(typeOptionData: value.typeOptionData));
+          didReceiveFieldUpdated: (field) {
+            emit(state.copyWith(field: field));
           },
         );
       },
@@ -27,27 +32,26 @@ class FieldEditorPannelBloc extends Bloc<FieldEditorPannelEvent, FieldEditorPann
 
   @override
   Future<void> close() async {
+    if (_fieldListenFn != null) {
+      _fieldContext.removeFieldListener(_fieldListenFn!);
+    }
     return super.close();
   }
 }
 
 @freezed
 class FieldEditorPannelEvent with _$FieldEditorPannelEvent {
-  const factory FieldEditorPannelEvent.toFieldType(Field field, List<int> typeOptionData) = _ToFieldType;
-  const factory FieldEditorPannelEvent.didUpdateTypeOptionData(Uint8List typeOptionData) = _DidUpdateTypeOptionData;
+  const factory FieldEditorPannelEvent.initial() = _Initial;
+  const factory FieldEditorPannelEvent.didReceiveFieldUpdated(Field field) = _DidReceiveFieldUpdated;
 }
 
 @freezed
 class FieldEditorPannelState with _$FieldEditorPannelState {
   const factory FieldEditorPannelState({
-    required String gridId,
     required Field field,
-    required Uint8List typeOptionData,
   }) = _FieldEditorPannelState;
 
-  factory FieldEditorPannelState.initial(FieldTypeOptionData data) => FieldEditorPannelState(
-        gridId: data.gridId,
-        field: data.field_2,
-        typeOptionData: Uint8List.fromList(data.typeOptionData),
+  factory FieldEditorPannelState.initial(GridFieldContext fieldContext) => FieldEditorPannelState(
+        field: fieldContext.field,
       );
 }

+ 111 - 0
frontend/app_flowy/lib/workspace/application/grid/field/field_service.dart

@@ -1,9 +1,12 @@
 import 'package:dartz/dartz.dart';
 import 'package:flowy_sdk/dispatch/dispatch.dart';
+import 'package:flowy_sdk/log.dart';
 import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
+import 'package:flutter/foundation.dart';
 import 'package:freezed_annotation/freezed_annotation.dart';
+import 'package:protobuf/protobuf.dart';
 part 'field_service.freezed.dart';
 
 class FieldService {
@@ -199,3 +202,111 @@ class DefaultFieldContextLoader extends FieldContextLoader {
     return fieldService.switchToField(fieldType);
   }
 }
+
+class GridFieldContext {
+  final String gridId;
+  final FieldContextLoader _loader;
+
+  late FieldTypeOptionData _data;
+  ValueNotifier<Field>? _fieldNotifier;
+
+  GridFieldContext({
+    required this.gridId,
+    required FieldContextLoader loader,
+  }) : _loader = loader;
+
+  Future<Either<Unit, FlowyError>> loadData() async {
+    final result = await _loader.load();
+    return result.fold(
+      (data) {
+        data.freeze();
+        _data = data;
+
+        if (_fieldNotifier == null) {
+          _fieldNotifier = ValueNotifier(data.field_2);
+        } else {
+          _fieldNotifier?.value = data.field_2;
+        }
+
+        return left(unit);
+      },
+      (err) {
+        Log.error(err);
+        return right(err);
+      },
+    );
+  }
+
+  Field get field => _data.field_2;
+
+  set field(Field field) {
+    _updateData(newField: field);
+  }
+
+  List<int> get typeOptionData => _data.typeOptionData;
+
+  set fieldName(String name) {
+    _updateData(name: name);
+  }
+
+  set typeOptionData(List<int> typeOptionData) {
+    _updateData(typeOptionData: typeOptionData);
+  }
+
+  void _updateData({String? name, Field? newField, List<int>? typeOptionData}) {
+    _data = _data.rebuild((rebuildData) {
+      if (name != null) {
+        rebuildData.field_2 = rebuildData.field_2.rebuild((rebuildField) {
+          rebuildField.name = name;
+        });
+      }
+
+      if (newField != null) {
+        rebuildData.field_2 = newField;
+      }
+
+      if (typeOptionData != null) {
+        rebuildData.typeOptionData = typeOptionData;
+      }
+    });
+
+    if (_data.field_2 != _fieldNotifier?.value) {
+      _fieldNotifier?.value = _data.field_2;
+    }
+
+    FieldService.insertField(
+      gridId: gridId,
+      field: field,
+      typeOptionData: typeOptionData,
+    );
+  }
+
+  Future<void> switchToField(FieldType newFieldType) {
+    return _loader.switchToField(field.id, newFieldType).then((result) {
+      return result.fold(
+        (fieldTypeOptionData) {
+          _updateData(
+            newField: fieldTypeOptionData.field_2,
+            typeOptionData: fieldTypeOptionData.typeOptionData,
+          );
+        },
+        (err) {
+          Log.error(err);
+        },
+      );
+    });
+  }
+
+  void Function() addFieldListener(void Function(Field) callback) {
+    listener() {
+      callback(field);
+    }
+
+    _fieldNotifier?.addListener(listener);
+    return listener;
+  }
+
+  void removeFieldListener(void Function() listener) {
+    _fieldNotifier?.removeListener(listener);
+  }
+}

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

@@ -38,15 +38,17 @@ abstract class TypeOptionDataBuilder<T> {
 }
 
 class TypeOptionContext {
-  final String gridId;
-  final Field field;
-  final Uint8List data;
+  final GridFieldContext _fieldContext;
 
   TypeOptionContext({
-    required this.gridId,
-    required this.field,
-    required this.data,
-  });
+    required GridFieldContext fieldContext,
+  }) : _fieldContext = fieldContext;
+
+  String get gridId => _fieldContext.gridId;
+
+  Field get field => _fieldContext.field;
+
+  Uint8List get data => Uint8List.fromList(_fieldContext.typeOptionData);
 }
 
 abstract class TypeOptionFieldDelegate {

+ 34 - 36
frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/select_option_cell/select_option_editor.dart

@@ -224,48 +224,46 @@ class _SelectOptionCell extends StatelessWidget {
     final theme = context.watch<AppTheme>();
     return SizedBox(
       height: GridSize.typeOptionItemHeight,
-      child: Stack(
-        fit: StackFit.expand,
+      child: Row(
         children: [
-          _body(theme, context),
-          InkWell(
-            onTap: () {
-              context.read<SelectOptionCellEditorBloc>().add(SelectOptionEditorEvent.selectOption(option.id));
-            },
-          ),
-        ],
-      ),
-    );
-  }
-
-  FlowyHover _body(AppTheme theme, BuildContext context) {
-    return FlowyHover(
-      style: HoverStyle(hoverColor: theme.hover),
-      builder: (_, onHover) {
-        List<Widget> children = [
-          SelectOptionTag(
-            name: option.name,
-            color: option.color.make(context),
-            isSelected: isSelected,
-          ),
-          const Spacer(),
-        ];
-
-        if (isSelected) {
-          children.add(svgWidget("grid/checkmark"));
-        }
-
-        if (onHover) {
-          children.add(FlowyIconButton(
+          Expanded(child: _body(theme, context)),
+          FlowyIconButton(
             width: 30,
             onPressed: () => _showEditPannel(context),
             iconPadding: const EdgeInsets.fromLTRB(4, 4, 4, 4),
             icon: svgWidget("editor/details", color: theme.iconColor),
-          ));
-        }
+          )
+        ],
+      ),
+    );
+  }
 
-        return Row(children: children);
-      },
+  Widget _body(AppTheme theme, BuildContext context) {
+    return Stack(
+      fit: StackFit.expand,
+      children: [
+        FlowyHover(
+          style: HoverStyle(hoverColor: theme.hover),
+          builder: (_, onHover) {
+            return InkWell(
+              child: Row(children: [
+                const HSpace(6),
+                SelectOptionTag(
+                  name: option.name,
+                  color: option.color.make(context),
+                  isSelected: isSelected,
+                ),
+                const Spacer(),
+                if (isSelected) svgWidget("grid/checkmark"),
+                const HSpace(6),
+              ]),
+              onTap: () {
+                context.read<SelectOptionCellEditorBloc>().add(SelectOptionEditorEvent.selectOption(option.id));
+              },
+            );
+          },
+        ),
+      ],
     );
   }
 

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

@@ -63,6 +63,7 @@ class GridFieldCell extends StatelessWidget {
 
     FieldEditor(
       gridId: state.gridId,
+      fieldName: state.field.name,
       contextLoader: DefaultFieldContextLoader(
         gridId: state.gridId,
         field: state.field,

+ 46 - 53
frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_editor.dart

@@ -1,4 +1,3 @@
-import 'package:app_flowy/startup/startup.dart';
 import 'package:app_flowy/workspace/application/grid/field/field_editor_bloc.dart';
 import 'package:app_flowy/workspace/application/grid/field/field_service.dart';
 import 'package:easy_localization/easy_localization.dart';
@@ -11,16 +10,42 @@ import 'package:app_flowy/generated/locale_keys.g.dart';
 import 'field_name_input.dart';
 import 'field_editor_pannel.dart';
 
-class FieldEditor extends FlowyOverlayDelegate {
+class FieldEditor extends StatelessWidget with FlowyOverlayDelegate {
   final String gridId;
-  final FieldEditorBloc _fieldEditorBloc;
+  final String fieldName;
+
   final FieldContextLoader contextLoader;
-  FieldEditor({
+  const FieldEditor({
     required this.gridId,
+    required this.fieldName,
     required this.contextLoader,
     Key? key,
-  }) : _fieldEditorBloc = getIt<FieldEditorBloc>(param1: gridId, param2: contextLoader) {
-    _fieldEditorBloc.add(const FieldEditorEvent.initial());
+  }) : super(key: key);
+
+  @override
+  Widget build(BuildContext context) {
+    return BlocProvider(
+      create: (context) => FieldEditorBloc(
+        gridId: gridId,
+        fieldName: fieldName,
+        fieldContextLoader: contextLoader,
+      )..add(const FieldEditorEvent.initial()),
+      child: BlocBuilder<FieldEditorBloc, FieldEditorState>(
+        buildWhen: (p, c) => false,
+        builder: (context, state) {
+          return ListView(
+            shrinkWrap: true,
+            children: [
+              FlowyText.medium(LocaleKeys.grid_field_editProperty.tr(), fontSize: 12),
+              const VSpace(10),
+              const _FieldNameTextField(),
+              const VSpace(10),
+              const _FieldPannel(),
+            ],
+          );
+        },
+      ),
+    );
   }
 
   void show(
@@ -28,10 +53,9 @@ class FieldEditor extends FlowyOverlayDelegate {
     AnchorDirection anchorDirection = AnchorDirection.bottomWithLeftAligned,
   }) {
     FlowyOverlay.of(context).remove(identifier());
-    final child = _FieldEditorPage(_fieldEditorBloc, contextLoader);
     FlowyOverlay.of(context).insertWithAnchor(
       widget: OverlayContainer(
-        child: child,
+        child: this,
         constraints: BoxConstraints.loose(const Size(280, 400)),
       ),
       identifier: identifier(),
@@ -46,49 +70,23 @@ class FieldEditor extends FlowyOverlayDelegate {
     return (FieldEditor).toString();
   }
 
-  @override
-  void didRemove() {
-    _fieldEditorBloc.add(const FieldEditorEvent.done());
-  }
-
   @override
   bool asBarrier() => true;
 }
 
-class _FieldEditorPage extends StatelessWidget {
-  final FieldEditorBloc editorBloc;
-  final FieldContextLoader contextLoader;
-  const _FieldEditorPage(this.editorBloc, this.contextLoader, {Key? key}) : super(key: key);
+class _FieldPannel extends StatelessWidget {
+  const _FieldPannel({Key? key}) : super(key: key);
 
   @override
   Widget build(BuildContext context) {
-    return BlocProvider.value(
-      value: editorBloc,
-      child: BlocBuilder<FieldEditorBloc, FieldEditorState>(
-        builder: (context, state) {
-          return state.fieldTypeOptionData.fold(
-            () => const SizedBox(),
-            (fieldTypeOptionContext) => ListView(
-              shrinkWrap: true,
-              children: [
-                FlowyText.medium(LocaleKeys.grid_field_editProperty.tr(), fontSize: 12),
-                const VSpace(10),
-                const _FieldNameTextField(),
-                const VSpace(10),
-                FieldEditorPannel(
-                  fieldTypeOptionData: fieldTypeOptionContext,
-                  onSwitchToField: (fieldId, fieldType) {
-                    return contextLoader.switchToField(fieldId, fieldType);
-                  },
-                  onUpdated: (field, typeOptionData) {
-                    context.read<FieldEditorBloc>().add(FieldEditorEvent.updateField(field, typeOptionData));
-                  },
-                ),
-              ],
-            ),
-          );
-        },
-      ),
+    return BlocBuilder<FieldEditorBloc, FieldEditorState>(
+      buildWhen: (p, c) => p.fieldContext != c.fieldContext,
+      builder: (context, state) {
+        return state.fieldContext.fold(
+          () => const SizedBox(),
+          (fieldContext) => FieldEditorPannel(fieldContext: fieldContext),
+        );
+      },
     );
   }
 }
@@ -98,16 +96,11 @@ class _FieldNameTextField extends StatelessWidget {
 
   @override
   Widget build(BuildContext context) {
-    return BlocSelector<FieldEditorBloc, FieldEditorState, String>(
-      selector: (state) {
-        return state.fieldTypeOptionData.fold(
-          () => "",
-          (fieldTypeOptionContext) => fieldTypeOptionContext.field_2.name,
-        );
-      },
-      builder: (context, name) {
+    return BlocBuilder<FieldEditorBloc, FieldEditorState>(
+      buildWhen: (p, c) => p.name != c.name,
+      builder: (context, state) {
         return FieldNameTextField(
-          name: name,
+          name: state.name,
           errorText: context.read<FieldEditorBloc>().state.errorText,
           onNameChanged: (newName) {
             context.read<FieldEditorBloc>().add(FieldEditorEvent.updateName(newName));

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

@@ -7,14 +7,12 @@ 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_sdk/log.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/checkbox_type_option.pbserver.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/text_type_option.pb.dart';
 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/layout/sizes.dart';
 import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/header/field_type_list.dart';
@@ -31,14 +29,10 @@ typedef SwitchToFieldCallback = Future<Either<FieldTypeOptionData, FlowyError>>
 );
 
 class FieldEditorPannel extends StatefulWidget {
-  final FieldTypeOptionData fieldTypeOptionData;
-  final UpdateFieldCallback onUpdated;
-  final SwitchToFieldCallback onSwitchToField;
+  final GridFieldContext fieldContext;
 
   const FieldEditorPannel({
-    required this.fieldTypeOptionData,
-    required this.onUpdated,
-    required this.onSwitchToField,
+    required this.fieldContext,
     Key? key,
   }) : super(key: key);
 
@@ -52,13 +46,10 @@ class _FieldEditorPannelState extends State<FieldEditorPannel> {
   @override
   Widget build(BuildContext context) {
     return BlocProvider(
-      create: (context) => getIt<FieldEditorPannelBloc>(param1: widget.fieldTypeOptionData),
-      child: BlocConsumer<FieldEditorPannelBloc, FieldEditorPannelState>(
-        listener: (context, state) {
-          widget.onUpdated(state.field, state.typeOptionData);
-        },
+      create: (context) => FieldEditorPannelBloc(widget.fieldContext)..add(const FieldEditorPannelEvent.initial()),
+      child: BlocBuilder<FieldEditorPannelBloc, FieldEditorPannelState>(
         builder: (context, state) {
-          List<Widget> children = [_switchFieldTypeButton(context, state.field)];
+          List<Widget> children = [_switchFieldTypeButton(context, widget.fieldContext.field)];
           final typeOptionWidget = _typeOptionWidget(context: context, state: state);
 
           if (typeOptionWidget != null) {
@@ -84,19 +75,7 @@ class _FieldEditorPannelState extends State<FieldEditorPannel> {
         hoverColor: theme.hover,
         onTap: () {
           final list = FieldTypeList(onSelectField: (newFieldType) {
-            widget.onSwitchToField(field.id, newFieldType).then((result) {
-              result.fold(
-                (fieldTypeOptionContext) {
-                  context.read<FieldEditorPannelBloc>().add(
-                        FieldEditorPannelEvent.toFieldType(
-                          fieldTypeOptionContext.field_2,
-                          fieldTypeOptionContext.typeOptionData,
-                        ),
-                      );
-                },
-                (err) => Log.error(err),
-              );
-            });
+            widget.fieldContext.switchToField(newFieldType);
           });
           _showOverlay(context, list);
         },
@@ -116,15 +95,11 @@ class _FieldEditorPannelState extends State<FieldEditorPannel> {
     );
 
     final dataDelegate = TypeOptionDataDelegate(didUpdateTypeOptionData: (data) {
-      context.read<FieldEditorPannelBloc>().add(FieldEditorPannelEvent.didUpdateTypeOptionData(data));
+      widget.fieldContext.typeOptionData = data;
     });
 
     final builder = _makeTypeOptionBuild(
-      typeOptionContext: TypeOptionContext(
-        gridId: state.gridId,
-        field: state.field,
-        data: state.typeOptionData,
-      ),
+      typeOptionContext: TypeOptionContext(fieldContext: widget.fieldContext),
       overlayDelegate: overlayDelegate,
       dataDelegate: dataDelegate,
     );

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

@@ -150,6 +150,7 @@ class CreateFieldButton extends StatelessWidget {
       hoverColor: theme.hover,
       onTap: () => FieldEditor(
         gridId: gridId,
+        fieldName: "",
         contextLoader: NewFieldContextLoader(gridId: gridId),
       ).show(context),
       leftIcon: svgWidget("home/add"),

+ 1 - 0
frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/row/row_detail.dart

@@ -178,6 +178,7 @@ class _RowDetailCell extends StatelessWidget {
   void _showFieldEditor(BuildContext context) {
     FieldEditor(
       gridId: gridCell.gridId,
+      fieldName: gridCell.field.name,
       contextLoader: DefaultFieldContextLoader(
         gridId: gridCell.gridId,
         field: gridCell.field,

+ 1 - 0
frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/toolbar/grid_property.dart

@@ -115,6 +115,7 @@ class _GridPropertyCell extends StatelessWidget {
       onTap: () {
         FieldEditor(
           gridId: gridId,
+          fieldName: field.name,
           contextLoader: DefaultFieldContextLoader(gridId: gridId, field: field),
         ).show(context, anchorDirection: AnchorDirection.bottomRight);
       },

+ 9 - 4
frontend/app_flowy/packages/flowy_infra_ui/lib/style_widget/hover.dart

@@ -109,7 +109,7 @@ abstract class HoverWidget extends StatefulWidget {
 }
 
 class FlowyHover2 extends StatefulWidget {
-  final HoverWidget child;
+  final Widget child;
   final EdgeInsets contentPadding;
   const FlowyHover2({
     required this.child,
@@ -127,9 +127,14 @@ class _FlowyHover2State extends State<FlowyHover2> {
   @override
   void initState() {
     _hoverState = FlowyHoverState();
-    widget.child.onFocus.addListener(() {
-      _hoverState.onFocus = widget.child.onFocus.value;
-    });
+
+    if (widget.child is HoverWidget) {
+      final hoverWidget = widget.child as HoverWidget;
+      hoverWidget.onFocus.addListener(() {
+        _hoverState.onFocus = hoverWidget.onFocus.value;
+      });
+    }
+
     super.initState();
   }
 

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

@@ -212,7 +212,7 @@ pub struct FieldTypeOptionData {
     pub grid_id: String,
 
     #[pb(index = 2)]
-    pub field_id: String,
+    pub field: Field,
 
     #[pb(index = 3)]
     pub type_option_data: Vec<u8>,

+ 56 - 41
shared-lib/flowy-grid-data-model/src/protobuf/model/grid.rs

@@ -2174,7 +2174,7 @@ impl ::protobuf::reflect::ProtobufValue for FieldTypeOptionContext {
 pub struct FieldTypeOptionData {
     // message fields
     pub grid_id: ::std::string::String,
-    pub field_id: ::std::string::String,
+    pub field: ::protobuf::SingularPtrField<Field>,
     pub type_option_data: ::std::vec::Vec<u8>,
     // special fields
     pub unknown_fields: ::protobuf::UnknownFields,
@@ -2218,30 +2218,37 @@ impl FieldTypeOptionData {
         ::std::mem::replace(&mut self.grid_id, ::std::string::String::new())
     }
 
-    // string field_id = 2;
+    // .Field field = 2;
 
 
-    pub fn get_field_id(&self) -> &str {
-        &self.field_id
+    pub fn get_field(&self) -> &Field {
+        self.field.as_ref().unwrap_or_else(|| <Field as ::protobuf::Message>::default_instance())
     }
-    pub fn clear_field_id(&mut self) {
-        self.field_id.clear();
+    pub fn clear_field(&mut self) {
+        self.field.clear();
+    }
+
+    pub fn has_field(&self) -> bool {
+        self.field.is_some()
     }
 
     // Param is passed by value, moved
-    pub fn set_field_id(&mut self, v: ::std::string::String) {
-        self.field_id = v;
+    pub fn set_field(&mut self, v: Field) {
+        self.field = ::protobuf::SingularPtrField::some(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
+    pub fn mut_field(&mut self) -> &mut Field {
+        if self.field.is_none() {
+            self.field.set_default();
+        }
+        self.field.as_mut().unwrap()
     }
 
     // Take field
-    pub fn take_field_id(&mut self) -> ::std::string::String {
-        ::std::mem::replace(&mut self.field_id, ::std::string::String::new())
+    pub fn take_field(&mut self) -> Field {
+        self.field.take().unwrap_or_else(|| Field::new())
     }
 
     // bytes type_option_data = 3;
@@ -2273,6 +2280,11 @@ impl FieldTypeOptionData {
 
 impl ::protobuf::Message for FieldTypeOptionData {
     fn is_initialized(&self) -> bool {
+        for v in &self.field {
+            if !v.is_initialized() {
+                return false;
+            }
+        };
         true
     }
 
@@ -2284,7 +2296,7 @@ impl ::protobuf::Message for FieldTypeOptionData {
                     ::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)?;
+                    ::protobuf::rt::read_singular_message_into(wire_type, is, &mut self.field)?;
                 },
                 3 => {
                     ::protobuf::rt::read_singular_proto3_bytes_into(wire_type, is, &mut self.type_option_data)?;
@@ -2304,8 +2316,9 @@ impl ::protobuf::Message for FieldTypeOptionData {
         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 let Some(ref v) = self.field.as_ref() {
+            let len = v.compute_size();
+            my_size += 1 + ::protobuf::rt::compute_raw_varint32_size(len) + len;
         }
         if !self.type_option_data.is_empty() {
             my_size += ::protobuf::rt::bytes_size(3, &self.type_option_data);
@@ -2319,8 +2332,10 @@ impl ::protobuf::Message for FieldTypeOptionData {
         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 let Some(ref v) = self.field.as_ref() {
+            os.write_tag(2, ::protobuf::wire_format::WireTypeLengthDelimited)?;
+            os.write_raw_varint32(v.get_cached_size())?;
+            v.write_to_with_cached_sizes(os)?;
         }
         if !self.type_option_data.is_empty() {
             os.write_bytes(3, &self.type_option_data)?;
@@ -2368,10 +2383,10 @@ impl ::protobuf::Message for FieldTypeOptionData {
                 |m: &FieldTypeOptionData| { &m.grid_id },
                 |m: &mut FieldTypeOptionData| { &mut m.grid_id },
             ));
-            fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
-                "field_id",
-                |m: &FieldTypeOptionData| { &m.field_id },
-                |m: &mut FieldTypeOptionData| { &mut m.field_id },
+            fields.push(::protobuf::reflect::accessor::make_singular_ptr_field_accessor::<_, ::protobuf::types::ProtobufTypeMessage<Field>>(
+                "field",
+                |m: &FieldTypeOptionData| { &m.field },
+                |m: &mut FieldTypeOptionData| { &mut m.field },
             ));
             fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeBytes>(
                 "type_option_data",
@@ -2395,7 +2410,7 @@ impl ::protobuf::Message for FieldTypeOptionData {
 impl ::protobuf::Clear for FieldTypeOptionData {
     fn clear(&mut self) {
         self.grid_id.clear();
-        self.field_id.clear();
+        self.field.clear();
         self.type_option_data.clear();
         self.unknown_fields.clear();
     }
@@ -8302,25 +8317,25 @@ static file_descriptor_proto_data: &'static [u8] = b"\
     ne_of_field_id\"\x82\x01\n\x16FieldTypeOptionContext\x12\x17\n\x07grid_i\
     d\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\"s\n\x13FieldTypeOptionData\x12\x17\n\x07grid_id\x18\
-    \x01\x20\x01(\tR\x06gridId\x12\x19\n\x08field_id\x18\x02\x20\x01(\tR\x07\
-    fieldId\x12(\n\x10type_option_data\x18\x03\x20\x01(\x0cR\x0etypeOptionDa\
-    ta\"-\n\rRepeatedField\x12\x1c\n\x05items\x18\x01\x20\x03(\x0b2\x06.Fiel\
-    dR\x05items\"7\n\x12RepeatedFieldOrder\x12!\n\x05items\x18\x01\x20\x03(\
-    \x0b2\x0b.FieldOrderR\x05items\"T\n\x08RowOrder\x12\x15\n\x06row_id\x18\
-    \x01\x20\x01(\tR\x05rowId\x12\x19\n\x08block_id\x18\x02\x20\x01(\tR\x07b\
-    lockId\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_fiel\
-    d_id\x18\x02\x20\x03(\x0b2\x17.Row.CellByFieldIdEntryR\rcellByFieldId\
-    \x12\x16\n\x06height\x18\x03\x20\x01(\x05R\x06height\x1aG\n\x12CellByFie\
-    ldIdEntry\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\x11Repe\
-    atedGridBlock\x12\x20\n\x05items\x18\x01\x20\x03(\x0b2\n.GridBlockR\x05i\
-    tems\"U\n\x0eGridBlockOrder\x12\x19\n\x08block_id\x18\x01\x20\x01(\tR\
-    \x07blockId\x12(\n\nrow_orders\x18\x02\x20\x03(\x0b2\t.RowOrderR\trowOrd\
-    ers\"_\n\rIndexRowOrder\x12&\n\trow_order\x18\x01\x20\x01(\x0b2\t.RowOrd\
-    erR\x08rowOrder\x12\x16\n\x05index\x18\x02\x20\x01(\x05H\0R\x05indexB\
+    \x0etypeOptionData\"v\n\x13FieldTypeOptionData\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\x0etype\
+    OptionData\"-\n\rRepeatedField\x12\x1c\n\x05items\x18\x01\x20\x03(\x0b2\
+    \x06.FieldR\x05items\"7\n\x12RepeatedFieldOrder\x12!\n\x05items\x18\x01\
+    \x20\x03(\x0b2\x0b.FieldOrderR\x05items\"T\n\x08RowOrder\x12\x15\n\x06ro\
+    w_id\x18\x01\x20\x01(\tR\x05rowId\x12\x19\n\x08block_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_b\
+    y_field_id\x18\x02\x20\x03(\x0b2\x17.Row.CellByFieldIdEntryR\rcellByFiel\
+    dId\x12\x16\n\x06height\x18\x03\x20\x01(\x05R\x06height\x1aG\n\x12CellBy\
+    FieldIdEntry\x12\x10\n\x03key\x18\x01\x20\x01(\tR\x03key\x12\x1b\n\x05va\
+    lue\x18\x02\x20\x01(\x0b2\x05.CellR\x05value:\x028\x01\")\n\x0bRepeatedR\
+    ow\x12\x1a\n\x05items\x18\x01\x20\x03(\x0b2\x04.RowR\x05items\"5\n\x11Re\
+    peatedGridBlock\x12\x20\n\x05items\x18\x01\x20\x03(\x0b2\n.GridBlockR\
+    \x05items\"U\n\x0eGridBlockOrder\x12\x19\n\x08block_id\x18\x01\x20\x01(\
+    \tR\x07blockId\x12(\n\nrow_orders\x18\x02\x20\x03(\x0b2\t.RowOrderR\trow\
+    Orders\"_\n\rIndexRowOrder\x12&\n\trow_order\x18\x01\x20\x01(\x0b2\t.Row\
+    OrderR\x08rowOrder\x12\x16\n\x05index\x18\x02\x20\x01(\x05H\0R\x05indexB\
     \x0e\n\x0cone_of_index\"Q\n\x0fUpdatedRowOrder\x12&\n\trow_order\x18\x01\
     \x20\x01(\x0b2\t.RowOrderR\x08rowOrder\x12\x16\n\x03row\x18\x02\x20\x01(\
     \x0b2\x04.RowR\x03row\"\xc6\x01\n\x11GridRowsChangeset\x12\x19\n\x08bloc\

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

@@ -45,7 +45,7 @@ message FieldTypeOptionContext {
 }
 message FieldTypeOptionData {
     string grid_id = 1;
-    string field_id = 2;
+    Field field = 2;
     bytes type_option_data = 3;
 }
 message RepeatedField {