From 82effbf8e4e4f654b072e398a4c610e9d2b0271f Mon Sep 17 00:00:00 2001 From: Lucas Date: Tue, 5 Nov 2024 10:52:22 +0800 Subject: [PATCH] fix: toggle heading issues (#6714) * fix: pages overflow when selecting homepage * fix: toggle heading issues * Revert "fix: pages overflow when selecting homepage" This reverts commit 156882a9a7e039a38cd206306e19b94eb391f948. * chore: optimize code logic * fix: assertion in toggle list * fix: make the turn into menu and color menu exclusive * test: add toggle heading test --- ...cument_with_toggle_heading_block_test.dart | 46 +++++++++++++ .../lib/plugins/document/document_page.dart | 1 - .../presentation/editor_configuration.dart | 17 +++-- .../actions/block_action_option_button.dart | 57 ++++++++++----- .../actions/block_action_option_cubit.dart | 8 +++ .../actions/option/color_option_action.dart | 63 +++++++++++++---- .../option/turn_into_option_action.dart | 69 +++++++++++++++---- .../heading/heading_toolbar_item.dart | 49 ++++++++++--- .../shortcuts/heading_block_shortcuts.dart | 2 + .../toggle/toggle_block_component.dart | 60 +++++++++++----- frontend/appflowy_flutter/pubspec.lock | 4 +- frontend/appflowy_flutter/pubspec.yaml | 2 +- frontend/resources/translations/en.json | 2 +- 13 files changed, 298 insertions(+), 82 deletions(-) diff --git a/frontend/appflowy_flutter/integration_test/desktop/document/document_with_toggle_heading_block_test.dart b/frontend/appflowy_flutter/integration_test/desktop/document/document_with_toggle_heading_block_test.dart index dbbda93453..8eb47fc15f 100644 --- a/frontend/appflowy_flutter/integration_test/desktop/document/document_with_toggle_heading_block_test.dart +++ b/frontend/appflowy_flutter/integration_test/desktop/document/document_with_toggle_heading_block_test.dart @@ -1,6 +1,8 @@ import 'package:appflowy/generated/locale_keys.g.dart'; import 'package:appflowy/plugins/document/presentation/editor_plugins/plugins.dart'; +import 'package:appflowy_editor/appflowy_editor.dart'; import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; @@ -60,6 +62,50 @@ void main() { findsNWidgets(6), ); }); + + testWidgets('insert toggle heading and convert it to heading', + (tester) async { + await tester.initializeAppFlowy(); + await tester.tapAnonymousSignInButton(); + + await tester.createNewPageWithNameUnderParent( + name: 'toggle heading block test', + ); + + await tester.editor.tapLineOfEditorAt(0); + await tester.ime.insertText('# > $_heading1\n'); + await tester.simulateKeyEvent(LogicalKeyboardKey.enter); + await tester.ime.insertText('item 1'); + await tester.pumpAndSettle(); + + await tester.editor.updateSelection( + Selection( + start: Position(path: [0]), + end: Position(path: [0], offset: _heading1.length), + ), + ); + + await tester.tapButton(find.byType(HeadingPopup)); + await tester.pumpAndSettle(); + + expect( + find.byType(HeadingButton), + findsNWidgets(3), + ); + + // tap the H1 button + await tester.tapButton(find.byType(HeadingButton).at(0)); + await tester.pumpAndSettle(); + + final editorState = tester.editor.getCurrentEditorState(); + final node1 = editorState.document.nodeAtPath([0])!; + expect(node1.type, HeadingBlockKeys.type); + expect(node1.attributes[HeadingBlockKeys.level], 1); + + final node2 = editorState.document.nodeAtPath([1])!; + expect(node2.type, ParagraphBlockKeys.type); + expect(node2.delta!.toPlainText(), 'item 1'); + }); }); } diff --git a/frontend/appflowy_flutter/lib/plugins/document/document_page.dart b/frontend/appflowy_flutter/lib/plugins/document/document_page.dart index 4d7f1ce0fe..23be6c299c 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/document_page.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/document_page.dart @@ -231,7 +231,6 @@ class _DocumentPageState extends State final Path? path = _getPathFromAction(action, editorState); if (path != null) { - debugPrint('jump to block: $path'); editorState.updateSelectionWithReason( Selection.collapsed(Position(path: path)), ); 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 8bf7c997e4..0171bfc6b7 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_configuration.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_configuration.dart @@ -134,12 +134,19 @@ void _customBlockOptionActions( ); builder.actionBuilder = (context, state) { - final top = builder.configuration.padding(context.node).top; - final padding = context.node.type == HeadingBlockKeys.type - ? EdgeInsets.only(top: top + 8.0) - : EdgeInsets.only(top: top + 2.0); + double top = builder.configuration.padding(context.node).top; + final type = context.node.type; + final level = context.node.attributes[HeadingBlockKeys.level] ?? 0; + if ((type == HeadingBlockKeys.type || + type == ToggleListBlockKeys.type) && + level > 0) { + final offset = [14.0, 11.0, 8.0, 6.0, 4.0, 2.0]; + top += offset[level - 1]; + } else { + top += 2.0; + } return Padding( - padding: padding, + padding: EdgeInsets.only(top: top), child: BlockActionList( blockComponentContext: context, blockComponentState: state, diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/actions/block_action_option_button.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/actions/block_action_option_button.dart index 908a80e5bd..29dabd0da1 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/actions/block_action_option_button.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/actions/block_action_option_button.dart @@ -9,7 +9,7 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'drag_to_reorder/draggable_option_button.dart'; -class BlockOptionButton extends StatelessWidget { +class BlockOptionButton extends StatefulWidget { const BlockOptionButton({ super.key, required this.blockComponentContext, @@ -25,6 +25,16 @@ class BlockOptionButton extends StatelessWidget { final EditorState editorState; final Map blockComponentBuilder; + @override + State createState() => _BlockOptionButtonState(); +} + +class _BlockOptionButtonState extends State { + // the mutex is used to ensure that only one popover is open at a time + // for example, when the user is selecting the color, the turn into option + // should not be shown. + final mutex = PopoverMutex(); + @override Widget build(BuildContext context) { final direction = @@ -34,8 +44,8 @@ class BlockOptionButton extends StatelessWidget { : PopoverDirection.leftWithCenterAligned; return BlocProvider( create: (context) => BlockActionOptionCubit( - editorState: editorState, - blockComponentBuilder: blockComponentBuilder, + editorState: widget.editorState, + blockComponentBuilder: widget.blockComponentBuilder, ), child: BlocBuilder( builder: (context, _) => PopoverActionList( @@ -55,30 +65,41 @@ class BlockOptionButton extends StatelessWidget { ), buildChild: (controller) => DraggableOptionButton( controller: controller, - editorState: editorState, - blockComponentContext: blockComponentContext, - blockComponentBuilder: blockComponentBuilder, + editorState: widget.editorState, + blockComponentContext: widget.blockComponentContext, + blockComponentBuilder: widget.blockComponentBuilder, ), ), ), ); } + @override + void dispose() { + mutex.dispose(); + + super.dispose(); + } + List _buildPopoverActions(BuildContext context) { - return actions.map((e) { + return widget.actions.map((e) { switch (e) { case OptionAction.divider: return DividerOptionAction(); case OptionAction.color: - return ColorOptionAction(editorState: editorState); + return ColorOptionAction( + editorState: widget.editorState, + mutex: mutex, + ); case OptionAction.align: - return AlignOptionAction(editorState: editorState); + return AlignOptionAction(editorState: widget.editorState); case OptionAction.depth: - return DepthOptionAction(editorState: editorState); + return DepthOptionAction(editorState: widget.editorState); case OptionAction.turnInto: return TurnIntoOptionAction( - editorState: editorState, - blockComponentBuilder: blockComponentBuilder, + editorState: widget.editorState, + blockComponentBuilder: widget.blockComponentBuilder, + mutex: mutex, ); default: return OptionActionWrapper(e); @@ -88,15 +109,17 @@ class BlockOptionButton extends StatelessWidget { void _onPopoverBuilder() { keepEditorFocusNotifier.increase(); - blockComponentState.alwaysShowActions = true; + widget.blockComponentState.alwaysShowActions = true; } void _onPopoverClosed(BuildContext context) { WidgetsBinding.instance.addPostFrameCallback((timeStamp) { - editorState.selectionType = null; - editorState.selection = null; - blockComponentState.alwaysShowActions = false; + widget.editorState.selectionType = null; + widget.editorState.selection = null; + widget.blockComponentState.alwaysShowActions = false; }); + + PopoverContainer.maybeOf(context)?.closeAll(); } void _onActionSelected( @@ -110,7 +133,7 @@ class BlockOptionButton extends StatelessWidget { context.read().handleAction( action.inner, - blockComponentContext.node, + widget.blockComponentContext.node, ); controller.close(); } diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/actions/block_action_option_cubit.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/actions/block_action_option_cubit.dart index 9de77326b3..235d9d5efe 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/actions/block_action_option_cubit.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/actions/block_action_option_cubit.dart @@ -389,6 +389,14 @@ class BlockActionOptionCubit extends Cubit { ]); if (afterSelection != null) { transaction.afterSelection = afterSelection; + } else if (insertedNodes.isNotEmpty) { + // select the blocks + transaction.afterSelection = Selection( + start: Position(path: node.path.child(0)), + end: Position(path: node.path.child(insertedNodes.length - 1)), + ); + } else { + transaction.afterSelection = transaction.beforeSelection; } await editorState.apply(transaction); diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/actions/option/color_option_action.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/actions/option/color_option_action.dart index f9bcbc5ae6..72f806ec68 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/actions/option/color_option_action.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/actions/option/color_option_action.dart @@ -13,10 +13,12 @@ const optionActionColorDefaultColor = 'appflowy_theme_default_color'; class ColorOptionAction extends CustomActionCell { ColorOptionAction({ required this.editorState, + required this.mutex, }); final EditorState editorState; final PopoverController innerController = PopoverController(); + final PopoverMutex mutex; @override Widget buildWithContext( @@ -24,16 +26,49 @@ class ColorOptionAction extends CustomActionCell { PopoverController controller, PopoverMutex? mutex, ) { + return ColorOptionButton( + editorState: editorState, + mutex: this.mutex, + controller: controller, + ); + } +} + +class ColorOptionButton extends StatefulWidget { + const ColorOptionButton({ + super.key, + required this.editorState, + required this.mutex, + required this.controller, + }); + + final EditorState editorState; + final PopoverMutex mutex; + final PopoverController controller; + + @override + State createState() => _ColorOptionButtonState(); +} + +class _ColorOptionButtonState extends State { + final PopoverController innerController = PopoverController(); + bool isOpen = false; + + @override + Widget build(BuildContext context) { return AppFlowyPopover( asBarrier: true, controller: innerController, - mutex: mutex, - popupBuilder: (context) => _buildColorOptionMenu( - context, - controller, - ), + mutex: widget.mutex, + popupBuilder: (context) { + isOpen = true; + return _buildColorOptionMenu( + context, + widget.controller, + ); + }, + onClose: () => isOpen = false, direction: PopoverDirection.rightWithCenterAligned, - offset: const Offset(10, 0), animationDuration: Durations.short3, beginScaleFactor: 1.0, beginOpacity: 0.8, @@ -45,7 +80,9 @@ class ColorOptionAction extends CustomActionCell { ), name: LocaleKeys.document_plugins_optionAction_color.tr(), onTap: () { - innerController.show(); + if (!isOpen) { + innerController.show(); + } }, ), ); @@ -55,12 +92,12 @@ class ColorOptionAction extends CustomActionCell { BuildContext context, PopoverController controller, ) { - final selection = editorState.selection?.normalized; + final selection = widget.editorState.selection?.normalized; if (selection == null) { return const SizedBox.shrink(); } - final node = editorState.getNodeAtPath(selection.start.path); + final node = widget.editorState.getNodeAtPath(selection.start.path); if (node == null) { return const SizedBox.shrink(); } @@ -73,11 +110,11 @@ class ColorOptionAction extends CustomActionCell { Node node, PopoverController controller, ) { - final selection = editorState.selection?.normalized; + final selection = widget.editorState.selection?.normalized; if (selection == null) { return const SizedBox.shrink(); } - final node = editorState.getNodeAtPath(selection.start.path); + final node = widget.editorState.getNodeAtPath(selection.start.path); if (node == null) { return const SizedBox.shrink(); } @@ -110,11 +147,11 @@ class ColorOptionAction extends CustomActionCell { color: AFThemeExtension.of(context).onBackground, ), onTap: (option, index) async { - final transaction = editorState.transaction; + final transaction = widget.editorState.transaction; transaction.updateNode(node, { blockComponentBackgroundColor: option.id, }); - await editorState.apply(transaction); + await widget.editorState.apply(transaction); innerController.close(); controller.close(); diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/actions/option/turn_into_option_action.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/actions/option/turn_into_option_action.dart index cba15950e3..430542ae08 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/actions/option/turn_into_option_action.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/actions/option/turn_into_option_action.dart @@ -15,11 +15,13 @@ class TurnIntoOptionAction extends CustomActionCell { TurnIntoOptionAction({ required this.editorState, required this.blockComponentBuilder, + required this.mutex, }); final EditorState editorState; final Map blockComponentBuilder; final PopoverController innerController = PopoverController(); + final PopoverMutex mutex; @override Widget buildWithContext( @@ -27,21 +29,54 @@ class TurnIntoOptionAction extends CustomActionCell { PopoverController controller, PopoverMutex? mutex, ) { + return TurnInfoButton( + editorState: editorState, + blockComponentBuilder: blockComponentBuilder, + mutex: this.mutex, + ); + } +} + +class TurnInfoButton extends StatefulWidget { + const TurnInfoButton({ + super.key, + required this.editorState, + required this.blockComponentBuilder, + required this.mutex, + }); + + final EditorState editorState; + final Map blockComponentBuilder; + final PopoverMutex mutex; + + @override + State createState() => _TurnInfoButtonState(); +} + +class _TurnInfoButtonState extends State { + final PopoverController innerController = PopoverController(); + bool isOpen = false; + + @override + Widget build(BuildContext context) { return AppFlowyPopover( asBarrier: true, controller: innerController, - mutex: mutex, - popupBuilder: (context) => BlocProvider( - create: (_) => BlockActionOptionCubit( - editorState: editorState, - blockComponentBuilder: blockComponentBuilder, - ), - child: BlocBuilder( - builder: (context, _) => _buildTurnIntoOptionMenu(context), - ), - ), + mutex: widget.mutex, + popupBuilder: (context) { + isOpen = true; + return BlocProvider( + create: (context) => BlockActionOptionCubit( + editorState: widget.editorState, + blockComponentBuilder: widget.blockComponentBuilder, + ), + child: BlocBuilder( + builder: (context, _) => _buildTurnIntoOptionMenu(context), + ), + ); + }, + onClose: () => isOpen = false, direction: PopoverDirection.rightWithCenterAligned, - offset: const Offset(10, 0), animationDuration: Durations.short3, beginScaleFactor: 1.0, beginOpacity: 0.8, @@ -50,13 +85,17 @@ class TurnIntoOptionAction extends CustomActionCell { // todo(lucas): replace the svg with the correct one leftIcon: const FlowySvg(FlowySvgs.turninto_s), name: LocaleKeys.document_plugins_optionAction_turnInto.tr(), - onTap: innerController.show, + onTap: () { + if (!isOpen) { + innerController.show(); + } + }, ), ); } Widget _buildTurnIntoOptionMenu(BuildContext context) { - final selection = editorState.selection?.normalized; + final selection = widget.editorState.selection?.normalized; // the selection may not be collapsed, for example, if a block contains some children, // the selection will be the start from the current block and end at the last child block. // we should take care of this case: @@ -66,7 +105,7 @@ class TurnIntoOptionAction extends CustomActionCell { return const SizedBox.shrink(); } - final node = editorState.getNodeAtPath(selection.start.path); + final node = widget.editorState.getNodeAtPath(selection.start.path); if (node == null) { return const SizedBox.shrink(); } @@ -78,7 +117,7 @@ class TurnIntoOptionAction extends CustomActionCell { } bool _hasNonSupportedTypes(Selection selection) { - final nodes = editorState.getNodesInSelection(selection); + final nodes = widget.editorState.getNodesInSelection(selection); if (nodes.isEmpty) { return false; } diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/heading/heading_toolbar_item.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/heading/heading_toolbar_item.dart index 0337b901db..68fb3305de 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/heading/heading_toolbar_item.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/heading/heading_toolbar_item.dart @@ -49,7 +49,7 @@ final headingsToolbarItem = ToolbarItem( ], ), ); - return _HeadingPopup( + return HeadingPopup( currentLevel: isHighlight ? level : -1, highlightColor: highlightColor, child: child, @@ -60,9 +60,9 @@ final headingsToolbarItem = ToolbarItem( ? ParagraphBlockKeys.type : HeadingBlockKeys.type; - await editorState.formatNode( - selection, - (node) => node.copyWith( + if (type == HeadingBlockKeys.type) { + // from paragraph to heading + final newNode = node.copyWith( type: type, attributes: { HeadingBlockKeys.level: newLevel, @@ -72,15 +72,41 @@ final headingsToolbarItem = ToolbarItem( node.attributes[blockComponentTextDirection], blockComponentDelta: delta, }, - ), - ); + ); + final children = node.children.map((child) => child.copyWith()); + + final transaction = editorState.transaction; + transaction.insertNodes( + selection.start.path.next, + [newNode, ...children], + ); + transaction.deleteNode(node); + await editorState.apply(transaction); + } else { + // from heading to paragraph + await editorState.formatNode( + selection, + (node) => node.copyWith( + type: type, + attributes: { + HeadingBlockKeys.level: newLevel, + blockComponentBackgroundColor: + node.attributes[blockComponentBackgroundColor], + blockComponentTextDirection: + node.attributes[blockComponentTextDirection], + blockComponentDelta: delta, + }, + ), + ); + } }, ); }, ); -class _HeadingPopup extends StatelessWidget { - const _HeadingPopup({ +class HeadingPopup extends StatelessWidget { + const HeadingPopup({ + super.key, required this.currentLevel, required this.highlightColor, required this.onLevelChanged, @@ -144,7 +170,7 @@ class _HeadingButtons extends StatelessWidget { final svg = data.$1; final message = data.$2; return [ - _HeadingButton( + HeadingButton( icon: svg, tooltip: message, onTap: () => onLevelChanged(index + 1), @@ -163,8 +189,9 @@ class _HeadingButtons extends StatelessWidget { } } -class _HeadingButton extends StatelessWidget { - const _HeadingButton({ +class HeadingButton extends StatelessWidget { + const HeadingButton({ + super.key, required this.icon, required this.tooltip, required this.onTap, diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/shortcuts/heading_block_shortcuts.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/shortcuts/heading_block_shortcuts.dart index 6ecdd8a90e..7417aa9184 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/shortcuts/heading_block_shortcuts.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/shortcuts/heading_block_shortcuts.dart @@ -1,4 +1,5 @@ import 'package:appflowy/plugins/document/presentation/editor_plugins/plugins.dart'; +import 'package:appflowy/plugins/document/presentation/editor_plugins/toggle/toggle_block_component.dart'; import 'package:appflowy_editor/appflowy_editor.dart'; /// Convert '# ' to bulleted list @@ -35,6 +36,7 @@ CharacterShortcutEvent customFormatSignToHeading = CharacterShortcutEvent( level: numberOfSign, delta: delta.compose(Delta()..delete(numberOfSign)), collapsed: collapsed ?? false, + children: node.children.map((child) => child.copyWith()), ), ]; } diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/toggle/toggle_block_component.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/toggle/toggle_block_component.dart index 8dd7d25841..aab3181127 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/toggle/toggle_block_component.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/toggle/toggle_block_component.dart @@ -180,6 +180,31 @@ class _ToggleListBlockComponentWidgetState : buildComponentWithChildren(context); } + @override + Widget buildComponentWithChildren(BuildContext context) { + return Stack( + children: [ + if (backgroundColor != Colors.transparent) + Positioned.fill( + left: cachedLeft, + top: padding.top, + child: Container( + width: double.infinity, + color: backgroundColor, + ), + ), + NestedListWidget( + indentPadding: indentPadding, + child: buildComponent(context), + children: editorState.renderer.buildList( + context, + widget.node.children, + ), + ), + ], + ); + } + @override Widget buildComponent( BuildContext context, { @@ -190,9 +215,6 @@ class _ToggleListBlockComponentWidgetState ); Widget child = Container( - color: withBackgroundColor || backgroundColor != Colors.transparent - ? backgroundColor - : null, width: double.infinity, alignment: alignment, child: Row( @@ -208,12 +230,6 @@ class _ToggleListBlockComponentWidgetState ), ); - child = Padding( - key: blockComponentKey, - padding: padding, - child: child, - ); - child = BlockSelectionContainer( node: node, delegate: this, @@ -225,6 +241,18 @@ class _ToggleListBlockComponentWidgetState child: child, ); + child = Padding( + padding: padding, + child: Container( + key: blockComponentKey, + color: withBackgroundColor || + (backgroundColor != Colors.transparent && collapsed) + ? backgroundColor + : null, + child: child, + ), + ); + if (widget.showActions && widget.actionBuilder != null) { child = BlockComponentActionWrapper( node: node, @@ -293,16 +321,16 @@ class _ToggleListBlockComponentWidgetState minHeight: buttonHeight, ), padding: EdgeInsets.only(top: top, right: 4.0), - child: AnimatedRotation( - turns: collapsed ? 0.0 : 0.25, - duration: const Duration(milliseconds: 200), - child: FlowyIconButton( - width: 20.0, - icon: const Icon( + child: FlowyIconButton( + width: 20.0, + onPressed: onCollapsed, + icon: AnimatedRotation( + turns: collapsed ? 0.0 : 0.25, + duration: const Duration(milliseconds: 200), + child: const Icon( Icons.arrow_right, size: 18.0, ), - onPressed: onCollapsed, ), ), ); diff --git a/frontend/appflowy_flutter/pubspec.lock b/frontend/appflowy_flutter/pubspec.lock index 24305a7cff..c0f8465004 100644 --- a/frontend/appflowy_flutter/pubspec.lock +++ b/frontend/appflowy_flutter/pubspec.lock @@ -61,8 +61,8 @@ packages: dependency: "direct main" description: path: "." - ref: eccb244 - resolved-ref: eccb24452a451c734bc2aeae56c1fd81398d849c + ref: "2903792" + resolved-ref: "2903792fa319e1b4077164eeb684f6e8d1c63e27" url: "https://github.com/AppFlowy-IO/appflowy-editor.git" source: git version: "4.0.0" diff --git a/frontend/appflowy_flutter/pubspec.yaml b/frontend/appflowy_flutter/pubspec.yaml index 2d1a919333..8826e9d408 100644 --- a/frontend/appflowy_flutter/pubspec.yaml +++ b/frontend/appflowy_flutter/pubspec.yaml @@ -172,7 +172,7 @@ dependency_overrides: appflowy_editor: git: url: https://github.com/AppFlowy-IO/appflowy-editor.git - ref: "eccb244" + ref: "2903792" appflowy_editor_plugins: git: diff --git a/frontend/resources/translations/en.json b/frontend/resources/translations/en.json index 3d10c35a82..fc26fe7e60 100644 --- a/frontend/resources/translations/en.json +++ b/frontend/resources/translations/en.json @@ -2495,7 +2495,7 @@ "spaceIcon": "Icon", "dangerZone": "Danger Zone", "unableToDeleteLastSpace": "Unable to delete the last Space", - "unableToDeleteSpaceNotCreatedByYou": "Unable to delete Spaces created by others", + "unableToDeleteSpaceNotCreatedByYou": "Unable to delete spaces created by others", "enableSpacesForYourWorkspace": "Enable Spaces for your workspace", "title": "Spaces", "defaultSpaceName": "General",