diff --git a/frontend/app_flowy/assets/translations/en.json b/frontend/app_flowy/assets/translations/en.json index 7e20a947dc..83a7826918 100644 --- a/frontend/app_flowy/assets/translations/en.json +++ b/frontend/app_flowy/assets/translations/en.json @@ -174,6 +174,10 @@ "addOption": "Add option", "editProperty": "Edit property" }, + "row": { + "duplicate": "Duplicate", + "delete": "Delete" + }, "selectOption": { "purpleColor": "Purple", "pinkColor": "Pink", diff --git a/frontend/app_flowy/lib/startup/deps_resolver.dart b/frontend/app_flowy/lib/startup/deps_resolver.dart index f54a7d0a27..5f69ab1ad4 100644 --- a/frontend/app_flowy/lib/startup/deps_resolver.dart +++ b/frontend/app_flowy/lib/startup/deps_resolver.dart @@ -154,7 +154,6 @@ void _resolveGridDeps(GetIt getIt) { getIt.registerFactoryParam( (data, _) => RowBloc( rowData: data, - rowlistener: RowListener(rowId: data.rowId), ), ); diff --git a/frontend/app_flowy/lib/workspace/application/grid/field/action_sheet_bloc.dart b/frontend/app_flowy/lib/workspace/application/grid/field/field_action_sheet_bloc.dart similarity index 67% rename from frontend/app_flowy/lib/workspace/application/grid/field/action_sheet_bloc.dart rename to frontend/app_flowy/lib/workspace/application/grid/field/field_action_sheet_bloc.dart index 5e8cb5a932..40ac699e7f 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/field/action_sheet_bloc.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/field/field_action_sheet_bloc.dart @@ -5,14 +5,14 @@ import 'package:freezed_annotation/freezed_annotation.dart'; import 'dart:async'; import 'field_service.dart'; -part 'action_sheet_bloc.freezed.dart'; +part 'field_action_sheet_bloc.freezed.dart'; -class FieldActionSheetBloc extends Bloc { +class FieldActionSheetBloc extends Bloc { final FieldService service; FieldActionSheetBloc({required Field field, required this.service}) - : super(ActionSheetState.initial(EditFieldContext.create()..gridField = field)) { - on( + : super(FieldActionSheetState.initial(EditFieldContext.create()..gridField = field)) { + on( (event, emit) async { await event.map( updateFieldName: (_UpdateFieldName value) async { @@ -56,23 +56,23 @@ class FieldActionSheetBloc extends Bloc { } @freezed -class ActionSheetEvent with _$ActionSheetEvent { - const factory ActionSheetEvent.updateFieldName(String name) = _UpdateFieldName; - const factory ActionSheetEvent.hideField() = _HideField; - const factory ActionSheetEvent.duplicateField() = _DuplicateField; - const factory ActionSheetEvent.deleteField() = _DeleteField; - const factory ActionSheetEvent.saveField() = _SaveField; +class FieldActionSheetEvent with _$FieldActionSheetEvent { + const factory FieldActionSheetEvent.updateFieldName(String name) = _UpdateFieldName; + const factory FieldActionSheetEvent.hideField() = _HideField; + const factory FieldActionSheetEvent.duplicateField() = _DuplicateField; + const factory FieldActionSheetEvent.deleteField() = _DeleteField; + const factory FieldActionSheetEvent.saveField() = _SaveField; } @freezed -class ActionSheetState with _$ActionSheetState { - const factory ActionSheetState({ +class FieldActionSheetState with _$FieldActionSheetState { + const factory FieldActionSheetState({ required EditFieldContext editContext, required String errorText, required String fieldName, - }) = _ActionSheetState; + }) = _FieldActionSheetState; - factory ActionSheetState.initial(EditFieldContext editContext) => ActionSheetState( + factory FieldActionSheetState.initial(EditFieldContext editContext) => FieldActionSheetState( editContext: editContext, errorText: '', fieldName: editContext.gridField.name, diff --git a/frontend/app_flowy/lib/workspace/application/grid/grid_bloc.dart b/frontend/app_flowy/lib/workspace/application/grid/grid_bloc.dart index 33d3cbe40c..a46136a6b5 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/grid_bloc.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/grid_bloc.dart @@ -111,7 +111,6 @@ class GridBloc extends Bloc { rows.addAll(gridBlock.rowOrders.map( (rowOrder) => GridBlockRow( gridId: view.id, - blockId: gridBlock.id, rowId: rowOrder.rowId, height: rowOrder.height.toDouble(), ), diff --git a/frontend/app_flowy/lib/workspace/application/grid/prelude.dart b/frontend/app_flowy/lib/workspace/application/grid/prelude.dart index f941b01056..a795e23fb2 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/prelude.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/prelude.dart @@ -7,7 +7,7 @@ export 'data.dart'; // Field export 'field/field_service.dart'; export 'field/grid_header_bloc.dart'; -export 'field/action_sheet_bloc.dart'; +export 'field/field_action_sheet_bloc.dart'; export 'field/field_editor_bloc.dart'; export 'field/field_switch_bloc.dart'; diff --git a/frontend/app_flowy/lib/workspace/application/grid/row/row_action_sheet_bloc.dart b/frontend/app_flowy/lib/workspace/application/grid/row/row_action_sheet_bloc.dart new file mode 100644 index 0000000000..93ee70afa0 --- /dev/null +++ b/frontend/app_flowy/lib/workspace/application/grid/row/row_action_sheet_bloc.dart @@ -0,0 +1,58 @@ +import 'package:app_flowy/workspace/application/grid/row/row_service.dart'; +import 'package:flowy_sdk/log.dart'; +import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:freezed_annotation/freezed_annotation.dart'; +import 'dart:async'; +import 'package:dartz/dartz.dart'; + +part 'row_action_sheet_bloc.freezed.dart'; + +class RowActionSheetBloc extends Bloc { + final RowService _rowService; + + RowActionSheetBloc({required RowData rowData}) + : _rowService = RowService(gridId: rowData.gridId, rowId: rowData.rowId), + super(RowActionSheetState.initial(rowData)) { + on( + (event, emit) async { + await event.map( + deleteRow: (_DeleteRow value) async { + final result = await _rowService.deleteRow(); + logResult(result); + }, + duplicateRow: (_DuplicateRow value) async { + final result = await _rowService.duplicateRow(); + logResult(result); + }, + ); + }, + ); + } + + @override + Future close() async { + return super.close(); + } + + void logResult(Either result) { + result.fold((l) => null, (err) => Log.error(err)); + } +} + +@freezed +class RowActionSheetEvent with _$RowActionSheetEvent { + const factory RowActionSheetEvent.duplicateRow() = _DuplicateRow; + const factory RowActionSheetEvent.deleteRow() = _DeleteRow; +} + +@freezed +class RowActionSheetState with _$RowActionSheetState { + const factory RowActionSheetState({ + required RowData rowData, + }) = _RowActionSheetState; + + factory RowActionSheetState.initial(RowData rowData) => RowActionSheetState( + rowData: rowData, + ); +} diff --git a/frontend/app_flowy/lib/workspace/application/grid/row/row_bloc.dart b/frontend/app_flowy/lib/workspace/application/grid/row/row_bloc.dart index 418cb23e88..6c0037d289 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/row/row_bloc.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/row/row_bloc.dart @@ -1,7 +1,6 @@ import 'dart:collection'; import 'package:app_flowy/workspace/application/grid/field/grid_listenr.dart'; -import 'package:app_flowy/workspace/application/grid/grid_bloc.dart'; 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'; @@ -16,19 +15,14 @@ part 'row_bloc.freezed.dart'; typedef CellDataMap = LinkedHashMap; class RowBloc extends Bloc { - final RowService rowService; - final RowListener rowlistener; - final GridFieldsListener fieldListener; + final RowService _rowService; + final RowListener _rowlistener; + final GridFieldsListener _fieldListener; - RowBloc({required RowData rowData, required this.rowlistener}) - : rowService = RowService( - gridId: rowData.gridId, - blockId: rowData.blockId, - rowId: rowData.rowId, - ), - fieldListener = GridFieldsListener( - gridId: rowData.gridId, - ), + RowBloc({required RowData rowData}) + : _rowService = RowService(gridId: rowData.gridId, rowId: rowData.rowId), + _fieldListener = GridFieldsListener(gridId: rowData.gridId), + _rowlistener = RowListener(rowId: rowData.rowId), super(RowState.initial(rowData)) { on( (event, emit) async { @@ -38,7 +32,7 @@ class RowBloc extends Bloc { await _loadRow(emit); }, createRow: (_CreateRow value) { - rowService.createRow(); + _rowService.createRow(); }, didReceiveFieldUpdate: (_DidReceiveFieldUpdate value) async { await _handleFieldUpdate(emit, value); @@ -52,7 +46,7 @@ class RowBloc extends Bloc { } void _handleRowUpdate(_DidUpdateRow value, Emitter emit) { - final CellDataMap cellDataMap = _makeCellDatas(value.row, state.fields); + final CellDataMap cellDataMap = _makeCellDatas(value.row, state.rowData.fields); emit(state.copyWith( row: Future(() => Some(value.row)), cellDataMap: Some(cellDataMap), @@ -67,39 +61,39 @@ class RowBloc extends Bloc { ); emit(state.copyWith( - fields: value.fields, + rowData: state.rowData.copyWith(fields: value.fields), cellDataMap: Some(cellDataMap), )); } @override Future close() async { - await rowlistener.stop(); - await fieldListener.stop(); + await _rowlistener.stop(); + await _fieldListener.stop(); return super.close(); } Future _startListening() async { - rowlistener.updateRowNotifier.addPublishListener((result) { + _rowlistener.updateRowNotifier.addPublishListener((result) { result.fold( (row) => add(RowEvent.didUpdateRow(row)), (err) => Log.error(err), ); }); - fieldListener.updateFieldsNotifier.addPublishListener((result) { + _fieldListener.updateFieldsNotifier.addPublishListener((result) { result.fold( (fields) => add(RowEvent.didReceiveFieldUpdate(fields)), (err) => Log.error(err), ); }); - rowlistener.start(); - fieldListener.start(); + _rowlistener.start(); + _fieldListener.start(); } Future _loadRow(Emitter emit) async { - rowService.getRow().then((result) { + _rowService.getRow().then((result) { return result.fold( (row) => add(RowEvent.didUpdateRow(row)), (err) => Log.error(err), @@ -113,7 +107,7 @@ class RowBloc extends Bloc { if (field.visibility) { map[field.id] = CellData( rowId: row.id, - gridId: rowService.gridId, + gridId: _rowService.gridId, cell: row.cellByFieldId[field.id], field: field, ); @@ -134,17 +128,13 @@ class RowEvent with _$RowEvent { @freezed class RowState with _$RowState { const factory RowState({ - required String rowId, - required double rowHeight, - required List fields, + required RowData rowData, required Future> row, required Option cellDataMap, }) = _RowState; - factory RowState.initial(RowData data) => RowState( - rowId: data.rowId, - rowHeight: data.height, - fields: data.fields, + factory RowState.initial(RowData rowData) => RowState( + rowData: rowData, row: Future(() => none()), cellDataMap: none(), ); diff --git a/frontend/app_flowy/lib/workspace/application/grid/row/row_service.dart b/frontend/app_flowy/lib/workspace/application/grid/row/row_service.dart index 7ed5477364..f87d9f8574 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/row/row_service.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/row/row_service.dart @@ -10,9 +10,8 @@ part 'row_service.freezed.dart'; class RowService { final String gridId; final String rowId; - final String blockId; - RowService({required this.gridId, required this.rowId, required this.blockId}); + RowService({required this.gridId, required this.rowId}); Future> createRow() { CreateRowPayload payload = CreateRowPayload.create() @@ -29,6 +28,22 @@ class RowService { return GridEventGetRow(payload).send(); } + + Future> deleteRow() { + final payload = RowIdentifierPayload.create() + ..gridId = gridId + ..rowId = rowId; + + return GridEventDeleteRow(payload).send(); + } + + Future> duplicateRow() { + final payload = RowIdentifierPayload.create() + ..gridId = gridId + ..rowId = rowId; + + return GridEventDuplicateRow(payload).send(); + } } @freezed @@ -46,7 +61,6 @@ class RowData with _$RowData { const factory RowData({ required String gridId, required String rowId, - required String blockId, required List fields, required double height, }) = _RowData; @@ -55,7 +69,6 @@ class RowData with _$RowData { return RowData( gridId: row.gridId, rowId: row.rowId, - blockId: row.blockId, fields: fields, height: row.height, ); @@ -67,7 +80,6 @@ class GridBlockRow with _$GridBlockRow { const factory GridBlockRow({ required String gridId, required String rowId, - required String blockId, required double height, }) = _GridBlockRow; } diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/grid_page.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/grid_page.dart index 5de9cc75a6..b96b89ba2e 100755 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/grid_page.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/grid_page.dart @@ -73,6 +73,7 @@ class FlowyGrid extends StatefulWidget { class _FlowyGridState extends State { final _scrollController = GridScrollController(); + final _key = GlobalKey(); @override void dispose() { @@ -91,6 +92,7 @@ class _FlowyGridState extends State { return const Center(child: CircularProgressIndicator.adaptive()); } +// _key.currentState.insertItem(index) final child = BlocBuilder( builder: (context, state) { return SizedBox( @@ -153,20 +155,24 @@ class _FlowyGridState extends State { return rowChanged; }, builder: (context, state) { - return SliverList( - delegate: SliverChildBuilderDelegate( - (context, index) { - final blockRow = context.read().state.rows[index]; - final fields = context.read().state.fields; - final rowData = RowData.fromBlockRow(blockRow, fields); - return GridRowWidget(data: rowData, key: ValueKey(rowData.rowId)); - }, - childCount: context.read().state.rows.length, - addRepaintBoundaries: true, - addAutomaticKeepAlives: true, - ), + return SliverAnimatedList( + key: _key, + initialItemCount: context.read().state.rows.length, + itemBuilder: (BuildContext context, int index, Animation animation) { + final blockRow = context.read().state.rows[index]; + final fields = context.read().state.fields; + final rowData = RowData.fromBlockRow(blockRow, fields); + return _renderRow(rowData, animation); + }, ); }, ); } + + Widget _renderRow(RowData rowData, Animation animation) { + return SizeTransition( + sizeFactor: animation, + child: GridRowWidget(data: rowData, key: ValueKey(rowData.rowId)), + ); + } } diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/layout/sizes.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/layout/sizes.dart index 23a541e610..8dc0b20263 100755 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/layout/sizes.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/layout/sizes.dart @@ -6,7 +6,7 @@ class GridSize { static double get scrollBarSize => 12 * scale; static double get headerHeight => 40 * scale; static double get footerHeight => 40 * scale; - static double get leadingHeaderPadding => 30 * scale; + static double get leadingHeaderPadding => 50 * scale; static double get trailHeaderPadding => 140 * scale; static double get headerContainerPadding => 0 * scale; static double get cellHPadding => 10 * scale; diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_cell_action_sheet.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_cell_action_sheet.dart index 42d49b69fe..2dc1e2976b 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_cell_action_sheet.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_cell_action_sheet.dart @@ -23,7 +23,7 @@ class GridFieldCellActionSheet extends StatelessWidget with FlowyOverlayDelegate child: this, constraints: BoxConstraints.loose(const Size(240, 200)), ), - identifier: identifier(), + identifier: GridFieldCellActionSheet.identifier(), anchorContext: overlayContext, anchorDirection: AnchorDirection.bottomWithLeftAligned, delegate: this, @@ -68,7 +68,7 @@ class _EditFieldButton extends StatelessWidget { @override Widget build(BuildContext context) { final theme = context.watch(); - return BlocBuilder( + return BlocBuilder( builder: (context, state) { return SizedBox( height: GridSize.typeOptionItemHeight, @@ -100,16 +100,6 @@ class _FieldOperationList extends StatelessWidget { ) .toList(); - return FieldOperationList(actions: actions); - } -} - -class FieldOperationList extends StatelessWidget { - final List actions; - const FieldOperationList({required this.actions, Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { return GridView( // https://api.flutter.dev/flutter/widgets/AnimatedList/shrinkWrap.html shrinkWrap: true, @@ -182,13 +172,13 @@ extension _FieldActionExtension on FieldAction { void run(BuildContext context) { switch (this) { case FieldAction.hide: - context.read().add(const ActionSheetEvent.hideField()); + context.read().add(const FieldActionSheetEvent.hideField()); break; case FieldAction.duplicate: - context.read().add(const ActionSheetEvent.duplicateField()); + context.read().add(const FieldActionSheetEvent.duplicateField()); break; case FieldAction.delete: - context.read().add(const ActionSheetEvent.deleteField()); + context.read().add(const FieldActionSheetEvent.deleteField()); break; } } diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/row/grid_row.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/row/grid_row.dart index 08d68f197a..a5b6a405a6 100755 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/row/grid_row.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/row/grid_row.dart @@ -5,10 +5,13 @@ import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/cell/p import 'package:flowy_infra/image.dart'; import 'package:flowy_infra/theme.dart'; import 'package:flowy_infra_ui/style_widget/icon_button.dart'; +import 'package:flowy_infra_ui/widget/spacing.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:provider/provider.dart'; +import 'row_action_sheet.dart'; + class GridRowWidget extends StatefulWidget { final RowData data; const GridRowWidget({required this.data, Key? key}) : super(key: key); @@ -39,10 +42,10 @@ class _GridRowWidgetState extends State { onEnter: (p) => _rowStateNotifier.onEnter = true, onExit: (p) => _rowStateNotifier.onEnter = false, child: BlocBuilder( - buildWhen: (p, c) => p.rowHeight != c.rowHeight, + buildWhen: (p, c) => p.rowData.height != c.rowData.height, builder: (context, state) { return SizedBox( - height: _rowBloc.state.rowHeight, + height: _rowBloc.state.rowData.height, child: Row( crossAxisAlignment: CrossAxisAlignment.stretch, children: const [ @@ -83,7 +86,8 @@ class _RowLeading extends StatelessWidget { return Row( mainAxisAlignment: MainAxisAlignment.center, children: const [ - AppendRowButton(), + _InsertRowButton(), + _DeleteRowButton(), ], ); } @@ -98,15 +102,16 @@ class _RowTrailing extends StatelessWidget { } } -class AppendRowButton extends StatelessWidget { - const AppendRowButton({Key? key}) : super(key: key); +class _InsertRowButton extends StatelessWidget { + const _InsertRowButton({Key? key}) : super(key: key); @override Widget build(BuildContext context) { final theme = context.watch(); return FlowyIconButton( hoverColor: theme.hover, - width: 22, + width: 20, + height: 30, onPressed: () => context.read().add(const RowEvent.createRow()), iconPadding: const EdgeInsets.all(3), icon: svgWidget("home/add"), @@ -114,6 +119,25 @@ class AppendRowButton extends StatelessWidget { } } +class _DeleteRowButton extends StatelessWidget { + const _DeleteRowButton({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + final theme = context.watch(); + return FlowyIconButton( + hoverColor: theme.hover, + width: 20, + height: 30, + onPressed: () => GridRowActionSheet( + rowData: context.read().state.rowData, + ).show(context), + iconPadding: const EdgeInsets.all(3), + icon: svgWidget("editor/details"), + ); + } +} + class _RowCells extends StatelessWidget { const _RowCells({Key? key}) : super(key: key); diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/row/row_action_sheet.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/row/row_action_sheet.dart new file mode 100644 index 0000000000..f1ff46af08 --- /dev/null +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/row/row_action_sheet.dart @@ -0,0 +1,133 @@ +import 'package:app_flowy/workspace/application/grid/row/row_action_sheet_bloc.dart'; +import 'package:app_flowy/workspace/application/grid/row/row_service.dart'; +import 'package:app_flowy/workspace/presentation/plugins/grid/src/layout/sizes.dart'; +import 'package:easy_localization/easy_localization.dart'; +import 'package:app_flowy/generated/locale_keys.g.dart'; +import 'package:flowy_infra/image.dart'; +import 'package:flowy_infra/theme.dart'; +import 'package:flowy_infra_ui/flowy_infra_ui.dart'; +import 'package:flowy_infra_ui/style_widget/button.dart'; +import 'package:flowy_infra_ui/style_widget/scrolling/styled_list.dart'; +import 'package:flowy_infra_ui/style_widget/text.dart'; +import 'package:flowy_infra_ui/widget/spacing.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +class GridRowActionSheet extends StatelessWidget { + final RowData rowData; + const GridRowActionSheet({required this.rowData, Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return BlocProvider( + create: (context) => RowActionSheetBloc(rowData: rowData), + child: BlocBuilder( + builder: (context, state) { + final cells = _RowAction.values + .map( + (action) => _RowActionCell( + action: action, + onDismissed: () => remove(context), + ), + ) + .toList(); + + // + final list = ListView.separated( + shrinkWrap: true, + controller: ScrollController(), + itemCount: cells.length, + separatorBuilder: (context, index) { + return VSpace(GridSize.typeOptionSeparatorHeight); + }, + physics: StyledScrollPhysics(), + itemBuilder: (BuildContext context, int index) { + return cells[index]; + }, + ); + return list; + }, + ), + ); + } + + void show(BuildContext overlayContext) { + FlowyOverlay.of(overlayContext).insertWithAnchor( + widget: OverlayContainer( + child: this, + constraints: BoxConstraints.loose(const Size(140, 200)), + ), + identifier: GridRowActionSheet.identifier(), + anchorContext: overlayContext, + anchorDirection: AnchorDirection.leftWithCenterAligned, + ); + } + + void remove(BuildContext overlayContext) { + FlowyOverlay.of(overlayContext).remove(GridRowActionSheet.identifier()); + } + + static String identifier() { + return (GridRowActionSheet).toString(); + } +} + +class _RowActionCell extends StatelessWidget { + final _RowAction action; + final VoidCallback onDismissed; + const _RowActionCell({required this.action, required this.onDismissed, Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + final theme = context.watch(); + + return SizedBox( + height: GridSize.typeOptionItemHeight, + child: FlowyButton( + text: FlowyText.medium(action.title(), fontSize: 12), + hoverColor: theme.hover, + onTap: () { + action.performAction(context); + onDismissed(); + }, + leftIcon: svgWidget(action.iconName(), color: theme.iconColor), + ), + ); + } +} + +enum _RowAction { + delete, + duplicate, +} + +extension _RowActionExtension on _RowAction { + String iconName() { + switch (this) { + case _RowAction.duplicate: + return 'grid/duplicate'; + case _RowAction.delete: + return 'grid/delete'; + } + } + + String title() { + switch (this) { + case _RowAction.duplicate: + return LocaleKeys.grid_row_duplicate.tr(); + case _RowAction.delete: + return LocaleKeys.grid_row_delete.tr(); + } + } + + void performAction(BuildContext context) { + switch (this) { + case _RowAction.duplicate: + // context.read().add(const RowActionSheetEvent.duplicateRow()); + break; + case _RowAction.delete: + // context.read().add(const RowActionSheetEvent.deleteRow()); + break; + } + } +} diff --git a/frontend/app_flowy/packages/flowy_sdk/lib/dispatch/dart_event/flowy-grid/dart_event.dart b/frontend/app_flowy/packages/flowy_sdk/lib/dispatch/dart_event/flowy-grid/dart_event.dart index a4871a6bef..8544bc6edd 100644 --- a/frontend/app_flowy/packages/flowy_sdk/lib/dispatch/dart_event/flowy-grid/dart_event.dart +++ b/frontend/app_flowy/packages/flowy_sdk/lib/dispatch/dart_event/flowy-grid/dart_event.dart @@ -239,6 +239,40 @@ class GridEventGetRow { } } +class GridEventDeleteRow { + RowIdentifierPayload request; + GridEventDeleteRow(this.request); + + Future> send() { + final request = FFIRequest.create() + ..event = GridEvent.DeleteRow.toString() + ..payload = requestToBytes(this.request); + + return Dispatch.asyncRequest(request) + .then((bytesResult) => bytesResult.fold( + (bytes) => left(unit), + (errBytes) => right(FlowyError.fromBuffer(errBytes)), + )); + } +} + +class GridEventDuplicateRow { + RowIdentifierPayload request; + GridEventDuplicateRow(this.request); + + Future> send() { + final request = FFIRequest.create() + ..event = GridEvent.DuplicateRow.toString() + ..payload = requestToBytes(this.request); + + return Dispatch.asyncRequest(request) + .then((bytesResult) => bytesResult.fold( + (bytes) => left(unit), + (errBytes) => right(FlowyError.fromBuffer(errBytes)), + )); + } +} + class GridEventGetCell { CellIdentifierPayload request; GridEventGetCell(this.request); diff --git a/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/event_map.pbenum.dart b/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/event_map.pbenum.dart index 37032ec41b..5be5844119 100644 --- a/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/event_map.pbenum.dart +++ b/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/event_map.pbenum.dart @@ -24,6 +24,8 @@ class GridEvent extends $pb.ProtobufEnum { static const GridEvent ApplySelectOptionChangeset = GridEvent._(32, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'ApplySelectOptionChangeset'); static const GridEvent CreateRow = GridEvent._(50, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'CreateRow'); static const GridEvent GetRow = GridEvent._(51, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'GetRow'); + static const GridEvent DeleteRow = GridEvent._(52, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'DeleteRow'); + static const GridEvent DuplicateRow = GridEvent._(53, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'DuplicateRow'); static const GridEvent GetCell = GridEvent._(70, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'GetCell'); static const GridEvent UpdateCell = GridEvent._(71, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'UpdateCell'); static const GridEvent ApplySelectOptionCellChangeset = GridEvent._(72, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'ApplySelectOptionCellChangeset'); @@ -43,6 +45,8 @@ class GridEvent extends $pb.ProtobufEnum { ApplySelectOptionChangeset, CreateRow, GetRow, + DeleteRow, + DuplicateRow, GetCell, UpdateCell, ApplySelectOptionCellChangeset, diff --git a/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/event_map.pbjson.dart b/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/event_map.pbjson.dart index bc4b838bca..2f2921564b 100644 --- a/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/event_map.pbjson.dart +++ b/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid/event_map.pbjson.dart @@ -26,6 +26,8 @@ const GridEvent$json = const { const {'1': 'ApplySelectOptionChangeset', '2': 32}, const {'1': 'CreateRow', '2': 50}, const {'1': 'GetRow', '2': 51}, + const {'1': 'DeleteRow', '2': 52}, + const {'1': 'DuplicateRow', '2': 53}, const {'1': 'GetCell', '2': 70}, const {'1': 'UpdateCell', '2': 71}, const {'1': 'ApplySelectOptionCellChangeset', '2': 72}, @@ -33,4 +35,4 @@ const GridEvent$json = const { }; /// Descriptor for `GridEvent`. Decode as a `google.protobuf.EnumDescriptorProto`. -final $typed_data.Uint8List gridEventDescriptor = $convert.base64Decode('CglHcmlkRXZlbnQSDwoLR2V0R3JpZERhdGEQABIRCg1HZXRHcmlkQmxvY2tzEAESDQoJR2V0RmllbGRzEAoSDwoLVXBkYXRlRmllbGQQCxIPCgtDcmVhdGVGaWVsZBAMEg8KC0RlbGV0ZUZpZWxkEA0SEQoNU3dpdGNoVG9GaWVsZBAOEhIKDkR1cGxpY2F0ZUZpZWxkEA8SFwoTR2V0RWRpdEZpZWxkQ29udGV4dBAQEhMKD05ld1NlbGVjdE9wdGlvbhAeEhoKFkdldFNlbGVjdE9wdGlvbkNvbnRleHQQHxIeChpBcHBseVNlbGVjdE9wdGlvbkNoYW5nZXNldBAgEg0KCUNyZWF0ZVJvdxAyEgoKBkdldFJvdxAzEgsKB0dldENlbGwQRhIOCgpVcGRhdGVDZWxsEEcSIgoeQXBwbHlTZWxlY3RPcHRpb25DZWxsQ2hhbmdlc2V0EEg='); +final $typed_data.Uint8List gridEventDescriptor = $convert.base64Decode('CglHcmlkRXZlbnQSDwoLR2V0R3JpZERhdGEQABIRCg1HZXRHcmlkQmxvY2tzEAESDQoJR2V0RmllbGRzEAoSDwoLVXBkYXRlRmllbGQQCxIPCgtDcmVhdGVGaWVsZBAMEg8KC0RlbGV0ZUZpZWxkEA0SEQoNU3dpdGNoVG9GaWVsZBAOEhIKDkR1cGxpY2F0ZUZpZWxkEA8SFwoTR2V0RWRpdEZpZWxkQ29udGV4dBAQEhMKD05ld1NlbGVjdE9wdGlvbhAeEhoKFkdldFNlbGVjdE9wdGlvbkNvbnRleHQQHxIeChpBcHBseVNlbGVjdE9wdGlvbkNoYW5nZXNldBAgEg0KCUNyZWF0ZVJvdxAyEgoKBkdldFJvdxAzEg0KCURlbGV0ZVJvdxA0EhAKDER1cGxpY2F0ZVJvdxA1EgsKB0dldENlbGwQRhIOCgpVcGRhdGVDZWxsEEcSIgoeQXBwbHlTZWxlY3RPcHRpb25DZWxsQ2hhbmdlc2V0EEg='); diff --git a/frontend/rust-lib/flowy-grid/src/event_handler.rs b/frontend/rust-lib/flowy-grid/src/event_handler.rs index 467c3e334b..d94753c4b0 100644 --- a/frontend/rust-lib/flowy-grid/src/event_handler.rs +++ b/frontend/rust-lib/flowy-grid/src/event_handler.rs @@ -179,6 +179,22 @@ pub(crate) async fn get_row_handler( } } +#[tracing::instrument(level = "debug", skip(data, manager), err)] +pub(crate) async fn delete_row_handler( + data: Data, + manager: AppData>, +) -> DataResult { + todo!() +} + +#[tracing::instrument(level = "debug", skip(data, manager), err)] +pub(crate) async fn duplicate_row_handler( + data: Data, + manager: AppData>, +) -> DataResult { + todo!() +} + #[tracing::instrument(level = "debug", skip(data, manager), err)] pub(crate) async fn create_row_handler( data: Data, diff --git a/frontend/rust-lib/flowy-grid/src/event_map.rs b/frontend/rust-lib/flowy-grid/src/event_map.rs index 8a4c68e468..7246cc7f4f 100644 --- a/frontend/rust-lib/flowy-grid/src/event_map.rs +++ b/frontend/rust-lib/flowy-grid/src/event_map.rs @@ -20,6 +20,8 @@ pub fn create(grid_manager: Arc) -> Module { // Row .event(GridEvent::CreateRow, create_row_handler) .event(GridEvent::GetRow, get_row_handler) + .event(GridEvent::DeleteRow, delete_row_handler) + .event(GridEvent::DuplicateRow, duplicate_row_handler) // Cell .event(GridEvent::GetCell, get_cell_handler) .event(GridEvent::UpdateCell, update_cell_handler) @@ -81,6 +83,12 @@ pub enum GridEvent { #[event(input = "RowIdentifierPayload", output = "Row")] GetRow = 51, + #[event(input = "RowIdentifierPayload")] + DeleteRow = 52, + + #[event(input = "RowIdentifierPayload")] + DuplicateRow = 53, + #[event(input = "CellIdentifierPayload", output = "Cell")] GetCell = 70, diff --git a/frontend/rust-lib/flowy-grid/src/protobuf/model/event_map.rs b/frontend/rust-lib/flowy-grid/src/protobuf/model/event_map.rs index d9cfbac578..10bee819ce 100644 --- a/frontend/rust-lib/flowy-grid/src/protobuf/model/event_map.rs +++ b/frontend/rust-lib/flowy-grid/src/protobuf/model/event_map.rs @@ -39,6 +39,8 @@ pub enum GridEvent { ApplySelectOptionChangeset = 32, CreateRow = 50, GetRow = 51, + DeleteRow = 52, + DuplicateRow = 53, GetCell = 70, UpdateCell = 71, ApplySelectOptionCellChangeset = 72, @@ -65,6 +67,8 @@ impl ::protobuf::ProtobufEnum for GridEvent { 32 => ::std::option::Option::Some(GridEvent::ApplySelectOptionChangeset), 50 => ::std::option::Option::Some(GridEvent::CreateRow), 51 => ::std::option::Option::Some(GridEvent::GetRow), + 52 => ::std::option::Option::Some(GridEvent::DeleteRow), + 53 => ::std::option::Option::Some(GridEvent::DuplicateRow), 70 => ::std::option::Option::Some(GridEvent::GetCell), 71 => ::std::option::Option::Some(GridEvent::UpdateCell), 72 => ::std::option::Option::Some(GridEvent::ApplySelectOptionCellChangeset), @@ -88,6 +92,8 @@ impl ::protobuf::ProtobufEnum for GridEvent { GridEvent::ApplySelectOptionChangeset, GridEvent::CreateRow, GridEvent::GetRow, + GridEvent::DeleteRow, + GridEvent::DuplicateRow, GridEvent::GetCell, GridEvent::UpdateCell, GridEvent::ApplySelectOptionCellChangeset, @@ -119,15 +125,16 @@ impl ::protobuf::reflect::ProtobufValue for GridEvent { } static file_descriptor_proto_data: &'static [u8] = b"\ - \n\x0fevent_map.proto*\xde\x02\n\tGridEvent\x12\x0f\n\x0bGetGridData\x10\ + \n\x0fevent_map.proto*\xff\x02\n\tGridEvent\x12\x0f\n\x0bGetGridData\x10\ \0\x12\x11\n\rGetGridBlocks\x10\x01\x12\r\n\tGetFields\x10\n\x12\x0f\n\ \x0bUpdateField\x10\x0b\x12\x0f\n\x0bCreateField\x10\x0c\x12\x0f\n\x0bDe\ leteField\x10\r\x12\x11\n\rSwitchToField\x10\x0e\x12\x12\n\x0eDuplicateF\ ield\x10\x0f\x12\x17\n\x13GetEditFieldContext\x10\x10\x12\x13\n\x0fNewSe\ lectOption\x10\x1e\x12\x1a\n\x16GetSelectOptionContext\x10\x1f\x12\x1e\n\ \x1aApplySelectOptionChangeset\x10\x20\x12\r\n\tCreateRow\x102\x12\n\n\ - \x06GetRow\x103\x12\x0b\n\x07GetCell\x10F\x12\x0e\n\nUpdateCell\x10G\x12\ - \"\n\x1eApplySelectOptionCellChangeset\x10Hb\x06proto3\ + \x06GetRow\x103\x12\r\n\tDeleteRow\x104\x12\x10\n\x0cDuplicateRow\x105\ + \x12\x0b\n\x07GetCell\x10F\x12\x0e\n\nUpdateCell\x10G\x12\"\n\x1eApplySe\ + lectOptionCellChangeset\x10Hb\x06proto3\ "; static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT; diff --git a/frontend/rust-lib/flowy-grid/src/protobuf/proto/event_map.proto b/frontend/rust-lib/flowy-grid/src/protobuf/proto/event_map.proto index 1201bd759e..9fad8dd7b1 100644 --- a/frontend/rust-lib/flowy-grid/src/protobuf/proto/event_map.proto +++ b/frontend/rust-lib/flowy-grid/src/protobuf/proto/event_map.proto @@ -15,6 +15,8 @@ enum GridEvent { ApplySelectOptionChangeset = 32; CreateRow = 50; GetRow = 51; + DeleteRow = 52; + DuplicateRow = 53; GetCell = 70; UpdateCell = 71; ApplySelectOptionCellChangeset = 72;