diff --git a/frontend/app_flowy/packages/appflowy_editor/documentation/testing.md b/frontend/app_flowy/packages/appflowy_editor/documentation/testing.md index d51583a9c5..c721510844 100644 --- a/frontend/app_flowy/packages/appflowy_editor/documentation/testing.md +++ b/frontend/app_flowy/packages/appflowy_editor/documentation/testing.md @@ -22,8 +22,8 @@ editor.insertTextNode(text); // Insert the same text, but with the heading style. editor.insertTextNode(text, attributes: { - StyleKey.subtype: StyleKey.heading, - StyleKey.heading: StyleKey.h1, + BuiltInAttributeKey.subtype: BuiltInAttributeKey.heading, + BuiltInAttributeKey.heading: BuiltInAttributeKey.h1, }); // Insert our text with the bulleted list style and the bold style. @@ -31,10 +31,10 @@ editor.insertTextNode(text, attributes: { editor.insertTextNode( '', attributes: { - StyleKey.subtype: StyleKey.bulletedList, + BuiltInAttributeKey.subtype: BuiltInAttributeKey.bulletedList, }, delta: Delta([ - TextInsert(text, {StyleKey.bold: true}), + TextInsert(text, {BuiltInAttributeKey.bold: true}), ]), ); ``` diff --git a/frontend/app_flowy/packages/appflowy_editor/example/lib/main.dart b/frontend/app_flowy/packages/appflowy_editor/example/lib/main.dart index 921628ff21..5d2f4e9ebc 100644 --- a/frontend/app_flowy/packages/appflowy_editor/example/lib/main.dart +++ b/frontend/app_flowy/packages/appflowy_editor/example/lib/main.dart @@ -21,6 +21,10 @@ class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( + localizationsDelegates: const [ + AppFlowyEditorLocalizations.delegate, + ], + supportedLocales: AppFlowyEditorLocalizations.delegate.supportedLocales, debugShowCheckedModeBanner: false, theme: ThemeData( primarySwatch: Colors.blue, @@ -40,7 +44,9 @@ class MyHomePage extends StatefulWidget { class _MyHomePageState extends State { int _pageIndex = 0; - late EditorState _editorState; + EditorState? _editorState; + bool darkMode = false; + EditorStyle _editorStyle = EditorStyle.defaultStyle(); Future? _jsonString; @override @@ -78,24 +84,29 @@ class _MyHomePageState extends State { return FutureBuilder( future: jsonString, builder: (_, snapshot) { - if (snapshot.hasData) { - _editorState = EditorState( + if (snapshot.hasData && + snapshot.connectionState == ConnectionState.done) { + _editorState ??= EditorState( document: StateTree.fromJson( Map.from( json.decode(snapshot.data!), ), ), ); - _editorState.logConfiguration + _editorState!.logConfiguration ..level = LogLevel.all ..handler = (message) { debugPrint(message); }; - return SizedBox( + _editorState!.operationStream.listen((event) { + debugPrint('Operation: ${event.toJson()}'); + }); + return Container( + color: darkMode ? Colors.black : Colors.white, width: MediaQuery.of(context).size.width, child: AppFlowyEditor( - editorState: _editorState, - editorStyle: const EditorStyle.defaultStyle(), + editorState: _editorState!, + editorStyle: _editorStyle, shortcutEvents: [ underscoreToItalicEvent, ], @@ -127,12 +138,26 @@ class _MyHomePageState extends State { onPressed: () => _switchToPage(2), ), ActionButton( - icon: const Icon(Icons.print), - onPressed: () => {_exportDocument(_editorState)}), + icon: const Icon(Icons.print), + onPressed: () => _exportDocument(_editorState!), + ), ActionButton( icon: const Icon(Icons.import_export), onPressed: () => _importDocument(), ), + ActionButton( + icon: const Icon(Icons.color_lens), + onPressed: () { + setState(() { + _editorStyle = _editorStyle.copyWith( + textStyle: darkMode + ? BuiltInTextStyle.builtIn() + : BuiltInTextStyle.builtInDarkMode(), + ); + darkMode = !darkMode; + }); + }, + ), ], ); } @@ -166,6 +191,7 @@ class _MyHomePageState extends State { void _switchToPage(int pageIndex) { if (pageIndex != _pageIndex) { setState(() { + _editorState = null; _pageIndex = pageIndex; }); } diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/appflowy_editor.dart b/frontend/app_flowy/packages/appflowy_editor/lib/appflowy_editor.dart index 60f63a66e2..282ec39da2 100644 --- a/frontend/app_flowy/packages/appflowy_editor/lib/appflowy_editor.dart +++ b/frontend/app_flowy/packages/appflowy_editor/lib/appflowy_editor.dart @@ -26,3 +26,5 @@ export 'src/service/input_service.dart'; export 'src/service/shortcut_event/keybinding.dart'; export 'src/service/shortcut_event/shortcut_event.dart'; export 'src/service/shortcut_event/shortcut_event_handler.dart'; +export 'src/extensions/attributes_extension.dart'; +export 'src/l10n/l10n.dart'; diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/l10n/intl_ca.arb b/frontend/app_flowy/packages/appflowy_editor/lib/l10n/intl_ca.arb new file mode 100644 index 0000000000..45190fb106 --- /dev/null +++ b/frontend/app_flowy/packages/appflowy_editor/lib/l10n/intl_ca.arb @@ -0,0 +1,35 @@ +{ + "@@locale": "ca", + "bold": "", + "@bold": {}, + "bulletedList": "", + "@bulletedList": {}, + "checkbox": "", + "@checkbox": {}, + "embedCode": "", + "@embedCode": {}, + "heading1": "", + "@heading1": {}, + "heading2": "", + "@heading2": {}, + "heading3": "", + "@heading3": {}, + "highlight": "", + "@highlight": {}, + "image": "", + "@image": {}, + "italic": "", + "@italic": {}, + "link": "", + "@link": {}, + "numberedList": "", + "@numberedList": {}, + "quote": "", + "@quote": {}, + "strikethrough": "", + "@strikethrough": {}, + "text": "", + "@text": {}, + "underline": "", + "@underline": {} +} \ No newline at end of file diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/l10n/intl_de_DE.arb b/frontend/app_flowy/packages/appflowy_editor/lib/l10n/intl_de_DE.arb new file mode 100644 index 0000000000..d4456b452e --- /dev/null +++ b/frontend/app_flowy/packages/appflowy_editor/lib/l10n/intl_de_DE.arb @@ -0,0 +1,35 @@ +{ + "@@locale": "de-DE", + "bold": "", + "@bold": {}, + "bulletedList": "", + "@bulletedList": {}, + "checkbox": "", + "@checkbox": {}, + "embedCode": "", + "@embedCode": {}, + "heading1": "", + "@heading1": {}, + "heading2": "", + "@heading2": {}, + "heading3": "", + "@heading3": {}, + "highlight": "", + "@highlight": {}, + "image": "", + "@image": {}, + "italic": "", + "@italic": {}, + "link": "", + "@link": {}, + "numberedList": "", + "@numberedList": {}, + "quote": "", + "@quote": {}, + "strikethrough": "", + "@strikethrough": {}, + "text": "", + "@text": {}, + "underline": "", + "@underline": {} +} \ No newline at end of file diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/l10n/intl_en.arb b/frontend/app_flowy/packages/appflowy_editor/lib/l10n/intl_en.arb new file mode 100644 index 0000000000..f9968ab07a --- /dev/null +++ b/frontend/app_flowy/packages/appflowy_editor/lib/l10n/intl_en.arb @@ -0,0 +1,35 @@ +{ + "@@locale": "en", + "bold": "Bold", + "@bold": {}, + "bulletedList": "Bulleted List", + "@bulletedList": {}, + "checkbox": "Checkbox", + "@checkbox": {}, + "embedCode": "Embed Code", + "@embedCode": {}, + "heading1": "H1", + "@heading1": {}, + "heading2": "H2", + "@heading2": {}, + "heading3": "H3", + "@heading3": {}, + "highlight": "Highlight", + "@highlight": {}, + "image": "Image", + "@image": {}, + "italic": "Italic", + "@italic": {}, + "link": "Link", + "@link": {}, + "numberedList": "Numbered List", + "@numberedList": {}, + "quote": "Quote", + "@quote": {}, + "strikethrough": "Strikethrough", + "@strikethrough": {}, + "text": "Text", + "@text": {}, + "underline": "Underline", + "@underline": {} +} \ No newline at end of file diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/l10n/intl_es_VE.arb b/frontend/app_flowy/packages/appflowy_editor/lib/l10n/intl_es_VE.arb new file mode 100644 index 0000000000..bace9331ae --- /dev/null +++ b/frontend/app_flowy/packages/appflowy_editor/lib/l10n/intl_es_VE.arb @@ -0,0 +1,35 @@ +{ + "@@locale": "es-VE", + "bold": "", + "@bold": {}, + "bulletedList": "", + "@bulletedList": {}, + "checkbox": "", + "@checkbox": {}, + "embedCode": "", + "@embedCode": {}, + "heading1": "", + "@heading1": {}, + "heading2": "", + "@heading2": {}, + "heading3": "", + "@heading3": {}, + "highlight": "", + "@highlight": {}, + "image": "", + "@image": {}, + "italic": "", + "@italic": {}, + "link": "", + "@link": {}, + "numberedList": "", + "@numberedList": {}, + "quote": "", + "@quote": {}, + "strikethrough": "", + "@strikethrough": {}, + "text": "", + "@text": {}, + "underline": "", + "@underline": {} +} \ No newline at end of file diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/l10n/intl_fr_CA.arb b/frontend/app_flowy/packages/appflowy_editor/lib/l10n/intl_fr_CA.arb new file mode 100644 index 0000000000..6875d45d72 --- /dev/null +++ b/frontend/app_flowy/packages/appflowy_editor/lib/l10n/intl_fr_CA.arb @@ -0,0 +1,35 @@ +{ + "@@locale": "fr-CA", + "bold": "", + "@bold": {}, + "bulletedList": "", + "@bulletedList": {}, + "checkbox": "", + "@checkbox": {}, + "embedCode": "", + "@embedCode": {}, + "heading1": "", + "@heading1": {}, + "heading2": "", + "@heading2": {}, + "heading3": "", + "@heading3": {}, + "highlight": "", + "@highlight": {}, + "image": "", + "@image": {}, + "italic": "", + "@italic": {}, + "link": "", + "@link": {}, + "numberedList": "", + "@numberedList": {}, + "quote": "", + "@quote": {}, + "strikethrough": "", + "@strikethrough": {}, + "text": "", + "@text": {}, + "underline": "", + "@underline": {} +} \ No newline at end of file diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/l10n/intl_fr_FR.arb b/frontend/app_flowy/packages/appflowy_editor/lib/l10n/intl_fr_FR.arb new file mode 100644 index 0000000000..051676b02c --- /dev/null +++ b/frontend/app_flowy/packages/appflowy_editor/lib/l10n/intl_fr_FR.arb @@ -0,0 +1,35 @@ +{ + "@@locale": "fr-FR", + "bold": "", + "@bold": {}, + "bulletedList": "", + "@bulletedList": {}, + "checkbox": "", + "@checkbox": {}, + "embedCode": "", + "@embedCode": {}, + "heading1": "", + "@heading1": {}, + "heading2": "", + "@heading2": {}, + "heading3": "", + "@heading3": {}, + "highlight": "", + "@highlight": {}, + "image": "", + "@image": {}, + "italic": "", + "@italic": {}, + "link": "", + "@link": {}, + "numberedList": "", + "@numberedList": {}, + "quote": "", + "@quote": {}, + "strikethrough": "", + "@strikethrough": {}, + "text": "", + "@text": {}, + "underline": "", + "@underline": {} +} \ No newline at end of file diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/l10n/intl_hu_HU.arb b/frontend/app_flowy/packages/appflowy_editor/lib/l10n/intl_hu_HU.arb new file mode 100644 index 0000000000..8b55ed3096 --- /dev/null +++ b/frontend/app_flowy/packages/appflowy_editor/lib/l10n/intl_hu_HU.arb @@ -0,0 +1,35 @@ +{ + "@@locale": "hu-HU", + "bold": "", + "@bold": {}, + "bulletedList": "", + "@bulletedList": {}, + "checkbox": "", + "@checkbox": {}, + "embedCode": "", + "@embedCode": {}, + "heading1": "", + "@heading1": {}, + "heading2": "", + "@heading2": {}, + "heading3": "", + "@heading3": {}, + "highlight": "", + "@highlight": {}, + "image": "", + "@image": {}, + "italic": "", + "@italic": {}, + "link": "", + "@link": {}, + "numberedList": "", + "@numberedList": {}, + "quote": "", + "@quote": {}, + "strikethrough": "", + "@strikethrough": {}, + "text": "", + "@text": {}, + "underline": "", + "@underline": {} +} \ No newline at end of file diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/l10n/intl_id_ID.arb b/frontend/app_flowy/packages/appflowy_editor/lib/l10n/intl_id_ID.arb new file mode 100644 index 0000000000..cadebb36aa --- /dev/null +++ b/frontend/app_flowy/packages/appflowy_editor/lib/l10n/intl_id_ID.arb @@ -0,0 +1,35 @@ +{ + "@@locale": "id-ID", + "bold": "", + "@bold": {}, + "bulletedList": "", + "@bulletedList": {}, + "checkbox": "", + "@checkbox": {}, + "embedCode": "", + "@embedCode": {}, + "heading1": "", + "@heading1": {}, + "heading2": "", + "@heading2": {}, + "heading3": "", + "@heading3": {}, + "highlight": "", + "@highlight": {}, + "image": "", + "@image": {}, + "italic": "", + "@italic": {}, + "link": "", + "@link": {}, + "numberedList": "", + "@numberedList": {}, + "quote": "", + "@quote": {}, + "strikethrough": "", + "@strikethrough": {}, + "text": "", + "@text": {}, + "underline": "", + "@underline": {} +} \ No newline at end of file diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/l10n/intl_it_IT.arb b/frontend/app_flowy/packages/appflowy_editor/lib/l10n/intl_it_IT.arb new file mode 100644 index 0000000000..908b80f275 --- /dev/null +++ b/frontend/app_flowy/packages/appflowy_editor/lib/l10n/intl_it_IT.arb @@ -0,0 +1,35 @@ +{ + "@@locale": "it-IT", + "bold": "", + "@bold": {}, + "bulletedList": "", + "@bulletedList": {}, + "checkbox": "", + "@checkbox": {}, + "embedCode": "", + "@embedCode": {}, + "heading1": "", + "@heading1": {}, + "heading2": "", + "@heading2": {}, + "heading3": "", + "@heading3": {}, + "highlight": "", + "@highlight": {}, + "image": "", + "@image": {}, + "italic": "", + "@italic": {}, + "link": "", + "@link": {}, + "numberedList": "", + "@numberedList": {}, + "quote": "", + "@quote": {}, + "strikethrough": "", + "@strikethrough": {}, + "text": "", + "@text": {}, + "underline": "", + "@underline": {} +} \ No newline at end of file diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/l10n/intl_ja_JP.arb b/frontend/app_flowy/packages/appflowy_editor/lib/l10n/intl_ja_JP.arb new file mode 100644 index 0000000000..5d49578cc3 --- /dev/null +++ b/frontend/app_flowy/packages/appflowy_editor/lib/l10n/intl_ja_JP.arb @@ -0,0 +1,35 @@ +{ + "@@locale": "ja-JP", + "bold": "", + "@bold": {}, + "bulletedList": "", + "@bulletedList": {}, + "checkbox": "", + "@checkbox": {}, + "embedCode": "", + "@embedCode": {}, + "heading1": "", + "@heading1": {}, + "heading2": "", + "@heading2": {}, + "heading3": "", + "@heading3": {}, + "highlight": "", + "@highlight": {}, + "image": "", + "@image": {}, + "italic": "", + "@italic": {}, + "link": "", + "@link": {}, + "numberedList": "", + "@numberedList": {}, + "quote": "", + "@quote": {}, + "strikethrough": "", + "@strikethrough": {}, + "text": "", + "@text": {}, + "underline": "", + "@underline": {} +} \ No newline at end of file diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/l10n/intl_pl_PL.arb b/frontend/app_flowy/packages/appflowy_editor/lib/l10n/intl_pl_PL.arb new file mode 100644 index 0000000000..ba97af8f26 --- /dev/null +++ b/frontend/app_flowy/packages/appflowy_editor/lib/l10n/intl_pl_PL.arb @@ -0,0 +1,35 @@ +{ + "@@locale": "pl-PL", + "bold": "", + "@bold": {}, + "bulletedList": "", + "@bulletedList": {}, + "checkbox": "", + "@checkbox": {}, + "embedCode": "", + "@embedCode": {}, + "heading1": "", + "@heading1": {}, + "heading2": "", + "@heading2": {}, + "heading3": "", + "@heading3": {}, + "highlight": "", + "@highlight": {}, + "image": "", + "@image": {}, + "italic": "", + "@italic": {}, + "link": "", + "@link": {}, + "numberedList": "", + "@numberedList": {}, + "quote": "", + "@quote": {}, + "strikethrough": "", + "@strikethrough": {}, + "text": "", + "@text": {}, + "underline": "", + "@underline": {} +} \ No newline at end of file diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/l10n/intl_pt_BR.arb b/frontend/app_flowy/packages/appflowy_editor/lib/l10n/intl_pt_BR.arb new file mode 100644 index 0000000000..8e9c87e0cf --- /dev/null +++ b/frontend/app_flowy/packages/appflowy_editor/lib/l10n/intl_pt_BR.arb @@ -0,0 +1,35 @@ +{ + "@@locale": "pt-BR", + "bold": "", + "@bold": {}, + "bulletedList": "", + "@bulletedList": {}, + "checkbox": "", + "@checkbox": {}, + "embedCode": "", + "@embedCode": {}, + "heading1": "", + "@heading1": {}, + "heading2": "", + "@heading2": {}, + "heading3": "", + "@heading3": {}, + "highlight": "", + "@highlight": {}, + "image": "", + "@image": {}, + "italic": "", + "@italic": {}, + "link": "", + "@link": {}, + "numberedList": "", + "@numberedList": {}, + "quote": "", + "@quote": {}, + "strikethrough": "", + "@strikethrough": {}, + "text": "", + "@text": {}, + "underline": "", + "@underline": {} +} \ No newline at end of file diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/l10n/intl_pt_PT.arb b/frontend/app_flowy/packages/appflowy_editor/lib/l10n/intl_pt_PT.arb new file mode 100644 index 0000000000..4e9187f282 --- /dev/null +++ b/frontend/app_flowy/packages/appflowy_editor/lib/l10n/intl_pt_PT.arb @@ -0,0 +1,35 @@ +{ + "@@locale": "pt-PT", + "bold": "", + "@bold": {}, + "bulletedList": "", + "@bulletedList": {}, + "checkbox": "", + "@checkbox": {}, + "embedCode": "", + "@embedCode": {}, + "heading1": "", + "@heading1": {}, + "heading2": "", + "@heading2": {}, + "heading3": "", + "@heading3": {}, + "highlight": "", + "@highlight": {}, + "image": "", + "@image": {}, + "italic": "", + "@italic": {}, + "link": "", + "@link": {}, + "numberedList": "", + "@numberedList": {}, + "quote": "", + "@quote": {}, + "strikethrough": "", + "@strikethrough": {}, + "text": "", + "@text": {}, + "underline": "", + "@underline": {} +} \ No newline at end of file diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/l10n/intl_ru_RU.arb b/frontend/app_flowy/packages/appflowy_editor/lib/l10n/intl_ru_RU.arb new file mode 100644 index 0000000000..5a346cf027 --- /dev/null +++ b/frontend/app_flowy/packages/appflowy_editor/lib/l10n/intl_ru_RU.arb @@ -0,0 +1,35 @@ +{ + "@@locale": "ru-RU", + "bold": "", + "@bold": {}, + "bulletedList": "", + "@bulletedList": {}, + "checkbox": "", + "@checkbox": {}, + "embedCode": "", + "@embedCode": {}, + "heading1": "", + "@heading1": {}, + "heading2": "", + "@heading2": {}, + "heading3": "", + "@heading3": {}, + "highlight": "", + "@highlight": {}, + "image": "", + "@image": {}, + "italic": "", + "@italic": {}, + "link": "", + "@link": {}, + "numberedList": "", + "@numberedList": {}, + "quote": "", + "@quote": {}, + "strikethrough": "", + "@strikethrough": {}, + "text": "", + "@text": {}, + "underline": "", + "@underline": {} +} \ No newline at end of file diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/l10n/intl_tr_TR.arb b/frontend/app_flowy/packages/appflowy_editor/lib/l10n/intl_tr_TR.arb new file mode 100644 index 0000000000..16887cd1cf --- /dev/null +++ b/frontend/app_flowy/packages/appflowy_editor/lib/l10n/intl_tr_TR.arb @@ -0,0 +1,35 @@ +{ + "@@locale": "tr-TR", + "bold": "", + "@bold": {}, + "bulletedList": "", + "@bulletedList": {}, + "checkbox": "", + "@checkbox": {}, + "embedCode": "", + "@embedCode": {}, + "heading1": "", + "@heading1": {}, + "heading2": "", + "@heading2": {}, + "heading3": "", + "@heading3": {}, + "highlight": "", + "@highlight": {}, + "image": "", + "@image": {}, + "italic": "", + "@italic": {}, + "link": "", + "@link": {}, + "numberedList": "", + "@numberedList": {}, + "quote": "", + "@quote": {}, + "strikethrough": "", + "@strikethrough": {}, + "text": "", + "@text": {}, + "underline": "", + "@underline": {} +} \ No newline at end of file diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/l10n/intl_zh_CN.arb b/frontend/app_flowy/packages/appflowy_editor/lib/l10n/intl_zh_CN.arb new file mode 100644 index 0000000000..0ebe83fa83 --- /dev/null +++ b/frontend/app_flowy/packages/appflowy_editor/lib/l10n/intl_zh_CN.arb @@ -0,0 +1,35 @@ +{ + "@@locale": "zh-CN", + "bold": "加粗", + "@bold": {}, + "bulletedList": "无序列表", + "@bulletedList": {}, + "checkbox": "复选框", + "@checkbox": {}, + "embedCode": "内嵌代码", + "@embedCode": {}, + "heading1": "标题 1", + "@heading1": {}, + "heading2": "标题 2", + "@heading2": {}, + "heading3": "标题 3", + "@heading3": {}, + "highlight": "高亮", + "@highlight": {}, + "image": "图片", + "@image": {}, + "italic": "斜体", + "@italic": {}, + "link": "链接", + "@link": {}, + "numberedList": "有序列表", + "@numberedList": {}, + "quote": "引用", + "@quote": {}, + "strikethrough": "删除线", + "@strikethrough": {}, + "text": "文字", + "@text": {}, + "underline": "下划线", + "@underline": {} +} \ No newline at end of file diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/l10n/intl_zh_TW.arb b/frontend/app_flowy/packages/appflowy_editor/lib/l10n/intl_zh_TW.arb new file mode 100644 index 0000000000..4d2778a608 --- /dev/null +++ b/frontend/app_flowy/packages/appflowy_editor/lib/l10n/intl_zh_TW.arb @@ -0,0 +1,35 @@ +{ + "@@locale": "zh-TW", + "bold": "", + "@bold": {}, + "bulletedList": "", + "@bulletedList": {}, + "checkbox": "", + "@checkbox": {}, + "embedCode": "", + "@embedCode": {}, + "heading1": "", + "@heading1": {}, + "heading2": "", + "@heading2": {}, + "heading3": "", + "@heading3": {}, + "highlight": "", + "@highlight": {}, + "image": "", + "@image": {}, + "italic": "", + "@italic": {}, + "link": "", + "@link": {}, + "numberedList": "", + "@numberedList": {}, + "quote": "", + "@quote": {}, + "strikethrough": "", + "@strikethrough": {}, + "text": "", + "@text": {}, + "underline": "", + "@underline": {} +} \ No newline at end of file diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/src/document/built_in_attribute_keys.dart b/frontend/app_flowy/packages/appflowy_editor/lib/src/document/built_in_attribute_keys.dart new file mode 100644 index 0000000000..0ed5440a9a --- /dev/null +++ b/frontend/app_flowy/packages/appflowy_editor/lib/src/document/built_in_attribute_keys.dart @@ -0,0 +1,59 @@ +/// +/// Supported partial rendering types: +/// bold, italic, +/// underline, strikethrough, +/// color, font, +/// href +/// +/// Supported global rendering types: +/// heading: h1, h2, h3, h4, h5, h6, ... +/// block quote, +/// list: ordered list, bulleted list, +/// code block +/// +class BuiltInAttributeKey { + static String bold = 'bold'; + static String italic = 'italic'; + static String underline = 'underline'; + static String strikethrough = 'strikethrough'; + static String color = 'color'; + static String backgroundColor = 'backgroundColor'; + static String font = 'font'; + static String href = 'href'; + + static String subtype = 'subtype'; + static String heading = 'heading'; + static String h1 = 'h1'; + static String h2 = 'h2'; + static String h3 = 'h3'; + static String h4 = 'h4'; + static String h5 = 'h5'; + static String h6 = 'h6'; + + static String bulletedList = 'bulleted-list'; + static String numberList = 'number-list'; + + static String quote = 'quote'; + static String checkbox = 'checkbox'; + static String code = 'code'; + static String number = 'number'; + + static List partialStyleKeys = [ + BuiltInAttributeKey.bold, + BuiltInAttributeKey.italic, + BuiltInAttributeKey.underline, + BuiltInAttributeKey.strikethrough, + BuiltInAttributeKey.backgroundColor, + BuiltInAttributeKey.href, + BuiltInAttributeKey.code, + ]; + + static List globalStyleKeys = [ + BuiltInAttributeKey.subtype, + BuiltInAttributeKey.heading, + BuiltInAttributeKey.checkbox, + BuiltInAttributeKey.bulletedList, + BuiltInAttributeKey.numberList, + BuiltInAttributeKey.quote, + ]; +} diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/src/document/node.dart b/frontend/app_flowy/packages/appflowy_editor/lib/src/document/node.dart index 909e7dd494..bfc40c4d32 100644 --- a/frontend/app_flowy/packages/appflowy_editor/lib/src/document/node.dart +++ b/frontend/app_flowy/packages/appflowy_editor/lib/src/document/node.dart @@ -24,6 +24,13 @@ class Node extends ChangeNotifier with LinkedListEntry { return null; } + String get id { + if (subtype != null) { + return '$type/$subtype'; + } + return type; + } + Path get path => _path(); Attributes get attributes => _attributes; diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/src/editor_state.dart b/frontend/app_flowy/packages/appflowy_editor/lib/src/editor_state.dart index 2750af07a6..4f2ca39a84 100644 --- a/frontend/app_flowy/packages/appflowy_editor/lib/src/editor_state.dart +++ b/frontend/app_flowy/packages/appflowy_editor/lib/src/editor_state.dart @@ -60,7 +60,11 @@ class EditorState { List selectionMenuItems = []; /// Stores the editor style. - EditorStyle editorStyle = const EditorStyle.defaultStyle(); + EditorStyle editorStyle = EditorStyle.defaultStyle(); + + /// Operation stream. + Stream get operationStream => _observer.stream; + final StreamController _observer = StreamController.broadcast(); final UndoManager undoManager = UndoManager(); Selection? _cursorSelection; @@ -72,6 +76,15 @@ class EditorState { return _cursorSelection; } + RenderBox? get renderBox { + final renderObject = + service.scrollServiceKey.currentContext?.findRenderObject(); + if (renderObject != null && renderObject is RenderBox) { + return renderObject; + } + return null; + } + updateCursorSelection(Selection? cursorSelection, [CursorUpdateReason reason = CursorUpdateReason.others]) { // broadcast to other users here @@ -151,5 +164,6 @@ class EditorState { } else if (op is TextEditOperation) { document.textEdit(op.path, op.delta); } + _observer.add(op); } } diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/src/extensions/attributes_extension.dart b/frontend/app_flowy/packages/appflowy_editor/lib/src/extensions/attributes_extension.dart new file mode 100644 index 0000000000..c80753b217 --- /dev/null +++ b/frontend/app_flowy/packages/appflowy_editor/lib/src/extensions/attributes_extension.dart @@ -0,0 +1,93 @@ +import 'package:appflowy_editor/src/document/attributes.dart'; +import 'package:appflowy_editor/src/document/built_in_attribute_keys.dart'; +import 'package:flutter/material.dart'; + +extension NodeAttributesExtensions on Attributes { + String? get heading { + if (containsKey(BuiltInAttributeKey.subtype) && + containsKey(BuiltInAttributeKey.heading) && + this[BuiltInAttributeKey.subtype] == BuiltInAttributeKey.heading && + this[BuiltInAttributeKey.heading] is String) { + return this[BuiltInAttributeKey.heading]; + } + return null; + } + + bool get quote { + return containsKey(BuiltInAttributeKey.quote); + } + + int? get number { + if (containsKey(BuiltInAttributeKey.number) && + this[BuiltInAttributeKey.number] is int) { + return this[BuiltInAttributeKey.number]; + } + return null; + } + + bool get code { + if (containsKey(BuiltInAttributeKey.code) && + this[BuiltInAttributeKey.code] == true) { + return this[BuiltInAttributeKey.code]; + } + return false; + } + + bool get check { + if (containsKey(BuiltInAttributeKey.checkbox) && + this[BuiltInAttributeKey.checkbox] is bool) { + return this[BuiltInAttributeKey.checkbox]; + } + return false; + } +} + +extension DeltaAttributesExtensions on Attributes { + bool get bold { + return (containsKey(BuiltInAttributeKey.bold) && + this[BuiltInAttributeKey.bold] == true); + } + + bool get italic { + return (containsKey(BuiltInAttributeKey.italic) && + this[BuiltInAttributeKey.italic] == true); + } + + bool get underline { + return (containsKey(BuiltInAttributeKey.underline) && + this[BuiltInAttributeKey.underline] == true); + } + + bool get strikethrough { + return (containsKey(BuiltInAttributeKey.strikethrough) && + this[BuiltInAttributeKey.strikethrough] == true); + } + + Color? get color { + if (containsKey(BuiltInAttributeKey.color) && + this[BuiltInAttributeKey.color] is String) { + return Color( + int.parse(this[BuiltInAttributeKey.color]), + ); + } + return null; + } + + Color? get backgroundColor { + if (containsKey(BuiltInAttributeKey.backgroundColor) && + this[BuiltInAttributeKey.backgroundColor] is String) { + return Color( + int.parse(this[BuiltInAttributeKey.backgroundColor]), + ); + } + return null; + } + + String? get href { + if (containsKey(BuiltInAttributeKey.href) && + this[BuiltInAttributeKey.href] is String) { + return this[BuiltInAttributeKey.href]; + } + return null; + } +} diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/src/extensions/text_node_extensions.dart b/frontend/app_flowy/packages/appflowy_editor/lib/src/extensions/text_node_extensions.dart index a07529cfdf..352cb69f97 100644 --- a/frontend/app_flowy/packages/appflowy_editor/lib/src/extensions/text_node_extensions.dart +++ b/frontend/app_flowy/packages/appflowy_editor/lib/src/extensions/text_node_extensions.dart @@ -3,7 +3,7 @@ import 'package:appflowy_editor/src/document/path.dart'; import 'package:appflowy_editor/src/document/position.dart'; import 'package:appflowy_editor/src/document/selection.dart'; import 'package:appflowy_editor/src/document/text_delta.dart'; -import 'package:appflowy_editor/src/render/rich_text/rich_text_style.dart'; +import 'package:appflowy_editor/src/document/built_in_attribute_keys.dart'; extension TextNodeExtension on TextNode { dynamic getAttributeInSelection(Selection selection, String styleKey) { @@ -29,27 +29,28 @@ extension TextNodeExtension on TextNode { } bool allSatisfyLinkInSelection(Selection selection) => - allSatisfyInSelection(selection, StyleKey.href, (value) { + allSatisfyInSelection(selection, BuiltInAttributeKey.href, (value) { return value != null; }); bool allSatisfyBoldInSelection(Selection selection) => - allSatisfyInSelection(selection, StyleKey.bold, (value) { + allSatisfyInSelection(selection, BuiltInAttributeKey.bold, (value) { return value == true; }); bool allSatisfyItalicInSelection(Selection selection) => - allSatisfyInSelection(selection, StyleKey.italic, (value) { + allSatisfyInSelection(selection, BuiltInAttributeKey.italic, (value) { return value == true; }); bool allSatisfyUnderlineInSelection(Selection selection) => - allSatisfyInSelection(selection, StyleKey.underline, (value) { + allSatisfyInSelection(selection, BuiltInAttributeKey.underline, (value) { return value == true; }); bool allSatisfyStrikethroughInSelection(Selection selection) => - allSatisfyInSelection(selection, StyleKey.strikethrough, (value) { + allSatisfyInSelection(selection, BuiltInAttributeKey.strikethrough, + (value) { return value == true; }); @@ -58,11 +59,11 @@ extension TextNodeExtension on TextNode { String styleKey, bool Function(dynamic value) test, ) { - if (StyleKey.globalStyleKeys.contains(styleKey)) { + if (BuiltInAttributeKey.globalStyleKeys.contains(styleKey)) { if (attributes.containsKey(styleKey)) { return test(attributes[styleKey]); } - } else if (StyleKey.partialStyleKeys.contains(styleKey)) { + } else if (BuiltInAttributeKey.partialStyleKeys.contains(styleKey)) { final ops = delta.whereType(); final startOffset = selection.isBackward ? selection.start.offset : selection.end.offset; @@ -120,28 +121,28 @@ extension TextNodeExtension on TextNode { extension TextNodesExtension on List { bool allSatisfyBoldInSelection(Selection selection) => allSatisfyInSelection( selection, - StyleKey.bold, + BuiltInAttributeKey.bold, (value) => value == true, ); bool allSatisfyItalicInSelection(Selection selection) => allSatisfyInSelection( selection, - StyleKey.italic, + BuiltInAttributeKey.italic, (value) => value == true, ); bool allSatisfyUnderlineInSelection(Selection selection) => allSatisfyInSelection( selection, - StyleKey.underline, + BuiltInAttributeKey.underline, (value) => value == true, ); bool allSatisfyStrikethroughInSelection(Selection selection) => allSatisfyInSelection( selection, - StyleKey.strikethrough, + BuiltInAttributeKey.strikethrough, (value) => value == true, ); diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/src/extensions/text_style_extension.dart b/frontend/app_flowy/packages/appflowy_editor/lib/src/extensions/text_style_extension.dart new file mode 100644 index 0000000000..5e2828bf07 --- /dev/null +++ b/frontend/app_flowy/packages/appflowy_editor/lib/src/extensions/text_style_extension.dart @@ -0,0 +1,73 @@ +import 'package:flutter/gestures.dart'; +import 'package:flutter/material.dart'; + +extension TextSpanExtensions on TextSpan { + TextSpan copyWith({ + String? text, + TextStyle? style, + List? children, + GestureRecognizer? recognizer, + String? semanticsLabel, + }) { + return TextSpan( + text: text ?? this.text, + style: style ?? this.style, + children: children ?? this.children, + recognizer: recognizer ?? this.recognizer, + semanticsLabel: semanticsLabel ?? this.semanticsLabel, + ); + } + + TextSpan updateTextStyle(TextStyle? other) { + if (other == null) { + return this; + } + return copyWith( + style: style?.combine(other), + children: children?.map((child) { + if (child is TextSpan) { + return child.updateTextStyle(other); + } + return child; + }).toList(growable: false), + ); + } +} + +extension TextStyleExtensions on TextStyle { + TextStyle combine(TextStyle? other) { + if (other == null) { + return this; + } + if (!other.inherit) { + return other; + } + + return copyWith( + color: other.color, + backgroundColor: other.backgroundColor, + fontSize: other.fontSize, + fontWeight: other.fontWeight, + fontStyle: other.fontStyle, + letterSpacing: other.letterSpacing, + wordSpacing: other.wordSpacing, + textBaseline: other.textBaseline, + height: other.height, + leadingDistribution: other.leadingDistribution, + locale: other.locale, + foreground: other.foreground, + background: other.background, + shadows: other.shadows, + fontFeatures: other.fontFeatures, + decoration: TextDecoration.combine([ + if (decoration != null) decoration!, + if (other.decoration != null) other.decoration!, + ]), + decorationColor: other.decorationColor, + decorationStyle: other.decorationStyle, + decorationThickness: other.decorationThickness, + fontFamilyFallback: other.fontFamilyFallback, + overflow: other.overflow, + ); + } +} diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/src/infra/html_converter.dart b/frontend/app_flowy/packages/appflowy_editor/lib/src/infra/html_converter.dart index fa49ee2e54..6e71fc007b 100644 --- a/frontend/app_flowy/packages/appflowy_editor/lib/src/infra/html_converter.dart +++ b/frontend/app_flowy/packages/appflowy_editor/lib/src/infra/html_converter.dart @@ -4,11 +4,11 @@ import 'dart:ui'; import 'package:appflowy_editor/src/document/attributes.dart'; import 'package:appflowy_editor/src/document/node.dart'; import 'package:appflowy_editor/src/document/text_delta.dart'; -import 'package:appflowy_editor/src/render/rich_text/rich_text_style.dart'; import 'package:appflowy_editor/src/extensions/color_extension.dart'; import 'package:flutter/material.dart'; import 'package:html/parser.dart' show parse; import 'package:html/dom.dart' as html; +import 'package:appflowy_editor/src/document/built_in_attribute_keys.dart'; class HTMLTag { static const h1 = "h1"; @@ -99,7 +99,8 @@ class HTMLToNodesConverter { for (final child in element.nodes.toList()) { if (child is html.Element) { - result.addAll(_handleElement(child, {"subtype": StyleKey.quote})); + result.addAll( + _handleElement(child, {"subtype": BuiltInAttributeKey.quote})); } } @@ -174,11 +175,11 @@ class HTMLToNodesConverter { final fontWeightStr = cssMap["font-weight"]; if (fontWeightStr != null) { if (fontWeightStr == "bold") { - attrs[StyleKey.bold] = true; + attrs[BuiltInAttributeKey.bold] = true; } else { int? weight = int.tryParse(fontWeightStr); if (weight != null && weight > 500) { - attrs[StyleKey.bold] = true; + attrs[BuiltInAttributeKey.bold] = true; } } } @@ -193,12 +194,12 @@ class HTMLToNodesConverter { ? null : ColorExtension.tryFromRgbaString(backgroundColorStr); if (backgroundColor != null) { - attrs[StyleKey.backgroundColor] = + attrs[BuiltInAttributeKey.backgroundColor] = '0x${backgroundColor.value.toRadixString(16)}'; } if (cssMap["font-style"] == "italic") { - attrs[StyleKey.italic] = true; + attrs[BuiltInAttributeKey.italic] = true; } return attrs.isEmpty ? null : attrs; @@ -208,9 +209,9 @@ class HTMLToNodesConverter { final decorations = decorationStr.split(" "); for (final d in decorations) { if (d == "line-through") { - attrs[StyleKey.strikethrough] = true; + attrs[BuiltInAttributeKey.strikethrough] = true; } else if (d == "underline") { - attrs[StyleKey.underline] = true; + attrs[BuiltInAttributeKey.underline] = true; } } } @@ -228,13 +229,13 @@ class HTMLToNodesConverter { delta.insert(element.text, attributes); } else if (element.localName == HTMLTag.strong || element.localName == HTMLTag.bold) { - delta.insert(element.text, {StyleKey.bold: true}); + delta.insert(element.text, {BuiltInAttributeKey.bold: true}); } else if (element.localName == HTMLTag.underline) { - delta.insert(element.text, {StyleKey.underline: true}); + delta.insert(element.text, {BuiltInAttributeKey.underline: true}); } else if (element.localName == HTMLTag.italic) { - delta.insert(element.text, {StyleKey.italic: true}); + delta.insert(element.text, {BuiltInAttributeKey.italic: true}); } else if (element.localName == HTMLTag.del) { - delta.insert(element.text, {StyleKey.strikethrough: true}); + delta.insert(element.text, {BuiltInAttributeKey.strikethrough: true}); } else { delta.insert(element.text); } @@ -273,7 +274,7 @@ class HTMLToNodesConverter { final textNode = TextNode(type: "text", delta: delta, attributes: attributes); if (isCheckbox) { - textNode.attributes["subtype"] = StyleKey.checkbox; + textNode.attributes["subtype"] = BuiltInAttributeKey.checkbox; textNode.attributes["checkbox"] = checked; } return textNode; @@ -291,8 +292,8 @@ class HTMLToNodesConverter { List _handleUnorderedList(html.Element element) { final result = []; for (var child in element.children) { - result.addAll( - _handleListElement(child, {"subtype": StyleKey.bulletedList})); + result.addAll(_handleListElement( + child, {"subtype": BuiltInAttributeKey.bulletedList})); } return result; } @@ -302,7 +303,7 @@ class HTMLToNodesConverter { for (var i = 0; i < element.children.length; i++) { final child = element.children[i]; result.addAll(_handleListElement( - child, {"subtype": StyleKey.numberList, "number": i + 1})); + child, {"subtype": BuiltInAttributeKey.numberList, "number": i + 1})); } return result; } @@ -401,7 +402,8 @@ class NodesToHTMLConverter { _addElement(TextNode textNode, html.Element element) { if (element.localName == HTMLTag.list) { - final isNumbered = textNode.attributes["subtype"] == StyleKey.numberList; + final isNumbered = + textNode.attributes["subtype"] == BuiltInAttributeKey.numberList; _stashListContainer ??= html.Element.tag( isNumbered ? HTMLTag.orderedList : HTMLTag.unorderedList); _stashListContainer?.append(element); @@ -433,10 +435,10 @@ class NodesToHTMLConverter { String _textDecorationsFromAttributes(Attributes attributes) { var textDecoration = []; - if (attributes[StyleKey.strikethrough] == true) { + if (attributes[BuiltInAttributeKey.strikethrough] == true) { textDecoration.add("line-through"); } - if (attributes[StyleKey.underline] == true) { + if (attributes[BuiltInAttributeKey.underline] == true) { textDecoration.add("underline"); } @@ -445,19 +447,19 @@ class NodesToHTMLConverter { String _attributesToCssStyle(Map attributes) { final cssMap = {}; - if (attributes[StyleKey.backgroundColor] != null) { + if (attributes[BuiltInAttributeKey.backgroundColor] != null) { final color = Color( - int.parse(attributes[StyleKey.backgroundColor]), + int.parse(attributes[BuiltInAttributeKey.backgroundColor]), ); cssMap["background-color"] = color.toRgbaString(); } - if (attributes[StyleKey.color] != null) { + if (attributes[BuiltInAttributeKey.color] != null) { final color = Color( - int.parse(attributes[StyleKey.color]), + int.parse(attributes[BuiltInAttributeKey.color]), ); cssMap["color"] = color.toRgbaString(); } - if (attributes[StyleKey.bold] == true) { + if (attributes[BuiltInAttributeKey.bold] == true) { cssMap["font-weight"] = "bold"; } @@ -466,7 +468,7 @@ class NodesToHTMLConverter { cssMap["text-decoration"] = textDecoration; } - if (attributes[StyleKey.italic] == true) { + if (attributes[BuiltInAttributeKey.italic] == true) { cssMap["font-style"] = "italic"; } return _cssMapToCssStyle(cssMap); @@ -507,23 +509,24 @@ class NodesToHTMLConverter { final childNodes = []; String tagName = HTMLTag.paragraph; - if (subType == StyleKey.bulletedList || subType == StyleKey.numberList) { + if (subType == BuiltInAttributeKey.bulletedList || + subType == BuiltInAttributeKey.numberList) { tagName = HTMLTag.list; - } else if (subType == StyleKey.checkbox) { + } else if (subType == BuiltInAttributeKey.checkbox) { final node = html.Element.html(''); if (checked != null && checked) { node.attributes["checked"] = "true"; } childNodes.add(node); - } else if (subType == StyleKey.heading) { - if (heading == StyleKey.h1) { + } else if (subType == BuiltInAttributeKey.heading) { + if (heading == BuiltInAttributeKey.h1) { tagName = HTMLTag.h1; - } else if (heading == StyleKey.h2) { + } else if (heading == BuiltInAttributeKey.h2) { tagName = HTMLTag.h2; - } else if (heading == StyleKey.h3) { + } else if (heading == BuiltInAttributeKey.h3) { tagName = HTMLTag.h3; } - } else if (subType == StyleKey.quote) { + } else if (subType == BuiltInAttributeKey.quote) { tagName = HTMLTag.blockQuote; } @@ -531,22 +534,23 @@ class NodesToHTMLConverter { if (op is TextInsert) { final attributes = op.attributes; if (attributes != null) { - if (attributes.length == 1 && attributes[StyleKey.bold] == true) { + if (attributes.length == 1 && + attributes[BuiltInAttributeKey.bold] == true) { final strong = html.Element.tag(HTMLTag.strong); strong.append(html.Text(op.content)); childNodes.add(strong); } else if (attributes.length == 1 && - attributes[StyleKey.underline] == true) { + attributes[BuiltInAttributeKey.underline] == true) { final strong = html.Element.tag(HTMLTag.underline); strong.append(html.Text(op.content)); childNodes.add(strong); } else if (attributes.length == 1 && - attributes[StyleKey.italic] == true) { + attributes[BuiltInAttributeKey.italic] == true) { final strong = html.Element.tag(HTMLTag.italic); strong.append(html.Text(op.content)); childNodes.add(strong); } else if (attributes.length == 1 && - attributes[StyleKey.strikethrough] == true) { + attributes[BuiltInAttributeKey.strikethrough] == true) { final strong = html.Element.tag(HTMLTag.del); strong.append(html.Text(op.content)); childNodes.add(strong); diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/src/l10n/intl/messages_all.dart b/frontend/app_flowy/packages/appflowy_editor/lib/src/l10n/intl/messages_all.dart new file mode 100644 index 0000000000..1dc2273626 --- /dev/null +++ b/frontend/app_flowy/packages/appflowy_editor/lib/src/l10n/intl/messages_all.dart @@ -0,0 +1,126 @@ +// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart +// This is a library that looks up messages for specific locales by +// delegating to the appropriate library. + +// Ignore issues from commonly used lints in this file. +// ignore_for_file:implementation_imports, file_names, unnecessary_new +// ignore_for_file:unnecessary_brace_in_string_interps, directives_ordering +// ignore_for_file:argument_type_not_assignable, invalid_assignment +// ignore_for_file:prefer_single_quotes, prefer_generic_function_type_aliases +// ignore_for_file:comment_references + +import 'dart:async'; + +import 'package:intl/intl.dart'; +import 'package:intl/message_lookup_by_library.dart'; +import 'package:intl/src/intl_helpers.dart'; + +import 'messages_ca.dart' as messages_ca; +import 'messages_de-DE.dart' as messages_de_de; +import 'messages_en.dart' as messages_en; +import 'messages_es-VE.dart' as messages_es_ve; +import 'messages_fr-CA.dart' as messages_fr_ca; +import 'messages_fr-FR.dart' as messages_fr_fr; +import 'messages_hu-HU.dart' as messages_hu_hu; +import 'messages_id-ID.dart' as messages_id_id; +import 'messages_it-IT.dart' as messages_it_it; +import 'messages_ja-JP.dart' as messages_ja_jp; +import 'messages_pl-PL.dart' as messages_pl_pl; +import 'messages_pt-BR.dart' as messages_pt_br; +import 'messages_pt-PT.dart' as messages_pt_pt; +import 'messages_ru-RU.dart' as messages_ru_ru; +import 'messages_tr-TR.dart' as messages_tr_tr; +import 'messages_zh-CN.dart' as messages_zh_cn; +import 'messages_zh-TW.dart' as messages_zh_tw; + +typedef Future LibraryLoader(); +Map _deferredLibraries = { + 'ca': () => new Future.value(null), + 'de_DE': () => new Future.value(null), + 'en': () => new Future.value(null), + 'es_VE': () => new Future.value(null), + 'fr_CA': () => new Future.value(null), + 'fr_FR': () => new Future.value(null), + 'hu_HU': () => new Future.value(null), + 'id_ID': () => new Future.value(null), + 'it_IT': () => new Future.value(null), + 'ja_JP': () => new Future.value(null), + 'pl_PL': () => new Future.value(null), + 'pt_BR': () => new Future.value(null), + 'pt_PT': () => new Future.value(null), + 'ru_RU': () => new Future.value(null), + 'tr_TR': () => new Future.value(null), + 'zh_CN': () => new Future.value(null), + 'zh_TW': () => new Future.value(null), +}; + +MessageLookupByLibrary? _findExact(String localeName) { + switch (localeName) { + case 'ca': + return messages_ca.messages; + case 'de_DE': + return messages_de_de.messages; + case 'en': + return messages_en.messages; + case 'es_VE': + return messages_es_ve.messages; + case 'fr_CA': + return messages_fr_ca.messages; + case 'fr_FR': + return messages_fr_fr.messages; + case 'hu_HU': + return messages_hu_hu.messages; + case 'id_ID': + return messages_id_id.messages; + case 'it_IT': + return messages_it_it.messages; + case 'ja_JP': + return messages_ja_jp.messages; + case 'pl_PL': + return messages_pl_pl.messages; + case 'pt_BR': + return messages_pt_br.messages; + case 'pt_PT': + return messages_pt_pt.messages; + case 'ru_RU': + return messages_ru_ru.messages; + case 'tr_TR': + return messages_tr_tr.messages; + case 'zh_CN': + return messages_zh_cn.messages; + case 'zh_TW': + return messages_zh_tw.messages; + default: + return null; + } +} + +/// User programs should call this before using [localeName] for messages. +Future initializeMessages(String localeName) async { + var availableLocale = Intl.verifiedLocale( + localeName, (locale) => _deferredLibraries[locale] != null, + onFailure: (_) => null); + if (availableLocale == null) { + return new Future.value(false); + } + var lib = _deferredLibraries[availableLocale]; + await (lib == null ? new Future.value(false) : lib()); + initializeInternalMessageLookup(() => new CompositeMessageLookup()); + messageLookup.addLocale(availableLocale, _findGeneratedMessagesFor); + return new Future.value(true); +} + +bool _messagesExistFor(String locale) { + try { + return _findExact(locale) != null; + } catch (e) { + return false; + } +} + +MessageLookupByLibrary? _findGeneratedMessagesFor(String locale) { + var actualLocale = + Intl.verifiedLocale(locale, _messagesExistFor, onFailure: (_) => null); + if (actualLocale == null) return null; + return _findExact(actualLocale); +} diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/src/l10n/intl/messages_ca.dart b/frontend/app_flowy/packages/appflowy_editor/lib/src/l10n/intl/messages_ca.dart new file mode 100644 index 0000000000..10c178027b --- /dev/null +++ b/frontend/app_flowy/packages/appflowy_editor/lib/src/l10n/intl/messages_ca.dart @@ -0,0 +1,42 @@ +// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart +// This is a library that provides messages for a ca locale. All the +// messages from the main program should be duplicated here with the same +// function name. + +// Ignore issues from commonly used lints in this file. +// ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new +// ignore_for_file:prefer_single_quotes,comment_references, directives_ordering +// ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases +// ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes +// ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes + +import 'package:intl/intl.dart'; +import 'package:intl/message_lookup_by_library.dart'; + +final messages = new MessageLookup(); + +typedef String MessageIfAbsent(String messageStr, List args); + +class MessageLookup extends MessageLookupByLibrary { + String get localeName => 'ca'; + + final messages = _notInlinedMessages(_notInlinedMessages); + static Map _notInlinedMessages(_) => { + "bold": MessageLookupByLibrary.simpleMessage(""), + "bulletedList": MessageLookupByLibrary.simpleMessage(""), + "checkbox": MessageLookupByLibrary.simpleMessage(""), + "embedCode": MessageLookupByLibrary.simpleMessage(""), + "heading1": MessageLookupByLibrary.simpleMessage(""), + "heading2": MessageLookupByLibrary.simpleMessage(""), + "heading3": MessageLookupByLibrary.simpleMessage(""), + "highlight": MessageLookupByLibrary.simpleMessage(""), + "image": MessageLookupByLibrary.simpleMessage(""), + "italic": MessageLookupByLibrary.simpleMessage(""), + "link": MessageLookupByLibrary.simpleMessage(""), + "numberedList": MessageLookupByLibrary.simpleMessage(""), + "quote": MessageLookupByLibrary.simpleMessage(""), + "strikethrough": MessageLookupByLibrary.simpleMessage(""), + "text": MessageLookupByLibrary.simpleMessage(""), + "underline": MessageLookupByLibrary.simpleMessage("") + }; +} diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/src/l10n/intl/messages_de-DE.dart b/frontend/app_flowy/packages/appflowy_editor/lib/src/l10n/intl/messages_de-DE.dart new file mode 100644 index 0000000000..a1aaebcddb --- /dev/null +++ b/frontend/app_flowy/packages/appflowy_editor/lib/src/l10n/intl/messages_de-DE.dart @@ -0,0 +1,42 @@ +// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart +// This is a library that provides messages for a de_DE locale. All the +// messages from the main program should be duplicated here with the same +// function name. + +// Ignore issues from commonly used lints in this file. +// ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new +// ignore_for_file:prefer_single_quotes,comment_references, directives_ordering +// ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases +// ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes +// ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes + +import 'package:intl/intl.dart'; +import 'package:intl/message_lookup_by_library.dart'; + +final messages = new MessageLookup(); + +typedef String MessageIfAbsent(String messageStr, List args); + +class MessageLookup extends MessageLookupByLibrary { + String get localeName => 'de_DE'; + + final messages = _notInlinedMessages(_notInlinedMessages); + static Map _notInlinedMessages(_) => { + "bold": MessageLookupByLibrary.simpleMessage(""), + "bulletedList": MessageLookupByLibrary.simpleMessage(""), + "checkbox": MessageLookupByLibrary.simpleMessage(""), + "embedCode": MessageLookupByLibrary.simpleMessage(""), + "heading1": MessageLookupByLibrary.simpleMessage(""), + "heading2": MessageLookupByLibrary.simpleMessage(""), + "heading3": MessageLookupByLibrary.simpleMessage(""), + "highlight": MessageLookupByLibrary.simpleMessage(""), + "image": MessageLookupByLibrary.simpleMessage(""), + "italic": MessageLookupByLibrary.simpleMessage(""), + "link": MessageLookupByLibrary.simpleMessage(""), + "numberedList": MessageLookupByLibrary.simpleMessage(""), + "quote": MessageLookupByLibrary.simpleMessage(""), + "strikethrough": MessageLookupByLibrary.simpleMessage(""), + "text": MessageLookupByLibrary.simpleMessage(""), + "underline": MessageLookupByLibrary.simpleMessage("") + }; +} diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/src/l10n/intl/messages_en.dart b/frontend/app_flowy/packages/appflowy_editor/lib/src/l10n/intl/messages_en.dart new file mode 100644 index 0000000000..0a834ae7eb --- /dev/null +++ b/frontend/app_flowy/packages/appflowy_editor/lib/src/l10n/intl/messages_en.dart @@ -0,0 +1,42 @@ +// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart +// This is a library that provides messages for a en locale. All the +// messages from the main program should be duplicated here with the same +// function name. + +// Ignore issues from commonly used lints in this file. +// ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new +// ignore_for_file:prefer_single_quotes,comment_references, directives_ordering +// ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases +// ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes +// ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes + +import 'package:intl/intl.dart'; +import 'package:intl/message_lookup_by_library.dart'; + +final messages = new MessageLookup(); + +typedef String MessageIfAbsent(String messageStr, List args); + +class MessageLookup extends MessageLookupByLibrary { + String get localeName => 'en'; + + final messages = _notInlinedMessages(_notInlinedMessages); + static Map _notInlinedMessages(_) => { + "bold": MessageLookupByLibrary.simpleMessage("Bold"), + "bulletedList": MessageLookupByLibrary.simpleMessage("Bulleted List"), + "checkbox": MessageLookupByLibrary.simpleMessage("Checkbox"), + "embedCode": MessageLookupByLibrary.simpleMessage("Embed Code"), + "heading1": MessageLookupByLibrary.simpleMessage("H1"), + "heading2": MessageLookupByLibrary.simpleMessage("H2"), + "heading3": MessageLookupByLibrary.simpleMessage("H3"), + "highlight": MessageLookupByLibrary.simpleMessage("Highlight"), + "image": MessageLookupByLibrary.simpleMessage("Image"), + "italic": MessageLookupByLibrary.simpleMessage("Italic"), + "link": MessageLookupByLibrary.simpleMessage("Link"), + "numberedList": MessageLookupByLibrary.simpleMessage("Numbered List"), + "quote": MessageLookupByLibrary.simpleMessage("Quote"), + "strikethrough": MessageLookupByLibrary.simpleMessage("Strikethrough"), + "text": MessageLookupByLibrary.simpleMessage("Text"), + "underline": MessageLookupByLibrary.simpleMessage("Underline") + }; +} diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/src/l10n/intl/messages_es-VE.dart b/frontend/app_flowy/packages/appflowy_editor/lib/src/l10n/intl/messages_es-VE.dart new file mode 100644 index 0000000000..d5b4cb5b06 --- /dev/null +++ b/frontend/app_flowy/packages/appflowy_editor/lib/src/l10n/intl/messages_es-VE.dart @@ -0,0 +1,42 @@ +// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart +// This is a library that provides messages for a es_VE locale. All the +// messages from the main program should be duplicated here with the same +// function name. + +// Ignore issues from commonly used lints in this file. +// ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new +// ignore_for_file:prefer_single_quotes,comment_references, directives_ordering +// ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases +// ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes +// ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes + +import 'package:intl/intl.dart'; +import 'package:intl/message_lookup_by_library.dart'; + +final messages = new MessageLookup(); + +typedef String MessageIfAbsent(String messageStr, List args); + +class MessageLookup extends MessageLookupByLibrary { + String get localeName => 'es_VE'; + + final messages = _notInlinedMessages(_notInlinedMessages); + static Map _notInlinedMessages(_) => { + "bold": MessageLookupByLibrary.simpleMessage(""), + "bulletedList": MessageLookupByLibrary.simpleMessage(""), + "checkbox": MessageLookupByLibrary.simpleMessage(""), + "embedCode": MessageLookupByLibrary.simpleMessage(""), + "heading1": MessageLookupByLibrary.simpleMessage(""), + "heading2": MessageLookupByLibrary.simpleMessage(""), + "heading3": MessageLookupByLibrary.simpleMessage(""), + "highlight": MessageLookupByLibrary.simpleMessage(""), + "image": MessageLookupByLibrary.simpleMessage(""), + "italic": MessageLookupByLibrary.simpleMessage(""), + "link": MessageLookupByLibrary.simpleMessage(""), + "numberedList": MessageLookupByLibrary.simpleMessage(""), + "quote": MessageLookupByLibrary.simpleMessage(""), + "strikethrough": MessageLookupByLibrary.simpleMessage(""), + "text": MessageLookupByLibrary.simpleMessage(""), + "underline": MessageLookupByLibrary.simpleMessage("") + }; +} diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/src/l10n/intl/messages_fr-CA.dart b/frontend/app_flowy/packages/appflowy_editor/lib/src/l10n/intl/messages_fr-CA.dart new file mode 100644 index 0000000000..7dd3517ac5 --- /dev/null +++ b/frontend/app_flowy/packages/appflowy_editor/lib/src/l10n/intl/messages_fr-CA.dart @@ -0,0 +1,42 @@ +// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart +// This is a library that provides messages for a fr_CA locale. All the +// messages from the main program should be duplicated here with the same +// function name. + +// Ignore issues from commonly used lints in this file. +// ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new +// ignore_for_file:prefer_single_quotes,comment_references, directives_ordering +// ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases +// ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes +// ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes + +import 'package:intl/intl.dart'; +import 'package:intl/message_lookup_by_library.dart'; + +final messages = new MessageLookup(); + +typedef String MessageIfAbsent(String messageStr, List args); + +class MessageLookup extends MessageLookupByLibrary { + String get localeName => 'fr_CA'; + + final messages = _notInlinedMessages(_notInlinedMessages); + static Map _notInlinedMessages(_) => { + "bold": MessageLookupByLibrary.simpleMessage(""), + "bulletedList": MessageLookupByLibrary.simpleMessage(""), + "checkbox": MessageLookupByLibrary.simpleMessage(""), + "embedCode": MessageLookupByLibrary.simpleMessage(""), + "heading1": MessageLookupByLibrary.simpleMessage(""), + "heading2": MessageLookupByLibrary.simpleMessage(""), + "heading3": MessageLookupByLibrary.simpleMessage(""), + "highlight": MessageLookupByLibrary.simpleMessage(""), + "image": MessageLookupByLibrary.simpleMessage(""), + "italic": MessageLookupByLibrary.simpleMessage(""), + "link": MessageLookupByLibrary.simpleMessage(""), + "numberedList": MessageLookupByLibrary.simpleMessage(""), + "quote": MessageLookupByLibrary.simpleMessage(""), + "strikethrough": MessageLookupByLibrary.simpleMessage(""), + "text": MessageLookupByLibrary.simpleMessage(""), + "underline": MessageLookupByLibrary.simpleMessage("") + }; +} diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/src/l10n/intl/messages_fr-FR.dart b/frontend/app_flowy/packages/appflowy_editor/lib/src/l10n/intl/messages_fr-FR.dart new file mode 100644 index 0000000000..dbe21e0e02 --- /dev/null +++ b/frontend/app_flowy/packages/appflowy_editor/lib/src/l10n/intl/messages_fr-FR.dart @@ -0,0 +1,42 @@ +// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart +// This is a library that provides messages for a fr_FR locale. All the +// messages from the main program should be duplicated here with the same +// function name. + +// Ignore issues from commonly used lints in this file. +// ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new +// ignore_for_file:prefer_single_quotes,comment_references, directives_ordering +// ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases +// ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes +// ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes + +import 'package:intl/intl.dart'; +import 'package:intl/message_lookup_by_library.dart'; + +final messages = new MessageLookup(); + +typedef String MessageIfAbsent(String messageStr, List args); + +class MessageLookup extends MessageLookupByLibrary { + String get localeName => 'fr_FR'; + + final messages = _notInlinedMessages(_notInlinedMessages); + static Map _notInlinedMessages(_) => { + "bold": MessageLookupByLibrary.simpleMessage(""), + "bulletedList": MessageLookupByLibrary.simpleMessage(""), + "checkbox": MessageLookupByLibrary.simpleMessage(""), + "embedCode": MessageLookupByLibrary.simpleMessage(""), + "heading1": MessageLookupByLibrary.simpleMessage(""), + "heading2": MessageLookupByLibrary.simpleMessage(""), + "heading3": MessageLookupByLibrary.simpleMessage(""), + "highlight": MessageLookupByLibrary.simpleMessage(""), + "image": MessageLookupByLibrary.simpleMessage(""), + "italic": MessageLookupByLibrary.simpleMessage(""), + "link": MessageLookupByLibrary.simpleMessage(""), + "numberedList": MessageLookupByLibrary.simpleMessage(""), + "quote": MessageLookupByLibrary.simpleMessage(""), + "strikethrough": MessageLookupByLibrary.simpleMessage(""), + "text": MessageLookupByLibrary.simpleMessage(""), + "underline": MessageLookupByLibrary.simpleMessage("") + }; +} diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/src/l10n/intl/messages_hu-HU.dart b/frontend/app_flowy/packages/appflowy_editor/lib/src/l10n/intl/messages_hu-HU.dart new file mode 100644 index 0000000000..ac9acad543 --- /dev/null +++ b/frontend/app_flowy/packages/appflowy_editor/lib/src/l10n/intl/messages_hu-HU.dart @@ -0,0 +1,42 @@ +// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart +// This is a library that provides messages for a hu_HU locale. All the +// messages from the main program should be duplicated here with the same +// function name. + +// Ignore issues from commonly used lints in this file. +// ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new +// ignore_for_file:prefer_single_quotes,comment_references, directives_ordering +// ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases +// ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes +// ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes + +import 'package:intl/intl.dart'; +import 'package:intl/message_lookup_by_library.dart'; + +final messages = new MessageLookup(); + +typedef String MessageIfAbsent(String messageStr, List args); + +class MessageLookup extends MessageLookupByLibrary { + String get localeName => 'hu_HU'; + + final messages = _notInlinedMessages(_notInlinedMessages); + static Map _notInlinedMessages(_) => { + "bold": MessageLookupByLibrary.simpleMessage(""), + "bulletedList": MessageLookupByLibrary.simpleMessage(""), + "checkbox": MessageLookupByLibrary.simpleMessage(""), + "embedCode": MessageLookupByLibrary.simpleMessage(""), + "heading1": MessageLookupByLibrary.simpleMessage(""), + "heading2": MessageLookupByLibrary.simpleMessage(""), + "heading3": MessageLookupByLibrary.simpleMessage(""), + "highlight": MessageLookupByLibrary.simpleMessage(""), + "image": MessageLookupByLibrary.simpleMessage(""), + "italic": MessageLookupByLibrary.simpleMessage(""), + "link": MessageLookupByLibrary.simpleMessage(""), + "numberedList": MessageLookupByLibrary.simpleMessage(""), + "quote": MessageLookupByLibrary.simpleMessage(""), + "strikethrough": MessageLookupByLibrary.simpleMessage(""), + "text": MessageLookupByLibrary.simpleMessage(""), + "underline": MessageLookupByLibrary.simpleMessage("") + }; +} diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/src/l10n/intl/messages_id-ID.dart b/frontend/app_flowy/packages/appflowy_editor/lib/src/l10n/intl/messages_id-ID.dart new file mode 100644 index 0000000000..e594bacdec --- /dev/null +++ b/frontend/app_flowy/packages/appflowy_editor/lib/src/l10n/intl/messages_id-ID.dart @@ -0,0 +1,42 @@ +// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart +// This is a library that provides messages for a id_ID locale. All the +// messages from the main program should be duplicated here with the same +// function name. + +// Ignore issues from commonly used lints in this file. +// ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new +// ignore_for_file:prefer_single_quotes,comment_references, directives_ordering +// ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases +// ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes +// ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes + +import 'package:intl/intl.dart'; +import 'package:intl/message_lookup_by_library.dart'; + +final messages = new MessageLookup(); + +typedef String MessageIfAbsent(String messageStr, List args); + +class MessageLookup extends MessageLookupByLibrary { + String get localeName => 'id_ID'; + + final messages = _notInlinedMessages(_notInlinedMessages); + static Map _notInlinedMessages(_) => { + "bold": MessageLookupByLibrary.simpleMessage(""), + "bulletedList": MessageLookupByLibrary.simpleMessage(""), + "checkbox": MessageLookupByLibrary.simpleMessage(""), + "embedCode": MessageLookupByLibrary.simpleMessage(""), + "heading1": MessageLookupByLibrary.simpleMessage(""), + "heading2": MessageLookupByLibrary.simpleMessage(""), + "heading3": MessageLookupByLibrary.simpleMessage(""), + "highlight": MessageLookupByLibrary.simpleMessage(""), + "image": MessageLookupByLibrary.simpleMessage(""), + "italic": MessageLookupByLibrary.simpleMessage(""), + "link": MessageLookupByLibrary.simpleMessage(""), + "numberedList": MessageLookupByLibrary.simpleMessage(""), + "quote": MessageLookupByLibrary.simpleMessage(""), + "strikethrough": MessageLookupByLibrary.simpleMessage(""), + "text": MessageLookupByLibrary.simpleMessage(""), + "underline": MessageLookupByLibrary.simpleMessage("") + }; +} diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/src/l10n/intl/messages_it-IT.dart b/frontend/app_flowy/packages/appflowy_editor/lib/src/l10n/intl/messages_it-IT.dart new file mode 100644 index 0000000000..6717858291 --- /dev/null +++ b/frontend/app_flowy/packages/appflowy_editor/lib/src/l10n/intl/messages_it-IT.dart @@ -0,0 +1,42 @@ +// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart +// This is a library that provides messages for a it_IT locale. All the +// messages from the main program should be duplicated here with the same +// function name. + +// Ignore issues from commonly used lints in this file. +// ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new +// ignore_for_file:prefer_single_quotes,comment_references, directives_ordering +// ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases +// ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes +// ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes + +import 'package:intl/intl.dart'; +import 'package:intl/message_lookup_by_library.dart'; + +final messages = new MessageLookup(); + +typedef String MessageIfAbsent(String messageStr, List args); + +class MessageLookup extends MessageLookupByLibrary { + String get localeName => 'it_IT'; + + final messages = _notInlinedMessages(_notInlinedMessages); + static Map _notInlinedMessages(_) => { + "bold": MessageLookupByLibrary.simpleMessage(""), + "bulletedList": MessageLookupByLibrary.simpleMessage(""), + "checkbox": MessageLookupByLibrary.simpleMessage(""), + "embedCode": MessageLookupByLibrary.simpleMessage(""), + "heading1": MessageLookupByLibrary.simpleMessage(""), + "heading2": MessageLookupByLibrary.simpleMessage(""), + "heading3": MessageLookupByLibrary.simpleMessage(""), + "highlight": MessageLookupByLibrary.simpleMessage(""), + "image": MessageLookupByLibrary.simpleMessage(""), + "italic": MessageLookupByLibrary.simpleMessage(""), + "link": MessageLookupByLibrary.simpleMessage(""), + "numberedList": MessageLookupByLibrary.simpleMessage(""), + "quote": MessageLookupByLibrary.simpleMessage(""), + "strikethrough": MessageLookupByLibrary.simpleMessage(""), + "text": MessageLookupByLibrary.simpleMessage(""), + "underline": MessageLookupByLibrary.simpleMessage("") + }; +} diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/src/l10n/intl/messages_ja-JP.dart b/frontend/app_flowy/packages/appflowy_editor/lib/src/l10n/intl/messages_ja-JP.dart new file mode 100644 index 0000000000..925acc9668 --- /dev/null +++ b/frontend/app_flowy/packages/appflowy_editor/lib/src/l10n/intl/messages_ja-JP.dart @@ -0,0 +1,42 @@ +// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart +// This is a library that provides messages for a ja_JP locale. All the +// messages from the main program should be duplicated here with the same +// function name. + +// Ignore issues from commonly used lints in this file. +// ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new +// ignore_for_file:prefer_single_quotes,comment_references, directives_ordering +// ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases +// ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes +// ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes + +import 'package:intl/intl.dart'; +import 'package:intl/message_lookup_by_library.dart'; + +final messages = new MessageLookup(); + +typedef String MessageIfAbsent(String messageStr, List args); + +class MessageLookup extends MessageLookupByLibrary { + String get localeName => 'ja_JP'; + + final messages = _notInlinedMessages(_notInlinedMessages); + static Map _notInlinedMessages(_) => { + "bold": MessageLookupByLibrary.simpleMessage(""), + "bulletedList": MessageLookupByLibrary.simpleMessage(""), + "checkbox": MessageLookupByLibrary.simpleMessage(""), + "embedCode": MessageLookupByLibrary.simpleMessage(""), + "heading1": MessageLookupByLibrary.simpleMessage(""), + "heading2": MessageLookupByLibrary.simpleMessage(""), + "heading3": MessageLookupByLibrary.simpleMessage(""), + "highlight": MessageLookupByLibrary.simpleMessage(""), + "image": MessageLookupByLibrary.simpleMessage(""), + "italic": MessageLookupByLibrary.simpleMessage(""), + "link": MessageLookupByLibrary.simpleMessage(""), + "numberedList": MessageLookupByLibrary.simpleMessage(""), + "quote": MessageLookupByLibrary.simpleMessage(""), + "strikethrough": MessageLookupByLibrary.simpleMessage(""), + "text": MessageLookupByLibrary.simpleMessage(""), + "underline": MessageLookupByLibrary.simpleMessage("") + }; +} diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/src/l10n/intl/messages_pl-PL.dart b/frontend/app_flowy/packages/appflowy_editor/lib/src/l10n/intl/messages_pl-PL.dart new file mode 100644 index 0000000000..30a9cc38b2 --- /dev/null +++ b/frontend/app_flowy/packages/appflowy_editor/lib/src/l10n/intl/messages_pl-PL.dart @@ -0,0 +1,42 @@ +// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart +// This is a library that provides messages for a pl_PL locale. All the +// messages from the main program should be duplicated here with the same +// function name. + +// Ignore issues from commonly used lints in this file. +// ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new +// ignore_for_file:prefer_single_quotes,comment_references, directives_ordering +// ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases +// ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes +// ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes + +import 'package:intl/intl.dart'; +import 'package:intl/message_lookup_by_library.dart'; + +final messages = new MessageLookup(); + +typedef String MessageIfAbsent(String messageStr, List args); + +class MessageLookup extends MessageLookupByLibrary { + String get localeName => 'pl_PL'; + + final messages = _notInlinedMessages(_notInlinedMessages); + static Map _notInlinedMessages(_) => { + "bold": MessageLookupByLibrary.simpleMessage(""), + "bulletedList": MessageLookupByLibrary.simpleMessage(""), + "checkbox": MessageLookupByLibrary.simpleMessage(""), + "embedCode": MessageLookupByLibrary.simpleMessage(""), + "heading1": MessageLookupByLibrary.simpleMessage(""), + "heading2": MessageLookupByLibrary.simpleMessage(""), + "heading3": MessageLookupByLibrary.simpleMessage(""), + "highlight": MessageLookupByLibrary.simpleMessage(""), + "image": MessageLookupByLibrary.simpleMessage(""), + "italic": MessageLookupByLibrary.simpleMessage(""), + "link": MessageLookupByLibrary.simpleMessage(""), + "numberedList": MessageLookupByLibrary.simpleMessage(""), + "quote": MessageLookupByLibrary.simpleMessage(""), + "strikethrough": MessageLookupByLibrary.simpleMessage(""), + "text": MessageLookupByLibrary.simpleMessage(""), + "underline": MessageLookupByLibrary.simpleMessage("") + }; +} diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/src/l10n/intl/messages_pt-BR.dart b/frontend/app_flowy/packages/appflowy_editor/lib/src/l10n/intl/messages_pt-BR.dart new file mode 100644 index 0000000000..c7551f9d78 --- /dev/null +++ b/frontend/app_flowy/packages/appflowy_editor/lib/src/l10n/intl/messages_pt-BR.dart @@ -0,0 +1,42 @@ +// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart +// This is a library that provides messages for a pt_BR locale. All the +// messages from the main program should be duplicated here with the same +// function name. + +// Ignore issues from commonly used lints in this file. +// ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new +// ignore_for_file:prefer_single_quotes,comment_references, directives_ordering +// ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases +// ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes +// ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes + +import 'package:intl/intl.dart'; +import 'package:intl/message_lookup_by_library.dart'; + +final messages = new MessageLookup(); + +typedef String MessageIfAbsent(String messageStr, List args); + +class MessageLookup extends MessageLookupByLibrary { + String get localeName => 'pt_BR'; + + final messages = _notInlinedMessages(_notInlinedMessages); + static Map _notInlinedMessages(_) => { + "bold": MessageLookupByLibrary.simpleMessage(""), + "bulletedList": MessageLookupByLibrary.simpleMessage(""), + "checkbox": MessageLookupByLibrary.simpleMessage(""), + "embedCode": MessageLookupByLibrary.simpleMessage(""), + "heading1": MessageLookupByLibrary.simpleMessage(""), + "heading2": MessageLookupByLibrary.simpleMessage(""), + "heading3": MessageLookupByLibrary.simpleMessage(""), + "highlight": MessageLookupByLibrary.simpleMessage(""), + "image": MessageLookupByLibrary.simpleMessage(""), + "italic": MessageLookupByLibrary.simpleMessage(""), + "link": MessageLookupByLibrary.simpleMessage(""), + "numberedList": MessageLookupByLibrary.simpleMessage(""), + "quote": MessageLookupByLibrary.simpleMessage(""), + "strikethrough": MessageLookupByLibrary.simpleMessage(""), + "text": MessageLookupByLibrary.simpleMessage(""), + "underline": MessageLookupByLibrary.simpleMessage("") + }; +} diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/src/l10n/intl/messages_pt-PT.dart b/frontend/app_flowy/packages/appflowy_editor/lib/src/l10n/intl/messages_pt-PT.dart new file mode 100644 index 0000000000..19a14c2509 --- /dev/null +++ b/frontend/app_flowy/packages/appflowy_editor/lib/src/l10n/intl/messages_pt-PT.dart @@ -0,0 +1,42 @@ +// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart +// This is a library that provides messages for a pt_PT locale. All the +// messages from the main program should be duplicated here with the same +// function name. + +// Ignore issues from commonly used lints in this file. +// ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new +// ignore_for_file:prefer_single_quotes,comment_references, directives_ordering +// ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases +// ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes +// ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes + +import 'package:intl/intl.dart'; +import 'package:intl/message_lookup_by_library.dart'; + +final messages = new MessageLookup(); + +typedef String MessageIfAbsent(String messageStr, List args); + +class MessageLookup extends MessageLookupByLibrary { + String get localeName => 'pt_PT'; + + final messages = _notInlinedMessages(_notInlinedMessages); + static Map _notInlinedMessages(_) => { + "bold": MessageLookupByLibrary.simpleMessage(""), + "bulletedList": MessageLookupByLibrary.simpleMessage(""), + "checkbox": MessageLookupByLibrary.simpleMessage(""), + "embedCode": MessageLookupByLibrary.simpleMessage(""), + "heading1": MessageLookupByLibrary.simpleMessage(""), + "heading2": MessageLookupByLibrary.simpleMessage(""), + "heading3": MessageLookupByLibrary.simpleMessage(""), + "highlight": MessageLookupByLibrary.simpleMessage(""), + "image": MessageLookupByLibrary.simpleMessage(""), + "italic": MessageLookupByLibrary.simpleMessage(""), + "link": MessageLookupByLibrary.simpleMessage(""), + "numberedList": MessageLookupByLibrary.simpleMessage(""), + "quote": MessageLookupByLibrary.simpleMessage(""), + "strikethrough": MessageLookupByLibrary.simpleMessage(""), + "text": MessageLookupByLibrary.simpleMessage(""), + "underline": MessageLookupByLibrary.simpleMessage("") + }; +} diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/src/l10n/intl/messages_ru-RU.dart b/frontend/app_flowy/packages/appflowy_editor/lib/src/l10n/intl/messages_ru-RU.dart new file mode 100644 index 0000000000..ec594b198d --- /dev/null +++ b/frontend/app_flowy/packages/appflowy_editor/lib/src/l10n/intl/messages_ru-RU.dart @@ -0,0 +1,42 @@ +// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart +// This is a library that provides messages for a ru_RU locale. All the +// messages from the main program should be duplicated here with the same +// function name. + +// Ignore issues from commonly used lints in this file. +// ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new +// ignore_for_file:prefer_single_quotes,comment_references, directives_ordering +// ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases +// ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes +// ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes + +import 'package:intl/intl.dart'; +import 'package:intl/message_lookup_by_library.dart'; + +final messages = new MessageLookup(); + +typedef String MessageIfAbsent(String messageStr, List args); + +class MessageLookup extends MessageLookupByLibrary { + String get localeName => 'ru_RU'; + + final messages = _notInlinedMessages(_notInlinedMessages); + static Map _notInlinedMessages(_) => { + "bold": MessageLookupByLibrary.simpleMessage(""), + "bulletedList": MessageLookupByLibrary.simpleMessage(""), + "checkbox": MessageLookupByLibrary.simpleMessage(""), + "embedCode": MessageLookupByLibrary.simpleMessage(""), + "heading1": MessageLookupByLibrary.simpleMessage(""), + "heading2": MessageLookupByLibrary.simpleMessage(""), + "heading3": MessageLookupByLibrary.simpleMessage(""), + "highlight": MessageLookupByLibrary.simpleMessage(""), + "image": MessageLookupByLibrary.simpleMessage(""), + "italic": MessageLookupByLibrary.simpleMessage(""), + "link": MessageLookupByLibrary.simpleMessage(""), + "numberedList": MessageLookupByLibrary.simpleMessage(""), + "quote": MessageLookupByLibrary.simpleMessage(""), + "strikethrough": MessageLookupByLibrary.simpleMessage(""), + "text": MessageLookupByLibrary.simpleMessage(""), + "underline": MessageLookupByLibrary.simpleMessage("") + }; +} diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/src/l10n/intl/messages_tr-TR.dart b/frontend/app_flowy/packages/appflowy_editor/lib/src/l10n/intl/messages_tr-TR.dart new file mode 100644 index 0000000000..50ba27e790 --- /dev/null +++ b/frontend/app_flowy/packages/appflowy_editor/lib/src/l10n/intl/messages_tr-TR.dart @@ -0,0 +1,42 @@ +// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart +// This is a library that provides messages for a tr_TR locale. All the +// messages from the main program should be duplicated here with the same +// function name. + +// Ignore issues from commonly used lints in this file. +// ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new +// ignore_for_file:prefer_single_quotes,comment_references, directives_ordering +// ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases +// ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes +// ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes + +import 'package:intl/intl.dart'; +import 'package:intl/message_lookup_by_library.dart'; + +final messages = new MessageLookup(); + +typedef String MessageIfAbsent(String messageStr, List args); + +class MessageLookup extends MessageLookupByLibrary { + String get localeName => 'tr_TR'; + + final messages = _notInlinedMessages(_notInlinedMessages); + static Map _notInlinedMessages(_) => { + "bold": MessageLookupByLibrary.simpleMessage(""), + "bulletedList": MessageLookupByLibrary.simpleMessage(""), + "checkbox": MessageLookupByLibrary.simpleMessage(""), + "embedCode": MessageLookupByLibrary.simpleMessage(""), + "heading1": MessageLookupByLibrary.simpleMessage(""), + "heading2": MessageLookupByLibrary.simpleMessage(""), + "heading3": MessageLookupByLibrary.simpleMessage(""), + "highlight": MessageLookupByLibrary.simpleMessage(""), + "image": MessageLookupByLibrary.simpleMessage(""), + "italic": MessageLookupByLibrary.simpleMessage(""), + "link": MessageLookupByLibrary.simpleMessage(""), + "numberedList": MessageLookupByLibrary.simpleMessage(""), + "quote": MessageLookupByLibrary.simpleMessage(""), + "strikethrough": MessageLookupByLibrary.simpleMessage(""), + "text": MessageLookupByLibrary.simpleMessage(""), + "underline": MessageLookupByLibrary.simpleMessage("") + }; +} diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/src/l10n/intl/messages_zh-CN.dart b/frontend/app_flowy/packages/appflowy_editor/lib/src/l10n/intl/messages_zh-CN.dart new file mode 100644 index 0000000000..00c6c66f4d --- /dev/null +++ b/frontend/app_flowy/packages/appflowy_editor/lib/src/l10n/intl/messages_zh-CN.dart @@ -0,0 +1,42 @@ +// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart +// This is a library that provides messages for a zh_CN locale. All the +// messages from the main program should be duplicated here with the same +// function name. + +// Ignore issues from commonly used lints in this file. +// ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new +// ignore_for_file:prefer_single_quotes,comment_references, directives_ordering +// ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases +// ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes +// ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes + +import 'package:intl/intl.dart'; +import 'package:intl/message_lookup_by_library.dart'; + +final messages = new MessageLookup(); + +typedef String MessageIfAbsent(String messageStr, List args); + +class MessageLookup extends MessageLookupByLibrary { + String get localeName => 'zh_CN'; + + final messages = _notInlinedMessages(_notInlinedMessages); + static Map _notInlinedMessages(_) => { + "bold": MessageLookupByLibrary.simpleMessage("加粗"), + "bulletedList": MessageLookupByLibrary.simpleMessage("无序列表"), + "checkbox": MessageLookupByLibrary.simpleMessage("复选框"), + "embedCode": MessageLookupByLibrary.simpleMessage("内嵌代码"), + "heading1": MessageLookupByLibrary.simpleMessage("标题 1"), + "heading2": MessageLookupByLibrary.simpleMessage("标题 2"), + "heading3": MessageLookupByLibrary.simpleMessage("标题 3"), + "highlight": MessageLookupByLibrary.simpleMessage("高亮"), + "image": MessageLookupByLibrary.simpleMessage("图片"), + "italic": MessageLookupByLibrary.simpleMessage("斜体"), + "link": MessageLookupByLibrary.simpleMessage("链接"), + "numberedList": MessageLookupByLibrary.simpleMessage("有序列表"), + "quote": MessageLookupByLibrary.simpleMessage("引用"), + "strikethrough": MessageLookupByLibrary.simpleMessage("删除线"), + "text": MessageLookupByLibrary.simpleMessage("文字"), + "underline": MessageLookupByLibrary.simpleMessage("下划线") + }; +} diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/src/l10n/intl/messages_zh-TW.dart b/frontend/app_flowy/packages/appflowy_editor/lib/src/l10n/intl/messages_zh-TW.dart new file mode 100644 index 0000000000..8fbe4a836b --- /dev/null +++ b/frontend/app_flowy/packages/appflowy_editor/lib/src/l10n/intl/messages_zh-TW.dart @@ -0,0 +1,42 @@ +// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart +// This is a library that provides messages for a zh_TW locale. All the +// messages from the main program should be duplicated here with the same +// function name. + +// Ignore issues from commonly used lints in this file. +// ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new +// ignore_for_file:prefer_single_quotes,comment_references, directives_ordering +// ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases +// ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes +// ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes + +import 'package:intl/intl.dart'; +import 'package:intl/message_lookup_by_library.dart'; + +final messages = new MessageLookup(); + +typedef String MessageIfAbsent(String messageStr, List args); + +class MessageLookup extends MessageLookupByLibrary { + String get localeName => 'zh_TW'; + + final messages = _notInlinedMessages(_notInlinedMessages); + static Map _notInlinedMessages(_) => { + "bold": MessageLookupByLibrary.simpleMessage(""), + "bulletedList": MessageLookupByLibrary.simpleMessage(""), + "checkbox": MessageLookupByLibrary.simpleMessage(""), + "embedCode": MessageLookupByLibrary.simpleMessage(""), + "heading1": MessageLookupByLibrary.simpleMessage(""), + "heading2": MessageLookupByLibrary.simpleMessage(""), + "heading3": MessageLookupByLibrary.simpleMessage(""), + "highlight": MessageLookupByLibrary.simpleMessage(""), + "image": MessageLookupByLibrary.simpleMessage(""), + "italic": MessageLookupByLibrary.simpleMessage(""), + "link": MessageLookupByLibrary.simpleMessage(""), + "numberedList": MessageLookupByLibrary.simpleMessage(""), + "quote": MessageLookupByLibrary.simpleMessage(""), + "strikethrough": MessageLookupByLibrary.simpleMessage(""), + "text": MessageLookupByLibrary.simpleMessage(""), + "underline": MessageLookupByLibrary.simpleMessage("") + }; +} diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/src/l10n/l10n.dart b/frontend/app_flowy/packages/appflowy_editor/lib/src/l10n/l10n.dart new file mode 100644 index 0000000000..ae80f115a1 --- /dev/null +++ b/frontend/app_flowy/packages/appflowy_editor/lib/src/l10n/l10n.dart @@ -0,0 +1,257 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND +import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; +import 'intl/messages_all.dart'; + +// ************************************************************************** +// Generator: Flutter Intl IDE plugin +// Made by Localizely +// ************************************************************************** + +// ignore_for_file: non_constant_identifier_names, lines_longer_than_80_chars +// ignore_for_file: join_return_with_assignment, prefer_final_in_for_each +// ignore_for_file: avoid_redundant_argument_values, avoid_escaping_inner_quotes + +class AppFlowyEditorLocalizations { + AppFlowyEditorLocalizations(); + + static AppFlowyEditorLocalizations? _current; + + static AppFlowyEditorLocalizations get current { + assert(_current != null, + 'No instance of AppFlowyEditorLocalizations was loaded. Try to initialize the AppFlowyEditorLocalizations delegate before accessing AppFlowyEditorLocalizations.current.'); + return _current!; + } + + static const AppLocalizationDelegate delegate = AppLocalizationDelegate(); + + static Future load(Locale locale) { + final name = (locale.countryCode?.isEmpty ?? false) + ? locale.languageCode + : locale.toString(); + final localeName = Intl.canonicalizedLocale(name); + return initializeMessages(localeName).then((_) { + Intl.defaultLocale = localeName; + final instance = AppFlowyEditorLocalizations(); + AppFlowyEditorLocalizations._current = instance; + + return instance; + }); + } + + static AppFlowyEditorLocalizations of(BuildContext context) { + final instance = AppFlowyEditorLocalizations.maybeOf(context); + assert(instance != null, + 'No instance of AppFlowyEditorLocalizations present in the widget tree. Did you add AppFlowyEditorLocalizations.delegate in localizationsDelegates?'); + return instance!; + } + + static AppFlowyEditorLocalizations? maybeOf(BuildContext context) { + return Localizations.of( + context, AppFlowyEditorLocalizations); + } + + /// `Bold` + String get bold { + return Intl.message( + 'Bold', + name: 'bold', + desc: '', + args: [], + ); + } + + /// `Bulleted List` + String get bulletedList { + return Intl.message( + 'Bulleted List', + name: 'bulletedList', + desc: '', + args: [], + ); + } + + /// `Checkbox` + String get checkbox { + return Intl.message( + 'Checkbox', + name: 'checkbox', + desc: '', + args: [], + ); + } + + /// `Embed Code` + String get embedCode { + return Intl.message( + 'Embed Code', + name: 'embedCode', + desc: '', + args: [], + ); + } + + /// `H1` + String get heading1 { + return Intl.message( + 'H1', + name: 'heading1', + desc: '', + args: [], + ); + } + + /// `H2` + String get heading2 { + return Intl.message( + 'H2', + name: 'heading2', + desc: '', + args: [], + ); + } + + /// `H3` + String get heading3 { + return Intl.message( + 'H3', + name: 'heading3', + desc: '', + args: [], + ); + } + + /// `Highlight` + String get highlight { + return Intl.message( + 'Highlight', + name: 'highlight', + desc: '', + args: [], + ); + } + + /// `Image` + String get image { + return Intl.message( + 'Image', + name: 'image', + desc: '', + args: [], + ); + } + + /// `Italic` + String get italic { + return Intl.message( + 'Italic', + name: 'italic', + desc: '', + args: [], + ); + } + + /// `Link` + String get link { + return Intl.message( + 'Link', + name: 'link', + desc: '', + args: [], + ); + } + + /// `Numbered List` + String get numberedList { + return Intl.message( + 'Numbered List', + name: 'numberedList', + desc: '', + args: [], + ); + } + + /// `Quote` + String get quote { + return Intl.message( + 'Quote', + name: 'quote', + desc: '', + args: [], + ); + } + + /// `Strikethrough` + String get strikethrough { + return Intl.message( + 'Strikethrough', + name: 'strikethrough', + desc: '', + args: [], + ); + } + + /// `Text` + String get text { + return Intl.message( + 'Text', + name: 'text', + desc: '', + args: [], + ); + } + + /// `Underline` + String get underline { + return Intl.message( + 'Underline', + name: 'underline', + desc: '', + args: [], + ); + } +} + +class AppLocalizationDelegate + extends LocalizationsDelegate { + const AppLocalizationDelegate(); + + List get supportedLocales { + return const [ + Locale.fromSubtags(languageCode: 'en'), + Locale.fromSubtags(languageCode: 'ca'), + Locale.fromSubtags(languageCode: 'de', countryCode: 'DE'), + Locale.fromSubtags(languageCode: 'es', countryCode: 'VE'), + Locale.fromSubtags(languageCode: 'fr', countryCode: 'CA'), + Locale.fromSubtags(languageCode: 'fr', countryCode: 'FR'), + Locale.fromSubtags(languageCode: 'hu', countryCode: 'HU'), + Locale.fromSubtags(languageCode: 'id', countryCode: 'ID'), + Locale.fromSubtags(languageCode: 'it', countryCode: 'IT'), + Locale.fromSubtags(languageCode: 'ja', countryCode: 'JP'), + Locale.fromSubtags(languageCode: 'pl', countryCode: 'PL'), + Locale.fromSubtags(languageCode: 'pt', countryCode: 'BR'), + Locale.fromSubtags(languageCode: 'pt', countryCode: 'PT'), + Locale.fromSubtags(languageCode: 'ru', countryCode: 'RU'), + Locale.fromSubtags(languageCode: 'tr', countryCode: 'TR'), + Locale.fromSubtags(languageCode: 'zh', countryCode: 'CN'), + Locale.fromSubtags(languageCode: 'zh', countryCode: 'TW'), + ]; + } + + @override + bool isSupported(Locale locale) => _isSupported(locale); + @override + Future load(Locale locale) => + AppFlowyEditorLocalizations.load(locale); + @override + bool shouldReload(AppLocalizationDelegate old) => false; + + bool _isSupported(Locale locale) { + for (var supportedLocale in supportedLocales) { + if (supportedLocale.languageCode == locale.languageCode) { + return true; + } + } + return false; + } +} diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/src/render/rich_text/built_in_text_widget.dart b/frontend/app_flowy/packages/appflowy_editor/lib/src/render/rich_text/built_in_text_widget.dart new file mode 100644 index 0000000000..954d5bd516 --- /dev/null +++ b/frontend/app_flowy/packages/appflowy_editor/lib/src/render/rich_text/built_in_text_widget.dart @@ -0,0 +1,61 @@ +import 'package:appflowy_editor/appflowy_editor.dart'; +import 'package:flutter/material.dart'; + +abstract class BuiltInTextWidget extends StatefulWidget { + const BuiltInTextWidget({ + Key? key, + }) : super(key: key); + + EditorState get editorState; + TextNode get textNode; +} + +mixin BuiltInStyleMixin on State { + EdgeInsets get padding { + final padding = widget.editorState.editorStyle.style( + widget.editorState, + widget.textNode, + 'padding', + ); + if (padding is EdgeInsets) { + return padding; + } + return const EdgeInsets.all(0); + } + + TextStyle get textStyle { + final textStyle = widget.editorState.editorStyle.style( + widget.editorState, + widget.textNode, + 'textStyle', + ); + if (textStyle is TextStyle) { + return textStyle; + } + return const TextStyle(); + } + + Size? get iconSize { + final iconSize = widget.editorState.editorStyle.style( + widget.editorState, + widget.textNode, + 'iconSize', + ); + if (iconSize is Size) { + return iconSize; + } + return const Size.square(18.0); + } + + EdgeInsets? get iconPadding { + final iconPadding = widget.editorState.editorStyle.style( + widget.editorState, + widget.textNode, + 'iconPadding', + ); + if (iconPadding is EdgeInsets) { + return iconPadding; + } + return const EdgeInsets.all(0); + } +} diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/src/render/rich_text/bulleted_list_text.dart b/frontend/app_flowy/packages/appflowy_editor/lib/src/render/rich_text/bulleted_list_text.dart index 58c86e7670..999b9517f4 100644 --- a/frontend/app_flowy/packages/appflowy_editor/lib/src/render/rich_text/bulleted_list_text.dart +++ b/frontend/app_flowy/packages/appflowy_editor/lib/src/render/rich_text/bulleted_list_text.dart @@ -1,9 +1,9 @@ import 'package:appflowy_editor/src/document/node.dart'; import 'package:appflowy_editor/src/editor_state.dart'; import 'package:appflowy_editor/src/infra/flowy_svg.dart'; +import 'package:appflowy_editor/src/render/rich_text/built_in_text_widget.dart'; import 'package:appflowy_editor/src/render/rich_text/default_selectable.dart'; import 'package:appflowy_editor/src/render/rich_text/flowy_rich_text.dart'; -import 'package:appflowy_editor/src/render/rich_text/rich_text_style.dart'; import 'package:appflowy_editor/src/render/selection/selectable.dart'; import 'package:appflowy_editor/src/service/render_plugin_service.dart'; import 'package:flutter/material.dart'; @@ -24,14 +24,16 @@ class BulletedListTextNodeWidgetBuilder extends NodeWidgetBuilder { }); } -class BulletedListTextNodeWidget extends StatefulWidget { +class BulletedListTextNodeWidget extends BuiltInTextWidget { const BulletedListTextNodeWidget({ Key? key, required this.textNode, required this.editorState, }) : super(key: key); + @override final TextNode textNode; + @override final EditorState editorState; @override @@ -42,36 +44,40 @@ class BulletedListTextNodeWidget extends StatefulWidget { // customize class _BulletedListTextNodeWidgetState extends State - with SelectableMixin, DefaultSelectable { + with SelectableMixin, DefaultSelectable, BuiltInStyleMixin { @override final iconKey = GlobalKey(); final _richTextKey = GlobalKey(debugLabel: 'bulleted_list_text'); - final _iconWidth = 20.0; - final _iconRightPadding = 5.0; @override SelectableMixin get forward => _richTextKey.currentState as SelectableMixin; + @override + Offset get baseOffset { + return super.baseOffset.translate(0, padding.top); + } + @override Widget build(BuildContext context) { return Padding( - padding: EdgeInsets.only(bottom: defaultLinePadding), + padding: padding, child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ FlowySvg( key: iconKey, - width: _iconWidth, - height: _iconWidth, - padding: EdgeInsets.only(right: _iconRightPadding), + width: iconSize?.width, + height: iconSize?.height, + padding: iconPadding, name: 'point', ), Flexible( child: FlowyRichText( key: _richTextKey, placeholderText: 'List', + lineHeight: widget.editorState.editorStyle.textStyle.lineHeight, textNode: widget.textNode, editorState: widget.editorState, ), diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/src/render/rich_text/checkbox_text.dart b/frontend/app_flowy/packages/appflowy_editor/lib/src/render/rich_text/checkbox_text.dart index d6458304d6..0c6295f4ce 100644 --- a/frontend/app_flowy/packages/appflowy_editor/lib/src/render/rich_text/checkbox_text.dart +++ b/frontend/app_flowy/packages/appflowy_editor/lib/src/render/rich_text/checkbox_text.dart @@ -1,12 +1,16 @@ +import 'package:appflowy_editor/src/document/built_in_attribute_keys.dart'; import 'package:appflowy_editor/src/document/node.dart'; import 'package:appflowy_editor/src/editor_state.dart'; import 'package:appflowy_editor/src/infra/flowy_svg.dart'; -import 'package:appflowy_editor/src/operation/transaction_builder.dart'; +import 'package:appflowy_editor/src/render/rich_text/built_in_text_widget.dart'; import 'package:appflowy_editor/src/render/rich_text/default_selectable.dart'; import 'package:appflowy_editor/src/render/rich_text/flowy_rich_text.dart'; -import 'package:appflowy_editor/src/render/rich_text/rich_text_style.dart'; import 'package:appflowy_editor/src/render/selection/selectable.dart'; +import 'package:appflowy_editor/src/service/default_text_operations/format_rich_text_style.dart'; + import 'package:appflowy_editor/src/service/render_plugin_service.dart'; +import 'package:appflowy_editor/src/extensions/attributes_extension.dart'; +import 'package:appflowy_editor/src/extensions/text_style_extension.dart'; import 'package:flutter/material.dart'; class CheckboxNodeWidgetBuilder extends NodeWidgetBuilder { @@ -21,18 +25,20 @@ class CheckboxNodeWidgetBuilder extends NodeWidgetBuilder { @override NodeValidator get nodeValidator => ((node) { - return node.attributes.containsKey(StyleKey.checkbox); + return node.attributes.containsKey(BuiltInAttributeKey.checkbox); }); } -class CheckboxNodeWidget extends StatefulWidget { +class CheckboxNodeWidget extends BuiltInTextWidget { const CheckboxNodeWidget({ Key? key, required this.textNode, required this.editorState, }) : super(key: key); + @override final TextNode textNode; + @override final EditorState editorState; @override @@ -40,18 +46,21 @@ class CheckboxNodeWidget extends StatefulWidget { } class _CheckboxNodeWidgetState extends State - with SelectableMixin, DefaultSelectable { + with SelectableMixin, DefaultSelectable, BuiltInStyleMixin { @override final iconKey = GlobalKey(); final _richTextKey = GlobalKey(debugLabel: 'checkbox_text'); - final _iconWidth = 20.0; - final _iconRightPadding = 5.0; @override SelectableMixin get forward => _richTextKey.currentState as SelectableMixin; + @override + Offset get baseOffset { + return super.baseOffset.translate(0, padding.top); + } + @override Widget build(BuildContext context) { if (widget.textNode.children.isEmpty) { @@ -64,33 +73,32 @@ class _CheckboxNodeWidgetState extends State Widget _buildWithSingle(BuildContext context) { final check = widget.textNode.attributes.check; return Padding( - padding: EdgeInsets.only(bottom: defaultLinePadding), + padding: padding, child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ GestureDetector( key: iconKey, child: FlowySvg( - width: _iconWidth, - height: _iconWidth, - padding: EdgeInsets.only(right: _iconRightPadding), + width: iconSize?.width, + height: iconSize?.height, + padding: iconPadding, name: check ? 'check' : 'uncheck', ), onTap: () { - TransactionBuilder(widget.editorState) - ..updateNode(widget.textNode, { - StyleKey.checkbox: !check, - }) - ..commit(); + formatCheckbox(widget.editorState, !check); }, ), Flexible( child: FlowyRichText( key: _richTextKey, placeholderText: 'To-do', + lineHeight: widget.editorState.editorStyle.textStyle.lineHeight, textNode: widget.textNode, - textSpanDecorator: _textSpanDecorator, - placeholderTextSpanDecorator: _textSpanDecorator, + textSpanDecorator: (textSpan) => + textSpan.updateTextStyle(textStyle), + placeholderTextSpanDecorator: (textSpan) => + textSpan.updateTextStyle(textStyle), editorState: widget.editorState, ), ), @@ -134,28 +142,4 @@ class _CheckboxNodeWidgetState extends State ], ); } - - TextSpan _textSpanDecorator(TextSpan textSpan) { - return TextSpan( - children: textSpan.children - ?.whereType() - .map( - (span) => TextSpan( - text: span.text, - style: widget.textNode.attributes.check - ? span.style?.copyWith( - color: Colors.grey, - decoration: TextDecoration.combine([ - TextDecoration.lineThrough, - if (span.style?.decoration != null) - span.style!.decoration! - ]), - ) - : span.style, - recognizer: span.recognizer, - ), - ) - .toList(), - ); - } } diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/src/render/rich_text/flowy_rich_text.dart b/frontend/app_flowy/packages/appflowy_editor/lib/src/render/rich_text/flowy_rich_text.dart index 9292c1e5cd..7dba4852ed 100644 --- a/frontend/app_flowy/packages/appflowy_editor/lib/src/render/rich_text/flowy_rich_text.dart +++ b/frontend/app_flowy/packages/appflowy_editor/lib/src/render/rich_text/flowy_rich_text.dart @@ -1,8 +1,6 @@ import 'dart:async'; import 'dart:ui'; -import 'package:appflowy_editor/src/extensions/url_launcher_extension.dart'; -import 'package:appflowy_editor/src/render/toolbar/toolbar_item.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; @@ -13,8 +11,12 @@ import 'package:appflowy_editor/src/document/position.dart'; import 'package:appflowy_editor/src/document/selection.dart'; import 'package:appflowy_editor/src/document/text_delta.dart'; import 'package:appflowy_editor/src/editor_state.dart'; -import 'package:appflowy_editor/src/render/rich_text/rich_text_style.dart'; +import 'package:appflowy_editor/src/extensions/url_launcher_extension.dart'; +import 'package:appflowy_editor/src/extensions/text_style_extension.dart'; +import 'package:appflowy_editor/src/extensions/attributes_extension.dart'; + import 'package:appflowy_editor/src/render/selection/selectable.dart'; +import 'package:appflowy_editor/src/render/toolbar/toolbar_item.dart'; typedef FlowyTextSpanDecorator = TextSpan Function(TextSpan textSpan); @@ -23,6 +25,7 @@ class FlowyRichText extends StatefulWidget { Key? key, this.cursorHeight, this.cursorWidth = 1.0, + this.lineHeight = 1.0, this.textSpanDecorator, this.placeholderText = ' ', this.placeholderTextSpanDecorator, @@ -34,6 +37,7 @@ class FlowyRichText extends StatefulWidget { final EditorState editorState; final double? cursorHeight; final double cursorWidth; + final double lineHeight; final FlowyTextSpanDecorator? textSpanDecorator; final String placeholderText; final FlowyTextSpanDecorator? placeholderTextSpanDecorator; @@ -46,8 +50,6 @@ class _FlowyRichTextState extends State with SelectableMixin { var _textKey = GlobalKey(); final _placeholderTextKey = GlobalKey(); - final _lineHeight = 1.5; - RenderParagraph get _renderParagraph => _textKey.currentContext?.findRenderObject() as RenderParagraph; @@ -90,20 +92,6 @@ class _FlowyRichTextState extends State with SelectableMixin { cursorOffset = _placeholderRenderParagraph.getOffsetForCaret( textPosition, Rect.zero); } - if (cursorHeight != null) { - // workaround: Calling the `getFullHeightForCaret` function will return - // the full height of rich text component instead of the plain text - // if we set the line height. - // So need to divide by the line height to get the expected value. - // - // And the default height of plain text is too short. Add a magic height - // to expand it. - const magicHeight = 3.0; - cursorOffset = cursorOffset.translate( - 0, (cursorHeight - cursorHeight / _lineHeight) / 2.0); - cursorHeight /= _lineHeight; - cursorHeight += magicHeight; - } final rect = Rect.fromLTWH( cursorOffset.dx - (widget.cursorWidth / 2), cursorOffset.dy, @@ -190,8 +178,8 @@ class _FlowyRichTextState extends State with SelectableMixin { key: _placeholderTextKey, textHeightBehavior: const TextHeightBehavior( applyHeightToFirstAscent: false, applyHeightToLastDescent: false), - text: widget.textSpanDecorator != null - ? widget.textSpanDecorator!(textSpan) + text: widget.placeholderTextSpanDecorator != null + ? widget.placeholderTextSpanDecorator!(textSpan) : textSpan, ); } @@ -210,42 +198,73 @@ class _FlowyRichTextState extends State with SelectableMixin { ); } - TextSpan get _textSpan { - var offset = 0; + TextSpan get _placeholderTextSpan { + final style = widget.editorState.editorStyle.textStyle; return TextSpan( - children: widget.textNode.delta.whereType().map((insert) { - GestureRecognizer? gestureRecognizer; - if (insert.attributes?[StyleKey.href] != null) { - gestureRecognizer = _buildTapHrefGestureRecognizer( - insert.attributes![StyleKey.href], - Selection.single( - path: widget.textNode.path, - startOffset: offset, - endOffset: offset + insert.length, - ), - ); - } - offset += insert.length; - final textSpan = RichTextStyle( - attributes: insert.attributes ?? {}, - text: insert.content, - height: _lineHeight, - gestureRecognizer: gestureRecognizer, - ).toTextSpan(); - return textSpan; - }).toList(growable: false), + children: [ + TextSpan( + text: widget.placeholderText, + style: style.defaultPlaceholderTextStyle, + ), + ], ); } - TextSpan get _placeholderTextSpan => TextSpan(children: [ - RichTextStyle( - text: widget.placeholderText, - attributes: { - StyleKey.color: '0xFF707070', - }, - height: _lineHeight, - ).toTextSpan() - ]); + TextSpan get _textSpan { + var offset = 0; + List textSpans = []; + final style = widget.editorState.editorStyle.textStyle; + final textInserts = widget.textNode.delta.whereType(); + for (final textInsert in textInserts) { + var textStyle = style.defaultTextStyle; + GestureRecognizer? recognizer; + final attributes = textInsert.attributes; + if (attributes != null) { + if (attributes.bold == true) { + textStyle = textStyle.combine(style.bold); + } + if (attributes.italic == true) { + textStyle = textStyle.combine(style.italic); + } + if (attributes.underline == true) { + textStyle = textStyle.combine(style.underline); + } + if (attributes.strikethrough == true) { + textStyle = textStyle.combine(style.strikethrough); + } + if (attributes.href != null) { + textStyle = textStyle.combine(style.href); + recognizer = _buildTapHrefGestureRecognizer( + attributes.href!, + Selection.single( + path: widget.textNode.path, + startOffset: offset, + endOffset: offset + textInsert.length, + ), + ); + } + if (attributes.code == true) { + textStyle = textStyle.combine(style.code); + } + if (attributes.backgroundColor != null) { + textStyle = textStyle.combine( + TextStyle(backgroundColor: attributes.backgroundColor), + ); + } + } + offset += textInsert.length; + textSpans.add( + TextSpan( + text: textInsert.content, + style: textStyle, + recognizer: recognizer, + ), + ); + } + return TextSpan( + children: textSpans, + ); + } GestureRecognizer _buildTapHrefGestureRecognizer( String href, Selection selection) { diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/src/render/rich_text/heading_text.dart b/frontend/app_flowy/packages/appflowy_editor/lib/src/render/rich_text/heading_text.dart index d58030ef15..e6307bb409 100644 --- a/frontend/app_flowy/packages/appflowy_editor/lib/src/render/rich_text/heading_text.dart +++ b/frontend/app_flowy/packages/appflowy_editor/lib/src/render/rich_text/heading_text.dart @@ -1,11 +1,13 @@ import 'package:appflowy_editor/src/document/node.dart'; import 'package:appflowy_editor/src/editor_state.dart'; +import 'package:appflowy_editor/src/render/rich_text/built_in_text_widget.dart'; import 'package:appflowy_editor/src/render/rich_text/default_selectable.dart'; import 'package:appflowy_editor/src/render/rich_text/flowy_rich_text.dart'; -import 'package:appflowy_editor/src/render/rich_text/rich_text_style.dart'; import 'package:appflowy_editor/src/render/selection/selectable.dart'; import 'package:appflowy_editor/src/service/render_plugin_service.dart'; import 'package:flutter/material.dart'; +import 'package:appflowy_editor/src/extensions/attributes_extension.dart'; +import 'package:appflowy_editor/src/extensions/text_style_extension.dart'; class HeadingTextNodeWidgetBuilder extends NodeWidgetBuilder { @override @@ -23,14 +25,16 @@ class HeadingTextNodeWidgetBuilder extends NodeWidgetBuilder { }); } -class HeadingTextNodeWidget extends StatefulWidget { +class HeadingTextNodeWidget extends BuiltInTextWidget { const HeadingTextNodeWidget({ Key? key, required this.textNode, required this.editorState, }) : super(key: key); + @override final TextNode textNode; + @override final EditorState editorState; @override @@ -39,12 +43,11 @@ class HeadingTextNodeWidget extends StatefulWidget { // customize class _HeadingTextNodeWidgetState extends State - with SelectableMixin, DefaultSelectable { + with SelectableMixin, DefaultSelectable, BuiltInStyleMixin { @override GlobalKey? get iconKey => null; final _richTextKey = GlobalKey(debugLabel: 'heading_text'); - final _topPadding = 5.0; @override SelectableMixin get forward => @@ -52,58 +55,23 @@ class _HeadingTextNodeWidgetState extends State @override Offset get baseOffset { - return Offset(0, _topPadding); + return padding.topLeft; } @override Widget build(BuildContext context) { return Padding( - padding: EdgeInsets.only( - top: _topPadding, - bottom: defaultLinePadding, - ), + padding: padding, child: FlowyRichText( key: _richTextKey, placeholderText: 'Heading', - placeholderTextSpanDecorator: _placeholderTextSpanDecorator, - textSpanDecorator: _textSpanDecorator, + placeholderTextSpanDecorator: (textSpan) => + textSpan.updateTextStyle(textStyle), + textSpanDecorator: (textSpan) => textSpan.updateTextStyle(textStyle), + lineHeight: widget.editorState.editorStyle.textStyle.lineHeight, textNode: widget.textNode, editorState: widget.editorState, ), ); } - - TextSpan _textSpanDecorator(TextSpan textSpan) { - return TextSpan( - children: textSpan.children - ?.whereType() - .map( - (span) => TextSpan( - text: span.text, - style: span.style?.copyWith( - fontSize: widget.textNode.attributes.fontSize, - ), - recognizer: span.recognizer, - ), - ) - .toList(), - ); - } - - TextSpan _placeholderTextSpanDecorator(TextSpan textSpan) { - return TextSpan( - children: textSpan.children - ?.whereType() - .map( - (span) => TextSpan( - text: span.text, - style: span.style?.copyWith( - fontSize: widget.textNode.attributes.fontSize, - ), - recognizer: span.recognizer, - ), - ) - .toList(), - ); - } } diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/src/render/rich_text/number_list_text.dart b/frontend/app_flowy/packages/appflowy_editor/lib/src/render/rich_text/number_list_text.dart index 2085b3a48a..59c0551750 100644 --- a/frontend/app_flowy/packages/appflowy_editor/lib/src/render/rich_text/number_list_text.dart +++ b/frontend/app_flowy/packages/appflowy_editor/lib/src/render/rich_text/number_list_text.dart @@ -1,12 +1,13 @@ import 'package:appflowy_editor/src/document/node.dart'; import 'package:appflowy_editor/src/editor_state.dart'; -import 'package:appflowy_editor/src/infra/flowy_svg.dart'; +import 'package:appflowy_editor/src/render/rich_text/built_in_text_widget.dart'; import 'package:appflowy_editor/src/render/rich_text/default_selectable.dart'; import 'package:appflowy_editor/src/render/rich_text/flowy_rich_text.dart'; -import 'package:appflowy_editor/src/render/rich_text/rich_text_style.dart'; import 'package:appflowy_editor/src/render/selection/selectable.dart'; import 'package:appflowy_editor/src/service/render_plugin_service.dart'; import 'package:flutter/material.dart'; +import 'package:appflowy_editor/src/extensions/attributes_extension.dart'; +import 'package:appflowy_editor/src/extensions/text_style_extension.dart'; class NumberListTextNodeWidgetBuilder extends NodeWidgetBuilder { @override @@ -24,14 +25,16 @@ class NumberListTextNodeWidgetBuilder extends NodeWidgetBuilder { }); } -class NumberListTextNodeWidget extends StatefulWidget { +class NumberListTextNodeWidget extends BuiltInTextWidget { const NumberListTextNodeWidget({ Key? key, required this.textNode, required this.editorState, }) : super(key: key); + @override final TextNode textNode; + @override final EditorState editorState; @override @@ -39,11 +42,8 @@ class NumberListTextNodeWidget extends StatefulWidget { _NumberListTextNodeWidgetState(); } -// customize -const double _numberHorizontalPadding = 8; - class _NumberListTextNodeWidgetState extends State - with SelectableMixin, DefaultSelectable { + with SelectableMixin, DefaultSelectable, BuiltInStyleMixin { @override final iconKey = GlobalKey(); @@ -53,31 +53,42 @@ class _NumberListTextNodeWidgetState extends State SelectableMixin get forward => _richTextKey.currentState as SelectableMixin; + @override + Offset get baseOffset { + return super.baseOffset.translate(0, padding.top); + } + @override Widget build(BuildContext context) { return Padding( - padding: EdgeInsets.only(bottom: defaultLinePadding), - child: Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Padding( - key: iconKey, - padding: const EdgeInsets.symmetric( - horizontal: _numberHorizontalPadding, vertical: 0), - child: Text( - '${widget.textNode.attributes.number.toString()}.', - style: const TextStyle(fontSize: 16), - ), + padding: padding, + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + key: iconKey, + padding: iconPadding, + child: Text( + '${widget.textNode.attributes.number.toString()}.', + // FIXME: customize + style: const TextStyle(fontSize: 16.0, color: Colors.black), ), - Flexible( - child: FlowyRichText( - key: _richTextKey, - placeholderText: 'List', - textNode: widget.textNode, - editorState: widget.editorState, - ), + ), + Flexible( + child: FlowyRichText( + key: _richTextKey, + placeholderText: 'List', + textNode: widget.textNode, + editorState: widget.editorState, + lineHeight: widget.editorState.editorStyle.textStyle.lineHeight, + placeholderTextSpanDecorator: (textSpan) => + textSpan.updateTextStyle(textStyle), + textSpanDecorator: (textSpan) => + textSpan.updateTextStyle(textStyle), ), - ], - )); + ), + ], + ), + ); } } diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/src/render/rich_text/quoted_text.dart b/frontend/app_flowy/packages/appflowy_editor/lib/src/render/rich_text/quoted_text.dart index 9e36acfad0..1c86cbbcb7 100644 --- a/frontend/app_flowy/packages/appflowy_editor/lib/src/render/rich_text/quoted_text.dart +++ b/frontend/app_flowy/packages/appflowy_editor/lib/src/render/rich_text/quoted_text.dart @@ -1,9 +1,9 @@ import 'package:appflowy_editor/src/document/node.dart'; import 'package:appflowy_editor/src/editor_state.dart'; import 'package:appflowy_editor/src/infra/flowy_svg.dart'; +import 'package:appflowy_editor/src/render/rich_text/built_in_text_widget.dart'; import 'package:appflowy_editor/src/render/rich_text/default_selectable.dart'; import 'package:appflowy_editor/src/render/rich_text/flowy_rich_text.dart'; -import 'package:appflowy_editor/src/render/rich_text/rich_text_style.dart'; import 'package:appflowy_editor/src/render/selection/selectable.dart'; import 'package:appflowy_editor/src/service/render_plugin_service.dart'; import 'package:flutter/material.dart'; @@ -24,14 +24,16 @@ class QuotedTextNodeWidgetBuilder extends NodeWidgetBuilder { }); } -class QuotedTextNodeWidget extends StatefulWidget { +class QuotedTextNodeWidget extends BuiltInTextWidget { const QuotedTextNodeWidget({ Key? key, required this.textNode, required this.editorState, }) : super(key: key); + @override final TextNode textNode; + @override final EditorState editorState; @override @@ -41,30 +43,33 @@ class QuotedTextNodeWidget extends StatefulWidget { // customize class _QuotedTextNodeWidgetState extends State - with SelectableMixin, DefaultSelectable { + with SelectableMixin, DefaultSelectable, BuiltInStyleMixin { @override final iconKey = GlobalKey(); final _richTextKey = GlobalKey(debugLabel: 'quoted_text'); - final _iconWidth = 20.0; - final _iconRightPadding = 5.0; @override SelectableMixin get forward => _richTextKey.currentState as SelectableMixin; + @override + Offset get baseOffset { + return super.baseOffset.translate(0, padding.top); + } + @override Widget build(BuildContext context) { return Padding( - padding: EdgeInsets.only(bottom: defaultLinePadding), + padding: padding, child: IntrinsicHeight( child: Row( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ FlowySvg( key: iconKey, - width: _iconWidth, - padding: EdgeInsets.only(right: _iconRightPadding), + width: iconSize?.width, + padding: iconPadding, name: 'quote', ), Flexible( @@ -72,6 +77,7 @@ class _QuotedTextNodeWidgetState extends State key: _richTextKey, placeholderText: 'Quote', textNode: widget.textNode, + lineHeight: widget.editorState.editorStyle.textStyle.lineHeight, editorState: widget.editorState, ), ), diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/src/render/rich_text/rich_text.dart b/frontend/app_flowy/packages/appflowy_editor/lib/src/render/rich_text/rich_text.dart index a00e1d7c14..2b98e31259 100644 --- a/frontend/app_flowy/packages/appflowy_editor/lib/src/render/rich_text/rich_text.dart +++ b/frontend/app_flowy/packages/appflowy_editor/lib/src/render/rich_text/rich_text.dart @@ -1,8 +1,8 @@ import 'package:appflowy_editor/src/document/node.dart'; import 'package:appflowy_editor/src/editor_state.dart'; +import 'package:appflowy_editor/src/render/rich_text/built_in_text_widget.dart'; import 'package:appflowy_editor/src/render/rich_text/default_selectable.dart'; import 'package:appflowy_editor/src/render/rich_text/flowy_rich_text.dart'; -import 'package:appflowy_editor/src/render/rich_text/rich_text_style.dart'; import 'package:appflowy_editor/src/render/selection/selectable.dart'; import 'package:appflowy_editor/src/service/render_plugin_service.dart'; import 'package:flutter/material.dart'; @@ -23,14 +23,16 @@ class RichTextNodeWidgetBuilder extends NodeWidgetBuilder { }); } -class RichTextNodeWidget extends StatefulWidget { +class RichTextNodeWidget extends BuiltInTextWidget { const RichTextNodeWidget({ Key? key, required this.textNode, required this.editorState, }) : super(key: key); + @override final TextNode textNode; + @override final EditorState editorState; @override @@ -40,7 +42,7 @@ class RichTextNodeWidget extends StatefulWidget { // customize class _RichTextNodeWidgetState extends State - with SelectableMixin, DefaultSelectable { + with SelectableMixin, DefaultSelectable, BuiltInStyleMixin { @override GlobalKey? get iconKey => null; @@ -50,13 +52,19 @@ class _RichTextNodeWidgetState extends State SelectableMixin get forward => _richTextKey.currentState as SelectableMixin; + @override + Offset get baseOffset { + return padding.topLeft; + } + @override Widget build(BuildContext context) { return Padding( - padding: EdgeInsets.only(bottom: defaultLinePadding), + padding: padding, child: FlowyRichText( key: _richTextKey, textNode: widget.textNode, + lineHeight: widget.editorState.editorStyle.textStyle.lineHeight, editorState: widget.editorState, ), ); diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/src/render/rich_text/rich_text_style.dart b/frontend/app_flowy/packages/appflowy_editor/lib/src/render/rich_text/rich_text_style.dart deleted file mode 100644 index a1fbfdbb57..0000000000 --- a/frontend/app_flowy/packages/appflowy_editor/lib/src/render/rich_text/rich_text_style.dart +++ /dev/null @@ -1,282 +0,0 @@ -import 'package:appflowy_editor/src/document/attributes.dart'; -import 'package:flutter/gestures.dart'; -import 'package:flutter/material.dart'; - -/// -/// Supported partial rendering types: -/// bold, italic, -/// underline, strikethrough, -/// color, font, -/// href -/// -/// Supported global rendering types: -/// heading: h1, h2, h3, h4, h5, h6, ... -/// block quote, -/// list: ordered list, bulleted list, -/// code block -/// -class StyleKey { - static String bold = 'bold'; - static String italic = 'italic'; - static String underline = 'underline'; - static String strikethrough = 'strikethrough'; - static String color = 'color'; - static String backgroundColor = 'backgroundColor'; - static String font = 'font'; - static String href = 'href'; - - static String subtype = 'subtype'; - static String heading = 'heading'; - static String h1 = 'h1'; - static String h2 = 'h2'; - static String h3 = 'h3'; - static String h4 = 'h4'; - static String h5 = 'h5'; - static String h6 = 'h6'; - - static String bulletedList = 'bulleted-list'; - static String numberList = 'number-list'; - - static String quote = 'quote'; - static String checkbox = 'checkbox'; - static String code = 'code'; - static String number = 'number'; - - static List partialStyleKeys = [ - StyleKey.bold, - StyleKey.italic, - StyleKey.underline, - StyleKey.strikethrough, - StyleKey.backgroundColor, - StyleKey.href, - StyleKey.code, - ]; - - static List globalStyleKeys = [ - StyleKey.subtype, - StyleKey.heading, - StyleKey.checkbox, - StyleKey.bulletedList, - StyleKey.numberList, - StyleKey.quote, - ]; -} - -// TODO: customize -double defaultLinePadding = 8.0; -double baseFontSize = 16.0; -String defaultHighlightColor = '0x6000BCF0'; -String defaultBackgroundColor = '0x00000000'; -// TODO: customize. -Map headingToFontSize = { - StyleKey.h1: baseFontSize + 15, - StyleKey.h2: baseFontSize + 12, - StyleKey.h3: baseFontSize + 9, - StyleKey.h4: baseFontSize + 6, - StyleKey.h5: baseFontSize + 3, - StyleKey.h6: baseFontSize, -}; - -extension NodeAttributesExtensions on Attributes { - String? get heading { - if (containsKey(StyleKey.subtype) && - containsKey(StyleKey.heading) && - this[StyleKey.subtype] == StyleKey.heading && - this[StyleKey.heading] is String) { - return this[StyleKey.heading]; - } - return null; - } - - double get fontSize { - if (heading != null) { - return headingToFontSize[heading]!; - } - return baseFontSize; - } - - bool get quote { - return containsKey(StyleKey.quote); - } - - Color? get quoteColor { - if (quote) { - return Colors.grey; - } - return null; - } - - int? get number { - if (containsKey(StyleKey.number) && this[StyleKey.number] is int) { - return this[StyleKey.number]; - } - return null; - } - - bool get code { - if (containsKey(StyleKey.code) && this[StyleKey.code] == true) { - return this[StyleKey.code]; - } - return false; - } - - bool get check { - if (containsKey(StyleKey.checkbox) && this[StyleKey.checkbox] is bool) { - return this[StyleKey.checkbox]; - } - return false; - } -} - -extension DeltaAttributesExtensions on Attributes { - bool get bold { - return (containsKey(StyleKey.bold) && this[StyleKey.bold] == true); - } - - bool get italic { - return (containsKey(StyleKey.italic) && this[StyleKey.italic] == true); - } - - bool get underline { - return (containsKey(StyleKey.underline) && - this[StyleKey.underline] == true); - } - - bool get strikethrough { - return (containsKey(StyleKey.strikethrough) && - this[StyleKey.strikethrough] == true); - } - - Color? get color { - if (containsKey(StyleKey.color) && this[StyleKey.color] is String) { - return Color( - int.parse(this[StyleKey.color]), - ); - } - return null; - } - - Color? get backgroundColor { - if (containsKey(StyleKey.backgroundColor) && - this[StyleKey.backgroundColor] is String) { - return Color( - int.parse(this[StyleKey.backgroundColor]), - ); - } - return null; - } - - String? get font { - // TODO: unspport now. - return null; - } - - String? get href { - if (containsKey(StyleKey.href) && this[StyleKey.href] is String) { - return this[StyleKey.href]; - } - return null; - } -} - -class RichTextStyle { - // TODO: customize - RichTextStyle({ - required this.attributes, - required this.text, - this.gestureRecognizer, - this.height = 1.5, - }); - - final Attributes attributes; - final String text; - final GestureRecognizer? gestureRecognizer; - final double height; - - TextSpan toTextSpan() => _toTextSpan(height); - - double get topPadding { - return 0; - } - - TextSpan _toTextSpan(double? height) { - return TextSpan( - text: text, - recognizer: _recognizer, - style: TextStyle( - fontWeight: _fontWeight, - fontStyle: _fontStyle, - fontSize: _fontSize, - color: _textColor, - decoration: _textDecoration, - background: _background, - height: height, - ), - ); - } - - Paint? get _background { - if (_backgroundColor != null) { - return Paint() - ..color = _backgroundColor! - ..strokeWidth = 24.0 - ..style = PaintingStyle.fill - ..strokeJoin = StrokeJoin.round; - } - return null; - } - - // bold - FontWeight get _fontWeight { - if (attributes.bold) { - return FontWeight.bold; - } - return FontWeight.normal; - } - - // underline or strikethrough - TextDecoration get _textDecoration { - var decorations = [TextDecoration.none]; - if (attributes.underline || attributes.href != null) { - decorations.add(TextDecoration.underline); - } - if (attributes.strikethrough) { - decorations.add(TextDecoration.lineThrough); - } - return TextDecoration.combine(decorations); - } - - // font - FontStyle get _fontStyle => - attributes.italic ? FontStyle.italic : FontStyle.normal; - - // text color - Color get _textColor { - if (attributes.href != null) { - return Colors.lightBlue; - } - if (attributes.code) { - return Colors.lightBlue.withOpacity(0.8); - } - return attributes.color ?? Colors.black; - } - - Color? get _backgroundColor { - if (attributes.backgroundColor != null) { - return attributes.backgroundColor!; - } else if (attributes.code) { - return Colors.blue.shade300.withOpacity(0.3); - } - return null; - } - - // font size - double get _fontSize { - return baseFontSize; - } - - // recognizer - GestureRecognizer? get _recognizer { - return gestureRecognizer; - } -} diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/src/render/selection_menu/selection_menu_service.dart b/frontend/app_flowy/packages/appflowy_editor/lib/src/render/selection_menu/selection_menu_service.dart index f4f2006af4..8d3d1e3453 100644 --- a/frontend/app_flowy/packages/appflowy_editor/lib/src/render/selection_menu/selection_menu_service.dart +++ b/frontend/app_flowy/packages/appflowy_editor/lib/src/render/selection_menu/selection_menu_service.dart @@ -1,10 +1,12 @@ import 'package:appflowy_editor/src/editor_state.dart'; import 'package:appflowy_editor/src/infra/flowy_svg.dart'; +import 'package:appflowy_editor/src/l10n/l10n.dart'; import 'package:appflowy_editor/src/render/image/image_upload_widget.dart'; -import 'package:appflowy_editor/src/render/rich_text/rich_text_style.dart'; import 'package:appflowy_editor/src/render/selection_menu/selection_menu_widget.dart'; import 'package:appflowy_editor/src/service/default_text_operations/format_rich_text_style.dart'; + import 'package:flutter/material.dart'; +import 'package:appflowy_editor/src/document/built_in_attribute_keys.dart'; abstract class SelectionMenuService { Offset get topLeft; @@ -54,7 +56,13 @@ class SelectionMenu implements SelectionMenuService { if (selectionRects.isEmpty) { return; } - final offset = selectionRects.first.bottomRight + const Offset(10, 10); + // Workaround: We can customize the padding through the [EditorStyle], + // but the coordinates of overlay are not properly converted currently. + // Just subtract the padding here as a result. + final baseOffset = + editorState.renderBox?.localToGlobal(Offset.zero) ?? Offset.zero; + final offset = + selectionRects.first.bottomRight + const Offset(10, 10) - baseOffset; _topLeft = offset; _selectionMenuEntry = OverlayEntry(builder: (context) { @@ -116,7 +124,7 @@ List get defaultSelectionMenuItems => _defaultSelectionMenuItems; final List _defaultSelectionMenuItems = [ SelectionMenuItem( - name: 'Text', + name: AppFlowyEditorLocalizations.current.text, icon: _selectionMenuIcon('text'), keywords: ['text'], handler: (editorState, _, __) { @@ -124,37 +132,37 @@ final List _defaultSelectionMenuItems = [ }, ), SelectionMenuItem( - name: 'Heading 1', + name: AppFlowyEditorLocalizations.current.heading1, icon: _selectionMenuIcon('h1'), keywords: ['heading 1, h1'], handler: (editorState, _, __) { - insertHeadingAfterSelection(editorState, StyleKey.h1); + insertHeadingAfterSelection(editorState, BuiltInAttributeKey.h1); }, ), SelectionMenuItem( - name: 'Heading 2', + name: AppFlowyEditorLocalizations.current.heading2, icon: _selectionMenuIcon('h2'), keywords: ['heading 2, h2'], handler: (editorState, _, __) { - insertHeadingAfterSelection(editorState, StyleKey.h2); + insertHeadingAfterSelection(editorState, BuiltInAttributeKey.h2); }, ), SelectionMenuItem( - name: 'Heading 3', + name: AppFlowyEditorLocalizations.current.heading3, icon: _selectionMenuIcon('h3'), keywords: ['heading 3, h3'], handler: (editorState, _, __) { - insertHeadingAfterSelection(editorState, StyleKey.h3); + insertHeadingAfterSelection(editorState, BuiltInAttributeKey.h3); }, ), SelectionMenuItem( - name: 'Image', + name: AppFlowyEditorLocalizations.current.image, icon: _selectionMenuIcon('image'), keywords: ['image'], handler: showImageUploadMenu, ), SelectionMenuItem( - name: 'Bulleted list', + name: AppFlowyEditorLocalizations.current.bulletedList, icon: _selectionMenuIcon('bulleted_list'), keywords: ['bulleted list', 'list', 'unordered list'], handler: (editorState, _, __) { @@ -162,7 +170,7 @@ final List _defaultSelectionMenuItems = [ }, ), SelectionMenuItem( - name: 'Checkbox', + name: AppFlowyEditorLocalizations.current.checkbox, icon: _selectionMenuIcon('checkbox'), keywords: ['todo list', 'list', 'checkbox list'], handler: (editorState, _, __) { @@ -170,7 +178,7 @@ final List _defaultSelectionMenuItems = [ }, ), SelectionMenuItem( - name: 'Quote', + name: AppFlowyEditorLocalizations.current.quote, icon: _selectionMenuIcon('quote'), keywords: ['quote', 'refer'], handler: (editorState, _, __) { diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/src/render/style/editor_style.dart b/frontend/app_flowy/packages/appflowy_editor/lib/src/render/style/editor_style.dart index e691ea689e..7ec426fff4 100644 --- a/frontend/app_flowy/packages/appflowy_editor/lib/src/render/style/editor_style.dart +++ b/frontend/app_flowy/packages/appflowy_editor/lib/src/render/style/editor_style.dart @@ -1,20 +1,254 @@ import 'package:flutter/material.dart'; +import 'package:appflowy_editor/src/document/node.dart'; +import 'package:appflowy_editor/src/editor_state.dart'; +import 'package:appflowy_editor/src/extensions/attributes_extension.dart'; + +typedef PluginStyler = Object Function(EditorState editorState, Node node); +typedef PluginStyle = Map; + /// Editor style configuration class EditorStyle { - const EditorStyle({ + EditorStyle({ required this.padding, - }); + required this.textStyle, + required this.cursorColor, + required this.selectionColor, + Map pluginStyles = const {}, + }) { + _pluginStyles.addAll(pluginStyles); + } - const EditorStyle.defaultStyle() - : padding = const EdgeInsets.fromLTRB(200.0, 0.0, 200.0, 0.0); + EditorStyle.defaultStyle() + : padding = const EdgeInsets.fromLTRB(200.0, 0.0, 200.0, 0.0), + textStyle = BuiltInTextStyle.builtIn(), + cursorColor = const Color(0xFF00BCF0), + selectionColor = const Color.fromARGB(53, 111, 201, 231); /// The margin of the document context from the editor. final EdgeInsets padding; + final BuiltInTextStyle textStyle; + final Color cursorColor; + final Color selectionColor; - EditorStyle copyWith({EdgeInsets? padding}) { + final Map _pluginStyles = Map.from(builtInTextStylers); + + Object? style(EditorState editorState, Node node, String key) { + final styler = _pluginStyles[node.id]?[key]; + if (styler != null) { + return styler(editorState, node); + } + return null; + } + + EditorStyle copyWith({ + EdgeInsets? padding, + BuiltInTextStyle? textStyle, + Color? cursorColor, + Color? selectionColor, + Map? pluginStyles, + }) { return EditorStyle( padding: padding ?? this.padding, + textStyle: textStyle ?? this.textStyle, + cursorColor: cursorColor ?? this.cursorColor, + selectionColor: selectionColor ?? this.selectionColor, + pluginStyles: pluginStyles ?? {}, ); } + + @override + bool operator ==(Object other) { + if (identical(this, other)) return true; + + return other is EditorStyle && + other.padding == padding && + other.textStyle == textStyle && + other.cursorColor == cursorColor && + other.selectionColor == selectionColor; + } + + @override + int get hashCode { + return padding.hashCode ^ + textStyle.hashCode ^ + cursorColor.hashCode ^ + selectionColor.hashCode; + } +} + +PluginStyle get builtInPluginStyle => Map.from({ + 'padding': (_, __) => const EdgeInsets.symmetric(vertical: 8.0), + 'textStyle': (_, __) => const TextStyle(), + 'iconSize': (_, __) => const Size.square(20.0), + 'iconPadding': (_, __) => const EdgeInsets.only(right: 5.0), + }); + +Map builtInTextStylers = { + 'text': builtInPluginStyle, + 'text/checkbox': builtInPluginStyle + ..update( + 'textStyle', + (_) => (EditorState editorState, Node node) { + if (node is TextNode && node.attributes.check == true) { + return const TextStyle( + color: Colors.grey, + decoration: TextDecoration.lineThrough, + ); + } + return const TextStyle(); + }, + ), + 'text/heading': builtInPluginStyle + ..update( + 'textStyle', + (_) => (EditorState editorState, Node node) { + final headingToFontSize = { + 'h1': 32.0, + 'h2': 28.0, + 'h3': 24.0, + 'h4': 18.0, + 'h5': 18.0, + 'h6': 18.0, + }; + final fontSize = headingToFontSize[node.attributes.heading] ?? 18.0; + return TextStyle(fontSize: fontSize, fontWeight: FontWeight.bold); + }, + ), + 'text/bulleted-list': builtInPluginStyle, + 'text/number-list': builtInPluginStyle + ..update( + 'iconPadding', + (_) => (EditorState editorState, Node node) { + return const EdgeInsets.only(left: 5.0, right: 5.0); + }, + ), + 'text/quote': builtInPluginStyle, + 'image': builtInPluginStyle, +}; + +class BuiltInTextStyle { + const BuiltInTextStyle({ + required this.defaultTextStyle, + required this.defaultPlaceholderTextStyle, + required this.bold, + required this.italic, + required this.underline, + required this.strikethrough, + required this.href, + required this.code, + this.highlightColorHex = '0x6000BCF0', + this.lineHeight = 1.5, + }); + + final TextStyle defaultTextStyle; + final TextStyle defaultPlaceholderTextStyle; + final TextStyle bold; + final TextStyle italic; + final TextStyle underline; + final TextStyle strikethrough; + final TextStyle href; + final TextStyle code; + final String highlightColorHex; + final double lineHeight; + + BuiltInTextStyle.builtIn() + : defaultTextStyle = const TextStyle(fontSize: 16.0, color: Colors.black), + defaultPlaceholderTextStyle = + const TextStyle(fontSize: 16.0, color: Colors.grey), + bold = const TextStyle(fontWeight: FontWeight.bold), + italic = const TextStyle(fontStyle: FontStyle.italic), + underline = const TextStyle(decoration: TextDecoration.underline), + strikethrough = const TextStyle(decoration: TextDecoration.lineThrough), + href = const TextStyle( + color: Colors.blue, + decoration: TextDecoration.underline, + ), + code = const TextStyle( + fontFamily: 'monospace', + color: Color(0xFF00BCF0), + backgroundColor: Color(0xFFE0F8FF), + ), + highlightColorHex = '0x6000BCF0', + lineHeight = 1.5; + + BuiltInTextStyle.builtInDarkMode() + : defaultTextStyle = const TextStyle(fontSize: 16.0, color: Colors.white), + defaultPlaceholderTextStyle = TextStyle( + fontSize: 16.0, + color: Colors.white.withOpacity(0.3), + ), + bold = const TextStyle(fontWeight: FontWeight.bold), + italic = const TextStyle(fontStyle: FontStyle.italic), + underline = const TextStyle(decoration: TextDecoration.underline), + strikethrough = const TextStyle(decoration: TextDecoration.lineThrough), + href = const TextStyle( + color: Colors.blue, + decoration: TextDecoration.underline, + ), + code = const TextStyle( + fontFamily: 'monospace', + color: Color(0xFF00BCF0), + backgroundColor: Color(0xFFE0F8FF), + ), + highlightColorHex = '0x6000BCF0', + lineHeight = 1.5; + + BuiltInTextStyle copyWith({ + TextStyle? defaultTextStyle, + TextStyle? defaultPlaceholderTextStyle, + TextStyle? bold, + TextStyle? italic, + TextStyle? underline, + TextStyle? strikethrough, + TextStyle? href, + TextStyle? code, + String? highlightColorHex, + double? lineHeight, + }) { + return BuiltInTextStyle( + defaultTextStyle: defaultTextStyle ?? this.defaultTextStyle, + defaultPlaceholderTextStyle: + defaultPlaceholderTextStyle ?? this.defaultPlaceholderTextStyle, + bold: bold ?? this.bold, + italic: italic ?? this.italic, + underline: underline ?? this.underline, + strikethrough: strikethrough ?? this.strikethrough, + href: href ?? this.href, + code: code ?? this.code, + highlightColorHex: highlightColorHex ?? this.highlightColorHex, + lineHeight: lineHeight ?? this.lineHeight, + ); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) return true; + + return other is BuiltInTextStyle && + other.defaultTextStyle == defaultTextStyle && + other.defaultPlaceholderTextStyle == defaultPlaceholderTextStyle && + other.bold == bold && + other.italic == italic && + other.underline == underline && + other.strikethrough == strikethrough && + other.href == href && + other.code == code && + other.highlightColorHex == highlightColorHex && + other.lineHeight == lineHeight; + } + + @override + int get hashCode { + return defaultTextStyle.hashCode ^ + defaultPlaceholderTextStyle.hashCode ^ + bold.hashCode ^ + italic.hashCode ^ + underline.hashCode ^ + strikethrough.hashCode ^ + href.hashCode ^ + code.hashCode ^ + highlightColorHex.hashCode ^ + lineHeight.hashCode; + } } diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/src/render/toolbar/toolbar_item.dart b/frontend/app_flowy/packages/appflowy_editor/lib/src/render/toolbar/toolbar_item.dart index 1b028abf50..02eaddc68d 100644 --- a/frontend/app_flowy/packages/appflowy_editor/lib/src/render/toolbar/toolbar_item.dart +++ b/frontend/app_flowy/packages/appflowy_editor/lib/src/render/toolbar/toolbar_item.dart @@ -2,12 +2,13 @@ import 'package:appflowy_editor/appflowy_editor.dart'; import 'package:appflowy_editor/src/extensions/url_launcher_extension.dart'; import 'package:appflowy_editor/src/infra/flowy_svg.dart'; import 'package:appflowy_editor/src/render/link_menu/link_menu.dart'; -import 'package:appflowy_editor/src/render/rich_text/rich_text_style.dart'; import 'package:appflowy_editor/src/extensions/text_node_extensions.dart'; import 'package:appflowy_editor/src/extensions/editor_state_extensions.dart'; import 'package:appflowy_editor/src/service/default_text_operations/format_rich_text_style.dart'; + import 'package:flutter/material.dart'; import 'package:rich_clipboard/rich_clipboard.dart'; +import 'package:appflowy_editor/src/document/built_in_attribute_keys.dart'; typedef ToolbarItemEventHandler = void Function( EditorState editorState, BuildContext context); @@ -63,7 +64,7 @@ List defaultToolbarItems = [ ToolbarItem( id: 'appflowy.toolbar.h1', type: 1, - tooltipsMessage: 'Heading 1', + tooltipsMessage: AppFlowyEditorLocalizations.current.heading1, iconBuilder: (isHighlight) => FlowySvg( name: 'toolbar/h1', color: isHighlight ? Colors.lightBlue : null, @@ -71,15 +72,16 @@ List defaultToolbarItems = [ validator: _onlyShowInSingleTextSelection, highlightCallback: (editorState) => _allSatisfy( editorState, - StyleKey.heading, - (value) => value == StyleKey.h1, + BuiltInAttributeKey.heading, + (value) => value == BuiltInAttributeKey.h1, ), - handler: (editorState, context) => formatHeading(editorState, StyleKey.h1), + handler: (editorState, context) => + formatHeading(editorState, BuiltInAttributeKey.h1), ), ToolbarItem( id: 'appflowy.toolbar.h2', type: 1, - tooltipsMessage: 'Heading 2', + tooltipsMessage: AppFlowyEditorLocalizations.current.heading2, iconBuilder: (isHighlight) => FlowySvg( name: 'toolbar/h2', color: isHighlight ? Colors.lightBlue : null, @@ -87,15 +89,16 @@ List defaultToolbarItems = [ validator: _onlyShowInSingleTextSelection, highlightCallback: (editorState) => _allSatisfy( editorState, - StyleKey.heading, - (value) => value == StyleKey.h2, + BuiltInAttributeKey.heading, + (value) => value == BuiltInAttributeKey.h2, ), - handler: (editorState, context) => formatHeading(editorState, StyleKey.h2), + handler: (editorState, context) => + formatHeading(editorState, BuiltInAttributeKey.h2), ), ToolbarItem( id: 'appflowy.toolbar.h3', type: 1, - tooltipsMessage: 'Heading 3', + tooltipsMessage: AppFlowyEditorLocalizations.current.heading3, iconBuilder: (isHighlight) => FlowySvg( name: 'toolbar/h3', color: isHighlight ? Colors.lightBlue : null, @@ -103,15 +106,16 @@ List defaultToolbarItems = [ validator: _onlyShowInSingleTextSelection, highlightCallback: (editorState) => _allSatisfy( editorState, - StyleKey.heading, - (value) => value == StyleKey.h3, + BuiltInAttributeKey.heading, + (value) => value == BuiltInAttributeKey.h3, ), - handler: (editorState, context) => formatHeading(editorState, StyleKey.h3), + handler: (editorState, context) => + formatHeading(editorState, BuiltInAttributeKey.h3), ), ToolbarItem( id: 'appflowy.toolbar.bold', type: 2, - tooltipsMessage: 'Bold', + tooltipsMessage: AppFlowyEditorLocalizations.current.bold, iconBuilder: (isHighlight) => FlowySvg( name: 'toolbar/bold', color: isHighlight ? Colors.lightBlue : null, @@ -119,7 +123,7 @@ List defaultToolbarItems = [ validator: _showInTextSelection, highlightCallback: (editorState) => _allSatisfy( editorState, - StyleKey.bold, + BuiltInAttributeKey.bold, (value) => value == true, ), handler: (editorState, context) => formatBold(editorState), @@ -127,7 +131,7 @@ List defaultToolbarItems = [ ToolbarItem( id: 'appflowy.toolbar.italic', type: 2, - tooltipsMessage: 'Italic', + tooltipsMessage: AppFlowyEditorLocalizations.current.italic, iconBuilder: (isHighlight) => FlowySvg( name: 'toolbar/italic', color: isHighlight ? Colors.lightBlue : null, @@ -135,7 +139,7 @@ List defaultToolbarItems = [ validator: _showInTextSelection, highlightCallback: (editorState) => _allSatisfy( editorState, - StyleKey.italic, + BuiltInAttributeKey.italic, (value) => value == true, ), handler: (editorState, context) => formatItalic(editorState), @@ -143,7 +147,7 @@ List defaultToolbarItems = [ ToolbarItem( id: 'appflowy.toolbar.underline', type: 2, - tooltipsMessage: 'Underline', + tooltipsMessage: AppFlowyEditorLocalizations.current.underline, iconBuilder: (isHighlight) => FlowySvg( name: 'toolbar/underline', color: isHighlight ? Colors.lightBlue : null, @@ -151,7 +155,7 @@ List defaultToolbarItems = [ validator: _showInTextSelection, highlightCallback: (editorState) => _allSatisfy( editorState, - StyleKey.underline, + BuiltInAttributeKey.underline, (value) => value == true, ), handler: (editorState, context) => formatUnderline(editorState), @@ -159,7 +163,7 @@ List defaultToolbarItems = [ ToolbarItem( id: 'appflowy.toolbar.strikethrough', type: 2, - tooltipsMessage: 'Strikethrough', + tooltipsMessage: AppFlowyEditorLocalizations.current.strikethrough, iconBuilder: (isHighlight) => FlowySvg( name: 'toolbar/strikethrough', color: isHighlight ? Colors.lightBlue : null, @@ -167,7 +171,7 @@ List defaultToolbarItems = [ validator: _showInTextSelection, highlightCallback: (editorState) => _allSatisfy( editorState, - StyleKey.strikethrough, + BuiltInAttributeKey.strikethrough, (value) => value == true, ), handler: (editorState, context) => formatStrikethrough(editorState), @@ -175,7 +179,7 @@ List defaultToolbarItems = [ ToolbarItem( id: 'appflowy.toolbar.code', type: 2, - tooltipsMessage: 'Embed Code', + tooltipsMessage: AppFlowyEditorLocalizations.current.embedCode, iconBuilder: (isHighlight) => FlowySvg( name: 'toolbar/code', color: isHighlight ? Colors.lightBlue : null, @@ -183,15 +187,15 @@ List defaultToolbarItems = [ validator: _showInTextSelection, highlightCallback: (editorState) => _allSatisfy( editorState, - StyleKey.code, - (value) => value == StyleKey.code, + BuiltInAttributeKey.code, + (value) => value == true, ), handler: (editorState, context) => formatEmbedCode(editorState), ), ToolbarItem( id: 'appflowy.toolbar.quote', type: 3, - tooltipsMessage: 'Quote', + tooltipsMessage: AppFlowyEditorLocalizations.current.quote, iconBuilder: (isHighlight) => FlowySvg( name: 'toolbar/quote', color: isHighlight ? Colors.lightBlue : null, @@ -199,15 +203,15 @@ List defaultToolbarItems = [ validator: _onlyShowInSingleTextSelection, highlightCallback: (editorState) => _allSatisfy( editorState, - StyleKey.subtype, - (value) => value == StyleKey.quote, + BuiltInAttributeKey.subtype, + (value) => value == BuiltInAttributeKey.quote, ), handler: (editorState, context) => formatQuote(editorState), ), ToolbarItem( id: 'appflowy.toolbar.bulleted_list', type: 3, - tooltipsMessage: 'Bulleted list', + tooltipsMessage: AppFlowyEditorLocalizations.current.bulletedList, iconBuilder: (isHighlight) => FlowySvg( name: 'toolbar/bulleted_list', color: isHighlight ? Colors.lightBlue : null, @@ -215,15 +219,15 @@ List defaultToolbarItems = [ validator: _onlyShowInSingleTextSelection, highlightCallback: (editorState) => _allSatisfy( editorState, - StyleKey.subtype, - (value) => value == StyleKey.bulletedList, + BuiltInAttributeKey.subtype, + (value) => value == BuiltInAttributeKey.bulletedList, ), handler: (editorState, context) => formatBulletedList(editorState), ), ToolbarItem( id: 'appflowy.toolbar.link', type: 4, - tooltipsMessage: 'Link', + tooltipsMessage: AppFlowyEditorLocalizations.current.link, iconBuilder: (isHighlight) => FlowySvg( name: 'toolbar/link', color: isHighlight ? Colors.lightBlue : null, @@ -231,7 +235,7 @@ List defaultToolbarItems = [ validator: _onlyShowInSingleTextSelection, highlightCallback: (editorState) => _allSatisfy( editorState, - StyleKey.href, + BuiltInAttributeKey.href, (value) => value != null, ), handler: (editorState, context) => showLinkMenu(context, editorState), @@ -239,7 +243,7 @@ List defaultToolbarItems = [ ToolbarItem( id: 'appflowy.toolbar.highlight', type: 4, - tooltipsMessage: 'Highlight', + tooltipsMessage: AppFlowyEditorLocalizations.current.highlight, iconBuilder: (isHighlight) => FlowySvg( name: 'toolbar/highlight', color: isHighlight ? Colors.lightBlue : null, @@ -247,10 +251,13 @@ List defaultToolbarItems = [ validator: _showInTextSelection, highlightCallback: (editorState) => _allSatisfy( editorState, - StyleKey.backgroundColor, + BuiltInAttributeKey.backgroundColor, (value) => value != null, ), - handler: (editorState, context) => formatHighlight(editorState), + handler: (editorState, context) => formatHighlight( + editorState, + editorState.editorStyle.textStyle.highlightColorHex, + ), ), ]; @@ -296,6 +303,9 @@ void showLinkMenu( matchRect = rect; } } + final baseOffset = + editorState.renderBox?.localToGlobal(Offset.zero) ?? Offset.zero; + matchRect = matchRect.shift(-baseOffset); _dismissLinkMenu(); _editorState = editorState; @@ -314,7 +324,8 @@ void showLinkMenu( final textNode = node.first as TextNode; String? linkText; if (textNode.allSatisfyLinkInSelection(selection)) { - linkText = textNode.getAttributeInSelection(selection, StyleKey.href); + linkText = + textNode.getAttributeInSelection(selection, BuiltInAttributeKey.href); } _linkMenuOverlay = OverlayEntry(builder: (context) { return Positioned( @@ -328,7 +339,8 @@ void showLinkMenu( }, onSubmitted: (text) { TransactionBuilder(editorState) - ..formatText(textNode, index, length, {StyleKey.href: text}) + ..formatText( + textNode, index, length, {BuiltInAttributeKey.href: text}) ..commit(); _dismissLinkMenu(); }, @@ -338,7 +350,8 @@ void showLinkMenu( }, onRemoveLink: () { TransactionBuilder(editorState) - ..formatText(textNode, index, length, {StyleKey.href: null}) + ..formatText( + textNode, index, length, {BuiltInAttributeKey.href: null}) ..commit(); _dismissLinkMenu(); }, diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/src/service/default_text_operations/format_rich_text_style.dart b/frontend/app_flowy/packages/appflowy_editor/lib/src/service/default_text_operations/format_rich_text_style.dart index b832f149b1..23ddc75a69 100644 --- a/frontend/app_flowy/packages/appflowy_editor/lib/src/service/default_text_operations/format_rich_text_style.dart +++ b/frontend/app_flowy/packages/appflowy_editor/lib/src/service/default_text_operations/format_rich_text_style.dart @@ -6,31 +6,31 @@ import 'package:appflowy_editor/src/editor_state.dart'; import 'package:appflowy_editor/src/extensions/text_node_extensions.dart'; import 'package:appflowy_editor/src/extensions/path_extensions.dart'; import 'package:appflowy_editor/src/operation/transaction_builder.dart'; -import 'package:appflowy_editor/src/render/rich_text/rich_text_style.dart'; +import 'package:appflowy_editor/src/document/built_in_attribute_keys.dart'; void insertHeadingAfterSelection(EditorState editorState, String heading) { insertTextNodeAfterSelection(editorState, { - StyleKey.subtype: StyleKey.heading, - StyleKey.heading: heading, + BuiltInAttributeKey.subtype: BuiltInAttributeKey.heading, + BuiltInAttributeKey.heading: heading, }); } void insertQuoteAfterSelection(EditorState editorState) { insertTextNodeAfterSelection(editorState, { - StyleKey.subtype: StyleKey.quote, + BuiltInAttributeKey.subtype: BuiltInAttributeKey.quote, }); } void insertCheckboxAfterSelection(EditorState editorState) { insertTextNodeAfterSelection(editorState, { - StyleKey.subtype: StyleKey.checkbox, - StyleKey.checkbox: false, + BuiltInAttributeKey.subtype: BuiltInAttributeKey.checkbox, + BuiltInAttributeKey.checkbox: false, }); } void insertBulletedListAfterSelection(EditorState editorState) { insertTextNodeAfterSelection(editorState, { - StyleKey.subtype: StyleKey.bulletedList, + BuiltInAttributeKey.subtype: BuiltInAttributeKey.bulletedList, }); } @@ -68,27 +68,27 @@ void formatText(EditorState editorState) { void formatHeading(EditorState editorState, String heading) { formatTextNodes(editorState, { - StyleKey.subtype: StyleKey.heading, - StyleKey.heading: heading, + BuiltInAttributeKey.subtype: BuiltInAttributeKey.heading, + BuiltInAttributeKey.heading: heading, }); } void formatQuote(EditorState editorState) { formatTextNodes(editorState, { - StyleKey.subtype: StyleKey.quote, + BuiltInAttributeKey.subtype: BuiltInAttributeKey.quote, }); } -void formatCheckbox(EditorState editorState) { +void formatCheckbox(EditorState editorState, bool check) { formatTextNodes(editorState, { - StyleKey.subtype: StyleKey.checkbox, - StyleKey.checkbox: false, + BuiltInAttributeKey.subtype: BuiltInAttributeKey.checkbox, + BuiltInAttributeKey.checkbox: check, }); } void formatBulletedList(EditorState editorState) { formatTextNodes(editorState, { - StyleKey.subtype: StyleKey.bulletedList, + BuiltInAttributeKey.subtype: BuiltInAttributeKey.bulletedList, }); } @@ -107,7 +107,7 @@ bool formatTextNodes(EditorState editorState, Attributes attributes) { ..updateNode( textNode, Attributes.fromIterable( - StyleKey.globalStyleKeys, + BuiltInAttributeKey.globalStyleKeys, value: (_) => null, )..addAll(attributes), ) @@ -124,44 +124,58 @@ bool formatTextNodes(EditorState editorState, Attributes attributes) { } bool formatBold(EditorState editorState) { - return formatRichTextPartialStyle(editorState, StyleKey.bold); + return formatRichTextPartialStyle(editorState, BuiltInAttributeKey.bold); } bool formatItalic(EditorState editorState) { - return formatRichTextPartialStyle(editorState, StyleKey.italic); + return formatRichTextPartialStyle(editorState, BuiltInAttributeKey.italic); } bool formatUnderline(EditorState editorState) { - return formatRichTextPartialStyle(editorState, StyleKey.underline); + return formatRichTextPartialStyle(editorState, BuiltInAttributeKey.underline); } bool formatStrikethrough(EditorState editorState) { - return formatRichTextPartialStyle(editorState, StyleKey.strikethrough); + return formatRichTextPartialStyle( + editorState, BuiltInAttributeKey.strikethrough); } bool formatEmbedCode(EditorState editorState) { - return formatRichTextPartialStyle(editorState, StyleKey.code); + return formatRichTextPartialStyle(editorState, BuiltInAttributeKey.code); } -bool formatHighlight(EditorState editorState) { +bool formatHighlight(EditorState editorState, String colorHex) { bool value = _allSatisfyInSelection( - editorState, StyleKey.backgroundColor, defaultHighlightColor); - return formatRichTextPartialStyle(editorState, StyleKey.backgroundColor, - customValue: value ? defaultBackgroundColor : defaultHighlightColor); + editorState, + BuiltInAttributeKey.backgroundColor, + colorHex, + ); + return formatRichTextPartialStyle( + editorState, + BuiltInAttributeKey.backgroundColor, + customValue: value ? '0x00000000' : colorHex, + ); } bool formatRichTextPartialStyle(EditorState editorState, String styleKey, {Object? customValue}) { Attributes attributes = { styleKey: customValue ?? - !_allSatisfyInSelection(editorState, styleKey, customValue ?? true), + !_allSatisfyInSelection( + editorState, + styleKey, + customValue ?? true, + ), }; return formatRichTextStyle(editorState, attributes); } bool _allSatisfyInSelection( - EditorState editorState, String styleKey, dynamic matchValue) { + EditorState editorState, + String styleKey, + dynamic matchValue, +) { final selection = editorState.service.selectionService.currentSelection.value; final nodes = editorState.service.selectionService.currentSelectedNodes; final textNodes = nodes.whereType().toList(growable: false); diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/src/service/editor_service.dart b/frontend/app_flowy/packages/appflowy_editor/lib/src/service/editor_service.dart index ba9c45732b..654d8e4b26 100644 --- a/frontend/app_flowy/packages/appflowy_editor/lib/src/service/editor_service.dart +++ b/frontend/app_flowy/packages/appflowy_editor/lib/src/service/editor_service.dart @@ -38,7 +38,7 @@ class AppFlowyEditor extends StatefulWidget { this.customBuilders = const {}, this.shortcutEvents = const [], this.selectionMenuItems = const [], - this.editorStyle = const EditorStyle.defaultStyle(), + required this.editorStyle, }) : super(key: key); final EditorState editorState; @@ -58,6 +58,8 @@ class AppFlowyEditor extends StatefulWidget { } class _AppFlowyEditorState extends State { + Widget? services; + EditorState get editorState => widget.editorState; @override @@ -75,19 +77,34 @@ class _AppFlowyEditorState extends State { if (editorState.service != oldWidget.editorState.service) { editorState.selectionMenuItems = widget.selectionMenuItems; - editorState.editorStyle = widget.editorStyle; editorState.service.renderPluginService = _createRenderPlugin(); } + + editorState.editorStyle = widget.editorStyle; + services = null; } @override Widget build(BuildContext context) { + services ??= _buildServices(context); + return Overlay( + initialEntries: [ + OverlayEntry( + builder: (context) => services!, + ), + ], + ); + } + + AppFlowyScroll _buildServices(BuildContext context) { return AppFlowyScroll( key: editorState.service.scrollServiceKey, child: Padding( padding: widget.editorStyle.padding, child: AppFlowySelection( key: editorState.service.selectionServiceKey, + cursorColor: widget.editorStyle.cursorColor, + selectionColor: widget.editorStyle.selectionColor, editorState: editorState, child: AppFlowyInput( key: editorState.service.inputServiceKey, diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/backspace_handler.dart b/frontend/app_flowy/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/backspace_handler.dart index 82d7f7f3b3..9d65088914 100644 --- a/frontend/app_flowy/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/backspace_handler.dart +++ b/frontend/app_flowy/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/backspace_handler.dart @@ -1,8 +1,7 @@ -import 'package:appflowy_editor/src/render/rich_text/rich_text_style.dart'; import 'package:appflowy_editor/src/service/internal_key_event_handlers/number_list_helper.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; - +import 'package:appflowy_editor/src/document/built_in_attribute_keys.dart'; import 'package:appflowy_editor/appflowy_editor.dart'; // Handle delete text. @@ -42,12 +41,12 @@ KeyEventResult _handleBackspace(EditorState editorState, RawKeyEvent event) { if (index < 0 && selection.isCollapsed) { // 1. style if (textNode.subtype != null) { - if (textNode.subtype == StyleKey.numberList) { + if (textNode.subtype == BuiltInAttributeKey.numberList) { cancelNumberListPath = textNode.path; } transactionBuilder ..updateNode(textNode, { - StyleKey.subtype: null, + BuiltInAttributeKey.subtype: null, textNode.subtype!: null, }) ..afterSelection = Selection.collapsed( @@ -91,7 +90,8 @@ KeyEventResult _handleBackspace(EditorState editorState, RawKeyEvent event) { _deleteTextNodes(transactionBuilder, textNodes, selection); transactionBuilder.commit(); - if (nodeAtStart is TextNode && nodeAtStart.subtype == StyleKey.numberList) { + if (nodeAtStart is TextNode && + nodeAtStart.subtype == BuiltInAttributeKey.numberList) { makeFollowingNodesIncremental( editorState, startPosition.path, @@ -130,7 +130,7 @@ KeyEventResult _backDeleteToPreviousTextNode( bool prevIsNumberList = false; while (previous != null) { if (previous is TextNode) { - if (previous.subtype == StyleKey.numberList) { + if (previous.subtype == BuiltInAttributeKey.numberList) { prevIsNumberList = true; } @@ -212,7 +212,8 @@ KeyEventResult _handleDelete(EditorState editorState, RawKeyEvent event) { _deleteTextNodes(transactionBuilder, textNodes, selection); transactionBuilder.commit(); - if (nodeAtStart is TextNode && nodeAtStart.subtype == StyleKey.numberList) { + if (nodeAtStart is TextNode && + nodeAtStart.subtype == BuiltInAttributeKey.numberList) { makeFollowingNodesIncremental( editorState, startPosition.path, transactionBuilder.afterSelection!); } @@ -236,7 +237,7 @@ KeyEventResult _mergeNextLineIntoThisLine( transactionBuilder.deleteNode(nextNode); transactionBuilder.commit(); - if (textNode.subtype == StyleKey.numberList) { + if (textNode.subtype == BuiltInAttributeKey.numberList) { makeFollowingNodesIncremental(editorState, textNode.path, selection); } diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/copy_paste_handler.dart b/frontend/app_flowy/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/copy_paste_handler.dart index df54753e86..e48ec3426d 100644 --- a/frontend/app_flowy/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/copy_paste_handler.dart +++ b/frontend/app_flowy/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/copy_paste_handler.dart @@ -1,7 +1,7 @@ import 'package:appflowy_editor/appflowy_editor.dart'; import 'package:appflowy_editor/src/infra/html_converter.dart'; import 'package:appflowy_editor/src/document/node_iterator.dart'; -import 'package:appflowy_editor/src/render/rich_text/rich_text_style.dart'; +import 'package:appflowy_editor/src/document/built_in_attribute_keys.dart'; import 'package:appflowy_editor/src/service/internal_key_event_handlers/number_list_helper.dart'; import 'package:flutter/material.dart'; import 'package:rich_clipboard/rich_clipboard.dart'; @@ -108,8 +108,8 @@ void _pasteMultipleLinesInText( if (nodeAtPath.type == "text" && firstNode.type == "text") { int? startNumber; - if (nodeAtPath.subtype == StyleKey.numberList) { - startNumber = nodeAtPath.attributes[StyleKey.number] as int; + if (nodeAtPath.subtype == BuiltInAttributeKey.numberList) { + startNumber = nodeAtPath.attributes[BuiltInAttributeKey.number] as int; } // split and merge diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/enter_without_shift_in_text_node_handler.dart b/frontend/app_flowy/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/enter_without_shift_in_text_node_handler.dart index 4ed21f21f8..a82e751083 100644 --- a/frontend/app_flowy/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/enter_without_shift_in_text_node_handler.dart +++ b/frontend/app_flowy/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/enter_without_shift_in_text_node_handler.dart @@ -3,7 +3,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:appflowy_editor/src/extensions/path_extensions.dart'; -import 'package:appflowy_editor/src/render/rich_text/rich_text_style.dart'; +import 'package:appflowy_editor/src/document/built_in_attribute_keys.dart'; import './number_list_helper.dart'; /// Handle some cases where enter is pressed and shift is not pressed. @@ -59,7 +59,8 @@ ShortcutEventHandler enterWithoutShiftInTextNodesHandler = ..afterSelection = afterSelection ..commit(); - if (startNode is TextNode && startNode.subtype == StyleKey.numberList) { + if (startNode is TextNode && + startNode.subtype == BuiltInAttributeKey.numberList) { makeFollowingNodesIncremental( editorState, selection.start.path, afterSelection); } @@ -82,17 +83,15 @@ ShortcutEventHandler enterWithoutShiftInTextNodesHandler = Position(path: textNode.path, offset: 0), ); TransactionBuilder(editorState) - ..updateNode( - textNode, - Attributes.fromIterable( - StyleKey.globalStyleKeys, - value: (_) => null, - )) + ..updateNode(textNode, { + BuiltInAttributeKey.subtype: null, + }) ..afterSelection = afterSelection ..commit(); final nextNode = textNode.next; - if (nextNode is TextNode && nextNode.subtype == StyleKey.numberList) { + if (nextNode is TextNode && + nextNode.subtype == BuiltInAttributeKey.numberList) { makeFollowingNodesIncremental( editorState, textNode.path, afterSelection, beginNum: 0); @@ -103,11 +102,13 @@ ShortcutEventHandler enterWithoutShiftInTextNodesHandler = Position(path: textNode.path.next, offset: 0), ); - if (subtype == StyleKey.numberList) { - final prevNumber = textNode.attributes[StyleKey.number] as int; + if (subtype == BuiltInAttributeKey.numberList) { + final prevNumber = + textNode.attributes[BuiltInAttributeKey.number] as int; final newNode = TextNode.empty(); - newNode.attributes[StyleKey.subtype] = StyleKey.numberList; - newNode.attributes[StyleKey.number] = prevNumber; + newNode.attributes[BuiltInAttributeKey.subtype] = + BuiltInAttributeKey.numberList; + newNode.attributes[BuiltInAttributeKey.number] = prevNumber; final insertPath = textNode.path; TransactionBuilder(editorState) ..insertNode( @@ -159,7 +160,7 @@ ShortcutEventHandler enterWithoutShiftInTextNodesHandler = // If the new type of a text node is number list, // the numbers of the following nodes should be incremental. - if (textNode.subtype == StyleKey.numberList) { + if (textNode.subtype == BuiltInAttributeKey.numberList) { makeFollowingNodesIncremental(editorState, nextPath, afterSelection); } @@ -169,17 +170,17 @@ ShortcutEventHandler enterWithoutShiftInTextNodesHandler = Attributes _attributesFromPreviousLine(TextNode textNode) { final prevAttributes = textNode.attributes; final subType = textNode.subtype; - if (subType == null || subType == StyleKey.heading) { + if (subType == null || subType == BuiltInAttributeKey.heading) { return {}; } final copy = Attributes.from(prevAttributes); - if (subType == StyleKey.numberList) { + if (subType == BuiltInAttributeKey.numberList) { return _nextNumberAttributesFromPreviousLine(copy, textNode); } - if (subType == StyleKey.checkbox) { - copy[StyleKey.checkbox] = false; + if (subType == BuiltInAttributeKey.checkbox) { + copy[BuiltInAttributeKey.checkbox] = false; return copy; } @@ -188,7 +189,7 @@ Attributes _attributesFromPreviousLine(TextNode textNode) { Attributes _nextNumberAttributesFromPreviousLine( Attributes copy, TextNode textNode) { - final prevNum = textNode.attributes[StyleKey.number] as int?; - copy[StyleKey.number] = prevNum == null ? 1 : prevNum + 1; + final prevNum = textNode.attributes[BuiltInAttributeKey.number] as int?; + copy[BuiltInAttributeKey.number] = prevNum == null ? 1 : prevNum + 1; return copy; } diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/format_style_handler.dart b/frontend/app_flowy/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/format_style_handler.dart index 2f17296ca6..db5c298526 100644 --- a/frontend/app_flowy/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/format_style_handler.dart +++ b/frontend/app_flowy/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/format_style_handler.dart @@ -1,8 +1,8 @@ +import 'package:appflowy_editor/src/service/default_text_operations/format_rich_text_style.dart'; import 'package:appflowy_editor/src/service/shortcut_event/shortcut_event_handler.dart'; import 'package:flutter/material.dart'; import 'package:appflowy_editor/src/document/node.dart'; -import 'package:appflowy_editor/src/service/default_text_operations/format_rich_text_style.dart'; ShortcutEventHandler formatBoldEventHandler = (editorState, event) { final selection = editorState.service.selectionService.currentSelection.value; @@ -55,7 +55,10 @@ ShortcutEventHandler formatHighlightEventHandler = (editorState, event) { if (selection == null || textNodes.isEmpty) { return KeyEventResult.ignored; } - formatHighlight(editorState); + formatHighlight( + editorState, + editorState.editorStyle.textStyle.highlightColorHex, + ); return KeyEventResult.handled; }; @@ -73,3 +76,14 @@ ShortcutEventHandler formatLinkEventHandler = (editorState, event) { } return KeyEventResult.ignored; }; + +ShortcutEventHandler formatEmbedCodeEventHandler = (editorState, event) { + final selection = editorState.service.selectionService.currentSelection.value; + final nodes = editorState.service.selectionService.currentSelectedNodes; + final textNodes = nodes.whereType().toList(growable: false); + if (selection == null || textNodes.isEmpty) { + return KeyEventResult.ignored; + } + formatEmbedCode(editorState); + return KeyEventResult.ignored; +}; diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/number_list_helper.dart b/frontend/app_flowy/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/number_list_helper.dart index 4e726fc86e..ed9018a71e 100644 --- a/frontend/app_flowy/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/number_list_helper.dart +++ b/frontend/app_flowy/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/number_list_helper.dart @@ -1,6 +1,6 @@ import 'package:appflowy_editor/src/document/selection.dart'; import 'package:appflowy_editor/src/editor_state.dart'; -import 'package:appflowy_editor/src/render/rich_text/rich_text_style.dart'; +import 'package:appflowy_editor/src/document/built_in_attribute_keys.dart'; import 'package:appflowy_editor/src/operation/transaction_builder.dart'; import 'package:appflowy_editor/src/document/attributes.dart'; @@ -11,7 +11,7 @@ void makeFollowingNodesIncremental( if (insertNode == null) { return; } - beginNum ??= insertNode.attributes[StyleKey.number] as int; + beginNum ??= insertNode.attributes[BuiltInAttributeKey.number] as int; int numPtr = beginNum + 1; var ptr = insertNode.next; @@ -19,13 +19,13 @@ void makeFollowingNodesIncremental( final builder = TransactionBuilder(editorState); while (ptr != null) { - if (ptr.subtype != StyleKey.numberList) { + if (ptr.subtype != BuiltInAttributeKey.numberList) { break; } - final currentNum = ptr.attributes[StyleKey.number] as int; + final currentNum = ptr.attributes[BuiltInAttributeKey.number] as int; if (currentNum != numPtr) { Attributes updateAttributes = {}; - updateAttributes[StyleKey.number] = numPtr; + updateAttributes[BuiltInAttributeKey.number] = numPtr; builder.updateNode(ptr, updateAttributes); } diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/whitespace_handler.dart b/frontend/app_flowy/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/whitespace_handler.dart index fb78fce1b3..fb865ce541 100644 --- a/frontend/app_flowy/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/whitespace_handler.dart +++ b/frontend/app_flowy/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/whitespace_handler.dart @@ -1,14 +1,14 @@ import 'package:appflowy_editor/src/service/shortcut_event/shortcut_event_handler.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; - +import 'package:appflowy_editor/src/document/built_in_attribute_keys.dart'; import 'package:appflowy_editor/src/document/node.dart'; import 'package:appflowy_editor/src/document/position.dart'; import 'package:appflowy_editor/src/document/selection.dart'; import 'package:appflowy_editor/src/editor_state.dart'; import 'package:appflowy_editor/src/operation/transaction_builder.dart'; -import 'package:appflowy_editor/src/render/rich_text/rich_text_style.dart'; import './number_list_helper.dart'; +import 'package:appflowy_editor/src/extensions/attributes_extension.dart'; @visibleForTesting List get checkboxListSymbols => _checkboxListSymbols; @@ -68,7 +68,7 @@ ShortcutEventHandler whiteSpaceHandler = (editorState, event) { KeyEventResult _toNumberList(EditorState editorState, TextNode textNode, String matchText, String numText) { - if (textNode.subtype == StyleKey.bulletedList) { + if (textNode.subtype == BuiltInAttributeKey.bulletedList) { return KeyEventResult.ignored; } @@ -86,8 +86,9 @@ KeyEventResult _toNumberList(EditorState editorState, TextNode textNode, final prevNode = textNode.previous; if (prevNode != null && prevNode is TextNode && - prevNode.attributes[StyleKey.subtype] == StyleKey.numberList) { - final prevNumber = prevNode.attributes[StyleKey.number] as int; + prevNode.attributes[BuiltInAttributeKey.subtype] == + BuiltInAttributeKey.numberList) { + final prevNumber = prevNode.attributes[BuiltInAttributeKey.number] as int; if (numValue != prevNumber + 1) { return KeyEventResult.ignored; } @@ -102,8 +103,10 @@ KeyEventResult _toNumberList(EditorState editorState, TextNode textNode, TransactionBuilder(editorState) ..deleteText(textNode, 0, matchText.length) - ..updateNode(textNode, - {StyleKey.subtype: StyleKey.numberList, StyleKey.number: numValue}) + ..updateNode(textNode, { + BuiltInAttributeKey.subtype: BuiltInAttributeKey.numberList, + BuiltInAttributeKey.number: numValue + }) ..afterSelection = afterSelection ..commit(); @@ -113,13 +116,13 @@ KeyEventResult _toNumberList(EditorState editorState, TextNode textNode, } KeyEventResult _toBulletedList(EditorState editorState, TextNode textNode) { - if (textNode.subtype == StyleKey.bulletedList) { + if (textNode.subtype == BuiltInAttributeKey.bulletedList) { return KeyEventResult.ignored; } TransactionBuilder(editorState) ..deleteText(textNode, 0, 1) ..updateNode(textNode, { - StyleKey.subtype: StyleKey.bulletedList, + BuiltInAttributeKey.subtype: BuiltInAttributeKey.bulletedList, }) ..afterSelection = Selection.collapsed( Position( @@ -132,7 +135,7 @@ KeyEventResult _toBulletedList(EditorState editorState, TextNode textNode) { } KeyEventResult _toCheckboxList(EditorState editorState, TextNode textNode) { - if (textNode.subtype == StyleKey.checkbox) { + if (textNode.subtype == BuiltInAttributeKey.checkbox) { return KeyEventResult.ignored; } final String symbol; @@ -152,8 +155,8 @@ KeyEventResult _toCheckboxList(EditorState editorState, TextNode textNode) { TransactionBuilder(editorState) ..deleteText(textNode, 0, symbol.length) ..updateNode(textNode, { - StyleKey.subtype: StyleKey.checkbox, - StyleKey.checkbox: check, + BuiltInAttributeKey.subtype: BuiltInAttributeKey.checkbox, + BuiltInAttributeKey.checkbox: check, }) ..afterSelection = Selection.collapsed( Position( @@ -178,8 +181,8 @@ KeyEventResult _toHeadingStyle( TransactionBuilder(editorState) ..deleteText(textNode, 0, x) ..updateNode(textNode, { - StyleKey.subtype: StyleKey.heading, - StyleKey.heading: hX, + BuiltInAttributeKey.subtype: BuiltInAttributeKey.heading, + BuiltInAttributeKey.heading: hX, }) ..afterSelection = Selection.collapsed( Position( diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/src/service/selection_service.dart b/frontend/app_flowy/packages/appflowy_editor/lib/src/service/selection_service.dart index ef3b501a5b..d755e8c9f9 100644 --- a/frontend/app_flowy/packages/appflowy_editor/lib/src/service/selection_service.dart +++ b/frontend/app_flowy/packages/appflowy_editor/lib/src/service/selection_service.dart @@ -82,7 +82,7 @@ abstract class AppFlowySelectionService { class AppFlowySelection extends StatefulWidget { const AppFlowySelection({ Key? key, - this.cursorColor = Colors.black, + this.cursorColor = const Color(0xFF00BCF0), this.selectionColor = const Color.fromARGB(53, 111, 201, 231), required this.editorState, required this.child, @@ -343,8 +343,10 @@ class _AppFlowySelectionState extends State currentSelectedNodes = nodes; // TODO: need to be refactored. - Rect? topmostRect; + Offset? toolbarOffset; LayerLink? layerLink; + final editorOffset = + editorState.renderBox?.localToGlobal(Offset.zero) ?? Offset.zero; final backwardNodes = selection.isBackward ? nodes : nodes.reversed.toList(growable: false); @@ -381,13 +383,20 @@ class _AppFlowySelectionState extends State } } + const baseToolbarOffset = Offset(0, 35.0); final rects = selectable.getRectsInSelection(newSelection); for (final rect in rects) { - // TODO: Need to compute more precise location. - topmostRect ??= rect; - layerLink ??= node.layerLink; + final selectionRect = _transformRectToGlobal(selectable, rect); + selectionRects.add(selectionRect); - selectionRects.add(_transformRectToGlobal(selectable, rect)); + // TODO: Need to compute more precise location. + if ((selectionRect.topLeft.dy - editorOffset.dy) <= + baseToolbarOffset.dy) { + toolbarOffset ??= rect.bottomLeft; + } else { + toolbarOffset ??= rect.topLeft - baseToolbarOffset; + } + layerLink ??= node.layerLink; final overlay = OverlayEntry( builder: (context) => SelectionWidget( @@ -402,9 +411,11 @@ class _AppFlowySelectionState extends State Overlay.of(context)?.insertAll(_selectionAreas); - if (topmostRect != null && layerLink != null) { - editorState.service.toolbarService - ?.showInOffset(topmostRect.topLeft, layerLink); + if (toolbarOffset != null && layerLink != null) { + editorState.service.toolbarService?.showInOffset( + toolbarOffset, + layerLink, + ); } } diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/src/service/shortcut_event/built_in_shortcut_events.dart b/frontend/app_flowy/packages/appflowy_editor/lib/src/service/shortcut_event/built_in_shortcut_events.dart index 20b9d9722a..bcba14db11 100644 --- a/frontend/app_flowy/packages/appflowy_editor/lib/src/service/shortcut_event/built_in_shortcut_events.dart +++ b/frontend/app_flowy/packages/appflowy_editor/lib/src/service/shortcut_event/built_in_shortcut_events.dart @@ -144,6 +144,12 @@ List builtInShortcutEvents = [ windowsCommand: 'ctrl+shift+h', handler: formatHighlightEventHandler, ), + ShortcutEvent( + key: 'Format embed code', + command: 'meta+e', + windowsCommand: 'ctrl+e', + handler: formatEmbedCodeEventHandler, + ), ShortcutEvent( key: 'Format link', command: 'meta+k', diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/src/service/toolbar_service.dart b/frontend/app_flowy/packages/appflowy_editor/lib/src/service/toolbar_service.dart index 5d79c44824..6575dced25 100644 --- a/frontend/app_flowy/packages/appflowy_editor/lib/src/service/toolbar_service.dart +++ b/frontend/app_flowy/packages/appflowy_editor/lib/src/service/toolbar_service.dart @@ -44,7 +44,7 @@ class _FlowyToolbarState extends State key: _toolbarWidgetKey, editorState: widget.editorState, layerLink: layerLink, - offset: offset.translate(0, -37.0), + offset: offset, items: _filterItems(defaultToolbarItems), ), ); diff --git a/frontend/app_flowy/packages/appflowy_editor/pubspec.yaml b/frontend/app_flowy/packages/appflowy_editor/pubspec.yaml index 17f75aea6a..af6c9c1f3f 100644 --- a/frontend/app_flowy/packages/appflowy_editor/pubspec.yaml +++ b/frontend/app_flowy/packages/appflowy_editor/pubspec.yaml @@ -22,6 +22,7 @@ dependencies: provider: ^6.0.3 url_launcher: ^6.1.5 logging: ^1.0.2 + intl_utils: ^2.7.0 dev_dependencies: flutter_test: @@ -66,3 +67,13 @@ flutter: # # For details regarding fonts in packages, see # https://flutter.dev/custom-fonts/#from-packages + +flutter_intl: + enabled: true + class_name: AppFlowyEditorLocalizations + main_locale: en + arb_dir: lib/l10n + output_dir: lib/src/l10n + use_deferred_loading: false + localizely: + project_id: b7199c7d-eca0-4025-894d-230cdcafa9aa diff --git a/frontend/app_flowy/packages/appflowy_editor/test/infra/test_editor.dart b/frontend/app_flowy/packages/appflowy_editor/test/infra/test_editor.dart index 7754785fd3..9e7e0a0bb8 100644 --- a/frontend/app_flowy/packages/appflowy_editor/test/infra/test_editor.dart +++ b/frontend/app_flowy/packages/appflowy_editor/test/infra/test_editor.dart @@ -23,18 +23,20 @@ class EditorWidgetTester { _editorState.service.selectionService.currentSelection.value; Future startTesting() async { - await tester.pumpWidget( - MaterialApp( - home: Scaffold( - body: AppFlowyEditor( - editorState: _editorState, - editorStyle: const EditorStyle( - padding: EdgeInsets.symmetric(vertical: 30), - ), - ), + final app = MaterialApp( + localizationsDelegates: const [ + AppFlowyEditorLocalizations.delegate, + ], + supportedLocales: AppFlowyEditorLocalizations.delegate.supportedLocales, + home: Scaffold( + body: AppFlowyEditor( + editorState: _editorState, + editorStyle: EditorStyle.defaultStyle(), ), ), ); + await tester.pumpWidget(app); + await tester.pump(); return this; } diff --git a/frontend/app_flowy/packages/appflowy_editor/test/infra/test_raw_key_event.dart b/frontend/app_flowy/packages/appflowy_editor/test/infra/test_raw_key_event.dart index 451f203e8b..5ad8ddfe3e 100644 --- a/frontend/app_flowy/packages/appflowy_editor/test/infra/test_raw_key_event.dart +++ b/frontend/app_flowy/packages/appflowy_editor/test/infra/test_raw_key_event.dart @@ -118,6 +118,9 @@ extension on LogicalKeyboardKey { if (this == LogicalKeyboardKey.keyC) { return PhysicalKeyboardKey.keyC; } + if (this == LogicalKeyboardKey.keyE) { + return PhysicalKeyboardKey.keyE; + } if (this == LogicalKeyboardKey.keyI) { return PhysicalKeyboardKey.keyI; } diff --git a/frontend/app_flowy/packages/appflowy_editor/test/render/rich_text/checkbox_text_test.dart b/frontend/app_flowy/packages/appflowy_editor/test/render/rich_text/checkbox_text_test.dart index afd89ddee9..2abda03538 100644 --- a/frontend/app_flowy/packages/appflowy_editor/test/render/rich_text/checkbox_text_test.dart +++ b/frontend/app_flowy/packages/appflowy_editor/test/render/rich_text/checkbox_text_test.dart @@ -1,9 +1,10 @@ import 'package:appflowy_editor/appflowy_editor.dart'; +import 'package:appflowy_editor/src/document/built_in_attribute_keys.dart'; import 'package:appflowy_editor/src/render/rich_text/default_selectable.dart'; -import 'package:appflowy_editor/src/render/rich_text/rich_text_style.dart'; import 'package:appflowy_editor/src/extensions/text_node_extensions.dart'; import 'package:flutter_test/flutter_test.dart'; import '../../infra/test_editor.dart'; +import 'package:appflowy_editor/src/extensions/attributes_extension.dart'; void main() async { setUpAll(() { @@ -25,15 +26,15 @@ void main() async { ..insertTextNode( '', attributes: { - StyleKey.subtype: StyleKey.checkbox, - StyleKey.checkbox: false, + BuiltInAttributeKey.subtype: BuiltInAttributeKey.checkbox, + BuiltInAttributeKey.checkbox: false, }, delta: Delta([ TextInsert(text, { - StyleKey.bold: true, - StyleKey.italic: true, - StyleKey.underline: true, - StyleKey.strikethrough: true, + BuiltInAttributeKey.bold: true, + BuiltInAttributeKey.italic: true, + BuiltInAttributeKey.underline: true, + BuiltInAttributeKey.strikethrough: true, }), ]), ); diff --git a/frontend/app_flowy/packages/appflowy_editor/test/render/rich_text/toolbar_rich_text_test.dart b/frontend/app_flowy/packages/appflowy_editor/test/render/rich_text/toolbar_rich_text_test.dart index 441cc6743b..ef78eeab34 100644 --- a/frontend/app_flowy/packages/appflowy_editor/test/render/rich_text/toolbar_rich_text_test.dart +++ b/frontend/app_flowy/packages/appflowy_editor/test/render/rich_text/toolbar_rich_text_test.dart @@ -1,10 +1,11 @@ import 'package:appflowy_editor/appflowy_editor.dart'; +import 'package:appflowy_editor/src/document/built_in_attribute_keys.dart'; import 'package:appflowy_editor/src/extensions/text_node_extensions.dart'; -import 'package:appflowy_editor/src/render/rich_text/rich_text_style.dart'; import 'package:appflowy_editor/src/render/toolbar/toolbar_item_widget.dart'; import 'package:appflowy_editor/src/render/toolbar/toolbar_widget.dart'; import 'package:flutter_test/flutter_test.dart'; import '../../infra/test_editor.dart'; +import 'package:appflowy_editor/src/extensions/attributes_extension.dart'; void main() async { setUpAll(() { @@ -229,7 +230,7 @@ void main() async { expect( node.allSatisfyInSelection( code, - StyleKey.code, + BuiltInAttributeKey.code, (value) { return value == true; }, @@ -319,7 +320,7 @@ void main() async { expect( node.allSatisfyInSelection( selection, - StyleKey.backgroundColor, + BuiltInAttributeKey.backgroundColor, (value) { return value == blue; }, diff --git a/frontend/app_flowy/packages/appflowy_editor/test/render/selection_menu/selection_menu_widget_test.dart b/frontend/app_flowy/packages/appflowy_editor/test/render/selection_menu/selection_menu_widget_test.dart index 6006fe6a7a..cc9a2fbd0b 100644 --- a/frontend/app_flowy/packages/appflowy_editor/test/render/selection_menu/selection_menu_widget_test.dart +++ b/frontend/app_flowy/packages/appflowy_editor/test/render/selection_menu/selection_menu_widget_test.dart @@ -1,5 +1,5 @@ import 'package:appflowy_editor/appflowy_editor.dart'; -import 'package:appflowy_editor/src/render/rich_text/rich_text_style.dart'; +import 'package:appflowy_editor/src/document/built_in_attribute_keys.dart'; import 'package:appflowy_editor/src/render/selection_menu/selection_menu_item_widget.dart'; import 'package:appflowy_editor/src/render/selection_menu/selection_menu_service.dart'; import 'package:appflowy_editor/src/render/selection_menu/selection_menu_widget.dart'; @@ -13,91 +13,99 @@ void main() async { }); group('selection_menu_widget.dart', () { - for (var i = 0; i < defaultSelectionMenuItems.length; i++) { - 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); - } + // 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); + } - 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('Search item in selection menu util no results', (tester) async { - final editor = await _prepare(tester); - await editor.pressLogicKey(LogicalKeyboardKey.keyT); - await editor.pressLogicKey(LogicalKeyboardKey.keyE); - expect( - find.byType(SelectionMenuItemWidget, skipOffstage: false), - findsNWidgets(3), - ); - await editor.pressLogicKey(LogicalKeyboardKey.backspace); - expect( - find.byType(SelectionMenuItemWidget, skipOffstage: false), - findsNWidgets(4), - ); - await editor.pressLogicKey(LogicalKeyboardKey.keyE); - expect( - find.byType(SelectionMenuItemWidget, skipOffstage: false), - findsNWidgets(3), - ); - await editor.pressLogicKey(LogicalKeyboardKey.keyX); - expect( - find.byType(SelectionMenuItemWidget, skipOffstage: false), - findsNWidgets(1), - ); - await editor.pressLogicKey(LogicalKeyboardKey.keyT); - expect( - find.byType(SelectionMenuItemWidget, skipOffstage: false), - findsNWidgets(1), - ); - await editor.pressLogicKey(LogicalKeyboardKey.keyT); - expect( - find.byType(SelectionMenuItemWidget, skipOffstage: false), - findsNothing, - ); - }); + testWidgets('Search item in selection menu util no results', + (tester) async { + final editor = await _prepare(tester); + await editor.pressLogicKey(LogicalKeyboardKey.keyT); + await editor.pressLogicKey(LogicalKeyboardKey.keyE); + expect( + find.byType(SelectionMenuItemWidget, skipOffstage: false), + findsNWidgets(3), + ); + await editor.pressLogicKey(LogicalKeyboardKey.backspace); + expect( + find.byType(SelectionMenuItemWidget, skipOffstage: false), + findsNWidgets(4), + ); + await editor.pressLogicKey(LogicalKeyboardKey.keyE); + expect( + find.byType(SelectionMenuItemWidget, skipOffstage: false), + findsNWidgets(3), + ); + await editor.pressLogicKey(LogicalKeyboardKey.keyX); + expect( + find.byType(SelectionMenuItemWidget, skipOffstage: false), + findsNWidgets(1), + ); + await editor.pressLogicKey(LogicalKeyboardKey.keyT); + expect( + find.byType(SelectionMenuItemWidget, skipOffstage: false), + findsNWidgets(1), + ); + await editor.pressLogicKey(LogicalKeyboardKey.keyT); + expect( + find.byType(SelectionMenuItemWidget, skipOffstage: false), + findsNothing, + ); + }); - testWidgets('Search item in selection menu and presses esc', (tester) async { - final editor = await _prepare(tester); - await editor.pressLogicKey(LogicalKeyboardKey.keyT); - await editor.pressLogicKey(LogicalKeyboardKey.keyE); - expect( - find.byType(SelectionMenuItemWidget, skipOffstage: false), - findsNWidgets(3), - ); - await editor.pressLogicKey(LogicalKeyboardKey.escape); - expect( - find.byType(SelectionMenuItemWidget, skipOffstage: false), - findsNothing, - ); - }); + testWidgets('Search item in selection menu and presses esc', + (tester) async { + final editor = await _prepare(tester); + await editor.pressLogicKey(LogicalKeyboardKey.keyT); + await editor.pressLogicKey(LogicalKeyboardKey.keyE); + expect( + find.byType(SelectionMenuItemWidget, skipOffstage: false), + findsNWidgets(3), + ); + await editor.pressLogicKey(LogicalKeyboardKey.escape); + expect( + find.byType(SelectionMenuItemWidget, skipOffstage: false), + findsNothing, + ); + }); - testWidgets('Search item in selection menu and presses backspace', - (tester) async { - final editor = await _prepare(tester); - await editor.pressLogicKey(LogicalKeyboardKey.keyT); - await editor.pressLogicKey(LogicalKeyboardKey.keyE); - expect( - find.byType(SelectionMenuItemWidget, skipOffstage: false), - findsNWidgets(3), - ); - await editor.pressLogicKey(LogicalKeyboardKey.backspace); - await editor.pressLogicKey(LogicalKeyboardKey.backspace); - await editor.pressLogicKey(LogicalKeyboardKey.backspace); - expect( - find.byType(SelectionMenuItemWidget, skipOffstage: false), - findsNothing, - ); + testWidgets('Search item in selection menu and presses backspace', + (tester) async { + final editor = await _prepare(tester); + await editor.pressLogicKey(LogicalKeyboardKey.keyT); + await editor.pressLogicKey(LogicalKeyboardKey.keyE); + expect( + find.byType(SelectionMenuItemWidget, skipOffstage: false), + findsNWidgets(3), + ); + await editor.pressLogicKey(LogicalKeyboardKey.backspace); + await editor.pressLogicKey(LogicalKeyboardKey.backspace); + await editor.pressLogicKey(LogicalKeyboardKey.backspace); + expect( + find.byType(SelectionMenuItemWidget, skipOffstage: false), + findsNothing, + ); + }); }); } @@ -135,18 +143,18 @@ Future _testDefaultSelectionMenuItems( if (item.name == 'Text') { expect(node?.subtype == null, true); } else if (item.name == 'Heading 1') { - expect(node?.subtype, StyleKey.heading); - expect(node?.attributes.heading, StyleKey.h1); + expect(node?.subtype, BuiltInAttributeKey.heading); + expect(node?.attributes.heading, BuiltInAttributeKey.h1); } else if (item.name == 'Heading 2') { - expect(node?.subtype, StyleKey.heading); - expect(node?.attributes.heading, StyleKey.h2); + expect(node?.subtype, BuiltInAttributeKey.heading); + expect(node?.attributes.heading, BuiltInAttributeKey.h2); } else if (item.name == 'Heading 3') { - expect(node?.subtype, StyleKey.heading); - expect(node?.attributes.heading, StyleKey.h3); + expect(node?.subtype, BuiltInAttributeKey.heading); + expect(node?.attributes.heading, BuiltInAttributeKey.h3); } else if (item.name == 'Bulleted list') { - expect(node?.subtype, StyleKey.bulletedList); + expect(node?.subtype, BuiltInAttributeKey.bulletedList); } else if (item.name == 'Checkbox') { - expect(node?.subtype, StyleKey.checkbox); + expect(node?.subtype, BuiltInAttributeKey.checkbox); expect(node?.attributes.check, false); } } diff --git a/frontend/app_flowy/packages/appflowy_editor/test/service/internal_key_event_handlers/backspace_handler_test.dart b/frontend/app_flowy/packages/appflowy_editor/test/service/internal_key_event_handlers/backspace_handler_test.dart index da5d22a786..e31ad0f252 100644 --- a/frontend/app_flowy/packages/appflowy_editor/test/service/internal_key_event_handlers/backspace_handler_test.dart +++ b/frontend/app_flowy/packages/appflowy_editor/test/service/internal_key_event_handlers/backspace_handler_test.dart @@ -1,10 +1,11 @@ import 'package:appflowy_editor/appflowy_editor.dart'; import 'package:appflowy_editor/src/render/image/image_node_widget.dart'; -import 'package:appflowy_editor/src/render/rich_text/rich_text_style.dart'; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:network_image_mock/network_image_mock.dart'; import '../../infra/test_editor.dart'; +import 'package:appflowy_editor/src/document/built_in_attribute_keys.dart'; +import 'package:appflowy_editor/src/extensions/attributes_extension.dart'; void main() async { setUpAll(() { @@ -132,17 +133,18 @@ void main() async { // testWidgets('Presses backspace key in styled text (checkbox)', (tester) async { - await _deleteStyledTextByBackspace(tester, StyleKey.checkbox); + await _deleteStyledTextByBackspace(tester, BuiltInAttributeKey.checkbox); }); testWidgets('Presses backspace key in styled text (bulletedList)', (tester) async { - await _deleteStyledTextByBackspace(tester, StyleKey.bulletedList); + await _deleteStyledTextByBackspace( + tester, BuiltInAttributeKey.bulletedList); }); testWidgets('Presses backspace key in styled text (heading)', (tester) async { - await _deleteStyledTextByBackspace(tester, StyleKey.heading); + await _deleteStyledTextByBackspace(tester, BuiltInAttributeKey.heading); }); testWidgets('Presses backspace key in styled text (quote)', (tester) async { - await _deleteStyledTextByBackspace(tester, StyleKey.quote); + await _deleteStyledTextByBackspace(tester, BuiltInAttributeKey.quote); }); // Before @@ -157,17 +159,17 @@ void main() async { // [Style] Welcome to Appflowy 😁 // testWidgets('Presses delete key in styled text (checkbox)', (tester) async { - await _deleteStyledTextByDelete(tester, StyleKey.checkbox); + await _deleteStyledTextByDelete(tester, BuiltInAttributeKey.checkbox); }); testWidgets('Presses delete key in styled text (bulletedList)', (tester) async { - await _deleteStyledTextByDelete(tester, StyleKey.bulletedList); + await _deleteStyledTextByDelete(tester, BuiltInAttributeKey.bulletedList); }); testWidgets('Presses delete key in styled text (heading)', (tester) async { - await _deleteStyledTextByDelete(tester, StyleKey.heading); + await _deleteStyledTextByDelete(tester, BuiltInAttributeKey.heading); }); testWidgets('Presses delete key in styled text (quote)', (tester) async { - await _deleteStyledTextByDelete(tester, StyleKey.quote); + await _deleteStyledTextByDelete(tester, BuiltInAttributeKey.quote); }); // Before @@ -250,7 +252,7 @@ void main() async { await editor.pressLogicKey(LogicalKeyboardKey.space); expect( (editor.nodeAtPath([0]) as TextNode).attributes.heading, - StyleKey.h1, + BuiltInAttributeKey.h1, ); await editor.pressLogicKey(LogicalKeyboardKey.backspace); @@ -263,7 +265,7 @@ void main() async { await editor.pressLogicKey(LogicalKeyboardKey.space); expect( (editor.nodeAtPath([0]) as TextNode).attributes.heading, - StyleKey.h1, + BuiltInAttributeKey.h1, ); }); } @@ -330,14 +332,14 @@ Future _deleteStyledTextByBackspace( WidgetTester tester, String style) async { const text = 'Welcome to Appflowy 😁'; Attributes attributes = { - StyleKey.subtype: style, + BuiltInAttributeKey.subtype: style, }; - if (style == StyleKey.checkbox) { - attributes[StyleKey.checkbox] = true; - } else if (style == StyleKey.numberList) { - attributes[StyleKey.number] = 1; - } else if (style == StyleKey.heading) { - attributes[StyleKey.heading] = StyleKey.h1; + if (style == BuiltInAttributeKey.checkbox) { + attributes[BuiltInAttributeKey.checkbox] = true; + } else if (style == BuiltInAttributeKey.numberList) { + attributes[BuiltInAttributeKey.number] = 1; + } else if (style == BuiltInAttributeKey.heading) { + attributes[BuiltInAttributeKey.heading] = BuiltInAttributeKey.h1; } final editor = tester.editor ..insertTextNode(text) @@ -377,14 +379,14 @@ Future _deleteStyledTextByDelete( WidgetTester tester, String style) async { const text = 'Welcome to Appflowy 😁'; Attributes attributes = { - StyleKey.subtype: style, + BuiltInAttributeKey.subtype: style, }; - if (style == StyleKey.checkbox) { - attributes[StyleKey.checkbox] = true; - } else if (style == StyleKey.numberList) { - attributes[StyleKey.number] = 1; - } else if (style == StyleKey.heading) { - attributes[StyleKey.heading] = StyleKey.h1; + if (style == BuiltInAttributeKey.checkbox) { + attributes[BuiltInAttributeKey.checkbox] = true; + } else if (style == BuiltInAttributeKey.numberList) { + attributes[BuiltInAttributeKey.number] = 1; + } else if (style == BuiltInAttributeKey.heading) { + attributes[BuiltInAttributeKey.heading] = BuiltInAttributeKey.h1; } final editor = tester.editor ..insertTextNode(text) diff --git a/frontend/app_flowy/packages/appflowy_editor/test/service/internal_key_event_handlers/enter_without_shift_in_text_node_handler_test.dart b/frontend/app_flowy/packages/appflowy_editor/test/service/internal_key_event_handlers/enter_without_shift_in_text_node_handler_test.dart index 5bfe1ada67..cb2d10ea2f 100644 --- a/frontend/app_flowy/packages/appflowy_editor/test/service/internal_key_event_handlers/enter_without_shift_in_text_node_handler_test.dart +++ b/frontend/app_flowy/packages/appflowy_editor/test/service/internal_key_event_handlers/enter_without_shift_in_text_node_handler_test.dart @@ -1,8 +1,8 @@ import 'package:appflowy_editor/appflowy_editor.dart'; -import 'package:appflowy_editor/src/render/rich_text/rich_text_style.dart'; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import '../../infra/test_editor.dart'; +import 'package:appflowy_editor/src/document/built_in_attribute_keys.dart'; void main() async { setUpAll(() { @@ -95,16 +95,16 @@ void main() async { // [Style] Welcome to Appflowy 😁 // [Style] testWidgets('Presses enter key in bulleted list', (tester) async { - await _testStyleNeedToBeCopy(tester, StyleKey.bulletedList); + await _testStyleNeedToBeCopy(tester, BuiltInAttributeKey.bulletedList); }); testWidgets('Presses enter key in numbered list', (tester) async { - await _testStyleNeedToBeCopy(tester, StyleKey.numberList); + await _testStyleNeedToBeCopy(tester, BuiltInAttributeKey.numberList); }); testWidgets('Presses enter key in checkbox styled text', (tester) async { - await _testStyleNeedToBeCopy(tester, StyleKey.checkbox); + await _testStyleNeedToBeCopy(tester, BuiltInAttributeKey.checkbox); }); testWidgets('Presses enter key in quoted text', (tester) async { - await _testStyleNeedToBeCopy(tester, StyleKey.quote); + await _testStyleNeedToBeCopy(tester, BuiltInAttributeKey.quote); }); testWidgets('Presses enter key in multiple selection from top to bottom', @@ -143,12 +143,12 @@ void main() async { Future _testStyleNeedToBeCopy(WidgetTester tester, String style) async { const text = 'Welcome to Appflowy 😁'; Attributes attributes = { - StyleKey.subtype: style, + BuiltInAttributeKey.subtype: style, }; - if (style == StyleKey.checkbox) { - attributes[StyleKey.checkbox] = true; - } else if (style == StyleKey.numberList) { - attributes[StyleKey.number] = 1; + if (style == BuiltInAttributeKey.checkbox) { + attributes[BuiltInAttributeKey.checkbox] = true; + } else if (style == BuiltInAttributeKey.numberList) { + attributes[BuiltInAttributeKey.number] = 1; } final editor = tester.editor ..insertTextNode(text) diff --git a/frontend/app_flowy/packages/appflowy_editor/test/service/internal_key_event_handlers/update_text_style_by_command_x_handler_test.dart b/frontend/app_flowy/packages/appflowy_editor/test/service/internal_key_event_handlers/format_style_handler_test.dart similarity index 91% rename from frontend/app_flowy/packages/appflowy_editor/test/service/internal_key_event_handlers/update_text_style_by_command_x_handler_test.dart rename to frontend/app_flowy/packages/appflowy_editor/test/service/internal_key_event_handlers/format_style_handler_test.dart index bf595b1deb..44ffb074d8 100644 --- a/frontend/app_flowy/packages/appflowy_editor/test/service/internal_key_event_handlers/update_text_style_by_command_x_handler_test.dart +++ b/frontend/app_flowy/packages/appflowy_editor/test/service/internal_key_event_handlers/format_style_handler_test.dart @@ -2,24 +2,24 @@ import 'dart:io'; import 'package:appflowy_editor/appflowy_editor.dart'; import 'package:appflowy_editor/src/render/link_menu/link_menu.dart'; -import 'package:appflowy_editor/src/render/rich_text/rich_text_style.dart'; import 'package:appflowy_editor/src/extensions/text_node_extensions.dart'; import 'package:appflowy_editor/src/render/toolbar/toolbar_widget.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import '../../infra/test_editor.dart'; +import 'package:appflowy_editor/src/document/built_in_attribute_keys.dart'; void main() async { setUpAll(() { TestWidgetsFlutterBinding.ensureInitialized(); }); - group('update_text_style_by_command_x_handler.dart', () { + group('format_style_handler.dart', () { testWidgets('Presses Command + B to update text style', (tester) async { await _testUpdateTextStyleByCommandX( tester, - StyleKey.bold, + BuiltInAttributeKey.bold, true, LogicalKeyboardKey.keyB, ); @@ -27,7 +27,7 @@ void main() async { testWidgets('Presses Command + I to update text style', (tester) async { await _testUpdateTextStyleByCommandX( tester, - StyleKey.italic, + BuiltInAttributeKey.italic, true, LogicalKeyboardKey.keyI, ); @@ -35,7 +35,7 @@ void main() async { testWidgets('Presses Command + U to update text style', (tester) async { await _testUpdateTextStyleByCommandX( tester, - StyleKey.underline, + BuiltInAttributeKey.underline, true, LogicalKeyboardKey.keyU, ); @@ -44,7 +44,7 @@ void main() async { (tester) async { await _testUpdateTextStyleByCommandX( tester, - StyleKey.strikethrough, + BuiltInAttributeKey.strikethrough, true, LogicalKeyboardKey.keyS, ); @@ -52,10 +52,11 @@ void main() async { testWidgets('Presses Command + Shift + H to update text style', (tester) async { + // FIXME: customize the highlight color instead of using magic number. await _testUpdateTextStyleByCommandX( tester, - StyleKey.backgroundColor, - defaultHighlightColor, + BuiltInAttributeKey.backgroundColor, + '0x6000BCF0', LogicalKeyboardKey.keyH, ); }); @@ -63,6 +64,15 @@ void main() async { testWidgets('Presses Command + K to trigger link menu', (tester) async { await _testLinkMenuInSingleTextSelection(tester); }); + + testWidgets('Presses Command + E to update text style', (tester) async { + await _testUpdateTextStyleByCommandX( + tester, + BuiltInAttributeKey.code, + true, + LogicalKeyboardKey.keyE, + ); + }); }); } @@ -256,7 +266,7 @@ Future _testLinkMenuInSingleTextSelection(WidgetTester tester) async { expect( node.allSatisfyInSelection( selection, - StyleKey.href, + BuiltInAttributeKey.href, (value) => value == link, ), true); @@ -293,7 +303,7 @@ Future _testLinkMenuInSingleTextSelection(WidgetTester tester) async { expect( node.allSatisfyInSelection( selection, - StyleKey.href, + BuiltInAttributeKey.href, (value) => value == link, ), false); diff --git a/frontend/app_flowy/packages/appflowy_editor/test/service/internal_key_event_handlers/white_space_handler_test.dart b/frontend/app_flowy/packages/appflowy_editor/test/service/internal_key_event_handlers/white_space_handler_test.dart index 21f9a5eb65..d6cffa3a44 100644 --- a/frontend/app_flowy/packages/appflowy_editor/test/service/internal_key_event_handlers/white_space_handler_test.dart +++ b/frontend/app_flowy/packages/appflowy_editor/test/service/internal_key_event_handlers/white_space_handler_test.dart @@ -1,9 +1,10 @@ import 'package:appflowy_editor/appflowy_editor.dart'; -import 'package:appflowy_editor/src/render/rich_text/rich_text_style.dart'; import 'package:appflowy_editor/src/service/internal_key_event_handlers/whitespace_handler.dart'; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import '../../infra/test_editor.dart'; +import 'package:appflowy_editor/src/document/built_in_attribute_keys.dart'; +import 'package:appflowy_editor/src/extensions/attributes_extension.dart'; void main() async { setUpAll(() { @@ -45,8 +46,8 @@ void main() async { final textNode = (editor.nodeAtPath([i - 1]) as TextNode); - expect(textNode.subtype, StyleKey.heading); - // StyleKey.h1 ~ StyleKey.h6 + expect(textNode.subtype, BuiltInAttributeKey.heading); + // BuiltInAttributeKey.h1 ~ BuiltInAttributeKey.h6 expect(textNode.attributes.heading, 'h$i'); } }); @@ -85,8 +86,8 @@ void main() async { final textNode = (editor.nodeAtPath([i - 1]) as TextNode); - expect(textNode.subtype, StyleKey.heading); - // StyleKey.h1 ~ StyleKey.h6 + expect(textNode.subtype, BuiltInAttributeKey.heading); + // BuiltInAttributeKey.h1 ~ BuiltInAttributeKey.h6 expect(textNode.attributes.heading, 'h$i'); expect(textNode.toRawString().startsWith('##'), true); } @@ -117,8 +118,8 @@ void main() async { await editor.insertText(textNode, '#' * i, 0); await editor.pressLogicKey(LogicalKeyboardKey.space); - expect(textNode.subtype, StyleKey.heading); - // StyleKey.h2 ~ StyleKey.h6 + expect(textNode.subtype, BuiltInAttributeKey.heading); + // BuiltInAttributeKey.h2 ~ BuiltInAttributeKey.h6 expect(textNode.attributes.heading, 'h$i'); } }); @@ -136,7 +137,7 @@ void main() async { ); await editor.insertText(textNode, symbol, 0); await editor.pressLogicKey(LogicalKeyboardKey.space); - expect(textNode.subtype, StyleKey.checkbox); + expect(textNode.subtype, BuiltInAttributeKey.checkbox); expect(textNode.attributes.check, false); } }); @@ -154,7 +155,7 @@ void main() async { ); await editor.insertText(textNode, symbol, 0); await editor.pressLogicKey(LogicalKeyboardKey.space); - expect(textNode.subtype, StyleKey.checkbox); + expect(textNode.subtype, BuiltInAttributeKey.checkbox); expect(textNode.attributes.check, true); } }); @@ -171,7 +172,7 @@ void main() async { ); await editor.insertText(textNode, symbol, 0); await editor.pressLogicKey(LogicalKeyboardKey.space); - expect(textNode.subtype, StyleKey.bulletedList); + expect(textNode.subtype, BuiltInAttributeKey.bulletedList); } }); }); diff --git a/frontend/app_flowy/packages/appflowy_editor/test/service/shortcut_event/shortcut_event_test.dart b/frontend/app_flowy/packages/appflowy_editor/test/service/shortcut_event/shortcut_event_test.dart index 93a0a7bb84..79b2f4d26c 100644 --- a/frontend/app_flowy/packages/appflowy_editor/test/service/shortcut_event/shortcut_event_test.dart +++ b/frontend/app_flowy/packages/appflowy_editor/test/service/shortcut_event/shortcut_event_test.dart @@ -81,6 +81,8 @@ void main() async { editor.documentSelection, Selection.single(path: [1], startOffset: 0), ); + + tester.pumpAndSettle(); }); }); } diff --git a/frontend/app_flowy/packages/appflowy_editor/test/service/toolbar_service_test.dart b/frontend/app_flowy/packages/appflowy_editor/test/service/toolbar_service_test.dart index 060e730af5..5b82a5dea9 100644 --- a/frontend/app_flowy/packages/appflowy_editor/test/service/toolbar_service_test.dart +++ b/frontend/app_flowy/packages/appflowy_editor/test/service/toolbar_service_test.dart @@ -1,10 +1,10 @@ import 'package:appflowy_editor/appflowy_editor.dart'; -import 'package:appflowy_editor/src/render/rich_text/rich_text_style.dart'; import 'package:appflowy_editor/src/render/toolbar/toolbar_item.dart'; import 'package:appflowy_editor/src/render/toolbar/toolbar_item_widget.dart'; import 'package:appflowy_editor/src/render/toolbar/toolbar_widget.dart'; import 'package:flutter_test/flutter_test.dart'; import '../infra/test_editor.dart'; +import 'package:appflowy_editor/src/document/built_in_attribute_keys.dart'; void main() async { setUpAll(() { @@ -45,13 +45,13 @@ void main() async { }); testWidgets( - 'Test toolbar service in single text selection with StyleKey.partialStyleKeys', + 'Test toolbar service in single text selection with BuiltInAttributeKey.partialStyleKeys', (tester) async { - final attributes = StyleKey.partialStyleKeys.fold({}, - (previousValue, element) { - if (element == StyleKey.backgroundColor) { + final attributes = BuiltInAttributeKey.partialStyleKeys + .fold({}, (previousValue, element) { + if (element == BuiltInAttributeKey.backgroundColor) { previousValue[element] = '0x6000BCF0'; - } else if (element == StyleKey.href) { + } else if (element == BuiltInAttributeKey.href) { previousValue[element] = 'appflowy.io'; } else { previousValue[element] = true; @@ -77,11 +77,11 @@ void main() async { expect(find.byType(ToolbarWidget), findsOneWidget); void testHighlight(bool expectedValue) { - for (final styleKey in StyleKey.partialStyleKeys) { + for (final styleKey in BuiltInAttributeKey.partialStyleKeys) { var key = styleKey; - if (styleKey == StyleKey.backgroundColor) { + if (styleKey == BuiltInAttributeKey.backgroundColor) { key = 'highlight'; - } else if (styleKey == StyleKey.href) { + } else if (styleKey == BuiltInAttributeKey.href) { key = 'link'; } else { continue; @@ -116,22 +116,24 @@ void main() async { }); testWidgets( - 'Test toolbar service in single text selection with StyleKey.globalStyleKeys', + 'Test toolbar service in single text selection with BuiltInAttributeKey.globalStyleKeys', (tester) async { const text = 'Welcome to Appflowy 😁'; final editor = tester.editor ..insertTextNode(text, attributes: { - StyleKey.subtype: StyleKey.heading, - StyleKey.heading: StyleKey.h1, + BuiltInAttributeKey.subtype: BuiltInAttributeKey.heading, + BuiltInAttributeKey.heading: BuiltInAttributeKey.h1, }) ..insertTextNode( text, - attributes: {StyleKey.subtype: StyleKey.quote}, + attributes: {BuiltInAttributeKey.subtype: BuiltInAttributeKey.quote}, ) ..insertTextNode( text, - attributes: {StyleKey.subtype: StyleKey.bulletedList}, + attributes: { + BuiltInAttributeKey.subtype: BuiltInAttributeKey.bulletedList + }, ); await editor.startTesting(); @@ -167,12 +169,12 @@ void main() async { ..insertTextNode( null, attributes: { - StyleKey.subtype: StyleKey.heading, - StyleKey.heading: StyleKey.h1, + BuiltInAttributeKey.subtype: BuiltInAttributeKey.heading, + BuiltInAttributeKey.heading: BuiltInAttributeKey.h1, }, delta: Delta([ TextInsert(text, { - StyleKey.bold: true, + BuiltInAttributeKey.bold: true, }) ]), )