diff --git a/frontend/app_flowy/packages/flowy_editor/example/assets/example.json b/frontend/app_flowy/packages/flowy_editor/example/assets/example.json index a552175146..0dad009cd2 100644 --- a/frontend/app_flowy/packages/flowy_editor/example/assets/example.json +++ b/frontend/app_flowy/packages/flowy_editor/example/assets/example.json @@ -13,10 +13,7 @@ "type": "text", "delta": [ { - "insert": "🌶 Read Me", - "attributes": { - "heading": "h1" - } + "insert": "🌶 Read Me" } ], "attributes": { @@ -27,10 +24,7 @@ "type": "text", "delta": [ { - "insert": "👋 Welcome to Appflowy", - "attributes": { - "heading": "h2" - } + "insert": "👋 Welcome to Appflowy" } ], "attributes": { @@ -41,10 +35,7 @@ "type": "text", "delta": [ { - "insert": "Here are the basics:", - "attributes": { - "heading": "h3" - } + "insert": "Here are the basics:" } ], "attributes": { @@ -102,10 +93,7 @@ "type": "text", "delta": [ { - "insert": "Here are the examples:", - "attributes": { - "heading": "h3" - } + "insert": "Here are the examples:" } ], "attributes": { @@ -149,8 +137,7 @@ "type": "text", "delta": [ { - "insert": "Hello world", - "attributes": { "quote": true } + "insert": "Hello world" } ], "attributes": { @@ -161,8 +148,7 @@ "type": "text", "delta": [ { - "insert": "Hello world", - "attributes": { "quote": true } + "insert": "Hello world" } ], "attributes": { diff --git a/frontend/app_flowy/packages/flowy_editor/lib/render/rich_text/flowy_rich_text.dart b/frontend/app_flowy/packages/flowy_editor/lib/render/rich_text/flowy_rich_text.dart index d90e739d97..66c87a2dd4 100644 --- a/frontend/app_flowy/packages/flowy_editor/lib/render/rich_text/flowy_rich_text.dart +++ b/frontend/app_flowy/packages/flowy_editor/lib/render/rich_text/flowy_rich_text.dart @@ -169,7 +169,7 @@ class _FlowyRichTextState extends State with Selectable { return SizedBox( width: MediaQuery.of(context).size.width - 20, // FIXME: use the const value - child: RichText(key: _textKey, text: _textSpan), + child: RichText(key: _textKey, text: _decorateTextSpanWithGlobalStyle), ); } @@ -255,6 +255,22 @@ class _FlowyRichTextState extends State with Selectable { return Rect.zero; } + TextSpan get _decorateTextSpanWithGlobalStyle => TextSpan( + children: _textSpan.children + ?.whereType() + .map( + (span) => TextSpan( + text: span.text, + style: span.style?.copyWith( + fontSize: _textNode.attributes.fontSize, + color: _textNode.attributes.quoteColor, + ), + recognizer: span.recognizer, + ), + ) + .toList(), + ); + TextSpan get _textSpan => TextSpan( children: _textNode.delta.operations .whereType() diff --git a/frontend/app_flowy/packages/flowy_editor/lib/render/rich_text/rich_text_style.dart b/frontend/app_flowy/packages/flowy_editor/lib/render/rich_text/rich_text_style.dart index 07d4bf4429..b4100f9b87 100644 --- a/frontend/app_flowy/packages/flowy_editor/lib/render/rich_text/rich_text_style.dart +++ b/frontend/app_flowy/packages/flowy_editor/lib/render/rich_text/rich_text_style.dart @@ -2,6 +2,19 @@ import 'package:flowy_editor/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'; @@ -11,6 +24,7 @@ class StyleKey { static String highlightColor = 'highlightColor'; static String font = 'font'; static String href = 'href'; + static String heading = 'heading'; static String quote = 'quote'; static String list = 'list'; @@ -19,7 +33,76 @@ class StyleKey { static String code = 'code'; } -extension AttributesExtensions on Attributes { +double baseFontSize = 16.0; +// TODO: customize. +Map headingToFontSize = { + 'h1': baseFontSize + 15, + 'h2': baseFontSize + 12, + 'h3': baseFontSize + 9, + 'h4': baseFontSize + 6, + 'h5': baseFontSize + 3, + 'h6': baseFontSize, +}; + +extension NodeAttributesExtensions on Attributes { + String? get heading { + if (containsKey(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 { + if (containsKey(StyleKey.quote) && this[StyleKey.quote] == true) { + return this[StyleKey.quote]; + } + return false; + } + + Color? get quoteColor { + if (quote) { + return Colors.grey; + } + return null; + } + + String? get list { + if (containsKey(StyleKey.list) && this[StyleKey.list] is String) { + return this[StyleKey.list]; + } + return null; + } + + int? get number { + if (containsKey(StyleKey.number) && this[StyleKey.number] is int) { + return this[StyleKey.number]; + } + return null; + } + + bool get todo { + if (containsKey(StyleKey.todo) && this[StyleKey.todo] is bool) { + return this[StyleKey.todo]; + } + return false; + } + + bool get code { + if (containsKey(StyleKey.code) && this[StyleKey.code] == true) { + return this[StyleKey.code]; + } + return false; + } +} + +extension DeltaAttributesExtensions on Attributes { bool get bold { return (containsKey(StyleKey.bold) && this[StyleKey.bold] == true); } @@ -68,63 +151,8 @@ extension AttributesExtensions on Attributes { } return null; } - - String? get heading { - if (containsKey(StyleKey.heading) && this[StyleKey.heading] is String) { - return this[StyleKey.heading]; - } - return null; - } - - bool get quote { - if (containsKey(StyleKey.quote) && this[StyleKey.quote] == true) { - return this[StyleKey.quote]; - } - return false; - } - - String? get list { - if (containsKey(StyleKey.list) && this[StyleKey.list] is String) { - return this[StyleKey.list]; - } - return null; - } - - int? get number { - if (containsKey(StyleKey.number) && this[StyleKey.number] is int) { - return this[StyleKey.number]; - } - return null; - } - - bool get todo { - if (containsKey(StyleKey.todo) && this[StyleKey.todo] is bool) { - return this[StyleKey.todo]; - } - return false; - } - - bool get code { - if (containsKey(StyleKey.code) && this[StyleKey.code] == true) { - return this[StyleKey.code]; - } - return false; - } } -/// -/// 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 RichTextStyle { // TODO: customize RichTextStyle({ @@ -154,8 +182,6 @@ class RichTextStyle { FontWeight get fontWeight { if (attributes.bold) { return FontWeight.bold; - } else if (attributes.heading != null) { - return FontWeight.bold; } return FontWeight.normal; } @@ -178,8 +204,6 @@ class RichTextStyle { Color get textColor { if (attributes.href != null) { return Colors.lightBlue; - } else if (attributes.quote) { - return Colors.grey; } return attributes.color ?? Colors.black; } @@ -190,14 +214,7 @@ class RichTextStyle { // font size double get fontSize { - final heading = attributes.heading; - if (heading != null) { - final headings = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6']; - final fontSizes = [30.0, 25.0, 20.0, 20.0, 20.0, 20.0]; - return fontSizes[headings.indexOf(heading)]; - } else { - return 16.0; - } + return baseFontSize; } // recognizer