feat: add alignment and indent/outdent toolbar item (#3927)

* chore: rename mobile list toolbar item

* feat: add alignment and indent/outdent toolbar item

* feat: adjust link menu on mobile platform
This commit is contained in:
Lucas.Xu 2023-11-13 18:08:39 +08:00 committed by GitHub
parent a63a7ea611
commit 3708a5b86a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 159 additions and 11 deletions

View File

@ -286,12 +286,15 @@ class _AppFlowyEditorPageState extends State<AppFlowyEditorPage> {
textDecorationMobileToolbarItem,
buildTextAndBackgroundColorMobileToolbarItem(),
headingMobileToolbarItem,
customListMobileToolbarItem,
mobileBlocksToolbarItem,
linkMobileToolbarItem,
dividerMobileToolbarItem,
imageMobileToolbarItem,
mathEquationMobileToolbarItem,
codeMobileToolbarItem,
mobileAlignToolbarItem,
mobileIndentToolbarItem,
mobileOutdentToolbarItem,
undoMobileToolbarItem,
redoMobileToolbarItem,
],

View File

@ -5,7 +5,7 @@ import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:flutter/material.dart';
final imageMobileToolbarItem = MobileToolbarItem.action(
itemIcon: const FlowySvg(FlowySvgs.m_toolbar_imae_lg),
itemIconBuilder: (_, __) => const FlowySvg(FlowySvgs.m_toolbar_imae_lg),
actionHandler: (editorState, selection) async {
final imagePlaceholderKey = GlobalKey<ImagePlaceholderState>();
await editorState.insertEmptyImageBlock(imagePlaceholderKey);

View File

@ -4,7 +4,8 @@ import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:flutter/material.dart';
final mathEquationMobileToolbarItem = MobileToolbarItem.action(
itemIcon: const SizedBox(width: 22, child: FlowySvg(FlowySvgs.math_lg)),
itemIconBuilder: (_, __) =>
const SizedBox(width: 22, child: FlowySvg(FlowySvgs.math_lg)),
actionHandler: (editorState, selection) async {
if (!selection.isCollapsed) {
return;

View File

@ -0,0 +1,103 @@
import 'package:appflowy/generated/flowy_svgs.g.dart';
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flutter/material.dart';
final mobileAlignToolbarItem = MobileToolbarItem.withMenu(
itemIconBuilder: (_, editorState) {
return onlyShowInTextType(editorState)
? const FlowySvg(
FlowySvgs.toolbar_align_center_s,
size: Size.square(32),
)
: null;
},
itemMenuBuilder: (editorState, selection, _) {
return _MobileAlignMenu(
editorState: editorState,
selection: selection,
);
},
);
class _MobileAlignMenu extends StatelessWidget {
const _MobileAlignMenu({
required this.editorState,
required this.selection,
});
final Selection selection;
final EditorState editorState;
@override
Widget build(BuildContext context) {
return GridView.count(
crossAxisCount: 3,
mainAxisSpacing: 8,
crossAxisSpacing: 8,
childAspectRatio: 3,
shrinkWrap: true,
children: [
_buildAlignmentButton(
context,
'left',
LocaleKeys.document_plugins_optionAction_left.tr(),
),
_buildAlignmentButton(
context,
'center',
LocaleKeys.document_plugins_optionAction_center.tr(),
),
_buildAlignmentButton(
context,
'right',
LocaleKeys.document_plugins_optionAction_right.tr(),
),
],
);
}
Widget _buildAlignmentButton(
BuildContext context,
String alignment,
String label,
) {
final nodes = editorState.getNodesInSelection(selection);
if (nodes.isEmpty) {
const SizedBox.shrink();
}
bool isSatisfyCondition(bool Function(Object? value) test) {
return nodes.every(
(n) => test(n.attributes[blockComponentAlign]),
);
}
final data = switch (alignment) {
'left' => FlowySvgs.toolbar_align_left_s,
'center' => FlowySvgs.toolbar_align_center_s,
'right' => FlowySvgs.toolbar_align_right_s,
_ => throw UnimplementedError(),
};
final isSelected = isSatisfyCondition((value) => value == alignment);
return MobileToolbarItemMenuBtn(
icon: FlowySvg(data, size: const Size.square(28)),
label: FlowyText(label),
isSelected: isSelected,
onPressed: () async {
await editorState.updateNode(
selection,
(node) => node.copyWith(
attributes: {
...node.attributes,
blockComponentAlign: alignment,
},
),
);
},
);
}
}

View File

@ -6,8 +6,9 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flutter/material.dart';
final customListMobileToolbarItem = MobileToolbarItem.withMenu(
itemIcon: const AFMobileIcon(afMobileIcons: AFMobileIcons.list),
final mobileBlocksToolbarItem = MobileToolbarItem.withMenu(
itemIconBuilder: (_, __) =>
const AFMobileIcon(afMobileIcons: AFMobileIcons.list),
itemMenuBuilder: (editorState, selection, _) {
return _MobileListMenu(
editorState: editorState,

View File

@ -0,0 +1,24 @@
import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:flutter/material.dart';
final mobileIndentToolbarItem = MobileToolbarItem.action(
itemIconBuilder: (_, editorState) {
return onlyShowInTextType(editorState)
? const Icon(Icons.format_indent_increase_rounded)
: null;
},
actionHandler: (editorState, selection) {
indentCommand.execute(editorState);
},
);
final mobileOutdentToolbarItem = MobileToolbarItem.action(
itemIconBuilder: (_, editorState) {
return onlyShowInTextType(editorState)
? const Icon(Icons.format_indent_decrease_rounded)
: null;
},
actionHandler: (editorState, selection) {
outdentCommand.execute(editorState);
},
);

View File

@ -26,7 +26,9 @@ export 'inline_math_equation/inline_math_equation.dart';
export 'inline_math_equation/inline_math_equation_toolbar_item.dart';
export 'math_equation/math_equation_block_component.dart';
export 'math_equation/mobile_math_equation_toolbar_item.dart';
export 'mobile_toolbar_item/list_mobile_toolbar_item.dart';
export 'mobile_toolbar_item/mobile_align_toolbar_item.dart';
export 'mobile_toolbar_item/mobile_blocks_toolbar_item.dart';
export 'mobile_toolbar_item/mobile_indent_toolbar_items.dart';
export 'openai/widgets/auto_completion_node_widget.dart';
export 'openai/widgets/smart_edit_node_widget.dart';
export 'openai/widgets/smart_edit_toolbar_item.dart';

View File

@ -2,7 +2,7 @@ import 'package:appflowy/generated/flowy_svgs.g.dart';
import 'package:appflowy_editor/appflowy_editor.dart';
final redoMobileToolbarItem = MobileToolbarItem.action(
itemIcon: const FlowySvg(FlowySvgs.m_redo_m),
itemIconBuilder: (_, __) => const FlowySvg(FlowySvgs.m_redo_m),
actionHandler: (editorState, selection) async {
editorState.undoManager.redo();
},

View File

@ -2,7 +2,7 @@ import 'package:appflowy/generated/flowy_svgs.g.dart';
import 'package:appflowy_editor/appflowy_editor.dart';
final undoMobileToolbarItem = MobileToolbarItem.action(
itemIcon: const FlowySvg(FlowySvgs.m_undo_m),
itemIconBuilder: (_, __) => const FlowySvg(FlowySvgs.m_undo_m),
actionHandler: (editorState, selection) async {
editorState.undoManager.undo();
},

View File

@ -7,6 +7,7 @@ import 'package:appflowy/plugins/inline_actions/inline_actions_menu.dart';
import 'package:appflowy/util/google_font_family_extension.dart';
import 'package:appflowy_editor/appflowy_editor.dart' hide Log;
import 'package:collection/collection.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:google_fonts/google_fonts.dart';
@ -264,6 +265,19 @@ class EditorStyleCustomizer {
);
}
// customize the link on mobile
final href = attributes[AppFlowyRichTextKeys.href] as String?;
if (PlatformExtension.isMobile && href != null) {
return TextSpan(
style: textSpan.style,
text: text.text,
recognizer: TapGestureRecognizer()
..onTap = () {
safeLaunchUrl(href);
},
);
}
return defaultTextSpanDecoratorForAttribute(
context,
node,

View File

@ -54,8 +54,8 @@ packages:
dependency: "direct main"
description:
path: "."
ref: "50117b6"
resolved-ref: "50117b6900e4b239603ee48f6f3e7b7bc603c865"
ref: "009115d"
resolved-ref: "009115da836616e9fb2d0abd327753809a78b983"
url: "https://github.com/AppFlowy-IO/appflowy-editor.git"
source: git
version: "1.5.2"

View File

@ -47,7 +47,7 @@ dependencies:
appflowy_editor:
git:
url: https://github.com/AppFlowy-IO/appflowy-editor.git
ref: 50117b6
ref: 009115d
appflowy_popover:
path: packages/appflowy_popover