Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: toggle format on mobile #587

Merged
merged 2 commits into from
Nov 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion lib/src/editor/toolbar/mobile/mobile_toolbar.dart
Original file line number Diff line number Diff line change
Expand Up @@ -240,8 +240,15 @@ class _ToolbarItemListView extends StatelessWidget {
return ListView.builder(
itemBuilder: (context, index) {
final toolbarItem = toolbarItems[index];
final icon = toolbarItem.itemIconBuilder(
context,
editorState,
);
if (icon == null) {
return const SizedBox.shrink();
}
return IconButton(
icon: toolbarItem.itemIcon,
icon: icon,
onPressed: () {
if (toolbarItem.hasMenu) {
// open /close current item menu through its parent widget(MobileToolbarWidget)
Expand Down
9 changes: 6 additions & 3 deletions lib/src/editor/toolbar/mobile/mobile_toolbar_item.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,23 @@ typedef MobileToolbarItemActionHandler = void Function(
class MobileToolbarItem {
/// Tool bar item that implements attribute directly(without opening menu)
const MobileToolbarItem.action({
required this.itemIcon,
required this.itemIconBuilder,
required this.actionHandler,
}) : hasMenu = false,
itemMenuBuilder = null;

/// Tool bar item that opens a menu to show options
const MobileToolbarItem.withMenu({
required this.itemIcon,
required this.itemIconBuilder,
required this.itemMenuBuilder,
}) : hasMenu = true,
actionHandler = null;

final bool hasMenu;
final Widget itemIcon;
final Widget? Function(
BuildContext context,
EditorState editorState,
) itemIconBuilder;
final MobileToolbarItemMenuBuilder? itemMenuBuilder;
final MobileToolbarItemActionHandler? actionHandler;
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import 'package:appflowy_editor/appflowy_editor.dart';

final codeMobileToolbarItem = MobileToolbarItem.action(
itemIcon: const AFMobileIcon(afMobileIcons: AFMobileIcons.code),
itemIconBuilder: (_, __) =>
const AFMobileIcon(afMobileIcons: AFMobileIcons.code),
actionHandler: (editorState, selection) =>
editorState.toggleAttribute(AppFlowyRichTextKeys.code),
);
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ MobileToolbarItem buildTextAndBackgroundColorMobileToolbarItem({
List<ColorOption>? backgroundColorOptions,
}) {
return MobileToolbarItem.withMenu(
itemIcon: const AFMobileIcon(afMobileIcons: AFMobileIcons.color),
itemIconBuilder: (_, __) =>
const AFMobileIcon(afMobileIcons: AFMobileIcons.color),
itemMenuBuilder: (editorState, selection, _) {
return _TextAndBackgroundColorMenu(
editorState,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import 'package:appflowy_editor/appflowy_editor.dart';

final dividerMobileToolbarItem = MobileToolbarItem.action(
itemIcon: const AFMobileIcon(afMobileIcons: AFMobileIcons.divider),
itemIconBuilder: (_, __) =>
const AFMobileIcon(afMobileIcons: AFMobileIcons.divider),
actionHandler: ((editorState, selection) {
// same as the [handler] of [dividerMenuItem] in Desktop
final selection = editorState.selection;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:flutter/material.dart';

final headingMobileToolbarItem = MobileToolbarItem.withMenu(
itemIcon: const AFMobileIcon(afMobileIcons: AFMobileIcons.heading),
itemIconBuilder: (_, __) =>
const AFMobileIcon(afMobileIcons: AFMobileIcons.heading),
itemMenuBuilder: (editorState, selection, _) {
return _HeadingMenu(
selection,
Expand Down Expand Up @@ -61,7 +62,10 @@ class _HeadingMenuState extends State<_HeadingMenu> {
width: (size.width - 4 * style.buttonSpacing) / 3,
),
child: MobileToolbarItemMenuBtn(
icon: AFMobileIcon(afMobileIcons: currentHeading.icon),
icon: AFMobileIcon(
afMobileIcons: currentHeading.icon,
size: 20,
),
label: Text(
currentHeading.label,
maxLines: 2,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:flutter/material.dart';

final linkMobileToolbarItem = MobileToolbarItem.withMenu(
itemIcon: const AFMobileIcon(
itemIconBuilder: (_, __) => const AFMobileIcon(
afMobileIcons: AFMobileIcons.link,
),
itemMenuBuilder: (editorState, selection, itemMenuService) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:flutter/material.dart';

final listMobileToolbarItem = MobileToolbarItem.withMenu(
itemIcon: const AFMobileIcon(afMobileIcons: AFMobileIcons.list),
itemIconBuilder: (_, __) =>
const AFMobileIcon(afMobileIcons: AFMobileIcons.list),
itemMenuBuilder: (editorState, selection, _) {
return _ListMenu(editorState, selection);
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import 'package:appflowy_editor/appflowy_editor.dart';

final quoteMobileToolbarItem = MobileToolbarItem.action(
itemIcon: const AFMobileIcon(afMobileIcons: AFMobileIcons.quote),
itemIconBuilder: (_, __) =>
const AFMobileIcon(afMobileIcons: AFMobileIcons.quote),
actionHandler: ((editorState, selection) {
final node = editorState.getNodeAtPath(selection.start.path)!;
final isQuote = node.type == QuoteBlockKeys.type;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
import 'package:flutter/material.dart';

final textDecorationMobileToolbarItem = MobileToolbarItem.withMenu(
itemIcon: const AFMobileIcon(afMobileIcons: AFMobileIcons.textDecoration),
itemIconBuilder: (_, __) =>
const AFMobileIcon(afMobileIcons: AFMobileIcons.textDecoration),
itemMenuBuilder: (editorState, selection, _) {
return _TextDecorationMenu(editorState, selection);
},
Expand All @@ -11,9 +12,8 @@
class _TextDecorationMenu extends StatefulWidget {
const _TextDecorationMenu(
this.editorState,
this.selection, {
Key? key,
}) : super(key: key);
this.selection,
);

final EditorState editorState;
final Selection selection;
Expand Down Expand Up @@ -50,12 +50,20 @@
final style = MobileToolbarStyle.of(context);
final btnList = textDecorations.map((currentDecoration) {
// Check current decoration is active or not
final nodes = widget.editorState.getNodesInSelection(widget.selection);
final isSelected = nodes.allSatisfyInSelection(widget.selection, (delta) {
return delta.everyAttributes(
(attributes) => attributes[currentDecoration.name] == true,
final selection = widget.selection;
final nodes = widget.editorState.getNodesInSelection(selection);
final bool isSelected;
if (selection.isCollapsed) {
isSelected = widget.editorState.toggledStyle.containsKey(
currentDecoration.name,

Check warning on line 58 in lib/src/editor/toolbar/mobile/toolbar_items/text_decoration_mobile_toolbar_item.dart

View check run for this annotation

Codecov / codecov/patch

lib/src/editor/toolbar/mobile/toolbar_items/text_decoration_mobile_toolbar_item.dart#L57-L58

Added lines #L57 - L58 were not covered by tests
);
});
} else {
isSelected = nodes.allSatisfyInSelection(selection, (delta) {
return delta.everyAttributes(
(attributes) => attributes[currentDecoration.name] == true,
);
});
}

return MobileToolbarItemMenuBtn(
icon: AFMobileIcon(
Expand All @@ -64,13 +72,9 @@
label: Text(currentDecoration.label),
isSelected: isSelected,
onPressed: () {
if (widget.selection.isCollapsed) {
// TODO(yijing): handle collapsed selection
} else {
setState(() {
widget.editorState.toggleAttribute(currentDecoration.name);
});
}
setState(() {
widget.editorState.toggleAttribute(currentDecoration.name);
});
},
);
}).toList();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import 'package:appflowy_editor/appflowy_editor.dart';

final todoListMobileToolbarItem = MobileToolbarItem.action(
itemIcon: const AFMobileIcon(afMobileIcons: AFMobileIcons.checkbox),
itemIconBuilder: (_, __) =>
const AFMobileIcon(afMobileIcons: AFMobileIcons.checkbox),
actionHandler: (editorState, selection) async {
final node = editorState.getNodeAtPath(selection.start.path)!;
final isTodoList = node.type == TodoListBlockKeys.type;
Expand Down
6 changes: 3 additions & 3 deletions test/mobile/toolbar/mobile/mobile_toolbar_item_test.dart
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';

void main() {
group('MobileToolbarItem', () {
test('action item should not have a menu', () {
final item = MobileToolbarItem.action(
itemIcon: const Icon(Icons.format_bold),
itemIconBuilder: (_, __) => const Icon(Icons.format_bold),
actionHandler: (editorState, selection) {},
);

Expand All @@ -15,7 +15,7 @@ void main() {

test('menu item should have a menu', () {
final item = MobileToolbarItem.withMenu(
itemIcon: const Icon(Icons.format_color_text),
itemIconBuilder: (_, __) => const Icon(Icons.format_color_text),
itemMenuBuilder: (editorState, selection, _) {
return Container();
},
Expand Down