chore: bump version 1.1.0 (#284)
This commit is contained in:
parent
2b2bce4f3c
commit
bb3771af63
7 changed files with 100 additions and 129 deletions
17
CHANGELOG.md
17
CHANGELOG.md
|
|
@ -1,3 +1,20 @@
|
||||||
|
## 1.1.0
|
||||||
|
* feat: support IME by @LucasXu0 in ([#253](https://github.com/AppFlowy-IO/appflowy-editor/pull/253))
|
||||||
|
* feat: support text and background color in mobile toolbar by @hyj1204 in ([#233](https://github.com/AppFlowy-IO/appflowy-editor/pull/233))
|
||||||
|
* feat: support broadcast the transaction before applying it by @LucasXu0 in ([#226](https://github.com/AppFlowy-IO/appflowy-editor/pull/226))
|
||||||
|
* feat: support customizing text attribute key and rendering by @LucasXu0 in ([#244](https://github.com/AppFlowy-IO/appflowy-editor/pull/244))
|
||||||
|
* feat: support customizing the block icon widget by @LucasXu0 in ([#274](https://github.com/AppFlowy-IO/appflowy-editor/pull/274))
|
||||||
|
* feat: support uploading images from local files by @Mukund-Tandon in ([#232](https://github.com/AppFlowy-IO/appflowy-editor/pull/232))
|
||||||
|
* feat: add underline syntax parser by @vedant-pandey in ([#256](https://github.com/AppFlowy-IO/appflowy-editor/pull/256))
|
||||||
|
* feat: migrate the delta encoder by @LucasXu0 in ([#277](https://github.com/AppFlowy-IO/appflowy-editor/pull/277))
|
||||||
|
* feat: support divider toolbar item in mobile by @hyj1204 in ([#281](https://github.com/AppFlowy-IO/appflowy-editor/pull/281))
|
||||||
|
* feat: customized color options by @hyj1204 in ([#270](https://github.com/AppFlowy-IO/appflowy-editor/pull/270))
|
||||||
|
* feat: support exiting link menu by ESC by @vincenzoursano in ([#124](https://github.com/AppFlowy-IO/appflowy-editor/pull/124))
|
||||||
|
* fix: focus node doesn't work on mobile by @LucasXu0 in ([#227](https://github.com/AppFlowy-IO/appflowy-editor/pull/227))
|
||||||
|
* fix: the cursor is inaccuracy when the text contains special emoji by @LucasXu0 in ([#238](https://github.com/AppFlowy-IO/appflowy-editor/pull/238))
|
||||||
|
* fix: extend attribute keys shouldn't be sliced by @LucasXu0 in ([#248](https://github.com/AppFlowy-IO/appflowy-editor/pull/248))
|
||||||
|
* fix: keep keyboard appearance as same brightness as system theme by @hyj1204 in ([#264](https://github.com/AppFlowy-IO/appflowy-editor/pull/264))
|
||||||
|
|
||||||
## 1.0.4
|
## 1.0.4
|
||||||
* feat: support mobile drag selection by @LucasXu0 in ([#209](https://github.com/AppFlowy-IO/appflowy-editor/pull/209))
|
* feat: support mobile drag selection by @LucasXu0 in ([#209](https://github.com/AppFlowy-IO/appflowy-editor/pull/209))
|
||||||
* feat: support customizing number of the numbered list by @LucasXu0 in ([#219](https://github.com/AppFlowy-IO/appflowy-editor/pull/219))
|
* feat: support customizing number of the numbered list by @LucasXu0 in ([#219](https://github.com/AppFlowy-IO/appflowy-editor/pull/219))
|
||||||
|
|
|
||||||
32
README.md
32
README.md
|
|
@ -33,12 +33,13 @@ and the Flutter guide for
|
||||||
## Key Features
|
## Key Features
|
||||||
|
|
||||||
* Build rich, intuitive editors
|
* Build rich, intuitive editors
|
||||||
* Design and modify an ever expanding list of customizable features including
|
* Design and modify an ever-expanding list of customizable features including
|
||||||
* components (such as form input controls, numbered lists, and rich text widgets)
|
* block components (such as form input controls, numbered lists, and rich text widgets)
|
||||||
* shortcut events
|
* shortcut events
|
||||||
* themes
|
* themes
|
||||||
* menu options (**coming soon!**)
|
* selection menu
|
||||||
* [Test-coverage](https://github.com/AppFlowy-IO/appflowy-editor/blob/main/documentation/testing.md) and ongoing maintenance by AppFlowy's core team and community of more than 1,000 builders
|
* toolbar menu
|
||||||
|
* [Test Coverage](https://github.com/AppFlowy-IO/appflowy-editor/blob/main/documentation/testing.md) and ongoing maintenance by AppFlowy's core team and community of more than 1,000 builders
|
||||||
|
|
||||||
## Getting Started
|
## Getting Started
|
||||||
|
|
||||||
|
|
@ -54,8 +55,8 @@ flutter pub get
|
||||||
Start by creating a new empty AppFlowyEditor object.
|
Start by creating a new empty AppFlowyEditor object.
|
||||||
|
|
||||||
```dart
|
```dart
|
||||||
final editorState = EditorState.empty(); // an empty state
|
final editorState = EditorState.blank(withInitialText: true); // with an empty paragraph
|
||||||
final editor = AppFlowyEditor(
|
final editor = AppFlowyEditor.standard(
|
||||||
editorState: editorState,
|
editorState: editorState,
|
||||||
);
|
);
|
||||||
```
|
```
|
||||||
|
|
@ -63,9 +64,9 @@ final editor = AppFlowyEditor(
|
||||||
You can also create an editor from a JSON object in order to configure your initial state. Or you can [create an editor from Markdown or Quill Delta](https://github.com/AppFlowy-IO/appflowy-editor/blob/main/documentation/importing.md).
|
You can also create an editor from a JSON object in order to configure your initial state. Or you can [create an editor from Markdown or Quill Delta](https://github.com/AppFlowy-IO/appflowy-editor/blob/main/documentation/importing.md).
|
||||||
|
|
||||||
```dart
|
```dart
|
||||||
final json = ...;
|
final json = jsonDecode('YOUR INPUT JSON STRING');
|
||||||
final editorState = EditorState(Document.fromJson(data));
|
final editorState = EditorState(document: Document.fromJson(json));
|
||||||
final editor = AppFlowyEditor(
|
final editor = AppFlowyEditor.standard(
|
||||||
editorState: editorState,
|
editorState: editorState,
|
||||||
);
|
);
|
||||||
```
|
```
|
||||||
|
|
@ -89,15 +90,15 @@ flutter run
|
||||||
|
|
||||||
## Customizing Your Editor
|
## Customizing Your Editor
|
||||||
|
|
||||||
### Customizing Components
|
### Customizing Block Components
|
||||||
|
|
||||||
Please refer to our documentation on customizing AppFlowy for a detailed discussion about [customizing components](https://github.com/AppFlowy-IO/appflowy-editor/blob/main/documentation/customizing.md#customize-a-component).
|
Please refer to our documentation on customizing AppFlowy for a detailed discussion about [customizing components](https://github.com/AppFlowy-IO/appflowy-editor/blob/main/documentation/customizing.md#customize-a-component).
|
||||||
|
|
||||||
Below are some examples of component customizations:
|
Below are some examples of component customizations:
|
||||||
|
|
||||||
* [Checkbox Text](https://github.com/AppFlowy-IO/appflowy-editor/blob/main/lib/src/render/rich_text/checkbox_text.dart) demonstrates how to extend new styles based on existing rich text components
|
* [Todo List Block Component](https://github.com/AppFlowy-IO/appflowy-editor/blob/main/lib/src/editor/block_component/todo_list_block_component/todo_list_block_component.dart) demonstrates how to extend new styles based on existing rich text components
|
||||||
* [Image](https://github.com/AppFlowy-IO/appflowy-editor/blob/main/example/lib/plugin/network_image_node_widget.dart) demonstrates how to extend a new node and render it
|
* [Divider Block Component](https://github.com/AppFlowy-IO/appflowy-editor/blob/main/lib/src/editor/block_component/divider_block_component/divider_block_component.dart) demonstrates how to extend a new block component and render it
|
||||||
* See further examples of [rich-text plugins](https://github.com/AppFlowy-IO/appflowy-editor/blob/main/lib/src/render/rich_text)
|
* See further examples of [Rich-Text Plugins](https://github.com/AppFlowy-IO/appflowy-editor/tree/main/lib/src/editor/block_component)
|
||||||
|
|
||||||
### Customizing Shortcut Events
|
### Customizing Shortcut Events
|
||||||
|
|
||||||
|
|
@ -105,9 +106,8 @@ Please refer to our documentation on customizing AppFlowy for a detailed discuss
|
||||||
|
|
||||||
Below are some examples of shortcut event customizations:
|
Below are some examples of shortcut event customizations:
|
||||||
|
|
||||||
* [BIUS](https://github.com/AppFlowy-IO/appflowy-editor/blob/main/lib/src/service/internal_key_event_handlers/format_style_handler.dart) demonstrates how to make text bold/italic/underline/strikethrough through shortcut keys
|
* [BIUS](https://github.com/AppFlowy-IO/appflowy-editor/tree/main/lib/src/editor/editor_component/service/shortcuts/character_shortcut_events/format_single_character) demonstrates how to make text bold/italic/underline/strikethrough through shortcut keys
|
||||||
* [Paste HTML](https://github.com/AppFlowy-IO/appflowy-editor/blob/main/lib/src/service/internal_key_event_handlers/copy_paste_handler.dart) gives you an idea on how to handle pasted styles through shortcut keys
|
* Need more examples? Check out [shortcuts](https://github.com/AppFlowy-IO/appflowy-editor/tree/main/lib/src/editor/editor_component/service/shortcuts)
|
||||||
* Need more examples? Check out [Internal key event handlers](https://github.com/AppFlowy-IO/appflowy-editor/blob/main/lib/src/service/internal_key_event_handlers)
|
|
||||||
|
|
||||||
## Glossary
|
## Glossary
|
||||||
Please refer to the API documentation.
|
Please refer to the API documentation.
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,5 @@
|
||||||
# Customizing Editor Features
|
# Customizing Editor Features
|
||||||
|
|
||||||
Note: `AppFlowyEditor` has since been depreciated and `AppFlowyEditor.standard` or `AppFlowyEditor.custom` should be used instead. To recreate the examples below, you would use `AppFlowyEditor.custom`.
|
|
||||||
|
|
||||||
## Customizing a Shortcut Event
|
## Customizing a Shortcut Event
|
||||||
|
|
||||||
We will use a simple example to illustrate how to quickly add a shortcut event.
|
We will use a simple example to illustrate how to quickly add a shortcut event.
|
||||||
|
|
@ -11,18 +9,20 @@ In this example, text that starts and ends with an underscore ( \_ ) character w
|
||||||
Let's start with a blank document:
|
Let's start with a blank document:
|
||||||
|
|
||||||
```dart
|
```dart
|
||||||
@override
|
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||||
Widget build(BuildContext context) {
|
import 'package:flutter/material.dart';
|
||||||
return Scaffold(
|
|
||||||
body: Container(
|
class UnderScoreToItalic extends StatelessWidget {
|
||||||
alignment: Alignment.topCenter,
|
const UnderScoreToItalic({super.key});
|
||||||
child: AppFlowyEditor(
|
|
||||||
editorState: EditorState.empty(),
|
@override
|
||||||
shortcutEvents: const [],
|
Widget build(BuildContext context) {
|
||||||
customBuilders: const {},
|
return AppFlowyEditor.custom(
|
||||||
),
|
editorState: EditorState.blank(withInitialText: true),
|
||||||
),
|
blockComponentBuilders: standardBlockComponentBuilderMap,
|
||||||
|
characterShortcutEvents: const [],
|
||||||
);
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -30,118 +30,69 @@ At this point, nothing magic will happen after typing `_xxx_`.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
To implement our shortcut event we will create a `ShortcutEvent` instance to handle an underscore input.
|
To implement our shortcut event we will create a `CharacterShortcutEvent` instance to handle an underscore input.
|
||||||
|
|
||||||
We need to define `key` and `command` in a ShortCutEvent object to customize hotkeys. We recommend using the description of your event as a key. For example, if the underscore `_` is defined to make text italic, the key can be 'Underscore to italic'.
|
We need to define `key` and `character` in a `CharacterShortcutEvent` object to customize hotkeys. We recommend using the description of your event as a key. For example, if the underscore `_` is defined to make text italic, the key can be 'Underscore to italic'.
|
||||||
|
|
||||||
> The command, made up of a single keyword such as `underscore` or a combination of keywords using the `+` sign in between to concatenate, is a condition that triggers a user-defined function. To see which keywords are available to define a command, please refer to [key_mapping.dart](../lib/src/service/shortcut_event/key_mapping.dart).
|
|
||||||
> If more than one commands trigger the same handler, then we use ',' to split them. For example, using CTRL and A or CMD and A to 'select all', we describe it as `cmd+a,ctrl+a`(case-insensitive).
|
|
||||||
|
|
||||||
```dart
|
```dart
|
||||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
ShortcutEvent underscoreToItalicEvent = ShortcutEvent(
|
// ...
|
||||||
|
|
||||||
|
CharacterShortcutEvent underscoreToItalicEvent = CharacterShortcutEvent(
|
||||||
key: 'Underscore to italic',
|
key: 'Underscore to italic',
|
||||||
command: 'shift+underscore',
|
character: '_',
|
||||||
handler: _underscoreToItalicHandler,
|
handler: (editorState) async => handleFormatByWrappingWithSingleCharacter(
|
||||||
);
|
editorState: editorState,
|
||||||
|
character: '_',
|
||||||
ShortcutEventHandler _underscoreToItalicHandler = (editorState, event) {
|
formatStyle: FormatStyleByWrappingWithSingleChar.italic,
|
||||||
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
Then, we need to determine if the currently selected node is a `TextNode` and if the selection is collapsed.
|
|
||||||
|
|
||||||
If so, we will continue.
|
|
||||||
|
|
||||||
```dart
|
|
||||||
// ...
|
|
||||||
ShortcutEventHandler _underscoreToItalicHandler = (editorState, event) {
|
|
||||||
// Obtain the selection and selected nodes of the current document through the 'selectionService'
|
|
||||||
// to determine whether the selection is collapsed and whether the selected node is a text node.
|
|
||||||
final selectionService = editorState.service.selectionService;
|
|
||||||
final selection = selectionService.currentSelection.value;
|
|
||||||
final textNodes = selectionService.currentSelectedNodes.whereType<TextNode>();
|
|
||||||
if (selection == null || !selection.isSingle || textNodes.length != 1) {
|
|
||||||
return KeyEventResult.ignored;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Now, we deal with handling the underscore.
|
|
||||||
|
|
||||||
Look for the position of the previous underscore and
|
|
||||||
1. if one is _not_ found, return without doing anything.
|
|
||||||
2. if one is found, the text enclosed within the two underscores will be formatted to display in italics.
|
|
||||||
|
|
||||||
```dart
|
|
||||||
// ...
|
|
||||||
ShortcutEventHandler _underscoreToItalicHandler = (editorState, event) {
|
|
||||||
// ...
|
|
||||||
|
|
||||||
final textNode = textNodes.first;
|
|
||||||
final text = textNode.toRawString();
|
|
||||||
// Determine if an 'underscore' already exists in the text node and only once.
|
|
||||||
final firstUnderscore = text.indexOf('_');
|
|
||||||
final lastUnderscore = text.lastIndexOf('_');
|
|
||||||
if (firstUnderscore == -1 ||
|
|
||||||
firstUnderscore != lastUnderscore ||
|
|
||||||
firstUnderscore == selection.start.offset - 1) {
|
|
||||||
return KeyEventResult.ignored;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete the previous 'underscore',
|
|
||||||
// update the style of the text surrounded by the two underscores to 'italic',
|
|
||||||
// and update the cursor position.
|
|
||||||
final transaction = editorState.transaction
|
|
||||||
..deleteText(textNode, firstUnderscore, 1)
|
|
||||||
..formatText(
|
|
||||||
textNode,
|
|
||||||
firstUnderscore,
|
|
||||||
selection.end.offset - firstUnderscore - 1,
|
|
||||||
{
|
|
||||||
BuiltInAttributeKey.italic: true,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
..afterSelection = Selection.collapsed(
|
|
||||||
Position(
|
|
||||||
path: textNode.path,
|
|
||||||
offset: selection.end.offset - 1,
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
editorState.apply(transaction);
|
|
||||||
|
|
||||||
return KeyEventResult.handled;
|
|
||||||
};
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Now our 'underscore handler' function is done and the only task left is to inject it into the AppFlowyEditor.
|
Now our 'underscore handler' function is done and the only task left is to inject it into the AppFlowyEditor.
|
||||||
|
|
||||||
```dart
|
```dart
|
||||||
@override
|
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||||
Widget build(BuildContext context) {
|
import 'package:flutter/material.dart';
|
||||||
return Scaffold(
|
|
||||||
body: Container(
|
class UnderScoreToItalic extends StatelessWidget {
|
||||||
alignment: Alignment.topCenter,
|
const UnderScoreToItalic({super.key});
|
||||||
child: AppFlowyEditor(
|
|
||||||
editorState: EditorState.empty(),
|
@override
|
||||||
customBuilders: const {},
|
Widget build(BuildContext context) {
|
||||||
shortcutEvents: [
|
return AppFlowyEditor.custom(
|
||||||
underscoreToItalic,
|
editorState: EditorState.blank(withInitialText: true),
|
||||||
|
blockComponentBuilders: standardBlockComponentBuilderMap,
|
||||||
|
characterShortcutEvents: [
|
||||||
|
underScoreToItalicEvent,
|
||||||
],
|
],
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CharacterShortcutEvent underScoreToItalicEvent = CharacterShortcutEvent(
|
||||||
|
key: 'Underscore to italic',
|
||||||
|
character: '_',
|
||||||
|
handler: (editorState) async => handleFormatByWrappingWithSingleCharacter(
|
||||||
|
editorState: editorState,
|
||||||
|
character: '_',
|
||||||
|
formatStyle: FormatStyleByWrappingWithSingleChar.italic,
|
||||||
|
),
|
||||||
|
);
|
||||||
```
|
```
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
Check out the [complete code](https://github.com/AppFlowy-IO/appflowy-editor/blob/main/lib/src/service/internal_key_event_handlers/markdown_syntax_to_styled_text.dart) file of this example.
|
Check out the [complete code](https://github.com/AppFlowy-IO/appflowy-editor/blob/main/example/lib/samples/underscore_to_italic.dart) file of this example.
|
||||||
|
|
||||||
|
|
||||||
## Customizing a Component
|
## Customizing a Component
|
||||||
|
|
||||||
|
> ⚠️ Notes: The content below is outdated.
|
||||||
|
|
||||||
We will use a simple example to show how to quickly add a custom component.
|
We will use a simple example to show how to quickly add a custom component.
|
||||||
|
|
||||||
In this example we will render an image from the network.
|
In this example we will render an image from the network.
|
||||||
|
|
|
||||||
|
|
@ -78,7 +78,7 @@ bool handleFormatByWrappingWithSingleCharacter({
|
||||||
// if the text is already formatted, we should remove the format.
|
// if the text is already formatted, we should remove the format.
|
||||||
final sliced = delta.slice(
|
final sliced = delta.slice(
|
||||||
headCharIndex + 1,
|
headCharIndex + 1,
|
||||||
selection.end.offset - headCharIndex - 1,
|
selection.end.offset,
|
||||||
);
|
);
|
||||||
final result = sliced.everyAttributes((element) => element[style] == true);
|
final result = sliced.everyAttributes((element) => element[style] == true);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
name: appflowy_editor
|
name: appflowy_editor
|
||||||
description: A highly customizable rich-text editor for Flutter. The AppFlowy Editor project for AppFlowy and beyond.
|
description: A highly customizable rich-text editor for Flutter. The AppFlowy Editor project for AppFlowy and beyond.
|
||||||
version: 1.0.4
|
version: 1.1.0
|
||||||
homepage: https://github.com/AppFlowy-IO/appflowy-editor
|
homepage: https://github.com/AppFlowy-IO/appflowy-editor
|
||||||
|
|
||||||
platforms:
|
platforms:
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import '../../../../new/infra/testable_editor.dart';
|
||||||
import '../test_helpers/mobile_app_with_toolbar_widget.dart';
|
import '../test_helpers/mobile_app_with_toolbar_widget.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
group('dividerMobileToolbarItem\n', () {
|
group('dividerMobileToolbarItem', () {
|
||||||
testWidgets(
|
testWidgets(
|
||||||
'If the user tries to insert a divider while some text is selected, no action should be taken',
|
'If the user tries to insert a divider while some text is selected, no action should be taken',
|
||||||
(WidgetTester tester) async {
|
(WidgetTester tester) async {
|
||||||
|
|
|
||||||
|
|
@ -150,9 +150,12 @@ void main() async {
|
||||||
|
|
||||||
expect(result, true);
|
expect(result, true);
|
||||||
final after = editorState.getNodeAtPath([0])!;
|
final after = editorState.getNodeAtPath([0])!;
|
||||||
expect(after.delta!.toPlainText(), '$text1$text2');
|
final afterDelta = after.delta!;
|
||||||
expect(after.delta!.toList()[0].attributes, null);
|
expect(afterDelta.toPlainText(), '$text1$text2');
|
||||||
expect(after.delta!.toList()[1].attributes, {'italic': true});
|
final deltaList = afterDelta.toList();
|
||||||
|
expect(deltaList.length, 2);
|
||||||
|
expect(deltaList[0].attributes, null);
|
||||||
|
expect(deltaList[1].attributes, {'italic': true});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Before
|
// Before
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue