Browse Source

chore: add edit card button

appflowy 2 years ago
parent
commit
a7349f43aa

+ 6 - 0
frontend/app_flowy/lib/plugins/board/application/card/board_text_cell_bloc.dart

@@ -27,6 +27,9 @@ class BoardTextCellBloc extends Bloc<BoardTextCellEvent, BoardTextCellState> {
               emit(state.copyWith(content: text));
               emit(state.copyWith(content: text));
             }
             }
           },
           },
+          enableEdit: (bool enabled) {
+            emit(state.copyWith(enableEdit: enabled));
+          },
         );
         );
       },
       },
     );
     );
@@ -57,6 +60,7 @@ class BoardTextCellBloc extends Bloc<BoardTextCellEvent, BoardTextCellState> {
 class BoardTextCellEvent with _$BoardTextCellEvent {
 class BoardTextCellEvent with _$BoardTextCellEvent {
   const factory BoardTextCellEvent.initial() = _InitialCell;
   const factory BoardTextCellEvent.initial() = _InitialCell;
   const factory BoardTextCellEvent.updateText(String text) = _UpdateContent;
   const factory BoardTextCellEvent.updateText(String text) = _UpdateContent;
+  const factory BoardTextCellEvent.enableEdit(bool enabled) = _EnableEdit;
   const factory BoardTextCellEvent.didReceiveCellUpdate(String cellContent) =
   const factory BoardTextCellEvent.didReceiveCellUpdate(String cellContent) =
       _DidReceiveCellUpdate;
       _DidReceiveCellUpdate;
 }
 }
@@ -65,10 +69,12 @@ class BoardTextCellEvent with _$BoardTextCellEvent {
 class BoardTextCellState with _$BoardTextCellState {
 class BoardTextCellState with _$BoardTextCellState {
   const factory BoardTextCellState({
   const factory BoardTextCellState({
     required String content,
     required String content,
+    required bool enableEdit,
   }) = _BoardTextCellState;
   }) = _BoardTextCellState;
 
 
   factory BoardTextCellState.initial(GridCellController context) =>
   factory BoardTextCellState.initial(GridCellController context) =>
       BoardTextCellState(
       BoardTextCellState(
         content: context.getCellData() ?? "",
         content: context.getCellData() ?? "",
+        enableEdit: false,
       );
       );
 }
 }

+ 4 - 1
frontend/app_flowy/lib/plugins/board/application/card/card_bloc.dart

@@ -107,7 +107,10 @@ class BoardCardState with _$BoardCardState {
 
 
   factory BoardCardState.initial(
   factory BoardCardState.initial(
           RowPB rowPB, UnmodifiableListView<BoardCellEquatable> cells) =>
           RowPB rowPB, UnmodifiableListView<BoardCellEquatable> cells) =>
-      BoardCardState(rowPB: rowPB, cells: cells);
+      BoardCardState(
+        rowPB: rowPB,
+        cells: cells,
+      );
 }
 }
 
 
 class BoardCellEquatable extends Equatable {
 class BoardCellEquatable extends Equatable {

+ 51 - 0
frontend/app_flowy/lib/plugins/board/presentation/card/board_cell.dart

@@ -1,3 +1,54 @@
+import 'package:app_flowy/plugins/grid/application/prelude.dart';
+import 'package:flowy_infra/notifier.dart';
+
 abstract class FocusableBoardCell {
 abstract class FocusableBoardCell {
   set becomeFocus(bool isFocus);
   set becomeFocus(bool isFocus);
 }
 }
+
+class EditableCellNotifier {
+  final Notifier becomeFirstResponder = Notifier();
+
+  final Notifier resignFirstResponder = Notifier();
+
+  EditableCellNotifier();
+}
+
+class EditableRowNotifier {
+  Map<EditableCellId, EditableCellNotifier> cells = {};
+
+  void insertCell(
+    GridCellIdentifier cellIdentifier,
+    EditableCellNotifier notifier,
+  ) {
+    cells[EditableCellId.from(cellIdentifier)] = notifier;
+  }
+
+  void becomeFirstResponder() {
+    for (final notifier in cells.values) {
+      notifier.becomeFirstResponder.notify();
+    }
+  }
+
+  void resignFirstResponder() {
+    for (final notifier in cells.values) {
+      notifier.resignFirstResponder.notify();
+    }
+  }
+}
+
+abstract class EditableCell {
+  EditableCellNotifier? get editableNotifier;
+}
+
+class EditableCellId {
+  String fieldId;
+  String rowId;
+
+  EditableCellId(this.rowId, this.fieldId);
+
+  factory EditableCellId.from(GridCellIdentifier cellIdentifier) =>
+      EditableCellId(
+        cellIdentifier.rowId,
+        cellIdentifier.fieldId,
+      );
+}

+ 6 - 1
frontend/app_flowy/lib/plugins/board/presentation/card/board_select_option_cell.dart

@@ -5,13 +5,18 @@ import 'package:app_flowy/plugins/grid/presentation/widgets/cell/select_option_c
 import 'package:flutter/material.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 
 
-class BoardSelectOptionCell extends StatefulWidget {
+import 'board_cell.dart';
+
+class BoardSelectOptionCell extends StatefulWidget with EditableCell {
   final String groupId;
   final String groupId;
   final GridCellControllerBuilder cellControllerBuilder;
   final GridCellControllerBuilder cellControllerBuilder;
+  @override
+  final EditableCellNotifier? editableNotifier;
 
 
   const BoardSelectOptionCell({
   const BoardSelectOptionCell({
     required this.groupId,
     required this.groupId,
     required this.cellControllerBuilder,
     required this.cellControllerBuilder,
+    this.editableNotifier,
     Key? key,
     Key? key,
   }) : super(key: key);
   }) : super(key: key);
 
 

+ 38 - 14
frontend/app_flowy/lib/plugins/board/presentation/card/board_text_cell.dart

@@ -4,15 +4,19 @@ import 'package:app_flowy/plugins/grid/presentation/widgets/cell/cell_builder.da
 import 'package:flutter/material.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 
 
-class BoardTextCell extends StatefulWidget {
+import 'board_cell.dart';
+
+class BoardTextCell extends StatefulWidget with EditableCell {
   final String groupId;
   final String groupId;
   final bool isFocus;
   final bool isFocus;
-
+  @override
+  final EditableCellNotifier? editableNotifier;
   final GridCellControllerBuilder cellControllerBuilder;
   final GridCellControllerBuilder cellControllerBuilder;
 
 
   const BoardTextCell({
   const BoardTextCell({
     required this.groupId,
     required this.groupId,
     required this.cellControllerBuilder,
     required this.cellControllerBuilder,
+    this.editableNotifier,
     this.isFocus = false,
     this.isFocus = false,
     Key? key,
     Key? key,
   }) : super(key: key);
   }) : super(key: key);
@@ -37,6 +41,18 @@ class _BoardTextCellState extends State<BoardTextCell> {
     if (widget.isFocus) {
     if (widget.isFocus) {
       focusNode.requestFocus();
       focusNode.requestFocus();
     }
     }
+
+    widget.editableNotifier?.becomeFirstResponder.addListener(() {
+      if (!mounted) return;
+      focusNode.requestFocus();
+      _cellBloc.add(const BoardTextCellEvent.enableEdit(true));
+    });
+
+    widget.editableNotifier?.resignFirstResponder.addListener(() {
+      if (!mounted) return;
+      _cellBloc.add(const BoardTextCellEvent.enableEdit(false));
+    });
+
     super.initState();
     super.initState();
   }
   }
 
 
@@ -50,18 +66,26 @@ class _BoardTextCellState extends State<BoardTextCell> {
             _controller.text = state.content;
             _controller.text = state.content;
           }
           }
         },
         },
-        child: TextField(
-          controller: _controller,
-          focusNode: focusNode,
-          onChanged: (value) => focusChanged(),
-          onEditingComplete: () => focusNode.unfocus(),
-          maxLines: 1,
-          style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w500),
-          decoration: const InputDecoration(
-            contentPadding: EdgeInsets.symmetric(vertical: 6),
-            border: InputBorder.none,
-            isDense: true,
-          ),
+        child: BlocBuilder<BoardTextCellBloc, BoardTextCellState>(
+          buildWhen: (previous, current) =>
+              previous.enableEdit != current.enableEdit,
+          builder: (context, state) {
+            return TextField(
+              // autofocus: true,
+              // enabled: state.enableEdit,
+              controller: _controller,
+              focusNode: focusNode,
+              onChanged: (value) => focusChanged(),
+              onEditingComplete: () => focusNode.unfocus(),
+              maxLines: 1,
+              style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w500),
+              decoration: const InputDecoration(
+                contentPadding: EdgeInsets.symmetric(vertical: 6),
+                border: InputBorder.none,
+                isDense: true,
+              ),
+            );
+          },
         ),
         ),
       ),
       ),
     );
     );

+ 41 - 2
frontend/app_flowy/lib/plugins/board/presentation/card/card.dart

@@ -7,6 +7,7 @@ import 'package:flowy_infra/theme.dart';
 import 'package:flowy_infra_ui/flowy_infra_ui_web.dart';
 import 'package:flowy_infra_ui/flowy_infra_ui_web.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
+import 'board_cell.dart';
 import 'card_cell_builder.dart';
 import 'card_cell_builder.dart';
 import 'card_container.dart';
 import 'card_container.dart';
 
 
@@ -36,9 +37,11 @@ class BoardCard extends StatefulWidget {
 
 
 class _BoardCardState extends State<BoardCard> {
 class _BoardCardState extends State<BoardCard> {
   late BoardCardBloc _cardBloc;
   late BoardCardBloc _cardBloc;
+  late EditableRowNotifier rowNotifier;
 
 
   @override
   @override
   void initState() {
   void initState() {
+    rowNotifier = EditableRowNotifier();
     _cardBloc = BoardCardBloc(
     _cardBloc = BoardCardBloc(
       gridId: widget.gridId,
       gridId: widget.gridId,
       fieldId: widget.fieldId,
       fieldId: widget.fieldId,
@@ -58,7 +61,12 @@ class _BoardCardState extends State<BoardCard> {
         builder: (context, state) {
         builder: (context, state) {
           return BoardCardContainer(
           return BoardCardContainer(
             accessoryBuilder: (context) {
             accessoryBuilder: (context) {
-              return [const _CardMoreOption()];
+              return [
+                _CardEditOption(
+                  startEditing: () => rowNotifier.becomeFirstResponder(),
+                ),
+                const _CardMoreOption(),
+              ];
             },
             },
             onTap: (context) {
             onTap: (context) {
               widget.openCard(context);
               widget.openCard(context);
@@ -83,11 +91,14 @@ class _BoardCardState extends State<BoardCard> {
     final List<Widget> children = [];
     final List<Widget> children = [];
     cells.asMap().forEach(
     cells.asMap().forEach(
       (int index, GridCellIdentifier cellId) {
       (int index, GridCellIdentifier cellId) {
+        final cellNotifier = EditableCellNotifier();
         Widget child = widget.cellBuilder.buildCell(
         Widget child = widget.cellBuilder.buildCell(
           widget.groupId,
           widget.groupId,
           cellId,
           cellId,
           widget.isEditing,
           widget.isEditing,
+          cellNotifier,
         );
         );
+        rowNotifier.insertCell(cellId, cellNotifier);
 
 
         if (index != 0) {
         if (index != 0) {
           child = Padding(
           child = Padding(
@@ -121,7 +132,11 @@ class _CardMoreOption extends StatelessWidget with CardAccessory {
 
 
   @override
   @override
   Widget build(BuildContext context) {
   Widget build(BuildContext context) {
-    return svgWidget('grid/details', color: context.read<AppTheme>().iconColor);
+    return Padding(
+      padding: const EdgeInsets.all(3.0),
+      child:
+          svgWidget('grid/details', color: context.read<AppTheme>().iconColor),
+    );
   }
   }
 
 
   @override
   @override
@@ -131,3 +146,27 @@ class _CardMoreOption extends StatelessWidget with CardAccessory {
     ).show(context, direction: AnchorDirection.bottomWithCenterAligned);
     ).show(context, direction: AnchorDirection.bottomWithCenterAligned);
   }
   }
 }
 }
+
+class _CardEditOption extends StatelessWidget with CardAccessory {
+  final VoidCallback startEditing;
+  const _CardEditOption({
+    required this.startEditing,
+    Key? key,
+  }) : super(key: key);
+
+  @override
+  Widget build(BuildContext context) {
+    return Padding(
+      padding: const EdgeInsets.all(3.0),
+      child: svgWidget(
+        'editor/edit',
+        color: context.read<AppTheme>().iconColor,
+      ),
+    );
+  }
+
+  @override
+  void onTap(BuildContext context) {
+    startEditing();
+  }
+}

+ 4 - 0
frontend/app_flowy/lib/plugins/board/presentation/card/card_cell_builder.dart

@@ -2,6 +2,7 @@ import 'package:app_flowy/plugins/grid/application/cell/cell_service/cell_servic
 import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter/material.dart';
 
 
+import 'board_cell.dart';
 import 'board_checkbox_cell.dart';
 import 'board_checkbox_cell.dart';
 import 'board_date_cell.dart';
 import 'board_date_cell.dart';
 import 'board_number_cell.dart';
 import 'board_number_cell.dart';
@@ -23,6 +24,7 @@ class BoardCellBuilder {
     String groupId,
     String groupId,
     GridCellIdentifier cellId,
     GridCellIdentifier cellId,
     bool isEditing,
     bool isEditing,
+    EditableCellNotifier cellNotifier,
   ) {
   ) {
     final cellControllerBuilder = GridCellControllerBuilder(
     final cellControllerBuilder = GridCellControllerBuilder(
       delegate: delegate,
       delegate: delegate,
@@ -54,6 +56,7 @@ class BoardCellBuilder {
         return BoardSelectOptionCell(
         return BoardSelectOptionCell(
           groupId: groupId,
           groupId: groupId,
           cellControllerBuilder: cellControllerBuilder,
           cellControllerBuilder: cellControllerBuilder,
+          editableNotifier: cellNotifier,
           key: key,
           key: key,
         );
         );
       case FieldType.Number:
       case FieldType.Number:
@@ -67,6 +70,7 @@ class BoardCellBuilder {
           groupId: groupId,
           groupId: groupId,
           cellControllerBuilder: cellControllerBuilder,
           cellControllerBuilder: cellControllerBuilder,
           isFocus: isEditing,
           isFocus: isEditing,
+          editableNotifier: cellNotifier,
           key: key,
           key: key,
         );
         );
       case FieldType.URL:
       case FieldType.URL:

+ 19 - 15
frontend/app_flowy/lib/plugins/board/presentation/card/card_container.dart

@@ -69,12 +69,11 @@ class CardAccessoryContainer extends StatelessWidget {
         style: HoverStyle(
         style: HoverStyle(
           hoverColor: theme.hover,
           hoverColor: theme.hover,
           backgroundColor: theme.surface,
           backgroundColor: theme.surface,
+          borderRadius: BorderRadius.zero,
         ),
         ),
-        builder: (_, onHover) => Container(
-          width: 26,
-          height: 26,
-          padding: const EdgeInsets.all(3),
-          decoration: _makeBoxDecoration(context),
+        builder: (_, onHover) => SizedBox(
+          width: 24,
+          height: 24,
           child: accessory,
           child: accessory,
         ),
         ),
       );
       );
@@ -85,7 +84,11 @@ class CardAccessoryContainer extends StatelessWidget {
       );
       );
     }).toList();
     }).toList();
 
 
-    return Wrap(children: children, spacing: 6);
+    return Container(
+      clipBehavior: Clip.hardEdge,
+      decoration: _makeBoxDecoration(context),
+      child: Row(children: children),
+    );
   }
   }
 }
 }
 
 
@@ -95,15 +98,16 @@ BoxDecoration _makeBoxDecoration(BuildContext context) {
   return BoxDecoration(
   return BoxDecoration(
     color: Colors.transparent,
     color: Colors.transparent,
     border: Border.fromBorderSide(borderSide),
     border: Border.fromBorderSide(borderSide),
-    boxShadow: const [
-      BoxShadow(
-        color: Colors.transparent,
-        spreadRadius: 0,
-        blurRadius: 2,
-        offset: Offset.zero,
-      )
-    ],
-    borderRadius: const BorderRadius.all(Radius.circular(6)),
+    // boxShadow: const [
+    //   BoxShadow(
+    //     color: Colors.transparent,
+    //     spreadRadius: 0,
+    //     blurRadius: 5,
+    //     offset: Offset.zero,
+    //   )
+    // ],
+
+    borderRadius: const BorderRadius.all(Radius.circular(4)),
   );
   );
 }
 }
 
 

+ 8 - 1
frontend/app_flowy/packages/flowy_infra/lib/notifier.dart

@@ -31,7 +31,8 @@ class PublishNotifier<T> extends ChangeNotifier {
 
 
   T? get currentValue => _value;
   T? get currentValue => _value;
 
 
-  void addPublishListener(void Function(T) callback, {bool Function()? listenWhen}) {
+  void addPublishListener(void Function(T) callback,
+      {bool Function()? listenWhen}) {
     super.addListener(
     super.addListener(
       () {
       () {
         if (_value == null) {
         if (_value == null) {
@@ -47,3 +48,9 @@ class PublishNotifier<T> extends ChangeNotifier {
     );
     );
   }
   }
 }
 }
+
+class Notifier extends ChangeNotifier {
+  void notify() {
+    notifyListeners();
+  }
+}

+ 1 - 0
frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/select_option.rs

@@ -154,6 +154,7 @@ pub fn select_option_color_from_index(index: usize) -> SelectOptionColorPB {
         _ => SelectOptionColorPB::Purple,
         _ => SelectOptionColorPB::Purple,
     }
     }
 }
 }
+
 pub struct SelectOptionIds(Vec<String>);
 pub struct SelectOptionIds(Vec<String>);
 
 
 impl SelectOptionIds {
 impl SelectOptionIds {