From 0bbf17f776709a8f46fdcf5f6cef4e5fafe0edb2 Mon Sep 17 00:00:00 2001 From: appflowy Date: Thu, 3 Mar 2022 10:51:52 +0800 Subject: [PATCH] feat: add grid widget --- .../workspace/application/grid/grid_bloc.dart | 135 +++++ .../application/grid/grid_service.dart | 20 + .../controller/flowy_table_selection.dart | 73 +++ .../plugins/grid/controller/grid_scroll.dart | 22 + .../plugins/grid/grid_layout.dart | 17 + .../presentation/plugins/grid/grid_page.dart | 150 ++++++ .../presentation/plugins/grid/grid_sizes.dart | 16 + .../widgets/grid_content/cell_builder.dart | 17 + .../widgets/grid_content/cell_container.dart | 34 ++ .../widgets/grid_content/cell_decoration.dart | 10 + .../grid/widgets/grid_content/grid_cell.dart | 68 +++ .../grid/widgets/grid_content/grid_row.dart | 64 +++ .../plugins/grid/widgets/grid_error_page.dart | 14 + .../grid/widgets/grid_footer/grid_footer.dart | 46 ++ .../grid/widgets/grid_header/constants.dart | 5 + .../grid/widgets/grid_header/header.dart | 0 .../grid/widgets/grid_header/header_cell.dart | 52 ++ .../flowy_sdk/lib/dispatch/dispatch.dart | 3 + .../flowy-grid-data-model/grid.pb.dart | 138 ++++- .../flowy-grid-data-model/grid.pbjson.dart | 25 +- .../src/entities/grid.rs | 17 +- .../src/protobuf/model/grid.rs | 492 +++++++++++++++--- .../src/protobuf/proto/grid.proto | 9 +- 23 files changed, 1343 insertions(+), 84 deletions(-) create mode 100644 frontend/app_flowy/lib/workspace/application/grid/grid_bloc.dart create mode 100644 frontend/app_flowy/lib/workspace/application/grid/grid_service.dart create mode 100755 frontend/app_flowy/lib/workspace/presentation/plugins/grid/controller/flowy_table_selection.dart create mode 100755 frontend/app_flowy/lib/workspace/presentation/plugins/grid/controller/grid_scroll.dart create mode 100755 frontend/app_flowy/lib/workspace/presentation/plugins/grid/grid_layout.dart create mode 100755 frontend/app_flowy/lib/workspace/presentation/plugins/grid/grid_page.dart create mode 100755 frontend/app_flowy/lib/workspace/presentation/plugins/grid/grid_sizes.dart create mode 100755 frontend/app_flowy/lib/workspace/presentation/plugins/grid/widgets/grid_content/cell_builder.dart create mode 100755 frontend/app_flowy/lib/workspace/presentation/plugins/grid/widgets/grid_content/cell_container.dart create mode 100755 frontend/app_flowy/lib/workspace/presentation/plugins/grid/widgets/grid_content/cell_decoration.dart create mode 100755 frontend/app_flowy/lib/workspace/presentation/plugins/grid/widgets/grid_content/grid_cell.dart create mode 100755 frontend/app_flowy/lib/workspace/presentation/plugins/grid/widgets/grid_content/grid_row.dart create mode 100755 frontend/app_flowy/lib/workspace/presentation/plugins/grid/widgets/grid_error_page.dart create mode 100755 frontend/app_flowy/lib/workspace/presentation/plugins/grid/widgets/grid_footer/grid_footer.dart create mode 100755 frontend/app_flowy/lib/workspace/presentation/plugins/grid/widgets/grid_header/constants.dart create mode 100644 frontend/app_flowy/lib/workspace/presentation/plugins/grid/widgets/grid_header/header.dart create mode 100755 frontend/app_flowy/lib/workspace/presentation/plugins/grid/widgets/grid_header/header_cell.dart diff --git a/frontend/app_flowy/lib/workspace/application/grid/grid_bloc.dart b/frontend/app_flowy/lib/workspace/application/grid/grid_bloc.dart new file mode 100644 index 0000000000..e6663c4515 --- /dev/null +++ b/frontend/app_flowy/lib/workspace/application/grid/grid_bloc.dart @@ -0,0 +1,135 @@ +import 'dart:async'; + +import 'package:dartz/dartz.dart'; +import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart'; +import 'package:flowy_sdk/protobuf/flowy-folder-data-model/view.pb.dart'; +import 'package:flowy_sdk/protobuf/flowy-grid-data-model/protobuf.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:freezed_annotation/freezed_annotation.dart'; + +import 'grid_service.dart'; + +part 'grid_bloc.freezed.dart'; + +class GridBloc extends Bloc { + final GridService service; + final View view; + late Grid _grid; + + GridBloc({required this.view, required this.service}) : super(GridState.initial()) { + on( + (event, emit) async { + await event.map( + initial: (Initial value) async { + await _initial(value, emit); + }, + createRow: (_CreateRow value) { + service.createRow(gridId: view.id); + }, + delete: (_Delete value) {}, + rename: (_Rename value) {}, + updateDesc: (_Desc value) {}, + ); + }, + ); + } + + @override + Future close() async { + return super.close(); + } + + Future _initial(Initial value, Emitter emit) async { + final result = await service.openGrid(gridId: view.id); + result.fold( + (grid) { + _grid = grid; + _loadGridInfo(emit); + }, + (err) { + emit(state.copyWith(loadingState: GridLoadingState.finish(right(err)))); + }, + ); + } + + Future _loadGridInfo(Emitter emit) async { + emit( + state.copyWith(loadingState: GridLoadingState.finish(left(unit))), + ); + } +} + +@freezed +abstract class GridEvent with _$GridEvent { + const factory GridEvent.initial() = Initial; + const factory GridEvent.rename(String gridId, String name) = _Rename; + const factory GridEvent.updateDesc(String gridId, String desc) = _Desc; + const factory GridEvent.delete(String gridId) = _Delete; + const factory GridEvent.createRow() = _CreateRow; +} + +@freezed +abstract class GridState with _$GridState { + const factory GridState({ + required GridLoadingState loadingState, + required Option> gridInfo, + }) = _GridState; + + factory GridState.initial() => GridState( + loadingState: const _Loading(), + gridInfo: none(), + ); +} + +@freezed +class GridLoadingState with _$GridLoadingState { + const factory GridLoadingState.loading() = _Loading; + const factory GridLoadingState.finish(Either successOrFail) = _Finish; +} + +typedef FieldById = Map; +typedef RowById = Map; +typedef CellById = Map; + +class GridInfo { + List rowOrders; + List fieldOrders; + RowById rowMap; + FieldById fieldMap; + + GridInfo({ + required this.rowOrders, + required this.fieldOrders, + required this.fieldMap, + required this.rowMap, + }); + + RowInfo rowInfoAtIndex(int index) { + final rowOrder = rowOrders[index]; + final Row row = rowMap[rowOrder.rowId]!; + final cellMap = row.cellByFieldId; + + final displayCellMap = {}; + + return RowInfo( + fieldOrders: fieldOrders, + fieldMap: fieldMap, + displayCellMap: displayCellMap, + ); + } + + int numberOfRows() { + return rowOrders.length; + } +} + +class RowInfo { + List fieldOrders; + FieldById fieldMap; + CellById displayCellMap; + RowInfo({ + required this.fieldOrders, + required this.fieldMap, + required this.displayCellMap, + }); +} diff --git a/frontend/app_flowy/lib/workspace/application/grid/grid_service.dart b/frontend/app_flowy/lib/workspace/application/grid/grid_service.dart new file mode 100644 index 0000000000..b69e4a8e5a --- /dev/null +++ b/frontend/app_flowy/lib/workspace/application/grid/grid_service.dart @@ -0,0 +1,20 @@ +import 'package:flowy_sdk/dispatch/dispatch.dart'; +import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart'; +import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart'; +import 'package:dartz/dartz.dart'; + +class GridService { + Future> createGrid({required String name}) { + final payload = CreateGridPayload()..name = name; + return GridEventCreateGrid(payload).send(); + } + + Future> openGrid({required String gridId}) { + final payload = GridId(value: gridId); + return GridEventOpenGrid(payload).send(); + } + + Future> createRow({required String gridId}) { + throw UnimplementedError(); + } +} diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/controller/flowy_table_selection.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/controller/flowy_table_selection.dart new file mode 100755 index 0000000000..7f01b7dcf0 --- /dev/null +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/controller/flowy_table_selection.dart @@ -0,0 +1,73 @@ +/// The data structor representing each selection of flowy table. +enum FlowyTableSelectionType { + item, + row, + col, +} + +class FlowyTableSelectionItem { + final FlowyTableSelectionType type; + final int? row; + final int? column; + + const FlowyTableSelectionItem({ + required this.type, + this.row, + this.column, + }); + + @override + String toString() { + return '$type($row, $column)'; + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + return other is FlowyTableSelectionItem && + type == other.type && + row == other.row && + column == other.column; + } + + @override + int get hashCode => type.hashCode ^ row.hashCode ^ column.hashCode; +} + +class FlowyTableSelection { + Set _items = {}; + + Set get items => _items; + + FlowyTableSelection( + this._items, + ); + + FlowyTableSelection.combine( + FlowyTableSelection lhs, FlowyTableSelection rhs) { + this..combine(lhs)..combine(rhs); + } + + FlowyTableSelection operator +(FlowyTableSelection other) { + return this..combine(other); + } + + void combine(FlowyTableSelection other) { + var totalItems = items..union(other.items); + final rows = totalItems + .where((ele) => ele.type == FlowyTableSelectionType.row) + .map((e) => e.row) + .toSet(); + final cols = totalItems + .where((ele) => ele.type == FlowyTableSelectionType.col) + .map((e) => e.column) + .toSet(); + totalItems.removeWhere((ele) { + return ele.type == FlowyTableSelectionType.item && + (rows.contains(ele.row) || cols.contains(ele.column)); + }); + _items = totalItems; + } +} diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/controller/grid_scroll.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/controller/grid_scroll.dart new file mode 100755 index 0000000000..213b16cfae --- /dev/null +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/controller/grid_scroll.dart @@ -0,0 +1,22 @@ +import 'package:flutter/material.dart'; + +class GridScrollController { + final ScrollController _verticalController = ScrollController(); + final ScrollController _horizontalController = ScrollController(); + + ScrollController get verticalController => _verticalController; + ScrollController get horizontalController => _horizontalController; + + GridScrollController(); + + // final SelectionChangeCallback? onSelectionChanged; + + // final ShouldApplySelection? shouldApplySelection; + + // final ScrollCallback? onScroll; + + void dispose() { + verticalController.dispose(); + horizontalController.dispose(); + } +} diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/grid_layout.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/grid_layout.dart new file mode 100755 index 0000000000..df2b73cb12 --- /dev/null +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/grid_layout.dart @@ -0,0 +1,17 @@ +import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart'; + +import 'grid_sizes.dart'; + +class GridLayout { + static double headerWidth(List fieldOrders) { + if (fieldOrders.isEmpty) return 0; + + final fieldsWidth = fieldOrders + .map( + (fieldOrder) => fieldOrder.width.toDouble(), + ) + .reduce((value, element) => value + element); + + return fieldsWidth + GridSize.firstHeaderPadding; + } +} diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/grid_page.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/grid_page.dart new file mode 100755 index 0000000000..1eb102f7bb --- /dev/null +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/grid_page.dart @@ -0,0 +1,150 @@ +import 'package:app_flowy/startup/startup.dart'; +import 'package:app_flowy/workspace/application/grid/grid_bloc.dart'; +import 'package:flowy_infra_ui/style_widget/scrolling/styled_list.dart'; +import 'package:flowy_infra_ui/style_widget/scrolling/styled_scroll_bar.dart'; +import 'package:flowy_infra_ui/style_widget/scrolling/styled_scrollview.dart'; +import 'package:flowy_infra_ui/widget/error_page.dart'; +import 'package:flowy_sdk/protobuf/flowy-folder-data-model/view.pb.dart'; +import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter/material.dart'; +import 'package:app_flowy/workspace/presentation/plugins/grid/controller/grid_scroll.dart'; +import 'package:styled_widget/styled_widget.dart'; + +import 'grid_layout.dart'; +import 'grid_sizes.dart'; +import 'widgets/grid_content/grid_row.dart'; +import 'widgets/grid_footer/grid_footer.dart'; +import 'widgets/grid_header/header.dart'; + +class GridPage extends StatefulWidget { + final View view; + + GridPage({Key? key, required this.view}) : super(key: ValueKey(view.id)); + + @override + State createState() => _GridPageState(); +} + +class _GridPageState extends State { + @override + Widget build(BuildContext context) { + return MultiBlocProvider( + providers: [ + BlocProvider(create: (context) => getIt()), + ], + child: BlocBuilder( + builder: (context, state) { + return state.loadingState.map( + loading: (_) => const Center(child: CircularProgressIndicator.adaptive()), + finish: (result) => result.successOrFail.fold( + (_) => const GridBody(), + (err) => FlowyErrorPage(err.toString()), + ), + ); + }, + ), + ); + } + + @override + void dispose() { + super.dispose(); + } + + @override + void deactivate() { + super.deactivate(); + } + + @override + void didUpdateWidget(covariant GridPage oldWidget) { + super.didUpdateWidget(oldWidget); + } +} + +class GridBody extends StatefulWidget { + const GridBody({Key? key}) : super(key: key); + + @override + _GridBodyState createState() => _GridBodyState(); +} + +class _GridBodyState extends State { + final _scrollController = GridScrollController(); + + @override + void dispose() { + _scrollController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return BlocBuilder( + builder: (context, state) { + return state.gridInfo.fold( + () => const Center(child: CircularProgressIndicator.adaptive()), + (some) => some.fold( + (gridInfo) => _renderGrid(context, gridInfo), + (err) => FlowyErrorPage(err.toString()), + ), + ); + }, + ); + } + + Widget _renderGrid(BuildContext context, GridInfo gridInfo) { + return Stack( + children: [ + StyledSingleChildScrollView( + controller: _scrollController.horizontalController, + axis: Axis.horizontal, + child: SizedBox( + width: GridLayout.headerWidth(gridInfo.fieldOrders), + child: CustomScrollView( + physics: StyledScrollPhysics(), + controller: _scrollController.verticalController, + slivers: [ + _buildHeader(gridInfo.fieldOrders, gridInfo.fieldMap), + _buildRows(gridInfo), + _builderFooter(context), + ], + ), + ), + ), + ScrollbarListStack( + axis: Axis.vertical, + controller: _scrollController.verticalController, + barSize: GridSize.scrollBarSize, + child: Container(), + ).padding(right: 0, top: GridSize.headerHeight, bottom: GridSize.scrollBarSize), + ], + ); + } + + Widget _buildHeader(List fieldOrders, FieldById fieldById) { + return SliverPersistentHeader( + delegate: GridHeaderDelegate(fieldOrders, fieldById), + floating: true, + pinned: true, + ); + } + + Widget _buildRows(GridInfo gridInfo) { + return SliverList( + delegate: SliverChildBuilderDelegate((context, index) { + final rowInfo = gridInfo.rowInfoAtIndex(index); + return RepaintBoundary(child: GridRow(rowInfo)); + }, childCount: gridInfo.numberOfRows()), + ); + } + + Widget _builderFooter(BuildContext context) { + return GridFooter( + onAddRow: () { + context.read().add(const GridEvent.createRow()); + }, + ); + } +} diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/grid_sizes.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/grid_sizes.dart new file mode 100755 index 0000000000..e833df5cc4 --- /dev/null +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/grid_sizes.dart @@ -0,0 +1,16 @@ +class GridInsets { + static double scale = 1; + + static double get horizontal => 6 * scale; + static double get vertical => 6 * scale; +} + +class GridSize { + static double scale = 1; + + static double get scrollBarSize => 12 * scale; + static double get headerHeight => 50 * scale; + static double get rowHeight => 50 * scale; + static double get footerHeight => 40 * scale; + static double get firstHeaderPadding => 20 * scale; +} diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/widgets/grid_content/cell_builder.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/widgets/grid_content/cell_builder.dart new file mode 100755 index 0000000000..a4cab931c9 --- /dev/null +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/widgets/grid_content/cell_builder.dart @@ -0,0 +1,17 @@ +import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart'; +import 'grid_cell.dart'; + +class GridCellBuilder { + static GridCell buildCell(Field? field, DisplayCell? cell) { + if (field == null || cell == null) { + return BlankCell(); + } + + switch (field.fieldType) { + case FieldType.RichText: + return GridTextCell(cell.content); + default: + return BlankCell(); + } + } +} diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/widgets/grid_content/cell_container.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/widgets/grid_content/cell_container.dart new file mode 100755 index 0000000000..c769a60d34 --- /dev/null +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/widgets/grid_content/cell_container.dart @@ -0,0 +1,34 @@ +import 'package:app_flowy/workspace/presentation/plugins/grid/grid_sizes.dart'; +import 'package:flowy_infra_ui/widget/mouse_hover_builder.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'cell_decoration.dart'; +import 'grid_cell.dart'; + +class CellContainer extends StatelessWidget { + final GridCell child; + final double width; + const CellContainer({Key? key, required this.child, required this.width}) : super(key: key); + + @override + Widget build(BuildContext context) { + return GestureDetector( + behavior: HitTestBehavior.translucent, + onTap: () { + // context + // .read() + // .add(HomeEvent.setEditPannel(CellEditPannelContext())); + }, + child: MouseHoverBuilder( + builder: (_, isHovered) => Container( + width: width, + decoration: CellDecoration.box( + color: isHovered ? Colors.red.withOpacity(.1) : Colors.transparent, + ), + padding: EdgeInsets.symmetric(vertical: GridInsets.vertical, horizontal: GridInsets.horizontal), + child: child, + ), + ), + ); + } +} diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/widgets/grid_content/cell_decoration.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/widgets/grid_content/cell_decoration.dart new file mode 100755 index 0000000000..0e74073e14 --- /dev/null +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/widgets/grid_content/cell_decoration.dart @@ -0,0 +1,10 @@ +import 'package:flutter/material.dart'; + +class CellDecoration { + static BoxDecoration box({required Color color}) { + return BoxDecoration( + border: Border.all(color: Colors.black26, width: 0.2), + color: color, + ); + } +} diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/widgets/grid_content/grid_cell.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/widgets/grid_content/grid_cell.dart new file mode 100755 index 0000000000..a23259c8c3 --- /dev/null +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/widgets/grid_content/grid_cell.dart @@ -0,0 +1,68 @@ +import 'package:flutter/material.dart'; +// ignore: import_of_legacy_library_into_null_safe + +/// The interface of base cell. +abstract class GridCell extends StatelessWidget { + final canSelect = true; + + const GridCell({Key? key}) : super(key: key); +} + +class GridTextCell extends GridCell { + final String content; + const GridTextCell(this.content, {Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Text(content); + } +} + +class DateCell extends GridCell { + final String content; + const DateCell(this.content, {Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Text(content); + } +} + +class NumberCell extends GridCell { + final String content; + const NumberCell(this.content, {Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Text(content); + } +} + +class SingleSelectCell extends GridCell { + final String content; + const SingleSelectCell(this.content, {Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Text(content); + } +} + +class MultiSelectCell extends GridCell { + final String content; + const MultiSelectCell(this.content, {Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Text(content); + } +} + +class BlankCell extends GridCell { + const BlankCell({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Container(); + } +} diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/widgets/grid_content/grid_row.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/widgets/grid_content/grid_row.dart new file mode 100755 index 0000000000..8a1202197e --- /dev/null +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/widgets/grid_content/grid_row.dart @@ -0,0 +1,64 @@ +import 'package:app_flowy/workspace/application/grid/grid_bloc.dart'; +import 'package:app_flowy/workspace/presentation/plugins/grid/grid_sizes.dart'; +import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart' hide Row; +import 'package:flutter/material.dart'; +import 'cell_builder.dart'; +import 'cell_container.dart'; +import 'grid_row_leading.dart'; + +class GridRowContext { + final RepeatedFieldOrder fieldOrders; + final Map fieldById; + final Map cellByFieldId; + GridRowContext(this.fieldOrders, this.fieldById, this.cellByFieldId); +} + +class GridRow extends StatelessWidget { + final RowInfo rowInfo; + final Function(bool)? onHoverChange; + const GridRow(this.rowInfo, {Key? key, this.onHoverChange}) : super(key: key); + + @override + Widget build(BuildContext context) { + return SizedBox( + height: GridSize.rowHeight, + child: _buildRowBody(), + ); + } + + Widget _buildRowBody() { + Widget rowWidget = Row( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: _buildCells(), + ); + + if (onHoverChange != null) { + rowWidget = MouseRegion( + onEnter: (event) => onHoverChange!(true), + onExit: (event) => onHoverChange!(false), + cursor: MouseCursor.uncontrolled, + child: rowWidget, + ); + } + + return rowWidget; + } + + List _buildCells() { + var cells = List.empty(growable: true); + cells.add(const RowLeading()); + + rowInfo.fieldOrders.where((element) => element.visibility).forEach((fieldOrder) { + final field = rowInfo.fieldMap[fieldOrder.fieldId]; + final data = rowInfo.displayCellMap[fieldOrder.fieldId]; + + final cell = CellContainer( + width: fieldOrder.width.toDouble(), + child: GridCellBuilder.buildCell(field, data), + ); + + cells.add(cell); + }); + return cells; + } +} diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/widgets/grid_error_page.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/widgets/grid_error_page.dart new file mode 100755 index 0000000000..92973a05b3 --- /dev/null +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/widgets/grid_error_page.dart @@ -0,0 +1,14 @@ +import 'package:flutter/material.dart'; + +class GridUnknownError extends StatelessWidget { + const GridUnknownError({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Container( + child: Center( + child: CircularProgressIndicator(), + ), + ); + } +} diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/widgets/grid_footer/grid_footer.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/widgets/grid_footer/grid_footer.dart new file mode 100755 index 0000000000..b5ccee4892 --- /dev/null +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/widgets/grid_footer/grid_footer.dart @@ -0,0 +1,46 @@ +import 'package:app_flowy/workspace/presentation/plugins/grid/grid_sizes.dart'; +import 'package:app_flowy/workspace/presentation/plugins/grid/widgets/grid_content/cell_decoration.dart'; +import 'package:flowy_infra_ui/widget/mouse_hover_builder.dart'; +import 'package:flutter/material.dart'; + +class GridFooter extends StatelessWidget { + final VoidCallback? onAddRow; + const GridFooter({Key? key, required this.onAddRow}) : super(key: key); + + @override + Widget build(BuildContext context) { + return SliverToBoxAdapter( + child: SizedBox( + height: GridSize.footerHeight, + child: Row( + children: [ + AddRowButton(onTap: onAddRow), + ], + ), + ), + ); + } +} + +class AddRowButton extends StatelessWidget { + final VoidCallback? onTap; + const AddRowButton({Key? key, required this.onTap}) : super(key: key); + + @override + Widget build(BuildContext context) { + return GestureDetector( + behavior: HitTestBehavior.translucent, + onTap: onTap, + child: MouseHoverBuilder( + builder: (_, isHovered) => Container( + width: GridSize.firstHeaderPadding, + height: GridSize.footerHeight, + decoration: CellDecoration.box( + color: isHovered ? Colors.red.withOpacity(.1) : Colors.white, + ), + child: const Icon(Icons.add, size: 16), + ), + ), + ); + } +} diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/widgets/grid_header/constants.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/widgets/grid_header/constants.dart new file mode 100755 index 0000000000..0a02de5076 --- /dev/null +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/widgets/grid_header/constants.dart @@ -0,0 +1,5 @@ +import 'package:flutter/material.dart'; + +class GridHeaderConstants { + static Color get backgroundColor => Colors.grey; +} diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/widgets/grid_header/header.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/widgets/grid_header/header.dart new file mode 100644 index 0000000000..e69de29bb2 diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/widgets/grid_header/header_cell.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/widgets/grid_header/header_cell.dart new file mode 100755 index 0000000000..eda546416d --- /dev/null +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/widgets/grid_header/header_cell.dart @@ -0,0 +1,52 @@ +import 'package:app_flowy/workspace/presentation/plugins/grid/grid_sizes.dart'; +import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart' hide Row; +import 'package:flutter/material.dart'; +import 'constants.dart'; + +class HeaderCell extends StatelessWidget { + final Field field; + const HeaderCell(this.field, {Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Text( + field.name, + style: const TextStyle(fontSize: 15.0, color: Colors.black), + ); + } +} + +class HeaderCellContainer extends StatelessWidget { + final HeaderCell child; + final double width; + const HeaderCellContainer({Key? key, required this.child, required this.width}) : super(key: key); + + @override + Widget build(BuildContext context) { + return GestureDetector( + behavior: HitTestBehavior.translucent, + onTap: () {}, + child: Container( + width: width, + decoration: BoxDecoration( + border: Border.all(color: Colors.black26, width: 0.5), + color: GridHeaderConstants.backgroundColor, + ), + padding: EdgeInsets.symmetric(vertical: GridInsets.vertical, horizontal: GridInsets.horizontal), + child: child, + ), + ); + } +} + +class HeaderCellLeading extends StatelessWidget { + const HeaderCellLeading({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Container( + width: GridSize.firstHeaderPadding, + color: GridHeaderConstants.backgroundColor, + ); + } +} diff --git a/frontend/app_flowy/packages/flowy_sdk/lib/dispatch/dispatch.dart b/frontend/app_flowy/packages/flowy_sdk/lib/dispatch/dispatch.dart index 8c65b29b9b..b58175af0a 100644 --- a/frontend/app_flowy/packages/flowy_sdk/lib/dispatch/dispatch.dart +++ b/frontend/app_flowy/packages/flowy_sdk/lib/dispatch/dispatch.dart @@ -9,6 +9,7 @@ import 'package:flowy_sdk/protobuf/flowy-net/event.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-net/network_state.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-user/event_map.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-folder/event_map.pb.dart'; +import 'package:flowy_sdk/protobuf/flowy-grid/event_map.pb.dart'; import 'package:isolates/isolates.dart'; import 'package:isolates/ports.dart'; import 'package:ffi/ffi.dart'; @@ -21,6 +22,7 @@ import 'package:flowy_sdk/protobuf/flowy-user-data-model/protobuf.dart'; import 'package:flowy_sdk/protobuf/dart-ffi/protobuf.dart'; import 'package:flowy_sdk/protobuf/flowy-folder-data-model/protobuf.dart'; import 'package:flowy_sdk/protobuf/flowy-collaboration/protobuf.dart'; +import 'package:flowy_sdk/protobuf/flowy-grid-data-model/protobuf.dart'; // ignore: unused_import import 'package:protobuf/protobuf.dart'; @@ -30,6 +32,7 @@ import 'error.dart'; part 'dart_event/flowy-folder/dart_event.dart'; part 'dart_event/flowy-net/dart_event.dart'; part 'dart_event/flowy-user/dart_event.dart'; +part 'dart_event/flowy-grid/dart_event.dart'; enum FFIException { RequestIsEmpty, diff --git a/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/grid.pb.dart b/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/grid.pb.dart index 3fce9f98db..64e47d37d1 100644 --- a/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/grid.pb.dart +++ b/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/grid.pb.dart @@ -725,7 +725,6 @@ class Cell extends $pb.GeneratedMessage { ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'id') ..aOS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'rowId') ..aOS(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'fieldId') - ..aOM(4, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'data', subBuilder: AnyData.create) ..hasRequiredFields = false ; @@ -734,7 +733,6 @@ class Cell extends $pb.GeneratedMessage { $core.String? id, $core.String? rowId, $core.String? fieldId, - AnyData? data, }) { final _result = create(); if (id != null) { @@ -746,9 +744,6 @@ class Cell extends $pb.GeneratedMessage { if (fieldId != null) { _result.fieldId = fieldId; } - if (data != null) { - _result.data = data; - } return _result; } factory Cell.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); @@ -798,17 +793,130 @@ class Cell extends $pb.GeneratedMessage { $core.bool hasFieldId() => $_has(2); @$pb.TagNumber(3) void clearFieldId() => clearField(3); +} - @$pb.TagNumber(4) - AnyData get data => $_getN(3); - @$pb.TagNumber(4) - set data(AnyData v) { setField(4, v); } - @$pb.TagNumber(4) - $core.bool hasData() => $_has(3); - @$pb.TagNumber(4) - void clearData() => clearField(4); - @$pb.TagNumber(4) - AnyData ensureData() => $_ensure(3); +class DisplayCell extends $pb.GeneratedMessage { + static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'DisplayCell', createEmptyInstance: create) + ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'id') + ..aOS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'content') + ..hasRequiredFields = false + ; + + DisplayCell._() : super(); + factory DisplayCell({ + $core.String? id, + $core.String? content, + }) { + final _result = create(); + if (id != null) { + _result.id = id; + } + if (content != null) { + _result.content = content; + } + return _result; + } + factory DisplayCell.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory DisplayCell.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') + DisplayCell clone() => DisplayCell()..mergeFromMessage(this); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + DisplayCell copyWith(void Function(DisplayCell) updates) => super.copyWith((message) => updates(message as DisplayCell)) as DisplayCell; // ignore: deprecated_member_use + $pb.BuilderInfo get info_ => _i; + @$core.pragma('dart2js:noInline') + static DisplayCell create() => DisplayCell._(); + DisplayCell createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static DisplayCell getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static DisplayCell? _defaultInstance; + + @$pb.TagNumber(1) + $core.String get id => $_getSZ(0); + @$pb.TagNumber(1) + set id($core.String v) { $_setString(0, v); } + @$pb.TagNumber(1) + $core.bool hasId() => $_has(0); + @$pb.TagNumber(1) + void clearId() => clearField(1); + + @$pb.TagNumber(2) + $core.String get content => $_getSZ(1); + @$pb.TagNumber(2) + set content($core.String v) { $_setString(1, v); } + @$pb.TagNumber(2) + $core.bool hasContent() => $_has(1); + @$pb.TagNumber(2) + void clearContent() => clearField(2); +} + +class RawCell extends $pb.GeneratedMessage { + static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'RawCell', createEmptyInstance: create) + ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'id') + ..aOM(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'data', subBuilder: AnyData.create) + ..hasRequiredFields = false + ; + + RawCell._() : super(); + factory RawCell({ + $core.String? id, + AnyData? data, + }) { + final _result = create(); + if (id != null) { + _result.id = id; + } + if (data != null) { + _result.data = data; + } + return _result; + } + factory RawCell.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory RawCell.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') + RawCell clone() => RawCell()..mergeFromMessage(this); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + RawCell copyWith(void Function(RawCell) updates) => super.copyWith((message) => updates(message as RawCell)) as RawCell; // ignore: deprecated_member_use + $pb.BuilderInfo get info_ => _i; + @$core.pragma('dart2js:noInline') + static RawCell create() => RawCell._(); + RawCell createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static RawCell getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static RawCell? _defaultInstance; + + @$pb.TagNumber(1) + $core.String get id => $_getSZ(0); + @$pb.TagNumber(1) + set id($core.String v) { $_setString(0, v); } + @$pb.TagNumber(1) + $core.bool hasId() => $_has(0); + @$pb.TagNumber(1) + void clearId() => clearField(1); + + @$pb.TagNumber(2) + AnyData get data => $_getN(1); + @$pb.TagNumber(2) + set data(AnyData v) { setField(2, v); } + @$pb.TagNumber(2) + $core.bool hasData() => $_has(1); + @$pb.TagNumber(2) + void clearData() => clearField(2); + @$pb.TagNumber(2) + AnyData ensureData() => $_ensure(1); } class CreateGridPayload extends $pb.GeneratedMessage { diff --git a/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/grid.pbjson.dart b/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/grid.pbjson.dart index dfe0be5c29..93e221828a 100644 --- a/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/grid.pbjson.dart +++ b/frontend/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-grid-data-model/grid.pbjson.dart @@ -159,12 +159,33 @@ const Cell$json = const { const {'1': 'id', '3': 1, '4': 1, '5': 9, '10': 'id'}, const {'1': 'row_id', '3': 2, '4': 1, '5': 9, '10': 'rowId'}, const {'1': 'field_id', '3': 3, '4': 1, '5': 9, '10': 'fieldId'}, - const {'1': 'data', '3': 4, '4': 1, '5': 11, '6': '.AnyData', '10': 'data'}, ], }; /// Descriptor for `Cell`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List cellDescriptor = $convert.base64Decode('CgRDZWxsEg4KAmlkGAEgASgJUgJpZBIVCgZyb3dfaWQYAiABKAlSBXJvd0lkEhkKCGZpZWxkX2lkGAMgASgJUgdmaWVsZElkEhwKBGRhdGEYBCABKAsyCC5BbnlEYXRhUgRkYXRh'); +final $typed_data.Uint8List cellDescriptor = $convert.base64Decode('CgRDZWxsEg4KAmlkGAEgASgJUgJpZBIVCgZyb3dfaWQYAiABKAlSBXJvd0lkEhkKCGZpZWxkX2lkGAMgASgJUgdmaWVsZElk'); +@$core.Deprecated('Use displayCellDescriptor instead') +const DisplayCell$json = const { + '1': 'DisplayCell', + '2': const [ + const {'1': 'id', '3': 1, '4': 1, '5': 9, '10': 'id'}, + const {'1': 'content', '3': 2, '4': 1, '5': 9, '10': 'content'}, + ], +}; + +/// Descriptor for `DisplayCell`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List displayCellDescriptor = $convert.base64Decode('CgtEaXNwbGF5Q2VsbBIOCgJpZBgBIAEoCVICaWQSGAoHY29udGVudBgCIAEoCVIHY29udGVudA=='); +@$core.Deprecated('Use rawCellDescriptor instead') +const RawCell$json = const { + '1': 'RawCell', + '2': const [ + const {'1': 'id', '3': 1, '4': 1, '5': 9, '10': 'id'}, + const {'1': 'data', '3': 2, '4': 1, '5': 11, '6': '.AnyData', '10': 'data'}, + ], +}; + +/// Descriptor for `RawCell`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List rawCellDescriptor = $convert.base64Decode('CgdSYXdDZWxsEg4KAmlkGAEgASgJUgJpZBIcCgRkYXRhGAIgASgLMgguQW55RGF0YVIEZGF0YQ=='); @$core.Deprecated('Use createGridPayloadDescriptor instead') const CreateGridPayload$json = const { '1': 'CreateGridPayload', diff --git a/shared-lib/flowy-grid-data-model/src/entities/grid.rs b/shared-lib/flowy-grid-data-model/src/entities/grid.rs index 092fcbd3db..a0c5d919d3 100644 --- a/shared-lib/flowy-grid-data-model/src/entities/grid.rs +++ b/shared-lib/flowy-grid-data-model/src/entities/grid.rs @@ -142,8 +142,23 @@ pub struct Cell { #[pb(index = 3)] pub field_id: String, +} - #[pb(index = 4)] +#[derive(Debug, Default, ProtoBuf)] +pub struct DisplayCell { + #[pb(index = 1)] + pub id: String, + + #[pb(index = 2)] + pub content: String, +} + +#[derive(Debug, Default, ProtoBuf)] +pub struct RawCell { + #[pb(index = 1)] + pub id: String, + + #[pb(index = 2)] pub data: AnyData, } diff --git a/shared-lib/flowy-grid-data-model/src/protobuf/model/grid.rs b/shared-lib/flowy-grid-data-model/src/protobuf/model/grid.rs index 127bdfbeb8..52bbd4f113 100644 --- a/shared-lib/flowy-grid-data-model/src/protobuf/model/grid.rs +++ b/shared-lib/flowy-grid-data-model/src/protobuf/model/grid.rs @@ -2405,7 +2405,6 @@ pub struct Cell { pub id: ::std::string::String, pub row_id: ::std::string::String, pub field_id: ::std::string::String, - pub data: ::protobuf::SingularPtrField, // special fields pub unknown_fields: ::protobuf::UnknownFields, pub cached_size: ::protobuf::CachedSize, @@ -2499,48 +2498,10 @@ impl Cell { pub fn take_field_id(&mut self) -> ::std::string::String { ::std::mem::replace(&mut self.field_id, ::std::string::String::new()) } - - // .AnyData data = 4; - - - pub fn get_data(&self) -> &AnyData { - self.data.as_ref().unwrap_or_else(|| ::default_instance()) - } - pub fn clear_data(&mut self) { - self.data.clear(); - } - - pub fn has_data(&self) -> bool { - self.data.is_some() - } - - // Param is passed by value, moved - pub fn set_data(&mut self, v: AnyData) { - self.data = ::protobuf::SingularPtrField::some(v); - } - - // Mutable pointer to the field. - // If field is not initialized, it is initialized with default value first. - pub fn mut_data(&mut self) -> &mut AnyData { - if self.data.is_none() { - self.data.set_default(); - } - self.data.as_mut().unwrap() - } - - // Take field - pub fn take_data(&mut self) -> AnyData { - self.data.take().unwrap_or_else(|| AnyData::new()) - } } impl ::protobuf::Message for Cell { fn is_initialized(&self) -> bool { - for v in &self.data { - if !v.is_initialized() { - return false; - } - }; true } @@ -2557,9 +2518,6 @@ impl ::protobuf::Message for Cell { 3 => { ::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.field_id)?; }, - 4 => { - ::protobuf::rt::read_singular_message_into(wire_type, is, &mut self.data)?; - }, _ => { ::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?; }, @@ -2581,10 +2539,6 @@ impl ::protobuf::Message for Cell { if !self.field_id.is_empty() { my_size += ::protobuf::rt::string_size(3, &self.field_id); } - if let Some(ref v) = self.data.as_ref() { - let len = v.compute_size(); - my_size += 1 + ::protobuf::rt::compute_raw_varint32_size(len) + len; - } my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields()); self.cached_size.set(my_size); my_size @@ -2600,11 +2554,6 @@ impl ::protobuf::Message for Cell { if !self.field_id.is_empty() { os.write_string(3, &self.field_id)?; } - if let Some(ref v) = self.data.as_ref() { - os.write_tag(4, ::protobuf::wire_format::WireTypeLengthDelimited)?; - os.write_raw_varint32(v.get_cached_size())?; - v.write_to_with_cached_sizes(os)?; - } os.write_unknown_fields(self.get_unknown_fields())?; ::std::result::Result::Ok(()) } @@ -2658,11 +2607,6 @@ impl ::protobuf::Message for Cell { |m: &Cell| { &m.field_id }, |m: &mut Cell| { &mut m.field_id }, )); - fields.push(::protobuf::reflect::accessor::make_singular_ptr_field_accessor::<_, ::protobuf::types::ProtobufTypeMessage>( - "data", - |m: &Cell| { &m.data }, - |m: &mut Cell| { &mut m.data }, - )); ::protobuf::reflect::MessageDescriptor::new_pb_name::( "Cell", fields, @@ -2682,7 +2626,6 @@ impl ::protobuf::Clear for Cell { self.id.clear(); self.row_id.clear(); self.field_id.clear(); - self.data.clear(); self.unknown_fields.clear(); } } @@ -2699,6 +2642,423 @@ impl ::protobuf::reflect::ProtobufValue for Cell { } } +#[derive(PartialEq,Clone,Default)] +pub struct DisplayCell { + // message fields + pub id: ::std::string::String, + pub content: ::std::string::String, + // special fields + pub unknown_fields: ::protobuf::UnknownFields, + pub cached_size: ::protobuf::CachedSize, +} + +impl<'a> ::std::default::Default for &'a DisplayCell { + fn default() -> &'a DisplayCell { + ::default_instance() + } +} + +impl DisplayCell { + pub fn new() -> DisplayCell { + ::std::default::Default::default() + } + + // string id = 1; + + + pub fn get_id(&self) -> &str { + &self.id + } + pub fn clear_id(&mut self) { + self.id.clear(); + } + + // Param is passed by value, moved + pub fn set_id(&mut self, v: ::std::string::String) { + self.id = v; + } + + // Mutable pointer to the field. + // If field is not initialized, it is initialized with default value first. + pub fn mut_id(&mut self) -> &mut ::std::string::String { + &mut self.id + } + + // Take field + pub fn take_id(&mut self) -> ::std::string::String { + ::std::mem::replace(&mut self.id, ::std::string::String::new()) + } + + // string content = 2; + + + pub fn get_content(&self) -> &str { + &self.content + } + pub fn clear_content(&mut self) { + self.content.clear(); + } + + // Param is passed by value, moved + pub fn set_content(&mut self, v: ::std::string::String) { + self.content = v; + } + + // Mutable pointer to the field. + // If field is not initialized, it is initialized with default value first. + pub fn mut_content(&mut self) -> &mut ::std::string::String { + &mut self.content + } + + // Take field + pub fn take_content(&mut self) -> ::std::string::String { + ::std::mem::replace(&mut self.content, ::std::string::String::new()) + } +} + +impl ::protobuf::Message for DisplayCell { + fn is_initialized(&self) -> bool { + true + } + + fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::ProtobufResult<()> { + while !is.eof()? { + let (field_number, wire_type) = is.read_tag_unpack()?; + match field_number { + 1 => { + ::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.id)?; + }, + 2 => { + ::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.content)?; + }, + _ => { + ::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?; + }, + }; + } + ::std::result::Result::Ok(()) + } + + // Compute sizes of nested messages + #[allow(unused_variables)] + fn compute_size(&self) -> u32 { + let mut my_size = 0; + if !self.id.is_empty() { + my_size += ::protobuf::rt::string_size(1, &self.id); + } + if !self.content.is_empty() { + my_size += ::protobuf::rt::string_size(2, &self.content); + } + my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields()); + self.cached_size.set(my_size); + my_size + } + + fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::ProtobufResult<()> { + if !self.id.is_empty() { + os.write_string(1, &self.id)?; + } + if !self.content.is_empty() { + os.write_string(2, &self.content)?; + } + 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) -> ::std::boxed::Box { + self + } + + fn descriptor(&self) -> &'static ::protobuf::reflect::MessageDescriptor { + Self::descriptor_static() + } + + fn new() -> DisplayCell { + DisplayCell::new() + } + + fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor { + static descriptor: ::protobuf::rt::LazyV2<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::LazyV2::INIT; + descriptor.get(|| { + let mut fields = ::std::vec::Vec::new(); + fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>( + "id", + |m: &DisplayCell| { &m.id }, + |m: &mut DisplayCell| { &mut m.id }, + )); + fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>( + "content", + |m: &DisplayCell| { &m.content }, + |m: &mut DisplayCell| { &mut m.content }, + )); + ::protobuf::reflect::MessageDescriptor::new_pb_name::( + "DisplayCell", + fields, + file_descriptor_proto() + ) + }) + } + + fn default_instance() -> &'static DisplayCell { + static instance: ::protobuf::rt::LazyV2 = ::protobuf::rt::LazyV2::INIT; + instance.get(DisplayCell::new) + } +} + +impl ::protobuf::Clear for DisplayCell { + fn clear(&mut self) { + self.id.clear(); + self.content.clear(); + self.unknown_fields.clear(); + } +} + +impl ::std::fmt::Debug for DisplayCell { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + ::protobuf::text_format::fmt(self, f) + } +} + +impl ::protobuf::reflect::ProtobufValue for DisplayCell { + fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef { + ::protobuf::reflect::ReflectValueRef::Message(self) + } +} + +#[derive(PartialEq,Clone,Default)] +pub struct RawCell { + // message fields + pub id: ::std::string::String, + pub data: ::protobuf::SingularPtrField, + // special fields + pub unknown_fields: ::protobuf::UnknownFields, + pub cached_size: ::protobuf::CachedSize, +} + +impl<'a> ::std::default::Default for &'a RawCell { + fn default() -> &'a RawCell { + ::default_instance() + } +} + +impl RawCell { + pub fn new() -> RawCell { + ::std::default::Default::default() + } + + // string id = 1; + + + pub fn get_id(&self) -> &str { + &self.id + } + pub fn clear_id(&mut self) { + self.id.clear(); + } + + // Param is passed by value, moved + pub fn set_id(&mut self, v: ::std::string::String) { + self.id = v; + } + + // Mutable pointer to the field. + // If field is not initialized, it is initialized with default value first. + pub fn mut_id(&mut self) -> &mut ::std::string::String { + &mut self.id + } + + // Take field + pub fn take_id(&mut self) -> ::std::string::String { + ::std::mem::replace(&mut self.id, ::std::string::String::new()) + } + + // .AnyData data = 2; + + + pub fn get_data(&self) -> &AnyData { + self.data.as_ref().unwrap_or_else(|| ::default_instance()) + } + pub fn clear_data(&mut self) { + self.data.clear(); + } + + pub fn has_data(&self) -> bool { + self.data.is_some() + } + + // Param is passed by value, moved + pub fn set_data(&mut self, v: AnyData) { + self.data = ::protobuf::SingularPtrField::some(v); + } + + // Mutable pointer to the field. + // If field is not initialized, it is initialized with default value first. + pub fn mut_data(&mut self) -> &mut AnyData { + if self.data.is_none() { + self.data.set_default(); + } + self.data.as_mut().unwrap() + } + + // Take field + pub fn take_data(&mut self) -> AnyData { + self.data.take().unwrap_or_else(|| AnyData::new()) + } +} + +impl ::protobuf::Message for RawCell { + fn is_initialized(&self) -> bool { + for v in &self.data { + if !v.is_initialized() { + return false; + } + }; + true + } + + fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::ProtobufResult<()> { + while !is.eof()? { + let (field_number, wire_type) = is.read_tag_unpack()?; + match field_number { + 1 => { + ::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.id)?; + }, + 2 => { + ::protobuf::rt::read_singular_message_into(wire_type, is, &mut self.data)?; + }, + _ => { + ::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?; + }, + }; + } + ::std::result::Result::Ok(()) + } + + // Compute sizes of nested messages + #[allow(unused_variables)] + fn compute_size(&self) -> u32 { + let mut my_size = 0; + if !self.id.is_empty() { + my_size += ::protobuf::rt::string_size(1, &self.id); + } + if let Some(ref v) = self.data.as_ref() { + let len = v.compute_size(); + my_size += 1 + ::protobuf::rt::compute_raw_varint32_size(len) + len; + } + my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields()); + self.cached_size.set(my_size); + my_size + } + + fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::ProtobufResult<()> { + if !self.id.is_empty() { + os.write_string(1, &self.id)?; + } + if let Some(ref v) = self.data.as_ref() { + os.write_tag(2, ::protobuf::wire_format::WireTypeLengthDelimited)?; + os.write_raw_varint32(v.get_cached_size())?; + v.write_to_with_cached_sizes(os)?; + } + 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) -> ::std::boxed::Box { + self + } + + fn descriptor(&self) -> &'static ::protobuf::reflect::MessageDescriptor { + Self::descriptor_static() + } + + fn new() -> RawCell { + RawCell::new() + } + + fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor { + static descriptor: ::protobuf::rt::LazyV2<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::LazyV2::INIT; + descriptor.get(|| { + let mut fields = ::std::vec::Vec::new(); + fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>( + "id", + |m: &RawCell| { &m.id }, + |m: &mut RawCell| { &mut m.id }, + )); + fields.push(::protobuf::reflect::accessor::make_singular_ptr_field_accessor::<_, ::protobuf::types::ProtobufTypeMessage>( + "data", + |m: &RawCell| { &m.data }, + |m: &mut RawCell| { &mut m.data }, + )); + ::protobuf::reflect::MessageDescriptor::new_pb_name::( + "RawCell", + fields, + file_descriptor_proto() + ) + }) + } + + fn default_instance() -> &'static RawCell { + static instance: ::protobuf::rt::LazyV2 = ::protobuf::rt::LazyV2::INIT; + instance.get(RawCell::new) + } +} + +impl ::protobuf::Clear for RawCell { + fn clear(&mut self) { + self.id.clear(); + self.data.clear(); + self.unknown_fields.clear(); + } +} + +impl ::std::fmt::Debug for RawCell { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + ::protobuf::text_format::fmt(self, f) + } +} + +impl ::protobuf::reflect::ProtobufValue for RawCell { + fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef { + ::protobuf::reflect::ReflectValueRef::Message(self) + } +} + #[derive(PartialEq,Clone,Default)] pub struct CreateGridPayload { // message fields @@ -3107,15 +3467,17 @@ static file_descriptor_proto_data: &'static [u8] = b"\ difiedTime\x12@\n\x10cell_by_field_id\x18\x04\x20\x03(\x0b2\x17.Row.Cell\ ByFieldIdEntryR\rcellByFieldId\x1aG\n\x12CellByFieldIdEntry\x12\x10\n\ \x03key\x18\x01\x20\x01(\tR\x03key\x12\x1b\n\x05value\x18\x02\x20\x01(\ - \x0b2\x05.CellR\x05value:\x028\x01\"f\n\x04Cell\x12\x0e\n\x02id\x18\x01\ + \x0b2\x05.CellR\x05value:\x028\x01\"H\n\x04Cell\x12\x0e\n\x02id\x18\x01\ \x20\x01(\tR\x02id\x12\x15\n\x06row_id\x18\x02\x20\x01(\tR\x05rowId\x12\ - \x19\n\x08field_id\x18\x03\x20\x01(\tR\x07fieldId\x12\x1c\n\x04data\x18\ - \x04\x20\x01(\x0b2\x08.AnyDataR\x04data\"'\n\x11CreateGridPayload\x12\ - \x12\n\x04name\x18\x01\x20\x01(\tR\x04name\"\x1e\n\x06GridId\x12\x14\n\ - \x05value\x18\x01\x20\x01(\tR\x05value*d\n\tFieldType\x12\x0c\n\x08RichT\ - ext\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\x06proto3\ + \x19\n\x08field_id\x18\x03\x20\x01(\tR\x07fieldId\"7\n\x0bDisplayCell\ + \x12\x0e\n\x02id\x18\x01\x20\x01(\tR\x02id\x12\x18\n\x07content\x18\x02\ + \x20\x01(\tR\x07content\"7\n\x07RawCell\x12\x0e\n\x02id\x18\x01\x20\x01(\ + \tR\x02id\x12\x1c\n\x04data\x18\x02\x20\x01(\x0b2\x08.AnyDataR\x04data\"\ + '\n\x11CreateGridPayload\x12\x12\n\x04name\x18\x01\x20\x01(\tR\x04name\"\ + \x1e\n\x06GridId\x12\x14\n\x05value\x18\x01\x20\x01(\tR\x05value*d\n\tFi\ + eldType\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\x0bMult\ + iSelect\x10\x04\x12\x0c\n\x08Checkbox\x10\x05b\x06proto3\ "; static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT; diff --git a/shared-lib/flowy-grid-data-model/src/protobuf/proto/grid.proto b/shared-lib/flowy-grid-data-model/src/protobuf/proto/grid.proto index 012f53aaab..d1233a3e28 100644 --- a/shared-lib/flowy-grid-data-model/src/protobuf/proto/grid.proto +++ b/shared-lib/flowy-grid-data-model/src/protobuf/proto/grid.proto @@ -52,7 +52,14 @@ message Cell { string id = 1; string row_id = 2; string field_id = 3; - AnyData data = 4; +} +message DisplayCell { + string id = 1; + string content = 2; +} +message RawCell { + string id = 1; + AnyData data = 2; } message CreateGridPayload { string name = 1;