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
This commit is contained in:
Lucas.Xu 2023-02-01 14:37:45 +07:00 committed by GitHub
parent 71022ed934
commit 2e91dfb4be
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 524 additions and 320 deletions

View File

@ -317,7 +317,10 @@
},
"slashMenu": {
"board": {
"selectABoardToLinkTo": "Select a board to link to"
"selectABoardToLinkTo": "Select a Board to link to"
},
"grid": {
"selectAGridToLinkTo": "Select a Grid to link to"
}
}
},

View File

@ -1,5 +1,7 @@
import 'package:app_flowy/plugins/document/presentation/plugins/board/board_menu_item.dart';
import 'package:app_flowy/plugins/document/presentation/plugins/board/board_node_widget.dart';
import 'package:app_flowy/plugins/document/presentation/plugins/grid/grid_menu_item.dart';
import 'package:app_flowy/plugins/document/presentation/plugins/grid/grid_node_widget.dart';
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:appflowy_editor_plugins/appflowy_editor_plugins.dart';
@ -111,6 +113,8 @@ class _DocumentPageState extends State<DocumentPage> {
kCodeBlockType: CodeBlockNodeWidgetBuilder(),
// Board
kBoardType: BoardNodeWidgetBuilder(),
// Grid
kGridType: GridNodeWidgetBuilder(),
// Card
kCalloutType: CalloutNodeWidgetBuilder(),
},
@ -133,6 +137,8 @@ class _DocumentPageState extends State<DocumentPage> {
emojiMenuItem,
// Board
boardMenuItem,
// Grid
gridMenuItem,
],
themeData: theme.copyWith(extensions: [
...theme.extensions.values,

View File

@ -9,7 +9,7 @@ EditorStyle customEditorTheme(BuildContext context) {
? EditorStyle.dark
: EditorStyle.light;
editorStyle = editorStyle.copyWith(
padding: const EdgeInsets.symmetric(horizontal: 40),
padding: const EdgeInsets.symmetric(horizontal: 100),
textStyle: editorStyle.textStyle?.copyWith(
fontFamily: 'poppins',
fontSize: documentStyle.fontSize,

View File

@ -0,0 +1,160 @@
import 'package:app_flowy/plugins/document/presentation/plugins/base/insert_page_command.dart';
import 'package:app_flowy/startup/startup.dart';
import 'package:app_flowy/workspace/application/app/app_service.dart';
import 'package:app_flowy/workspace/application/view/view_ext.dart';
import 'package:app_flowy/workspace/presentation/home/home_stack.dart';
import 'package:app_flowy/workspace/presentation/home/menu/menu.dart';
import 'package:app_flowy/workspace/presentation/widgets/pop_up_action.dart';
import 'package:appflowy_backend/protobuf/flowy-error/errors.pbserver.dart';
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:appflowy_popover/appflowy_popover.dart';
import 'package:dartz/dartz.dart' as dartz;
import 'package:flowy_infra_ui/style_widget/icon_button.dart';
import 'package:flutter/material.dart';
import 'package:app_flowy/generated/locale_keys.g.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra/image.dart';
class BuiltInPageWidget extends StatefulWidget {
const BuiltInPageWidget({
Key? key,
required this.node,
required this.editorState,
required this.builder,
}) : super(key: key);
final Node node;
final EditorState editorState;
final Widget Function(ViewPB viewPB) builder;
@override
State<BuiltInPageWidget> createState() => _BuiltInPageWidgetState();
}
class _BuiltInPageWidgetState extends State<BuiltInPageWidget> {
final focusNode = FocusNode();
String get gridID {
return widget.node.attributes[kViewID];
}
String get appID {
return widget.node.attributes[kAppID];
}
@override
Widget build(BuildContext context) {
return FutureBuilder<dartz.Either<ViewPB, FlowyError>>(
builder: (context, snapshot) {
if (snapshot.hasData) {
final board = snapshot.data?.getLeftOrNull<ViewPB>();
if (board != null) {
return _build(context, board);
}
}
return const Center(
child: CircularProgressIndicator(),
);
},
future: AppService().getView(appID, gridID),
);
}
@override
void dispose() {
focusNode.dispose();
super.dispose();
}
Widget _build(BuildContext context, ViewPB viewPB) {
return MouseRegion(
onEnter: (event) {
widget.editorState.service.scrollService?.disable();
},
onExit: (event) {
widget.editorState.service.scrollService?.enable();
},
child: SizedBox(
height: 400,
child: Stack(
children: [
_buildMenu(context, viewPB),
_buildGrid(context, viewPB),
],
),
),
);
}
Widget _buildGrid(BuildContext context, ViewPB viewPB) {
return Focus(
focusNode: focusNode,
onFocusChange: (value) {
if (value) {
widget.editorState.service.selectionService.clearSelection();
}
},
child: widget.builder(viewPB),
);
}
Widget _buildMenu(BuildContext context, ViewPB viewPB) {
return Positioned(
top: 5,
left: 5,
child: PopoverActionList<_ActionWrapper>(
direction: PopoverDirection.bottomWithCenterAligned,
actions:
_ActionType.values.map((action) => _ActionWrapper(action)).toList(),
buildChild: (controller) {
return FlowyIconButton(
tooltipText: LocaleKeys.tooltip_openMenu.tr(),
width: 25,
height: 30,
iconPadding: const EdgeInsets.all(3),
icon: svgWidget('editor/details'),
onPressed: () => controller.show(),
);
},
onSelected: (action, controller) async {
switch (action.inner) {
case _ActionType.openAsPage:
getIt<MenuSharedState>().latestOpenView = viewPB;
getIt<HomeStackManager>().setPlugin(viewPB.plugin());
break;
case _ActionType.delete:
final transaction = widget.editorState.transaction;
transaction.deleteNode(widget.node);
widget.editorState.apply(transaction);
break;
}
controller.close();
},
),
);
}
}
enum _ActionType {
openAsPage,
delete,
}
class _ActionWrapper extends ActionCell {
final _ActionType inner;
_ActionWrapper(this.inner);
Widget? icon(Color iconColor) => null;
@override
String get name {
switch (inner) {
case _ActionType.openAsPage:
return LocaleKeys.tooltip_openAsPage.tr();
case _ActionType.delete:
return LocaleKeys.disclosureAction_delete.tr();
}
}
}

View File

@ -0,0 +1,42 @@
import 'package:app_flowy/plugins/document/presentation/plugins/board/board_node_widget.dart';
import 'package:app_flowy/plugins/document/presentation/plugins/grid/grid_node_widget.dart';
import 'package:appflowy_backend/protobuf/flowy-folder/app.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
import 'package:appflowy_editor/appflowy_editor.dart';
const String kAppID = 'app_id';
const String kViewID = 'view_id';
extension InsertPage on EditorState {
void insertPage(AppPB appPB, ViewPB viewPB) {
final selection = service.selectionService.currentSelection.value;
final textNodes =
service.selectionService.currentSelectedNodes.whereType<TextNode>();
if (selection == null || textNodes.isEmpty) {
return;
}
final transaction = this.transaction;
transaction.insertNode(
selection.end.path,
Node(
type: _convertPageType(viewPB),
attributes: {
kAppID: appPB.id,
kViewID: viewPB.id,
},
),
);
apply(transaction);
}
String _convertPageType(ViewPB viewPB) {
switch (viewPB.layout) {
case ViewLayoutTypePB.Grid:
return kGridType;
case ViewLayoutTypePB.Board:
return kBoardType;
default:
throw Exception('Unknown layout type');
}
}
}

View File

@ -0,0 +1,186 @@
import 'package:app_flowy/workspace/application/app/app_service.dart';
import 'package:appflowy_backend/protobuf/flowy-folder/app.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:dartz/dartz.dart' as dartz;
import 'package:flowy_infra/image.dart';
import 'package:flowy_infra_ui/style_widget/button.dart';
import 'package:flowy_infra_ui/style_widget/text.dart';
import 'package:flutter/material.dart';
import 'insert_page_command.dart';
import 'package:app_flowy/generated/locale_keys.g.dart';
import 'package:easy_localization/easy_localization.dart';
EditorState? _editorState;
OverlayEntry? _linkToPageMenu;
void showLinkToPageMenu(
EditorState editorState,
SelectionMenuService menuService,
BuildContext context,
ViewLayoutTypePB pageType,
) {
final aligment = menuService.alignment;
final offset = menuService.offset;
menuService.dismiss();
_editorState = editorState;
String hintText = '';
switch (pageType) {
case ViewLayoutTypePB.Grid:
hintText = LocaleKeys.document_slashMenu_grid_selectAGridToLinkTo.tr();
break;
case ViewLayoutTypePB.Board:
hintText = LocaleKeys.document_slashMenu_board_selectABoardToLinkTo.tr();
break;
default:
throw Exception('Unknown layout type');
}
_linkToPageMenu?.remove();
_linkToPageMenu = OverlayEntry(builder: (context) {
return Positioned(
top: aligment == Alignment.bottomLeft ? offset.dy : null,
bottom: aligment == Alignment.topLeft ? offset.dy : null,
left: offset.dx,
child: Material(
color: Colors.transparent,
child: LinkToPageMenu(
editorState: editorState,
layoutType: pageType,
hintText: hintText,
onSelected: (appPB, viewPB) {
editorState.insertPage(appPB, viewPB);
},
),
),
);
});
Overlay.of(context)?.insert(_linkToPageMenu!);
editorState.service.selectionService.currentSelection
.addListener(dismissLinkToPageMenu);
}
void dismissLinkToPageMenu() {
_linkToPageMenu?.remove();
_linkToPageMenu = null;
_editorState?.service.selectionService.currentSelection
.removeListener(dismissLinkToPageMenu);
_editorState = null;
}
class LinkToPageMenu extends StatefulWidget {
const LinkToPageMenu({
super.key,
required this.editorState,
required this.layoutType,
required this.hintText,
required this.onSelected,
});
final EditorState editorState;
final ViewLayoutTypePB layoutType;
final String hintText;
final void Function(AppPB appPB, ViewPB viewPB) onSelected;
@override
State<LinkToPageMenu> createState() => _LinkToPageMenuState();
}
class _LinkToPageMenuState extends State<LinkToPageMenu> {
EditorStyle get style => widget.editorState.editorStyle;
@override
Widget build(BuildContext context) {
return Container(
color: Colors.transparent,
width: 300,
child: Container(
padding: const EdgeInsets.fromLTRB(10, 6, 10, 6),
decoration: BoxDecoration(
color: style.selectionMenuBackgroundColor,
boxShadow: [
BoxShadow(
blurRadius: 5,
spreadRadius: 1,
color: Colors.black.withOpacity(0.1),
),
],
borderRadius: BorderRadius.circular(6.0),
),
child: _buildListWidget(context),
),
);
}
Widget _buildListWidget(BuildContext context) {
return FutureBuilder<List<dartz.Tuple2<AppPB, List<ViewPB>>>>(
builder: (context, snapshot) {
if (snapshot.hasData &&
snapshot.connectionState == ConnectionState.done) {
final apps = snapshot.data;
final children = <Widget>[
Padding(
padding: const EdgeInsets.symmetric(vertical: 4),
child: FlowyText.regular(
widget.hintText,
fontSize: 10,
color: Colors.grey,
),
),
];
if (apps != null && apps.isNotEmpty) {
for (final app in apps) {
if (app.value2.isNotEmpty) {
children.add(
Padding(
padding: const EdgeInsets.symmetric(vertical: 4),
child: FlowyText.regular(
app.value1.name,
),
),
);
for (final value in app.value2) {
children.add(
FlowyButton(
leftIcon: svgWidget(
_iconName(value),
color: Theme.of(context).colorScheme.onSurface,
),
text: FlowyText.regular(value.name),
onTap: () => widget.onSelected(app.value1, value),
),
);
}
}
}
}
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: children,
);
} else {
return const Center(
child: CircularProgressIndicator(),
);
}
},
future: AppService().fetchViews(widget.layoutType),
);
}
String _iconName(ViewPB viewPB) {
switch (viewPB.layout) {
case ViewLayoutTypePB.Grid:
return 'editor/grid';
case ViewLayoutTypePB.Board:
return 'editor/board';
default:
throw Exception('Unknown layout type');
}
}
}

View File

@ -1,14 +1,9 @@
import 'package:app_flowy/generated/locale_keys.g.dart';
import 'package:app_flowy/plugins/document/presentation/plugins/board/board_node_widget.dart';
import 'package:app_flowy/workspace/application/app/app_service.dart';
import 'package:appflowy_backend/protobuf/flowy-folder/app.pb.dart';
import 'package:app_flowy/plugins/document/presentation/plugins/base/link_to_page_widget.dart';
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:dartz/dartz.dart' as dartz;
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra/image.dart';
import 'package:flowy_infra_ui/style_widget/button.dart';
import 'package:flowy_infra_ui/style_widget/text.dart';
import 'package:flutter/material.dart';
SelectionMenuItem boardMenuItem = SelectionMenuItem(
@ -22,174 +17,13 @@ SelectionMenuItem boardMenuItem = SelectionMenuItem(
: editorState.editorStyle.selectionMenuItemIconColor,
);
},
keywords: ['board'],
handler: _showLinkToPageMenu,
keywords: ['board', 'kanban'],
handler: (editorState, menuService, context) {
showLinkToPageMenu(
editorState,
menuService,
context,
ViewLayoutTypePB.Board,
);
},
);
EditorState? _editorState;
OverlayEntry? _linkToPageMenu;
void _dismissLinkToPageMenu() {
_linkToPageMenu?.remove();
_linkToPageMenu = null;
_editorState?.service.selectionService.currentSelection
.removeListener(_dismissLinkToPageMenu);
_editorState = null;
}
void _showLinkToPageMenu(
EditorState editorState,
SelectionMenuService menuService,
BuildContext context,
) {
final aligment = menuService.alignment;
final offset = menuService.offset;
menuService.dismiss();
_editorState = editorState;
_linkToPageMenu?.remove();
_linkToPageMenu = OverlayEntry(builder: (context) {
return Positioned(
top: aligment == Alignment.bottomLeft ? offset.dy : null,
bottom: aligment == Alignment.topLeft ? offset.dy : null,
left: offset.dx,
child: Material(
color: Colors.transparent,
child: LinkToPageMenu(
editorState: editorState,
),
),
);
});
Overlay.of(context)?.insert(_linkToPageMenu!);
editorState.service.selectionService.currentSelection
.addListener(_dismissLinkToPageMenu);
}
class LinkToPageMenu extends StatefulWidget {
final EditorState editorState;
const LinkToPageMenu({
super.key,
required this.editorState,
});
@override
State<LinkToPageMenu> createState() => _LinkToPageMenuState();
}
class _LinkToPageMenuState extends State<LinkToPageMenu> {
EditorStyle get style => widget.editorState.editorStyle;
@override
Widget build(BuildContext context) {
return Container(
color: Colors.transparent,
width: 300,
child: Container(
padding: const EdgeInsets.fromLTRB(10, 6, 10, 6),
decoration: BoxDecoration(
color: style.selectionMenuBackgroundColor,
boxShadow: [
BoxShadow(
blurRadius: 5,
spreadRadius: 1,
color: Colors.black.withOpacity(0.1),
),
],
borderRadius: BorderRadius.circular(6.0),
),
child: _buildBoardListWidget(context),
),
);
}
Future<List<dartz.Tuple2<AppPB, List<ViewPB>>>> fetchBoards() async {
return AppService().fetchViews(ViewLayoutTypePB.Board);
}
Widget _buildBoardListWidget(BuildContext context) {
return FutureBuilder<List<dartz.Tuple2<AppPB, List<ViewPB>>>>(
builder: (context, snapshot) {
if (snapshot.hasData &&
snapshot.connectionState == ConnectionState.done) {
final apps = snapshot.data;
final children = <Widget>[
Padding(
padding: const EdgeInsets.symmetric(vertical: 4),
child: FlowyText.regular(
LocaleKeys.document_slashMenu_board_selectABoardToLinkTo.tr(),
fontSize: 10,
color: Colors.grey,
),
),
];
if (apps != null && apps.isNotEmpty) {
for (final app in apps) {
if (app.value2.isNotEmpty) {
children.add(
Padding(
padding: const EdgeInsets.symmetric(vertical: 4),
child: FlowyText.regular(
app.value1.name,
),
),
);
for (final board in app.value2) {
children.add(
FlowyButton(
leftIcon: svgWidget(
'editor/board',
color: Theme.of(context).colorScheme.onSurface,
),
text: FlowyText.regular(board.name),
onTap: () => widget.editorState.insertBoard(
app.value1,
board,
),
),
);
}
}
}
}
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: children,
);
} else {
return const Center(
child: CircularProgressIndicator(),
);
}
},
future: fetchBoards(),
);
}
}
extension on EditorState {
void insertBoard(AppPB appPB, ViewPB viewPB) {
final selection = service.selectionService.currentSelection.value;
final textNodes =
service.selectionService.currentSelectedNodes.whereType<TextNode>();
if (selection == null || textNodes.isEmpty) {
return;
}
final transaction = this.transaction;
transaction.insertNode(
selection.end.path,
Node(
type: kBoardType,
attributes: {
kAppID: appPB.id,
kBoardID: viewPB.id,
},
),
);
apply(transaction);
}
}

View File

@ -1,19 +1,10 @@
import 'package:app_flowy/plugins/board/presentation/board_page.dart';
import 'package:app_flowy/startup/startup.dart';
import 'package:app_flowy/workspace/application/app/app_service.dart';
import 'package:app_flowy/workspace/application/view/view_ext.dart';
import 'package:app_flowy/workspace/presentation/home/home_stack.dart';
import 'package:app_flowy/workspace/presentation/home/menu/menu.dart';
import 'package:appflowy_backend/protobuf/flowy-error/errors.pbserver.dart';
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
import 'package:app_flowy/plugins/document/presentation/plugins/base/built_in_page_widget.dart';
import 'package:app_flowy/plugins/document/presentation/plugins/base/insert_page_command.dart';
import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:dartz/dartz.dart' as dartz;
import 'package:flowy_infra_ui/style_widget/button.dart';
import 'package:flutter/material.dart';
const String kBoardType = 'board';
const String kAppID = 'app_id';
const String kBoardID = 'board_id';
class BoardNodeWidgetBuilder extends NodeWidgetBuilder<Node> {
@override
@ -27,7 +18,7 @@ class BoardNodeWidgetBuilder extends NodeWidgetBuilder<Node> {
@override
NodeValidator<Node> get nodeValidator => (node) {
return node.attributes[kBoardID] is String &&
return node.attributes[kViewID] is String &&
node.attributes[kAppID] is String;
};
}
@ -46,130 +37,18 @@ class _BoardWidget extends StatefulWidget {
State<_BoardWidget> createState() => _BoardWidgetState();
}
class _BoardWidgetState extends State<_BoardWidget> with SelectableMixin {
RenderBox get _renderBox => context.findRenderObject() as RenderBox;
String get boardID {
return widget.node.attributes[kBoardID];
}
String get appID {
return widget.node.attributes[kAppID];
}
late Future<dartz.Either<ViewPB, FlowyError>> board;
@override
void initState() {
super.initState();
board = _fetchBoard();
}
class _BoardWidgetState extends State<_BoardWidget> {
@override
Widget build(BuildContext context) {
return FutureBuilder<dartz.Either<ViewPB, FlowyError>>(
builder: (context, snapshot) {
if (snapshot.hasData) {
final board = snapshot.data?.getLeftOrNull<ViewPB>();
if (board != null) {
return _buildBoard(context, board);
}
}
return const Center(
child: CircularProgressIndicator(),
return BuiltInPageWidget(
node: widget.node,
editorState: widget.editorState,
builder: (viewPB) {
return BoardPage(
key: ValueKey(viewPB.id),
view: viewPB,
);
},
future: board,
);
}
Future<dartz.Either<ViewPB, FlowyError>> _fetchBoard() async {
return AppService().getView(appID, boardID);
}
Widget _buildBoard(BuildContext context, ViewPB viewPB) {
return MouseRegion(
onHover: (event) {
if (widget.node.isSelected(widget.editorState)) {
widget.editorState.service.scrollService?.disable();
}
},
onExit: (event) {
widget.editorState.service.scrollService?.enable();
},
child: SizedBox(
height: 400,
child: Stack(
children: [
Positioned(
top: 0,
left: 20,
child: FlowyTextButton(
viewPB.name,
onPressed: () {
getIt<MenuSharedState>().latestOpenView = viewPB;
getIt<HomeStackManager>().setPlugin(viewPB.plugin());
},
),
),
BoardPage(
key: ValueKey(viewPB.id),
view: viewPB,
onEditStateChanged: () {
/// Clear selection when the edit state changes, otherwise the editor will prevent the keyboard event when the board is in edit mode.
widget.editorState.service.selectionService.clearSelection();
},
),
],
),
),
);
}
@override
bool get shouldCursorBlink => false;
@override
CursorStyle get cursorStyle => CursorStyle.borderLine;
@override
Position start() {
return Position(path: widget.node.path, offset: 0);
}
@override
Position end() {
return Position(path: widget.node.path, offset: 0);
}
@override
Position getPositionInOffset(Offset start) {
return end();
}
@override
List<Rect> getRectsInSelection(Selection selection) {
return [Offset.zero & _renderBox.size];
}
@override
Rect? getCursorRectInPosition(Position position) {
final size = _renderBox.size;
return Rect.fromLTWH(-size.width / 2.0, 0, size.width, size.height);
}
@override
Selection getSelectionInRange(Offset start, Offset end) {
return Selection.single(
path: widget.node.path,
startOffset: 0,
endOffset: 0,
);
}
@override
Offset localToGlobal(Offset offset) {
return _renderBox.localToGlobal(offset);
}
}

View File

@ -0,0 +1,29 @@
import 'package:app_flowy/generated/locale_keys.g.dart';
import 'package:app_flowy/plugins/document/presentation/plugins/base/link_to_page_widget.dart';
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra/image.dart';
import 'package:flutter/material.dart';
SelectionMenuItem gridMenuItem = SelectionMenuItem(
name: () => LocaleKeys.grid_menuName.tr(),
icon: (editorState, onSelected) {
return svgWidget(
'editor/grid',
size: const Size.square(18.0),
color: onSelected
? editorState.editorStyle.selectionMenuItemSelectedIconColor
: editorState.editorStyle.selectionMenuItemIconColor,
);
},
keywords: ['grid'],
handler: (editorState, menuService, context) {
showLinkToPageMenu(
editorState,
menuService,
context,
ViewLayoutTypePB.Grid,
);
},
);

View File

@ -0,0 +1,54 @@
import 'package:app_flowy/plugins/document/presentation/plugins/base/built_in_page_widget.dart';
import 'package:app_flowy/plugins/document/presentation/plugins/base/insert_page_command.dart';
import 'package:app_flowy/plugins/grid/presentation/grid_page.dart';
import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:flutter/material.dart';
const String kGridType = 'grid';
class GridNodeWidgetBuilder extends NodeWidgetBuilder<Node> {
@override
Widget build(NodeWidgetContext<Node> context) {
return _GridWidget(
key: context.node.key,
node: context.node,
editorState: context.editorState,
);
}
@override
NodeValidator<Node> get nodeValidator => (node) {
return node.attributes[kAppID] is String &&
node.attributes[kViewID] is String;
};
}
class _GridWidget extends StatefulWidget {
const _GridWidget({
Key? key,
required this.node,
required this.editorState,
}) : super(key: key);
final Node node;
final EditorState editorState;
@override
State<_GridWidget> createState() => _GridWidgetState();
}
class _GridWidgetState extends State<_GridWidget> {
@override
Widget build(BuildContext context) {
return BuiltInPageWidget(
node: widget.node,
editorState: widget.editorState,
builder: (viewPB) {
return GridPage(
key: ValueKey(viewPB.id),
view: viewPB,
);
},
);
}
}

View File

@ -105,8 +105,8 @@ class ShareActionList extends StatelessWidget {
break;
case ShareAction.copyLink:
NavigatorAlertDialog(
title: LocaleKeys.shareAction_workInProgress.tr())
.show(context);
title: LocaleKeys.shareAction_workInProgress.tr(),
).show(context);
break;
}
controller.close();
@ -128,5 +128,12 @@ class ShareActionWrapper extends ActionCell {
Widget? icon(Color iconColor) => null;
@override
String get name => inner.name;
String get name {
switch (inner) {
case ShareAction.markdown:
return LocaleKeys.shareAction_markdown.tr();
case ShareAction.copyLink:
return LocaleKeys.shareAction_copyLink.tr();
}
}
}

View File

@ -31,10 +31,6 @@ import 'widgets/shortcuts.dart';
import 'widgets/toolbar/grid_toolbar.dart';
class GridPage extends StatefulWidget {
final ViewPB view;
final GridController gridController;
final VoidCallback? onDeleted;
GridPage({
required this.view,
this.onDeleted,
@ -42,6 +38,10 @@ class GridPage extends StatefulWidget {
}) : gridController = GridController(view: view),
super(key: key);
final ViewPB view;
final GridController gridController;
final VoidCallback? onDeleted;
@override
State<GridPage> createState() => _GridPageState();
}

View File

@ -174,9 +174,13 @@ class HomeStackManager {
index: getIt<PluginSandbox>().indexOf(notifier.plugin.ty),
children: getIt<PluginSandbox>().supportPluginTypes.map((pluginType) {
if (pluginType == notifier.plugin.ty) {
return notifier.plugin.display
.buildWidget(PluginContext(onDeleted: onDeleted))
.padding(horizontal: 40, vertical: 28);
final pluginWidget = notifier.plugin.display
.buildWidget(PluginContext(onDeleted: onDeleted));
if (pluginType == PluginType.editor) {
return pluginWidget;
} else {
return pluginWidget.padding(horizontal: 40, vertical: 28);
}
} else {
return const BlankPage();
}