mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
feat: #1061 Support markdown to create a blockquote
This commit is contained in:
parent
53118a6c51
commit
b2bc59c6a1
@ -188,23 +188,6 @@ ShortcutEventHandler doubleTildeToStrikethrough = (editorState, event) {
|
|||||||
return KeyEventResult.handled;
|
return KeyEventResult.handled;
|
||||||
};
|
};
|
||||||
|
|
||||||
ShortcutEventHandler greaterToBlockquote = (editorState, event) {
|
|
||||||
final selectionService = editorState.service.selectionService;
|
|
||||||
final selection = selectionService.currentSelection.value;
|
|
||||||
final textNodes = selectionService.currentSelectedNodes.whereType<TextNode>();
|
|
||||||
if (selection == null || !selection.isSingle || textNodes.length != 1) {
|
|
||||||
return KeyEventResult.ignored;
|
|
||||||
}
|
|
||||||
|
|
||||||
//only convert > at the start of a paragraph
|
|
||||||
if (selection.startIndex != 0) {
|
|
||||||
return KeyEventResult.ignored;
|
|
||||||
}
|
|
||||||
formatQuote(editorState);
|
|
||||||
|
|
||||||
return KeyEventResult.handled;
|
|
||||||
};
|
|
||||||
|
|
||||||
ShortcutEventHandler markdownLinkOrImageHandler = (editorState, event) {
|
ShortcutEventHandler markdownLinkOrImageHandler = (editorState, event) {
|
||||||
final selectionService = editorState.service.selectionService;
|
final selectionService = editorState.service.selectionService;
|
||||||
final selection = selectionService.currentSelection.value;
|
final selection = selectionService.currentSelection.value;
|
||||||
|
@ -20,6 +20,8 @@ const _bulletedListSymbols = ['*', '-'];
|
|||||||
const _checkboxListSymbols = ['[x]', '-[x]'];
|
const _checkboxListSymbols = ['[x]', '-[x]'];
|
||||||
const _unCheckboxListSymbols = ['[]', '-[]'];
|
const _unCheckboxListSymbols = ['[]', '-[]'];
|
||||||
|
|
||||||
|
const _quoteSymbols = ['>'];
|
||||||
|
|
||||||
final _numberRegex = RegExp(r'^(\d+)\.');
|
final _numberRegex = RegExp(r'^(\d+)\.');
|
||||||
|
|
||||||
ShortcutEventHandler whiteSpaceHandler = (editorState, event) {
|
ShortcutEventHandler whiteSpaceHandler = (editorState, event) {
|
||||||
@ -49,6 +51,8 @@ ShortcutEventHandler whiteSpaceHandler = (editorState, event) {
|
|||||||
return _toBulletedList(editorState, textNode);
|
return _toBulletedList(editorState, textNode);
|
||||||
} else if (_countOfSign(text, selection) != 0) {
|
} else if (_countOfSign(text, selection) != 0) {
|
||||||
return _toHeadingStyle(editorState, textNode, selection);
|
return _toHeadingStyle(editorState, textNode, selection);
|
||||||
|
} else if (_quoteSymbols.contains(text)) {
|
||||||
|
return _toQuoteStyle(editorState, textNode);
|
||||||
} else if (numberMatch != null) {
|
} else if (numberMatch != null) {
|
||||||
final matchText = numberMatch.group(0);
|
final matchText = numberMatch.group(0);
|
||||||
final numText = numberMatch.group(1);
|
final numText = numberMatch.group(1);
|
||||||
@ -196,3 +200,22 @@ int _countOfSign(String text, Selection selection) {
|
|||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
KeyEventResult _toQuoteStyle(EditorState editorState, TextNode textNode) {
|
||||||
|
if (textNode.subtype == BuiltInAttributeKey.quote) {
|
||||||
|
return KeyEventResult.ignored;
|
||||||
|
}
|
||||||
|
final transaction = editorState.transaction
|
||||||
|
..deleteText(textNode, 0, 1)
|
||||||
|
..updateNode(textNode, {
|
||||||
|
BuiltInAttributeKey.subtype: BuiltInAttributeKey.quote,
|
||||||
|
})
|
||||||
|
..afterSelection = Selection.collapsed(
|
||||||
|
Position(
|
||||||
|
path: textNode.path,
|
||||||
|
offset: 0,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
editorState.apply(transaction);
|
||||||
|
return KeyEventResult.handled;
|
||||||
|
}
|
||||||
|
@ -285,11 +285,6 @@ List<ShortcutEvent> builtInShortcutEvents = [
|
|||||||
command: 'shift+tilde',
|
command: 'shift+tilde',
|
||||||
handler: doubleTildeToStrikethrough,
|
handler: doubleTildeToStrikethrough,
|
||||||
),
|
),
|
||||||
ShortcutEvent(
|
|
||||||
key: 'Greater to blockquote',
|
|
||||||
command: 'shift+greater',
|
|
||||||
handler: greaterToBlockquote,
|
|
||||||
),
|
|
||||||
ShortcutEvent(
|
ShortcutEvent(
|
||||||
key: 'Markdown link or image',
|
key: 'Markdown link or image',
|
||||||
command: 'shift+parenthesis right',
|
command: 'shift+parenthesis right',
|
||||||
|
@ -148,9 +148,6 @@ extension on LogicalKeyboardKey {
|
|||||||
if (this == LogicalKeyboardKey.tilde) {
|
if (this == LogicalKeyboardKey.tilde) {
|
||||||
return PhysicalKeyboardKey.backquote;
|
return PhysicalKeyboardKey.backquote;
|
||||||
}
|
}
|
||||||
if (this == LogicalKeyboardKey.greater) {
|
|
||||||
return PhysicalKeyboardKey.intlBackslash;
|
|
||||||
}
|
|
||||||
throw UnimplementedError();
|
throw UnimplementedError();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -256,76 +256,5 @@ void main() async {
|
|||||||
expect(textNode.toPlainText(), text);
|
expect(textNode.toPlainText(), text);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
group('convert geater to blockquote', () {
|
|
||||||
Future<void> insertGreater(
|
|
||||||
EditorWidgetTester editor, {
|
|
||||||
int repeat = 1,
|
|
||||||
}) async {
|
|
||||||
for (var i = 0; i < repeat; i++) {
|
|
||||||
await editor.pressLogicKey(
|
|
||||||
LogicalKeyboardKey.greater,
|
|
||||||
isShiftPressed: true,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
testWidgets('>AppFlowy to blockquote AppFlowy', (tester) async {
|
|
||||||
const text = 'AppFlowy';
|
|
||||||
final editor = tester.editor..insertTextNode('');
|
|
||||||
await editor.startTesting();
|
|
||||||
await editor.updateSelection(
|
|
||||||
Selection.single(path: [0], startOffset: 0),
|
|
||||||
);
|
|
||||||
await insertGreater(editor);
|
|
||||||
final textNode = editor.nodeAtPath([0]) as TextNode;
|
|
||||||
for (var i = 0; i < text.length; i++) {
|
|
||||||
await editor.insertText(textNode, text[i], i);
|
|
||||||
}
|
|
||||||
|
|
||||||
final isQuote = textNode.subtype == BuiltInAttributeKey.quote;
|
|
||||||
expect(isQuote, true);
|
|
||||||
expect(textNode.toPlainText(), 'AppFlowy');
|
|
||||||
});
|
|
||||||
|
|
||||||
testWidgets('AppFlowy> nothing changes', (tester) async {
|
|
||||||
const text = 'AppFlowy';
|
|
||||||
final editor = tester.editor..insertTextNode('');
|
|
||||||
await editor.startTesting();
|
|
||||||
await editor.updateSelection(
|
|
||||||
Selection.single(path: [0], startOffset: 0),
|
|
||||||
);
|
|
||||||
final textNode = editor.nodeAtPath([0]) as TextNode;
|
|
||||||
for (var i = 0; i < text.length; i++) {
|
|
||||||
await editor.insertText(textNode, text[i], i);
|
|
||||||
}
|
|
||||||
await insertGreater(editor);
|
|
||||||
|
|
||||||
final isQuote = textNode.subtype == BuiltInAttributeKey.quote;
|
|
||||||
expect(isQuote, false);
|
|
||||||
expect(textNode.toPlainText(), text);
|
|
||||||
});
|
|
||||||
|
|
||||||
testWidgets('> in front of text to blockquote', (tester) async {
|
|
||||||
const text = 'AppFlowy';
|
|
||||||
final editor = tester.editor..insertTextNode('');
|
|
||||||
await editor.startTesting();
|
|
||||||
await editor.updateSelection(
|
|
||||||
Selection.single(path: [0], startOffset: 0),
|
|
||||||
);
|
|
||||||
final textNode = editor.nodeAtPath([0]) as TextNode;
|
|
||||||
for (var i = 0; i < text.length; i++) {
|
|
||||||
await editor.insertText(textNode, text[i], i);
|
|
||||||
}
|
|
||||||
await editor.updateSelection(
|
|
||||||
Selection.single(path: [0], startOffset: 0),
|
|
||||||
);
|
|
||||||
await insertGreater(editor);
|
|
||||||
|
|
||||||
final isQuote = textNode.subtype == BuiltInAttributeKey.quote;
|
|
||||||
expect(isQuote, true);
|
|
||||||
expect(textNode.toPlainText(), text);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -184,6 +184,10 @@ void main() async {
|
|||||||
Selection.single(path: [0], startOffset: 0),
|
Selection.single(path: [0], startOffset: 0),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
await editor.insertText(textNode, '>', 0);
|
||||||
|
await editor.pressLogicKey(LogicalKeyboardKey.space);
|
||||||
|
expect(textNode.subtype, BuiltInAttributeKey.quote);
|
||||||
|
|
||||||
await editor.insertText(textNode, '*', 0);
|
await editor.insertText(textNode, '*', 0);
|
||||||
await editor.pressLogicKey(LogicalKeyboardKey.space);
|
await editor.pressLogicKey(LogicalKeyboardKey.space);
|
||||||
expect(textNode.subtype, BuiltInAttributeKey.bulletedList);
|
expect(textNode.subtype, BuiltInAttributeKey.bulletedList);
|
||||||
@ -227,5 +231,65 @@ void main() async {
|
|||||||
expect(textNode.subtype, null);
|
expect(textNode.subtype, null);
|
||||||
expect(textNode.toPlainText(), text);
|
expect(textNode.toPlainText(), text);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
group('convert geater to blockquote', () {
|
||||||
|
testWidgets('> AppFlowy to blockquote AppFlowy', (tester) async {
|
||||||
|
const text = 'AppFlowy';
|
||||||
|
final editor = tester.editor..insertTextNode('');
|
||||||
|
await editor.startTesting();
|
||||||
|
await editor.updateSelection(
|
||||||
|
Selection.single(path: [0], startOffset: 0),
|
||||||
|
);
|
||||||
|
|
||||||
|
final textNode = editor.nodeAtPath([0]) as TextNode;
|
||||||
|
await editor.insertText(textNode, '>', 0);
|
||||||
|
await editor.pressLogicKey(LogicalKeyboardKey.space);
|
||||||
|
expect(textNode.subtype, BuiltInAttributeKey.quote);
|
||||||
|
for (var i = 0; i < text.length; i++) {
|
||||||
|
await editor.insertText(textNode, text[i], i);
|
||||||
|
}
|
||||||
|
expect(textNode.toPlainText(), 'AppFlowy');
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('AppFlowy > nothing changes', (tester) async {
|
||||||
|
const text = 'AppFlowy >';
|
||||||
|
final editor = tester.editor..insertTextNode('');
|
||||||
|
await editor.startTesting();
|
||||||
|
await editor.updateSelection(
|
||||||
|
Selection.single(path: [0], startOffset: 0),
|
||||||
|
);
|
||||||
|
|
||||||
|
final textNode = editor.nodeAtPath([0]) as TextNode;
|
||||||
|
for (var i = 0; i < text.length; i++) {
|
||||||
|
await editor.insertText(textNode, text[i], i);
|
||||||
|
}
|
||||||
|
await editor.pressLogicKey(LogicalKeyboardKey.space);
|
||||||
|
final isQuote = textNode.subtype == BuiltInAttributeKey.quote;
|
||||||
|
expect(isQuote, false);
|
||||||
|
expect(textNode.toPlainText(), text);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('> in front of text to blockquote', (tester) async {
|
||||||
|
const text = 'AppFlowy';
|
||||||
|
final editor = tester.editor..insertTextNode('');
|
||||||
|
await editor.startTesting();
|
||||||
|
await editor.updateSelection(
|
||||||
|
Selection.single(path: [0], startOffset: 0),
|
||||||
|
);
|
||||||
|
final textNode = editor.nodeAtPath([0]) as TextNode;
|
||||||
|
for (var i = 0; i < text.length; i++) {
|
||||||
|
await editor.insertText(textNode, text[i], i);
|
||||||
|
}
|
||||||
|
await editor.updateSelection(
|
||||||
|
Selection.single(path: [0], startOffset: 0),
|
||||||
|
);
|
||||||
|
await editor.insertText(textNode, '>', 0);
|
||||||
|
await editor.pressLogicKey(LogicalKeyboardKey.space);
|
||||||
|
|
||||||
|
final isQuote = textNode.subtype == BuiltInAttributeKey.quote;
|
||||||
|
expect(isQuote, true);
|
||||||
|
expect(textNode.toPlainText(), text);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user