瀏覽代碼

feat: implement markdown input, like, #, *, -, -[]

Lucas.Xu 2 年之前
父節點
當前提交
19fc154681

+ 95 - 15
frontend/app_flowy/packages/flowy_editor/lib/service/internal_key_event_handlers/whitespace_handler.dart

@@ -4,10 +4,15 @@ import 'package:flutter/services.dart';
 import 'package:flowy_editor/document/node.dart';
 import 'package:flowy_editor/document/position.dart';
 import 'package:flowy_editor/document/selection.dart';
+import 'package:flowy_editor/editor_state.dart';
 import 'package:flowy_editor/operation/transaction_builder.dart';
 import 'package:flowy_editor/render/rich_text/rich_text_style.dart';
 import 'package:flowy_editor/service/keyboard_service.dart';
 
+const _bulletedListSymbols = ['*', '-'];
+const _checkboxListSymbols = ['[x]', '-[x]'];
+const _unCheckboxListSymbols = ['[]', '-[]'];
+
 FlowyKeyEventHandler whiteSpaceHandler = (editorState, event) {
   if (event.logicalKey != LogicalKeyboardKey.space) {
     return KeyEventResult.ignored;
@@ -28,24 +33,99 @@ FlowyKeyEventHandler whiteSpaceHandler = (editorState, event) {
     return KeyEventResult.ignored;
   }
 
-  final builder = TransactionBuilder(editorState);
   final textNode = textNodes.first;
   final text = textNode.toRawString();
-  if (text == '*' || text == '-') {
-    builder
-      ..deleteText(textNode, 0, 1)
-      ..updateNode(textNode, {
-        StyleKey.subtype: StyleKey.bulletedList,
-      })
-      ..afterSelection = Selection.collapsed(
-        Position(
-          path: textNode.path,
-          offset: 0,
-        ),
-      )
-      ..commit();
-    return KeyEventResult.handled;
+  if ((_checkboxListSymbols + _unCheckboxListSymbols).any(text.startsWith)) {
+    return _toCheckboxList(editorState, textNode);
+  } else if (_bulletedListSymbols.any(text.startsWith)) {
+    return _toBulletedList(editorState, textNode);
+  } else if (_countOfSign(text) != 0) {
+    return _toHeadingStyle(editorState, textNode);
   }
 
   return KeyEventResult.ignored;
 };
+
+KeyEventResult _toBulletedList(EditorState editorState, TextNode textNode) {
+  if (textNode.subtype == StyleKey.bulletedList) {
+    return KeyEventResult.ignored;
+  }
+  TransactionBuilder(editorState)
+    ..deleteText(textNode, 0, 1)
+    ..updateNode(textNode, {
+      StyleKey.subtype: StyleKey.bulletedList,
+    })
+    ..afterSelection = Selection.collapsed(
+      Position(
+        path: textNode.path,
+        offset: 0,
+      ),
+    )
+    ..commit();
+  return KeyEventResult.handled;
+}
+
+KeyEventResult _toCheckboxList(EditorState editorState, TextNode textNode) {
+  if (textNode.subtype == StyleKey.checkbox) {
+    return KeyEventResult.ignored;
+  }
+  final String symbol;
+  bool check = false;
+  final symbols = List<String>.from(_checkboxListSymbols)
+    ..retainWhere(textNode.toRawString().startsWith);
+  if (symbols.isNotEmpty) {
+    symbol = symbols.first;
+    check = true;
+  } else {
+    symbol = (List<String>.from(_unCheckboxListSymbols)
+          ..retainWhere(textNode.toRawString().startsWith))
+        .first;
+    check = false;
+  }
+
+  TransactionBuilder(editorState)
+    ..deleteText(textNode, 0, symbol.length)
+    ..updateNode(textNode, {
+      StyleKey.subtype: StyleKey.checkbox,
+      StyleKey.checkbox: check,
+    })
+    ..afterSelection = Selection.collapsed(
+      Position(
+        path: textNode.path,
+        offset: 0,
+      ),
+    )
+    ..commit();
+  return KeyEventResult.handled;
+}
+
+KeyEventResult _toHeadingStyle(EditorState editorState, TextNode textNode) {
+  final x = _countOfSign(textNode.toRawString());
+  final hX = 'h$x';
+  if (textNode.attributes.heading == hX) {
+    return KeyEventResult.ignored;
+  }
+  TransactionBuilder(editorState)
+    ..deleteText(textNode, 0, x)
+    ..updateNode(textNode, {
+      StyleKey.subtype: StyleKey.heading,
+      StyleKey.heading: hX,
+    })
+    ..afterSelection = Selection.collapsed(
+      Position(
+        path: textNode.path,
+        offset: 0,
+      ),
+    )
+    ..commit();
+  return KeyEventResult.handled;
+}
+
+int _countOfSign(String text) {
+  for (var i = 6; i >= 0; i--) {
+    if (text.startsWith('#' * i)) {
+      return i;
+    }
+  }
+  return 0;
+}