Browse Source

chore: move field

appflowy 3 years ago
parent
commit
2fa6adeee6
20 changed files with 730 additions and 101 deletions
  1. 2 3
      frontend/app_flowy/lib/workspace/application/grid/grid_listener.dart
  2. 4 0
      frontend/app_flowy/lib/workspace/application/grid/setting/property_bloc.dart
  3. 6 10
      frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/toolbar/grid_property.dart
  4. 17 0
      frontend/app_flowy/packages/flowy_sdk/lib/dispatch/dart_event/flowy-grid/dart_event.dart
  5. 116 13
      frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/grid.pb.dart
  6. 15 0
      frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/grid.pbenum.dart
  7. 30 5
      frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/grid.pbjson.dart
  8. 2 0
      frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/event_map.pbenum.dart
  9. 2 1
      frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/event_map.pbjson.dart
  10. 7 0
      frontend/app_flowy/pubspec.lock
  11. 1 0
      frontend/app_flowy/pubspec.yaml
  12. 11 0
      frontend/rust-lib/flowy-grid/src/event_handler.rs
  13. 6 3
      frontend/rust-lib/flowy-grid/src/event_map.rs
  14. 10 7
      frontend/rust-lib/flowy-grid/src/protobuf/model/event_map.rs
  15. 1 0
      frontend/rust-lib/flowy-grid/src/protobuf/proto/event_map.proto
  16. 7 7
      frontend/rust-lib/flowy-grid/src/services/block_meta_manager.rs
  17. 18 0
      frontend/rust-lib/flowy-grid/src/services/grid_editor.rs
  18. 60 5
      shared-lib/flowy-grid-data-model/src/entities/grid.rs
  19. 403 46
      shared-lib/flowy-grid-data-model/src/protobuf/model/grid.rs
  20. 12 1
      shared-lib/flowy-grid-data-model/src/protobuf/proto/grid.proto

+ 2 - 3
frontend/app_flowy/lib/workspace/application/grid/grid_listener.dart

