ソースを参照

feat: add cloumn bloc

appflowy 3 年 前
コミット
00db755c29

+ 8 - 0
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<ColumnBloc, List<Field>, void>(
+      (data, _) => ColumnBloc(
+        data: GridColumnData(fields: data),
+        service: ColumnService(),
+      ),
+    );
+
     // trash
     getIt.registerLazySingleton<TrashService>(() => TrashService());
     getIt.registerLazySingleton<TrashListener>(() => TrashListener());

+ 43 - 0
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<ColumnEvent, ColumnState> {
+  final ColumnService service;
+  final GridColumnData data;
+
+  ColumnBloc({required this.data, required this.service}) : super(ColumnState.initial(data.fields)) {
+    on<ColumnEvent>(
+      (event, emit) async {
+        await event.map(
+          initial: (_InitialColumn value) async {},
+          createColumn: (_CreateColumn value) {},
+        );
+      },
+    );
+  }
+
+  @override
+  Future<void> 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<Field> fields}) = _ColumnState;
+
+  factory ColumnState.initial(List<Field> fields) => ColumnState(fields: fields);
+}

+ 1 - 0
frontend/app_flowy/lib/workspace/application/grid/column_service.dart

@@ -0,0 +1 @@
+class ColumnService {}

+ 6 - 0
frontend/app_flowy/lib/workspace/application/grid/data.dart

@@ -33,3 +33,9 @@ class GridRowData {
     required this.cellMap,
   });
 }
+
+class GridColumnData {
+  final List<Field> fields;
+
+  GridColumnData({required this.fields});
+}

+ 2 - 0
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';

+ 1 - 9
frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/grid_page.dart

@@ -111,7 +111,7 @@ class _GridBodyState extends State<GridBody> {
               slivers: <Widget>[
                 _buildHeader(gridInfo.fields),
                 _buildRows(gridInfo),
-                _builderFooter(context),
+                const GridFooter(),
               ],
             ),
           ),
@@ -145,12 +145,4 @@ class _GridBodyState extends State<GridBody> {
       ),
     );
   }
-
-  Widget _builderFooter(BuildContext context) {
-    return GridFooter(
-      onAddRow: () {
-        context.read<GridBloc>().add(const GridEvent.createRow());
-      },
-    );
-  }
 }

+ 1 - 1
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;
   }
 }

+ 22 - 8
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,
+      );
 }

+ 1 - 1
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)),
       ),
     );

+ 0 - 61
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<RowBloc, RowState>(
-      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<AppTheme>();
-    return Tooltip(
-      message: '',
-      child: FlowyIconButton(
-        hoverColor: theme.hover,
-        width: 22,
-        onPressed: () => context.read<RowBloc>().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();
-  }
-}

+ 72 - 2
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<Widget> _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<RowBloc, RowState>(
+      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<AppTheme>();
+    final borderSide = BorderSide(color: theme.shader4, width: 0.4);
+
+    return BlocBuilder<RowBloc, RowState>(
+      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<AppTheme>();
+    return FlowyIconButton(
+      hoverColor: theme.hover,
+      width: 22,
+      onPressed: () => context.read<RowBloc>().add(const RowEvent.createRow()),
+      iconPadding: const EdgeInsets.all(3),
+      icon: svg("home/add"),
+    );
+  }
+}

+ 22 - 24
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<AppTheme>();
+    return FlowyButton(
+      text: const FlowyText.medium('New row', fontSize: 12),
+      hoverColor: theme.hover,
+      onTap: () => context.read<RowBloc>().add(const RowEvent.createRow()),
+      icon: svg("home/add"),
     );
   }
 }

+ 74 - 18
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<Field> fields;
-
   const GridHeader({required this.fields, Key? key}) : super(key: key);
 
   @override
   Widget build(BuildContext context) {
-    final headers = List<Widget>.empty(growable: true);
-    fields.asMap().forEach((index, field) {
-      final header = HeaderCellContainer(
-        width: field.width.toDouble(),
-        child: HeaderCell(field),
-      );
-
-      //
-      headers.add(header);
-    });
-
-    return Row(
-      crossAxisAlignment: CrossAxisAlignment.stretch,
-      children: [
-        const HeaderCellLeading(),
-        ...headers,
-      ],
+    return BlocProvider(
+      create: (context) => getIt<ColumnBloc>(param1: fields),
+      child: BlocBuilder<ColumnBloc, ColumnState>(
+        builder: (context, state) {
+          final headers = state.fields
+              .map(
+                (field) => HeaderCellContainer(
+                  width: field.width.toDouble(),
+                  child: HeaderCell(field),
+                ),
+              )
+              .toList();
+
+          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<AppTheme>();
+    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<AppTheme>();
+    return FlowyButton(
+      text: const FlowyText.medium('New column', fontSize: 12),
+      hoverColor: theme.hover,
+      onTap: () => context.read<ColumnBloc>().add(const ColumnEvent.createColumn()),
+      icon: svg("home/add"),
     );
   }
 }

+ 2 - 14
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<AppTheme>();
     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,
-    );
-  }
-}

+ 1 - 1
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(),
       ),
     );