mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
feat: implement heading, quote, bulleted_list in toolbar service
This commit is contained in:
parent
b11a127432
commit
9b6afcc5c9
@ -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);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
||||||
|
@ -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);
|
||||||
|
Loading…
Reference in New Issue
Block a user