@@ -9,8 +9,7 @@ import 'package:app_flowy/core/notification_helper.dart';
 
 class GridListener {
   final String gridId;
-  PublishNotifier<Either<List<GridBlockOrderChangeset>, FlowyError>> rowsUpdateNotifier =
-      PublishNotifier(comparable: null);
+  PublishNotifier<Either<List<GridRowsChangeset>, FlowyError>> rowsUpdateNotifier = PublishNotifier(comparable: null);
   GridNotificationListener? _listener;
 
   GridListener({required this.gridId});
@@ -26,7 +25,7 @@ class GridListener {
     switch (ty) {
       case GridNotification.DidUpdateGridBlock:
         result.fold(
-          (payload) => rowsUpdateNotifier.value = left([GridBlockOrderChangeset.fromBuffer(payload)]),
+          (payload) => rowsUpdateNotifier.value = left([GridRowsChangeset.fromBuffer(payload)]),
           (error) => rowsUpdateNotifier.value = right(error),
         );
         break;

+ 4 - 0
frontend/app_flowy/lib/workspace/application/grid/setting/property_bloc.dart

@@ -32,6 +32,9 @@ class GridPropertyBloc extends Bloc<GridPropertyEvent, GridPropertyState> {
           didReceiveFieldUpdate: (_DidReceiveFieldUpdate value) {
             emit(state.copyWith(fields: value.fields));
           },
+          moveField: (_MoveField value) {
+            //
+          },
         );
       },
     );
@@ -61,6 +64,7 @@ class GridPropertyEvent with _$GridPropertyEvent {
   const factory GridPropertyEvent.initial() = _Initial;
   const factory GridPropertyEvent.setFieldVisibility(String fieldId, bool visibility) = _SetFieldVisibility;
   const factory GridPropertyEvent.didReceiveFieldUpdate(List<Field> fields) = _DidReceiveFieldUpdate;
+  const factory GridPropertyEvent.moveField(int fromIndex, int toIndex) = _MoveField;
 }
 
 @freezed

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

@@ -46,20 +46,16 @@ class GridPropertyList extends StatelessWidget with FlowyOverlayDelegate {
           getIt<GridPropertyBloc>(param1: gridId, param2: fields)..add(const GridPropertyEvent.initial()),
       child: BlocBuilder<GridPropertyBloc, GridPropertyState>(
         builder: (context, state) {
-          final cells = state.fields.map((field) {
-            return _GridPropertyCell(gridId: gridId, field: field);
+          final children = state.fields.map((field) {
+            return _GridPropertyCell(gridId: gridId, field: field, key: ValueKey(field.id));
           }).toList();
 
-          return ListView.separated(
+          return ReorderableListView(
             shrinkWrap: true,
-            controller: ScrollController(),
-            separatorBuilder: (context, index) {
-              return VSpace(GridSize.typeOptionSeparatorHeight);
-            },
-            itemCount: cells.length,
-            itemBuilder: (BuildContext context, int index) {
-              return cells[index];
+            onReorder: (int oldIndex, int newIndex) {
+              context.read<GridPropertyBloc>().add(GridPropertyEvent.moveField(oldIndex, newIndex));
             },
+            children: children,
           );
         },
       ),

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

@@ -154,6 +154,23 @@ class GridEventGetEditFieldContext {
     }
 }
 
+class GridEventMoveItem {
+     MoveItemPayload request;
+     GridEventMoveItem(this.request);
+
+    Future<Either<Unit, FlowyError>> send() {
+    final request = FFIRequest.create()
+          ..event = GridEvent.MoveItem.toString()
+          ..payload = requestToBytes(this.request);
+
+    return Dispatch.asyncRequest(request)
+        .then((bytesResult) => bytesResult.fold(
+           (bytes) => left(unit),
+           (errBytes) => right(FlowyError.fromBuffer(errBytes)),
+        ));
+    }
+}
+
 class GridEventNewSelectOption {
      SelectOptionName request;
      GridEventNewSelectOption(this.request);

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

@@ -857,8 +857,8 @@ class GridBlockOrder extends $pb.GeneratedMessage {
   $core.List<RowOrder> get rowOrders => $_getList(1);
 }
 
-class GridBlockOrderChangeset extends $pb.GeneratedMessage {
-  static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'GridBlockOrderChangeset', createEmptyInstance: create)
+class GridRowsChangeset extends $pb.GeneratedMessage {
+  static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'GridRowsChangeset', createEmptyInstance: create)
     ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'blockId')
     ..pc<IndexRowOrder>(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'insertedRows', $pb.PbFieldType.PM, subBuilder: IndexRowOrder.create)
     ..pc<RowOrder>(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'deletedRows', $pb.PbFieldType.PM, subBuilder: RowOrder.create)
@@ -866,8 +866,8 @@ class GridBlockOrderChangeset extends $pb.GeneratedMessage {
     ..hasRequiredFields = false
   ;
 
-  GridBlockOrderChangeset._() : super();
-  factory GridBlockOrderChangeset({
+  GridRowsChangeset._() : super();
+  factory GridRowsChangeset({
     $core.String? blockId,
     $core.Iterable<IndexRowOrder>? insertedRows,
     $core.Iterable<RowOrder>? deletedRows,
@@ -888,26 +888,26 @@ class GridBlockOrderChangeset extends $pb.GeneratedMessage {
     }
     return _result;
   }
-  factory GridBlockOrderChangeset.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
-  factory GridBlockOrderChangeset.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
+  factory GridRowsChangeset.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
+  factory GridRowsChangeset.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')
-  GridBlockOrderChangeset clone() => GridBlockOrderChangeset()..mergeFromMessage(this);
+  GridRowsChangeset clone() => GridRowsChangeset()..mergeFromMessage(this);
   @$core.Deprecated(
   'Using this can add significant overhead to your binary. '
   'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
   'Will be removed in next major version')
-  GridBlockOrderChangeset copyWith(void Function(GridBlockOrderChangeset) updates) => super.copyWith((message) => updates(message as GridBlockOrderChangeset)) as GridBlockOrderChangeset; // ignore: deprecated_member_use
+  GridRowsChangeset copyWith(void Function(GridRowsChangeset) updates) => super.copyWith((message) => updates(message as GridRowsChangeset)) as GridRowsChangeset; // ignore: deprecated_member_use
   $pb.BuilderInfo get info_ => _i;
   @$core.pragma('dart2js:noInline')
-  static GridBlockOrderChangeset create() => GridBlockOrderChangeset._();
-  GridBlockOrderChangeset createEmptyInstance() => create();
-  static $pb.PbList<GridBlockOrderChangeset> createRepeated() => $pb.PbList<GridBlockOrderChangeset>();
+  static GridRowsChangeset create() => GridRowsChangeset._();
+  GridRowsChangeset createEmptyInstance() => create();
+  static $pb.PbList<GridRowsChangeset> createRepeated() => $pb.PbList<GridRowsChangeset>();
   @$core.pragma('dart2js:noInline')
-  static GridBlockOrderChangeset getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<GridBlockOrderChangeset>(create);
-  static GridBlockOrderChangeset? _defaultInstance;
+  static GridRowsChangeset getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<GridRowsChangeset>(create);
+  static GridRowsChangeset? _defaultInstance;
 
   @$pb.TagNumber(1)
   $core.String get blockId => $_getSZ(0);
@@ -1950,6 +1950,109 @@ class FieldChangesetPayload extends $pb.GeneratedMessage {
   void clearTypeOptionData() => clearField(9);
 }
 
+class MoveItemPayload extends $pb.GeneratedMessage {
+  static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'MoveItemPayload', createEmptyInstance: create)
+    ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'gridId')
+    ..aOS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'itemId')
+    ..a<$core.int>(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'fromIndex', $pb.PbFieldType.O3)
+    ..a<$core.int>(4, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'toIndex', $pb.PbFieldType.O3)
+    ..e<MoveItemType>(5, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'ty', $pb.PbFieldType.OE, defaultOrMaker: MoveItemType.MoveField, valueOf: MoveItemType.valueOf, enumValues: MoveItemType.values)
+    ..hasRequiredFields = false
+  ;
+
+  MoveItemPayload._() : super();
+  factory MoveItemPayload({
+    $core.String? gridId,
+    $core.String? itemId,
+    $core.int? fromIndex,
+    $core.int? toIndex,
+    MoveItemType? ty,
+  }) {
+    final _result = create();
+    if (gridId != null) {
+      _result.gridId = gridId;
+    }
+    if (itemId != null) {
+      _result.itemId = itemId;
+    }
+    if (fromIndex != null) {
+      _result.fromIndex = fromIndex;
+    }
+    if (toIndex != null) {
+      _result.toIndex = toIndex;
+    }
+    if (ty != null) {
+      _result.ty = ty;
+    }
+    return _result;
+  }
+  factory MoveItemPayload.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
+  factory MoveItemPayload.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')
+  MoveItemPayload clone() => MoveItemPayload()..mergeFromMessage(this);
+  @$core.Deprecated(
+  'Using this can add significant overhead to your binary. '
+  'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
+  'Will be removed in next major version')
+  MoveItemPayload copyWith(void Function(MoveItemPayload) updates) => super.copyWith((message) => updates(message as MoveItemPayload)) as MoveItemPayload; // ignore: deprecated_member_use
+  $pb.BuilderInfo get info_ => _i;
+  @$core.pragma('dart2js:noInline')
+  static MoveItemPayload create() => MoveItemPayload._();
+  MoveItemPayload createEmptyInstance() => create();
+  static $pb.PbList<MoveItemPayload> createRepeated() => $pb.PbList<MoveItemPayload>();
+  @$core.pragma('dart2js:noInline')
+  static MoveItemPayload getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<MoveItemPayload>(create);
+  static MoveItemPayload? _defaultInstance;
+
+  @$pb.TagNumber(1)
+  $core.String get gridId => $_getSZ(0);
+  @$pb.TagNumber(1)
+  set gridId($core.String v) { $_setString(0, v); }
+  @$pb.TagNumber(1)
+  $core.bool hasGridId() => $_has(0);
+  @$pb.TagNumber(1)
+  void clearGridId() => clearField(1);
+
+  @$pb.TagNumber(2)
+  $core.String get itemId => $_getSZ(1);
+  @$pb.TagNumber(2)
+  set itemId($core.String v) { $_setString(1, v); }
+  @$pb.TagNumber(2)
+  $core.bool hasItemId() => $_has(1);
+  @$pb.TagNumber(2)
+  void clearItemId() => clearField(2);
+
+  @$pb.TagNumber(3)
+  $core.int get fromIndex => $_getIZ(2);
+  @$pb.TagNumber(3)
+  set fromIndex($core.int v) { $_setSignedInt32(2, v); }
+  @$pb.TagNumber(3)
+  $core.bool hasFromIndex() => $_has(2);
+  @$pb.TagNumber(3)
+  void clearFromIndex() => clearField(3);
+
+  @$pb.TagNumber(4)
+  $core.int get toIndex => $_getIZ(3);
+  @$pb.TagNumber(4)
+  set toIndex($core.int v) { $_setSignedInt32(3, v); }
+  @$pb.TagNumber(4)
+  $core.bool hasToIndex() => $_has(3);
+  @$pb.TagNumber(4)
+  void clearToIndex() => clearField(4);
+
+  @$pb.TagNumber(5)
+  MoveItemType get ty => $_getN(4);
+  @$pb.TagNumber(5)
+  set ty(MoveItemType v) { setField(5, v); }
+  @$pb.TagNumber(5)
+  $core.bool hasTy() => $_has(4);
+  @$pb.TagNumber(5)
+  void clearTy() => clearField(5);
+}
+
 enum CellChangeset_OneOfData {
   data, 
   notSet

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

@@ -9,6 +9,21 @@
 import 'dart:core' as $core;
 import 'package:protobuf/protobuf.dart' as $pb;
 
+class MoveItemType extends $pb.ProtobufEnum {
+  static const MoveItemType MoveField = MoveItemType._(0, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'MoveField');
+  static const MoveItemType MoveRow = MoveItemType._(1, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'MoveRow');
+
+  static const $core.List<MoveItemType> values = <MoveItemType> [
+    MoveField,
+    MoveRow,
+  ];
+
+  static final $core.Map<$core.int, MoveItemType> _byValue = $pb.ProtobufEnum.initByValue(values);
+  static MoveItemType? valueOf($core.int value) => _byValue[value];
+
+  const MoveItemType._($core.int v, $core.String n) : super(v, n);
+}
+
 class FieldType extends $pb.ProtobufEnum {
   static const FieldType RichText = FieldType._(0, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'RichText');
   static const FieldType Number = FieldType._(1, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'Number');

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

@@ -8,6 +8,17 @@
 import 'dart:core' as $core;
 import 'dart:convert' as $convert;
 import 'dart:typed_data' as $typed_data;
+@$core.Deprecated('Use moveItemTypeDescriptor instead')
+const MoveItemType$json = const {
+  '1': 'MoveItemType',
+  '2': const [
+    const {'1': 'MoveField', '2': 0},
+    const {'1': 'MoveRow', '2': 1},
+  ],
+};
+
+/// Descriptor for `MoveItemType`. Decode as a `google.protobuf.EnumDescriptorProto`.
+final $typed_data.Uint8List moveItemTypeDescriptor = $convert.base64Decode('CgxNb3ZlSXRlbVR5cGUSDQoJTW92ZUZpZWxkEAASCwoHTW92ZVJvdxAB');
 @$core.Deprecated('Use fieldTypeDescriptor instead')
 const FieldType$json = const {
   '1': 'FieldType',
@@ -186,9 +197,9 @@ const GridBlockOrder$json = const {
 
 /// Descriptor for `GridBlockOrder`. Decode as a `google.protobuf.DescriptorProto`.
 final $typed_data.Uint8List gridBlockOrderDescriptor = $convert.base64Decode('Cg5HcmlkQmxvY2tPcmRlchIZCghibG9ja19pZBgBIAEoCVIHYmxvY2tJZBIoCgpyb3dfb3JkZXJzGAIgAygLMgkuUm93T3JkZXJSCXJvd09yZGVycw==');
-@$core.Deprecated('Use gridBlockOrderChangesetDescriptor instead')
-const GridBlockOrderChangeset$json = const {
-  '1': 'GridBlockOrderChangeset',
+@$core.Deprecated('Use gridRowsChangesetDescriptor instead')
+const GridRowsChangeset$json = const {
+  '1': 'GridRowsChangeset',
   '2': const [
     const {'1': 'block_id', '3': 1, '4': 1, '5': 9, '10': 'blockId'},
     const {'1': 'inserted_rows', '3': 2, '4': 3, '5': 11, '6': '.IndexRowOrder', '10': 'insertedRows'},
@@ -197,8 +208,8 @@ const GridBlockOrderChangeset$json = const {
   ],
 };
 
-/// Descriptor for `GridBlockOrderChangeset`. Decode as a `google.protobuf.DescriptorProto`.
-final $typed_data.Uint8List gridBlockOrderChangesetDescriptor = $convert.base64Decode('ChdHcmlkQmxvY2tPcmRlckNoYW5nZXNldBIZCghibG9ja19pZBgBIAEoCVIHYmxvY2tJZBIzCg1pbnNlcnRlZF9yb3dzGAIgAygLMg4uSW5kZXhSb3dPcmRlclIMaW5zZXJ0ZWRSb3dzEiwKDGRlbGV0ZWRfcm93cxgDIAMoCzIJLlJvd09yZGVyUgtkZWxldGVkUm93cxIsCgx1cGRhdGVkX3Jvd3MYBCADKAsyCS5Sb3dPcmRlclILdXBkYXRlZFJvd3M=');
+/// Descriptor for `GridRowsChangeset`. Decode as a `google.protobuf.DescriptorProto`.
+final $typed_data.Uint8List gridRowsChangesetDescriptor = $convert.base64Decode('ChFHcmlkUm93c0NoYW5nZXNldBIZCghibG9ja19pZBgBIAEoCVIHYmxvY2tJZBIzCg1pbnNlcnRlZF9yb3dzGAIgAygLMg4uSW5kZXhSb3dPcmRlclIMaW5zZXJ0ZWRSb3dzEiwKDGRlbGV0ZWRfcm93cxgDIAMoCzIJLlJvd09yZGVyUgtkZWxldGVkUm93cxIsCgx1cGRhdGVkX3Jvd3MYBCADKAsyCS5Sb3dPcmRlclILdXBkYXRlZFJvd3M=');
 @$core.Deprecated('Use indexRowOrderDescriptor instead')
 const IndexRowOrder$json = const {
   '1': 'IndexRowOrder',
@@ -370,6 +381,20 @@ const FieldChangesetPayload$json = const {
 
 /// Descriptor for `FieldChangesetPayload`. Decode as a `google.protobuf.DescriptorProto`.
 final $typed_data.Uint8List fieldChangesetPayloadDescriptor = $convert.base64Decode('ChVGaWVsZENoYW5nZXNldFBheWxvYWQSGQoIZmllbGRfaWQYASABKAlSB2ZpZWxkSWQSFwoHZ3JpZF9pZBgCIAEoCVIGZ3JpZElkEhQKBG5hbWUYAyABKAlIAFIEbmFtZRIUCgRkZXNjGAQgASgJSAFSBGRlc2MSKwoKZmllbGRfdHlwZRgFIAEoDjIKLkZpZWxkVHlwZUgCUglmaWVsZFR5cGUSGAoGZnJvemVuGAYgASgISANSBmZyb3plbhIgCgp2aXNpYmlsaXR5GAcgASgISARSCnZpc2liaWxpdHkSFgoFd2lkdGgYCCABKAVIBVIFd2lkdGgSKgoQdHlwZV9vcHRpb25fZGF0YRgJIAEoDEgGUg50eXBlT3B0aW9uRGF0YUINCgtvbmVfb2ZfbmFtZUINCgtvbmVfb2ZfZGVzY0ITChFvbmVfb2ZfZmllbGRfdHlwZUIPCg1vbmVfb2ZfZnJvemVuQhMKEW9uZV9vZl92aXNpYmlsaXR5Qg4KDG9uZV9vZl93aWR0aEIZChdvbmVfb2ZfdHlwZV9vcHRpb25fZGF0YQ==');
+@$core.Deprecated('Use moveItemPayloadDescriptor instead')
+const MoveItemPayload$json = const {
+  '1': 'MoveItemPayload',
+  '2': const [
+    const {'1': 'grid_id', '3': 1, '4': 1, '5': 9, '10': 'gridId'},
+    const {'1': 'item_id', '3': 2, '4': 1, '5': 9, '10': 'itemId'},
+    const {'1': 'from_index', '3': 3, '4': 1, '5': 5, '10': 'fromIndex'},
+    const {'1': 'to_index', '3': 4, '4': 1, '5': 5, '10': 'toIndex'},
+    const {'1': 'ty', '3': 5, '4': 1, '5': 14, '6': '.MoveItemType', '10': 'ty'},
+  ],
+};
+
+/// Descriptor for `MoveItemPayload`. Decode as a `google.protobuf.DescriptorProto`.
+final $typed_data.Uint8List moveItemPayloadDescriptor = $convert.base64Decode('Cg9Nb3ZlSXRlbVBheWxvYWQSFwoHZ3JpZF9pZBgBIAEoCVIGZ3JpZElkEhcKB2l0ZW1faWQYAiABKAlSBml0ZW1JZBIdCgpmcm9tX2luZGV4GAMgASgFUglmcm9tSW5kZXgSGQoIdG9faW5kZXgYBCABKAVSB3RvSW5kZXgSHQoCdHkYBSABKA4yDS5Nb3ZlSXRlbVR5cGVSAnR5');
 @$core.Deprecated('Use cellChangesetDescriptor instead')
 const CellChangeset$json = const {
   '1': 'CellChangeset',

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

@@ -19,6 +19,7 @@ class GridEvent extends $pb.ProtobufEnum {
   static const GridEvent SwitchToField = GridEvent._(14, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'SwitchToField');
   static const GridEvent DuplicateField = GridEvent._(15, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'DuplicateField');
   static const GridEvent GetEditFieldContext = GridEvent._(16, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'GetEditFieldContext');
+  static const GridEvent MoveItem = GridEvent._(17, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'MoveItem');
   static const GridEvent NewSelectOption = GridEvent._(30, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'NewSelectOption');
   static const GridEvent GetSelectOptionContext = GridEvent._(31, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'GetSelectOptionContext');
   static const GridEvent UpdateSelectOption = GridEvent._(32, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'UpdateSelectOption');
@@ -40,6 +41,7 @@ class GridEvent extends $pb.ProtobufEnum {
     SwitchToField,
     DuplicateField,
     GetEditFieldContext,
+    MoveItem,
     NewSelectOption,
     GetSelectOptionContext,
     UpdateSelectOption,

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

@@ -21,6 +21,7 @@ const GridEvent$json = const {
     const {'1': 'SwitchToField', '2': 14},
     const {'1': 'DuplicateField', '2': 15},
     const {'1': 'GetEditFieldContext', '2': 16},
+    const {'1': 'MoveItem', '2': 17},
     const {'1': 'NewSelectOption', '2': 30},
     const {'1': 'GetSelectOptionContext', '2': 31},
     const {'1': 'UpdateSelectOption', '2': 32},
@@ -35,4 +36,4 @@ const GridEvent$json = const {
 };
 
 /// Descriptor for `GridEvent`. Decode as a `google.protobuf.EnumDescriptorProto`.
-final $typed_data.Uint8List gridEventDescriptor = $convert.base64Decode('CglHcmlkRXZlbnQSDwoLR2V0R3JpZERhdGEQABIRCg1HZXRHcmlkQmxvY2tzEAESDQoJR2V0RmllbGRzEAoSDwoLVXBkYXRlRmllbGQQCxIPCgtJbnNlcnRGaWVsZBAMEg8KC0RlbGV0ZUZpZWxkEA0SEQoNU3dpdGNoVG9GaWVsZBAOEhIKDkR1cGxpY2F0ZUZpZWxkEA8SFwoTR2V0RWRpdEZpZWxkQ29udGV4dBAQEhMKD05ld1NlbGVjdE9wdGlvbhAeEhoKFkdldFNlbGVjdE9wdGlvbkNvbnRleHQQHxIWChJVcGRhdGVTZWxlY3RPcHRpb24QIBINCglDcmVhdGVSb3cQMhIKCgZHZXRSb3cQMxINCglEZWxldGVSb3cQNBIQCgxEdXBsaWNhdGVSb3cQNRILCgdHZXRDZWxsEEYSDgoKVXBkYXRlQ2VsbBBHEhoKFlVwZGF0ZUNlbGxTZWxlY3RPcHRpb24QSA==');
+final $typed_data.Uint8List gridEventDescriptor = $convert.base64Decode('CglHcmlkRXZlbnQSDwoLR2V0R3JpZERhdGEQABIRCg1HZXRHcmlkQmxvY2tzEAESDQoJR2V0RmllbGRzEAoSDwoLVXBkYXRlRmllbGQQCxIPCgtJbnNlcnRGaWVsZBAMEg8KC0RlbGV0ZUZpZWxkEA0SEQoNU3dpdGNoVG9GaWVsZBAOEhIKDkR1cGxpY2F0ZUZpZWxkEA8SFwoTR2V0RWRpdEZpZWxkQ29udGV4dBAQEgwKCE1vdmVJdGVtEBESEwoPTmV3U2VsZWN0T3B0aW9uEB4SGgoWR2V0U2VsZWN0T3B0aW9uQ29udGV4dBAfEhYKElVwZGF0ZVNlbGVjdE9wdGlvbhAgEg0KCUNyZWF0ZVJvdxAyEgoKBkdldFJvdxAzEg0KCURlbGV0ZVJvdxA0EhAKDER1cGxpY2F0ZVJvdxA1EgsKB0dldENlbGwQRhIOCgpVcGRhdGVDZWxsEEcSGgoWVXBkYXRlQ2VsbFNlbGVjdE9wdGlvbhBI');

+ 7 - 0
frontend/app_flowy/pubspec.lock

@@ -947,6 +947,13 @@ packages:
       url: "https://pub.dartlang.org"
     source: hosted
     version: "3.0.1+1"
+  reorderables:
+    dependency: "direct main"
+    description:
+      name: reorderables
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "0.4.3"
   shared_preferences:
     dependency: transitive
     description:

+ 1 - 0
frontend/app_flowy/pubspec.yaml

@@ -74,6 +74,7 @@ dependencies:
   device_info_plus: ^3.2.1
   fluttertoast: ^8.0.8
   table_calendar: ^3.0.5
+  reorderables:
 
 dev_dependencies:
   flutter_lints: ^1.0.0

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

@@ -130,6 +130,17 @@ pub(crate) async fn get_field_context_handler(
     data_result(edit_context)
 }
 
+#[tracing::instrument(level = "debug", skip(data, manager), err)]
+pub(crate) async fn move_item_handler(
+    data: Data<MoveItemPayload>,
+    manager: AppData<Arc<GridManager>>,
+) -> Result<(), FlowyError> {
+    let params: MoveItemParams = data.into_inner().try_into()?;
+    let editor = manager.get_grid_editor(&params.grid_id)?;
+    let _ = editor.move_item(params).await?;
+    Ok(())
+}
+
 async fn make_field_edit_context(
     grid_id: &str,
     field_id: Option<String>,

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

@@ -17,6 +17,8 @@ pub fn create(grid_manager: Arc<GridManager>) -> Module {
         .event(GridEvent::DeleteField, delete_field_handler)
         .event(GridEvent::SwitchToField, switch_to_field_handler)
         .event(GridEvent::DuplicateField, duplicate_field_handler)
+        .event(GridEvent::GetEditFieldContext, get_field_context_handler)
+        .event(GridEvent::MoveItem, move_item_handler)
         // Row
         .event(GridEvent::CreateRow, create_row_handler)
         .event(GridEvent::GetRow, get_row_handler)
@@ -29,9 +31,7 @@ pub fn create(grid_manager: Arc<GridManager>) -> Module {
         .event(GridEvent::NewSelectOption, new_select_option_handler)
         .event(GridEvent::UpdateSelectOption, update_select_option_handler)
         .event(GridEvent::GetSelectOptionContext, get_select_option_handler)
-        .event(GridEvent::UpdateCellSelectOption, update_cell_select_option_handler)
-        //
-        .event(GridEvent::GetEditFieldContext, get_field_context_handler);
+        .event(GridEvent::UpdateCellSelectOption, update_cell_select_option_handler);
 
     module
 }
@@ -66,6 +66,9 @@ pub enum GridEvent {
     #[event(input = "GetEditFieldContextPayload", output = "EditFieldContext")]
     GetEditFieldContext = 16,
 
+    #[event(input = "MoveItemPayload")]
+    MoveItem = 17,
+
     #[event(input = "SelectOptionName", output = "SelectOption")]
     NewSelectOption = 30,
 

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

@@ -34,6 +34,7 @@ pub enum GridEvent {
     SwitchToField = 14,
     DuplicateField = 15,
     GetEditFieldContext = 16,
+    MoveItem = 17,
     NewSelectOption = 30,
     GetSelectOptionContext = 31,
     UpdateSelectOption = 32,
@@ -62,6 +63,7 @@ impl ::protobuf::ProtobufEnum for GridEvent {
             14 => ::std::option::Option::Some(GridEvent::SwitchToField),
             15 => ::std::option::Option::Some(GridEvent::DuplicateField),
             16 => ::std::option::Option::Some(GridEvent::GetEditFieldContext),
+            17 => ::std::option::Option::Some(GridEvent::MoveItem),
             30 => ::std::option::Option::Some(GridEvent::NewSelectOption),
             31 => ::std::option::Option::Some(GridEvent::GetSelectOptionContext),
             32 => ::std::option::Option::Some(GridEvent::UpdateSelectOption),
@@ -87,6 +89,7 @@ impl ::protobuf::ProtobufEnum for GridEvent {
             GridEvent::SwitchToField,
             GridEvent::DuplicateField,
             GridEvent::GetEditFieldContext,
+            GridEvent::MoveItem,
             GridEvent::NewSelectOption,
             GridEvent::GetSelectOptionContext,
             GridEvent::UpdateSelectOption,
@@ -125,16 +128,16 @@ impl ::protobuf::reflect::ProtobufValue for GridEvent {
 }
 
 static file_descriptor_proto_data: &'static [u8] = b"\
-    \n\x0fevent_map.proto*\xef\x02\n\tGridEvent\x12\x0f\n\x0bGetGridData\x10\
+    \n\x0fevent_map.proto*\xfd\x02\n\tGridEvent\x12\x0f\n\x0bGetGridData\x10\
     \0\x12\x11\n\rGetGridBlocks\x10\x01\x12\r\n\tGetFields\x10\n\x12\x0f\n\
     \x0bUpdateField\x10\x0b\x12\x0f\n\x0bInsertField\x10\x0c\x12\x0f\n\x0bDe\
     leteField\x10\r\x12\x11\n\rSwitchToField\x10\x0e\x12\x12\n\x0eDuplicateF\
-    ield\x10\x0f\x12\x17\n\x13GetEditFieldContext\x10\x10\x12\x13\n\x0fNewSe\
-    lectOption\x10\x1e\x12\x1a\n\x16GetSelectOptionContext\x10\x1f\x12\x16\n\
-    \x12UpdateSelectOption\x10\x20\x12\r\n\tCreateRow\x102\x12\n\n\x06GetRow\
-    \x103\x12\r\n\tDeleteRow\x104\x12\x10\n\x0cDuplicateRow\x105\x12\x0b\n\
-    \x07GetCell\x10F\x12\x0e\n\nUpdateCell\x10G\x12\x1a\n\x16UpdateCellSelec\
-    tOption\x10Hb\x06proto3\
+    ield\x10\x0f\x12\x17\n\x13GetEditFieldContext\x10\x10\x12\x0c\n\x08MoveI\
+    tem\x10\x11\x12\x13\n\x0fNewSelectOption\x10\x1e\x12\x1a\n\x16GetSelectO\
+    ptionContext\x10\x1f\x12\x16\n\x12UpdateSelectOption\x10\x20\x12\r\n\tCr\
+    eateRow\x102\x12\n\n\x06GetRow\x103\x12\r\n\tDeleteRow\x104\x12\x10\n\
+    \x0cDuplicateRow\x105\x12\x0b\n\x07GetCell\x10F\x12\x0e\n\nUpdateCell\
+    \x10G\x12\x1a\n\x16UpdateCellSelectOption\x10Hb\x06proto3\
 ";
 
 static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;

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

@@ -10,6 +10,7 @@ enum GridEvent {
     SwitchToField = 14;
     DuplicateField = 15;
     GetEditFieldContext = 16;
+    MoveItem = 17;
     NewSelectOption = 30;
     GetSelectOptionContext = 31;
     UpdateSelectOption = 32;

+ 7 - 7
frontend/rust-lib/flowy-grid/src/services/block_meta_manager.rs

@@ -8,8 +8,8 @@ use std::borrow::Cow;
 use dashmap::DashMap;
 use flowy_error::FlowyResult;
 use flowy_grid_data_model::entities::{
-    CellChangeset, CellMeta, CellNotificationData, FieldMeta, GridBlockMeta, GridBlockMetaChangeset,
-    GridBlockOrderChangeset, IndexRowOrder, RowMeta, RowMetaChangeset, RowOrder,
+    CellChangeset, CellMeta, CellNotificationData, FieldMeta, GridBlockMeta, GridBlockMetaChangeset, GridRowsChangeset,
+    IndexRowOrder, RowMeta, RowMetaChangeset, RowOrder,
 };
 use flowy_revision::disk::SQLiteGridBlockMetaRevisionPersistence;
 use flowy_revision::{RevisionManager, RevisionPersistence};
@@ -76,7 +76,7 @@ impl GridBlockMetaEditorManager {
         index_row_order.index = row_index;
 
         let _ = self
-            .notify_did_update_grid_rows(GridBlockOrderChangeset::from_insert(block_id, vec![index_row_order]))
+            .notify_did_update_grid_rows(GridRowsChangeset::insert(block_id, vec![index_row_order]))
             .await?;
         Ok(row_count)
     }
@@ -98,7 +98,7 @@ impl GridBlockMetaEditorManager {
             changesets.push(GridBlockMetaChangeset::from_row_count(&block_id, row_count));
 
             let _ = self
-                .notify_did_update_grid_rows(GridBlockOrderChangeset::from_insert(&block_id, inserted_row_orders))
+                .notify_did_update_grid_rows(GridRowsChangeset::insert(&block_id, inserted_row_orders))
                 .await?;
         }
 
@@ -116,7 +116,7 @@ impl GridBlockMetaEditorManager {
         {
             None => {}
             Some(row_order) => {
-                let block_order_changeset = GridBlockOrderChangeset::from_update(&editor.block_id, vec![row_order]);
+                let block_order_changeset = GridRowsChangeset::update(&editor.block_id, vec![row_order]);
                 let _ = self.notify_did_update_grid_rows(block_order_changeset).await?;
             }
         }
@@ -131,7 +131,7 @@ impl GridBlockMetaEditorManager {
         let row_orders = editor.get_row_orders(Some(vec![Cow::Borrowed(&row_id)])).await?;
         let _ = editor.delete_rows(vec![Cow::Borrowed(&row_id)]).await?;
         let _ = self
-            .notify_did_update_grid_rows(GridBlockOrderChangeset::from_delete(&block_id, row_orders))
+            .notify_did_update_grid_rows(GridRowsChangeset::delete(&block_id, row_orders))
             .await?;
 
         Ok(())
@@ -213,7 +213,7 @@ impl GridBlockMetaEditorManager {
         Ok(block_cell_metas)
     }
 
-    async fn notify_did_update_grid_rows(&self, changeset: GridBlockOrderChangeset) -> FlowyResult<()> {
+    async fn notify_did_update_grid_rows(&self, changeset: GridRowsChangeset) -> FlowyResult<()> {
         send_dart_notification(&self.grid_id, GridNotification::DidUpdateGridBlock)
             .payload(changeset)
             .send();

+ 18 - 0
frontend/rust-lib/flowy-grid/src/services/grid_editor.rs

@@ -375,6 +375,24 @@ impl ClientGridEditor {
         Ok(snapshots)
     }
 
+    pub async fn move_item(&self, params: MoveItemParams) -> FlowyResult<()> {
+        match params.ty {
+            MoveItemType::MoveField => {
+                self.move_field(params.from_index, params.to_index, &params.item_id)
+                    .await
+            }
+            MoveItemType::MoveRow => self.move_row(params.from_index, params.to_index, &params.item_id).await,
+        }
+    }
+
+    pub async fn move_field(&self, from: i32, to: i32, field_id: &str) -> FlowyResult<()> {
+        todo!()
+    }
+
+    pub async fn move_row(&self, from: i32, to: i32, row_id: &str) -> FlowyResult<()> {
+        todo!()
+    }
+
     pub async fn delta_bytes(&self) -> Bytes {
         self.pad.read().await.delta_bytes()
     }

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

@@ -278,7 +278,7 @@ impl GridBlockOrder {
 }
 
 #[derive(Debug, Clone, Default, ProtoBuf)]
-pub struct GridBlockOrderChangeset {
+pub struct GridRowsChangeset {
     #[pb(index = 1)]
     pub block_id: String,
 
@@ -314,8 +314,8 @@ impl std::convert::From<&RowMeta> for IndexRowOrder {
     }
 }
 
-impl GridBlockOrderChangeset {
-    pub fn from_insert(block_id: &str, inserted_rows: Vec<IndexRowOrder>) -> Self {
+impl GridRowsChangeset {
+    pub fn insert(block_id: &str, inserted_rows: Vec<IndexRowOrder>) -> Self {
         Self {
             block_id: block_id.to_owned(),
             inserted_rows,
@@ -324,7 +324,7 @@ impl GridBlockOrderChangeset {
         }
     }
 
-    pub fn from_delete(block_id: &str, deleted_rows: Vec<RowOrder>) -> Self {
+    pub fn delete(block_id: &str, deleted_rows: Vec<RowOrder>) -> Self {
         Self {
             block_id: block_id.to_owned(),
             inserted_rows: vec![],
@@ -333,7 +333,7 @@ impl GridBlockOrderChangeset {
         }
     }
 
-    pub fn from_update(block_id: &str, updated_rows: Vec<RowOrder>) -> Self {
+    pub fn update(block_id: &str, updated_rows: Vec<RowOrder>) -> Self {
         Self {
             block_id: block_id.to_owned(),
             inserted_rows: vec![],
@@ -656,6 +656,61 @@ impl TryInto<FieldChangesetParams> for FieldChangesetPayload {
     }
 }
 
+#[derive(Debug, Clone, ProtoBuf_Enum)]
+pub enum MoveItemType {
+    MoveField = 0,
+    MoveRow = 1,
+}
+
+impl std::default::Default for MoveItemType {
+    fn default() -> Self {
+        MoveItemType::MoveField
+    }
+}
+
+#[derive(Debug, Clone, Default, ProtoBuf)]
+pub struct MoveItemPayload {
+    #[pb(index = 1)]
+    pub grid_id: String,
+
+    #[pb(index = 2)]
+    pub item_id: String,
+
+    #[pb(index = 3)]
+    pub from_index: i32,
+
+    #[pb(index = 4)]
+    pub to_index: i32,
+
+    #[pb(index = 5)]
+    pub ty: MoveItemType,
+}
+
+#[derive(Clone)]
+pub struct MoveItemParams {
+    pub grid_id: String,
+    pub item_id: String,
+    pub from_index: i32,
+    pub to_index: i32,
+    pub ty: MoveItemType,
+}
+
+impl TryInto<MoveItemParams> for MoveItemPayload {
+    type Error = ErrorCode;
+
+    fn try_into(self) -> Result<MoveItemParams, Self::Error> {
+        let grid_id = NotEmptyStr::parse(self.grid_id).map_err(|_| ErrorCode::GridIdIsEmpty)?;
+        let item_id = NotEmptyStr::parse(self.item_id).map_err(|_| ErrorCode::InvalidData)?;
+        Ok(MoveItemParams {
+            grid_id: grid_id.0,
+            item_id: item_id.0,
+            from_index: self.from_index,
+            to_index: self.to_index,
+            ty: self.ty,
+        })
+    }
+}
+
 #[derive(
     Debug,
     Clone,

+ 403 - 46
shared-lib/flowy-grid-data-model/src/protobuf/model/grid.rs

@@ -2920,7 +2920,7 @@ impl ::protobuf::reflect::ProtobufValue for GridBlockOrder {
 }
 
 #[derive(PartialEq,Clone,Default)]
-pub struct GridBlockOrderChangeset {
+pub struct GridRowsChangeset {
     // message fields
     pub block_id: ::std::string::String,
     pub inserted_rows: ::protobuf::RepeatedField<IndexRowOrder>,
@@ -2931,14 +2931,14 @@ pub struct GridBlockOrderChangeset {
     pub cached_size: ::protobuf::CachedSize,
 }
 
-impl<'a> ::std::default::Default for &'a GridBlockOrderChangeset {
-    fn default() -> &'a GridBlockOrderChangeset {
-        <GridBlockOrderChangeset as ::protobuf::Message>::default_instance()
+impl<'a> ::std::default::Default for &'a GridRowsChangeset {
+    fn default() -> &'a GridRowsChangeset {
+        <GridRowsChangeset as ::protobuf::Message>::default_instance()
     }
 }
 
-impl GridBlockOrderChangeset {
-    pub fn new() -> GridBlockOrderChangeset {
+impl GridRowsChangeset {
+    pub fn new() -> GridRowsChangeset {
         ::std::default::Default::default()
     }
 
@@ -3044,7 +3044,7 @@ impl GridBlockOrderChangeset {
     }
 }
 
-impl ::protobuf::Message for GridBlockOrderChangeset {
+impl ::protobuf::Message for GridRowsChangeset {
     fn is_initialized(&self) -> bool {
         for v in &self.inserted_rows {
             if !v.is_initialized() {
@@ -3161,8 +3161,8 @@ impl ::protobuf::Message for GridBlockOrderChangeset {
         Self::descriptor_static()
     }
 
-    fn new() -> GridBlockOrderChangeset {
-        GridBlockOrderChangeset::new()
+    fn new() -> GridRowsChangeset {
+        GridRowsChangeset::new()
     }
 
     fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor {
@@ -3171,39 +3171,39 @@ impl ::protobuf::Message for GridBlockOrderChangeset {
             let mut fields = ::std::vec::Vec::new();
             fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
                 "block_id",
-                |m: &GridBlockOrderChangeset| { &m.block_id },
-                |m: &mut GridBlockOrderChangeset| { &mut m.block_id },
+                |m: &GridRowsChangeset| { &m.block_id },
+                |m: &mut GridRowsChangeset| { &mut m.block_id },
             ));
             fields.push(::protobuf::reflect::accessor::make_repeated_field_accessor::<_, ::protobuf::types::ProtobufTypeMessage<IndexRowOrder>>(
                 "inserted_rows",
-                |m: &GridBlockOrderChangeset| { &m.inserted_rows },
-                |m: &mut GridBlockOrderChangeset| { &mut m.inserted_rows },
+                |m: &GridRowsChangeset| { &m.inserted_rows },
+                |m: &mut GridRowsChangeset| { &mut m.inserted_rows },
             ));
             fields.push(::protobuf::reflect::accessor::make_repeated_field_accessor::<_, ::protobuf::types::ProtobufTypeMessage<RowOrder>>(
                 "deleted_rows",
-                |m: &GridBlockOrderChangeset| { &m.deleted_rows },
-                |m: &mut GridBlockOrderChangeset| { &mut m.deleted_rows },
+                |m: &GridRowsChangeset| { &m.deleted_rows },
+                |m: &mut GridRowsChangeset| { &mut m.deleted_rows },
             ));
             fields.push(::protobuf::reflect::accessor::make_repeated_field_accessor::<_, ::protobuf::types::ProtobufTypeMessage<RowOrder>>(
                 "updated_rows",
-                |m: &GridBlockOrderChangeset| { &m.updated_rows },
-                |m: &mut GridBlockOrderChangeset| { &mut m.updated_rows },
+                |m: &GridRowsChangeset| { &m.updated_rows },
+                |m: &mut GridRowsChangeset| { &mut m.updated_rows },
             ));
-            ::protobuf::reflect::MessageDescriptor::new_pb_name::<GridBlockOrderChangeset>(
-                "GridBlockOrderChangeset",
+            ::protobuf::reflect::MessageDescriptor::new_pb_name::<GridRowsChangeset>(
+                "GridRowsChangeset",
                 fields,
                 file_descriptor_proto()
             )
         })
     }
 
-    fn default_instance() -> &'static GridBlockOrderChangeset {
-        static instance: ::protobuf::rt::LazyV2<GridBlockOrderChangeset> = ::protobuf::rt::LazyV2::INIT;
-        instance.get(GridBlockOrderChangeset::new)
+    fn default_instance() -> &'static GridRowsChangeset {
+        static instance: ::protobuf::rt::LazyV2<GridRowsChangeset> = ::protobuf::rt::LazyV2::INIT;
+        instance.get(GridRowsChangeset::new)
     }
 }
 
-impl ::protobuf::Clear for GridBlockOrderChangeset {
+impl ::protobuf::Clear for GridRowsChangeset {
     fn clear(&mut self) {
         self.block_id.clear();
         self.inserted_rows.clear();
@@ -3213,13 +3213,13 @@ impl ::protobuf::Clear for GridBlockOrderChangeset {
     }
 }
 
-impl ::std::fmt::Debug for GridBlockOrderChangeset {
+impl ::std::fmt::Debug for GridRowsChangeset {
     fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
         ::protobuf::text_format::fmt(self, f)
     }
 }
 
-impl ::protobuf::reflect::ProtobufValue for GridBlockOrderChangeset {
+impl ::protobuf::reflect::ProtobufValue for GridRowsChangeset {
     fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
         ::protobuf::reflect::ReflectValueRef::Message(self)
     }
@@ -6512,6 +6512,308 @@ impl ::protobuf::reflect::ProtobufValue for FieldChangesetPayload {
     }
 }
 
+#[derive(PartialEq,Clone,Default)]
+pub struct MoveItemPayload {
+    // message fields
+    pub grid_id: ::std::string::String,
+    pub item_id: ::std::string::String,
+    pub from_index: i32,
+    pub to_index: i32,
+    pub ty: MoveItemType,
+    // special fields
+    pub unknown_fields: ::protobuf::UnknownFields,
+    pub cached_size: ::protobuf::CachedSize,
+}
+
+impl<'a> ::std::default::Default for &'a MoveItemPayload {
+    fn default() -> &'a MoveItemPayload {
+        <MoveItemPayload as ::protobuf::Message>::default_instance()
+    }
+}
+
+impl MoveItemPayload {
+    pub fn new() -> MoveItemPayload {
+        ::std::default::Default::default()
+    }
+
+    // string grid_id = 1;
+
+
+    pub fn get_grid_id(&self) -> &str {
+        &self.grid_id
+    }
+    pub fn clear_grid_id(&mut self) {
+        self.grid_id.clear();
+    }
+
+    // Param is passed by value, moved
+    pub fn set_grid_id(&mut self, v: ::std::string::String) {
+        self.grid_id = v;
+    }
+
+    // Mutable pointer to the field.
+    // If field is not initialized, it is initialized with default value first.
+    pub fn mut_grid_id(&mut self) -> &mut ::std::string::String {
+        &mut self.grid_id
+    }
+
+    // Take field
+    pub fn take_grid_id(&mut self) -> ::std::string::String {
+        ::std::mem::replace(&mut self.grid_id, ::std::string::String::new())
+    }
+
+    // string item_id = 2;
+
+
+    pub fn get_item_id(&self) -> &str {
+        &self.item_id
+    }
+    pub fn clear_item_id(&mut self) {
+        self.item_id.clear();
+    }
+
+    // Param is passed by value, moved
+    pub fn set_item_id(&mut self, v: ::std::string::String) {
+        self.item_id = v;
+    }
+
+    // Mutable pointer to the field.
+    // If field is not initialized, it is initialized with default value first.
+    pub fn mut_item_id(&mut self) -> &mut ::std::string::String {
+        &mut self.item_id
+    }
+
+    // Take field
+    pub fn take_item_id(&mut self) -> ::std::string::String {
+        ::std::mem::replace(&mut self.item_id, ::std::string::String::new())
+    }
+
+    // int32 from_index = 3;
+
+
+    pub fn get_from_index(&self) -> i32 {
+        self.from_index
+    }
+    pub fn clear_from_index(&mut self) {
+        self.from_index = 0;
+    }
+
+    // Param is passed by value, moved
+    pub fn set_from_index(&mut self, v: i32) {
+        self.from_index = v;
+    }
+
+    // int32 to_index = 4;
+
+
+    pub fn get_to_index(&self) -> i32 {
+        self.to_index
+    }
+    pub fn clear_to_index(&mut self) {
+        self.to_index = 0;
+    }
+
+    // Param is passed by value, moved
+    pub fn set_to_index(&mut self, v: i32) {
+        self.to_index = v;
+    }
+
+    // .MoveItemType ty = 5;
+
+
+    pub fn get_ty(&self) -> MoveItemType {
+        self.ty
+    }
+    pub fn clear_ty(&mut self) {
+        self.ty = MoveItemType::MoveField;
+    }
+
+    // Param is passed by value, moved
+    pub fn set_ty(&mut self, v: MoveItemType) {
+        self.ty = v;
+    }
+}
+
+impl ::protobuf::Message for MoveItemPayload {
+    fn is_initialized(&self) -> bool {
+        true
+    }
+
+    fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::ProtobufResult<()> {
+        while !is.eof()? {
+            let (field_number, wire_type) = is.read_tag_unpack()?;
+            match field_number {
+                1 => {
+                    ::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.grid_id)?;
+                },
+                2 => {
+                    ::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.item_id)?;
+                },
+                3 => {
+                    if wire_type != ::protobuf::wire_format::WireTypeVarint {
+                        return ::std::result::Result::Err(::protobuf::rt::unexpected_wire_type(wire_type));
+                    }
+                    let tmp = is.read_int32()?;
+                    self.from_index = tmp;
+                },
+                4 => {
+                    if wire_type != ::protobuf::wire_format::WireTypeVarint {
+                        return ::std::result::Result::Err(::protobuf::rt::unexpected_wire_type(wire_type));
+                    }
+                    let tmp = is.read_int32()?;
+                    self.to_index = tmp;
+                },
+                5 => {
+                    ::protobuf::rt::read_proto3_enum_with_unknown_fields_into(wire_type, is, &mut self.ty, 5, &mut self.unknown_fields)?
+                },
+                _ => {
+                    ::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?;
+                },
+            };
+        }
+        ::std::result::Result::Ok(())
+    }
+
+    // Compute sizes of nested messages
+    #[allow(unused_variables)]
+    fn compute_size(&self) -> u32 {
+        let mut my_size = 0;
+        if !self.grid_id.is_empty() {
+            my_size += ::protobuf::rt::string_size(1, &self.grid_id);
+        }
+        if !self.item_id.is_empty() {
+            my_size += ::protobuf::rt::string_size(2, &self.item_id);
+        }
+        if self.from_index != 0 {
+            my_size += ::protobuf::rt::value_size(3, self.from_index, ::protobuf::wire_format::WireTypeVarint);
+        }
+        if self.to_index != 0 {
+            my_size += ::protobuf::rt::value_size(4, self.to_index, ::protobuf::wire_format::WireTypeVarint);
+        }
+        if self.ty != MoveItemType::MoveField {
+            my_size += ::protobuf::rt::enum_size(5, self.ty);
+        }
+        my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields());
+        self.cached_size.set(my_size);
+        my_size
+    }
+
+    fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::ProtobufResult<()> {
+        if !self.grid_id.is_empty() {
+            os.write_string(1, &self.grid_id)?;
+        }
+        if !self.item_id.is_empty() {
+            os.write_string(2, &self.item_id)?;
+        }
+        if self.from_index != 0 {
+            os.write_int32(3, self.from_index)?;
+        }
+        if self.to_index != 0 {
+            os.write_int32(4, self.to_index)?;
+        }
+        if self.ty != MoveItemType::MoveField {
+            os.write_enum(5, ::protobuf::ProtobufEnum::value(&self.ty))?;
+        }
+        os.write_unknown_fields(self.get_unknown_fields())?;
+        ::std::result::Result::Ok(())
+    }
+
+    fn get_cached_size(&self) -> u32 {
+        self.cached_size.get()
+    }
+
+    fn get_unknown_fields(&self) -> &::protobuf::UnknownFields {
+        &self.unknown_fields
+    }
+
+    fn mut_unknown_fields(&mut self) -> &mut ::protobuf::UnknownFields {
+        &mut self.unknown_fields
+    }
+
+    fn as_any(&self) -> &dyn (::std::any::Any) {
+        self as &dyn (::std::any::Any)
+    }
+    fn as_any_mut(&mut self) -> &mut dyn (::std::any::Any) {
+        self as &mut dyn (::std::any::Any)
+    }
+    fn into_any(self: ::std::boxed::Box<Self>) -> ::std::boxed::Box<dyn (::std::any::Any)> {
+        self
+    }
+
+    fn descriptor(&self) -> &'static ::protobuf::reflect::MessageDescriptor {
+        Self::descriptor_static()
+    }
+
+    fn new() -> MoveItemPayload {
+        MoveItemPayload::new()
+    }
+
+    fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor {
+        static descriptor: ::protobuf::rt::LazyV2<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::LazyV2::INIT;
+        descriptor.get(|| {
+            let mut fields = ::std::vec::Vec::new();
+            fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
+                "grid_id",
+                |m: &MoveItemPayload| { &m.grid_id },
+                |m: &mut MoveItemPayload| { &mut m.grid_id },
+            ));
+            fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
+                "item_id",
+                |m: &MoveItemPayload| { &m.item_id },
+                |m: &mut MoveItemPayload| { &mut m.item_id },
+            ));
+            fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeInt32>(
+                "from_index",
+                |m: &MoveItemPayload| { &m.from_index },
+                |m: &mut MoveItemPayload| { &mut m.from_index },
+            ));
+            fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeInt32>(
+                "to_index",
+                |m: &MoveItemPayload| { &m.to_index },
+                |m: &mut MoveItemPayload| { &mut m.to_index },
+            ));
+            fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeEnum<MoveItemType>>(
+                "ty",
+                |m: &MoveItemPayload| { &m.ty },
+                |m: &mut MoveItemPayload| { &mut m.ty },
+            ));
+            ::protobuf::reflect::MessageDescriptor::new_pb_name::<MoveItemPayload>(
+                "MoveItemPayload",
+                fields,
+                file_descriptor_proto()
+            )
+        })
+    }
+
+    fn default_instance() -> &'static MoveItemPayload {
+        static instance: ::protobuf::rt::LazyV2<MoveItemPayload> = ::protobuf::rt::LazyV2::INIT;
+        instance.get(MoveItemPayload::new)
+    }
+}
+
+impl ::protobuf::Clear for MoveItemPayload {
+    fn clear(&mut self) {
+        self.grid_id.clear();
+        self.item_id.clear();
+        self.from_index = 0;
+        self.to_index = 0;
+        self.ty = MoveItemType::MoveField;
+        self.unknown_fields.clear();
+    }
+}
+
+impl ::std::fmt::Debug for MoveItemPayload {
+    fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
+        ::protobuf::text_format::fmt(self, f)
+    }
+}
+
+impl ::protobuf::reflect::ProtobufValue for MoveItemPayload {
+    fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
+        ::protobuf::reflect::ReflectValueRef::Message(self)
+    }
+}
+
 #[derive(PartialEq,Clone,Default)]
 pub struct CellChangeset {
     // message fields
@@ -6837,6 +7139,56 @@ impl ::protobuf::reflect::ProtobufValue for CellChangeset {
     }
 }
 
+#[derive(Clone,PartialEq,Eq,Debug,Hash)]
+pub enum MoveItemType {
+    MoveField = 0,
+    MoveRow = 1,
+}
+
+impl ::protobuf::ProtobufEnum for MoveItemType {
+    fn value(&self) -> i32 {
+        *self as i32
+    }
+
+    fn from_i32(value: i32) -> ::std::option::Option<MoveItemType> {
+        match value {
+            0 => ::std::option::Option::Some(MoveItemType::MoveField),
+            1 => ::std::option::Option::Some(MoveItemType::MoveRow),
+            _ => ::std::option::Option::None
+        }
+    }
+
+    fn values() -> &'static [Self] {
+        static values: &'static [MoveItemType] = &[
+            MoveItemType::MoveField,
+            MoveItemType::MoveRow,
+        ];
+        values
+    }
+
+    fn enum_descriptor_static() -> &'static ::protobuf::reflect::EnumDescriptor {
+        static descriptor: ::protobuf::rt::LazyV2<::protobuf::reflect::EnumDescriptor> = ::protobuf::rt::LazyV2::INIT;
+        descriptor.get(|| {
+            ::protobuf::reflect::EnumDescriptor::new_pb_name::<MoveItemType>("MoveItemType", file_descriptor_proto())
+        })
+    }
+}
+
+impl ::std::marker::Copy for MoveItemType {
+}
+
+impl ::std::default::Default for MoveItemType {
+    fn default() -> Self {
+        MoveItemType::MoveField
+    }
+}
+
+impl ::protobuf::reflect::ProtobufValue for MoveItemType {
+    fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
+        ::protobuf::reflect::ReflectValueRef::Enum(::protobuf::ProtobufEnum::descriptor(self))
+    }
+}
+
 #[derive(Clone,PartialEq,Eq,Debug,Hash)]
 pub enum FieldType {
     RichText = 0,
@@ -6932,19 +7284,19 @@ static file_descriptor_proto_data: &'static [u8] = b"\
     \x04.RowR\x05items\"5\n\x11RepeatedGridBlock\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\trowOrders\"\xc5\x01\n\x17GridBlockOrderChang\
-    eset\x12\x19\n\x08block_id\x18\x01\x20\x01(\tR\x07blockId\x123\n\rinsert\
-    ed_rows\x18\x02\x20\x03(\x0b2\x0e.IndexRowOrderR\x0cinsertedRows\x12,\n\
-    \x0cdeleted_rows\x18\x03\x20\x03(\x0b2\t.RowOrderR\x0bdeletedRows\x12,\n\
-    \x0cupdated_rows\x18\x04\x20\x03(\x0b2\t.RowOrderR\x0bupdatedRows\"_\n\r\
-    IndexRowOrder\x12&\n\trow_order\x18\x01\x20\x01(\x0b2\t.RowOrderR\x08row\
-    Order\x12\x16\n\x05index\x18\x02\x20\x01(\x05H\0R\x05indexB\x0e\n\x0cone\
-    _of_index\"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\x07conte\
-    nt\x18\x02\x20\x01(\tR\x07content\"\x8f\x01\n\x14CellNotificationData\
-    \x12\x17\n\x07grid_id\x18\x01\x20\x01(\tR\x06gridId\x12\x19\n\x08field_i\
-    d\x18\x02\x20\x01(\tR\x07fieldId\x12\x15\n\x06row_id\x18\x03\x20\x01(\tR\
+    \x20\x03(\x0b2\t.RowOrderR\trowOrders\"\xbf\x01\n\x11GridRowsChangeset\
+    \x12\x19\n\x08block_id\x18\x01\x20\x01(\tR\x07blockId\x123\n\rinserted_r\
+    ows\x18\x02\x20\x03(\x0b2\x0e.IndexRowOrderR\x0cinsertedRows\x12,\n\x0cd\
+    eleted_rows\x18\x03\x20\x03(\x0b2\t.RowOrderR\x0bdeletedRows\x12,\n\x0cu\
+    pdated_rows\x18\x04\x20\x03(\x0b2\t.RowOrderR\x0bupdatedRows\"_\n\rIndex\
+    RowOrder\x12&\n\trow_order\x18\x01\x20\x01(\x0b2\t.RowOrderR\x08rowOrder\
+    \x12\x16\n\x05index\x18\x02\x20\x01(\x05H\0R\x05indexB\x0e\n\x0cone_of_i\
+    ndex\"E\n\tGridBlock\x12\x0e\n\x02id\x18\x01\x20\x01(\tR\x02id\x12(\n\nr\
+    ow_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\"\x8f\x01\n\x14CellNotificationData\x12\
+    \x17\n\x07grid_id\x18\x01\x20\x01(\tR\x06gridId\x12\x19\n\x08field_id\
+    \x18\x02\x20\x01(\tR\x07fieldId\x12\x15\n\x06row_id\x18\x03\x20\x01(\tR\
     \x05rowId\x12\x1a\n\x07content\x18\x04\x20\x01(\tH\0R\x07contentB\x10\n\
     \x0eone_of_content\"+\n\x0cRepeatedCell\x12\x1b\n\x05items\x18\x01\x20\
     \x03(\x0b2\x05.CellR\x05items\"'\n\x11CreateGridPayload\x12\x12\n\x04nam\
@@ -6971,14 +7323,19 @@ static file_descriptor_proto_data: &'static [u8] = b"\
     \x05H\x05R\x05width\x12*\n\x10type_option_data\x18\t\x20\x01(\x0cH\x06R\
     \x0etypeOptionDataB\r\n\x0bone_of_nameB\r\n\x0bone_of_descB\x13\n\x11one\
     _of_field_typeB\x0f\n\rone_of_frozenB\x13\n\x11one_of_visibilityB\x0e\n\
-    \x0cone_of_widthB\x19\n\x17one_of_type_option_data\"\x7f\n\rCellChangese\
-    t\x12\x17\n\x07grid_id\x18\x01\x20\x01(\tR\x06gridId\x12\x15\n\x06row_id\
-    \x18\x02\x20\x01(\tR\x05rowId\x12\x19\n\x08field_id\x18\x03\x20\x01(\tR\
-    \x07fieldId\x12\x14\n\x04data\x18\x04\x20\x01(\tH\0R\x04dataB\r\n\x0bone\
-    _of_data*d\n\tFieldType\x12\x0c\n\x08RichText\x10\0\x12\n\n\x06Number\
-    \x10\x01\x12\x0c\n\x08DateTime\x10\x02\x12\x10\n\x0cSingleSelect\x10\x03\
-    \x12\x0f\n\x0bMultiSelect\x10\x04\x12\x0c\n\x08Checkbox\x10\x05b\x06prot\
-    o3\
+    \x0cone_of_widthB\x19\n\x17one_of_type_option_data\"\x9c\x01\n\x0fMoveIt\
+    emPayload\x12\x17\n\x07grid_id\x18\x01\x20\x01(\tR\x06gridId\x12\x17\n\
+    \x07item_id\x18\x02\x20\x01(\tR\x06itemId\x12\x1d\n\nfrom_index\x18\x03\
+    \x20\x01(\x05R\tfromIndex\x12\x19\n\x08to_index\x18\x04\x20\x01(\x05R\
+    \x07toIndex\x12\x1d\n\x02ty\x18\x05\x20\x01(\x0e2\r.MoveItemTypeR\x02ty\
+    \"\x7f\n\rCellChangeset\x12\x17\n\x07grid_id\x18\x01\x20\x01(\tR\x06grid\
+    Id\x12\x15\n\x06row_id\x18\x02\x20\x01(\tR\x05rowId\x12\x19\n\x08field_i\
+    d\x18\x03\x20\x01(\tR\x07fieldId\x12\x14\n\x04data\x18\x04\x20\x01(\tH\0\
+    R\x04dataB\r\n\x0bone_of_data**\n\x0cMoveItemType\x12\r\n\tMoveField\x10\
+    \0\x12\x0b\n\x07MoveRow\x10\x01*d\n\tFieldType\x12\x0c\n\x08RichText\x10\
+    \0\x12\n\n\x06Number\x10\x01\x12\x0c\n\x08DateTime\x10\x02\x12\x10\n\x0c\
+    SingleSelect\x10\x03\x12\x0f\n\x0bMultiSelect\x10\x04\x12\x0c\n\x08Check\
+    box\x10\x05b\x06proto3\
 ";
 
 static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;

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

@@ -58,7 +58,7 @@ message GridBlockOrder {
     string block_id = 1;
     repeated RowOrder row_orders = 2;
 }
-message GridBlockOrderChangeset {
+message GridRowsChangeset {
     string block_id = 1;
     repeated IndexRowOrder inserted_rows = 2;
     repeated RowOrder deleted_rows = 3;
@@ -123,12 +123,23 @@ message FieldChangesetPayload {
     oneof one_of_width { int32 width = 8; };
     oneof one_of_type_option_data { bytes type_option_data = 9; };
 }
+message MoveItemPayload {
+    string grid_id = 1;
+    string item_id = 2;
+    int32 from_index = 3;
+    int32 to_index = 4;
+    MoveItemType ty = 5;
+}
 message CellChangeset {
     string grid_id = 1;
     string row_id = 2;
     string field_id = 3;
     oneof one_of_data { string data = 4; };
 }
+enum MoveItemType {
+    MoveField = 0;
+    MoveRow = 1;
+}
 enum FieldType {
     RichText = 0;
     Number = 1;