mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
fix: remove keyword when click selection menu item
This commit is contained in:
@ -29,7 +29,7 @@ EXTERNAL SOURCES:
|
|||||||
:path: Flutter/ephemeral/.symlinks/plugins/wakelock_macos/macos
|
:path: Flutter/ephemeral/.symlinks/plugins/wakelock_macos/macos
|
||||||
|
|
||||||
SPEC CHECKSUMS:
|
SPEC CHECKSUMS:
|
||||||
FlutterMacOS: 57701585bf7de1b3fc2bb61f6378d73bbdea8424
|
FlutterMacOS: ae6af50a8ea7d6103d888583d46bd8328a7e9811
|
||||||
path_provider_macos: 3c0c3b4b0d4a76d2bf989a913c2de869c5641a19
|
path_provider_macos: 3c0c3b4b0d4a76d2bf989a913c2de869c5641a19
|
||||||
rich_clipboard_macos: 43364b66b9dc69d203eb8dd6d758e2d12e02723c
|
rich_clipboard_macos: 43364b66b9dc69d203eb8dd6d758e2d12e02723c
|
||||||
url_launcher_macos: 597e05b8e514239626bcf4a850fcf9ef5c856ec3
|
url_launcher_macos: 597e05b8e514239626bcf4a850fcf9ef5c856ec3
|
||||||
|
@ -37,7 +37,7 @@ class SelectionMenuItemWidget extends StatelessWidget {
|
|||||||
: MaterialStateProperty.all(Colors.transparent),
|
: MaterialStateProperty.all(Colors.transparent),
|
||||||
),
|
),
|
||||||
label: Text(
|
label: Text(
|
||||||
item.name,
|
item.name(),
|
||||||
textAlign: TextAlign.left,
|
textAlign: TextAlign.left,
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
color: Colors.black,
|
color: Colors.black,
|
||||||
|
@ -124,7 +124,7 @@ List<SelectionMenuItem> get defaultSelectionMenuItems =>
|
|||||||
_defaultSelectionMenuItems;
|
_defaultSelectionMenuItems;
|
||||||
final List<SelectionMenuItem> _defaultSelectionMenuItems = [
|
final List<SelectionMenuItem> _defaultSelectionMenuItems = [
|
||||||
SelectionMenuItem(
|
SelectionMenuItem(
|
||||||
name: AppFlowyEditorLocalizations.current.text,
|
name: () => AppFlowyEditorLocalizations.current.text,
|
||||||
icon: _selectionMenuIcon('text'),
|
icon: _selectionMenuIcon('text'),
|
||||||
keywords: ['text'],
|
keywords: ['text'],
|
||||||
handler: (editorState, _, __) {
|
handler: (editorState, _, __) {
|
||||||
@ -132,7 +132,7 @@ final List<SelectionMenuItem> _defaultSelectionMenuItems = [
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
SelectionMenuItem(
|
SelectionMenuItem(
|
||||||
name: AppFlowyEditorLocalizations.current.heading1,
|
name: () => AppFlowyEditorLocalizations.current.heading1,
|
||||||
icon: _selectionMenuIcon('h1'),
|
icon: _selectionMenuIcon('h1'),
|
||||||
keywords: ['heading 1, h1'],
|
keywords: ['heading 1, h1'],
|
||||||
handler: (editorState, _, __) {
|
handler: (editorState, _, __) {
|
||||||
@ -140,7 +140,7 @@ final List<SelectionMenuItem> _defaultSelectionMenuItems = [
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
SelectionMenuItem(
|
SelectionMenuItem(
|
||||||
name: AppFlowyEditorLocalizations.current.heading2,
|
name: () => AppFlowyEditorLocalizations.current.heading2,
|
||||||
icon: _selectionMenuIcon('h2'),
|
icon: _selectionMenuIcon('h2'),
|
||||||
keywords: ['heading 2, h2'],
|
keywords: ['heading 2, h2'],
|
||||||
handler: (editorState, _, __) {
|
handler: (editorState, _, __) {
|
||||||
@ -148,7 +148,7 @@ final List<SelectionMenuItem> _defaultSelectionMenuItems = [
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
SelectionMenuItem(
|
SelectionMenuItem(
|
||||||
name: AppFlowyEditorLocalizations.current.heading3,
|
name: () => AppFlowyEditorLocalizations.current.heading3,
|
||||||
icon: _selectionMenuIcon('h3'),
|
icon: _selectionMenuIcon('h3'),
|
||||||
keywords: ['heading 3, h3'],
|
keywords: ['heading 3, h3'],
|
||||||
handler: (editorState, _, __) {
|
handler: (editorState, _, __) {
|
||||||
@ -156,13 +156,13 @@ final List<SelectionMenuItem> _defaultSelectionMenuItems = [
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
SelectionMenuItem(
|
SelectionMenuItem(
|
||||||
name: AppFlowyEditorLocalizations.current.image,
|
name: () => AppFlowyEditorLocalizations.current.image,
|
||||||
icon: _selectionMenuIcon('image'),
|
icon: _selectionMenuIcon('image'),
|
||||||
keywords: ['image'],
|
keywords: ['image'],
|
||||||
handler: showImageUploadMenu,
|
handler: showImageUploadMenu,
|
||||||
),
|
),
|
||||||
SelectionMenuItem(
|
SelectionMenuItem(
|
||||||
name: AppFlowyEditorLocalizations.current.bulletedList,
|
name: () => AppFlowyEditorLocalizations.current.bulletedList,
|
||||||
icon: _selectionMenuIcon('bulleted_list'),
|
icon: _selectionMenuIcon('bulleted_list'),
|
||||||
keywords: ['bulleted list', 'list', 'unordered list'],
|
keywords: ['bulleted list', 'list', 'unordered list'],
|
||||||
handler: (editorState, _, __) {
|
handler: (editorState, _, __) {
|
||||||
@ -170,7 +170,7 @@ final List<SelectionMenuItem> _defaultSelectionMenuItems = [
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
SelectionMenuItem(
|
SelectionMenuItem(
|
||||||
name: AppFlowyEditorLocalizations.current.checkbox,
|
name: () => AppFlowyEditorLocalizations.current.checkbox,
|
||||||
icon: _selectionMenuIcon('checkbox'),
|
icon: _selectionMenuIcon('checkbox'),
|
||||||
keywords: ['todo list', 'list', 'checkbox list'],
|
keywords: ['todo list', 'list', 'checkbox list'],
|
||||||
handler: (editorState, _, __) {
|
handler: (editorState, _, __) {
|
||||||
@ -178,7 +178,7 @@ final List<SelectionMenuItem> _defaultSelectionMenuItems = [
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
SelectionMenuItem(
|
SelectionMenuItem(
|
||||||
name: AppFlowyEditorLocalizations.current.quote,
|
name: () => AppFlowyEditorLocalizations.current.quote,
|
||||||
icon: _selectionMenuIcon('quote'),
|
icon: _selectionMenuIcon('quote'),
|
||||||
keywords: ['quote', 'refer'],
|
keywords: ['quote', 'refer'],
|
||||||
handler: (editorState, _, __) {
|
handler: (editorState, _, __) {
|
||||||
|
@ -6,27 +6,54 @@ import 'package:appflowy_editor/src/render/selection_menu/selection_menu_service
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
|
|
||||||
|
typedef SelectionMenuItemHandler = void Function(
|
||||||
|
EditorState editorState,
|
||||||
|
SelectionMenuService menuService,
|
||||||
|
BuildContext context,
|
||||||
|
);
|
||||||
|
|
||||||
/// Selection Menu Item
|
/// Selection Menu Item
|
||||||
class SelectionMenuItem {
|
class SelectionMenuItem {
|
||||||
SelectionMenuItem({
|
SelectionMenuItem({
|
||||||
required this.name,
|
required this.name,
|
||||||
required this.icon,
|
required this.icon,
|
||||||
required this.keywords,
|
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;
|
final Widget icon;
|
||||||
|
|
||||||
/// Customizes keywords for item.
|
/// Customizes keywords for item.
|
||||||
///
|
///
|
||||||
/// The keywords are used to quickly retrieve items.
|
/// The keywords are used to quickly retrieve items.
|
||||||
final List<String> keywords;
|
final List<String> keywords;
|
||||||
final void Function(
|
late final SelectionMenuItemHandler handler;
|
||||||
EditorState editorState,
|
|
||||||
SelectionMenuService menuService,
|
void _deleteToSlash(EditorState editorState) {
|
||||||
BuildContext context,
|
final selectionService = editorState.service.selectionService;
|
||||||
) handler;
|
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 {
|
class SelectionMenuWidget extends StatefulWidget {
|
||||||
@ -204,11 +231,8 @@ class _SelectionMenuWidgetState extends State<SelectionMenuWidget> {
|
|||||||
|
|
||||||
if (event.logicalKey == LogicalKeyboardKey.enter) {
|
if (event.logicalKey == LogicalKeyboardKey.enter) {
|
||||||
if (0 <= _selectedIndex && _selectedIndex < _showingItems.length) {
|
if (0 <= _selectedIndex && _selectedIndex < _showingItems.length) {
|
||||||
_deleteLastCharacters(length: keyword.length + 1);
|
_showingItems[_selectedIndex]
|
||||||
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
|
.handler(widget.editorState, widget.menuService, context);
|
||||||
_showingItems[_selectedIndex]
|
|
||||||
.handler(widget.editorState, widget.menuService, context);
|
|
||||||
});
|
|
||||||
return KeyEventResult.handled;
|
return KeyEventResult.handled;
|
||||||
}
|
}
|
||||||
} else if (event.logicalKey == LogicalKeyboardKey.escape) {
|
} else if (event.logicalKey == LogicalKeyboardKey.escape) {
|
||||||
|
@ -17,7 +17,7 @@ void main() async {
|
|||||||
final menuService = _TestSelectionMenuService();
|
final menuService = _TestSelectionMenuService();
|
||||||
const icon = Icon(Icons.abc);
|
const icon = Icon(Icons.abc);
|
||||||
final item = SelectionMenuItem(
|
final item = SelectionMenuItem(
|
||||||
name: 'example',
|
name: () => 'example',
|
||||||
icon: icon,
|
icon: icon,
|
||||||
keywords: ['example A', 'example B'],
|
keywords: ['example A', 'example B'],
|
||||||
handler: (editorState, menuService, context) {
|
handler: (editorState, menuService, context) {
|
||||||
|
@ -13,29 +13,40 @@ void main() async {
|
|||||||
});
|
});
|
||||||
|
|
||||||
group('selection_menu_widget.dart', () {
|
group('selection_menu_widget.dart', () {
|
||||||
// const i = defaultSelectionMenuItems.length;
|
for (var i = 0; i < defaultSelectionMenuItems.length; i += 1) {
|
||||||
//
|
testWidgets('Selects number.$i item in selection menu with enter', (
|
||||||
// Because the `defaultSelectionMenuItems` uses localization,
|
tester) async {
|
||||||
// and the MaterialApp has not been initialized at the time of getting the value,
|
final editor = await _prepare(tester);
|
||||||
// it will crash.
|
for (var j = 0; j < i; j++) {
|
||||||
//
|
await editor.pressLogicKey(LogicalKeyboardKey.arrowDown);
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
await editor.pressLogicKey(LogicalKeyboardKey.enter);
|
await editor.pressLogicKey(LogicalKeyboardKey.enter);
|
||||||
expect(
|
expect(
|
||||||
find.byType(SelectionMenuWidget, skipOffstage: false),
|
find.byType(SelectionMenuWidget, skipOffstage: false),
|
||||||
findsNothing,
|
findsNothing,
|
||||||
);
|
);
|
||||||
if (defaultSelectionMenuItems[i].name != 'Image') {
|
if (defaultSelectionMenuItems[i].name() != 'Image') {
|
||||||
await _testDefaultSelectionMenuItems(i, editor);
|
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',
|
testWidgets('Search item in selection menu util no results',
|
||||||
(tester) async {
|
(tester) async {
|
||||||
@ -138,23 +149,27 @@ Future<void> _testDefaultSelectionMenuItems(
|
|||||||
int index, EditorWidgetTester editor) async {
|
int index, EditorWidgetTester editor) async {
|
||||||
expect(editor.documentLength, 4);
|
expect(editor.documentLength, 4);
|
||||||
expect(editor.documentSelection, Selection.single(path: [2], startOffset: 0));
|
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 node = editor.nodeAtPath([2]);
|
||||||
final item = defaultSelectionMenuItems[index];
|
final item = defaultSelectionMenuItems[index];
|
||||||
if (item.name == 'Text') {
|
final itemName = item.name();
|
||||||
|
if (itemName == 'Text') {
|
||||||
expect(node?.subtype == null, true);
|
expect(node?.subtype == null, true);
|
||||||
} else if (item.name == 'Heading 1') {
|
} else if (itemName == 'Heading 1') {
|
||||||
expect(node?.subtype, BuiltInAttributeKey.heading);
|
expect(node?.subtype, BuiltInAttributeKey.heading);
|
||||||
expect(node?.attributes.heading, BuiltInAttributeKey.h1);
|
expect(node?.attributes.heading, BuiltInAttributeKey.h1);
|
||||||
} else if (item.name == 'Heading 2') {
|
} else if (itemName == 'Heading 2') {
|
||||||
expect(node?.subtype, BuiltInAttributeKey.heading);
|
expect(node?.subtype, BuiltInAttributeKey.heading);
|
||||||
expect(node?.attributes.heading, BuiltInAttributeKey.h2);
|
expect(node?.attributes.heading, BuiltInAttributeKey.h2);
|
||||||
} else if (item.name == 'Heading 3') {
|
} else if (itemName == 'Heading 3') {
|
||||||
expect(node?.subtype, BuiltInAttributeKey.heading);
|
expect(node?.subtype, BuiltInAttributeKey.heading);
|
||||||
expect(node?.attributes.heading, BuiltInAttributeKey.h3);
|
expect(node?.attributes.heading, BuiltInAttributeKey.h3);
|
||||||
} else if (item.name == 'Bulleted list') {
|
} else if (itemName == 'Bulleted list') {
|
||||||
expect(node?.subtype, BuiltInAttributeKey.bulletedList);
|
expect(node?.subtype, BuiltInAttributeKey.bulletedList);
|
||||||
} else if (item.name == 'Checkbox') {
|
} else if (itemName == 'Checkbox') {
|
||||||
expect(node?.subtype, BuiltInAttributeKey.checkbox);
|
expect(node?.subtype, BuiltInAttributeKey.checkbox);
|
||||||
expect(node?.attributes.check, false);
|
expect(node?.attributes.check, false);
|
||||||
|
} else if (itemName == 'Quote') {
|
||||||
|
expect(node?.subtype, BuiltInAttributeKey.quote);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user