integrate board plugin into document (#1675)

* fix: cursor doesn't blink when opening selection menu

* feat: add board plugin

* feat: integrate board plugin into document

* feat: add i10n and fix known bugs

* feat: support jump to board page on document

* feat: disable editor scroll only when the board plugin is selected

* chore: dart fix

* chore: remove unused files

* fix: dart lint
This commit is contained in:
Lucas.Xu 2023-01-30 12:22:13 +07:00 • committed by GitHub
commit be640c0cc5
9 changed files with 20 additions and 23 deletions

View file

@ -23,6 +23,7 @@
]
},
{ "type": "text", "delta": [] },
{ "type": "board" },
{
"type": "text",
"delta": [

View file

@ -44,3 +44,4 @@ export 'src/plugins/markdown/document_markdown.dart';
export 'src/plugins/quill_delta/delta_document_encoder.dart';
export 'src/commands/text/text_commands.dart';
export 'src/render/toolbar/toolbar_item.dart';
export 'src/extensions/node_extensions.dart';

View file

@ -71,7 +71,7 @@ class Node extends ChangeNotifier with LinkedListEntry<Node> {
Attributes _attributes;
// Renderable
GlobalKey? key;
final key = GlobalKey();
final layerLink = LayerLink();
Attributes get attributes => {..._attributes};

View file

@ -1,17 +1,18 @@
import 'package:appflowy_editor/src/core/document/node.dart';
import 'package:appflowy_editor/src/core/document/path.dart';
import 'package:appflowy_editor/src/core/location/selection.dart';
import 'package:appflowy_editor/src/editor_state.dart';
import 'package:appflowy_editor/src/extensions/object_extensions.dart';
import 'package:appflowy_editor/src/render/selection/selectable.dart';
import 'package:flutter/material.dart';
extension NodeExtensions on Node {
RenderBox? get renderBox =>
key?.currentContext?.findRenderObject()?.unwrapOrNull<RenderBox>();
key.currentContext?.findRenderObject()?.unwrapOrNull<RenderBox>();
BuildContext? get context => key?.currentContext;
BuildContext? get context => key.currentContext;
SelectableMixin? get selectable =>
key?.currentState?.unwrapOrNull<SelectableMixin>();
key.currentState?.unwrapOrNull<SelectableMixin>();
bool inSelection(Selection selection) {
if (selection.start.path <= selection.end.path) {
@ -28,4 +29,11 @@ extension NodeExtensions on Node {
}
return Rect.zero;
}
bool isSelected(EditorState editorState) {
final currentSelectedNodes =
editorState.service.selectionService.currentSelectedNodes;
return currentSelectedNodes.length == 1 &&
currentSelectedNodes.first == this;
}
}

View file

@ -1,5 +1,4 @@
import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:appflowy_editor/src/extensions/node_extensions.dart';
import 'package:flutter/material.dart';
ShortcutEventHandler cursorLeftSelect = (editorState, event) {

View file

@ -74,8 +74,6 @@ class AppFlowyRenderPlugin extends AppFlowyRenderPluginService {
node.subtype == null ? node.type : '${node.type}/${node.subtype!}';
final builder = _builders[name];
if (builder != null && builder.nodeValidator(node)) {
final key = GlobalKey(debugLabel: name);
node.key = key;
return _autoUpdateNodeWidget(builder, context);
} else {
// Returns a SizeBox with 0 height if no builder found.

View file

@ -1,28 +1,18 @@
import 'dart:collection';
import 'dart:ui';
import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/mockito.dart';
import 'package:appflowy_editor/src/extensions/node_extensions.dart';
class MockNode extends Mock implements Node {}
void main() {
final mockNode = MockNode();
group('NodeExtensions::', () {
final selection = Selection(
start: Position(path: [0]),
end: Position(path: [1]),
);
test('rect - renderBox is null', () {
when(mockNode.renderBox).thenReturn(null);
final result = mockNode.rect;
expect(result, Rect.zero);
});
test('inSelection', () {
// I use an empty implementation instead of mock, because the mocked
// version throws error trying to access the path.

View file

@ -43,7 +43,7 @@ void main() async {
final selection =
Selection.single(path: [0], startOffset: 0, endOffset: text.length);
var node = editor.nodeAtPath([0]) as TextNode;
var state = node.key?.currentState as DefaultSelectable;
var state = node.key.currentState as DefaultSelectable;
var checkboxWidget = find.byKey(state.iconKey!);
await tester.tap(checkboxWidget);
await tester.pumpAndSettle();
@ -56,7 +56,7 @@ void main() async {
expect(node.allSatisfyStrikethroughInSelection(selection), true);
node = editor.nodeAtPath([0]) as TextNode;
state = node.key?.currentState as DefaultSelectable;
state = node.key.currentState as DefaultSelectable;
await tester.ensureVisible(find.byKey(state.iconKey!));
await tester.tap(find.byKey(state.iconKey!));
await tester.pump();

View file

@ -21,7 +21,7 @@ void main() async {
await editor.startTesting();
final secondTextNode = editor.nodeAtPath([1]);
final finder = find.byKey(secondTextNode!.key!);
final finder = find.byKey(secondTextNode!.key);
final rect = tester.getRect(finder);
// tap at the beginning
@ -48,7 +48,7 @@ void main() async {
await editor.startTesting();
final secondTextNode = editor.nodeAtPath([1]);
final finder = find.byKey(secondTextNode!.key!);
final finder = find.byKey(secondTextNode!.key);
final rect = tester.getRect(finder);
// double tap
@ -70,7 +70,7 @@ void main() async {
await editor.startTesting();
final secondTextNode = editor.nodeAtPath([1]);
final finder = find.byKey(secondTextNode!.key!);
final finder = find.byKey(secondTextNode!.key);
final rect = tester.getRect(finder);
// triple tap
@ -93,7 +93,7 @@ void main() async {
await editor.startTesting();
final secondTextNode = editor.nodeAtPath([1]) as TextNode;
final finder = find.byKey(secondTextNode.key!);
final finder = find.byKey(secondTextNode.key);
final rect = tester.getRect(finder);
// secondary tap