feat: migrate link menu and color picker (#4)

* chore: move selectionRects to editorState

* chore: delete unused attributeKey font

* feat: refactor showLinkMenu and LinkMenu

* feat: replace old linkMenu

* feat: add text color toolbar item
- separate text color and highlight color item in toolbar
- renaming in BuiltInAttributeKey(color -> textColor, backgroundColor -> highlightColor)
- add text color and highlight color attribute in example.json
- add icons for text color and highlight color in the toolbar

* feat: add highlight color item in toolbar
- add clear highlight color button
- add onDismiss in ColorPicker to remove the overlay after remove highlight color

* chore: delete old ColorPicker

* test: migrate link menu test

* chore: remove editorStyle from color menu and color picker

* test: add color picker test

* feat: remove editor style from link menu
- add utils function and widgets for overlay
- organize utils folder
- add constant text into AppFlowyEditorLocalizations
- improve pop up menu style

* feat: add show link menu command shortcut
- refactor CommandShortcutEventHandler
- update related callback

* feat: add showTextColorMenuCommand and showHighlightColorMenuCommand

* fix: fix merge error in example.json

* refactor: reorganize the file hierarchy

* refactor: migrate format_style_handler_test.dart
- delete format_style_handler_test.dart
- migrate to markdown_commands_test.dart
- move highlight test outside of markdown_commands_test

* feat: delete show menu type command shortcut and their tooltip

* fix: fix tooltip text

* chore: delete context from CommandShortcutEventHandler

* feat: add command shortcut for link menu

* test: add show link menu command test
This commit is contained in:
Yijing Huang 2023-05-10 22:26:52 -05:00 • committed by GitHub
commit 5c43d1daad
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
49 changed files with 1356 additions and 1379 deletions

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="48" viewBox="0 96 960 960" width="48"><path d="M757 780 316 339l164-163 225 222q44 44 69.5 101T800 622q0 39-12 81t-31 77Zm49 220L676 870q-38 31-90 48.5T480 936q-132 0-226-91.5T160 621q0-57 17-106t53-91L57 251l43-43 749 749-43 43Z" fill="white"/></svg>

After

Width:  |  Height:  |  Size: 299 B

View file

@ -1,5 +0,0 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M13 7.87829V5.125C13 4.82663 12.8946 4.54048 12.7071 4.3295C12.5196 4.11853 12.2652 4 12 4H4C3.73478 4 3.48043 4.11853 3.29289 4.3295C3.10536 4.54048 3 4.82663 3 5.125V10.875C3 11.1734 3.10536 11.4595 3.29289 11.6705C3.48043 11.8815 3.73478 12 4 12H8.44737" stroke="white" stroke-linecap="round" stroke-linejoin="round"/>
<rect width="1" height="4" rx="0.5" transform="matrix(-1 0 0 1 13.5 10)" fill="white"/>
<rect width="1" height="4" rx="0.5" transform="matrix(1.19249e-08 -1 -1 -1.19249e-08 15 12.5)" fill="white"/>
</svg>

Before

Width:  |  Height:  |  Size: 632 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="48" viewBox="0 96 960 960" width="48"><path d="M80 1056V935h800v121H80Zm343-631 200 200-172 172q-19 19-52.5 19T346 797l-11-11-43 43H137l120-120-4-4q-22-22-21.5-55.5T254 594l169-169Zm43-43 189-189q17-17 43-17t43 17l112 112q17 17 16.5 45.5T852 396L666 582 466 382Z" fill="white"/></svg>

After

Width:  |  Height:  |  Size: 332 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="48" viewBox="0 96 960 960" width="48"><path d="M90 1056V916h780v140H90Zm130-280 220-560h80l220 560h-75l-57-150H352l-57 150h-75Zm156-214h208L482 291h-4L376 562Z" fill="white"/></svg>

After

Width:  |  Height:  |  Size: 229 B

View file

@ -129,7 +129,8 @@
"attributes": { "attributes": {
"italic": true, "italic": true,
"bold": true, "bold": true,
"backgroundColor": "0x6000BCF0" "textColor": "0xffD70040",
"highlightColor": "0x6000BCF0"
} }
}, },
{ {

View file

@ -48,7 +48,8 @@ class SimpleEditor extends StatelessWidget {
numberedListItem, numberedListItem,
placeholderItem, placeholderItem,
linkItem, linkItem,
colorItem, textColorItem,
highlightColorItem
], ],
editorState: editorState, editorState: editorState,
scrollController: scrollController, scrollController: scrollController,

View file

@ -2,7 +2,7 @@
/// Supported partial rendering types: /// Supported partial rendering types:
/// bold, italic, /// bold, italic,
/// underline, strikethrough, /// underline, strikethrough,
/// color, font, /// textColor, highlightColor,
/// href /// href
/// ///
/// Supported global rendering types: /// Supported global rendering types:
@ -16,9 +16,9 @@ class BuiltInAttributeKey {
static String italic = 'italic'; static String italic = 'italic';
static String underline = 'underline'; static String underline = 'underline';
static String strikethrough = 'strikethrough'; static String strikethrough = 'strikethrough';
static String color = 'color'; static String textColor = 'textColor';
static String backgroundColor = 'backgroundColor'; static String highlightColor = 'highlightColor';
static String font = 'font'; static String code = 'code';
static String href = 'href'; static String href = 'href';
static String subtype = 'subtype'; static String subtype = 'subtype';
@ -30,12 +30,10 @@ class BuiltInAttributeKey {
static String h5 = 'h5'; static String h5 = 'h5';
static String h6 = 'h6'; static String h6 = 'h6';
static String checkbox = 'checkbox';
static String bulletedList = 'bulleted-list'; static String bulletedList = 'bulleted-list';
static String numberList = 'number-list'; static String numberList = 'number-list';
static String quote = 'quote'; static String quote = 'quote';
static String checkbox = 'checkbox';
static String code = 'code';
static String number = 'number'; static String number = 'number';
static List<String> partialStyleKeys = [ static List<String> partialStyleKeys = [
@ -43,8 +41,8 @@ class BuiltInAttributeKey {
BuiltInAttributeKey.italic, BuiltInAttributeKey.italic,
BuiltInAttributeKey.underline, BuiltInAttributeKey.underline,
BuiltInAttributeKey.strikethrough, BuiltInAttributeKey.strikethrough,
BuiltInAttributeKey.backgroundColor, BuiltInAttributeKey.highlightColor,
BuiltInAttributeKey.color, BuiltInAttributeKey.textColor,
BuiltInAttributeKey.href, BuiltInAttributeKey.href,
BuiltInAttributeKey.code, BuiltInAttributeKey.code,
]; ];

View file

@ -45,6 +45,8 @@ class FullScreenOverlayEntry {
right: right, right: right,
child: IgnoreParentPointer( child: IgnoreParentPointer(
child: Material( child: Material(
// Avoid background color behind the child, so the child can fully control the overlay style
color: Colors.transparent,
child: builder(context), child: builder(context),
), ),
), ),

View file

@ -1,5 +1,5 @@
export 'shortcuts/character_shortcut_events/character_shortcut_events.dart'; export 'shortcuts/character_shortcut_events/character_shortcut_events.dart';
export 'shortcuts/command_shortcut_events.dart'; export 'shortcuts/command_shortcut_events/command_shortcut_events.dart';
export 'shortcuts/character_shortcut_event.dart'; export 'shortcuts/character_shortcut_event.dart';
export 'shortcuts/command_shortcut_event.dart'; export 'shortcuts/command_shortcut_event.dart';

View file

@ -1,2 +0,0 @@
export 'character_shortcut_events/insert_newline.dart';
export 'character_shortcut_events/slash_command.dart';

View file

@ -1,3 +1,5 @@
export 'insert_newline.dart'; export 'insert_newline.dart';
export 'slash_command.dart'; export 'slash_command.dart';
export 'markdown_syntax_character_shortcut_events.dart'; export 'markdown_syntax_character_shortcut_events.dart';
export 'format_by_wrapping_with_single_character/format_by_wrapping_with_single_character.dart';
export 'format_by_wrapping_with_double_character/format_by_wrapping_with_double_character.dart';

View file

@ -0,0 +1,3 @@
export 'format_bold.dart';
export 'format_strikethrough.dart';
export 'handle_format_by_wrapping_with_double_character.dart';

View file

@ -0,0 +1,4 @@
export 'format_code.dart';
export 'format_italic.dart';
export 'format_strikethrough.dart';
export 'handle_format_by_wrapping_with_single_character.dart';

View file

@ -1,13 +0,0 @@
export 'command_shortcut_events/backspace_command.dart';
export 'command_shortcut_events/arrow_left_command.dart';
export 'command_shortcut_events/arrow_right_command.dart';
export 'command_shortcut_events/home_command.dart';
export 'command_shortcut_events/end_command.dart';
export 'command_shortcut_events/arrow_up_command.dart';
export 'command_shortcut_events/arrow_down_command.dart';
export 'command_shortcut_events/escape_command.dart';
export 'command_shortcut_events/markdown_commands.dart';
export 'command_shortcut_events/page_up_command.dart';
export 'command_shortcut_events/page_down_command.dart';
export 'command_shortcut_events/undo_redo_command.dart';
export 'command_shortcut_events/select_all_command.dart';

View file

@ -0,0 +1,14 @@
export 'backspace_command.dart';
export 'arrow_left_command.dart';
export 'arrow_right_command.dart';
export 'home_command.dart';
export 'end_command.dart';
export 'arrow_up_command.dart';
export 'arrow_down_command.dart';
export 'escape_command.dart';
export 'markdown_commands.dart';
export 'page_up_command.dart';
export 'page_down_command.dart';
export 'undo_redo_command.dart';
export 'select_all_command.dart';
export 'show_link_menu_command.dart';

View file

@ -15,10 +15,7 @@ final List<CommandShortcutEvent> toggleMarkdownCommands = [
/// Cmd / Ctrl + I: toggle italic /// Cmd / Ctrl + I: toggle italic
/// Cmd / Ctrl + U: toggle underline /// Cmd / Ctrl + U: toggle underline
/// Cmd / Ctrl + Shift + S: toggle strikethrough /// Cmd / Ctrl + Shift + S: toggle strikethrough
/// Cmd / Ctrl + Shift + H: toggle highlight
/// Cmd / Ctrl + k: link
/// Cmd / Ctrl + E: code /// Cmd / Ctrl + E: code
///
/// - support /// - support
/// - desktop /// - desktop
/// - web /// - web
@ -58,8 +55,6 @@ final CommandShortcutEvent toggleCodeCommand = CommandShortcutEvent(
handler: (editorState) => _toggleAttribute(editorState, 'code'), handler: (editorState) => _toggleAttribute(editorState, 'code'),
); );
// TODO: implement the link and color command
KeyEventResult _toggleAttribute( KeyEventResult _toggleAttribute(
EditorState editorState, EditorState editorState,
String key, String key,

View file

@ -0,0 +1,47 @@
import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:flutter/material.dart';
/// Cmd / Ctrl + K: show link menu
/// - support
/// - desktop
/// - web
final CommandShortcutEvent showLinkMenuCommand = CommandShortcutEvent(
key: 'link menu',
command: 'ctrl+k',
macOSCommand: 'cmd+k',
handler: (editorState) => _showLinkMenu(editorState),
);
KeyEventResult _showLinkMenu(
EditorState editorState,
) {
if (PlatformExtension.isMobile) {
assert(false, 'homeCommand is not supported on mobile platform.');
return KeyEventResult.ignored;
}
final selection = editorState.selection;
if (selection == null || selection.isCollapsed) {
return KeyEventResult.ignored;
}
final context =
editorState.getNodeAtPath(selection.end.path)?.key.currentContext;
if (context == null) {
return KeyEventResult.ignored;
}
final nodes = editorState.getNodesInSelection(selection);
final isHref = nodes.allSatisfyInSelection(selection, (delta) {
return delta.everyAttributes(
(attributes) => attributes['href'] != null,
);
});
showLinkMenu(
context,
editorState,
selection,
isHref,
);
return KeyEventResult.handled;
}

View file

@ -118,7 +118,7 @@ class _FloatingToolbarState extends State<FloatingToolbar> {
} }
void _showToolbar() { void _showToolbar() {
final rects = _computeSelectionRects(); final rects = editorState.selectionRects;
if (rects.isEmpty) { if (rects.isEmpty) {
return; return;
} }
@ -144,52 +144,6 @@ class _FloatingToolbarState extends State<FloatingToolbar> {
return _toolbarWidget!; return _toolbarWidget!;
} }
/// Compute the rects of the selection.
List<Rect> _computeSelectionRects() {
final selection = editorState.selection;
if (selection == null || selection.isCollapsed) {
return [];
}
final nodes = editorState.getNodesInSelection(selection);
final rects = <Rect>[];
for (final node in nodes) {
final selectable = node.selectable;
if (selectable == null) {
continue;
}
final nodeRects = selectable.getRectsInSelection(selection);
if (nodeRects.isEmpty) {
continue;
}
final renderBox = node.renderBox;
if (renderBox == null) {
continue;
}
for (final rect in nodeRects) {
final globalOffset = renderBox.localToGlobal(rect.topLeft);
rects.add(globalOffset & rect.size);
}
}
/*
final rects = nodes
.map(
(node) => node.selectable
?.getRectsInSelection(selection)
.map(
(rect) => node.renderBox?.localToGlobal(rect.topLeft),
)
.whereNotNull(),
)
.whereNotNull()
.expand((element) => element)
.toList();
*/
return rects;
}
Offset _findSuitableOffset(Iterable<Offset> offsets) { Offset _findSuitableOffset(Iterable<Offset> offsets) {
assert(offsets.isNotEmpty); assert(offsets.isNotEmpty);

View file

@ -0,0 +1,4 @@
export 'highlight_color_toolbar_item.dart';
export 'text_color_toolbar_item.dart';
export 'color_picker.dart';
export 'color_menu.dart';

Some files were not shown because too many files have changed in this diff Show more