feat: support RTL and auto direction (#191)
This commit is contained in:
parent
3d31f1c8c2
commit
f8da435ad5
76 changed files with 1148 additions and 473 deletions
|
|
@ -246,4 +246,24 @@ Widget build(BuildContext context) {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
## Changing default text direction
|
||||||
|
|
||||||
|
The text direction is LTR by default, but you can change it by wrapping AppFlowyEditor with Directionality.
|
||||||
|
|
||||||
|
In code snippet below we set the default direction to RTL.
|
||||||
|
|
||||||
|
```dart
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
body: Directionality(
|
||||||
|
textDirection: TextDirection.rtl,
|
||||||
|
child: AppFlowyEditor(
|
||||||
|
editorState: EditorState.blank(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,8 @@ class Editor extends StatelessWidget {
|
||||||
numberedListItem,
|
numberedListItem,
|
||||||
linkItem,
|
linkItem,
|
||||||
buildTextColorItem(),
|
buildTextColorItem(),
|
||||||
buildHighlightColorItem()
|
buildHighlightColorItem(),
|
||||||
|
...textDirectionItems
|
||||||
],
|
],
|
||||||
editorState: editorState,
|
editorState: editorState,
|
||||||
scrollController: scrollController,
|
scrollController: scrollController,
|
||||||
|
|
|
||||||
|
|
@ -199,7 +199,8 @@ final class Node extends ChangeNotifier with LinkedListEntry<Node> {
|
||||||
.toList(growable: false);
|
.toList(growable: false);
|
||||||
}
|
}
|
||||||
if (attributes.isNotEmpty) {
|
if (attributes.isNotEmpty) {
|
||||||
map['data'] = attributes;
|
// filter the null value
|
||||||
|
map['data'] = attributes..removeWhere((_, value) => value == null);
|
||||||
}
|
}
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,7 @@
|
||||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
const blockComponentBackgroundColor = 'bgColor';
|
mixin BlockComponentBackgroundColorMixin {
|
||||||
|
|
||||||
mixin BackgroundColorMixin {
|
|
||||||
Node get node;
|
Node get node;
|
||||||
|
|
||||||
Color get backgroundColor {
|
Color get backgroundColor {
|
||||||
|
|
|
||||||
|
|
@ -5,14 +5,25 @@ import 'package:flutter/material.dart';
|
||||||
class BlockComponentConfiguration {
|
class BlockComponentConfiguration {
|
||||||
const BlockComponentConfiguration({
|
const BlockComponentConfiguration({
|
||||||
this.padding = _padding,
|
this.padding = _padding,
|
||||||
|
this.indentPadding = _indentPadding,
|
||||||
this.placeholderText = _placeholderText,
|
this.placeholderText = _placeholderText,
|
||||||
this.textStyle = _textStyle,
|
this.textStyle = _textStyle,
|
||||||
this.placeholderTextStyle = _placeholderTextStyle,
|
this.placeholderTextStyle = _placeholderTextStyle,
|
||||||
});
|
});
|
||||||
|
|
||||||
/// The padding of a block component.
|
/// The padding of a block component.
|
||||||
|
///
|
||||||
|
/// It works only for the block component itself, not for the children.
|
||||||
final EdgeInsets Function(Node node) padding;
|
final EdgeInsets Function(Node node) padding;
|
||||||
|
|
||||||
|
/// The padding of a block component.
|
||||||
|
///
|
||||||
|
/// It works only for the block that needs to be indented.
|
||||||
|
final EdgeInsets Function(
|
||||||
|
Node node,
|
||||||
|
TextDirection textDirection,
|
||||||
|
) indentPadding;
|
||||||
|
|
||||||
/// The text style of a block component.
|
/// The text style of a block component.
|
||||||
final TextStyle Function(Node node) textStyle;
|
final TextStyle Function(Node node) textStyle;
|
||||||
|
|
||||||
|
|
@ -57,6 +68,15 @@ EdgeInsets _padding(Node node) {
|
||||||
return const EdgeInsets.symmetric(vertical: 4.0);
|
return const EdgeInsets.symmetric(vertical: 4.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
EdgeInsets _indentPadding(Node node, TextDirection textDirection) {
|
||||||
|
switch (textDirection) {
|
||||||
|
case TextDirection.ltr:
|
||||||
|
return const EdgeInsets.only(left: 30.0);
|
||||||
|
case TextDirection.rtl:
|
||||||
|
return const EdgeInsets.only(right: 30.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
TextStyle _textStyle(Node node) {
|
TextStyle _textStyle(Node node) {
|
||||||
return const TextStyle();
|
return const TextStyle();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class BlockComponentPadding extends StatelessWidget {
|
||||||
|
const BlockComponentPadding({
|
||||||
|
super.key,
|
||||||
|
required this.node,
|
||||||
|
required this.padding,
|
||||||
|
this.indentPadding = EdgeInsets.zero,
|
||||||
|
required this.child,
|
||||||
|
});
|
||||||
|
|
||||||
|
final Node node;
|
||||||
|
final Widget child;
|
||||||
|
final EdgeInsets padding;
|
||||||
|
final EdgeInsets indentPadding;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final level = node.level.toDouble();
|
||||||
|
return Padding(
|
||||||
|
padding: padding,
|
||||||
|
child: Padding(
|
||||||
|
padding: indentPadding * level,
|
||||||
|
child: child,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -39,14 +39,16 @@ CommandShortcutEventHandler _convertToParagraphCommandHandler = (editorState) {
|
||||||
if (index >= 0) {
|
if (index >= 0) {
|
||||||
return KeyEventResult.ignored;
|
return KeyEventResult.ignored;
|
||||||
}
|
}
|
||||||
|
final textDirection = node.attributes[blockComponentTextDirection];
|
||||||
final transaction = editorState.transaction;
|
final transaction = editorState.transaction;
|
||||||
transaction
|
transaction
|
||||||
..insertNode(
|
..insertNode(
|
||||||
node.path,
|
node.path,
|
||||||
paragraphNode(
|
paragraphNode(
|
||||||
attributes: {
|
attributes: {
|
||||||
'delta': delta.toJson(),
|
ParagraphBlockKeys.delta: delta.toJson(),
|
||||||
},
|
},
|
||||||
|
textDirection: textDirection,
|
||||||
children: node.children,
|
children: node.children,
|
||||||
),
|
),
|
||||||
deepCopy: true,
|
deepCopy: true,
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,69 @@
|
||||||
|
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
/// This regular expression matches either a single character from specific RTL (right-to-left) script categories or a digit or character from various other script categories.
|
||||||
|
/// It can be useful for identifying or manipulating text with mixed directional properties.
|
||||||
|
final _regex = RegExp(
|
||||||
|
r"(?:([\p{sc=Arabic}\p{sc=Hebrew}\p{sc=Syriac}\p{sc=Thaana}])|([0-9\p{sc=Armenian}\p{sc=Bengali}\p{sc=Bopomofo}\p{sc=Braille}\p{sc=Buhid}\p{sc=Canadian_Aboriginal}\p{sc=Cherokee}\p{sc=Cyrillic}\p{sc=Devanagari}\p{sc=Ethiopic}\p{sc=Georgian}\p{sc=Greek}\p{sc=Gujarati}\p{sc=Gurmukhi}\p{sc=Han}\p{sc=Hangul}\p{sc=Hanunoo}\p{sc=Hiragana}\p{sc=Inherited}\p{sc=Kannada}\p{sc=Katakana}\p{sc=Khmer}\p{sc=Lao}\p{sc=Latin}\p{sc=Limbu}\p{sc=Malayalam}\p{sc=Mongolian}\p{sc=Myanmar}\p{sc=Ogham}\p{sc=Oriya}\p{sc=Runic}\p{sc=Sinhala}\p{sc=Tagalog}\p{sc=Tagbanwa}\p{sc=Tamil}\p{sc=Telugu}\p{sc=Thai}\p{sc=Tibetan}\p{sc=Yi}]))",
|
||||||
|
unicode: true,
|
||||||
|
);
|
||||||
|
|
||||||
|
mixin BlockComponentTextDirectionMixin {
|
||||||
|
Node get node;
|
||||||
|
|
||||||
|
/// Calculate the text direction of a block component.
|
||||||
|
TextDirection calculateTextDirection({
|
||||||
|
TextDirection? defaultTextDirection,
|
||||||
|
}) {
|
||||||
|
defaultTextDirection = defaultTextDirection ?? TextDirection.ltr;
|
||||||
|
// if the block component has a text direction attribute, use it
|
||||||
|
final value = node.attributes[blockComponentTextDirection] as String?;
|
||||||
|
if (value != null && value != blockComponentTextDirectionAuto) {
|
||||||
|
return value.toTextDirection(fallback: defaultTextDirection);
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the block component doesn't has a text direction attribute, but has a
|
||||||
|
// parent, use the text direction of the parent
|
||||||
|
final previousNodeContainsTextDirection = node.previousNodeWhere(
|
||||||
|
(element) => element.attributes.containsKey(blockComponentTextDirection),
|
||||||
|
);
|
||||||
|
if (value == blockComponentTextDirectionAuto &&
|
||||||
|
previousNodeContainsTextDirection != null) {
|
||||||
|
final String previousValue = previousNodeContainsTextDirection
|
||||||
|
.attributes[blockComponentTextDirection];
|
||||||
|
defaultTextDirection =
|
||||||
|
previousValue.toTextDirection(fallback: defaultTextDirection);
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the value is null or the text is null or empty,
|
||||||
|
// use the default text direction
|
||||||
|
final text = node.delta?.toPlainText();
|
||||||
|
if (value == null || text == null || text.isEmpty) {
|
||||||
|
return defaultTextDirection;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the value is auto and the text isn't null or empty,
|
||||||
|
// calculate the text direction by the text
|
||||||
|
final matches = _regex.firstMatch(text);
|
||||||
|
if (matches != null) {
|
||||||
|
if (matches.group(1) != null) {
|
||||||
|
return TextDirection.rtl;
|
||||||
|
} else if (matches.group(2) != null) {
|
||||||
|
return TextDirection.ltr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return defaultTextDirection;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension on String {
|
||||||
|
TextDirection toTextDirection({required TextDirection fallback}) {
|
||||||
|
if (this == blockComponentTextDirectionLTR) {
|
||||||
|
return TextDirection.ltr;
|
||||||
|
} else if (this == blockComponentTextDirectionRTL) {
|
||||||
|
return TextDirection.rtl;
|
||||||
|
}
|
||||||
|
return fallback;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -3,12 +3,10 @@ import 'package:flutter/material.dart';
|
||||||
class NestedListWidget extends StatelessWidget {
|
class NestedListWidget extends StatelessWidget {
|
||||||
const NestedListWidget({
|
const NestedListWidget({
|
||||||
super.key,
|
super.key,
|
||||||
this.padding = const EdgeInsets.only(left: 30.0),
|
|
||||||
required this.child,
|
required this.child,
|
||||||
required this.children,
|
required this.children,
|
||||||
});
|
});
|
||||||
|
|
||||||
final EdgeInsets padding;
|
|
||||||
final Widget child;
|
final Widget child;
|
||||||
final List<Widget> children;
|
final List<Widget> children;
|
||||||
|
|
||||||
|
|
@ -20,14 +18,11 @@ class NestedListWidget extends StatelessWidget {
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
child,
|
child,
|
||||||
Padding(
|
Column(
|
||||||
padding: padding,
|
mainAxisSize: MainAxisSize.max,
|
||||||
child: Column(
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
mainAxisSize: MainAxisSize.max,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
children: children,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: children,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
|
||||||
24
lib/src/editor/block_component/base_component_keys.dart
Normal file
24
lib/src/editor/block_component/base_component_keys.dart
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
/// the delta of the block component
|
||||||
|
///
|
||||||
|
/// its value is a string of json format, e.g. '{"insert":"Hello World"}'
|
||||||
|
/// for more information, please refer to https://quilljs.com/docs/delta/
|
||||||
|
const blockComponentDelta = 'delta';
|
||||||
|
|
||||||
|
/// the background of the block component
|
||||||
|
///
|
||||||
|
/// its value is a string of hex code, e.g. '#00000000'
|
||||||
|
const blockComponentBackgroundColor = 'bgColor';
|
||||||
|
|
||||||
|
/// the text direction of the block component
|
||||||
|
///
|
||||||
|
/// its value must be one of the following:
|
||||||
|
/// - [blockComponentTextDirectionLTR] or 'ltr': left to right
|
||||||
|
/// - [blockComponentTextDirectionRTL] or 'rtl': right to left
|
||||||
|
/// - [blockComponentTextDirectionAuto] or auto: depends on the text
|
||||||
|
///
|
||||||
|
/// only works for the block with text,
|
||||||
|
/// e.g. paragraph, heading, quote, to-do list, bulleted list, numbered list
|
||||||
|
const blockComponentTextDirection = 'textDirection';
|
||||||
|
const blockComponentTextDirectionAuto = 'auto';
|
||||||
|
const blockComponentTextDirectionLTR = 'ltr';
|
||||||
|
const blockComponentTextDirectionRTL = 'rtl';
|
||||||
|
|
@ -37,6 +37,7 @@ export 'base_component/convert_to_paragraph_command.dart';
|
||||||
export 'base_component/insert_newline_in_type_command.dart';
|
export 'base_component/insert_newline_in_type_command.dart';
|
||||||
export 'base_component/indent_command.dart';
|
export 'base_component/indent_command.dart';
|
||||||
export 'base_component/outdent_command.dart';
|
export 'base_component/outdent_command.dart';
|
||||||
|
export 'base_component/block_component_padding.dart';
|
||||||
export 'base_component/widget/full_screen_overlay_entry.dart';
|
export 'base_component/widget/full_screen_overlay_entry.dart';
|
||||||
export 'base_component/widget/ignore_parent_pointer.dart';
|
export 'base_component/widget/ignore_parent_pointer.dart';
|
||||||
|
|
||||||
|
|
@ -48,8 +49,10 @@ export 'rich_text/appflowy_rich_text_keys.dart';
|
||||||
export 'base_component/block_component_configuration.dart';
|
export 'base_component/block_component_configuration.dart';
|
||||||
export 'base_component/text_style_configuration.dart';
|
export 'base_component/text_style_configuration.dart';
|
||||||
export 'base_component/background_color_mixin.dart';
|
export 'base_component/background_color_mixin.dart';
|
||||||
|
export 'base_component/text_direction_mixin.dart';
|
||||||
export 'base_component/markdown_format_helper.dart';
|
export 'base_component/markdown_format_helper.dart';
|
||||||
export 'base_component/widget/nested_list_widget.dart';
|
export 'base_component/widget/nested_list_widget.dart';
|
||||||
export 'base_component/block_component_action_wrapper.dart';
|
export 'base_component/block_component_action_wrapper.dart';
|
||||||
|
|
||||||
export 'standard_block_components.dart';
|
export 'standard_block_components.dart';
|
||||||
|
export 'base_component_keys.dart';
|
||||||
|
|
|
||||||
|
|
@ -6,18 +6,27 @@ class BulletedListBlockKeys {
|
||||||
BulletedListBlockKeys._();
|
BulletedListBlockKeys._();
|
||||||
|
|
||||||
static const String type = 'bulleted_list';
|
static const String type = 'bulleted_list';
|
||||||
|
|
||||||
|
static const String delta = blockComponentDelta;
|
||||||
|
|
||||||
|
static const String backgroundColor = blockComponentBackgroundColor;
|
||||||
|
|
||||||
|
static const String textDirection = blockComponentTextDirection;
|
||||||
}
|
}
|
||||||
|
|
||||||
Node bulletedListNode({
|
Node bulletedListNode({
|
||||||
Delta? delta,
|
Delta? delta,
|
||||||
|
String? textDirection,
|
||||||
Attributes? attributes,
|
Attributes? attributes,
|
||||||
Iterable<Node>? children,
|
Iterable<Node>? children,
|
||||||
}) {
|
}) {
|
||||||
attributes ??= {'delta': (delta ?? Delta()).toJson()};
|
attributes ??= {BulletedListBlockKeys.delta: (delta ?? Delta()).toJson()};
|
||||||
return Node(
|
return Node(
|
||||||
type: BulletedListBlockKeys.type,
|
type: BulletedListBlockKeys.type,
|
||||||
attributes: {
|
attributes: {
|
||||||
...attributes,
|
...attributes,
|
||||||
|
if (textDirection != null)
|
||||||
|
BulletedListBlockKeys.textDirection: textDirection,
|
||||||
},
|
},
|
||||||
children: children ?? [],
|
children: children ?? [],
|
||||||
);
|
);
|
||||||
|
|
@ -77,8 +86,9 @@ class _BulletedListBlockComponentWidgetState
|
||||||
SelectableMixin,
|
SelectableMixin,
|
||||||
DefaultSelectableMixin,
|
DefaultSelectableMixin,
|
||||||
BlockComponentConfigurable,
|
BlockComponentConfigurable,
|
||||||
BackgroundColorMixin,
|
BlockComponentBackgroundColorMixin,
|
||||||
NestedBlockComponentStatefulWidgetMixin {
|
NestedBlockComponentStatefulWidgetMixin,
|
||||||
|
BlockComponentTextDirectionMixin {
|
||||||
@override
|
@override
|
||||||
final forwardKey = GlobalKey(debugLabel: 'flowy_rich_text');
|
final forwardKey = GlobalKey(debugLabel: 'flowy_rich_text');
|
||||||
|
|
||||||
|
|
@ -91,14 +101,23 @@ class _BulletedListBlockComponentWidgetState
|
||||||
@override
|
@override
|
||||||
Node get node => widget.node;
|
Node get node => widget.node;
|
||||||
|
|
||||||
|
String? lastStartText;
|
||||||
|
TextDirection? lastDirection;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget buildComponent(BuildContext context) {
|
Widget buildComponent(BuildContext context) {
|
||||||
|
final textDirection = calculateTextDirection(
|
||||||
|
defaultTextDirection: Directionality.maybeOf(context),
|
||||||
|
);
|
||||||
|
|
||||||
Widget child = Container(
|
Widget child = Container(
|
||||||
color: backgroundColor,
|
color: backgroundColor,
|
||||||
|
width: double.infinity,
|
||||||
child: Row(
|
child: Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
textDirection: textDirection,
|
||||||
children: [
|
children: [
|
||||||
widget.iconBuilder != null
|
widget.iconBuilder != null
|
||||||
? widget.iconBuilder!(context, node)
|
? widget.iconBuilder!(context, node)
|
||||||
|
|
@ -119,6 +138,7 @@ class _BulletedListBlockComponentWidgetState
|
||||||
textSpan.updateTextStyle(
|
textSpan.updateTextStyle(
|
||||||
placeholderTextStyle,
|
placeholderTextStyle,
|
||||||
),
|
),
|
||||||
|
textDirection: textDirection,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
@ -133,7 +153,13 @@ class _BulletedListBlockComponentWidgetState
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return child;
|
final indentPadding = configuration.indentPadding(node, textDirection);
|
||||||
|
return BlockComponentPadding(
|
||||||
|
node: node,
|
||||||
|
padding: padding,
|
||||||
|
indentPadding: indentPadding,
|
||||||
|
child: child,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -68,7 +68,14 @@ class DividerBlockComponentWidget extends BlockComponentStatefulWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _DividerBlockComponentWidgetState
|
class _DividerBlockComponentWidgetState
|
||||||
extends State<DividerBlockComponentWidget> with SelectableMixin {
|
extends State<DividerBlockComponentWidget>
|
||||||
|
with SelectableMixin, BlockComponentConfigurable {
|
||||||
|
@override
|
||||||
|
BlockComponentConfiguration get configuration => widget.configuration;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Node get node => widget.node;
|
||||||
|
|
||||||
final dividerKey = GlobalKey();
|
final dividerKey = GlobalKey();
|
||||||
RenderBox get _renderBox => context.findRenderObject() as RenderBox;
|
RenderBox get _renderBox => context.findRenderObject() as RenderBox;
|
||||||
|
|
||||||
|
|
@ -92,7 +99,11 @@ class _DividerBlockComponentWidgetState
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return child;
|
return BlockComponentPadding(
|
||||||
|
node: node,
|
||||||
|
padding: padding,
|
||||||
|
child: child,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
@ -138,4 +149,9 @@ class _DividerBlockComponentWidgetState
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Offset localToGlobal(Offset offset) => _renderBox.localToGlobal(offset);
|
Offset localToGlobal(Offset offset) => _renderBox.localToGlobal(offset);
|
||||||
|
|
||||||
|
@override
|
||||||
|
TextDirection textDirection() {
|
||||||
|
return TextDirection.ltr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,22 +13,27 @@ class HeadingBlockKeys {
|
||||||
/// The value is a int.
|
/// The value is a int.
|
||||||
static const String level = 'level';
|
static const String level = 'level';
|
||||||
|
|
||||||
static const String delta = 'delta';
|
static const String delta = blockComponentDelta;
|
||||||
|
|
||||||
static const backgroundColor = blockComponentBackgroundColor;
|
static const String backgroundColor = blockComponentBackgroundColor;
|
||||||
|
|
||||||
|
static const String textDirection = blockComponentTextDirection;
|
||||||
}
|
}
|
||||||
|
|
||||||
Node headingNode({
|
Node headingNode({
|
||||||
required int level,
|
required int level,
|
||||||
Delta? delta,
|
Delta? delta,
|
||||||
|
String? textDirection,
|
||||||
Attributes? attributes,
|
Attributes? attributes,
|
||||||
}) {
|
}) {
|
||||||
|
assert(level >= 1 && level <= 6);
|
||||||
attributes ??= {'delta': (delta ?? Delta()).toJson()};
|
attributes ??= {'delta': (delta ?? Delta()).toJson()};
|
||||||
return Node(
|
return Node(
|
||||||
type: HeadingBlockKeys.type,
|
type: HeadingBlockKeys.type,
|
||||||
attributes: {
|
attributes: {
|
||||||
HeadingBlockKeys.level: level,
|
|
||||||
...attributes,
|
...attributes,
|
||||||
|
HeadingBlockKeys.level: level,
|
||||||
|
if (textDirection != null) HeadingBlockKeys.textDirection: textDirection,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -92,7 +97,8 @@ class _HeadingBlockComponentWidgetState
|
||||||
SelectableMixin,
|
SelectableMixin,
|
||||||
DefaultSelectableMixin,
|
DefaultSelectableMixin,
|
||||||
BlockComponentConfigurable,
|
BlockComponentConfigurable,
|
||||||
BackgroundColorMixin {
|
BlockComponentBackgroundColorMixin,
|
||||||
|
BlockComponentTextDirectionMixin {
|
||||||
@override
|
@override
|
||||||
final forwardKey = GlobalKey(debugLabel: 'flowy_rich_text');
|
final forwardKey = GlobalKey(debugLabel: 'flowy_rich_text');
|
||||||
|
|
||||||
|
|
@ -111,8 +117,13 @@ class _HeadingBlockComponentWidgetState
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final textDirection = calculateTextDirection(
|
||||||
|
defaultTextDirection: Directionality.maybeOf(context),
|
||||||
|
);
|
||||||
|
|
||||||
Widget child = Container(
|
Widget child = Container(
|
||||||
color: backgroundColor,
|
color: backgroundColor,
|
||||||
|
width: double.infinity,
|
||||||
child: AppFlowyRichText(
|
child: AppFlowyRichText(
|
||||||
key: forwardKey,
|
key: forwardKey,
|
||||||
node: widget.node,
|
node: widget.node,
|
||||||
|
|
@ -130,6 +141,7 @@ class _HeadingBlockComponentWidgetState
|
||||||
.updateTextStyle(
|
.updateTextStyle(
|
||||||
widget.textStyleBuilder?.call(level) ?? defaultTextStyle(level),
|
widget.textStyleBuilder?.call(level) ?? defaultTextStyle(level),
|
||||||
),
|
),
|
||||||
|
textDirection: textDirection,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -141,7 +153,13 @@ class _HeadingBlockComponentWidgetState
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return child;
|
final indentPadding = configuration.indentPadding(node, textDirection);
|
||||||
|
return BlockComponentPadding(
|
||||||
|
node: node,
|
||||||
|
padding: padding,
|
||||||
|
indentPadding: indentPadding,
|
||||||
|
child: child,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
TextStyle? defaultTextStyle(int level) {
|
TextStyle? defaultTextStyle(int level) {
|
||||||
|
|
|
||||||
|
|
@ -113,7 +113,13 @@ class ImageBlockComponentWidget extends BlockComponentStatefulWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class ImageBlockComponentWidgetState extends State<ImageBlockComponentWidget>
|
class ImageBlockComponentWidgetState extends State<ImageBlockComponentWidget>
|
||||||
with SelectableMixin {
|
with SelectableMixin, BlockComponentConfigurable {
|
||||||
|
@override
|
||||||
|
BlockComponentConfiguration get configuration => widget.configuration;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Node get node => widget.node;
|
||||||
|
|
||||||
final imageKey = GlobalKey();
|
final imageKey = GlobalKey();
|
||||||
RenderBox get _renderBox => context.findRenderObject() as RenderBox;
|
RenderBox get _renderBox => context.findRenderObject() as RenderBox;
|
||||||
|
|
||||||
|
|
@ -185,7 +191,11 @@ class ImageBlockComponentWidgetState extends State<ImageBlockComponentWidget>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return child;
|
return BlockComponentPadding(
|
||||||
|
node: node,
|
||||||
|
padding: padding,
|
||||||
|
child: child,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
|
||||||
|
|
@ -9,12 +9,19 @@ class NumberedListBlockKeys {
|
||||||
static const String type = 'numbered_list';
|
static const String type = 'numbered_list';
|
||||||
|
|
||||||
static const String number = 'number';
|
static const String number = 'number';
|
||||||
|
|
||||||
|
static const String delta = blockComponentDelta;
|
||||||
|
|
||||||
|
static const String backgroundColor = blockComponentBackgroundColor;
|
||||||
|
|
||||||
|
static const String textDirection = blockComponentTextDirection;
|
||||||
}
|
}
|
||||||
|
|
||||||
Node numberedListNode({
|
Node numberedListNode({
|
||||||
Delta? delta,
|
Delta? delta,
|
||||||
Attributes? attributes,
|
Attributes? attributes,
|
||||||
int? number,
|
int? number,
|
||||||
|
String? textDirection,
|
||||||
Iterable<Node>? children,
|
Iterable<Node>? children,
|
||||||
}) {
|
}) {
|
||||||
attributes ??= {
|
attributes ??= {
|
||||||
|
|
@ -25,6 +32,8 @@ Node numberedListNode({
|
||||||
type: NumberedListBlockKeys.type,
|
type: NumberedListBlockKeys.type,
|
||||||
attributes: {
|
attributes: {
|
||||||
...attributes,
|
...attributes,
|
||||||
|
if (textDirection != null)
|
||||||
|
NumberedListBlockKeys.textDirection: textDirection,
|
||||||
},
|
},
|
||||||
children: children ?? [],
|
children: children ?? [],
|
||||||
);
|
);
|
||||||
|
|
@ -84,8 +93,9 @@ class _NumberedListBlockComponentWidgetState
|
||||||
SelectableMixin,
|
SelectableMixin,
|
||||||
DefaultSelectableMixin,
|
DefaultSelectableMixin,
|
||||||
BlockComponentConfigurable,
|
BlockComponentConfigurable,
|
||||||
BackgroundColorMixin,
|
BlockComponentBackgroundColorMixin,
|
||||||
NestedBlockComponentStatefulWidgetMixin {
|
NestedBlockComponentStatefulWidgetMixin,
|
||||||
|
BlockComponentTextDirectionMixin {
|
||||||
@override
|
@override
|
||||||
final forwardKey = GlobalKey(debugLabel: 'flowy_rich_text');
|
final forwardKey = GlobalKey(debugLabel: 'flowy_rich_text');
|
||||||
|
|
||||||
|
|
@ -100,18 +110,25 @@ class _NumberedListBlockComponentWidgetState
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget buildComponent(BuildContext context) {
|
Widget buildComponent(BuildContext context) {
|
||||||
|
final textDirection = calculateTextDirection(
|
||||||
|
defaultTextDirection: Directionality.maybeOf(context),
|
||||||
|
);
|
||||||
|
|
||||||
Widget child = Container(
|
Widget child = Container(
|
||||||
color: backgroundColor,
|
color: backgroundColor,
|
||||||
|
width: double.infinity,
|
||||||
child: Row(
|
child: Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
textDirection: textDirection,
|
||||||
children: [
|
children: [
|
||||||
widget.iconBuilder != null
|
widget.iconBuilder != null
|
||||||
? widget.iconBuilder!(context, node)
|
? widget.iconBuilder!(context, node)
|
||||||
: _NumberedListIcon(
|
: _NumberedListIcon(
|
||||||
node: node,
|
node: node,
|
||||||
textStyle: textStyle,
|
textStyle: textStyle,
|
||||||
|
direction: textDirection,
|
||||||
),
|
),
|
||||||
Flexible(
|
Flexible(
|
||||||
child: AppFlowyRichText(
|
child: AppFlowyRichText(
|
||||||
|
|
@ -126,6 +143,7 @@ class _NumberedListBlockComponentWidgetState
|
||||||
textSpan.updateTextStyle(
|
textSpan.updateTextStyle(
|
||||||
placeholderTextStyle,
|
placeholderTextStyle,
|
||||||
),
|
),
|
||||||
|
textDirection: textDirection,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
@ -140,7 +158,13 @@ class _NumberedListBlockComponentWidgetState
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return child;
|
final indentPadding = configuration.indentPadding(node, textDirection);
|
||||||
|
return BlockComponentPadding(
|
||||||
|
node: node,
|
||||||
|
padding: padding,
|
||||||
|
indentPadding: indentPadding,
|
||||||
|
child: child,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -148,10 +172,12 @@ class _NumberedListIcon extends StatelessWidget {
|
||||||
const _NumberedListIcon({
|
const _NumberedListIcon({
|
||||||
required this.node,
|
required this.node,
|
||||||
required this.textStyle,
|
required this.textStyle,
|
||||||
|
required this.direction,
|
||||||
});
|
});
|
||||||
|
|
||||||
final Node node;
|
final Node node;
|
||||||
final TextStyle textStyle;
|
final TextStyle textStyle;
|
||||||
|
final TextDirection direction;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
|
@ -166,6 +192,7 @@ class _NumberedListIcon extends StatelessWidget {
|
||||||
applyHeightToLastDescent: false,
|
applyHeightToLastDescent: false,
|
||||||
),
|
),
|
||||||
TextSpan(text: '$level.', style: text.combine(textStyle)),
|
TextSpan(text: '$level.', style: text.combine(textStyle)),
|
||||||
|
textDirection: direction,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,10 +7,17 @@ class QuoteBlockKeys {
|
||||||
const QuoteBlockKeys._();
|
const QuoteBlockKeys._();
|
||||||
|
|
||||||
static const String type = 'quote';
|
static const String type = 'quote';
|
||||||
|
|
||||||
|
static const String delta = blockComponentDelta;
|
||||||
|
|
||||||
|
static const String backgroundColor = blockComponentBackgroundColor;
|
||||||
|
|
||||||
|
static const String textDirection = blockComponentTextDirection;
|
||||||
}
|
}
|
||||||
|
|
||||||
Node quoteNode({
|
Node quoteNode({
|
||||||
Delta? delta,
|
Delta? delta,
|
||||||
|
String? textDirection,
|
||||||
Attributes? attributes,
|
Attributes? attributes,
|
||||||
Iterable<Node>? children,
|
Iterable<Node>? children,
|
||||||
}) {
|
}) {
|
||||||
|
|
@ -19,6 +26,7 @@ Node quoteNode({
|
||||||
type: QuoteBlockKeys.type,
|
type: QuoteBlockKeys.type,
|
||||||
attributes: {
|
attributes: {
|
||||||
...attributes,
|
...attributes,
|
||||||
|
if (textDirection != null) QuoteBlockKeys.textDirection: textDirection,
|
||||||
},
|
},
|
||||||
children: children ?? [],
|
children: children ?? [],
|
||||||
);
|
);
|
||||||
|
|
@ -77,7 +85,8 @@ class _QuoteBlockComponentWidgetState extends State<QuoteBlockComponentWidget>
|
||||||
SelectableMixin,
|
SelectableMixin,
|
||||||
DefaultSelectableMixin,
|
DefaultSelectableMixin,
|
||||||
BlockComponentConfigurable,
|
BlockComponentConfigurable,
|
||||||
BackgroundColorMixin {
|
BlockComponentBackgroundColorMixin,
|
||||||
|
BlockComponentTextDirectionMixin {
|
||||||
@override
|
@override
|
||||||
final forwardKey = GlobalKey(debugLabel: 'flowy_rich_text');
|
final forwardKey = GlobalKey(debugLabel: 'flowy_rich_text');
|
||||||
|
|
||||||
|
|
@ -94,13 +103,19 @@ class _QuoteBlockComponentWidgetState extends State<QuoteBlockComponentWidget>
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final textDirection = calculateTextDirection(
|
||||||
|
defaultTextDirection: Directionality.maybeOf(context),
|
||||||
|
);
|
||||||
|
|
||||||
Widget child = Container(
|
Widget child = Container(
|
||||||
color: backgroundColor,
|
color: backgroundColor,
|
||||||
|
width: double.infinity,
|
||||||
child: IntrinsicHeight(
|
child: IntrinsicHeight(
|
||||||
child: Row(
|
child: Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
textDirection: textDirection,
|
||||||
children: [
|
children: [
|
||||||
widget.iconBuilder != null
|
widget.iconBuilder != null
|
||||||
? widget.iconBuilder!(context, node)
|
? widget.iconBuilder!(context, node)
|
||||||
|
|
@ -118,6 +133,7 @@ class _QuoteBlockComponentWidgetState extends State<QuoteBlockComponentWidget>
|
||||||
textSpan.updateTextStyle(
|
textSpan.updateTextStyle(
|
||||||
placeholderTextStyle,
|
placeholderTextStyle,
|
||||||
),
|
),
|
||||||
|
textDirection: textDirection,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
@ -133,7 +149,13 @@ class _QuoteBlockComponentWidgetState extends State<QuoteBlockComponentWidget>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return child;
|
final indentPadding = configuration.indentPadding(node, textDirection);
|
||||||
|
return BlockComponentPadding(
|
||||||
|
node: node,
|
||||||
|
padding: padding,
|
||||||
|
indentPadding: indentPadding,
|
||||||
|
child: child,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@ class AppFlowyRichText extends StatefulWidget {
|
||||||
this.textSpanDecorator,
|
this.textSpanDecorator,
|
||||||
this.placeholderText = ' ',
|
this.placeholderText = ' ',
|
||||||
this.placeholderTextSpanDecorator,
|
this.placeholderTextSpanDecorator,
|
||||||
|
this.textDirection = TextDirection.ltr,
|
||||||
this.textSpanDecoratorForCustomAttributes,
|
this.textSpanDecoratorForCustomAttributes,
|
||||||
required this.node,
|
required this.node,
|
||||||
required this.editorState,
|
required this.editorState,
|
||||||
|
|
@ -69,6 +70,7 @@ class AppFlowyRichText extends StatefulWidget {
|
||||||
/// You can use this to customize the text span for custom attributes
|
/// You can use this to customize the text span for custom attributes
|
||||||
/// or override the existing one.
|
/// or override the existing one.
|
||||||
final TextSpanDecoratorForAttribute? textSpanDecoratorForCustomAttributes;
|
final TextSpanDecoratorForAttribute? textSpanDecoratorForCustomAttributes;
|
||||||
|
final TextDirection textDirection;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<AppFlowyRichText> createState() => _AppFlowyRichTextState();
|
State<AppFlowyRichText> createState() => _AppFlowyRichTextState();
|
||||||
|
|
@ -128,6 +130,14 @@ class _AppFlowyRichTextState extends State<AppFlowyRichText>
|
||||||
Rect.zero,
|
Rect.zero,
|
||||||
) ??
|
) ??
|
||||||
Offset.zero;
|
Offset.zero;
|
||||||
|
if (textDirection() == TextDirection.rtl) {
|
||||||
|
if (widget.placeholderText.trim().isNotEmpty) {
|
||||||
|
cursorOffset = cursorOffset.translate(
|
||||||
|
_placeholderRenderParagraph?.size.width ?? 0,
|
||||||
|
0,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (widget.cursorHeight != null && cursorHeight != null) {
|
if (widget.cursorHeight != null && cursorHeight != null) {
|
||||||
cursorOffset = Offset(
|
cursorOffset = Offset(
|
||||||
|
|
@ -208,6 +218,11 @@ class _AppFlowyRichTextState extends State<AppFlowyRichText>
|
||||||
return _renderParagraph.localToGlobal(offset);
|
return _renderParagraph.localToGlobal(offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
TextDirection textDirection() {
|
||||||
|
return widget.textDirection;
|
||||||
|
}
|
||||||
|
|
||||||
Widget _buildPlaceholderText(BuildContext context) {
|
Widget _buildPlaceholderText(BuildContext context) {
|
||||||
final textSpan = getPlaceholderTextSpan();
|
final textSpan = getPlaceholderTextSpan();
|
||||||
return RichText(
|
return RichText(
|
||||||
|
|
@ -219,6 +234,7 @@ class _AppFlowyRichTextState extends State<AppFlowyRichText>
|
||||||
text: widget.placeholderTextSpanDecorator != null
|
text: widget.placeholderTextSpanDecorator != null
|
||||||
? widget.placeholderTextSpanDecorator!(textSpan)
|
? widget.placeholderTextSpanDecorator!(textSpan)
|
||||||
: textSpan,
|
: textSpan,
|
||||||
|
textDirection: textDirection(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -233,6 +249,7 @@ class _AppFlowyRichTextState extends State<AppFlowyRichText>
|
||||||
text: widget.textSpanDecorator != null
|
text: widget.textSpanDecorator != null
|
||||||
? widget.textSpanDecorator!(textSpan)
|
? widget.textSpanDecorator!(textSpan)
|
||||||
: textSpan,
|
: textSpan,
|
||||||
|
textDirection: textDirection(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -361,7 +378,7 @@ class _AppFlowyRichTextState extends State<AppFlowyRichText>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension FlowyRichTextAttributes on Attributes {
|
extension AppFlowyRichTextAttributes on Attributes {
|
||||||
bool get bold => this[AppFlowyRichTextKeys.bold] == true;
|
bool get bold => this[AppFlowyRichTextKeys.bold] == true;
|
||||||
|
|
||||||
bool get italic => this[AppFlowyRichTextKeys.italic] == true;
|
bool get italic => this[AppFlowyRichTextKeys.italic] == true;
|
||||||
|
|
|
||||||
|
|
@ -45,4 +45,6 @@ mixin DefaultSelectableMixin {
|
||||||
Position start() => forward.start();
|
Position start() => forward.start();
|
||||||
|
|
||||||
Position end() => forward.end();
|
Position end() => forward.end();
|
||||||
|
|
||||||
|
TextDirection textDirection() => forward.textDirection();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,14 +6,17 @@ class ParagraphBlockKeys {
|
||||||
|
|
||||||
static const String type = 'paragraph';
|
static const String type = 'paragraph';
|
||||||
|
|
||||||
static const String delta = 'delta';
|
static const String delta = blockComponentDelta;
|
||||||
|
|
||||||
static const String backgroundColor = blockComponentBackgroundColor;
|
static const String backgroundColor = blockComponentBackgroundColor;
|
||||||
|
|
||||||
|
static const String textDirection = blockComponentTextDirection;
|
||||||
}
|
}
|
||||||
|
|
||||||
Node paragraphNode({
|
Node paragraphNode({
|
||||||
String? text,
|
String? text,
|
||||||
Delta? delta,
|
Delta? delta,
|
||||||
|
String? textDirection,
|
||||||
Attributes? attributes,
|
Attributes? attributes,
|
||||||
Iterable<Node> children = const [],
|
Iterable<Node> children = const [],
|
||||||
}) {
|
}) {
|
||||||
|
|
@ -24,6 +27,8 @@ Node paragraphNode({
|
||||||
type: ParagraphBlockKeys.type,
|
type: ParagraphBlockKeys.type,
|
||||||
attributes: {
|
attributes: {
|
||||||
...attributes,
|
...attributes,
|
||||||
|
if (textDirection != null)
|
||||||
|
ParagraphBlockKeys.textDirection: textDirection,
|
||||||
},
|
},
|
||||||
children: children,
|
children: children,
|
||||||
);
|
);
|
||||||
|
|
@ -77,8 +82,9 @@ class _TextBlockComponentWidgetState extends State<TextBlockComponentWidget>
|
||||||
SelectableMixin,
|
SelectableMixin,
|
||||||
DefaultSelectableMixin,
|
DefaultSelectableMixin,
|
||||||
BlockComponentConfigurable,
|
BlockComponentConfigurable,
|
||||||
BackgroundColorMixin,
|
BlockComponentBackgroundColorMixin,
|
||||||
NestedBlockComponentStatefulWidgetMixin {
|
NestedBlockComponentStatefulWidgetMixin,
|
||||||
|
BlockComponentTextDirectionMixin {
|
||||||
@override
|
@override
|
||||||
final forwardKey = GlobalKey(debugLabel: 'flowy_rich_text');
|
final forwardKey = GlobalKey(debugLabel: 'flowy_rich_text');
|
||||||
|
|
||||||
|
|
@ -91,21 +97,39 @@ class _TextBlockComponentWidgetState extends State<TextBlockComponentWidget>
|
||||||
@override
|
@override
|
||||||
Node get node => widget.node;
|
Node get node => widget.node;
|
||||||
|
|
||||||
|
String? lastStartText;
|
||||||
|
TextDirection? lastDirection;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget buildComponent(BuildContext context) {
|
Widget buildComponent(BuildContext context) {
|
||||||
|
final textDirection = calculateTextDirection(
|
||||||
|
defaultTextDirection: Directionality.maybeOf(context),
|
||||||
|
);
|
||||||
|
|
||||||
Widget child = Container(
|
Widget child = Container(
|
||||||
color: backgroundColor,
|
color: backgroundColor,
|
||||||
child: AppFlowyRichText(
|
width: double.infinity,
|
||||||
key: forwardKey,
|
child: Column(
|
||||||
node: widget.node,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
editorState: editorState,
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
placeholderText: placeholderText,
|
mainAxisSize: MainAxisSize.min,
|
||||||
textSpanDecorator: (textSpan) => textSpan.updateTextStyle(
|
textDirection: textDirection,
|
||||||
textStyle,
|
children: [
|
||||||
),
|
AppFlowyRichText(
|
||||||
placeholderTextSpanDecorator: (textSpan) => textSpan.updateTextStyle(
|
key: forwardKey,
|
||||||
placeholderTextStyle,
|
node: widget.node,
|
||||||
),
|
editorState: editorState,
|
||||||
|
placeholderText: placeholderText,
|
||||||
|
textSpanDecorator: (textSpan) => textSpan.updateTextStyle(
|
||||||
|
textStyle,
|
||||||
|
),
|
||||||
|
placeholderTextSpanDecorator: (textSpan) =>
|
||||||
|
textSpan.updateTextStyle(
|
||||||
|
placeholderTextStyle,
|
||||||
|
),
|
||||||
|
textDirection: textDirection,
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
if (showActions) {
|
if (showActions) {
|
||||||
|
|
@ -115,6 +139,13 @@ class _TextBlockComponentWidgetState extends State<TextBlockComponentWidget>
|
||||||
child: child,
|
child: child,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return child;
|
|
||||||
|
final indentPadding = configuration.indentPadding(node, textDirection);
|
||||||
|
return BlockComponentPadding(
|
||||||
|
node: node,
|
||||||
|
padding: padding,
|
||||||
|
indentPadding: indentPadding,
|
||||||
|
child: child,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue