mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
feat: move divider plugin to appflowy editor plugins directory
This commit is contained in:
parent
7ba638268b
commit
157f929ff9
@ -1,6 +1,7 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
import 'package:appflowy_editor_plugins/appflowy_editor_plugins.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class SimpleEditor extends StatelessWidget {
|
||||
@ -30,10 +31,20 @@ class SimpleEditor extends StatelessWidget {
|
||||
),
|
||||
);
|
||||
onEditorStateChange(editorState);
|
||||
|
||||
return AppFlowyEditor(
|
||||
editorState: editorState,
|
||||
themeData: themeData,
|
||||
autoFocus: editorState.document.isEmpty,
|
||||
customBuilders: {
|
||||
kDividerType: DividerWidgetBuilder(),
|
||||
},
|
||||
shortcutEvents: [
|
||||
insertDividerEvent,
|
||||
],
|
||||
selectionMenuItems: [
|
||||
dividerMenuItem,
|
||||
],
|
||||
);
|
||||
} else {
|
||||
return const Center(
|
||||
|
@ -1,166 +0,0 @@
|
||||
import 'dart:collection';
|
||||
|
||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
ShortcutEvent insertHorizontalRule = ShortcutEvent(
|
||||
key: 'Horizontal rule',
|
||||
command: 'Minus',
|
||||
handler: _insertHorzaontalRule,
|
||||
);
|
||||
|
||||
ShortcutEventHandler _insertHorzaontalRule = (editorState, event) {
|
||||
final selection = editorState.service.selectionService.currentSelection.value;
|
||||
final textNodes = editorState.service.selectionService.currentSelectedNodes
|
||||
.whereType<TextNode>();
|
||||
if (textNodes.length != 1 || selection == null) {
|
||||
return KeyEventResult.ignored;
|
||||
}
|
||||
final textNode = textNodes.first;
|
||||
if (textNode.toPlainText() == '--') {
|
||||
final transaction = editorState.transaction
|
||||
..deleteText(textNode, 0, 2)
|
||||
..insertNode(
|
||||
textNode.path,
|
||||
Node(
|
||||
type: 'horizontal_rule',
|
||||
children: LinkedList(),
|
||||
attributes: {},
|
||||
),
|
||||
)
|
||||
..afterSelection =
|
||||
Selection.single(path: textNode.path.next, startOffset: 0);
|
||||
editorState.apply(transaction);
|
||||
return KeyEventResult.handled;
|
||||
}
|
||||
return KeyEventResult.ignored;
|
||||
};
|
||||
|
||||
SelectionMenuItem horizontalRuleMenuItem = SelectionMenuItem(
|
||||
name: () => 'Horizontal rule',
|
||||
icon: (_, __) => const Icon(
|
||||
Icons.horizontal_rule,
|
||||
color: Colors.black,
|
||||
size: 18.0,
|
||||
),
|
||||
keywords: ['horizontal rule'],
|
||||
handler: (editorState, _, __) {
|
||||
final selection =
|
||||
editorState.service.selectionService.currentSelection.value;
|
||||
final textNodes = editorState.service.selectionService.currentSelectedNodes
|
||||
.whereType<TextNode>();
|
||||
if (selection == null || textNodes.isEmpty) {
|
||||
return;
|
||||
}
|
||||
final textNode = textNodes.first;
|
||||
if (textNode.toPlainText().isEmpty) {
|
||||
final transaction = editorState.transaction
|
||||
..insertNode(
|
||||
textNode.path,
|
||||
Node(
|
||||
type: 'horizontal_rule',
|
||||
children: LinkedList(),
|
||||
attributes: {},
|
||||
),
|
||||
)
|
||||
..afterSelection =
|
||||
Selection.single(path: textNode.path.next, startOffset: 0);
|
||||
editorState.apply(transaction);
|
||||
} else {
|
||||
final transaction = editorState.transaction
|
||||
..insertNode(
|
||||
selection.end.path.next,
|
||||
TextNode(
|
||||
children: LinkedList(),
|
||||
attributes: {
|
||||
'subtype': 'horizontal_rule',
|
||||
},
|
||||
delta: Delta()..insert('---'),
|
||||
),
|
||||
)
|
||||
..afterSelection = selection;
|
||||
editorState.apply(transaction);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
class HorizontalRuleWidgetBuilder extends NodeWidgetBuilder<Node> {
|
||||
@override
|
||||
Widget build(NodeWidgetContext<Node> context) {
|
||||
return _HorizontalRuleWidget(
|
||||
key: context.node.key,
|
||||
node: context.node,
|
||||
editorState: context.editorState,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
NodeValidator<Node> get nodeValidator => (node) {
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
class _HorizontalRuleWidget extends StatefulWidget {
|
||||
const _HorizontalRuleWidget({
|
||||
Key? key,
|
||||
required this.node,
|
||||
required this.editorState,
|
||||
}) : super(key: key);
|
||||
|
||||
final Node node;
|
||||
final EditorState editorState;
|
||||
|
||||
@override
|
||||
State<_HorizontalRuleWidget> createState() => __HorizontalRuleWidgetState();
|
||||
}
|
||||
|
||||
class __HorizontalRuleWidgetState extends State<_HorizontalRuleWidget>
|
||||
with SelectableMixin {
|
||||
RenderBox get _renderBox => context.findRenderObject() as RenderBox;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 10),
|
||||
child: Container(
|
||||
height: 1,
|
||||
color: Colors.grey,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Position start() => Position(path: widget.node.path, offset: 0);
|
||||
|
||||
@override
|
||||
Position end() => Position(path: widget.node.path, offset: 1);
|
||||
|
||||
@override
|
||||
Position getPositionInOffset(Offset start) => end();
|
||||
|
||||
@override
|
||||
bool get shouldCursorBlink => false;
|
||||
|
||||
@override
|
||||
CursorStyle get cursorStyle => CursorStyle.borderLine;
|
||||
|
||||
@override
|
||||
Rect? getCursorRectInPosition(Position position) {
|
||||
final size = _renderBox.size;
|
||||
return Rect.fromLTWH(-size.width / 2.0, 0, size.width, size.height);
|
||||
}
|
||||
|
||||
@override
|
||||
List<Rect> getRectsInSelection(Selection selection) =>
|
||||
[Offset.zero & _renderBox.size];
|
||||
|
||||
@override
|
||||
Selection getSelectionInRange(Offset start, Offset end) => Selection.single(
|
||||
path: widget.node.path,
|
||||
startOffset: 0,
|
||||
endOffset: 1,
|
||||
);
|
||||
|
||||
@override
|
||||
Offset localToGlobal(Offset offset) => _renderBox.localToGlobal(offset);
|
||||
}
|
@ -45,6 +45,8 @@ dependencies:
|
||||
universal_html: ^2.0.8
|
||||
highlight: ^0.7.0
|
||||
flutter_math_fork: ^0.6.3+1
|
||||
appflowy_editor_plugins:
|
||||
path: ../../../packages/appflowy_editor_plugins
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
@ -1,7 +1,4 @@
|
||||
library appflowy_editor_plugins;
|
||||
|
||||
/// A Calculator.
|
||||
class Calculator {
|
||||
/// Returns [value] plus 1.
|
||||
int addOne(int value) => value + 1;
|
||||
}
|
||||
export 'src/divider/divider_node_widget.dart';
|
||||
export 'src/divider/divider_shortcut_event.dart';
|
||||
|
@ -0,0 +1,84 @@
|
||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
const String kDividerType = 'divider';
|
||||
|
||||
class DividerWidgetBuilder extends NodeWidgetBuilder<Node> {
|
||||
@override
|
||||
Widget build(NodeWidgetContext<Node> context) {
|
||||
return _DividerWidget(
|
||||
key: context.node.key,
|
||||
node: context.node,
|
||||
editorState: context.editorState,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
NodeValidator<Node> get nodeValidator => (node) {
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
class _DividerWidget extends StatefulWidget {
|
||||
const _DividerWidget({
|
||||
Key? key,
|
||||
required this.node,
|
||||
required this.editorState,
|
||||
}) : super(key: key);
|
||||
|
||||
final Node node;
|
||||
final EditorState editorState;
|
||||
|
||||
@override
|
||||
State<_DividerWidget> createState() => _DividerWidgetState();
|
||||
}
|
||||
|
||||
class _DividerWidgetState extends State<_DividerWidget> with SelectableMixin {
|
||||
RenderBox get _renderBox => context.findRenderObject() as RenderBox;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 10),
|
||||
child: Container(
|
||||
height: 1,
|
||||
color: Colors.grey,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Position start() => Position(path: widget.node.path, offset: 0);
|
||||
|
||||
@override
|
||||
Position end() => Position(path: widget.node.path, offset: 1);
|
||||
|
||||
@override
|
||||
Position getPositionInOffset(Offset start) => end();
|
||||
|
||||
@override
|
||||
bool get shouldCursorBlink => false;
|
||||
|
||||
@override
|
||||
CursorStyle get cursorStyle => CursorStyle.borderLine;
|
||||
|
||||
@override
|
||||
Rect? getCursorRectInPosition(Position position) {
|
||||
final size = _renderBox.size;
|
||||
return Rect.fromLTWH(-size.width / 2.0, 0, size.width, size.height);
|
||||
}
|
||||
|
||||
@override
|
||||
List<Rect> getRectsInSelection(Selection selection) =>
|
||||
[Offset.zero & _renderBox.size];
|
||||
|
||||
@override
|
||||
Selection getSelectionInRange(Offset start, Offset end) => Selection.single(
|
||||
path: widget.node.path,
|
||||
startOffset: 0,
|
||||
endOffset: 1,
|
||||
);
|
||||
|
||||
@override
|
||||
Offset localToGlobal(Offset offset) => _renderBox.localToGlobal(offset);
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
import 'package:appflowy_editor_plugins/src/divider/divider_node_widget.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
// insert divider into a document by typing three minuses.
|
||||
// ---
|
||||
ShortcutEvent insertDividerEvent = ShortcutEvent(
|
||||
key: 'Divider',
|
||||
command: 'Minus',
|
||||
handler: _insertDividerHandler,
|
||||
);
|
||||
|
||||
ShortcutEventHandler _insertDividerHandler = (editorState, event) {
|
||||
final selection = editorState.service.selectionService.currentSelection.value;
|
||||
final textNodes = editorState.service.selectionService.currentSelectedNodes
|
||||
.whereType<TextNode>();
|
||||
if (textNodes.length != 1 || selection == null) {
|
||||
return KeyEventResult.ignored;
|
||||
}
|
||||
final textNode = textNodes.first;
|
||||
if (textNode.toPlainText() != '--') {
|
||||
return KeyEventResult.ignored;
|
||||
}
|
||||
final transaction = editorState.transaction
|
||||
..deleteText(textNode, 0, 2) // remove the existing minuses.
|
||||
..insertNode(textNode.path, Node(type: kDividerType)) // insert the divder
|
||||
..afterSelection = Selection.single(
|
||||
// update selection to the next text node.
|
||||
path: textNode.path.next,
|
||||
startOffset: 0,
|
||||
);
|
||||
editorState.apply(transaction);
|
||||
return KeyEventResult.handled;
|
||||
};
|
||||
|
||||
SelectionMenuItem dividerMenuItem = SelectionMenuItem(
|
||||
name: () => 'Divider',
|
||||
icon: (editorState, onSelected) => Icon(
|
||||
Icons.horizontal_rule,
|
||||
color: onSelected
|
||||
? editorState.editorStyle.selectionMenuItemSelectedIconColor
|
||||
: editorState.editorStyle.selectionMenuItemIconColor,
|
||||
size: 18.0,
|
||||
),
|
||||
keywords: ['horizontal rule', 'divider'],
|
||||
handler: (editorState, _, __) {
|
||||
final selection =
|
||||
editorState.service.selectionService.currentSelection.value;
|
||||
final textNodes = editorState.service.selectionService.currentSelectedNodes
|
||||
.whereType<TextNode>();
|
||||
if (textNodes.length != 1 || selection == null) {
|
||||
return;
|
||||
}
|
||||
final textNode = textNodes.first;
|
||||
// insert the divider at current path if the text node is empty.
|
||||
if (textNode.toPlainText().isEmpty) {
|
||||
final transaction = editorState.transaction
|
||||
..insertNode(textNode.path, Node(type: kDividerType))
|
||||
..afterSelection = Selection.single(
|
||||
path: textNode.path.next,
|
||||
startOffset: 0,
|
||||
);
|
||||
editorState.apply(transaction);
|
||||
} else {
|
||||
// insert the divider at the path next to current path if the text node is not empty.
|
||||
final transaction = editorState.transaction
|
||||
..insertNode(selection.end.path.next, Node(type: kDividerType))
|
||||
..afterSelection = selection;
|
||||
editorState.apply(transaction);
|
||||
}
|
||||
},
|
||||
);
|
@ -1,7 +1,9 @@
|
||||
name: appflowy_editor_plugins
|
||||
description: A new Flutter package project.
|
||||
version: 0.0.1
|
||||
homepage:
|
||||
homepage: https://github.com/AppFlowy-IO/AppFlowy
|
||||
|
||||
publish_to: none
|
||||
|
||||
environment:
|
||||
sdk: ">=2.17.6 <3.0.0"
|
||||
@ -10,6 +12,8 @@ environment:
|
||||
dependencies:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
appflowy_editor:
|
||||
path: ../appflowy_editor
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
@ -1,12 +1 @@
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
import 'package:appflowy_editor_plugins/appflowy_editor_plugins.dart';
|
||||
|
||||
void main() {
|
||||
test('adds one to input values', () {
|
||||
final calculator = Calculator();
|
||||
expect(calculator.addOne(2), 3);
|
||||
expect(calculator.addOne(-7), -6);
|
||||
expect(calculator.addOne(0), 1);
|
||||
});
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user