From 00db755c29355aca7e3d40cf80ebd26c4e40394d Mon Sep 17 00:00:00 2001 From: appflowy Date: Wed, 9 Mar 2022 09:31:06 +0800 Subject: [PATCH] feat: add cloumn bloc --- .../lib/startup/home_deps_resolver.dart | 8 ++ .../application/grid/column_bloc.dart | 43 +++++++++ .../application/grid/column_service.dart | 1 + .../lib/workspace/application/grid/data.dart | 6 ++ .../workspace/application/grid/prelude.dart | 2 + .../plugins/grid/src/grid_page.dart | 10 +-- .../plugins/grid/src/layout/layout.dart | 2 +- .../plugins/grid/src/layout/sizes.dart | 30 +++++-- .../src/widgets/content/cell_container.dart | 2 +- .../grid/src/widgets/content/grid_cell.dart | 61 ------------- .../grid/src/widgets/content/grid_row.dart | 74 ++++++++++++++- .../grid/src/widgets/footer/grid_footer.dart | 46 +++++----- .../grid/src/widgets/header/header.dart | 90 +++++++++++++++---- .../grid/src/widgets/header/header_cell.dart | 16 +--- .../lib/style_widget/button.dart | 2 +- 15 files changed, 255 insertions(+), 138 deletions(-) create mode 100644 frontend/app_flowy/lib/workspace/application/grid/column_bloc.dart create mode 100644 frontend/app_flowy/lib/workspace/application/grid/column_service.dart diff --git a/frontend/app_flowy/lib/startup/home_deps_resolver.dart b/frontend/app_flowy/lib/startup/home_deps_resolver.dart index 9a2a47301e..54275b95b3 100644 --- a/frontend/app_flowy/lib/startup/home_deps_resolver.dart +++ b/frontend/app_flowy/lib/startup/home_deps_resolver.dart @@ -12,6 +12,7 @@ import 'package:app_flowy/workspace/application/menu/prelude.dart'; import 'package:app_flowy/workspace/presentation/home/home_stack.dart'; import 'package:flowy_sdk/protobuf/flowy-folder-data-model/app.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-folder-data-model/view.pb.dart'; +import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-user-data-model/user_profile.pb.dart'; import 'package:get_it/get_it.dart'; @@ -102,6 +103,13 @@ class HomeDepsResolver { ), ); + getIt.registerFactoryParam, void>( + (data, _) => ColumnBloc( + data: GridColumnData(fields: data), + service: ColumnService(), + ), + ); + // trash getIt.registerLazySingleton(() => TrashService()); getIt.registerLazySingleton(() => TrashListener()); diff --git a/frontend/app_flowy/lib/workspace/application/grid/column_bloc.dart b/frontend/app_flowy/lib/workspace/application/grid/column_bloc.dart new file mode 100644 index 0000000000..844360fa9b --- /dev/null +++ b/frontend/app_flowy/lib/workspace/application/grid/column_bloc.dart @@ -0,0 +1,43 @@ +import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:freezed_annotation/freezed_annotation.dart'; +import 'package:dartz/dartz.dart'; +import 'dart:async'; +import 'column_service.dart'; +import 'data.dart'; + +part 'column_bloc.freezed.dart'; + +class ColumnBloc extends Bloc { + final ColumnService service; + final GridColumnData data; + + ColumnBloc({required this.data, required this.service}) : super(ColumnState.initial(data.fields)) { + on( + (event, emit) async { + await event.map( + initial: (_InitialColumn value) async {}, + createColumn: (_CreateColumn value) {}, + ); + }, + ); + } + + @override + Future close() async { + return super.close(); + } +} + +@freezed +abstract class ColumnEvent with _$ColumnEvent { + const factory ColumnEvent.initial() = _InitialColumn; + const factory ColumnEvent.createColumn() = _CreateColumn; +} + +@freezed +abstract class ColumnState with _$ColumnState { + const factory ColumnState({required List fields}) = _ColumnState; + + factory ColumnState.initial(List fields) => ColumnState(fields: fields); +} diff --git a/frontend/app_flowy/lib/workspace/application/grid/column_service.dart b/frontend/app_flowy/lib/workspace/application/grid/column_service.dart new file mode 100644 index 0000000000..c074dcf616 --- /dev/null +++ b/frontend/app_flowy/lib/workspace/application/grid/column_service.dart @@ -0,0 +1 @@ +class ColumnService {} diff --git a/frontend/app_flowy/lib/workspace/application/grid/data.dart b/frontend/app_flowy/lib/workspace/application/grid/data.dart index 6c815b7f35..b0e260fe8e 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/data.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/data.dart @@ -33,3 +33,9 @@ class GridRowData { required this.cellMap, }); } + +class GridColumnData { + final List fields; + + GridColumnData({required this.fields}); +} diff --git a/frontend/app_flowy/lib/workspace/application/grid/prelude.dart b/frontend/app_flowy/lib/workspace/application/grid/prelude.dart index bf48b7cc8b..3cbe922a58 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/prelude.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/prelude.dart @@ -3,3 +3,5 @@ export 'row_bloc.dart'; export 'row_service.dart'; export 'grid_service.dart'; export 'data.dart'; +export 'column_service.dart'; +export 'column_bloc.dart'; 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 ae3934b7cb..ba89c63662 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 @@ -111,7 +111,7 @@ class _GridBodyState extends State { slivers: [ _buildHeader(gridInfo.fields), _buildRows(gridInfo), - _builderFooter(context), + const GridFooter(), ], ), ), @@ -145,12 +145,4 @@ class _GridBodyState extends State { ), ); } - - Widget _builderFooter(BuildContext context) { - return GridFooter( - onAddRow: () { - context.read().add(const GridEvent.createRow()); - }, - ); - } } diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/layout/layout.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/layout/layout.dart index 5b58d3c1c5..3f076fc89f 100755 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/layout/layout.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/layout/layout.dart @@ -8,6 +8,6 @@ class GridLayout { final fieldsWidth = fields.map((field) => field.width.toDouble()).reduce((value, element) => value + element); - return fieldsWidth + GridSize.startHeaderPadding; + return fieldsWidth + GridSize.leadingHeaderPadding + GridSize.trailHeaderPadding; } } 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 e577311200..e669a78217 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 @@ -1,15 +1,29 @@ -class GridInsets { - static double scale = 1; - - static double get horizontal => 8 * scale; - static double get vertical => 8 * scale; -} +import 'package:flutter/widgets.dart'; class GridSize { static double scale = 1; static double get scrollBarSize => 12 * scale; - static double get headerHeight => 50 * scale; + static double get headerHeight => 40 * scale; static double get footerHeight => 40 * scale; - static double get startHeaderPadding => 30 * scale; + static double get leadingHeaderPadding => 30 * scale; + static double get trailHeaderPadding => 140 * scale; + static double get headerContentPadding => 8 * scale; + static double get cellContentPadding => 8 * scale; + // + static EdgeInsets get headerContentInsets => EdgeInsets.symmetric( + horizontal: GridSize.headerContentPadding, + vertical: GridSize.headerContentPadding, + ); + static EdgeInsets get cellContentInsets => EdgeInsets.symmetric( + horizontal: GridSize.cellContentPadding, + vertical: GridSize.cellContentPadding, + ); + + static EdgeInsets get footerContentInsets => EdgeInsets.fromLTRB( + 0, + GridSize.headerContentPadding, + GridSize.headerContentPadding, + GridSize.headerContentPadding, + ); } diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/content/cell_container.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/content/cell_container.dart index 5b771b8c92..0fbdc3b53c 100755 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/content/cell_container.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/content/cell_container.dart @@ -27,7 +27,7 @@ class CellContainer extends StatelessWidget { decoration: BoxDecoration( border: Border(right: borderSide, bottom: borderSide), ), - padding: EdgeInsets.symmetric(vertical: GridInsets.vertical, horizontal: GridInsets.horizontal), + padding: GridSize.cellContentInsets, child: Center(child: IntrinsicHeight(child: child)), ), ); diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/content/grid_cell.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/content/grid_cell.dart index 5646376bf7..be04555bd4 100755 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/content/grid_cell.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/content/grid_cell.dart @@ -1,15 +1,4 @@ -import 'package:app_flowy/workspace/application/grid/row_bloc.dart'; -import 'package:app_flowy/workspace/presentation/home/menu/app/header/add_button.dart'; -import 'package:app_flowy/workspace/presentation/plugins/grid/src/layout/sizes.dart'; -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/mouse_hover_builder.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; - -import 'cell_decoration.dart'; -// ignore: import_of_legacy_library_into_null_safe /// The interface of base cell. abstract class GridCellWidget extends StatelessWidget { @@ -89,53 +78,3 @@ class BlankCell extends GridCellWidget { return Container(); } } - -class RowLeading extends StatelessWidget { - final String rowId; - const RowLeading({required this.rowId, Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return BlocBuilder( - builder: (context, state) { - if (state.isHighlight) { - return Row( - children: const [ - CreateRowButton(), - DrawRowButton(), - ], - ); - } - - return const SizedBox.expand(); - }, - ); - } -} - -class CreateRowButton extends StatelessWidget { - const CreateRowButton({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - final theme = context.watch(); - return Tooltip( - message: '', - child: FlowyIconButton( - hoverColor: theme.hover, - width: 22, - onPressed: () => context.read().add(const RowEvent.createRow()), - icon: svg("home/add"), - ), - ); - } -} - -class DrawRowButton extends StatelessWidget { - const DrawRowButton({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Container(); - } -} diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/content/grid_row.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/content/grid_row.dart index 3ef343493a..3e43d4fd8d 100755 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/content/grid_row.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/content/grid_row.dart @@ -1,7 +1,9 @@ import 'package:app_flowy/startup/startup.dart'; import 'package:app_flowy/workspace/application/grid/prelude.dart'; import 'package:app_flowy/workspace/presentation/plugins/grid/src/layout/sizes.dart'; -import 'package:flowy_infra_ui/widget/mouse_hover_builder.dart'; +import 'package:flowy_infra/image.dart'; +import 'package:flowy_infra/theme.dart'; +import 'package:flowy_infra_ui/style_widget/icon_button.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'cell_builder.dart'; @@ -42,7 +44,10 @@ class GridRowWidget extends StatelessWidget { List _buildCells() { return [ - SizedBox(width: GridSize.startHeaderPadding, child: RowLeading(rowId: data.row.id)), + SizedBox( + width: GridSize.leadingHeaderPadding, + child: LeadingRow(rowId: data.row.id), + ), ...data.fields.map( (field) { final cellData = data.cellMap[field.id]; @@ -51,7 +56,72 @@ class GridRowWidget extends StatelessWidget { child: GridCellBuilder.buildCell(field, cellData), ); }, + ), + SizedBox( + width: GridSize.trailHeaderPadding, + child: TrailingRow(rowId: data.row.id), ) ].toList(); } } + +class LeadingRow extends StatelessWidget { + final String rowId; + const LeadingRow({required this.rowId, Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return BlocBuilder( + builder: (context, state) { + if (state.isHighlight) { + return Row( + mainAxisAlignment: MainAxisAlignment.center, + children: const [ + CreateRowButton(), + ], + ); + } + return const SizedBox.expand(); + }, + ); + } +} + +class TrailingRow extends StatelessWidget { + final String rowId; + const TrailingRow({required this.rowId, Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + final theme = context.watch(); + final borderSide = BorderSide(color: theme.shader4, width: 0.4); + + return BlocBuilder( + builder: (context, state) { + return Container( + width: GridSize.trailHeaderPadding, + decoration: BoxDecoration( + border: Border(bottom: borderSide), + ), + padding: GridSize.cellContentInsets, + ); + }, + ); + } +} + +class CreateRowButton extends StatelessWidget { + const CreateRowButton({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + final theme = context.watch(); + return FlowyIconButton( + hoverColor: theme.hover, + width: 22, + onPressed: () => context.read().add(const RowEvent.createRow()), + iconPadding: const EdgeInsets.all(3), + icon: svg("home/add"), + ); + } +} diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/footer/grid_footer.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/footer/grid_footer.dart index 6e1befe18e..5ca3bf0f6b 100755 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/footer/grid_footer.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/footer/grid_footer.dart @@ -1,22 +1,28 @@ +import 'package:app_flowy/workspace/application/grid/row_bloc.dart'; import 'package:app_flowy/workspace/presentation/plugins/grid/src/layout/sizes.dart'; -import 'package:flowy_infra_ui/widget/mouse_hover_builder.dart'; +import 'package:flowy_infra/image.dart'; +import 'package:flowy_infra/theme.dart'; +import 'package:flowy_infra_ui/style_widget/button.dart'; +import 'package:flowy_infra_ui/style_widget/text.dart'; import 'package:flutter/material.dart'; - -import '../content/cell_decoration.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; class GridFooter extends StatelessWidget { - final VoidCallback? onAddRow; - const GridFooter({Key? key, required this.onAddRow}) : super(key: key); + const GridFooter({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return SliverToBoxAdapter( child: SizedBox( height: GridSize.footerHeight, - child: Row( - children: [ - AddRowButton(onTap: onAddRow), - ], + child: Padding( + padding: GridSize.headerContentInsets, + child: Row( + children: [ + SizedBox(width: GridSize.leadingHeaderPadding), + const SizedBox(width: 120, child: AddRowButton()), + ], + ), ), ), ); @@ -24,24 +30,16 @@ class GridFooter extends StatelessWidget { } class AddRowButton extends StatelessWidget { - final VoidCallback? onTap; - const AddRowButton({Key? key, required this.onTap}) : super(key: key); + const AddRowButton({Key? key}) : super(key: key); @override Widget build(BuildContext context) { - return GestureDetector( - behavior: HitTestBehavior.translucent, - onTap: onTap, - child: MouseHoverBuilder( - builder: (_, isHovered) => Container( - width: GridSize.startHeaderPadding, - height: GridSize.footerHeight, - decoration: CellDecoration.box( - color: isHovered ? Colors.red.withOpacity(.1) : Colors.white, - ), - child: const Icon(Icons.add, size: 16), - ), - ), + final theme = context.watch(); + return FlowyButton( + text: const FlowyText.medium('New row', fontSize: 12), + hoverColor: theme.hover, + onTap: () => context.read().add(const RowEvent.createRow()), + icon: svg("home/add"), ); } } diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/header.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/header.dart index c70d3ce3b2..d3cf4a5255 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/header.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/header.dart @@ -1,6 +1,13 @@ +import 'package:app_flowy/startup/startup.dart'; +import 'package:app_flowy/workspace/application/grid/column_bloc.dart'; import 'package:app_flowy/workspace/presentation/plugins/grid/src/layout/sizes.dart'; +import 'package:flowy_infra/image.dart'; +import 'package:flowy_infra/theme.dart'; +import 'package:flowy_infra_ui/style_widget/button.dart'; +import 'package:flowy_infra_ui/style_widget/text.dart'; import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart' hide Row; import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'header_cell.dart'; @@ -31,28 +38,77 @@ class GridHeaderDelegate extends SliverPersistentHeaderDelegate { class GridHeader extends StatelessWidget { final List fields; - const GridHeader({required this.fields, Key? key}) : super(key: key); @override Widget build(BuildContext context) { - final headers = List.empty(growable: true); - fields.asMap().forEach((index, field) { - final header = HeaderCellContainer( - width: field.width.toDouble(), - child: HeaderCell(field), - ); + return BlocProvider( + create: (context) => getIt(param1: fields), + child: BlocBuilder( + builder: (context, state) { + final headers = state.fields + .map( + (field) => HeaderCellContainer( + width: field.width.toDouble(), + child: HeaderCell(field), + ), + ) + .toList(); - // - headers.add(header); - }); - - return Row( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - const HeaderCellLeading(), - ...headers, - ], + return Row( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + const LeadingHeaderCell(), + ...headers, + const TrailingHeaderCell(), + ], + ); + }, + ), + ); + } +} + +class LeadingHeaderCell extends StatelessWidget { + const LeadingHeaderCell({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return SizedBox( + width: GridSize.leadingHeaderPadding, + ); + } +} + +class TrailingHeaderCell extends StatelessWidget { + const TrailingHeaderCell({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + final theme = context.watch(); + final borderSide = BorderSide(color: theme.shader4, width: 0.4); + return Container( + width: GridSize.trailHeaderPadding, + decoration: BoxDecoration( + border: Border(top: borderSide, bottom: borderSide), + ), + padding: GridSize.headerContentInsets, + child: const CreateColumnButton(), + ); + } +} + +class CreateColumnButton extends StatelessWidget { + const CreateColumnButton({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + final theme = context.watch(); + return FlowyButton( + text: const FlowyText.medium('New column', fontSize: 12), + hoverColor: theme.hover, + onTap: () => context.read().add(const ColumnEvent.createColumn()), + icon: svg("home/add"), ); } } diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/header_cell.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/header_cell.dart index e059208aff..9b77af4b3e 100755 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/header_cell.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/header_cell.dart @@ -5,7 +5,6 @@ import 'package:flowy_infra_ui/style_widget/text.dart'; import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'constants.dart'; class HeaderCell extends StatelessWidget { final Field field; @@ -15,7 +14,7 @@ class HeaderCell extends StatelessWidget { Widget build(BuildContext context) { final theme = context.watch(); return FlowyButton( - text: FlowyText.medium(field.name), + text: FlowyText.medium(field.name, fontSize: 12), hoverColor: theme.hover, onTap: () {}, ); @@ -40,19 +39,8 @@ class HeaderCellContainer extends StatelessWidget { decoration: BoxDecoration( border: Border(top: borderSide, right: borderSide, bottom: borderSide), ), - padding: EdgeInsets.symmetric(vertical: GridInsets.vertical, horizontal: GridInsets.horizontal), + padding: GridSize.headerContentInsets, child: child, ); } } - -class HeaderCellLeading extends StatelessWidget { - const HeaderCellLeading({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return SizedBox( - width: GridSize.startHeaderPadding, - ); - } -} diff --git a/frontend/app_flowy/packages/flowy_infra_ui/lib/style_widget/button.dart b/frontend/app_flowy/packages/flowy_infra_ui/lib/style_widget/button.dart index 6b67081ca2..b7d8ea4e88 100644 --- a/frontend/app_flowy/packages/flowy_infra_ui/lib/style_widget/button.dart +++ b/frontend/app_flowy/packages/flowy_infra_ui/lib/style_widget/button.dart @@ -24,7 +24,7 @@ class FlowyButton extends StatelessWidget { return InkWell( onTap: onTap, child: FlowyHover( - config: HoverDisplayConfig(borderRadius: Corners.s6Border, hoverColor: hoverColor), + config: HoverDisplayConfig(borderRadius: Corners.s5Border, hoverColor: hoverColor), builder: (context, onHover) => _render(), ), );