mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
feat: move all the heading toolbar items into a popup menu (#5890)
* chore: udpate translations * feat: move all the heading items into a popup menu * chore: add arrow down icon after heading toolbar items * fix: compile * chore: adjust heading toolbar style
This commit is contained in:
parent
e279ad1cc7
commit
98b7882d43
@ -130,8 +130,8 @@ class _AppFlowyEditorPageState extends State<AppFlowyEditorPage> {
|
||||
final List<ToolbarItem> toolbarItems = [
|
||||
smartEditItem..isActive = onlyShowInSingleTextTypeSelectionAndExcludeTable,
|
||||
paragraphItem..isActive = onlyShowInSingleTextTypeSelectionAndExcludeTable,
|
||||
...headingItems
|
||||
..forEach((e) => e.isActive = onlyShowInSingleSelectionAndTextType),
|
||||
headingsToolbarItem
|
||||
..isActive = onlyShowInSingleTextTypeSelectionAndExcludeTable,
|
||||
...markdownFormatItems..forEach((e) => e.isActive = showInAnyTextType),
|
||||
quoteItem..isActive = onlyShowInSingleTextTypeSelectionAndExcludeTable,
|
||||
bulletedListItem
|
||||
|
@ -94,9 +94,9 @@ class _AlignmentButtonsState extends State<_AlignmentButtons> {
|
||||
windowPadding: const EdgeInsets.all(0),
|
||||
margin: const EdgeInsets.symmetric(vertical: 2.0),
|
||||
direction: PopoverDirection.bottomWithCenterAligned,
|
||||
offset: const Offset(0, 12),
|
||||
offset: const Offset(0, 10),
|
||||
decorationColor: Theme.of(context).colorScheme.onTertiary,
|
||||
borderRadius: const BorderRadius.all(Radius.circular(4)),
|
||||
borderRadius: BorderRadius.circular(6.0),
|
||||
popupBuilder: (_) {
|
||||
keepEditorFocusNotifier.increase();
|
||||
return _AlignButtons(onAlignChanged: widget.onAlignChanged);
|
||||
|
@ -0,0 +1,207 @@
|
||||
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:appflowy_popover/appflowy_popover.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
final _headingData = [
|
||||
(FlowySvgs.h1_s, LocaleKeys.editor_heading1.tr()),
|
||||
(FlowySvgs.h2_s, LocaleKeys.editor_heading2.tr()),
|
||||
(FlowySvgs.h3_s, LocaleKeys.editor_heading3.tr()),
|
||||
];
|
||||
|
||||
final headingsToolbarItem = ToolbarItem(
|
||||
id: 'editor.headings',
|
||||
group: 1,
|
||||
isActive: onlyShowInTextType,
|
||||
builder: (context, editorState, highlightColor, _, __) {
|
||||
final selection = editorState.selection!;
|
||||
final node = editorState.getNodeAtPath(selection.start.path)!;
|
||||
final delta = (node.delta ?? Delta()).toJson();
|
||||
int level = node.attributes[HeadingBlockKeys.level] ?? 1;
|
||||
final isHighlight =
|
||||
node.type == HeadingBlockKeys.type && (level >= 1 && level <= 3);
|
||||
// only supports the level 1 - 3 in the toolbar, ignore the other levels
|
||||
level = level.clamp(1, 3);
|
||||
|
||||
final svg = _headingData[level - 1].$1;
|
||||
final message = _headingData[level - 1].$2;
|
||||
|
||||
final child = FlowyTooltip(
|
||||
message: message,
|
||||
preferBelow: false,
|
||||
child: Row(
|
||||
children: [
|
||||
FlowySvg(
|
||||
svg,
|
||||
size: const Size.square(18),
|
||||
color: isHighlight ? highlightColor : Colors.white,
|
||||
),
|
||||
const HSpace(2.0),
|
||||
const FlowySvg(
|
||||
FlowySvgs.arrow_down_s,
|
||||
size: Size.square(12),
|
||||
color: Colors.grey,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
return _HeadingPopup(
|
||||
currentLevel: isHighlight ? level : -1,
|
||||
highlightColor: highlightColor,
|
||||
child: child,
|
||||
onLevelChanged: (level) async {
|
||||
await editorState.formatNode(
|
||||
selection,
|
||||
(node) => node.copyWith(
|
||||
type: isHighlight ? ParagraphBlockKeys.type : HeadingBlockKeys.type,
|
||||
attributes: {
|
||||
HeadingBlockKeys.level: level,
|
||||
blockComponentBackgroundColor:
|
||||
node.attributes[blockComponentBackgroundColor],
|
||||
blockComponentTextDirection:
|
||||
node.attributes[blockComponentTextDirection],
|
||||
blockComponentDelta: delta,
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
class _HeadingPopup extends StatelessWidget {
|
||||
const _HeadingPopup({
|
||||
required this.currentLevel,
|
||||
required this.highlightColor,
|
||||
required this.onLevelChanged,
|
||||
required this.child,
|
||||
});
|
||||
|
||||
final int currentLevel;
|
||||
final Color highlightColor;
|
||||
final Function(int level) onLevelChanged;
|
||||
final Widget child;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AppFlowyPopover(
|
||||
windowPadding: const EdgeInsets.all(0),
|
||||
margin: const EdgeInsets.symmetric(vertical: 2.0),
|
||||
direction: PopoverDirection.bottomWithCenterAligned,
|
||||
offset: const Offset(0, 10),
|
||||
decorationColor: Theme.of(context).colorScheme.onTertiary,
|
||||
borderRadius: BorderRadius.circular(6.0),
|
||||
popupBuilder: (_) {
|
||||
keepEditorFocusNotifier.increase();
|
||||
return _HeadingButtons(
|
||||
currentLevel: currentLevel,
|
||||
highlightColor: highlightColor,
|
||||
onLevelChanged: onLevelChanged,
|
||||
);
|
||||
},
|
||||
onClose: () {
|
||||
keepEditorFocusNotifier.decrease();
|
||||
},
|
||||
child: FlowyButton(
|
||||
useIntrinsicWidth: true,
|
||||
hoverColor: Colors.grey.withOpacity(0.3),
|
||||
text: child,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _HeadingButtons extends StatelessWidget {
|
||||
const _HeadingButtons({
|
||||
required this.highlightColor,
|
||||
required this.currentLevel,
|
||||
required this.onLevelChanged,
|
||||
});
|
||||
|
||||
final int currentLevel;
|
||||
final Color highlightColor;
|
||||
final Function(int level) onLevelChanged;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SizedBox(
|
||||
height: 28,
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const HSpace(4),
|
||||
..._headingData.mapIndexed((index, data) {
|
||||
final svg = data.$1;
|
||||
final message = data.$2;
|
||||
return [
|
||||
_HeadingButton(
|
||||
icon: svg,
|
||||
tooltip: message,
|
||||
onTap: () => onLevelChanged(index + 1),
|
||||
isHighlight: index + 1 == currentLevel,
|
||||
highlightColor: highlightColor,
|
||||
),
|
||||
index != _headingData.length - 1
|
||||
? const _Divider()
|
||||
: const SizedBox.shrink(),
|
||||
];
|
||||
}).flattened,
|
||||
const HSpace(4),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _HeadingButton extends StatelessWidget {
|
||||
const _HeadingButton({
|
||||
required this.icon,
|
||||
required this.tooltip,
|
||||
required this.onTap,
|
||||
required this.highlightColor,
|
||||
required this.isHighlight,
|
||||
});
|
||||
|
||||
final Color highlightColor;
|
||||
final FlowySvgData icon;
|
||||
final String tooltip;
|
||||
final VoidCallback onTap;
|
||||
final bool isHighlight;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return FlowyButton(
|
||||
useIntrinsicWidth: true,
|
||||
hoverColor: Colors.grey.withOpacity(0.3),
|
||||
onTap: onTap,
|
||||
text: FlowyTooltip(
|
||||
message: tooltip,
|
||||
preferBelow: true,
|
||||
child: FlowySvg(
|
||||
icon,
|
||||
size: const Size.square(18),
|
||||
color: isHighlight ? highlightColor : Colors.white,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _Divider extends StatelessWidget {
|
||||
const _Divider();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(4),
|
||||
child: Container(
|
||||
width: 1,
|
||||
color: Colors.grey,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -14,15 +14,17 @@ export 'database/inline_database_menu_item.dart';
|
||||
export 'database/referenced_database_menu_item.dart';
|
||||
export 'error/error_block_component_builder.dart';
|
||||
export 'extensions/flowy_tint_extension.dart';
|
||||
export 'file/file_block.dart';
|
||||
export 'find_and_replace/find_and_replace_menu.dart';
|
||||
export 'font/customize_font_toolbar_item.dart';
|
||||
export 'header/cover_editor_bloc.dart';
|
||||
export 'header/custom_cover_picker.dart';
|
||||
export 'header/document_header_node_widget.dart';
|
||||
export 'heading/heading_toolbar_item.dart';
|
||||
export 'image/custom_image_block_component/image_menu.dart';
|
||||
export 'image/multi_image_block_component/multi_image_menu.dart';
|
||||
export 'image/image_selection_menu.dart';
|
||||
export 'image/mobile_image_toolbar_item.dart';
|
||||
export 'image/multi_image_block_component/multi_image_menu.dart';
|
||||
export 'inline_math_equation/inline_math_equation.dart';
|
||||
export 'inline_math_equation/inline_math_equation_toolbar_item.dart';
|
||||
export 'link_preview/custom_link_preview.dart';
|
||||
@ -53,4 +55,3 @@ export 'table/table_option_action.dart';
|
||||
export 'todo_list/todo_list_icon.dart';
|
||||
export 'toggle/toggle_block_component.dart';
|
||||
export 'toggle/toggle_block_shortcut_event.dart';
|
||||
export 'file/file_block.dart';
|
||||
|
@ -1479,7 +1479,7 @@
|
||||
"autoGeneratorRewrite": "Rewrite",
|
||||
"smartEdit": "AI Assistants",
|
||||
"aI": "AI",
|
||||
"smartEditFixSpelling": "Fix spelling",
|
||||
"smartEditFixSpelling": "Fix spelling & grammar",
|
||||
"warning": "⚠️ AI responses can be inaccurate or misleading.",
|
||||
"smartEditSummarize": "Summarize",
|
||||
"smartEditImproveWriting": "Improve writing",
|
||||
@ -2376,4 +2376,4 @@
|
||||
"commentAddedSuccessfully": "Comment added successfully.",
|
||||
"commentAddedSuccessTip": "You've just added or replied to a comment. Would you like to jump to the top to see the latest comments?"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user