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:
parent
11f557d4dd
commit
5c43d1daad
49 changed files with 1356 additions and 1379 deletions
1
assets/images/toolbar/clear_highlight_color.svg
Normal file
1
assets/images/toolbar/clear_highlight_color.svg
Normal 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 |
|
|
@ -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 |
1
assets/images/toolbar/highlight_color.svg
Normal file
1
assets/images/toolbar/highlight_color.svg
Normal 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 |
1
assets/images/toolbar/text_color.svg
Normal file
1
assets/images/toolbar/text_color.svg
Normal 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 |
|
|
@ -129,7 +129,8 @@
|
||||||
"attributes": {
|
"attributes": {
|
||||||
"italic": true,
|
"italic": true,
|
||||||
"bold": true,
|
"bold": true,
|
||||||
"backgroundColor": "0x6000BCF0"
|
"textColor": "0xffD70040",
|
||||||
|
"highlightColor": "0x6000BCF0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
];
|
];
|
||||||
|
|
|
||||||
|
|
@ -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),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -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';
|
||||||
|
|
|
||||||
|
|
@ -1,2 +0,0 @@
|
||||||
export 'character_shortcut_events/insert_newline.dart';
|
|
||||||
export 'character_shortcut_events/slash_command.dart';
|
|
||||||
|
|
@ -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';
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
export 'format_bold.dart';
|
||||||
|
export 'format_strikethrough.dart';
|
||||||
|
export 'handle_format_by_wrapping_with_double_character.dart';
|
||||||
|
|
@ -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';
|
||||||
|
|
@ -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';
|
|
||||||
|
|
@ -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';
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
||||||
4
lib/src/editor/toolbar/items/color/color.dart
Normal file
4
lib/src/editor/toolbar/items/color/color.dart
Normal 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
Loading…
Reference in a new issue