Переглянути джерело

Merge pull request #1133 from flappyBug/fix/click-selection-menu-item-delete-text

fix: remove keyword when click selection menu item
Lucas.Xu 2 роки тому
батько
коміт
5e7a54183a

+ 1 - 1
frontend/app_flowy/packages/appflowy_editor/example/macos/Podfile.lock

@@ -24,7 +24,7 @@ EXTERNAL SOURCES:
     :path: Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos
 
 SPEC CHECKSUMS:
-  FlutterMacOS: 57701585bf7de1b3fc2bb61f6378d73bbdea8424
+  FlutterMacOS: ae6af50a8ea7d6103d888583d46bd8328a7e9811
   path_provider_macos: 3c0c3b4b0d4a76d2bf989a913c2de869c5641a19
   rich_clipboard_macos: 43364b66b9dc69d203eb8dd6d758e2d12e02723c
   url_launcher_macos: 597e05b8e514239626bcf4a850fcf9ef5c856ec3

+ 1 - 1
frontend/app_flowy/packages/appflowy_editor/lib/src/render/selection_menu/selection_menu_item_widget.dart

@@ -37,7 +37,7 @@ class SelectionMenuItemWidget extends StatelessWidget {
                 : MaterialStateProperty.all(Colors.transparent),
           ),
           label: Text(
-            item.name,
+            item.name(),
             textAlign: TextAlign.left,
             style: const TextStyle(
               color: Colors.black,

+ 8 - 8
frontend/app_flowy/packages/appflowy_editor/lib/src/render/selection_menu/selection_menu_service.dart

@@ -124,7 +124,7 @@ List<SelectionMenuItem> get defaultSelectionMenuItems =>
     _defaultSelectionMenuItems;
 final List<SelectionMenuItem> _defaultSelectionMenuItems = [
   SelectionMenuItem(
-    name: AppFlowyEditorLocalizations.current.text,
+    name: () => AppFlowyEditorLocalizations.current.text,
     icon: _selectionMenuIcon('text'),
     keywords: ['text'],
     handler: (editorState, _, __) {
@@ -132,7 +132,7 @@ final List<SelectionMenuItem> _defaultSelectionMenuItems = [
     },
   ),
   SelectionMenuItem(
-    name: AppFlowyEditorLocalizations.current.heading1,
+    name: () => AppFlowyEditorLocalizations.current.heading1,
     icon: _selectionMenuIcon('h1'),
     keywords: ['heading 1, h1'],
     handler: (editorState, _, __) {
@@ -140,7 +140,7 @@ final List<SelectionMenuItem> _defaultSelectionMenuItems = [
     },
   ),
   SelectionMenuItem(
-    name: AppFlowyEditorLocalizations.current.heading2,
+    name: () => AppFlowyEditorLocalizations.current.heading2,
     icon: _selectionMenuIcon('h2'),
     keywords: ['heading 2, h2'],
     handler: (editorState, _, __) {
@@ -148,7 +148,7 @@ final List<SelectionMenuItem> _defaultSelectionMenuItems = [
     },
   ),
   SelectionMenuItem(
-    name: AppFlowyEditorLocalizations.current.heading3,
+    name: () => AppFlowyEditorLocalizations.current.heading3,
     icon: _selectionMenuIcon('h3'),
     keywords: ['heading 3, h3'],
     handler: (editorState, _, __) {
@@ -156,13 +156,13 @@ final List<SelectionMenuItem> _defaultSelectionMenuItems = [
     },
   ),
   SelectionMenuItem(
-    name: AppFlowyEditorLocalizations.current.image,
+    name: () => AppFlowyEditorLocalizations.current.image,
     icon: _selectionMenuIcon('image'),
     keywords: ['image'],
     handler: showImageUploadMenu,
   ),
   SelectionMenuItem(
-    name: AppFlowyEditorLocalizations.current.bulletedList,
+    name: () => AppFlowyEditorLocalizations.current.bulletedList,
     icon: _selectionMenuIcon('bulleted_list'),
     keywords: ['bulleted list', 'list', 'unordered list'],
     handler: (editorState, _, __) {
@@ -170,7 +170,7 @@ final List<SelectionMenuItem> _defaultSelectionMenuItems = [
     },
   ),
   SelectionMenuItem(
-    name: AppFlowyEditorLocalizations.current.checkbox,
+    name: () => AppFlowyEditorLocalizations.current.checkbox,
     icon: _selectionMenuIcon('checkbox'),
     keywords: ['todo list', 'list', 'checkbox list'],
     handler: (editorState, _, __) {
@@ -178,7 +178,7 @@ final List<SelectionMenuItem> _defaultSelectionMenuItems = [
     },
   ),
   SelectionMenuItem(
-    name: AppFlowyEditorLocalizations.current.quote,
+    name: () => AppFlowyEditorLocalizations.current.quote,
     icon: _selectionMenuIcon('quote'),
     keywords: ['quote', 'refer'],
     handler: (editorState, _, __) {

+ 37 - 13
frontend/app_flowy/packages/appflowy_editor/lib/src/render/selection_menu/selection_menu_widget.dart

@@ -6,27 +6,54 @@ import 'package:appflowy_editor/src/render/selection_menu/selection_menu_service
 import 'package:flutter/material.dart';
 import 'package:flutter/services.dart';
 
+typedef SelectionMenuItemHandler = void Function(
+    EditorState editorState,
+    SelectionMenuService menuService,
+    BuildContext context,
+    );
+
 /// Selection Menu Item
 class SelectionMenuItem {
   SelectionMenuItem({
     required this.name,
     required this.icon,
     required this.keywords,
-    required this.handler,
-  });
+    required SelectionMenuItemHandler handler,
+  }) {
+    this.handler = (editorState, menuService, context) {
+      _deleteToSlash(editorState);
+      WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
+            handler(editorState, menuService, context);
+      });
+    };
+  }
 
-  final String name;
+  final String Function() name;
   final Widget icon;
 
   /// Customizes keywords for item.
   ///
   /// The keywords are used to quickly retrieve items.
   final List<String> keywords;
-  final void Function(
-    EditorState editorState,
-    SelectionMenuService menuService,
-    BuildContext context,
-  ) handler;
+  late final SelectionMenuItemHandler handler;
+
+  void _deleteToSlash(EditorState editorState) {
+    final selectionService = editorState.service.selectionService;
+    final selection = selectionService.currentSelection.value;
+    final nodes = selectionService.currentSelectedNodes;
+    if (selection != null && nodes.length == 1) {
+      final node = nodes.first as TextNode;
+      final end = selection.start.offset;
+      final start = node.toRawString().substring(0, end).lastIndexOf('/');
+      TransactionBuilder(editorState)
+        ..deleteText(
+          node,
+          start,
+          selection.start.offset - start,
+        )
+        ..commit();
+    }
+  }
 }
 
 class SelectionMenuWidget extends StatefulWidget {
@@ -204,11 +231,8 @@ class _SelectionMenuWidgetState extends State<SelectionMenuWidget> {
 
     if (event.logicalKey == LogicalKeyboardKey.enter) {
       if (0 <= _selectedIndex && _selectedIndex < _showingItems.length) {
-        _deleteLastCharacters(length: keyword.length + 1);
-        WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
-          _showingItems[_selectedIndex]
-              .handler(widget.editorState, widget.menuService, context);
-        });
+        _showingItems[_selectedIndex]
+            .handler(widget.editorState, widget.menuService, context);
         return KeyEventResult.handled;
       }
     } else if (event.logicalKey == LogicalKeyboardKey.escape) {

+ 43 - 28
frontend/app_flowy/packages/appflowy_editor/test/render/selection_menu/selection_menu_widget_test.dart

@@ -12,29 +12,40 @@ void main() async {
   });
 
   group('selection_menu_widget.dart', () {
-    // const i = defaultSelectionMenuItems.length;
-    //
-    // Because the `defaultSelectionMenuItems` uses localization,
-    // and the MaterialApp has not been initialized at the time of getting the value,
-    // it will crash.
-    //
-    // Use const value temporarily instead.
-    const i = 7;
-    testWidgets('Selects number.$i item in selection menu', (tester) async {
-      final editor = await _prepare(tester);
-      for (var j = 0; j < i; j++) {
-        await editor.pressLogicKey(LogicalKeyboardKey.arrowDown);
-      }
+    for (var i = 0; i < defaultSelectionMenuItems.length; i += 1) {
+      testWidgets('Selects number.$i item in selection menu with enter', (
+          tester) async {
+        final editor = await _prepare(tester);
+        for (var j = 0; j < i; j++) {
+          await editor.pressLogicKey(LogicalKeyboardKey.arrowDown);
+        }
 
-      await editor.pressLogicKey(LogicalKeyboardKey.enter);
-      expect(
-        find.byType(SelectionMenuWidget, skipOffstage: false),
-        findsNothing,
-      );
-      if (defaultSelectionMenuItems[i].name != 'Image') {
-        await _testDefaultSelectionMenuItems(i, editor);
-      }
-    });
+        await editor.pressLogicKey(LogicalKeyboardKey.enter);
+        expect(
+          find.byType(SelectionMenuWidget, skipOffstage: false),
+          findsNothing,
+        );
+        if (defaultSelectionMenuItems[i].name() != 'Image') {
+          await _testDefaultSelectionMenuItems(i, editor);
+        }
+      });
+
+      testWidgets('Selects number.$i item in selection menu with click', (
+          tester) async {
+        final editor = await _prepare(tester);
+
+        await tester.tap(find.byType(SelectionMenuItemWidget).at(i));
+        await tester.pumpAndSettle();
+
+        expect(
+          find.byType(SelectionMenuWidget, skipOffstage: false),
+          findsNothing,
+        );
+        if (defaultSelectionMenuItems[i].name() != 'Image') {
+          await _testDefaultSelectionMenuItems(i, editor);
+        }
+      });
+    }
 
     testWidgets('Search item in selection menu util no results',
         (tester) async {
@@ -137,23 +148,27 @@ Future<void> _testDefaultSelectionMenuItems(
     int index, EditorWidgetTester editor) async {
   expect(editor.documentLength, 4);
   expect(editor.documentSelection, Selection.single(path: [2], startOffset: 0));
+  expect((editor.nodeAtPath([1]) as TextNode).toRawString(), 'Welcome to Appflowy 😁');
   final node = editor.nodeAtPath([2]);
   final item = defaultSelectionMenuItems[index];
-  if (item.name == 'Text') {
+  final itemName = item.name();
+  if (itemName == 'Text') {
     expect(node?.subtype == null, true);
-  } else if (item.name == 'Heading 1') {
+  } else if (itemName == 'Heading 1') {
     expect(node?.subtype, BuiltInAttributeKey.heading);
     expect(node?.attributes.heading, BuiltInAttributeKey.h1);
-  } else if (item.name == 'Heading 2') {
+  } else if (itemName == 'Heading 2') {
     expect(node?.subtype, BuiltInAttributeKey.heading);
     expect(node?.attributes.heading, BuiltInAttributeKey.h2);
-  } else if (item.name == 'Heading 3') {
+  } else if (itemName == 'Heading 3') {
     expect(node?.subtype, BuiltInAttributeKey.heading);
     expect(node?.attributes.heading, BuiltInAttributeKey.h3);
-  } else if (item.name == 'Bulleted list') {
+  } else if (itemName == 'Bulleted list') {
     expect(node?.subtype, BuiltInAttributeKey.bulletedList);
-  } else if (item.name == 'Checkbox') {
+  } else if (itemName == 'Checkbox') {
     expect(node?.subtype, BuiltInAttributeKey.checkbox);
     expect(node?.attributes.check, false);
+  } else if (itemName == 'Quote') {
+    expect(node?.subtype, BuiltInAttributeKey.quote);
   }
 }