mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
feat: support text align and block align (#3292)
* feat: support text align and block align * test: add test
This commit is contained in:
@ -66,6 +66,7 @@ class _AppFlowyEditorPageState extends State<AppFlowyEditorPage> {
|
||||
numberedListItem,
|
||||
inlineMathEquationItem,
|
||||
linkItem,
|
||||
alignToolbarItem,
|
||||
buildTextColorItem(),
|
||||
buildHighlightColorItem(),
|
||||
];
|
||||
|
@ -0,0 +1,153 @@
|
||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
import 'package:appflowy_popover/appflowy_popover.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
final alignToolbarItem = ToolbarItem(
|
||||
id: 'editor.align',
|
||||
group: 4,
|
||||
isActive: onlyShowInTextType,
|
||||
builder: (context, editorState, highlightColor) {
|
||||
final selection = editorState.selection!;
|
||||
final nodes = editorState.getNodesInSelection(selection);
|
||||
|
||||
bool isSatisfyCondition(bool Function(Object? value) test) {
|
||||
return nodes.every(
|
||||
(n) => test(n.attributes[blockComponentAlign]),
|
||||
);
|
||||
}
|
||||
|
||||
bool isHighlight = false;
|
||||
FlowySvgData data = FlowySvgs.toolbar_align_left_s;
|
||||
if (isSatisfyCondition((value) => value == 'left')) {
|
||||
isHighlight = true;
|
||||
data = FlowySvgs.toolbar_align_left_s;
|
||||
} else if (isSatisfyCondition((value) => value == 'center')) {
|
||||
isHighlight = true;
|
||||
data = FlowySvgs.toolbar_align_center_s;
|
||||
} else if (isSatisfyCondition((value) => value == 'right')) {
|
||||
isHighlight = true;
|
||||
data = FlowySvgs.toolbar_align_right_s;
|
||||
}
|
||||
|
||||
final child = FlowySvg(
|
||||
data,
|
||||
size: const Size.square(16),
|
||||
color: isHighlight ? highlightColor : Colors.white,
|
||||
);
|
||||
return _AlignmentButtons(
|
||||
child: child,
|
||||
onAlignChanged: (align) async {
|
||||
await editorState.updateNode(
|
||||
selection,
|
||||
(node) => node.copyWith(
|
||||
attributes: {
|
||||
...node.attributes,
|
||||
blockComponentAlign: align,
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
class _AlignmentButtons extends StatefulWidget {
|
||||
const _AlignmentButtons({
|
||||
required this.child,
|
||||
required this.onAlignChanged,
|
||||
});
|
||||
|
||||
final Widget child;
|
||||
final Function(String align) onAlignChanged;
|
||||
|
||||
@override
|
||||
State<_AlignmentButtons> createState() => _AlignmentButtonsState();
|
||||
}
|
||||
|
||||
class _AlignmentButtonsState extends State<_AlignmentButtons> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AppFlowyPopover(
|
||||
windowPadding: const EdgeInsets.all(0),
|
||||
margin: const EdgeInsets.all(0),
|
||||
direction: PopoverDirection.bottomWithCenterAligned,
|
||||
offset: const Offset(0, 10),
|
||||
child: widget.child,
|
||||
popupBuilder: (_) => _AlignButtons(onAlignChanged: widget.onAlignChanged),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _AlignButtons extends StatelessWidget {
|
||||
const _AlignButtons({
|
||||
required this.onAlignChanged,
|
||||
});
|
||||
|
||||
final Function(String align) onAlignChanged;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SizedBox(
|
||||
height: 32,
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const HSpace(4),
|
||||
_AlignButton(
|
||||
icon: FlowySvgs.toolbar_align_left_s,
|
||||
onTap: () => onAlignChanged('left'),
|
||||
),
|
||||
const _Divider(),
|
||||
_AlignButton(
|
||||
icon: FlowySvgs.toolbar_align_center_s,
|
||||
onTap: () => onAlignChanged('center'),
|
||||
),
|
||||
const _Divider(),
|
||||
_AlignButton(
|
||||
icon: FlowySvgs.toolbar_align_right_s,
|
||||
onTap: () => onAlignChanged('right'),
|
||||
),
|
||||
const HSpace(4),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _AlignButton extends StatelessWidget {
|
||||
const _AlignButton({
|
||||
required this.icon,
|
||||
required this.onTap,
|
||||
});
|
||||
|
||||
final FlowySvgData icon;
|
||||
final VoidCallback onTap;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GestureDetector(
|
||||
onTap: onTap,
|
||||
child: FlowySvg(
|
||||
icon,
|
||||
size: const Size.square(16),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _Divider extends StatelessWidget {
|
||||
const _Divider();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: Container(
|
||||
width: 1,
|
||||
color: Colors.grey,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -3,7 +3,6 @@ import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
import 'package:appflowy_popover/appflowy_popover.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flowy_infra_ui/widget/ignore_parent_gesture.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
@ -19,6 +19,7 @@ export 'image/image_menu.dart';
|
||||
export 'image/image_selection_menu.dart';
|
||||
export 'inline_math_equation/inline_math_equation.dart';
|
||||
export 'inline_math_equation/inline_math_equation_toolbar_item.dart';
|
||||
export 'align_toolbar_item/align_toolbar_item.dart';
|
||||
export 'math_equation/math_equation_block_component.dart';
|
||||
export 'openai/widgets/auto_completion_node_widget.dart';
|
||||
export 'openai/widgets/smart_edit_node_widget.dart';
|
||||
|
@ -132,7 +132,7 @@ class _ToggleListBlockComponentWidgetState
|
||||
EdgeInsets get indentPadding => configuration.indentPadding(
|
||||
node,
|
||||
calculateTextDirection(
|
||||
defaultTextDirection: Directionality.maybeOf(context),
|
||||
layoutDirection: Directionality.maybeOf(context),
|
||||
),
|
||||
);
|
||||
|
||||
@ -148,7 +148,7 @@ class _ToggleListBlockComponentWidgetState
|
||||
@override
|
||||
Widget buildComponent(BuildContext context) {
|
||||
final textDirection = calculateTextDirection(
|
||||
defaultTextDirection: Directionality.maybeOf(context),
|
||||
layoutDirection: Directionality.maybeOf(context),
|
||||
);
|
||||
|
||||
Widget child = Container(
|
||||
|
Reference in New Issue
Block a user