From 20bff9003ec297c68eb593b0c6c2cdd1eb5b5e65 Mon Sep 17 00:00:00 2001 From: Lucas Date: Tue, 24 Dec 2024 09:39:15 +0800 Subject: [PATCH] feat: support table align on desktop (#7034) * feat: support table align on desktop * test: update table align * test: add integration test --- .../document_with_simple_table_test.dart | 66 +++++++++++++++++++ .../presentation/editor_configuration.dart | 16 ++--- .../actions/option/align_option_action.dart | 35 +++++++--- .../actions/option/option_actions.dart | 1 + .../simple_table_cell_block_component.dart | 4 +- .../simple_table_node_extension.dart | 41 ++++++++++++ .../simple_table_style_operation.dart | 3 +- .../simple_table_style_operation_test.dart | 19 ++++++ 8 files changed, 165 insertions(+), 20 deletions(-) diff --git a/frontend/appflowy_flutter/integration_test/desktop/document/document_with_simple_table_test.dart b/frontend/appflowy_flutter/integration_test/desktop/document/document_with_simple_table_test.dart index 9548aa4a29..23bba09b5b 100644 --- a/frontend/appflowy_flutter/integration_test/desktop/document/document_with_simple_table_test.dart +++ b/frontend/appflowy_flutter/integration_test/desktop/document/document_with_simple_table_test.dart @@ -561,6 +561,72 @@ void main() { final node = editorState.document.nodeAtPath([0, 0, 0])!; expect(node.children.length, 1); }); + + testWidgets('using option menu to set table align', (tester) async { + await tester.initializeAppFlowy(); + await tester.tapAnonymousSignInButton(); + await tester.createNewPageWithNameUnderParent( + name: 'simple_table_test', + ); + + await tester.editor.tapLineOfEditorAt(0); + await tester.insertTableInDocument(); + await tester.editor.hoverAndClickOptionMenuButton([0]); + + final editorState = tester.editor.getCurrentEditorState(); + final beforeAlign = editorState.document.nodeAtPath([0])!.tableAlign; + expect(beforeAlign, TableAlign.left); + + await tester.tapButton( + find.text( + LocaleKeys.document_plugins_simpleTable_moreActions_align.tr(), + ), + ); + await tester.pumpAndSettle(); + await tester.tapButton( + find.text( + LocaleKeys.document_plugins_optionAction_center.tr(), + ), + ); + await tester.pumpAndSettle(); + + final afterAlign = editorState.document.nodeAtPath([0])!.tableAlign; + expect(afterAlign, TableAlign.center); + + await tester.editor.hoverAndClickOptionMenuButton([0]); + await tester.tapButton( + find.text( + LocaleKeys.document_plugins_simpleTable_moreActions_align.tr(), + ), + ); + await tester.pumpAndSettle(); + await tester.tapButton( + find.text( + LocaleKeys.document_plugins_optionAction_right.tr(), + ), + ); + await tester.pumpAndSettle(); + + final afterAlign2 = editorState.document.nodeAtPath([0])!.tableAlign; + expect(afterAlign2, TableAlign.right); + + await tester.editor.hoverAndClickOptionMenuButton([0]); + await tester.tapButton( + find.text( + LocaleKeys.document_plugins_simpleTable_moreActions_align.tr(), + ), + ); + await tester.pumpAndSettle(); + await tester.tapButton( + find.text( + LocaleKeys.document_plugins_optionAction_left.tr(), + ), + ); + await tester.pumpAndSettle(); + + final afterAlign3 = editorState.document.nodeAtPath([0])!.tableAlign; + expect(afterAlign3, TableAlign.left); + }); } extension on WidgetTester { diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_configuration.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_configuration.dart index efa7a6405f..3675fe1a94 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_configuration.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_configuration.dart @@ -126,6 +126,14 @@ List _buildOptionActions(BuildContext context, String type) { standardActions.add(OptionAction.turnInto); + if (SimpleTableBlockKeys.type == type) { + standardActions.addAll([ + OptionAction.divider, + OptionAction.setToPageWidth, + OptionAction.distributeColumnsEvenly, + ]); + } + if (EditorOptionActionType.color.supportTypes.contains(type)) { standardActions.addAll([OptionAction.divider, OptionAction.color]); } @@ -138,14 +146,6 @@ List _buildOptionActions(BuildContext context, String type) { standardActions.addAll([OptionAction.divider, OptionAction.depth]); } - if (SimpleTableBlockKeys.type == type) { - standardActions.addAll([ - OptionAction.divider, - OptionAction.setToPageWidth, - OptionAction.distributeColumnsEvenly, - ]); - } - return standardActions; } diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/actions/option/align_option_action.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/actions/option/align_option_action.dart index 1c84f5256a..7f91f7e2b0 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/actions/option/align_option_action.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/actions/option/align_option_action.dart @@ -1,10 +1,10 @@ import 'package:appflowy/generated/flowy_svgs.g.dart'; import 'package:appflowy/generated/locale_keys.g.dart'; +import 'package:appflowy/plugins/document/presentation/editor_plugins/simple_table/simple_table.dart'; import 'package:appflowy/workspace/presentation/widgets/pop_up_action.dart'; import 'package:appflowy_editor/appflowy_editor.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; -import 'package:styled_widget/styled_widget.dart'; enum OptionAlignType { left, @@ -58,8 +58,8 @@ class AlignOptionAction extends PopoverActionCell { Widget? leftIcon(Color iconColor) { return FlowySvg( align.svg, - size: const Size.square(12), - ).padding(all: 2.0); + size: const Size.square(18), + ); } @override @@ -102,7 +102,11 @@ class AlignOptionAction extends PopoverActionCell { return HoverButton( onTap: () => onTap(e.inner), itemHeight: ActionListSizes.itemHeight, - leftIcon: leftIcon, + leftIcon: SizedBox( + width: 16, + height: 16, + child: leftIcon, + ), name: e.name, rightIcon: rightIcon, ); @@ -115,7 +119,9 @@ class AlignOptionAction extends PopoverActionCell { return OptionAlignType.center; } final node = editorState.getNodeAtPath(selection.start.path); - final align = node?.attributes[blockComponentAlign]; + final align = node?.type == SimpleTableBlockKeys.type + ? node?.tableAlign.key + : node?.attributes[blockComponentAlign]; return OptionAlignType.fromString(align); } @@ -131,11 +137,20 @@ class AlignOptionAction extends PopoverActionCell { if (node == null) { return; } - final transaction = editorState.transaction; - transaction.updateNode(node, { - blockComponentAlign: align.name, - }); - await editorState.apply(transaction); + // the align attribute for simple table is not same as the align type, + // so we need to convert the align type to the align attribute + if (node.type == SimpleTableBlockKeys.type) { + await editorState.updateTableAlign( + tableNode: node, + align: TableAlign.fromString(align.name), + ); + } else { + final transaction = editorState.transaction; + transaction.updateNode(node, { + blockComponentAlign: align.name, + }); + await editorState.apply(transaction); + } } } diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/actions/option/option_actions.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/actions/option/option_actions.dart index 5a9389514b..14ec6773a6 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/actions/option/option_actions.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/actions/option/option_actions.dart @@ -46,6 +46,7 @@ enum EditorOptionActionType { case EditorOptionActionType.align: return { ImageBlockKeys.type, + SimpleTableBlockKeys.type, }; case EditorOptionActionType.depth: return { diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_cell_block_component.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_cell_block_component.dart index 4b79eee10a..5e6bbf2d70 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_cell_block_component.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_cell_block_component.dart @@ -348,7 +348,9 @@ class SimpleTableCellBlockWidgetState extends State Widget _buildTableActionMenu() { final tableNode = node.parentTableNode; - if (tableNode == null) { + + // the table action menu is only available on mobile platform. + if (tableNode == null || UniversalPlatform.isDesktop) { return const SizedBox.shrink(); } diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_operations/simple_table_node_extension.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_operations/simple_table_node_extension.dart index 683f9cbeea..a888db92a7 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_operations/simple_table_node_extension.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_operations/simple_table_node_extension.dart @@ -16,6 +16,13 @@ enum TableAlign { center, right; + static TableAlign fromString(String align) { + return TableAlign.values.firstWhere( + (e) => e.key.toLowerCase() == align.toLowerCase(), + orElse: () => TableAlign.left, + ); + } + String get name => switch (this) { TableAlign.left => 'Left', TableAlign.center => 'Center', @@ -808,4 +815,38 @@ extension TableNodeExtension on Node { } return children.last.getLastChildIndex(); } + + /// Get table align of column + /// + /// If one of the align is not same as the others, it will return TableAlign.left. + TableAlign get allColumnAlign { + final alignSet = columnAligns.values.toSet(); + if (alignSet.length == 1) { + return TableAlign.fromString(alignSet.first); + } + return TableAlign.left; + } + + /// Get table align of row + /// + /// If one of the align is not same as the others, it will return TableAlign.left. + TableAlign get allRowAlign { + final alignSet = rowAligns.values.toSet(); + if (alignSet.length == 1) { + return TableAlign.fromString(alignSet.first); + } + return TableAlign.left; + } + + /// Get table align of the table. + /// + /// If one of the align is not same as the others, it will return TableAlign.left. + TableAlign get tableAlign { + if (allColumnAlign != TableAlign.left) { + return allColumnAlign; + } else if (allRowAlign != TableAlign.left) { + return allRowAlign; + } + return TableAlign.left; + } } diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_operations/simple_table_style_operation.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_operations/simple_table_style_operation.dart index 2b6bd73e62..48ffaae3cd 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_operations/simple_table_style_operation.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/simple_table/simple_table_operations/simple_table_style_operation.dart @@ -173,7 +173,8 @@ extension TableOptionOperation on EditorState { for (var i = 0; i < tableNode.columnLength; i++) { attributes = attributes.mergeValues( SimpleTableBlockKeys.columnAligns, - attributes[SimpleTableBlockKeys.columnAligns], + attributes[SimpleTableBlockKeys.columnAligns] ?? + SimpleTableColumnAlignMap(), duplicatedEntry: MapEntry(i.toString(), align.key), ); } diff --git a/frontend/appflowy_flutter/test/unit_test/simple_table/simple_table_style_operation_test.dart b/frontend/appflowy_flutter/test/unit_test/simple_table/simple_table_style_operation_test.dart index 54b3b93660..940f03711a 100644 --- a/frontend/appflowy_flutter/test/unit_test/simple_table/simple_table_style_operation_test.dart +++ b/frontend/appflowy_flutter/test/unit_test/simple_table/simple_table_style_operation_test.dart @@ -174,5 +174,24 @@ void main() { '1': '0xFF0000FF', }); }); + + test('update table align', () async { + final (editorState, tableNode) = createEditorStateAndTable( + rowCount: 2, + columnCount: 3, + ); + + for (final align in [ + TableAlign.center, + TableAlign.right, + TableAlign.left, + ]) { + await editorState.updateTableAlign( + tableNode: tableNode, + align: align, + ); + expect(tableNode.tableAlign, align); + } + }); }); }