Skip to content

Commit 2e91dfb

Browse files
authored
Integrate Grid into Document (#1759)
* 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 * feat: integrate grid plugin into document * feat: add more menu to grid plugins * feat: refactor built-in page plugins, including board and grid * feat: remove padding set up when plugin type equals to editor
1 parent 71022ed commit 2e91dfb

File tree

13 files changed

+524
-320
lines changed

13 files changed

+524
-320
lines changed

frontend/app_flowy/assets/translations/en.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -317,7 +317,10 @@
317317
},
318318
"slashMenu": {
319319
"board": {
320-
"selectABoardToLinkTo": "Select a board to link to"
320+
"selectABoardToLinkTo": "Select a Board to link to"
321+
},
322+
"grid": {
323+
"selectAGridToLinkTo": "Select a Grid to link to"
321324
}
322325
}
323326
},

frontend/app_flowy/lib/plugins/document/document_page.dart

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import 'package:app_flowy/plugins/document/presentation/plugins/board/board_menu_item.dart';
22
import 'package:app_flowy/plugins/document/presentation/plugins/board/board_node_widget.dart';
3+
import 'package:app_flowy/plugins/document/presentation/plugins/grid/grid_menu_item.dart';
4+
import 'package:app_flowy/plugins/document/presentation/plugins/grid/grid_node_widget.dart';
35
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
46
import 'package:appflowy_editor/appflowy_editor.dart';
57
import 'package:appflowy_editor_plugins/appflowy_editor_plugins.dart';
@@ -111,6 +113,8 @@ class _DocumentPageState extends State<DocumentPage> {
111113
kCodeBlockType: CodeBlockNodeWidgetBuilder(),
112114
// Board
113115
kBoardType: BoardNodeWidgetBuilder(),
116+
// Grid
117+
kGridType: GridNodeWidgetBuilder(),
114118
// Card
115119
kCalloutType: CalloutNodeWidgetBuilder(),
116120
},
@@ -133,6 +137,8 @@ class _DocumentPageState extends State<DocumentPage> {
133137
emojiMenuItem,
134138
// Board
135139
boardMenuItem,
140+
// Grid
141+
gridMenuItem,
136142
],
137143
themeData: theme.copyWith(extensions: [
138144
...theme.extensions.values,

frontend/app_flowy/lib/plugins/document/editor_styles.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ EditorStyle customEditorTheme(BuildContext context) {
99
? EditorStyle.dark
1010
: EditorStyle.light;
1111
editorStyle = editorStyle.copyWith(
12-
padding: const EdgeInsets.symmetric(horizontal: 40),
12+
padding: const EdgeInsets.symmetric(horizontal: 100),
1313
textStyle: editorStyle.textStyle?.copyWith(
1414
fontFamily: 'poppins',
1515
fontSize: documentStyle.fontSize,
Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
import 'package:app_flowy/plugins/document/presentation/plugins/base/insert_page_command.dart';
2+
import 'package:app_flowy/startup/startup.dart';
3+
import 'package:app_flowy/workspace/application/app/app_service.dart';
4+
import 'package:app_flowy/workspace/application/view/view_ext.dart';
5+
import 'package:app_flowy/workspace/presentation/home/home_stack.dart';
6+
import 'package:app_flowy/workspace/presentation/home/menu/menu.dart';
7+
import 'package:app_flowy/workspace/presentation/widgets/pop_up_action.dart';
8+
import 'package:appflowy_backend/protobuf/flowy-error/errors.pbserver.dart';
9+
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
10+
import 'package:appflowy_editor/appflowy_editor.dart';
11+
import 'package:appflowy_popover/appflowy_popover.dart';
12+
import 'package:dartz/dartz.dart' as dartz;
13+
import 'package:flowy_infra_ui/style_widget/icon_button.dart';
14+
import 'package:flutter/material.dart';
15+
import 'package:app_flowy/generated/locale_keys.g.dart';
16+
import 'package:easy_localization/easy_localization.dart';
17+
import 'package:flowy_infra/image.dart';
18+
19+
class BuiltInPageWidget extends StatefulWidget {
20+
const BuiltInPageWidget({
21+
Key? key,
22+
required this.node,
23+
required this.editorState,
24+
required this.builder,
25+
}) : super(key: key);
26+
27+
final Node node;
28+
final EditorState editorState;
29+
final Widget Function(ViewPB viewPB) builder;
30+
31+
@override
32+
State<BuiltInPageWidget> createState() => _BuiltInPageWidgetState();
33+
}
34+
35+
class _BuiltInPageWidgetState extends State<BuiltInPageWidget> {
36+
final focusNode = FocusNode();
37+
38+
String get gridID {
39+
return widget.node.attributes[kViewID];
40+
}
41+
42+
String get appID {
43+
return widget.node.attributes[kAppID];
44+
}
45+
46+
@override
47+
Widget build(BuildContext context) {
48+
return FutureBuilder<dartz.Either<ViewPB, FlowyError>>(
49+
builder: (context, snapshot) {
50+
if (snapshot.hasData) {
51+
final board = snapshot.data?.getLeftOrNull<ViewPB>();
52+
if (board != null) {
53+
return _build(context, board);
54+
}
55+
}
56+
return const Center(
57+
child: CircularProgressIndicator(),
58+
);
59+
},
60+
future: AppService().getView(appID, gridID),
61+
);
62+
}
63+
64+
@override
65+
void dispose() {
66+
focusNode.dispose();
67+
super.dispose();
68+
}
69+
70+
Widget _build(BuildContext context, ViewPB viewPB) {
71+
return MouseRegion(
72+
onEnter: (event) {
73+
widget.editorState.service.scrollService?.disable();
74+
},
75+
onExit: (event) {
76+
widget.editorState.service.scrollService?.enable();
77+
},
78+
child: SizedBox(
79+
height: 400,
80+
child: Stack(
81+
children: [
82+
_buildMenu(context, viewPB),
83+
_buildGrid(context, viewPB),
84+
],
85+
),
86+
),
87+
);
88+
}
89+
90+
Widget _buildGrid(BuildContext context, ViewPB viewPB) {
91+
return Focus(
92+
focusNode: focusNode,
93+
onFocusChange: (value) {
94+
if (value) {
95+
widget.editorState.service.selectionService.clearSelection();
96+
}
97+
},
98+
child: widget.builder(viewPB),
99+
);
100+
}
101+
102+
Widget _buildMenu(BuildContext context, ViewPB viewPB) {
103+
return Positioned(
104+
top: 5,
105+
left: 5,
106+
child: PopoverActionList<_ActionWrapper>(
107+
direction: PopoverDirection.bottomWithCenterAligned,
108+
actions:
109+
_ActionType.values.map((action) => _ActionWrapper(action)).toList(),
110+
buildChild: (controller) {
111+
return FlowyIconButton(
112+
tooltipText: LocaleKeys.tooltip_openMenu.tr(),
113+
width: 25,
114+
height: 30,
115+
iconPadding: const EdgeInsets.all(3),
116+
icon: svgWidget('editor/details'),
117+
onPressed: () => controller.show(),
118+
);
119+
},
120+
onSelected: (action, controller) async {
121+
switch (action.inner) {
122+
case _ActionType.openAsPage:
123+
getIt<MenuSharedState>().latestOpenView = viewPB;
124+
getIt<HomeStackManager>().setPlugin(viewPB.plugin());
125+
break;
126+
case _ActionType.delete:
127+
final transaction = widget.editorState.transaction;
128+
transaction.deleteNode(widget.node);
129+
widget.editorState.apply(transaction);
130+
break;
131+
}
132+
controller.close();
133+
},
134+
),
135+
);
136+
}
137+
}
138+
139+
enum _ActionType {
140+
openAsPage,
141+
delete,
142+
}
143+
144+
class _ActionWrapper extends ActionCell {
145+
final _ActionType inner;
146+
147+
_ActionWrapper(this.inner);
148+
149+
Widget? icon(Color iconColor) => null;
150+
151+
@override
152+
String get name {
153+
switch (inner) {
154+
case _ActionType.openAsPage:
155+
return LocaleKeys.tooltip_openAsPage.tr();
156+
case _ActionType.delete:
157+
return LocaleKeys.disclosureAction_delete.tr();
158+
}
159+
}
160+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import 'package:app_flowy/plugins/document/presentation/plugins/board/board_node_widget.dart';
2+
import 'package:app_flowy/plugins/document/presentation/plugins/grid/grid_node_widget.dart';
3+
import 'package:appflowy_backend/protobuf/flowy-folder/app.pb.dart';
4+
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
5+
import 'package:appflowy_editor/appflowy_editor.dart';
6+
7+
const String kAppID = 'app_id';
8+
const String kViewID = 'view_id';
9+
10+
extension InsertPage on EditorState {
11+
void insertPage(AppPB appPB, ViewPB viewPB) {
12+
final selection = service.selectionService.currentSelection.value;
13+
final textNodes =
14+
service.selectionService.currentSelectedNodes.whereType<TextNode>();
15+
if (selection == null || textNodes.isEmpty) {
16+
return;
17+
}
18+
final transaction = this.transaction;
19+
transaction.insertNode(
20+
selection.end.path,
21+
Node(
22+
type: _convertPageType(viewPB),
23+
attributes: {
24+
kAppID: appPB.id,
25+
kViewID: viewPB.id,
26+
},
27+
),
28+
);
29+
apply(transaction);
30+
}
31+
32+
String _convertPageType(ViewPB viewPB) {
33+
switch (viewPB.layout) {
34+
case ViewLayoutTypePB.Grid:
35+
return kGridType;
36+
case ViewLayoutTypePB.Board:
37+
return kBoardType;
38+
default:
39+
throw Exception('Unknown layout type');
40+
}
41+
}
42+
}

0 commit comments

Comments
 (0)