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,
|
textDecorationMobileToolbarItem,
|
||||||
buildTextAndBackgroundColorMobileToolbarItem(),
|
buildTextAndBackgroundColorMobileToolbarItem(),
|
||||||
headingMobileToolbarItem,
|
headingMobileToolbarItem,
|
||||||
customListMobileToolbarItem,
|
mobileBlocksToolbarItem,
|
||||||
linkMobileToolbarItem,
|
linkMobileToolbarItem,
|
||||||
dividerMobileToolbarItem,
|
dividerMobileToolbarItem,
|
||||||
imageMobileToolbarItem,
|
imageMobileToolbarItem,
|
||||||
mathEquationMobileToolbarItem,
|
mathEquationMobileToolbarItem,
|
||||||
codeMobileToolbarItem,
|
codeMobileToolbarItem,
|
||||||
|
mobileAlignToolbarItem,
|
||||||
|
mobileIndentToolbarItem,
|
||||||
|
mobileOutdentToolbarItem,
|
||||||
undoMobileToolbarItem,
|
undoMobileToolbarItem,
|
||||||
redoMobileToolbarItem,
|
redoMobileToolbarItem,
|
||||||
],
|
],
|
||||||
|
@ -5,7 +5,7 @@ import 'package:appflowy_editor/appflowy_editor.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
final imageMobileToolbarItem = MobileToolbarItem.action(
|
final imageMobileToolbarItem = MobileToolbarItem.action(
|
||||||
itemIcon: const FlowySvg(FlowySvgs.m_toolbar_imae_lg),
|
itemIconBuilder: (_, __) => const FlowySvg(FlowySvgs.m_toolbar_imae_lg),
|
||||||
actionHandler: (editorState, selection) async {
|
actionHandler: (editorState, selection) async {
|
||||||
final imagePlaceholderKey = GlobalKey<ImagePlaceholderState>();
|
final imagePlaceholderKey = GlobalKey<ImagePlaceholderState>();
|
||||||
await editorState.insertEmptyImageBlock(imagePlaceholderKey);
|
await editorState.insertEmptyImageBlock(imagePlaceholderKey);
|
||||||
|
@ -4,7 +4,8 @@ import 'package:appflowy_editor/appflowy_editor.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
final mathEquationMobileToolbarItem = MobileToolbarItem.action(
|
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 {
|
actionHandler: (editorState, selection) async {
|
||||||
if (!selection.isCollapsed) {
|
if (!selection.isCollapsed) {
|
||||||
return;
|
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:flowy_infra_ui/flowy_infra_ui.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
final customListMobileToolbarItem = MobileToolbarItem.withMenu(
|
final mobileBlocksToolbarItem = MobileToolbarItem.withMenu(
|
||||||
itemIcon: const AFMobileIcon(afMobileIcons: AFMobileIcons.list),
|
itemIconBuilder: (_, __) =>
|
||||||
|
const AFMobileIcon(afMobileIcons: AFMobileIcons.list),
|
||||||
itemMenuBuilder: (editorState, selection, _) {
|
itemMenuBuilder: (editorState, selection, _) {
|
||||||
return _MobileListMenu(
|
return _MobileListMenu(
|
||||||
editorState: editorState,
|
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 'inline_math_equation/inline_math_equation_toolbar_item.dart';
|
||||||
export 'math_equation/math_equation_block_component.dart';
|
export 'math_equation/math_equation_block_component.dart';
|
||||||
export 'math_equation/mobile_math_equation_toolbar_item.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/auto_completion_node_widget.dart';
|
||||||
export 'openai/widgets/smart_edit_node_widget.dart';
|
export 'openai/widgets/smart_edit_node_widget.dart';
|
||||||
export 'openai/widgets/smart_edit_toolbar_item.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';
|
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||||
|
|
||||||
final redoMobileToolbarItem = MobileToolbarItem.action(
|
final redoMobileToolbarItem = MobileToolbarItem.action(
|
||||||
itemIcon: const FlowySvg(FlowySvgs.m_redo_m),
|
itemIconBuilder: (_, __) => const FlowySvg(FlowySvgs.m_redo_m),
|
||||||
actionHandler: (editorState, selection) async {
|
actionHandler: (editorState, selection) async {
|
||||||
editorState.undoManager.redo();
|
editorState.undoManager.redo();
|
||||||
},
|
},
|
||||||
|
@ -2,7 +2,7 @@ import 'package:appflowy/generated/flowy_svgs.g.dart';
|
|||||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||||
|
|
||||||
final undoMobileToolbarItem = MobileToolbarItem.action(
|
final undoMobileToolbarItem = MobileToolbarItem.action(
|
||||||
itemIcon: const FlowySvg(FlowySvgs.m_undo_m),
|
itemIconBuilder: (_, __) => const FlowySvg(FlowySvgs.m_undo_m),
|
||||||
actionHandler: (editorState, selection) async {
|
actionHandler: (editorState, selection) async {
|
||||||
editorState.undoManager.undo();
|
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/util/google_font_family_extension.dart';
|
||||||
import 'package:appflowy_editor/appflowy_editor.dart' hide Log;
|
import 'package:appflowy_editor/appflowy_editor.dart' hide Log;
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
|
import 'package:flutter/gestures.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:google_fonts/google_fonts.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(
|
return defaultTextSpanDecoratorForAttribute(
|
||||||
context,
|
context,
|
||||||
node,
|
node,
|
||||||
|
@ -54,8 +54,8 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
path: "."
|
path: "."
|
||||||
ref: "50117b6"
|
ref: "009115d"
|
||||||
resolved-ref: "50117b6900e4b239603ee48f6f3e7b7bc603c865"
|
resolved-ref: "009115da836616e9fb2d0abd327753809a78b983"
|
||||||
url: "https://github.com/AppFlowy-IO/appflowy-editor.git"
|
url: "https://github.com/AppFlowy-IO/appflowy-editor.git"
|
||||||
source: git
|
source: git
|
||||||
version: "1.5.2"
|
version: "1.5.2"
|
||||||
|
@ -47,7 +47,7 @@ dependencies:
|
|||||||
appflowy_editor:
|
appflowy_editor:
|
||||||
git:
|
git:
|
||||||
url: https://github.com/AppFlowy-IO/appflowy-editor.git
|
url: https://github.com/AppFlowy-IO/appflowy-editor.git
|
||||||
ref: 50117b6
|
ref: 009115d
|
||||||
|
|
||||||
appflowy_popover:
|
appflowy_popover:
|
||||||
path: packages/appflowy_popover
|
path: packages/appflowy_popover
|
||||||
|
Loading…
Reference in New Issue
Block a user