diff --git a/frontend/app_flowy/lib/plugins/board/application/board_bloc.dart b/frontend/app_flowy/lib/plugins/board/application/board_bloc.dart index 903df92f78..673c159c8d 100644 --- a/frontend/app_flowy/lib/plugins/board/application/board_bloc.dart +++ b/frontend/app_flowy/lib/plugins/board/application/board_bloc.dart @@ -99,7 +99,6 @@ class BoardBloc extends Bloc { )); }, endEditRow: (rowId) { - assert(state.editingRow.isSome()); state.editingRow.fold(() => null, (editingRow) { assert(editingRow.row.id == rowId); emit(state.copyWith(editingRow: none())); diff --git a/frontend/app_flowy/lib/plugins/board/application/card/card_bloc.dart b/frontend/app_flowy/lib/plugins/board/application/card/card_bloc.dart index e4dccbf0df..82372896d1 100644 --- a/frontend/app_flowy/lib/plugins/board/application/card/card_bloc.dart +++ b/frontend/app_flowy/lib/plugins/board/application/card/card_bloc.dart @@ -7,20 +7,20 @@ import 'package:flowy_sdk/protobuf/flowy-grid/block_entities.pb.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; import 'dart:async'; - import 'card_data_controller.dart'; part 'card_bloc.freezed.dart'; class BoardCardBloc extends Bloc { - final String fieldId; + final String groupFieldId; final RowFFIService _rowService; final CardDataController _dataController; BoardCardBloc({ - required this.fieldId, + required this.groupFieldId, required String gridId, required CardDataController dataController, + required bool isEditing, }) : _rowService = RowFFIService( gridId: gridId, blockId: dataController.rowPB.blockId, @@ -29,7 +29,8 @@ class BoardCardBloc extends Bloc { super( BoardCardState.initial( dataController.rowPB, - _makeCells(fieldId, dataController.loadData()), + _makeCells(groupFieldId, dataController.loadData()), + isEditing, ), ) { on( @@ -44,6 +45,9 @@ class BoardCardBloc extends Bloc { changeReason: reason, )); }, + setIsEditing: (bool isEditing) { + emit(state.copyWith(isEditing: isEditing)); + }, ); }, ); @@ -69,7 +73,7 @@ class BoardCardBloc extends Bloc { _dataController.addListener( onRowChanged: (cellMap, reason) { if (!isClosed) { - final cells = _makeCells(fieldId, cellMap); + final cells = _makeCells(groupFieldId, cellMap); add(BoardCardEvent.didReceiveCells(cells, reason)); } }, @@ -77,22 +81,24 @@ class BoardCardBloc extends Bloc { } } -UnmodifiableListView _makeCells( - String fieldId, GridCellMap originalCellMap) { +List _makeCells( + String groupFieldId, GridCellMap originalCellMap) { List cells = []; for (final entry in originalCellMap.entries) { - if (entry.value.fieldId != fieldId) { + // Filter out the cell if it's fieldId equal to the groupFieldId + if (entry.value.fieldId != groupFieldId) { cells.add(BoardCellEquatable(entry.value)); } } - return UnmodifiableListView(cells); + return cells; } @freezed class BoardCardEvent with _$BoardCardEvent { const factory BoardCardEvent.initial() = _InitialRow; + const factory BoardCardEvent.setIsEditing(bool isEditing) = _IsEditing; const factory BoardCardEvent.didReceiveCells( - UnmodifiableListView cells, + List cells, RowsChangedReason reason, ) = _DidReceiveCells; } @@ -101,15 +107,20 @@ class BoardCardEvent with _$BoardCardEvent { class BoardCardState with _$BoardCardState { const factory BoardCardState({ required RowPB rowPB, - required UnmodifiableListView cells, + required List cells, + required bool isEditing, RowsChangedReason? changeReason, }) = _BoardCardState; factory BoardCardState.initial( - RowPB rowPB, UnmodifiableListView cells) => + RowPB rowPB, + List cells, + bool isEditing, + ) => BoardCardState( rowPB: rowPB, cells: cells, + isEditing: isEditing, ); } @@ -119,10 +130,12 @@ class BoardCellEquatable extends Equatable { const BoardCellEquatable(this.identifier); @override - List get props => [ - identifier.fieldContext.id, - identifier.fieldContext.fieldType, - identifier.fieldContext.visibility, - identifier.fieldContext.width, - ]; + List get props { + return [ + identifier.fieldContext.id, + identifier.fieldContext.fieldType, + identifier.fieldContext.visibility, + identifier.fieldContext.width, + ]; + } } diff --git a/frontend/app_flowy/lib/plugins/board/presentation/board_page.dart b/frontend/app_flowy/lib/plugins/board/presentation/board_page.dart index a771d84c36..1f4df9468a 100644 --- a/frontend/app_flowy/lib/plugins/board/presentation/board_page.dart +++ b/frontend/app_flowy/lib/plugins/board/presentation/board_page.dart @@ -64,6 +64,7 @@ class BoardContent extends StatefulWidget { class _BoardContentState extends State { late AppFlowyBoardScrollController scrollManager; + final Map cardKeysCache = {}; final config = AppFlowyBoardConfig( groupBackgroundColor: HexColor.fromHex('#F7F8FC'), @@ -83,6 +84,7 @@ class _BoardContentState extends State { buildWhen: (previous, current) => previous.groupIds != current.groupIds, builder: (context, state) { final column = Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [const _ToolbarBlocAdaptor(), _buildBoard(context)], ); @@ -240,8 +242,15 @@ class _BoardContentState extends State { }, ); + ValueKey? key = cardKeysCache[columnItem.id]; + if (key == null) { + final newKey = ValueKey(columnItem.id); + cardKeysCache[columnItem.id] = newKey; + key = newKey; + } + return AppFlowyGroupCard( - key: ValueKey(columnItem.id), + key: key, margin: config.cardPadding, decoration: _makeBoxDecoration(context), child: BoardCard( diff --git a/frontend/app_flowy/lib/plugins/board/presentation/card/board_cell.dart b/frontend/app_flowy/lib/plugins/board/presentation/card/board_cell.dart index e8a0406e0f..e979b2510e 100644 --- a/frontend/app_flowy/lib/plugins/board/presentation/card/board_cell.dart +++ b/frontend/app_flowy/lib/plugins/board/presentation/card/board_cell.dart @@ -1,47 +1,74 @@ import 'package:app_flowy/plugins/grid/application/prelude.dart'; -import 'package:flowy_infra/notifier.dart'; +import 'package:flutter/material.dart'; abstract class FocusableBoardCell { set becomeFocus(bool isFocus); } class EditableCellNotifier { - final Notifier becomeFirstResponder = Notifier(); + final ValueNotifier isCellEditing; - final Notifier resignFirstResponder = Notifier(); + EditableCellNotifier({bool isEditing = false}) + : isCellEditing = ValueNotifier(isEditing); - EditableCellNotifier(); + void dispose() { + isCellEditing.dispose(); + } } class EditableRowNotifier { final Map _cells = {}; + final ValueNotifier isEditing; + + EditableRowNotifier({required bool isEditing}) + : isEditing = ValueNotifier(isEditing); void insertCell( GridCellIdentifier cellIdentifier, EditableCellNotifier notifier, ) { + assert( + _cells.values.isEmpty, + 'Only one cell can receive the notification', + ); + final id = EditableCellId.from(cellIdentifier); + _cells[id]?.dispose(); + + notifier.isCellEditing.addListener(() { + isEditing.value = notifier.isCellEditing.value; + }); + _cells[EditableCellId.from(cellIdentifier)] = notifier; } void becomeFirstResponder() { - for (final notifier in _cells.values) { - notifier.becomeFirstResponder.notify(); - } + if (_cells.values.isEmpty) return; + assert( + _cells.values.length == 1, + 'Only one cell can receive the notification', + ); + _cells.values.first.isCellEditing.value = true; } void resignFirstResponder() { - for (final notifier in _cells.values) { - notifier.resignFirstResponder.notify(); - } + if (_cells.values.isEmpty) return; + assert( + _cells.values.length == 1, + 'Only one cell can receive the notification', + ); + _cells.values.first.isCellEditing.value = false; } void clear() { + for (final notifier in _cells.values) { + notifier.dispose(); + } _cells.clear(); } void dispose() { for (final notifier in _cells.values) { - notifier.resignFirstResponder.notify(); + notifier.dispose(); } _cells.clear(); diff --git a/frontend/app_flowy/lib/plugins/board/presentation/card/board_date_cell.dart b/frontend/app_flowy/lib/plugins/board/presentation/card/board_date_cell.dart index 47472a0f9f..18c0a84f47 100644 --- a/frontend/app_flowy/lib/plugins/board/presentation/card/board_date_cell.dart +++ b/frontend/app_flowy/lib/plugins/board/presentation/card/board_date_cell.dart @@ -5,6 +5,8 @@ import 'package:flowy_infra_ui/style_widget/text.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'define.dart'; + class BoardDateCell extends StatefulWidget { final String groupId; final GridCellControllerBuilder cellControllerBuilder; @@ -44,10 +46,15 @@ class _BoardDateCellState extends State { } else { return Align( alignment: Alignment.centerLeft, - child: FlowyText.regular( - state.dateStr, - fontSize: 13, - color: context.read().shader3, + child: Padding( + padding: EdgeInsets.symmetric( + vertical: BoardSizes.cardCellVPadding, + ), + child: FlowyText.regular( + state.dateStr, + fontSize: 13, + color: context.read().shader3, + ), ), ); } diff --git a/frontend/app_flowy/lib/plugins/board/presentation/card/board_number_cell.dart b/frontend/app_flowy/lib/plugins/board/presentation/card/board_number_cell.dart index 0f4aca6b61..76a5f84bbf 100644 --- a/frontend/app_flowy/lib/plugins/board/presentation/card/board_number_cell.dart +++ b/frontend/app_flowy/lib/plugins/board/presentation/card/board_number_cell.dart @@ -4,6 +4,8 @@ import 'package:flowy_infra_ui/style_widget/text.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'define.dart'; + class BoardNumberCell extends StatefulWidget { final String groupId; final GridCellControllerBuilder cellControllerBuilder; @@ -43,9 +45,14 @@ class _BoardNumberCellState extends State { } else { return Align( alignment: Alignment.centerLeft, - child: FlowyText.medium( - state.content, - fontSize: 14, + child: Padding( + padding: EdgeInsets.symmetric( + vertical: BoardSizes.cardCellVPadding, + ), + child: FlowyText.medium( + state.content, + fontSize: 14, + ), ), ); } diff --git a/frontend/app_flowy/lib/plugins/board/presentation/card/board_select_option_cell.dart b/frontend/app_flowy/lib/plugins/board/presentation/card/board_select_option_cell.dart index 8814d83a5e..6469a4e106 100644 --- a/frontend/app_flowy/lib/plugins/board/presentation/card/board_select_option_cell.dart +++ b/frontend/app_flowy/lib/plugins/board/presentation/card/board_select_option_cell.dart @@ -56,23 +56,23 @@ class _BoardSelectOptionCellState extends State { (option) => SelectOptionTag.fromOption( context: context, option: option, + onSelected: () { + SelectOptionCellEditor.show( + context: context, + cellController: widget.cellControllerBuilder.build() + as GridSelectOptionCellController, + ); + }, ), ) .toList(); return IntrinsicHeight( - child: Stack( - alignment: AlignmentDirectional.center, - fit: StackFit.expand, - children: [ - Padding( - padding: const EdgeInsets.symmetric(vertical: 6), - child: Wrap(spacing: 4, runSpacing: 2, children: children), - ), - _SelectOptionDialog( - controller: widget.cellControllerBuilder.build(), - ), - ], + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 6), + child: SizedBox.expand( + child: Wrap(spacing: 4, runSpacing: 2, children: children), + ), ), ); } @@ -87,23 +87,3 @@ class _BoardSelectOptionCellState extends State { super.dispose(); } } - -class _SelectOptionDialog extends StatelessWidget { - final GridSelectOptionCellController _controller; - const _SelectOptionDialog({ - Key? key, - required IGridCellController controller, - }) : _controller = controller as GridSelectOptionCellController, - super(key: key); - - @override - Widget build(BuildContext context) { - return InkWell(onTap: () { - SelectOptionCellEditor.show( - context, - _controller, - () {}, - ); - }); - } -} diff --git a/frontend/app_flowy/lib/plugins/board/presentation/card/board_text_cell.dart b/frontend/app_flowy/lib/plugins/board/presentation/card/board_text_cell.dart index 99be8cdb3d..4a48e8fe48 100644 --- a/frontend/app_flowy/lib/plugins/board/presentation/card/board_text_cell.dart +++ b/frontend/app_flowy/lib/plugins/board/presentation/card/board_text_cell.dart @@ -9,7 +9,6 @@ import 'define.dart'; class BoardTextCell extends StatefulWidget with EditableCell { final String groupId; - final bool isFocus; @override final EditableCellNotifier? editableNotifier; final GridCellControllerBuilder cellControllerBuilder; @@ -18,7 +17,6 @@ class BoardTextCell extends StatefulWidget with EditableCell { required this.groupId, required this.cellControllerBuilder, this.editableNotifier, - this.isFocus = false, Key? key, }) : super(key: key); @@ -39,40 +37,42 @@ class _BoardTextCellState extends State { _cellBloc = BoardTextCellBloc(cellController: cellController) ..add(const BoardTextCellEvent.initial()); _controller = TextEditingController(text: _cellBloc.state.content); - focusWhenInit = widget.isFocus; - - if (widget.isFocus) { + focusWhenInit = widget.editableNotifier?.isCellEditing.value ?? false; + if (focusWhenInit) { focusNode.requestFocus(); } focusNode.addListener(() { if (!focusNode.hasFocus) { + focusWhenInit = false; + widget.editableNotifier?.isCellEditing.value = false; _cellBloc.add(const BoardTextCellEvent.enableEdit(false)); - - if (focusWhenInit) { - setState(() { - focusWhenInit = false; - }); - } } }); - - widget.editableNotifier?.becomeFirstResponder.addListener(() { - if (!mounted) return; - WidgetsBinding.instance.addPostFrameCallback((_) { - focusNode.requestFocus(); - }); - _cellBloc.add(const BoardTextCellEvent.enableEdit(true)); - }); - - widget.editableNotifier?.resignFirstResponder.addListener(() { - if (!mounted) return; - _cellBloc.add(const BoardTextCellEvent.enableEdit(false)); - }); - + _bindEditableNotifier(); super.initState(); } + void _bindEditableNotifier() { + widget.editableNotifier?.isCellEditing.addListener(() { + if (!mounted) return; + + final isEditing = widget.editableNotifier?.isCellEditing.value ?? false; + if (isEditing) { + WidgetsBinding.instance.addPostFrameCallback((_) { + focusNode.requestFocus(); + }); + } + _cellBloc.add(BoardTextCellEvent.enableEdit(isEditing)); + }); + } + + @override + void didUpdateWidget(covariant BoardTextCell oldWidget) { + _bindEditableNotifier(); + super.didUpdateWidget(oldWidget); + } + @override Widget build(BuildContext context) { return BlocProvider.value( @@ -84,6 +84,15 @@ class _BoardTextCellState extends State { } }, child: BlocBuilder( + buildWhen: (previous, current) { + if (previous.content != current.content && + _controller.text == current.content && + current.enableEdit) { + return false; + } + + return previous != current; + }, builder: (context, state) { if (state.content.isEmpty && state.enableEdit == false && @@ -127,24 +136,26 @@ class _BoardTextCellState extends State { } Widget _buildTextField() { - return TextField( - controller: _controller, - focusNode: focusNode, - onChanged: (value) => focusChanged(), - onEditingComplete: () => focusNode.unfocus(), - maxLines: 1, - style: const TextStyle( - fontSize: 14, - fontWeight: FontWeight.w500, - fontFamily: 'Mulish', - ), - decoration: InputDecoration( - // Magic number 4 makes the textField take up the same space as FlowyText - contentPadding: EdgeInsets.symmetric( - vertical: BoardSizes.cardCellVPadding + 4, + return IntrinsicHeight( + child: TextField( + controller: _controller, + focusNode: focusNode, + onChanged: (value) => focusChanged(), + onEditingComplete: () => focusNode.unfocus(), + maxLines: null, + style: const TextStyle( + fontSize: 14, + fontWeight: FontWeight.w500, + fontFamily: 'Mulish', + ), + decoration: InputDecoration( + // Magic number 4 makes the textField take up the same space as FlowyText + contentPadding: EdgeInsets.symmetric( + vertical: BoardSizes.cardCellVPadding + 4, + ), + border: InputBorder.none, + isDense: true, ), - border: InputBorder.none, - isDense: true, ), ); } diff --git a/frontend/app_flowy/lib/plugins/board/presentation/card/board_url_cell.dart b/frontend/app_flowy/lib/plugins/board/presentation/card/board_url_cell.dart index 40cdec7c2f..f5d7dabf79 100644 --- a/frontend/app_flowy/lib/plugins/board/presentation/card/board_url_cell.dart +++ b/frontend/app_flowy/lib/plugins/board/presentation/card/board_url_cell.dart @@ -4,6 +4,8 @@ import 'package:flowy_infra/theme.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'define.dart'; + class BoardUrlCell extends StatefulWidget { final String groupId; final GridCellControllerBuilder cellControllerBuilder; @@ -43,14 +45,19 @@ class _BoardUrlCellState extends State { } else { return Align( alignment: Alignment.centerLeft, - child: RichText( - textAlign: TextAlign.left, - text: TextSpan( - text: state.content, - style: TextStyle( - color: theme.main2, - fontSize: 14, - decoration: TextDecoration.underline, + child: Padding( + padding: EdgeInsets.symmetric( + vertical: BoardSizes.cardCellVPadding, + ), + child: RichText( + textAlign: TextAlign.left, + text: TextSpan( + text: state.content, + style: TextStyle( + color: theme.main2, + fontSize: 14, + decoration: TextDecoration.underline, + ), ), ), ), diff --git a/frontend/app_flowy/lib/plugins/board/presentation/card/card.dart b/frontend/app_flowy/lib/plugins/board/presentation/card/card.dart index 9d5aefd9bc..f5111224e1 100644 --- a/frontend/app_flowy/lib/plugins/board/presentation/card/card.dart +++ b/frontend/app_flowy/lib/plugins/board/presentation/card/card.dart @@ -5,6 +5,7 @@ import 'package:app_flowy/plugins/grid/presentation/widgets/row/row_action_sheet import 'package:flowy_infra/image.dart'; import 'package:flowy_infra/theme.dart'; import 'package:flowy_infra_ui/flowy_infra_ui_web.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'board_cell.dart'; @@ -41,12 +42,19 @@ class _BoardCardState extends State { @override void initState() { - rowNotifier = EditableRowNotifier(); + rowNotifier = EditableRowNotifier(isEditing: widget.isEditing); _cardBloc = BoardCardBloc( gridId: widget.gridId, - fieldId: widget.fieldId, + groupFieldId: widget.fieldId, dataController: widget.dataController, + isEditing: widget.isEditing, )..add(const BoardCardEvent.initial()); + + rowNotifier.isEditing.addListener(() { + if (!mounted) return; + _cardBloc.add(BoardCardEvent.setIsEditing(rowNotifier.isEditing.value)); + }); + super.initState(); } @@ -56,10 +64,15 @@ class _BoardCardState extends State { value: _cardBloc, child: BlocBuilder( buildWhen: (previous, current) { - return previous.cells.length != current.cells.length; + if (previous.cells.length != current.cells.length || + previous.isEditing != current.isEditing) { + return true; + } + return !listEquals(previous.cells, current.cells); }, builder: (context, state) { return BoardCardContainer( + buildAccessoryWhen: () => state.isEditing == false, accessoryBuilder: (context) { return [ _CardEditOption( @@ -92,17 +105,24 @@ class _BoardCardState extends State { rowNotifier.clear(); cells.asMap().forEach( (int index, GridCellIdentifier cellId) { - final cellNotifier = EditableCellNotifier(); + EditableCellNotifier cellNotifier; + if (index == 0) { + // Only use the first cell to receive user's input when click the edit + // button + cellNotifier = EditableCellNotifier( + isEditing: rowNotifier.isEditing.value, + ); + rowNotifier.insertCell(cellId, cellNotifier); + } else { + cellNotifier = EditableCellNotifier(); + } + Widget child = widget.cellBuilder.buildCell( widget.groupId, cellId, - index == 0 ? widget.isEditing : false, cellNotifier, ); - if (index == 0) { - rowNotifier.insertCell(cellId, cellNotifier); - } child = Padding( key: cellId.key(), padding: const EdgeInsets.only(left: 4, right: 4), diff --git a/frontend/app_flowy/lib/plugins/board/presentation/card/card_cell_builder.dart b/frontend/app_flowy/lib/plugins/board/presentation/card/card_cell_builder.dart index 99bd3f3b3a..1485bb7bd0 100644 --- a/frontend/app_flowy/lib/plugins/board/presentation/card/card_cell_builder.dart +++ b/frontend/app_flowy/lib/plugins/board/presentation/card/card_cell_builder.dart @@ -23,7 +23,6 @@ class BoardCellBuilder { Widget buildCell( String groupId, GridCellIdentifier cellId, - bool isEditing, EditableCellNotifier cellNotifier, ) { final cellControllerBuilder = GridCellControllerBuilder( @@ -69,7 +68,6 @@ class BoardCellBuilder { return BoardTextCell( groupId: groupId, cellControllerBuilder: cellControllerBuilder, - isFocus: isEditing, editableNotifier: cellNotifier, key: key, ); diff --git a/frontend/app_flowy/lib/plugins/board/presentation/card/card_container.dart b/frontend/app_flowy/lib/plugins/board/presentation/card/card_container.dart index d28e6712c9..7479f3044e 100644 --- a/frontend/app_flowy/lib/plugins/board/presentation/card/card_container.dart +++ b/frontend/app_flowy/lib/plugins/board/presentation/card/card_container.dart @@ -7,11 +7,13 @@ import 'package:styled_widget/styled_widget.dart'; class BoardCardContainer extends StatelessWidget { final Widget child; final CardAccessoryBuilder? accessoryBuilder; + final bool Function()? buildAccessoryWhen; final void Function(BuildContext) onTap; const BoardCardContainer({ required this.child, required this.onTap, this.accessoryBuilder, + this.buildAccessoryWhen, Key? key, }) : super(key: key); @@ -22,7 +24,12 @@ class BoardCardContainer extends StatelessWidget { child: Consumer<_CardContainerNotifier>( builder: (context, notifier, _) { Widget container = Center(child: child); - if (accessoryBuilder != null) { + bool shouldBuildAccessory = true; + if (buildAccessoryWhen != null) { + shouldBuildAccessory = buildAccessoryWhen!.call(); + } + + if (accessoryBuilder != null && shouldBuildAccessory) { final accessories = accessoryBuilder!(context); if (accessories.isNotEmpty) { container = _CardEnterRegion( diff --git a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/date_cell/date_cell.dart b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/date_cell/date_cell.dart index 2b7ef9b6ab..2a5c79b2f0 100644 --- a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/date_cell/date_cell.dart +++ b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/date_cell/date_cell.dart @@ -46,7 +46,8 @@ class _DateCellState extends GridCellState { @override void initState() { _popover = PopoverController(); - final cellController = widget.cellControllerBuilder.build(); + final cellController = + widget.cellControllerBuilder.build() as GridDateCellController; _cellBloc = getIt(param1: cellController) ..add(const DateCellEvent.initial()); super.initState(); diff --git a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/select_option_cell/select_option_editor.dart b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/select_option_cell/select_option_editor.dart index 04252ed987..024d5ff356 100644 --- a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/select_option_cell/select_option_editor.dart +++ b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/select_option_cell/select_option_editor.dart @@ -27,13 +27,13 @@ const double _editorPannelWidth = 300; class SelectOptionCellEditor extends StatelessWidget with FlowyOverlayDelegate { final GridSelectOptionCellController cellController; - final VoidCallback onDismissed; + final VoidCallback? onDismissed; static double editorPanelWidth = 300; const SelectOptionCellEditor({ required this.cellController, - required this.onDismissed, + this.onDismissed, Key? key, }) : super(key: key); @@ -61,14 +61,14 @@ class SelectOptionCellEditor extends StatelessWidget with FlowyOverlayDelegate { ); } - static void show( - BuildContext context, - GridSelectOptionCellController cellContext, - VoidCallback onDismissed, - ) { + static void show({ + required BuildContext context, + required GridSelectOptionCellController cellController, + VoidCallback? onDismissed, + }) { SelectOptionCellEditor.remove(context); final editor = SelectOptionCellEditor( - cellController: cellContext, + cellController: cellController, onDismissed: onDismissed, ); @@ -97,7 +97,7 @@ class SelectOptionCellEditor extends StatelessWidget with FlowyOverlayDelegate { bool asBarrier() => true; @override - void didRemove() => onDismissed(); + void didRemove() => onDismissed?.call(); } class _OptionList extends StatelessWidget { diff --git a/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/styled_widgets/footer.dart b/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/styled_widgets/footer.dart index 54ad4e242d..b130f1e099 100644 --- a/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/styled_widgets/footer.dart +++ b/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/styled_widgets/footer.dart @@ -25,7 +25,7 @@ class AppFlowyGroupFooter extends StatefulWidget { class _AppFlowyGroupFooterState extends State { @override Widget build(BuildContext context) { - return GestureDetector( + return InkWell( onTap: widget.onAddButtonClick, child: SizedBox( height: widget.height, diff --git a/frontend/rust-lib/flowy-grid/src/services/group/action.rs b/frontend/rust-lib/flowy-grid/src/services/group/action.rs index b339722905..fc92b68c46 100644 --- a/frontend/rust-lib/flowy-grid/src/services/group/action.rs +++ b/frontend/rust-lib/flowy-grid/src/services/group/action.rs @@ -8,7 +8,9 @@ pub trait GroupAction: Send + Sync { fn default_cell_rev(&self) -> Option { None } - + fn use_default_group(&self) -> bool { + true + } fn can_group(&self, content: &str, cell_data: &Self::CellDataType) -> bool; fn add_row_if_match(&mut self, row_rev: &RowRevision, cell_data: &Self::CellDataType) -> Vec; fn remove_row_if_match(&mut self, row_rev: &RowRevision, cell_data: &Self::CellDataType) -> Vec; diff --git a/frontend/rust-lib/flowy-grid/src/services/group/configuration.rs b/frontend/rust-lib/flowy-grid/src/services/group/configuration.rs index c110d01ad4..1518240442 100644 --- a/frontend/rust-lib/flowy-grid/src/services/group/configuration.rs +++ b/frontend/rust-lib/flowy-grid/src/services/group/configuration.rs @@ -97,11 +97,8 @@ where self.groups_map.values().collect() } - /// Returns the all the groups that contain the default group. - pub(crate) fn clone_groups(&self) -> Vec { - let mut groups: Vec = self.groups_map.values().cloned().collect(); - groups.push(self.default_group.clone()); - groups + pub(crate) fn default_group(&self) -> &Group { + &self.default_group } /// Iterate mut the groups. The default group will be the last one that get mutated. diff --git a/frontend/rust-lib/flowy-grid/src/services/group/controller.rs b/frontend/rust-lib/flowy-grid/src/services/group/controller.rs index 75bc16f3b2..7bdbb57368 100644 --- a/frontend/rust-lib/flowy-grid/src/services/group/controller.rs +++ b/frontend/rust-lib/flowy-grid/src/services/group/controller.rs @@ -182,7 +182,13 @@ where } fn groups(&self) -> Vec { - self.group_ctx.clone_groups() + if self.use_default_group() { + let mut groups: Vec = self.group_ctx.concrete_groups().into_iter().cloned().collect(); + groups.push(self.group_ctx.default_group().clone()); + groups + } else { + self.group_ctx.concrete_groups().into_iter().cloned().collect() + } } fn get_group(&self, group_id: &str) -> Option<(usize, Group)> { @@ -243,7 +249,7 @@ where let cell_data = cell_bytes.parser::

()?; let mut changesets = self.add_row_if_match(row_rev, &cell_data); let default_group_changeset = self.update_default_group(row_rev, &changesets); - tracing::info!("default_group_changeset: {}", default_group_changeset); + tracing::trace!("default_group_changeset: {}", default_group_changeset); if !default_group_changeset.is_empty() { changesets.push(default_group_changeset); } diff --git a/frontend/rust-lib/flowy-grid/src/services/group/controller_impls/checkbox_controller.rs b/frontend/rust-lib/flowy-grid/src/services/group/controller_impls/checkbox_controller.rs index c95a6e4af2..62f21d708b 100644 --- a/frontend/rust-lib/flowy-grid/src/services/group/controller_impls/checkbox_controller.rs +++ b/frontend/rust-lib/flowy-grid/src/services/group/controller_impls/checkbox_controller.rs @@ -27,6 +27,10 @@ impl GroupAction for CheckboxGroupController { Some(CellRevision::new(UNCHECK.to_string())) } + fn use_default_group(&self) -> bool { + false + } + fn can_group(&self, content: &str, cell_data: &Self::CellDataType) -> bool { if cell_data.is_check() { content == CHECK diff --git a/frontend/rust-lib/flowy-grid/src/util.rs b/frontend/rust-lib/flowy-grid/src/util.rs index 65f6440c1f..9d82cedff9 100644 --- a/frontend/rust-lib/flowy-grid/src/util.rs +++ b/frontend/rust-lib/flowy-grid/src/util.rs @@ -3,7 +3,6 @@ use crate::services::field::*; use crate::services::row::RowRevisionBuilder; use flowy_grid_data_model::revision::BuildGridContext; use flowy_sync::client_grid::GridBuilder; -use lib_infra::util::timestamp; pub fn make_default_grid() -> BuildGridContext { let mut grid_builder = GridBuilder::new(); @@ -59,6 +58,45 @@ pub fn make_default_board() -> BuildGridContext { let single_select_field_id = single_select_field.id.clone(); grid_builder.add_field(single_select_field); + for i in 0..3 { + let mut row_builder = RowRevisionBuilder::new(grid_builder.block_id(), grid_builder.field_revs()); + row_builder.insert_select_option_cell(&single_select_field_id, to_do_option.id.clone()); + let data = format!("Card {}", i + 1); + row_builder.insert_text_cell(&text_field_id, data); + let row = row_builder.build(); + grid_builder.add_row(row); + } + + grid_builder.build() +} + +#[allow(dead_code)] +pub fn make_default_board_2() -> BuildGridContext { + let mut grid_builder = GridBuilder::new(); + // text + let text_field = FieldBuilder::new(RichTextTypeOptionBuilder::default()) + .name("Description") + .visibility(true) + .primary(true) + .build(); + let text_field_id = text_field.id.clone(); + grid_builder.add_field(text_field); + + // single select + let to_do_option = SelectOptionPB::with_color("To Do", SelectOptionColorPB::Purple); + let doing_option = SelectOptionPB::with_color("Doing", SelectOptionColorPB::Orange); + let done_option = SelectOptionPB::with_color("Done", SelectOptionColorPB::Yellow); + let single_select_type_option = SingleSelectTypeOptionBuilder::default() + .add_option(to_do_option.clone()) + .add_option(doing_option.clone()) + .add_option(done_option.clone()); + let single_select_field = FieldBuilder::new(single_select_type_option) + .name("Status") + .visibility(true) + .build(); + let single_select_field_id = single_select_field.id.clone(); + grid_builder.add_field(single_select_field); + // MultiSelect let work_option = SelectOptionPB::with_color("Work", SelectOptionColorPB::Aqua); let travel_option = SelectOptionPB::with_color("Travel", SelectOptionColorPB::Green); @@ -152,102 +190,3 @@ pub fn make_default_board() -> BuildGridContext { grid_builder.build() } - -#[allow(dead_code)] -pub fn make_default_board2() -> BuildGridContext { - let mut grid_builder = GridBuilder::new(); - // text - let text_field = FieldBuilder::new(RichTextTypeOptionBuilder::default()) - .name("Name") - .visibility(true) - .primary(true) - .build(); - let text_field_id = text_field.id.clone(); - grid_builder.add_field(text_field); - - // date - let date_type_option = DateTypeOptionBuilder::default(); - let date_field = FieldBuilder::new(date_type_option) - .name("Date") - .visibility(true) - .build(); - let date_field_id = date_field.id.clone(); - let timestamp = timestamp(); - grid_builder.add_field(date_field); - - // single select - let in_progress_option = SelectOptionPB::new("In progress"); - let not_started_option = SelectOptionPB::new("Not started"); - let done_option = SelectOptionPB::new("Done"); - let single_select_type_option = SingleSelectTypeOptionBuilder::default() - .add_option(not_started_option.clone()) - .add_option(in_progress_option) - .add_option(done_option); - let single_select_field = FieldBuilder::new(single_select_type_option) - .name("Status") - .visibility(true) - .build(); - let single_select_field_id = single_select_field.id.clone(); - grid_builder.add_field(single_select_field); - - // MultiSelect - let apple_option = SelectOptionPB::new("Apple"); - let banana_option = SelectOptionPB::new("Banana"); - let pear_option = SelectOptionPB::new("Pear"); - let multi_select_type_option = MultiSelectTypeOptionBuilder::default() - .add_option(banana_option.clone()) - .add_option(apple_option.clone()) - .add_option(pear_option); - let multi_select_field = FieldBuilder::new(multi_select_type_option) - .name("Fruit") - .visibility(true) - .build(); - let multi_select_field_id = multi_select_field.id.clone(); - grid_builder.add_field(multi_select_field); - - // Number - let number_type_option = NumberTypeOptionBuilder::default().set_format(NumberFormat::USD); - let number_field = FieldBuilder::new(number_type_option) - .name("Price") - .visibility(true) - .build(); - let number_field_id = number_field.id.clone(); - grid_builder.add_field(number_field); - - // Checkbox - let checkbox_type_option = CheckboxTypeOptionBuilder::default(); - let checkbox_field = FieldBuilder::new(checkbox_type_option).name("Reimbursement").build(); - let checkbox_field_id = checkbox_field.id.clone(); - grid_builder.add_field(checkbox_field); - - // Url - let url_type_option = URLTypeOptionBuilder::default(); - let url_field = FieldBuilder::new(url_type_option).name("Shop Link").build(); - let url_field_id = url_field.id.clone(); - grid_builder.add_field(url_field); - - // Insert rows - for i in 0..10 { - // insert single select - let mut row_builder = RowRevisionBuilder::new(grid_builder.block_id(), grid_builder.field_revs()); - row_builder.insert_select_option_cell(&single_select_field_id, not_started_option.id.clone()); - // insert multi select - row_builder.insert_select_option_cell(&multi_select_field_id, apple_option.id.clone()); - row_builder.insert_select_option_cell(&multi_select_field_id, banana_option.id.clone()); - // insert text - row_builder.insert_text_cell(&text_field_id, format!("Card {}", i)); - // insert date - row_builder.insert_date_cell(&date_field_id, timestamp); - // number - row_builder.insert_number_cell(&number_field_id, i); - // checkbox - row_builder.insert_checkbox_cell(&checkbox_field_id, i % 2 == 0); - // url - row_builder.insert_url_cell(&url_field_id, "https://appflowy.io".to_string()); - - let row = row_builder.build(); - grid_builder.add_row(row); - } - - grid_builder.build() -}