feat: expose text span for text style builder (#1011)

* feat: expose textspan for text style builder

* fix: content menu will show when collaborating
This commit is contained in:
Lucas 2025-01-06 11:40:17 +08:00 • committed by GitHub
commit 2490f698c3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 47 additions and 31 deletions

View file

@ -85,7 +85,7 @@ class _CustomizeThemeForEditorState extends State<CustomizeThemeForEditor> {
}
return const EdgeInsets.symmetric(vertical: 5);
},
textStyle: (node) {
textStyle: (node, {TextSpan? textSpan}) {
if (HeadingBlockKeys.type == node.type) {
return const TextStyle(color: Colors.yellow);
}

View file

@ -1,6 +1,11 @@
import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:flutter/material.dart';
typedef BlockComponentTextStyleBuilder = TextStyle Function(
Node node, {
TextSpan? textSpan,
});
/// only for the common config of block component
class BlockComponentConfiguration {
const BlockComponentConfiguration({
@ -27,7 +32,7 @@ class BlockComponentConfiguration {
) indentPadding;
/// The text style of a block component.
final TextStyle Function(Node node) textStyle;
final BlockComponentTextStyleBuilder textStyle;
/// The placeholder text of a block component.
final String Function(Node node) placeholderText;
@ -35,7 +40,7 @@ class BlockComponentConfiguration {
/// The placeholder text style of a block component.
///
/// It inherits the style from [textStyle].
final TextStyle Function(Node node) placeholderTextStyle;
final BlockComponentTextStyleBuilder placeholderTextStyle;
/// The padding of a block selection area.
final EdgeInsets Function(Node node) blockSelectionAreaMargin;
@ -48,9 +53,9 @@ class BlockComponentConfiguration {
BlockComponentConfiguration copyWith({
EdgeInsets Function(Node node)? padding,
TextStyle Function(Node node)? textStyle,
BlockComponentTextStyleBuilder? textStyle,
String Function(Node node)? placeholderText,
TextStyle Function(Node node)? placeholderTextStyle,
BlockComponentTextStyleBuilder? placeholderTextStyle,
EdgeInsets Function(Node node)? blockSelectionAreaMargin,
TextAlign Function(Node node)? textAlign,
}) {
@ -72,13 +77,14 @@ mixin BlockComponentConfigurable<T extends StatefulWidget> on State<T> {
EdgeInsets get padding => configuration.padding(node);
TextStyle get textStyle => configuration.textStyle(node);
TextStyle textStyleWithTextSpan({TextSpan? textSpan}) =>
configuration.textStyle(node, textSpan: textSpan);
TextStyle placeholderTextStyleWithTextSpan({TextSpan? textSpan}) =>
configuration.placeholderTextStyle(node, textSpan: textSpan);
String get placeholderText => configuration.placeholderText(node);
TextStyle get placeholderTextStyle =>
configuration.placeholderTextStyle(node);
TextAlign get textAlign => configuration.textAlign(node);
}
@ -95,7 +101,7 @@ EdgeInsets _indentPadding(Node node, TextDirection textDirection) {
}
}
TextStyle _textStyle(Node node) {
TextStyle _textStyle(Node node, {TextSpan? textSpan}) {
return const TextStyle();
}
@ -103,7 +109,7 @@ String _placeholderText(Node node) {
return ' ';
}
TextStyle _placeholderTextStyle(Node node) {
TextStyle _placeholderTextStyle(Node node, {TextSpan? textSpan}) {
return const TextStyle(
color: Colors.grey,
);

View file

@ -129,7 +129,7 @@ class _BulletedListBlockComponentWidgetState
? widget.iconBuilder!(context, node)
: _BulletedListIcon(
node: widget.node,
textStyle: textStyle,
textStyle: textStyleWithTextSpan(),
),
Flexible(
child: AppFlowyRichText(
@ -140,11 +140,11 @@ class _BulletedListBlockComponentWidgetState
textAlign: alignment?.toTextAlign ?? textAlign,
placeholderText: placeholderText,
textSpanDecorator: (textSpan) => textSpan.updateTextStyle(
textStyle,
textStyleWithTextSpan(textSpan: textSpan),
),
placeholderTextSpanDecorator: (textSpan) =>
textSpan.updateTextStyle(
placeholderTextStyle,
placeholderTextStyleWithTextSpan(textSpan: textSpan),
),
textDirection: textDirection,
cursorColor: editorState.editorStyle.cursorColor,

View file

@ -139,7 +139,9 @@ class _HeadingBlockComponentWidgetState
editorState: editorState,
textAlign: alignment?.toTextAlign ?? textAlign,
textSpanDecorator: (textSpan) {
var result = textSpan.updateTextStyle(textStyle);
var result = textSpan.updateTextStyle(
textStyleWithTextSpan(textSpan: textSpan),
);
result = result.updateTextStyle(
widget.textStyleBuilder?.call(level) ??
defaultTextStyle(level),
@ -153,7 +155,7 @@ class _HeadingBlockComponentWidgetState
defaultTextStyle(level),
)
.updateTextStyle(
placeholderTextStyle,
placeholderTextStyleWithTextSpan(textSpan: textSpan),
),
textDirection: textDirection,
cursorColor: editorState.editorStyle.cursorColor,

View file

@ -143,7 +143,7 @@ class _NumberedListBlockComponentWidgetState
)
: _NumberedListIcon(
node: node,
textStyle: textStyle,
textStyle: textStyleWithTextSpan(),
direction: textDirection,
),
Flexible(
@ -155,11 +155,11 @@ class _NumberedListBlockComponentWidgetState
textAlign: alignment?.toTextAlign ?? textAlign,
placeholderText: placeholderText,
textSpanDecorator: (textSpan) => textSpan.updateTextStyle(
textStyle,
textStyleWithTextSpan(textSpan: textSpan),
),
placeholderTextSpanDecorator: (textSpan) =>
textSpan.updateTextStyle(
placeholderTextStyle,
placeholderTextStyleWithTextSpan(textSpan: textSpan),
),
textDirection: textDirection,
cursorColor: editorState.editorStyle.cursorColor,

View file

@ -163,10 +163,15 @@ class _ParagraphBlockComponentWidgetState
editorState: editorState,
textAlign: alignment?.toTextAlign ?? textAlign,
placeholderText: _showPlaceholder ? placeholderText : ' ',
textSpanDecorator: (textSpan) =>
textSpan.updateTextStyle(textStyle),
textSpanDecorator: (textSpan) => textSpan.updateTextStyle(
textStyleWithTextSpan(
textSpan: textSpan,
),
),
placeholderTextSpanDecorator: (textSpan) =>
textSpan.updateTextStyle(placeholderTextStyle),
textSpan.updateTextStyle(
placeholderTextStyleWithTextSpan(textSpan: textSpan),
),
textDirection: textDirection,
cursorColor: editorState.editorStyle.cursorColor,
selectionColor: editorState.editorStyle.selectionColor,

View file

@ -133,11 +133,11 @@ class _QuoteBlockComponentWidgetState extends State<QuoteBlockComponentWidget>
textAlign: alignment?.toTextAlign ?? textAlign,
placeholderText: placeholderText,
textSpanDecorator: (textSpan) => textSpan.updateTextStyle(
textStyle,
textStyleWithTextSpan(textSpan: textSpan),
),
placeholderTextSpanDecorator: (textSpan) =>
textSpan.updateTextStyle(
placeholderTextStyle,
placeholderTextStyleWithTextSpan(textSpan: textSpan),
),
textDirection: textDirection,
cursorColor: editorState.editorStyle.cursorColor,

View file

@ -172,14 +172,15 @@ class _TodoListBlockComponentWidgetState
textAlign: alignment?.toTextAlign ?? textAlign,
placeholderText: placeholderText,
textDirection: textDirection,
textSpanDecorator: (textSpan) =>
textSpan.updateTextStyle(textStyle).updateTextStyle(
widget.textStyleBuilder?.call(checked) ??
defaultTextStyle(),
),
textSpanDecorator: (textSpan) => textSpan
.updateTextStyle(textStyleWithTextSpan())
.updateTextStyle(
widget.textStyleBuilder?.call(checked) ??
defaultTextStyle(),
),
placeholderTextSpanDecorator: (textSpan) =>
textSpan.updateTextStyle(
placeholderTextStyle,
placeholderTextStyleWithTextSpan(textSpan: textSpan),
),
cursorColor: editorState.editorStyle.cursorColor,
selectionColor: editorState.editorStyle.selectionColor,

View file

@ -204,7 +204,7 @@ class SelectionMenu extends SelectionMenuService {
// 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.
const menuHeight = 200.0;
const menuHeight = 300.0;
const menuOffset = Offset(0, 10);
final editorOffset =
editorState.renderBox?.localToGlobal(Offset.zero) ?? Offset.zero;

View file

@ -57,6 +57,7 @@ enum CursorUpdateReason {
enum SelectionUpdateReason {
uiEvent, // like mouse click, keyboard event
transaction, // like insert, delete, format
remote, // like remote selection
selectAll,
searchHighlight, // Highlighting search results
}
@ -359,6 +360,7 @@ class EditorState {
final completer = Completer<void>();
if (isRemote) {
_selectionUpdateReason = SelectionUpdateReason.remote;
selection = _applyTransactionFromRemote(transaction);
} else {
// broadcast to other users here, before applying the transaction