feat: implement heading, quote, bulleted_list in toolbar service

This commit is contained in:
Lucas.Xu 2022-08-02 11:42:16 +08:00
parent b11a127432
commit 9b6afcc5c9
4 changed files with 105 additions and 43 deletions

View File

@ -21,7 +21,7 @@ class CheckboxNodeWidgetBuilder extends NodeWidgetBuilder<TextNode> {
@override @override
NodeValidator<Node> get nodeValidator => ((node) { NodeValidator<Node> get nodeValidator => ((node) {
return node.attributes.containsKey(StyleKey.check); return node.attributes.containsKey(StyleKey.checkbox);
}); });
} }

View File

@ -25,26 +25,48 @@ class StyleKey {
static String font = 'font'; static String font = 'font';
static String href = 'href'; static String href = 'href';
static String quote = 'quote';
static String list = 'list';
static String number = 'number';
static String todo = 'todo';
static String code = 'code';
static String subtype = 'subtype'; static String subtype = 'subtype';
static String check = 'checkbox';
static String heading = 'heading'; 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<String> partialStyleKeys = [
StyleKey.bold,
StyleKey.italic,
StyleKey.underline,
StyleKey.strikethrough,
];
static List<String> globalStyleKeys = [
StyleKey.heading,
StyleKey.bulletedList,
StyleKey.numberList,
StyleKey.quote,
StyleKey.code,
];
} }
double baseFontSize = 16.0; double baseFontSize = 16.0;
// TODO: customize. // TODO: customize.
Map<String, double> headingToFontSize = { Map<String, double> headingToFontSize = {
'h1': baseFontSize + 15, StyleKey.h1: baseFontSize + 15,
'h2': baseFontSize + 12, StyleKey.h2: baseFontSize + 12,
'h3': baseFontSize + 9, StyleKey.h3: baseFontSize + 9,
'h4': baseFontSize + 6, StyleKey.h4: baseFontSize + 6,
'h5': baseFontSize + 3, StyleKey.h5: baseFontSize + 3,
'h6': baseFontSize, StyleKey.h6: baseFontSize,
}; };
extension NodeAttributesExtensions on Attributes { extension NodeAttributesExtensions on Attributes {
@ -73,13 +95,6 @@ extension NodeAttributesExtensions on Attributes {
return null; return null;
} }
String? get list {
if (containsKey(StyleKey.list) && this[StyleKey.list] is String) {
return this[StyleKey.list];
}
return null;
}
int? get number { int? get number {
if (containsKey(StyleKey.number) && this[StyleKey.number] is int) { if (containsKey(StyleKey.number) && this[StyleKey.number] is int) {
return this[StyleKey.number]; return this[StyleKey.number];
@ -87,13 +102,6 @@ extension NodeAttributesExtensions on Attributes {
return null; return null;
} }
bool get todo {
if (containsKey(StyleKey.todo) && this[StyleKey.todo] is bool) {
return this[StyleKey.todo];
}
return false;
}
bool get code { bool get code {
if (containsKey(StyleKey.code) && this[StyleKey.code] == true) { if (containsKey(StyleKey.code) && this[StyleKey.code] == true) {
return this[StyleKey.code]; return this[StyleKey.code];
@ -102,8 +110,8 @@ extension NodeAttributesExtensions on Attributes {
} }
bool get check { bool get check {
if (containsKey(StyleKey.check) && this[StyleKey.check] is bool) { if (containsKey(StyleKey.checkbox) && this[StyleKey.checkbox] is bool) {
return this[StyleKey.check]; return this[StyleKey.checkbox];
} }
return false; return false;
} }

View File

@ -1,8 +1,9 @@
import 'package:flowy_editor/render/rich_text/rich_text_style.dart';
import 'package:flutter/material.dart';
import 'package:flowy_editor/editor_state.dart'; import 'package:flowy_editor/editor_state.dart';
import 'package:flowy_editor/infra/flowy_svg.dart'; import 'package:flowy_editor/infra/flowy_svg.dart';
import 'package:flowy_editor/render/rich_text/rich_text_style.dart';
import 'package:flowy_editor/service/default_text_operations/format_rich_text_style.dart'; import 'package:flowy_editor/service/default_text_operations/format_rich_text_style.dart';
import 'package:flutter/material.dart';
typedef ToolbarEventHandler = void Function(EditorState editorState); typedef ToolbarEventHandler = void Function(EditorState editorState);
@ -13,17 +14,20 @@ ToolbarEventHandlers defaultToolbarEventHandlers = {
'italic': (editorState) => formatItalic(editorState), 'italic': (editorState) => formatItalic(editorState),
'strikethrough': (editorState) => formatStrikethrough(editorState), 'strikethrough': (editorState) => formatStrikethrough(editorState),
'underline': (editorState) => formatUnderline(editorState), 'underline': (editorState) => formatUnderline(editorState),
'quote': ((editorState) {}), 'quote': (editorState) => formatQuote(editorState),
'number_list': ((editorState) {}), 'number_list': (editorState) {},
'bulleted_list': ((editorState) {}), 'bulleted_list': (editorState) => formatBulletedList(editorState),
'H1': (editorState) => formatHeading(editorState, StyleKey.h1),
'H2': (editorState) => formatHeading(editorState, StyleKey.h2),
'H3': (editorState) => formatHeading(editorState, StyleKey.h3),
}; };
List<String> defaultListToolbarEventNames = [ List<String> defaultListToolbarEventNames = [
'H1', 'H1',
'H2', 'H2',
'H3', 'H3',
'B-List', // 'B-List',
'N-List', // 'N-List',
]; ];
class ToolbarWidget extends StatefulWidget { class ToolbarWidget extends StatefulWidget {

View File

@ -1,27 +1,77 @@
import 'package:flowy_editor/document/attributes.dart'; import 'package:flowy_editor/document/attributes.dart';
import 'package:flowy_editor/document/node.dart'; import 'package:flowy_editor/document/node.dart';
import 'package:flowy_editor/editor_state.dart'; import 'package:flowy_editor/editor_state.dart';
import 'package:flowy_editor/extensions/text_node_extensions.dart';
import 'package:flowy_editor/operation/transaction_builder.dart'; import 'package:flowy_editor/operation/transaction_builder.dart';
import 'package:flowy_editor/render/rich_text/rich_text_style.dart'; import 'package:flowy_editor/render/rich_text/rich_text_style.dart';
import 'package:flowy_editor/extensions/text_node_extensions.dart';
void formatHeading(EditorState editorState, String heading) {
formatTextNodes(editorState, {
StyleKey.subtype: StyleKey.heading,
StyleKey.heading: heading,
});
}
void formatQuote(EditorState editorState) {
formatTextNodes(editorState, {
StyleKey.subtype: StyleKey.quote,
});
}
void formatCheckbox(EditorState editorState) {
formatTextNodes(editorState, {
StyleKey.subtype: StyleKey.checkbox,
StyleKey.checkbox: false,
});
}
void formatBulletedList(EditorState editorState) {
formatTextNodes(editorState, {
StyleKey.subtype: StyleKey.bulletedList,
});
}
bool formatTextNodes(EditorState editorState, Attributes attributes) {
final nodes = editorState.service.selectionService.currentSelectedNodes.value;
final textNodes = nodes.whereType<TextNode>().toList();
if (textNodes.isEmpty) {
return false;
}
final builder = TransactionBuilder(editorState);
for (final textNode in textNodes) {
builder.updateNode(
textNode,
Attributes.fromIterable(
StyleKey.globalStyleKeys,
value: (_) => null,
)..addAll(attributes),
);
}
builder.commit();
return true;
}
bool formatBold(EditorState editorState) { bool formatBold(EditorState editorState) {
return formatRichText(editorState, StyleKey.bold); return formatRichTextPartialStyle(editorState, StyleKey.bold);
} }
bool formatItalic(EditorState editorState) { bool formatItalic(EditorState editorState) {
return formatRichText(editorState, StyleKey.italic); return formatRichTextPartialStyle(editorState, StyleKey.italic);
} }
bool formatUnderline(EditorState editorState) { bool formatUnderline(EditorState editorState) {
return formatRichText(editorState, StyleKey.underline); return formatRichTextPartialStyle(editorState, StyleKey.underline);
} }
bool formatStrikethrough(EditorState editorState) { bool formatStrikethrough(EditorState editorState) {
return formatRichText(editorState, StyleKey.strikethrough); return formatRichTextPartialStyle(editorState, StyleKey.strikethrough);
} }
bool formatRichText(EditorState editorState, String styleKey) { bool formatRichTextPartialStyle(EditorState editorState, String styleKey) {
final selection = editorState.service.selectionService.currentSelection; final selection = editorState.service.selectionService.currentSelection;
final nodes = editorState.service.selectionService.currentSelectedNodes.value; final nodes = editorState.service.selectionService.currentSelectedNodes.value;
final textNodes = nodes.whereType<TextNode>().toList(growable: false); final textNodes = nodes.whereType<TextNode>().toList(growable: false);