mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
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:
parent
a63a7ea611
commit
3708a5b86a
@ -286,12 +286,15 @@ class _AppFlowyEditorPageState extends State<AppFlowyEditorPage> {
|
||||
textDecorationMobileToolbarItem,
|
||||
buildTextAndBackgroundColorMobileToolbarItem(),
|
||||
headingMobileToolbarItem,
|
||||
customListMobileToolbarItem,
|
||||
mobileBlocksToolbarItem,
|
||||
linkMobileToolbarItem,
|
||||
dividerMobileToolbarItem,
|
||||
imageMobileToolbarItem,
|
||||
mathEquationMobileToolbarItem,
|
||||
codeMobileToolbarItem,
|
||||
mobileAlignToolbarItem,
|
||||
mobileIndentToolbarItem,
|
||||
mobileOutdentToolbarItem,
|
||||
undoMobileToolbarItem,
|
||||
redoMobileToolbarItem,
|
||||
],
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
@ -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,
|
@ -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);
|
||||
},
|
||||
);
|
@ -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';
|
||||
|
@ -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();
|
||||
},
|
||||
|
@ -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();
|
||||
},
|
||||
|
@ -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,
|
||||
|
@ -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"
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user