From c02699071aabbfc587b63b25ab8d03e5cefc634a Mon Sep 17 00:00:00 2001 From: Ellet <73608287+freshtechtips@users.noreply.github.com> Date: Thu, 7 Dec 2023 21:05:15 +0300 Subject: [PATCH] Version 9.0.0 (#1566) * Prepare for Version 9.0.0 --- .github/PULL_REQUEST_TEMPLATE.md | 2 +- .github/workflows/build.yml | 3 + .github/workflows/main.yml | 3 + .github/workflows/publish.yml | 23 +- .pubignore | 5 +- CHANGELOG.md | 52 + CONTRIBUTING.md | 2 +- README.md | 22 +- doc/development_notes.md | 6 + doc/migration.md | 431 +---- doc/migration/7_8.md | 429 +++++ doc/migration/8_9.md | 83 + doc/todo.md | 11 +- doc/translation.md | 2 +- example/android/app/build.gradle | 2 +- .../android/app/src/main/AndroidManifest.xml | 16 +- example/android/build.gradle | 2 +- .../gradle/wrapper/gradle-wrapper.properties | 2 +- ...quill_editor.dart => my_quill_editor.dart} | 3 +- ...ill_toolbar.dart => my_quill_toolbar.dart} | 163 +- .../lib/presentation/quill/quill_screen.dart | 98 +- .../quill/samples/quill_images_sample.dart | 6 +- .../presentation/simple/simple_screen.dart | 36 + .../flutter/generated_plugin_registrant.cc | 12 +- example/linux/flutter/generated_plugins.cmake | 3 +- .../Flutter/GeneratedPluginRegistrant.swift | 6 +- example/macos/Podfile.lock | 16 +- example/pubspec.yaml | 15 +- .../flutter/generated_plugin_registrant.cc | 9 +- .../windows/flutter/generated_plugins.cmake | 3 +- flutter_quill_extensions/CHANGELOG.md | 1672 ++++++++++++++++- flutter_quill_extensions/README.md | 26 +- .../formula/toolbar/formula_button.dart | 13 +- .../lib/embeds/image/editor/image_embed.dart | 28 +- .../image/editor/image_embed_types.dart | 3 +- .../lib/embeds/image/editor/image_menu.dart | 102 +- .../embeds/image/toolbar/image_button.dart | 25 +- .../image/toolbar/select_image_source.dart | 8 +- .../others/camera_button/camera_button.dart | 34 +- .../camera_button/select_camera_action.dart | 56 +- .../lib/embeds/others/image_video_utils.dart | 36 +- .../others/media_button/media_button.dart | 4 +- .../lib/embeds/video/editor/video_embed.dart | 5 +- .../video/toolbar/select_video_source.dart | 3 +- .../embeds/video/toolbar/video_button.dart | 31 +- .../lib/embeds/video/video.dart | 2 +- .../lib/embeds/widgets/image.dart | 7 +- .../{controller.dart => controller_ext.dart} | 0 .../lib/flutter_quill_extensions.dart | 8 +- .../models/config/shared_configurations.dart | 46 +- .../element_utils/element_shared_utils.dart | 73 +- .../utils/element_utils/element_utils.dart | 24 +- .../lib/utils/patterns.dart | 17 + flutter_quill_extensions/lib/utils/utils.dart | 28 +- flutter_quill_extensions/pubspec.yaml | 8 +- flutter_quill_test/CHANGELOG.md | 1622 +++++++++++++++- flutter_quill_test/pubspec.yaml | 4 +- lib/flutter_quill.dart | 20 +- lib/markdown_quill.dart | 5 + .../extensions/quill_configurations_ext.dart | 93 + ...troller.dart => quill_controller_ext.dart} | 6 +- lib/src/extensions/quill_provider.dart | 160 -- lib/src/extensions/uri_ext.dart | 11 + .../l10n/generated/quill_localizations.dart | 28 +- .../generated/quill_localizations_ar.dart | 18 +- .../generated/quill_localizations_bg.dart | 18 +- .../generated/quill_localizations_bn.dart | 18 +- .../generated/quill_localizations_cs.dart | 18 +- .../generated/quill_localizations_da.dart | 18 +- .../generated/quill_localizations_de.dart | 18 +- .../generated/quill_localizations_en.dart | 18 +- .../generated/quill_localizations_es.dart | 18 +- .../generated/quill_localizations_fa.dart | 18 +- .../generated/quill_localizations_fr.dart | 18 +- .../generated/quill_localizations_he.dart | 18 +- .../generated/quill_localizations_hi.dart | 18 +- .../generated/quill_localizations_id.dart | 18 +- .../generated/quill_localizations_it.dart | 18 +- .../generated/quill_localizations_ja.dart | 18 +- .../generated/quill_localizations_ko.dart | 18 +- .../generated/quill_localizations_ms.dart | 18 +- .../generated/quill_localizations_nl.dart | 18 +- .../generated/quill_localizations_no.dart | 18 +- .../generated/quill_localizations_pl.dart | 18 +- .../generated/quill_localizations_pt.dart | 18 +- .../generated/quill_localizations_ru.dart | 18 +- .../generated/quill_localizations_sr.dart | 18 +- .../generated/quill_localizations_sw.dart | 18 +- .../generated/quill_localizations_tk.dart | 18 +- .../generated/quill_localizations_tr.dart | 18 +- .../generated/quill_localizations_uk.dart | 18 +- .../generated/quill_localizations_ur.dart | 18 +- .../generated/quill_localizations_vi.dart | 18 +- .../generated/quill_localizations_zh.dart | 18 +- lib/src/l10n/quill_en.arb | 8 +- lib/src/l10n/untranslated.json | 132 ++ lib/src/l10n/widgets/localizations.dart | 4 +- ...ations.dart => editor_configurations.dart} | 21 +- .../models/config/editor/element_options.dart | 9 + .../editor/elements/list/ordered_list.dart | 14 + .../editor/elements/list/unordered_list.dart | 14 + lib/src/models/config/others/animations.dart | 46 +- .../models/config/quill_configurations.dart | 21 +- .../config/quill_shared_configurations.dart | 15 +- ...ns.dart => raw_editor_configurations.dart} | 16 +- .../toolbar/base_toolbar_configurations.dart | 33 - .../{base.dart => base_configurations.dart} | 5 +- ....dart => clear_format_configurations.dart} | 0 .../{color.dart => color_configurations.dart} | 4 +- ...dart => custom_button_configurations.dart} | 2 +- ...y.dart => font_family_configurations.dart} | 2 +- ...ize.dart => font_size_configurations.dart} | 4 +- ...story.dart => history_configurations.dart} | 5 - ...indent.dart => indent_configurations.dart} | 5 +- ...2.dart => link_style2_configurations.dart} | 2 +- ...le.dart => link_style_configurations.dart} | 0 ...search.dart => search_configurations.dart} | 0 ...t => select_alignment_configurations.dart} | 2 +- ...> select_header_style_configurations.dart} | 2 +- ... => toggle_check_list_configurations.dart} | 0 ....dart => toggle_style_configurations.dart} | 2 +- .../simple_toolbar_configurations.dart | 322 ++++ .../toolbar/toolbar_configurations.dart | 309 +-- .../toolbar_shared_configurations.dart | 8 +- lib/src/models/documents/document.dart | 3 +- lib/src/models/documents/nodes/container.dart | 2 +- lib/src/models/documents/nodes/leaf.dart | 2 +- lib/src/models/documents/nodes/line.dart | 2 +- lib/src/models/documents/nodes/node.dart | 2 +- lib/src/models/rules/insert.dart | 4 +- .../custom_quill_attributes.dart | 11 + .../quill_markdown/delta_to_markdown.dart | 359 ++++ .../embeddable_table_syntax.dart | 117 ++ .../quill_markdown/markdown_to_delta.dart | 424 +++++ lib/src/packages/quill_markdown/utils.dart | 59 + lib/src/utils/embeds.dart | 2 +- lib/src/widgets/editor/editor.dart | 75 +- lib/src/widgets/{ => others}/box.dart | 2 +- lib/src/widgets/{ => others}/cursor.dart | 2 +- .../widgets/{ => others}/default_styles.dart | 10 +- lib/src/widgets/{ => others}/delegate.dart | 13 +- .../widgets/{ => others}/float_cursor.dart | 0 .../{ => others}/keyboard_listener.dart | 0 lib/src/widgets/{ => others}/link.dart | 6 +- lib/src/widgets/{ => others}/proxy.dart | 3 +- .../widgets/{ => others}/text_selection.dart | 4 +- lib/src/widgets/{ => quill}/embeds.dart | 10 +- .../quill_controller.dart} | 68 +- lib/src/widgets/{ => quill}/text_block.dart | 34 +- lib/src/widgets/{ => quill}/text_line.dart | 49 +- .../quill_single_child_scroll_view.dart | 0 lib/src/widgets/raw_editor/raw_editor.dart | 40 +- .../raw_editor/raw_editor_actions.dart | 4 +- .../raw_editor/raw_editor_render_object.dart | 2 +- .../widgets/raw_editor/raw_editor_state.dart | 491 +++-- ...editor_state_selection_delegate_mixin.dart | 6 +- ..._editor_state_text_input_client_mixin.dart | 7 +- .../widgets/style_widgets/bullet_point.dart | 10 +- .../widgets/style_widgets/checkbox_point.dart | 17 - .../widgets/style_widgets/number_point.dart | 19 +- lib/src/widgets/toolbar/base_toolbar.dart | 141 +- ....dart => arrow_indicated_list_button.dart} | 0 ...r_format.dart => clear_format_button.dart} | 15 +- .../color/{color.dart => color_button.dart} | 41 +- .../color/{dialog.dart => color_dialog.dart} | 2 +- ..._button.dart => custom_button_button.dart} | 13 +- ...nt_family.dart => font_family_button.dart} | 35 +- .../{font_size.dart => font_size_button.dart} | 13 +- .../{history.dart => history_button.dart} | 30 +- .../{indent.dart => indent_button.dart} | 18 +- ...nk_style2.dart => link_style2_button.dart} | 51 +- ...link_style.dart => link_style_button.dart} | 41 +- .../widgets/toolbar/buttons/quill_icon.dart | 54 - .../toolbar/buttons/quill_icon_button.dart | 33 + .../{search.dart => search_button.dart} | 35 +- .../toolbar/buttons/search/search_dialog.dart | 2 +- .../toolbar/buttons/select_alignment.dart | 281 --- .../buttons/select_alignment_buttons.dart | 52 + .../buttons/select_header_style_button.dart | 116 ++ ....dart => select_header_style_buttons.dart} | 8 +- ...ist.dart => toggle_check_list_button.dart} | 12 +- ...le_style.dart => toggle_style_button.dart} | 50 +- lib/src/widgets/toolbar/simple_toolbar.dart | 521 +++++ lib/src/widgets/toolbar/toolbar.dart | 459 ----- lib/src/widgets/utils/provider.dart | 123 +- packages/quill_html_converter/CHANGELOG.md | 1627 +++++++++++++++- .../lib/quill_html_converter.dart | 32 - packages/quill_html_converter/pubspec.yaml | 9 +- pubspec.yaml | 13 +- scripts/{before-push.sh => before_push.sh} | 0 ...slations.sh => regenerate_translations.sh} | 2 +- scripts/regenerate_versions.dart | 40 + scripts/regenerate_versions.sh | 3 + test/bug_fix_test.dart | 54 +- test/widgets/editor_test.dart | 66 +- version.dart | 1 + 196 files changed, 9714 insertions(+), 3359 deletions(-) create mode 100644 doc/migration/7_8.md create mode 100644 doc/migration/8_9.md rename example/lib/presentation/quill/{quill_editor.dart => my_quill_editor.dart} (97%) rename example/lib/presentation/quill/{quill_toolbar.dart => my_quill_toolbar.dart} (68%) create mode 100644 example/lib/presentation/simple/simple_screen.dart rename flutter_quill_extensions/lib/extensions/{controller.dart => controller_ext.dart} (100%) create mode 100644 flutter_quill_extensions/lib/utils/patterns.dart create mode 100644 lib/markdown_quill.dart create mode 100644 lib/src/extensions/quill_configurations_ext.dart rename lib/src/extensions/{quill_controller.dart => quill_controller_ext.dart} (69%) delete mode 100644 lib/src/extensions/quill_provider.dart create mode 100644 lib/src/extensions/uri_ext.dart rename lib/src/models/config/editor/{configurations.dart => editor_configurations.dart} (95%) create mode 100644 lib/src/models/config/editor/elements/list/ordered_list.dart create mode 100644 lib/src/models/config/editor/elements/list/unordered_list.dart rename lib/src/models/config/raw_editor/{configurations.dart => raw_editor_configurations.dart} (96%) delete mode 100644 lib/src/models/config/toolbar/base_toolbar_configurations.dart rename lib/src/models/config/toolbar/buttons/{base.dart => base_configurations.dart} (94%) rename lib/src/models/config/toolbar/buttons/{clear_format.dart => clear_format_configurations.dart} (100%) rename lib/src/models/config/toolbar/buttons/{color.dart => color_configurations.dart} (94%) rename lib/src/models/config/toolbar/buttons/{custom_button.dart => custom_button_configurations.dart} (95%) rename lib/src/models/config/toolbar/buttons/{font_family.dart => font_family_configurations.dart} (98%) rename lib/src/models/config/toolbar/buttons/{font_size.dart => font_size_configurations.dart} (96%) rename lib/src/models/config/toolbar/buttons/{history.dart => history_configurations.dart} (87%) rename lib/src/models/config/toolbar/buttons/{indent.dart => indent_configurations.dart} (83%) rename lib/src/models/config/toolbar/buttons/{link_style2.dart => link_style2_configurations.dart} (97%) rename lib/src/models/config/toolbar/buttons/{link_style.dart => link_style_configurations.dart} (100%) rename lib/src/models/config/toolbar/buttons/{search.dart => search_configurations.dart} (100%) rename lib/src/models/config/toolbar/buttons/{select_alignment.dart => select_alignment_configurations.dart} (98%) rename lib/src/models/config/toolbar/buttons/{select_header_style.dart => select_header_style_configurations.dart} (93%) rename lib/src/models/config/toolbar/buttons/{toggle_check_list.dart => toggle_check_list_configurations.dart} (100%) rename lib/src/models/config/toolbar/buttons/{toggle_style.dart => toggle_style_configurations.dart} (96%) create mode 100644 lib/src/models/config/toolbar/simple_toolbar_configurations.dart create mode 100644 lib/src/packages/quill_markdown/custom_quill_attributes.dart create mode 100644 lib/src/packages/quill_markdown/delta_to_markdown.dart create mode 100644 lib/src/packages/quill_markdown/embeddable_table_syntax.dart create mode 100644 lib/src/packages/quill_markdown/markdown_to_delta.dart create mode 100644 lib/src/packages/quill_markdown/utils.dart rename lib/src/widgets/{ => others}/box.dart (98%) rename lib/src/widgets/{ => others}/cursor.dart (99%) rename lib/src/widgets/{ => others}/default_styles.dart (97%) rename lib/src/widgets/{ => others}/delegate.dart (97%) rename lib/src/widgets/{ => others}/float_cursor.dart (100%) rename lib/src/widgets/{ => others}/keyboard_listener.dart (100%) rename lib/src/widgets/{ => others}/link.dart (97%) rename lib/src/widgets/{ => others}/proxy.dart (98%) rename lib/src/widgets/{ => others}/text_selection.dart (99%) rename lib/src/widgets/{ => quill}/embeds.dart (73%) rename lib/src/widgets/{controller.dart => quill/quill_controller.dart} (87%) rename lib/src/widgets/{ => quill}/text_block.dart (96%) rename lib/src/widgets/{ => quill}/text_line.dart (97%) rename lib/src/widgets/{ => raw_editor}/quill_single_child_scroll_view.dart (100%) rename lib/src/widgets/toolbar/buttons/{arrow_indicated_list.dart => arrow_indicated_list_button.dart} (100%) rename lib/src/widgets/toolbar/buttons/{clear_format.dart => clear_format_button.dart} (89%) rename lib/src/widgets/toolbar/buttons/color/{color.dart => color_button.dart} (88%) rename lib/src/widgets/toolbar/buttons/color/{dialog.dart => color_dialog.dart} (99%) rename lib/src/widgets/toolbar/buttons/{custom_button.dart => custom_button_button.dart} (88%) rename lib/src/widgets/toolbar/buttons/{font_family.dart => font_family_button.dart} (91%) rename lib/src/widgets/toolbar/buttons/{font_size.dart => font_size_button.dart} (95%) rename lib/src/widgets/toolbar/buttons/{history.dart => history_button.dart} (85%) rename lib/src/widgets/toolbar/buttons/{indent.dart => indent_button.dart} (86%) rename lib/src/widgets/toolbar/buttons/{link_style2.dart => link_style2_button.dart} (91%) rename lib/src/widgets/toolbar/buttons/{link_style.dart => link_style_button.dart} (91%) delete mode 100644 lib/src/widgets/toolbar/buttons/quill_icon.dart create mode 100644 lib/src/widgets/toolbar/buttons/quill_icon_button.dart rename lib/src/widgets/toolbar/buttons/search/{search.dart => search_button.dart} (81%) delete mode 100644 lib/src/widgets/toolbar/buttons/select_alignment.dart create mode 100644 lib/src/widgets/toolbar/buttons/select_alignment_buttons.dart create mode 100644 lib/src/widgets/toolbar/buttons/select_header_style_button.dart rename lib/src/widgets/toolbar/buttons/{select_header_style.dart => select_header_style_buttons.dart} (96%) rename lib/src/widgets/toolbar/buttons/{toggle_check_list.dart => toggle_check_list_button.dart} (94%) rename lib/src/widgets/toolbar/buttons/{toggle_style.dart => toggle_style_button.dart} (83%) create mode 100644 lib/src/widgets/toolbar/simple_toolbar.dart delete mode 100644 lib/src/widgets/toolbar/toolbar.dart rename scripts/{before-push.sh => before_push.sh} (100%) rename scripts/{regenerate-translations.sh => regenerate_translations.sh} (93%) create mode 100644 scripts/regenerate_versions.dart create mode 100755 scripts/regenerate_versions.sh create mode 100644 version.dart diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index c62f265d1..b68f4c4dd 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -28,7 +28,7 @@ - [ ] I titled the PR using [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0). - [ ] I did not modify the `CHANGELOG.md` nor the plugin version in `pubspec.yaml` files. - [ ] All existing and new tests are passing. -- [ ] I have run the commands in `./scripts/before-push.sh` and it all passed successfully +- [ ] I have run the commands in `./scripts/before_push.sh` and it all passed successfully ## Breaking Change diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 221b2359d..5fdd3e33b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -17,6 +17,9 @@ jobs: - name: Check flutter version run: flutter --version + + - name: Enable Local Dev + run: ./scripts/enable_local_dev.sh - name: Install dependencies run: flutter pub get diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 2e71b9a15..9e4b19eeb 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -20,6 +20,9 @@ jobs: - name: Check flutter version run: flutter --version + - name: Enable Local Dev + run: ./scripts/enable_local_dev.sh + - name: Install dependencies run: flutter pub get diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 87fec6831..45970dc53 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -16,26 +16,39 @@ jobs: - uses: subosito/flutter-action@v2 with: channel: 'stable' + cache: true - name: Check flutter version run: flutter --version + + - name: Enable Local Dev + run: ./scripts/enable_local_dev.sh - name: Install dependencies run: flutter pub get # Here you can insert custom steps you need # - run: dart tool/generate-code.dart - - - name: Re-generate the translations - run: ./scripts/regenerate-translations.sh - # This is needed in order for the authentication to success # dart pub token add https://pub.dev --env-var PUB_TOKEN # Requests to "https://pub.dev" will now be authenticated using the secret token stored in the environment variable "PUB_TOKEN". - uses: dart-lang/setup-dart@v1 + ## dart-lang/setup-dart/.github/workflows/publish.yml@v1 # - name: Update the authorization requests to "https://pub.dev" to use the environment variable "PUB_TOKEN". # run: dart pub token add https://pub.dev --env-var PUB_TOKEN - - name: Publish + - name: Publish flutter_quill + run: flutter pub publish --force + + - name: Publish flutter_quill_extensions + run: flutter pub publish --force + working-directory: ./flutter_quill_extensions/ + + - name: Publish flutter_quill_test + run: flutter pub publish --force + working-directory: ./flutter_quill_test/ + + - name: Publish quill_html_converter run: flutter pub publish --force + working-directory: ./packages/quill_html_converter/ diff --git a/.pubignore b/.pubignore index ffddfe1a6..15f51beec 100644 --- a/.pubignore +++ b/.pubignore @@ -10,7 +10,4 @@ example/.fvm/ example/build/ example/.dart_tool/ -scripts/ - \ No newline at end of file +scripts/ \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 0a6cd7db3..09d629a35 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,58 @@ All notable changes to this project will be documented in this file. +## 9.0.0-dev-8 +* Better support for pasting HTML contents from external websites to the editor +* The experimental support of converting the HTML from `quill_html_converter` is now built-in in the `flutter_quill` and removed from there (Breaking change for `quill_html_converter`) + +## 9.0.0-dev-7 +* Fix a bug in chaning the background/font color of ol/ul list +* Flutter Quill Extensions: + * Fix link bug in the video url + * Fix patterns + +## 9.0.0-dev-6 +* Move the `child` from `QuillToolbarConfigurations` into `QuillToolbar` directly +* Bug fixes +* Add the ability to change the background and font color of the ol/ul elements dots and numbers +* Flutter Quill Extensions: + * **Breaking Change**: The `imageProviderBuilder`is now providing the context and image url + +## 9.0.0-dev-5 +* The `QuillToolbar` is now accepting only `child` with no configurations so you can customize everything you wants, the `QuillToolbar.simple()` or `QuillSimpleToolbar` implements a simple toolbar that is based on `QuillToolbar`, you are free to use it but it just an example and not standard +* Flutter Quill Extensions: + * Improve the camera button + +## 9.0.0-dev-4 +* The options parameter in all of the buttons is no longer required which can be useful to create custom toolbar with minimal efforts +* Toolbar buttons fixes in both `flutter_quill` and `flutter_quill_extensions` +* The `QuillProvider` has been dropped and no longer used, the providers will be used only internally from now on and we will not using them as much as possible + +## 9.0.0-dev-3 +* Breaking Changes: + * Rename `QuillToolbar` to `QuillSimpleToolbar` + * Rename `QuillBaseToolbar` to `QuillToolbar` + * Replace `pasteboard` with `rich_cliboard` +* Fix a bug in the example when inserting an image from url +* Flutter Quill Extensions: + * Add support for copying the image to the system cliboard + +## 9.0.0-dev-2 +* An attemp to fix CI automated publishing + +## 9.0.0-dev-1 +* An attemp to fix CI automated publishing + +## 9.0.0-dev +* **Major Breaking change**: The `QuillProvider` is now optional, the `controller` parameter has been moved to the `QuillEditor` and `QuillToolbar` once again. +* Flutter Quill Extensions; + * **Breaking Change**: Completly change the way how the source code structured to more basic and simple way, organize folders and file names, if you use the library +from `flutter_quill_extensions.dart` then there is nothing you need to do, but if you are using any other import then you need to re-imports +embed, this won't affect how quill js work + * Improvemenets to the image embed + * Add support for `margin` for web + * Add untranslated strings to the `quill_en.arb` + ## 8.6.4 * The default value of `keyboardAppearance` for the iOS will be the one from the App/System theme mode instead of always using the `Brightness.light` * Fix typos in `README.md` diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 85d81a00d..029923e73 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -39,7 +39,7 @@ to `pubspec_overrides.yaml` which will be ignored by `.gitignore` and will be us 9. Mention the new changes in the [CHANGELOG.md](../CHANGELOG.md) in the next block 10. Run the following script if possible ``` - ./scripts/before-push.sh + ./scripts/before_push.sh ``` 11. When you are done sending your pull request, run: ``` diff --git a/README.md b/README.md index afdff9447..750f7a74f 100644 --- a/README.md +++ b/README.md @@ -94,17 +94,27 @@ dependencies: > Your input and insights are valuable in shaping a stable and reliable version for all our users. Thank you for being part of the open-source community! > -Compatible versions: + ## Usage +Before using the package, we must inform you the package use the following plugins: + ``` + url_launcher + flutter_keyboard_visibility + device_info_plus + super_clipboard + ``` + +All of them doesn't require any platform spesefic setup, except [super_clipboard](https://pub.dev/packages/super_clipboard) which needs some setup on Android only, it's optional but to support copying images and pasting them into editor then you must setup it, open the page in pub.dev and read the `README.md` to get the instructions. + First, you need to instantiate a controller ```dart @@ -228,11 +238,13 @@ To see how to use the extension package, please take a look at the [README](./fl Having your document stored in Quill Delta format is sometimes not enough. Often you'll need to convert it to other formats such as HTML to publish it, or send an email. +**Note**: This package support converting from HTML back to Quill delta but it's experimental and used internally when pasting Html content from the cliboard to the Quill Editor + You have two options: -1. Using [quill_html_converter](./packages/quill_html_converter/) to convert to/from HTML, the package can convert the Quill delta to HTML well -(it uses [vsc_quill_delta_to_html](https://pub.dev/packages/vsc_quill_delta_to_html)) but the converting from HTML back to Quill delta is experimental -2. Another option is to use +1. Using [quill_html_converter](./packages/quill_html_converter/) to convert to HTML, the package can convert the Quill delta to HTML well +(it uses [vsc_quill_delta_to_html](https://pub.dev/packages/vsc_quill_delta_to_html)), it just a handy extension to do it more quickly +1. Another option is to use [vsc_quill_delta_to_html](https://pub.dev/packages/vsc_quill_delta_to_html) to convert your document to HTML. This package has full support for all Quill operations—including images, videos, formulas, diff --git a/doc/development_notes.md b/doc/development_notes.md index 25633cdac..23d60db85 100644 --- a/doc/development_notes.md +++ b/doc/development_notes.md @@ -1,3 +1,9 @@ # Development notes - When updating the translations or localizations in the app, please take a look at the [Translation](./translation.md) page as it has important notes in order to work, if you also add a feature that adds new localizations then you need to the instructions of it in order for the translations to take effect +- Only update the `version.dart` and `CHANGELOG.md` at the root folder of the repo, then run the script: + + ```console + dart ./scripts/regenerate_versions.dart + ``` + You must mention the changes of the other packages in the repo in the root `CHANGELOG.md` only and the script will replace the `CHANGELOG.md` in the other packages with the root one, and change the version in `pubspec.yaml` with the one in `version.dart` in the root folder \ No newline at end of file diff --git a/doc/migration.md b/doc/migration.md index 8c3a175c2..e7b3908f4 100644 --- a/doc/migration.md +++ b/doc/migration.md @@ -5,435 +5,12 @@ Here you can find the migration guide between different versions, you can contri - [Migration guide](#migration-guide) - [from 7.0.0 to 8.0.0](#from-700-to-800) + - [from 8.0.0 to 9.0.0](#from-800-to-900) ## from 7.0.0 to 8.0.0 -We have refactored a lot of the base code to allow you to customize everything you want, and it allows us to add new configurations very easily using inherited widgets without passing configurations all over the constructors everywhere which will be very hard to test, fix bugs, and maintain +Open [this](./migration/7_8.md) md file -1. Passing the controller +## from 8.0.0 to 9.0.0 -The controller code (should be the same) -```dart -QuillController _controller = QuillController.basic(); -``` - -**Old code**: -```dart - -Column( - children: [ - QuillToolbar.basic(controller: _controller), - Expanded( - child: QuillEditor.basic( - controller: _controller, - readOnly: false, // true for view only mode - ), - ) - ], -) - -``` - -**New code**: - -```dart -QuillProvider( - configurations: QuillConfigurations( - controller: _controller, - sharedConfigurations: const QuillSharedConfigurations(), - ), - child: Column( - children: [ - const QuillToolbar(), - Expanded( - child: QuillEditor.basic( - configurations: const QuillEditorConfigurations( - readOnly: false, // true for view only mode - ), - ), - ) - ], - ), -) -``` - -The `QuillProvider` is an inherited widget that allows you to pass configurations once and use them in the children of it. here we are passing the `_controller` once in the configurations of `QuillProvider` and the `QuillToolbar` and `QuillEditor` will get the `QuillConfigurations` internally, if it doesn't exist you will get an exception. - -we also added the `sharedConfigurations` which allow you to configure shared things like the `Local` so you don't have to define them twice, we have removed those from the `QuillToolbar` and `QuillEditor` - -2. Regarding The QuillToolbar buttons, we have renamed almost all the buttons, examples: - - `QuillHistory` to `QuillToolbarHistoryButton` - - `IndentButton` to `QuillToolbarIndentButton` - -and they usually have two parameters, `controller` and `options`, for example the type for the buttons - - `QuillToolbarHistoryButton` have `QuillToolbarHistoryButtonOptions` - - `QuillToolbarIndentButton` have `QuillToolbarIndentButtonOptions` - - `QuillToolbarClearFormatButton` have `QuillToolbarClearFormatButtonOptions` - -All the options have parent `QuillToolbarBaseButtonOptions` which have common things like - -```dart - /// By default it will use Icon data from Icons that come from material - /// library for each button, to change this, please pass a different value - /// If there is no Icon in this button then pass null in the child class - final IconData? iconData; - - /// To change the icon size pass a different value, by default will be - /// [kDefaultIconSize]. - /// This will be used for all the buttons but you can override this - final double globalIconSize; - - /// The factor of how much larger the button is in relation to the icon, - /// by default it will be [kIconButtonFactor]. - final double globalIconButtonFactor; - - /// To do extra logic after pressing the button - final VoidCallback? afterButtonPressed; - - /// By default it will use the default tooltip which already localized - final String? tooltip; - - /// Use custom theme - final QuillIconTheme? iconTheme; - - /// If you want to dispaly a differnet widget based using a builder - final QuillToolbarButtonOptionsChildBuilder childBuilder; - - /// By default it will be from the one in [QuillProvider] - /// To override it you must pass not null controller - /// if you wish to use the controller in the [childBuilder], please use the - /// one from the extraOptions since it will be not null and will be the one - /// which will be used from the quill toolbar - final QuillController? controller; -``` - -The `QuillToolbarBaseButtonOptions is`: -```dart -/// The [T] is the option for the button, usually should reference itself -/// it's used in [childBuilder] so the developer can customize this when using it -/// The [I] is an extra option for the button, usually for its state -@immutable -class QuillToolbarBaseButtonOptions extends Equatable -``` - -Example for the clear format button: - -```dart -class QuillToolbarClearFormatButtonExtraOptions - extends QuillToolbarBaseButtonExtraOptions { - const QuillToolbarClearFormatButtonExtraOptions({ - required super.controller, - required super.context, - required super.onPressed, - }); -} - -class QuillToolbarClearFormatButtonOptions - extends QuillToolbarBaseButtonOptions { - const QuillToolbarClearFormatButtonOptions({ - super.iconData, - super.afterButtonPressed, - super.childBuilder, - super.controller, - super.iconTheme, - super.tooltip, - this.iconSize, - }); - - final double? iconSize; -} - -``` - -The base for extra options: -```dart -@immutable -class QuillToolbarBaseButtonExtraOptions extends Equatable { - const QuillToolbarBaseButtonExtraOptions({ - required this.controller, - required this.context, - required this.onPressed, - }); - - /// If you need the not null controller for some usage in the [childBuilder] - /// Then please use this instead of the one in the [options] - final QuillController controller; - - /// If the child builder you must use this when the widget is tapped or pressed - /// in order to do what it expected to do - final VoidCallback? onPressed; - - final BuildContext context; - @override - List get props => [ - controller, - ]; -} -``` - -which usually share common things, it also add an extra property which was not exist, which is `childBuilder` which allow to rendering of custom widget based on the state of the button and the options it - -```dart -QuillToolbar( - configurations: QuillToolbarConfigurations( - buttonOptions: QuillToolbarButtonOptions( - clearFormat: QuillToolbarClearFormatButtonOptions( - childBuilder: (options, extraOptions) { - return IconButton.filled( - onPressed: extraOptions.onPressed, - icon: const Icon( - CupertinoIcons.clear // or options.iconData - ), - ); - }, - ), - ), - ), -), -``` - -the `extraOptions` usually contains the state variables and the events that you need to trigger like the `onPressed`, it also has the end context and the controller that will be used -while the `options` has the custom controller for each button and it's nullable because there could be no custom controller so we will just use the global one - -3. The `QuillToolbar` and `QuillToolbar.basic()` factory constructor - -since the basic factory constructor has more options than the original `QuillToolbar` which doesn't make much sense, at least to some developers, we have refactored the `QuillToolbar.basic()` to a different widget called the `QuillToolbar` and the `QuillToolbar` has been renamed to `QuillBaseToolbar` which is the base for `QuillToolbar` or any custom toolbar, sure you can create custom toolbar from scratch by just using the `controller` but if you want more support from the library use the `QuillBaseToolbar` - -the children widgets of the new `QuillToolbar` and `QuillEditor` access to their configurations by another two inherited widgets -since `QuillToolbar` and `QuillEditor` take the configuration class and provide them internally using `QuillToolbarProvider` and `QuillEditorProvider` -however the `QuillBaseToolbar` has a little bit different configurations so it has a different provider called `QuillBaseToolbarProvider` and it also already provided by default - -But there is one **note**: -> If you are using the toolbar buttons like `QuillToolbarHistoryButton`, `QuillToolbarToggleStyleButton` somewhere like the the custom toolbar (using `QuillBaseToolbar` or any custom widget) then you must provide them with `QuillToolbarProvider` inherited widget, you don't have to do this if you are using the `QuillToolbar` since it will be done for you -> - -Example of a custom toolbar: - -```dart -QuillProvider( - configurations: QuillConfigurations( - controller: _controller, - sharedConfigurations: const QuillSharedConfigurations(), - ), - child: Column( - children: [ - QuillToolbarProvider( - toolbarConfigurations: const QuillToolbarConfigurations(), - child: QuillBaseToolbar( - configurations: QuillBaseToolbarConfigurations( - toolbarSize: 15 * 2, - multiRowsDisplay: false, - childrenBuilder: (context) { - final controller = context.requireQuillController; // new extension which is a little bit shorter to access the quill provider then the controller - - // there are many options, feel free to explore them all!! - return [ - QuillToolbarHistoryButton( - controller: controller, - options: const QuillToolbarHistoryButtonOptions( - isUndo: true), - ), - QuillToolbarHistoryButton( - controller: controller, - options: const QuillToolbarHistoryButtonOptions( - isUndo: false), - ), - QuillToolbarToggleStyleButton( - attribute: Attribute.bold, - controller: controller, - options: const QuillToolbarToggleStyleButtonOptions( - iconData: Icons.format_bold, - iconSize: 20, - ), - ), - QuillToolbarToggleStyleButton( - attribute: Attribute.italic, - controller: controller, - options: const QuillToolbarToggleStyleButtonOptions( - iconData: Icons.format_italic, - iconSize: 20, - ), - ), - QuillToolbarToggleStyleButton( - attribute: Attribute.underline, - controller: controller, - options: const QuillToolbarToggleStyleButtonOptions( - iconData: Icons.format_underline, - iconSize: 20, - ), - ), - QuillToolbarClearFormatButton( - controller: controller, - options: const QuillToolbarClearFormatButtonOptions( - iconData: Icons.format_clear, - iconSize: 20, - ), - ), - VerticalDivider( - indent: 12, - endIndent: 12, - color: Colors.grey.shade400, - ), - QuillToolbarSelectHeaderStyleButtons( - controller: controller, - options: - const QuillToolbarSelectHeaderStyleButtonsOptions( - iconSize: 20, - ), - ), - QuillToolbarToggleStyleButton( - attribute: Attribute.ol, - controller: controller, - options: const QuillToolbarToggleStyleButtonOptions( - iconData: Icons.format_list_numbered, - iconSize: 20, - ), - ), - QuillToolbarToggleStyleButton( - attribute: Attribute.ul, - controller: controller, - options: const QuillToolbarToggleStyleButtonOptions( - iconData: Icons.format_list_bulleted, - iconSize: 20, - ), - ), - QuillToolbarToggleStyleButton( - attribute: Attribute.blockQuote, - controller: controller, - options: const QuillToolbarToggleStyleButtonOptions( - iconData: Icons.format_quote, - iconSize: 20, - ), - ), - VerticalDivider( - indent: 12, - endIndent: 12, - color: Colors.grey.shade400, - ), - QuillToolbarIndentButton( - controller: controller, - isIncrease: true, - options: const QuillToolbarIndentButtonOptions( - iconData: Icons.format_indent_increase, - iconSize: 20, - )), - QuillToolbarIndentButton( - controller: controller, - isIncrease: false, - options: const QuillToolbarIndentButtonOptions( - iconData: Icons.format_indent_decrease, - iconSize: 20, - ), - ), - ]; - }, - ), - ), - ), - Expanded( - child: QuillEditor.basic( - configurations: const QuillEditorConfigurations( - readOnly: false, - placeholder: 'Write your notes', - padding: EdgeInsets.all(16), - ), - ), - ) - ], - ), -) -``` - -4. The `QuillEditor` and `QuillEditor.basic()` - -since the `QuillEditor.basic()` is a lighter version than the original `QuillEditor` since it has fewer required configurations we didn't change much, other than the configuration class, but we must inform you if you plan on sending pull request or you are a maintainer and when you add new property or change anything in `QuillEditorConfigurations` please regenerate the `copyWith` (using IDE extension or plugin) otherwise the `QuilEditor.basic()` will not apply some configurations - -we have disabled the line numbers in the code block by default, you can enable them again using the following: - -```dart -QuillEditor.basic( - configurations: const QuillEditorConfigurations( - elementOptions: QuillEditorElementOptions( - codeBlock: QuillEditorCodeBlockElementOptions( - enableLineNumbers: true, - ), - ), - ), -) -``` - -5. `QuillCustomButton`: - -We have renamed the property `icon` to `iconData` to indicate it an icon data and not an icon widget -```dart - QuillCustomButton( - iconData: Icons.ac_unit, - onTap: () { - debugPrint('snowflake'); - } - ), -``` - -6. Using custom local for both `QuillEditor` and `QuillToolbar` - -We have added shared configurations property for shared things -```dart - QuillProvider( - configurations: QuillConfigurations( - controller: _controller, - sharedConfigurations: const QuillSharedConfigurations( - locale: Locale('fr'), - ), - ), - child: Column( - children: [ - const QuillToolbar( - configurations: QuillToolbarConfigurations(), - ), - Expanded( - child: QuillEditor.basic( - configurations: const QuillEditorConfigurations(), - ), - ) - ], - ), -) -``` - -7. Image size for all platforms - -We have added new properties `width`, `height`, `margin`, `alignment` for all platforms other than mobile and web for the images for example - -```dart -{ - "insert": { - "image": "https://user-images.githubusercontent.com/122956/72955931-ccc07900-3d52-11ea-89b1-d468a6e2aa2b.png" - }, - "attributes":{ - "style":"width: 50; height: 50; margin: 10; alignment: topLeft" - } -} -``` - -8. Other Improvements - -You don't need anything to get this done, we have used const more when possible, removed unused events, flutter best practices, converted to stateless widgets when possible, and used better ways to listen for changes example: - - instead of - -```dart -MediaQuery.of(context).size; -``` - -we will use -```dart -MediaQuery.sizeOf(context); -``` -We also minimized the number of rebuilds using more efficient logic and there is more. - -9. More options - -We have added more options in the extension package, for all the buttons, configurations, animations, enable and disable things - -If you are facing any issues or questions feel free to ask us on GitHub issues +Open [this](./migration/8_9.md) md file \ No newline at end of file diff --git a/doc/migration/7_8.md b/doc/migration/7_8.md new file mode 100644 index 000000000..e797ca30b --- /dev/null +++ b/doc/migration/7_8.md @@ -0,0 +1,429 @@ +We have refactored a lot of the base code to allow you to customize everything you want, and it allows us to add new configurations very easily using inherited widgets without passing configurations all over the constructors everywhere which will be very hard to test, fix bugs, and maintain + +1. Passing the controller + +The controller code (should be the same) +```dart +QuillController _controller = QuillController.basic(); +``` + +**Old code**: +```dart + +Column( + children: [ + QuillToolbar.basic(controller: _controller), + Expanded( + child: QuillEditor.basic( + controller: _controller, + readOnly: false, // true for view only mode + ), + ) + ], +) + +``` + +**New code**: + +```dart +QuillProvider( + configurations: QuillConfigurations( + controller: _controller, + sharedConfigurations: const QuillSharedConfigurations(), + ), + child: Column( + children: [ + const QuillToolbar(), + Expanded( + child: QuillEditor.basic( + configurations: const QuillEditorConfigurations( + readOnly: false, // true for view only mode + ), + ), + ) + ], + ), +) +``` + +The `QuillProvider` is an inherited widget that allows you to pass configurations once and use them in the children of it. here we are passing the `_controller` once in the configurations of `QuillProvider` and the `QuillToolbar` and `QuillEditor` will get the `QuillConfigurations` internally, if it doesn't exist you will get an exception. + +we also added the `sharedConfigurations` which allow you to configure shared things like the `Local` so you don't have to define them twice, we have removed those from the `QuillToolbar` and `QuillEditor` + +2. Regarding The QuillToolbar buttons, we have renamed almost all the buttons, examples: + - `QuillHistory` to `QuillToolbarHistoryButton` + - `IndentButton` to `QuillToolbarIndentButton` + +and they usually have two parameters, `controller` and `options`, for example the type for the buttons + - `QuillToolbarHistoryButton` have `QuillToolbarHistoryButtonOptions` + - `QuillToolbarIndentButton` have `QuillToolbarIndentButtonOptions` + - `QuillToolbarClearFormatButton` have `QuillToolbarClearFormatButtonOptions` + +All the options have parent `QuillToolbarBaseButtonOptions` which have common things like + +```dart + /// By default it will use Icon data from Icons that come from material + /// library for each button, to change this, please pass a different value + /// If there is no Icon in this button then pass null in the child class + final IconData? iconData; + + /// To change the icon size pass a different value, by default will be + /// [kDefaultIconSize]. + /// This will be used for all the buttons but you can override this + final double globalIconSize; + + /// The factor of how much larger the button is in relation to the icon, + /// by default it will be [kIconButtonFactor]. + final double globalIconButtonFactor; + + /// To do extra logic after pressing the button + final VoidCallback? afterButtonPressed; + + /// By default it will use the default tooltip which already localized + final String? tooltip; + + /// Use custom theme + final QuillIconTheme? iconTheme; + + /// If you want to dispaly a differnet widget based using a builder + final QuillToolbarButtonOptionsChildBuilder childBuilder; + + /// By default it will be from the one in [QuillProvider] + /// To override it you must pass not null controller + /// if you wish to use the controller in the [childBuilder], please use the + /// one from the extraOptions since it will be not null and will be the one + /// which will be used from the quill toolbar + final QuillController? controller; +``` + +The `QuillToolbarBaseButtonOptions is`: +```dart +/// The [T] is the option for the button, usually should reference itself +/// it's used in [childBuilder] so the developer can customize this when using it +/// The [I] is an extra option for the button, usually for its state +@immutable +class QuillToolbarBaseButtonOptions extends Equatable +``` + +Example for the clear format button: + +```dart +class QuillToolbarClearFormatButtonExtraOptions + extends QuillToolbarBaseButtonExtraOptions { + const QuillToolbarClearFormatButtonExtraOptions({ + required super.controller, + required super.context, + required super.onPressed, + }); +} + +class QuillToolbarClearFormatButtonOptions + extends QuillToolbarBaseButtonOptions { + const QuillToolbarClearFormatButtonOptions({ + super.iconData, + super.afterButtonPressed, + super.childBuilder, + super.controller, + super.iconTheme, + super.tooltip, + this.iconSize, + }); + + final double? iconSize; +} + +``` + +The base for extra options: +```dart +@immutable +class QuillToolbarBaseButtonExtraOptions extends Equatable { + const QuillToolbarBaseButtonExtraOptions({ + required this.controller, + required this.context, + required this.onPressed, + }); + + /// If you need the not null controller for some usage in the [childBuilder] + /// Then please use this instead of the one in the [options] + final QuillController controller; + + /// If the child builder you must use this when the widget is tapped or pressed + /// in order to do what it expected to do + final VoidCallback? onPressed; + + final BuildContext context; + @override + List get props => [ + controller, + ]; +} +``` + +which usually share common things, it also add an extra property which was not exist, which is `childBuilder` which allow to rendering of custom widget based on the state of the button and the options it + +```dart +QuillToolbar( + configurations: QuillToolbarConfigurations( + buttonOptions: QuillToolbarButtonOptions( + clearFormat: QuillToolbarClearFormatButtonOptions( + childBuilder: (options, extraOptions) { + return IconButton.filled( + onPressed: extraOptions.onPressed, + icon: const Icon( + CupertinoIcons.clear // or options.iconData + ), + ); + }, + ), + ), + ), +), +``` + +the `extraOptions` usually contains the state variables and the events that you need to trigger like the `onPressed`, it also has the end context and the controller that will be used +while the `options` has the custom controller for each button and it's nullable because there could be no custom controller so we will just use the global one + +3. The `QuillToolbar` and `QuillToolbar.basic()` factory constructor + +since the basic factory constructor has more options than the original `QuillToolbar` which doesn't make much sense, at least to some developers, we have refactored the `QuillToolbar.basic()` to a different widget called the `QuillToolbar` and the `QuillToolbar` has been renamed to `QuillBaseToolbar` which is the base for `QuillToolbar` or any custom toolbar, sure you can create custom toolbar from scratch by just using the `controller` but if you want more support from the library use the `QuillBaseToolbar` + +the children widgets of the new `QuillToolbar` and `QuillEditor` access to their configurations by another two inherited widgets +since `QuillToolbar` and `QuillEditor` take the configuration class and provide them internally using `QuillToolbarProvider` and `QuillEditorProvider` +however the `QuillBaseToolbar` has a little bit different configurations so it has a different provider called `QuillBaseToolbarProvider` and it also already provided by default + +But there is one **note**: +> If you are using the toolbar buttons like `QuillToolbarHistoryButton`, `QuillToolbarToggleStyleButton` somewhere like the the custom toolbar (using `QuillBaseToolbar` or any custom widget) then you must provide them with `QuillToolbarProvider` inherited widget, you don't have to do this if you are using the `QuillToolbar` since it will be done for you +> + +Example of a custom toolbar: + +```dart +QuillProvider( + configurations: QuillConfigurations( + controller: _controller, + sharedConfigurations: const QuillSharedConfigurations(), + ), + child: Column( + children: [ + QuillToolbarProvider( + toolbarConfigurations: const QuillToolbarConfigurations(), + child: QuillBaseToolbar( + configurations: QuillBaseToolbarConfigurations( + toolbarSize: 15 * 2, + multiRowsDisplay: false, + childrenBuilder: (context) { + final controller = context.requireQuillController; // new extension which is a little bit shorter to access the quill provider then the controller + + // there are many options, feel free to explore them all!! + return [ + QuillToolbarHistoryButton( + controller: controller, + options: const QuillToolbarHistoryButtonOptions( + isUndo: true), + ), + QuillToolbarHistoryButton( + controller: controller, + options: const QuillToolbarHistoryButtonOptions( + isUndo: false), + ), + QuillToolbarToggleStyleButton( + attribute: Attribute.bold, + controller: controller, + options: const QuillToolbarToggleStyleButtonOptions( + iconData: Icons.format_bold, + iconSize: 20, + ), + ), + QuillToolbarToggleStyleButton( + attribute: Attribute.italic, + controller: controller, + options: const QuillToolbarToggleStyleButtonOptions( + iconData: Icons.format_italic, + iconSize: 20, + ), + ), + QuillToolbarToggleStyleButton( + attribute: Attribute.underline, + controller: controller, + options: const QuillToolbarToggleStyleButtonOptions( + iconData: Icons.format_underline, + iconSize: 20, + ), + ), + QuillToolbarClearFormatButton( + controller: controller, + options: const QuillToolbarClearFormatButtonOptions( + iconData: Icons.format_clear, + iconSize: 20, + ), + ), + VerticalDivider( + indent: 12, + endIndent: 12, + color: Colors.grey.shade400, + ), + QuillToolbarSelectHeaderStyleButtons( + controller: controller, + options: + const QuillToolbarSelectHeaderStyleButtonsOptions( + iconSize: 20, + ), + ), + QuillToolbarToggleStyleButton( + attribute: Attribute.ol, + controller: controller, + options: const QuillToolbarToggleStyleButtonOptions( + iconData: Icons.format_list_numbered, + iconSize: 20, + ), + ), + QuillToolbarToggleStyleButton( + attribute: Attribute.ul, + controller: controller, + options: const QuillToolbarToggleStyleButtonOptions( + iconData: Icons.format_list_bulleted, + iconSize: 20, + ), + ), + QuillToolbarToggleStyleButton( + attribute: Attribute.blockQuote, + controller: controller, + options: const QuillToolbarToggleStyleButtonOptions( + iconData: Icons.format_quote, + iconSize: 20, + ), + ), + VerticalDivider( + indent: 12, + endIndent: 12, + color: Colors.grey.shade400, + ), + QuillToolbarIndentButton( + controller: controller, + isIncrease: true, + options: const QuillToolbarIndentButtonOptions( + iconData: Icons.format_indent_increase, + iconSize: 20, + )), + QuillToolbarIndentButton( + controller: controller, + isIncrease: false, + options: const QuillToolbarIndentButtonOptions( + iconData: Icons.format_indent_decrease, + iconSize: 20, + ), + ), + ]; + }, + ), + ), + ), + Expanded( + child: QuillEditor.basic( + configurations: const QuillEditorConfigurations( + readOnly: false, + placeholder: 'Write your notes', + padding: EdgeInsets.all(16), + ), + ), + ) + ], + ), +) +``` + +4. The `QuillEditor` and `QuillEditor.basic()` + +since the `QuillEditor.basic()` is a lighter version than the original `QuillEditor` since it has fewer required configurations we didn't change much, other than the configuration class, but we must inform you if you plan on sending pull request or you are a maintainer and when you add new property or change anything in `QuillEditorConfigurations` please regenerate the `copyWith` (using IDE extension or plugin) otherwise the `QuilEditor.basic()` will not apply some configurations + +we have disabled the line numbers in the code block by default, you can enable them again using the following: + +```dart +QuillEditor.basic( + configurations: const QuillEditorConfigurations( + elementOptions: QuillEditorElementOptions( + codeBlock: QuillEditorCodeBlockElementOptions( + enableLineNumbers: true, + ), + ), + ), +) +``` + +5. `QuillCustomButton`: + +We have renamed the property `icon` to `iconData` to indicate it an icon data and not an icon widget +```dart + QuillCustomButton( + iconData: Icons.ac_unit, + onTap: () { + debugPrint('snowflake'); + } + ), +``` + +6. Using custom local for both `QuillEditor` and `QuillToolbar` + +We have added shared configurations property for shared things +```dart + QuillProvider( + configurations: QuillConfigurations( + controller: _controller, + sharedConfigurations: const QuillSharedConfigurations( + locale: Locale('fr'), + ), + ), + child: Column( + children: [ + const QuillToolbar( + configurations: QuillToolbarConfigurations(), + ), + Expanded( + child: QuillEditor.basic( + configurations: const QuillEditorConfigurations(), + ), + ) + ], + ), +) +``` + +7. Image size for all platforms + +We have added new properties `width`, `height`, `margin`, `alignment` for all platforms other than mobile and web for the images for example + +```dart +{ + "insert": { + "image": "https://user-images.githubusercontent.com/122956/72955931-ccc07900-3d52-11ea-89b1-d468a6e2aa2b.png" + }, + "attributes":{ + "style":"width: 50; height: 50; margin: 10; alignment: topLeft" + } +} +``` + +8. Other Improvements + +You don't need anything to get this done, we have used const more when possible, removed unused events, flutter best practices, converted to stateless widgets when possible, and used better ways to listen for changes example: + + instead of + +```dart +MediaQuery.of(context).size; +``` + +we will use +```dart +MediaQuery.sizeOf(context); +``` +We also minimized the number of rebuilds using more efficient logic and there is more. + +9. More options + +We have added more options in the extension package, for all the buttons, configurations, animations, enable and disable things + +If you are facing any issues or questions feel free to ask us on GitHub issues diff --git a/doc/migration/8_9.md b/doc/migration/8_9.md new file mode 100644 index 000000000..ade5124b0 --- /dev/null +++ b/doc/migration/8_9.md @@ -0,0 +1,83 @@ +1. Removing the `QuillProvider` + +We got a lot of feedbacks about `QuillProvider`, while the provider help removing duplicate lines for simple usage, for more advance usage it become very messy +So from now on we will use providers for the `QuillToolbar` and `QuillEditor` only internally, you don't need it anymore + +Instead you will need to pass the configurations directly in the `QuillToolbar` and `QuillEditor` + +**Old code**: + +```dart +QuillProvider( + configurations: QuillConfigurations( + controller: _controller, + sharedConfigurations: const QuillSharedConfigurations(), + ), + child: Column( + children: [ + const QuillToolbar(), + Expanded( + child: QuillEditor.basic( + configurations: const QuillEditorConfigurations( + readOnly: false, // true for view only mode + ), + ), + ) + ], + ), +) +``` + +**New code**: + +```dart + +Column( + children: [ + QuillToolbar.simple( + QuillSimpleToolbarConfigurations(controller: _controller)), + Expanded( + child: QuillEditor.basic( + configurations: QuillEditorConfigurations(controller: _controller), + ), + ) + ], +) + +``` + +2. Refactoring the Base Toolbar + +From now on, the `QuillToolbar` will be a widget that only provides the things that the buttons needs like localizations and `QuillToolbarProvider` +So you can define your own toolbar from scratch just like in Quill JS + +The `QuillToolbar` is now accepting only `child` with no configurations so you can customize everything you wants, the `QuillToolbar.simple()` or `QuillSimpleToolbar` implements a simple toolbar that is based on `QuillToolbar`, you are free to use it but it just an example and not standard + +1. Source Code Structure + +Completly changed the way how the source code structured to more basic and simple way, organize folders and file names, if you use the library +from `flutter_quill_extensions.dart` then there is nothing you need to do, but if you are using any other import then you need to re-imports + +4. Change the version system + +For [more details](https://github.com/singerdmx/flutter-quill/discussions/1560) + +5. Dependencies changes + + 1. Add `gal_linux` in `flutter_quill_extensions` + 2. Replace `pasteboard` with `rich_cliboard` + 3. Remove `flutter_animate` + +6. Optional options for the buttons + +All the buttons from now on, have optional options parameter + +7. Improve Flutter Quill Extensions + +Bug fixes and new features, the extensions package keep getting better thanks to the community. + +8. Migrate to Material 3 + +We have migrated all of the buttons to material 3, removed a lot of old parameters, if you want to customize one or all the buttons to replacing it with completly different widget with the same state use the `base` in the button options for all or the button name for one + +We have also replaced the header style buttons with one dropdown button as a default, replaced the alignment buttons with less code and a lot more \ No newline at end of file diff --git a/doc/todo.md b/doc/todo.md index 4f38ed016..813a90669 100644 --- a/doc/todo.md +++ b/doc/todo.md @@ -29,6 +29,10 @@ This is a todo list page that added recently and will be updated soon. - Extract the shared properties between `QuillRawEditorConfigurations` and `QuillEditorConfigurations` - The todo in the this [commit](https://github.com/singerdmx/flutter-quill/commit/79597ea6425357795c0663588ac079665241f23a) needs to be checked - use `maybeOf` and of instead `ofNotNull` in the providers to follow flutter offical convenstion, completly rework the providers and update the build context extensions + - Add line through to the text when the check point checked is true + - Change the color of the numbers and dots in ol/ul to match the ones in the item list + - Fix the bugs of the font family and font size + - Try to update Quill Html Converter ### Bugs @@ -39,12 +43,7 @@ Please go to the [issues](https://github.com/singerdmx/flutter-quill/issues) ## Flutter Quill Extensions ### Features -- Add support for copying images to the Clipboard ### Improvemenets -Please check the todos, this list will be updated soon. - -### Bugs - -Please check the todos, this list will be updated soon. \ No newline at end of file +### Bugs \ No newline at end of file diff --git a/doc/translation.md b/doc/translation.md index 5bdaf542d..5d801ba80 100644 --- a/doc/translation.md +++ b/doc/translation.md @@ -56,7 +56,7 @@ flutter gen-l10n or: ``` -./scripts/regenerate-translations.sh +./scripts/regenerate_translations.sh ``` diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle index d2bb91174..aa36e402b 100644 --- a/example/android/app/build.gradle +++ b/example/android/app/build.gradle @@ -24,7 +24,7 @@ if (flutterVersionName == null) { android { namespace "com.example.example" - compileSdk flutter.compileSdkVersion + compileSdkVersion flutter.compileSdkVersion ndkVersion flutter.ndkVersion compileOptions { diff --git a/example/android/app/src/main/AndroidManifest.xml b/example/android/app/src/main/AndroidManifest.xml index e047f006d..bf856053a 100644 --- a/example/android/app/src/main/AndroidManifest.xml +++ b/example/android/app/src/main/AndroidManifest.xml @@ -1,10 +1,11 @@ - + - @@ -43,7 +44,7 @@ - + + + + diff --git a/example/android/build.gradle b/example/android/build.gradle index d88cfa132..f86105d78 100644 --- a/example/android/build.gradle +++ b/example/android/build.gradle @@ -8,7 +8,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:8.1.4' + classpath 'com.android.tools.build:gradle:8.2.0' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } diff --git a/example/android/gradle/wrapper/gradle-wrapper.properties b/example/android/gradle/wrapper/gradle-wrapper.properties index 5e6b54271..aa49780cd 100644 --- a/example/android/gradle/wrapper/gradle-wrapper.properties +++ b/example/android/gradle/wrapper/gradle-wrapper.properties @@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-all.zip diff --git a/example/lib/presentation/quill/quill_editor.dart b/example/lib/presentation/quill/my_quill_editor.dart similarity index 97% rename from example/lib/presentation/quill/quill_editor.dart rename to example/lib/presentation/quill/my_quill_editor.dart index 06f7eb766..390f08577 100644 --- a/example/lib/presentation/quill/quill_editor.dart +++ b/example/lib/presentation/quill/my_quill_editor.dart @@ -82,7 +82,7 @@ class MyQuillEditor extends StatelessWidget { 'Error while loading an image: ${error.toString()}', ); }, - imageProviderBuilder: (imageUrl) { + imageProviderBuilder: (context, imageUrl) { // cached_network_image is supported // only for Android, iOS and web @@ -99,6 +99,7 @@ class MyQuillEditor extends StatelessWidget { return getImageProviderByImageSource( imageUrl, imageProviderBuilder: null, + context: context, assetsPrefix: QuillSharedExtensionsConfigurations.get( context: context) .assetsPrefix, diff --git a/example/lib/presentation/quill/quill_toolbar.dart b/example/lib/presentation/quill/my_quill_toolbar.dart similarity index 68% rename from example/lib/presentation/quill/quill_toolbar.dart rename to example/lib/presentation/quill/my_quill_toolbar.dart index f7aedf2af..f7c22b1be 100644 --- a/example/lib/presentation/quill/quill_toolbar.dart +++ b/example/lib/presentation/quill/my_quill_toolbar.dart @@ -10,16 +10,17 @@ import 'package:path/path.dart' as path; import 'package:path_provider/path_provider.dart' show getApplicationDocumentsDirectory; -import '../extensions/scaffold_messenger.dart'; import '../settings/cubit/settings_cubit.dart'; import 'embeds/timestamp_embed.dart'; class MyQuillToolbar extends StatelessWidget { const MyQuillToolbar({ + required this.controller, required this.focusNode, super.key, }); + final QuillController controller; final FocusNode focusNode; Future onImageInsertWithCropping( @@ -65,7 +66,7 @@ class MyQuillToolbar extends StatelessWidget { } Future onImageInsert(String image, QuillController controller) async { - if (isWeb()) { + if (isWeb() || isHttpBasedUrl(image)) { controller.insertImageBlock(imageSource: image); return; } @@ -98,132 +99,114 @@ class MyQuillToolbar extends StatelessWidget { if (state.useCustomQuillToolbar) { // For more info // https://github.com/singerdmx/flutter-quill/blob/master/doc/custom_toolbar.md - return QuillBaseToolbar( - configurations: QuillBaseToolbarConfigurations( - toolbarSize: 15 * 2, - multiRowsDisplay: false, - buttonOptions: const QuillToolbarButtonOptions( + return QuillToolbar( + configurations: const QuillToolbarConfigurations( + buttonOptions: QuillToolbarButtonOptions( base: QuillToolbarBaseButtonOptions( - globalIconSize: 30, + globalIconSize: 20, + globalIconButtonFactor: 1.4, ), ), - childrenBuilder: (context) { - final controller = context.requireQuillController; - return [ - QuillToolbarImageButton( - controller: controller, - options: const QuillToolbarImageButtonOptions(), + ), + child: SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: Row( + children: [ + IconButton( + onPressed: () => context + .read() + .updateSettings( + state.copyWith(useCustomQuillToolbar: false)), + icon: const Icon( + Icons.width_normal, + ), ), QuillToolbarHistoryButton( + isUndo: true, controller: controller, - options: - const QuillToolbarHistoryButtonOptions(isUndo: true), ), QuillToolbarHistoryButton( + isUndo: false, controller: controller, - options: - const QuillToolbarHistoryButtonOptions(isUndo: false), ), QuillToolbarToggleStyleButton( - attribute: Attribute.bold, + options: const QuillToolbarToggleStyleButtonOptions(), controller: controller, - options: QuillToolbarToggleStyleButtonOptions( - childBuilder: (options, extraOptions) { - if (extraOptions.isToggled) { - return IconButton.filled( - onPressed: extraOptions.onPressed, - icon: Icon(options.iconData), - ); - } - return IconButton( - onPressed: extraOptions.onPressed, - icon: Icon(options.iconData), - ); - }, - ), + attribute: Attribute.bold, ), QuillToolbarToggleStyleButton( - attribute: Attribute.italic, + options: const QuillToolbarToggleStyleButtonOptions(), controller: controller, - options: const QuillToolbarToggleStyleButtonOptions( - iconData: Icons.format_italic, - ), + attribute: Attribute.italic, ), QuillToolbarToggleStyleButton( - attribute: Attribute.underline, controller: controller, - options: const QuillToolbarToggleStyleButtonOptions( - iconData: Icons.format_underline, - iconSize: 20, - ), + attribute: Attribute.underline, ), QuillToolbarClearFormatButton( controller: controller, - options: const QuillToolbarClearFormatButtonOptions( - iconData: Icons.format_clear, - ), ), - VerticalDivider( - indent: 12, - endIndent: 12, - color: Colors.grey.shade400, + const VerticalDivider(), + QuillToolbarImageButton( + controller: controller, ), - QuillToolbarSelectHeaderStyleButtons( + QuillToolbarCameraButton( + controller: controller, + ), + QuillToolbarVideoButton( + controller: controller, + ), + const VerticalDivider(), + QuillToolbarColorButton( + controller: controller, + isBackground: false, + ), + QuillToolbarColorButton( + controller: controller, + isBackground: true, + ), + const VerticalDivider(), + QuillToolbarSelectHeaderStyleButton( + controller: controller, + ), + const VerticalDivider(), + QuillToolbarToggleCheckListButton( controller: controller, - options: const QuillToolbarSelectHeaderStyleButtonsOptions( - iconSize: 20, - ), ), QuillToolbarToggleStyleButton( - attribute: Attribute.ol, controller: controller, - options: const QuillToolbarToggleStyleButtonOptions( - iconData: Icons.format_list_numbered, - iconSize: 39, - ), + attribute: Attribute.ol, ), QuillToolbarToggleStyleButton( - attribute: Attribute.ul, controller: controller, - options: const QuillToolbarToggleStyleButtonOptions( - iconData: Icons.format_list_bulleted, - ), + attribute: Attribute.ul, ), QuillToolbarToggleStyleButton( - attribute: Attribute.blockQuote, controller: controller, - options: const QuillToolbarToggleStyleButtonOptions( - iconData: Icons.format_quote, - iconSize: 15, - ), + attribute: Attribute.inlineCode, ), - VerticalDivider( - indent: 12, - endIndent: 12, - color: Colors.grey.shade400, + QuillToolbarToggleStyleButton( + controller: controller, + attribute: Attribute.blockQuote, ), QuillToolbarIndentButton( - controller: controller, - isIncrease: true, - options: const QuillToolbarIndentButtonOptions( - iconData: Icons.format_indent_increase, - iconSize: 20, - )), + controller: controller, + isIncrease: true, + ), QuillToolbarIndentButton( controller: controller, isIncrease: false, - options: const QuillToolbarIndentButtonOptions( - iconData: Icons.format_indent_decrease, - iconSize: 20, - ), ), - ]; - }, + const VerticalDivider(), + QuillToolbarLinkStyleButton(controller: controller), + ], + ), ), ); } - return QuillToolbar( - configurations: QuillToolbarConfigurations( + return QuillSimpleToolbar( + configurations: QuillSimpleToolbarConfigurations( + controller: controller, showAlignmentButtons: true, buttonOptions: QuillToolbarButtonOptions( base: QuillToolbarBaseButtonOptions( @@ -235,7 +218,6 @@ class MyQuillToolbar extends StatelessWidget { QuillToolbarCustomButtonOptions( icon: const Icon(Icons.add_alarm_rounded), onPressed: () { - final controller = context.requireQuillController; controller.document .insert(controller.selection.extentOffset, '\n'); controller.updateSelection( @@ -279,13 +261,10 @@ class MyQuillToolbar extends StatelessWidget { }, ), QuillToolbarCustomButtonOptions( - icon: const Icon(Icons.ac_unit), + icon: const Icon(Icons.dashboard_customize), onPressed: () { - ScaffoldMessenger.of(context) - ..clearSnackBars() - ..showText( - 'Custom button!', - ); + context.read().updateSettings( + state.copyWith(useCustomQuillToolbar: true)); }, ), ], diff --git a/example/lib/presentation/quill/quill_screen.dart b/example/lib/presentation/quill/quill_screen.dart index aab91c83a..b3b73eee5 100644 --- a/example/lib/presentation/quill/quill_screen.dart +++ b/example/lib/presentation/quill/quill_screen.dart @@ -4,14 +4,13 @@ import 'package:flutter/material.dart'; import 'package:flutter_quill/flutter_quill.dart'; import 'package:flutter_quill_extensions/flutter_quill_extensions.dart' show FlutterQuillEmbeds, QuillSharedExtensionsConfigurations; - import 'package:quill_html_converter/quill_html_converter.dart'; import 'package:share_plus/share_plus.dart' show Share; import '../extensions/scaffold_messenger.dart'; import '../shared/widgets/home_screen_button.dart'; -import 'quill_editor.dart'; -import 'quill_toolbar.dart'; +import 'my_quill_editor.dart'; +import 'my_quill_toolbar.dart'; @immutable class QuillScreenArgs { @@ -46,6 +45,18 @@ class _QuillScreenState extends State { _controller.document = widget.args.document; } + // Future _init() async { + // final reader = await ClipboardReader.readClipboard(); + // if (reader.canProvide(Formats.htmlText)) { + // final html = await reader.readValue(Formats.htmlText); + // if (html == null) { + // return; + // } + // final delta = DeltaHtmlExt.fromHtml(html); + // _controller.document = Document.fromDelta(delta); + // } + // } + @override void dispose() { _controller.dispose(); @@ -65,7 +76,7 @@ class _QuillScreenState extends State { onPressed: () { final html = _controller.document.toDelta().toHtml(); _controller.document = - Document.fromDelta(DeltaHtmlExt.fromHtml(html)); + Document.fromDelta(QuillController.fromHtml(html)); }, icon: const Icon(Icons.html), ), @@ -100,40 +111,43 @@ class _QuillScreenState extends State { const HomeScreenButton(), ], ), - body: QuillProvider( - configurations: QuillConfigurations( - controller: _controller, - sharedConfigurations: QuillSharedConfigurations( - animationConfigurations: QuillAnimationConfigurations.disableAll(), - extraConfigurations: const { - QuillSharedExtensionsConfigurations.key: - QuillSharedExtensionsConfigurations( - assetsPrefix: 'assets', - ), - }, - ), - ), - child: Column( - children: [ - if (!_isReadOnly) - MyQuillToolbar( - focusNode: _editorFocusNode, - ), - Builder( - builder: (context) { - return Expanded( - child: MyQuillEditor( - configurations: QuillEditorConfigurations( - readOnly: _isReadOnly, + body: Column( + children: [ + if (!_isReadOnly) + MyQuillToolbar( + controller: _controller, + focusNode: _editorFocusNode, + ), + Builder( + builder: (context) { + return Expanded( + child: MyQuillEditor( + configurations: QuillEditorConfigurations( + sharedConfigurations: _sharedConfigurations, + controller: _controller, + readOnly: _isReadOnly, + customStyles: const DefaultStyles(), + elementOptions: const QuillEditorElementOptions( + codeBlock: QuillEditorCodeBlockElementOptions( + enableLineNumbers: true, + ), + // orderedList: QuillEditorOrderedListElementOptions( + // backgroundColor: Colors.amber, + // fontColor: Colors.black, + // ), + // unorderedList: QuillEditorUnOrderedListElementOptions( + // backgroundColor: Colors.green, + // fontColor: Colors.red, + // ), ), - scrollController: _editorScrollController, - focusNode: _editorFocusNode, ), - ); - }, - ), - ], - ), + scrollController: _editorScrollController, + focusNode: _editorFocusNode, + ), + ); + }, + ), + ], ), floatingActionButton: FloatingActionButton( child: Icon(_isReadOnly ? Icons.lock : Icons.edit), @@ -141,4 +155,16 @@ class _QuillScreenState extends State { ), ); } + + QuillSharedConfigurations get _sharedConfigurations { + return const QuillSharedConfigurations( + // locale: Locale('en'), + extraConfigurations: { + QuillSharedExtensionsConfigurations.key: + QuillSharedExtensionsConfigurations( + assetsPrefix: 'assets', // Defaults to assets + ), + }, + ); + } } diff --git a/example/lib/presentation/quill/samples/quill_images_sample.dart b/example/lib/presentation/quill/samples/quill_images_sample.dart index 9dadcd2ca..0395af1e8 100644 --- a/example/lib/presentation/quill/samples/quill_images_sample.dart +++ b/example/lib/presentation/quill/samples/quill_images_sample.dart @@ -5,11 +5,7 @@ final quillImagesSample = [ {'insert': '\n'}, { 'insert': {'image': Assets.images.screenshot1.path}, - 'attributes': { - 'width': '200', - 'height': '500', - 'style': 'width:500px; height:350px; margin: 20px;' - } + 'attributes': {'style': 'width: 40vh; height:350px; margin: 20px;'} }, {'insert': '\n'}, {'insert': 'Here is a network image: \n'}, diff --git a/example/lib/presentation/simple/simple_screen.dart b/example/lib/presentation/simple/simple_screen.dart new file mode 100644 index 000000000..a49d48ba0 --- /dev/null +++ b/example/lib/presentation/simple/simple_screen.dart @@ -0,0 +1,36 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_quill/flutter_quill.dart'; + +class SimpleScreen extends StatefulWidget { + const SimpleScreen({super.key}); + + @override + State createState() => _SimpleScreenState(); +} + +class _SimpleScreenState extends State { + final _controller = QuillController.basic(); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(), + body: Column( + children: [ + QuillToolbar.simple( + configurations: + QuillSimpleToolbarConfigurations(controller: _controller), + ), + Expanded( + child: QuillEditor.basic( + configurations: QuillEditorConfigurations( + controller: _controller, + padding: const EdgeInsets.all(16), + ), + ), + ), + ], + ), + ); + } +} diff --git a/example/linux/flutter/generated_plugin_registrant.cc b/example/linux/flutter/generated_plugin_registrant.cc index fe311fa2d..12dc8c820 100644 --- a/example/linux/flutter/generated_plugin_registrant.cc +++ b/example/linux/flutter/generated_plugin_registrant.cc @@ -8,7 +8,8 @@ #include #include -#include +#include +#include #include void fl_register_plugins(FlPluginRegistry* registry) { @@ -18,9 +19,12 @@ void fl_register_plugins(FlPluginRegistry* registry) { g_autoptr(FlPluginRegistrar) file_selector_linux_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "FileSelectorPlugin"); file_selector_plugin_register_with_registrar(file_selector_linux_registrar); - g_autoptr(FlPluginRegistrar) pasteboard_registrar = - fl_plugin_registry_get_registrar_for_plugin(registry, "PasteboardPlugin"); - pasteboard_plugin_register_with_registrar(pasteboard_registrar); + g_autoptr(FlPluginRegistrar) irondash_engine_context_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "IrondashEngineContextPlugin"); + irondash_engine_context_plugin_register_with_registrar(irondash_engine_context_registrar); + g_autoptr(FlPluginRegistrar) super_native_extensions_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "SuperNativeExtensionsPlugin"); + super_native_extensions_plugin_register_with_registrar(super_native_extensions_registrar); g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin"); url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar); diff --git a/example/linux/flutter/generated_plugins.cmake b/example/linux/flutter/generated_plugins.cmake index 3f7f250e4..b75263bb2 100644 --- a/example/linux/flutter/generated_plugins.cmake +++ b/example/linux/flutter/generated_plugins.cmake @@ -5,7 +5,8 @@ list(APPEND FLUTTER_PLUGIN_LIST desktop_drop file_selector_linux - pasteboard + irondash_engine_context + super_native_extensions url_launcher_linux ) diff --git a/example/macos/Flutter/GeneratedPluginRegistrant.swift b/example/macos/Flutter/GeneratedPluginRegistrant.swift index 6b3c06b5f..bf1bbe800 100644 --- a/example/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/example/macos/Flutter/GeneratedPluginRegistrant.swift @@ -9,10 +9,11 @@ import desktop_drop import device_info_plus import file_selector_macos import gal -import pasteboard +import irondash_engine_context import path_provider_foundation import share_plus import sqflite +import super_native_extensions import url_launcher_macos import video_player_avfoundation @@ -21,10 +22,11 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin")) FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin")) GalPlugin.register(with: registry.registrar(forPlugin: "GalPlugin")) - PasteboardPlugin.register(with: registry.registrar(forPlugin: "PasteboardPlugin")) + IrondashEngineContextPlugin.register(with: registry.registrar(forPlugin: "IrondashEngineContextPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin")) SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin")) + SuperNativeExtensionsPlugin.register(with: registry.registrar(forPlugin: "SuperNativeExtensionsPlugin")) UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) FVPVideoPlayerPlugin.register(with: registry.registrar(forPlugin: "FVPVideoPlayerPlugin")) } diff --git a/example/macos/Podfile.lock b/example/macos/Podfile.lock index 01dc23931..a562380ae 100644 --- a/example/macos/Podfile.lock +++ b/example/macos/Podfile.lock @@ -12,7 +12,7 @@ PODS: - gal (1.0.0): - Flutter - FlutterMacOS - - pasteboard (0.0.1): + - irondash_engine_context (0.0.1): - FlutterMacOS - path_provider_foundation (0.0.1): - Flutter @@ -22,6 +22,8 @@ PODS: - sqflite (0.0.2): - FlutterMacOS - FMDB (>= 2.7.5) + - super_native_extensions (0.0.1): + - FlutterMacOS - url_launcher_macos (0.0.1): - FlutterMacOS - video_player_avfoundation (0.0.1): @@ -34,10 +36,11 @@ DEPENDENCIES: - file_selector_macos (from `Flutter/ephemeral/.symlinks/plugins/file_selector_macos/macos`) - FlutterMacOS (from `Flutter/ephemeral`) - gal (from `Flutter/ephemeral/.symlinks/plugins/gal/darwin`) - - pasteboard (from `Flutter/ephemeral/.symlinks/plugins/pasteboard/macos`) + - irondash_engine_context (from `Flutter/ephemeral/.symlinks/plugins/irondash_engine_context/macos`) - path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin`) - share_plus (from `Flutter/ephemeral/.symlinks/plugins/share_plus/macos`) - sqflite (from `Flutter/ephemeral/.symlinks/plugins/sqflite/macos`) + - super_native_extensions (from `Flutter/ephemeral/.symlinks/plugins/super_native_extensions/macos`) - url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`) - video_player_avfoundation (from `Flutter/ephemeral/.symlinks/plugins/video_player_avfoundation/darwin`) @@ -56,14 +59,16 @@ EXTERNAL SOURCES: :path: Flutter/ephemeral gal: :path: Flutter/ephemeral/.symlinks/plugins/gal/darwin - pasteboard: - :path: Flutter/ephemeral/.symlinks/plugins/pasteboard/macos + irondash_engine_context: + :path: Flutter/ephemeral/.symlinks/plugins/irondash_engine_context/macos path_provider_foundation: :path: Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin share_plus: :path: Flutter/ephemeral/.symlinks/plugins/share_plus/macos sqflite: :path: Flutter/ephemeral/.symlinks/plugins/sqflite/macos + super_native_extensions: + :path: Flutter/ephemeral/.symlinks/plugins/super_native_extensions/macos url_launcher_macos: :path: Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos video_player_avfoundation: @@ -76,10 +81,11 @@ SPEC CHECKSUMS: FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24 FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a gal: 61e868295d28fe67ffa297fae6dacebf56fd53e1 - pasteboard: 9b69dba6fedbb04866be632205d532fe2f6b1d99 + irondash_engine_context: da62996ee25616d2f01bbeb85dc115d813359478 path_provider_foundation: 29f094ae23ebbca9d3d0cec13889cd9060c0e943 share_plus: 76dd39142738f7a68dd57b05093b5e8193f220f7 sqflite: a5789cceda41d54d23f31d6de539d65bb14100ea + super_native_extensions: 85efee3a7495b46b04befcfc86ed12069264ebf3 url_launcher_macos: d2691c7dd33ed713bf3544850a623080ec693d95 video_player_avfoundation: e9e6f9cae7d7a6d9b43519b0aab382bca60fcfd1 diff --git a/example/pubspec.yaml b/example/pubspec.yaml index 441cb8355..b3d2e15c1 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -13,8 +13,8 @@ dependencies: cupertino_icons: ^1.0.6 # Flutter Quill Packages - flutter_quill: ^8.6.2 - flutter_quill_extensions: ^0.7.0 + flutter_quill: ^8.6.4 + flutter_quill_extensions: ^0.7.2 flutter_quill_test: ^0.0.5 quill_html_converter: ^0.0.1-experimental.1 @@ -24,11 +24,6 @@ dependencies: cross_file: ^0.3.3+6 cached_network_image: ^3.3.0 - gal_linux: - git: - url: https://github.com/freshtechtips/gal-linux.git - ref: main - # Bloc libraries bloc: ^8.1.2 flutter_bloc: ^8.1.3 @@ -49,6 +44,7 @@ dependencies: file_picker: ^6.1.1 # For sharing text share_plus: ^7.2.1 + super_clipboard: ^0.7.3 dependency_overrides: flutter_quill: @@ -60,11 +56,6 @@ dependency_overrides: quill_html_converter: path: ../packages/quill_html_converter - gal: - git: - url: https://github.com/natsuk4ze/gal.git - ref: plugin_platform_interface - dev_dependencies: flutter_test: diff --git a/example/windows/flutter/generated_plugin_registrant.cc b/example/windows/flutter/generated_plugin_registrant.cc index ef6dc9060..084810413 100644 --- a/example/windows/flutter/generated_plugin_registrant.cc +++ b/example/windows/flutter/generated_plugin_registrant.cc @@ -9,8 +9,9 @@ #include #include #include -#include +#include #include +#include #include void RegisterPlugins(flutter::PluginRegistry* registry) { @@ -20,10 +21,12 @@ void RegisterPlugins(flutter::PluginRegistry* registry) { registry->GetRegistrarForPlugin("FileSelectorWindows")); GalPluginCApiRegisterWithRegistrar( registry->GetRegistrarForPlugin("GalPluginCApi")); - PasteboardPluginRegisterWithRegistrar( - registry->GetRegistrarForPlugin("PasteboardPlugin")); + IrondashEngineContextPluginCApiRegisterWithRegistrar( + registry->GetRegistrarForPlugin("IrondashEngineContextPluginCApi")); SharePlusWindowsPluginCApiRegisterWithRegistrar( registry->GetRegistrarForPlugin("SharePlusWindowsPluginCApi")); + SuperNativeExtensionsPluginCApiRegisterWithRegistrar( + registry->GetRegistrarForPlugin("SuperNativeExtensionsPluginCApi")); UrlLauncherWindowsRegisterWithRegistrar( registry->GetRegistrarForPlugin("UrlLauncherWindows")); } diff --git a/example/windows/flutter/generated_plugins.cmake b/example/windows/flutter/generated_plugins.cmake index abc6f6272..f568d1445 100644 --- a/example/windows/flutter/generated_plugins.cmake +++ b/example/windows/flutter/generated_plugins.cmake @@ -6,8 +6,9 @@ list(APPEND FLUTTER_PLUGIN_LIST desktop_drop file_selector_windows gal - pasteboard + irondash_engine_context share_plus + super_native_extensions url_launcher_windows ) diff --git a/flutter_quill_extensions/CHANGELOG.md b/flutter_quill_extensions/CHANGELOG.md index 5cf7f2802..09d629a35 100644 --- a/flutter_quill_extensions/CHANGELOG.md +++ b/flutter_quill_extensions/CHANGELOG.md @@ -2,157 +2,1625 @@ All notable changes to this project will be documented in this file. -## 0.8.0-dev -* **Breaking Change**: Completly change the way how the source code structured to more basic and simple way, organize folders and file names, if you use the library +## 9.0.0-dev-8 +* Better support for pasting HTML contents from external websites to the editor +* The experimental support of converting the HTML from `quill_html_converter` is now built-in in the `flutter_quill` and removed from there (Breaking change for `quill_html_converter`) + +## 9.0.0-dev-7 +* Fix a bug in chaning the background/font color of ol/ul list +* Flutter Quill Extensions: + * Fix link bug in the video url + * Fix patterns + +## 9.0.0-dev-6 +* Move the `child` from `QuillToolbarConfigurations` into `QuillToolbar` directly +* Bug fixes +* Add the ability to change the background and font color of the ol/ul elements dots and numbers +* Flutter Quill Extensions: + * **Breaking Change**: The `imageProviderBuilder`is now providing the context and image url + +## 9.0.0-dev-5 +* The `QuillToolbar` is now accepting only `child` with no configurations so you can customize everything you wants, the `QuillToolbar.simple()` or `QuillSimpleToolbar` implements a simple toolbar that is based on `QuillToolbar`, you are free to use it but it just an example and not standard +* Flutter Quill Extensions: + * Improve the camera button + +## 9.0.0-dev-4 +* The options parameter in all of the buttons is no longer required which can be useful to create custom toolbar with minimal efforts +* Toolbar buttons fixes in both `flutter_quill` and `flutter_quill_extensions` +* The `QuillProvider` has been dropped and no longer used, the providers will be used only internally from now on and we will not using them as much as possible + +## 9.0.0-dev-3 +* Breaking Changes: + * Rename `QuillToolbar` to `QuillSimpleToolbar` + * Rename `QuillBaseToolbar` to `QuillToolbar` + * Replace `pasteboard` with `rich_cliboard` +* Fix a bug in the example when inserting an image from url +* Flutter Quill Extensions: + * Add support for copying the image to the system cliboard + +## 9.0.0-dev-2 +* An attemp to fix CI automated publishing + +## 9.0.0-dev-1 +* An attemp to fix CI automated publishing + +## 9.0.0-dev +* **Major Breaking change**: The `QuillProvider` is now optional, the `controller` parameter has been moved to the `QuillEditor` and `QuillToolbar` once again. +* Flutter Quill Extensions; + * **Breaking Change**: Completly change the way how the source code structured to more basic and simple way, organize folders and file names, if you use the library from `flutter_quill_extensions.dart` then there is nothing you need to do, but if you are using any other import then you need to re-imports embed, this won't affect how quill js work -* Improvemenets to the image embed -* Add support for `margin` for web -* Add untranslated strings to the `quill_en.arb` + * Improvemenets to the image embed + * Add support for `margin` for web + * Add untranslated strings to the `quill_en.arb` + +## 8.6.4 +* The default value of `keyboardAppearance` for the iOS will be the one from the App/System theme mode instead of always using the `Brightness.light` +* Fix typos in `README.md` + +## 8.6.3 +* Update the minimum flutter version to `3.16.0` + +## 8.6.2 +* Restore use of alternative QuillToolbarLinkStyleButton2 widget + +## 8.6.1 +* Temporary revert style bug fix + +## 8.6.0 +* **Breaking Change** Support [Flutter 3.16](https://medium.com/flutter/whats-new-in-flutter-3-16-dba6cb1015d1), please upgrade to the latest stable version of flutter to use this update +* **Breaking Change**: Remove Deprecated Fields +* **Breaking Change**: Extract the shared things between `QuillToolbarConfigurations` and `QuillBaseToolbarConfigurations` +* **Breaking Change**: You no longer need to use `QuillToolbarProvider` when using custom toolbar buttons, the example has been updated +* Bug fixes + +## 8.5.5 +* Now when opening dialogs by `QuillToolbar` you will not get an exception when you don't use `FlutterQuillLocalizations.delegate` in your `WidgetsApp`, `MaterialApp`, or `CupertinoApp`. The fix is for the `QuillToolbarSearchButton`, `QuillToolbarLinkStyleButton`, and `QuillToolbarColorButton` buttons + +## 8.5.4 +* The `mobileWidth`, `mobileHeight`, `mobileMargin`, and `mobileAlignment` is now deprecated in `flutter_quill`, they are now defined in `flutter_quill_extensions` +* Deprecate `replaceStyleStringWithSize` function which is in `string.dart` +* Deprecate `alignment`, and `margin` as they don't conform to official Quill JS + +## 8.5.3 +* Update doc +* Update `README.md` and `CHANGELOG.md` +* Fix typos +* Use `immutable` when possible +* Update `.pubignore` + +## 8.5.2 +* Updated `README.md`. +* Feature: Added the ability to include a custom callback when the `QuillToolbarColorButton` is pressed. +* The `QuillToolbar` now implements `PreferredSizeWidget`, enabling usage in the AppBar, similar to `QuillBaseToolbar`. + +## 8.5.1 +* Updated `README.md`. + +## 8.5.0 +* Migrated to `flutter_localizations` for translations. +* Fixed: Translated all previously untranslated localizations. +* Fixed: Added translations for missing items. +* Fixed: Introduced default Chinese fallback translation. +* Removed: Unused parameters `items` in `QuillToolbarFontFamilyButtonOptions` and `QuillToolbarFontSizeButtonOptions`. +* Updated: Documentation. + +## 8.4.4 +* Updated `.pubignore` to ignore unnecessary files and folders. + +## 8.4.3 +* Updated `CHANGELOG.md`. + +## 8.4.2 +* **Breaking change**: Configuration for `QuillRawEditor` has been moved to a separate class. Additionally, `readOnly` has been renamed to `isReadOnly`. If using `QuillEditor`, no action is required. +* Introduced the ability for developers to override `TextInputAction` in both `QuillRawEditor` and `QuillEditor`. +* Enabled using `QuillRawEditor` without `QuillEditorProvider`. +* Bug fixes. +* Added image cropping implementation in the example. + +## 8.4.1 +* Added `copyWith` in `OptionalSize` class. + +## 8.4.0 +* **Breaking change**: Updated `QuillCustomButton` to use `QuillCustomButtonOptions`. Moved all properties from `QuillCustomButton` to `QuillCustomButtonOptions`, replacing `iconData` with `icon` widget for increased customization. +* **Breaking change**: `customButtons` in `QuillToolbarConfigurations` is now of type `List`. +* Bug fixes following the `8.0.0` update. +* Updated `README.md`. +* Improved platform checking. + +## 8.3.0 +* Added `iconButtonFactor` property to `QuillToolbarBaseButtonOptions` for customizing button size relative to its icon size (defaults to `kIconButtonFactor`, consistent with previous releases). + +## 8.2.6 +* Organized `QuillRawEditor` code. + +## 8.2.5 +* Added `builder` property in `QuillEditorConfigurations`. + +## 8.2.4 +* Adhered to Flutter best practices. +* Fixed auto-focus bug. + +## 8.2.3 +* Updated `README.md`. + +## 8.2.2 +* Moved `flutter_quill_test` to a separate package: [flutter_quill_test](https://pub.dev/packages/flutter_quill_test). + +## 8.2.1 +* Updated `README.md`. + +## 8.2.0 +* Added the option to add configurations for `flutter_quill_extensions` using `extraConfigurations`. + +## 8.1.11 +* Followed Dart best practices by using `lints` and removed `pedantic` and `platform` since they are not used. +* Fixed text direction bug. +* Updated `README.md`. + +## 8.1.10 +* Secret for automated publishing to pub.dev. + +## 8.1.9 +* Fixed automated publishing to pub.dev. + +## 8.1.8 +* Fixed automated publishing to pub.dev. + +## 8.1.7 +* Automated publishing to pub.dev. + +## 8.1.6 +* Fixed compatibility with `integration_test` by downgrading the minimum version of the platform package to 3.1.0. + +## 8.1.5 +* Reversed background/font color toolbar button icons. + +## 8.1.4 +* Reversed background/font color toolbar button tooltips. + +## 8.1.3 +* Moved images to screenshots instead of `README.md`. + +## 8.1.2 +* Fixed a bug related to the regexp of the insert link dialog. +* Required Dart 3 as the minimum version. +* Code cleanup. +* Added a spacer widget between each button in the `QuillToolbar`. + +## 8.1.1 +* Fixed null error in line.dart #1487(https://github.com/singerdmx/flutter*quill/issues/1487). + +## 8.1.0 +* Fixed a word typo of `mirgration` to `migration` in the readme & migration document. +* Updated migration guide. +* Removed property `enableUnfocusOnTapOutside` in `QuillEditor` configurations and added `isOnTapOutsideEnabled` instead. +* Added a new callback called `onTapOutside` in the `QuillEditorConfigurations` to perform actions when tapping outside the editor. +* Fixed a bug that caused the web platform to not unfocus the editor when tapping outside of it. To override this, please pass a value to the `onTapOutside` callback. +* Removed the old property of `iconTheme`. Instead, pass `iconTheme` in the button options; you will find the `base` property inside it with `iconTheme`. + +## 8.0.0 +* If you have migrated recently, don't be alarmed by this update; it adds documentation, a migration guide, and marks the version as a more stable release. Although there are breaking changes (as reported by some developers), the major version was not changed due to time constraints during development. A single property was also renamed from `code` to `codeBlock` in the `elements` of the new `QuillEditorConfigurations` class. +* Updated the README for better readability. + +## 7.10.2 +* Removed line numbers from code blocks by default. You can still enable this feature thanks to the new configurations in the `QuillEditor`. Find the `elementOptions` property and enable `enableLineNumbers`. + +## 7.10.1 +* Fixed issues and utilized the new parameters. +* No longer need to use `MaterialApp` for most toolbar button child builders. +* Compatibility with [fresh_quill_extensions](https://pub.dev/packages/fresh_quill_extensions), a temporary alternative to [flutter_quill_extensions](https://pub.dev/packages/flutter_quill_extensions). +* Updated most of the documentation in `README.md`. + +## 7.10.0 +* **Breaking change**: `QuillToolbar.basic()` can be accessed directly from `QuillToolbar()`, and the old `QuillToolbar` can be accessed from `QuillBaseToolbar`. +* Refactored Quill editor and toolbar configurations into a single class each. +* After changing checkbox list values, the controller will not request keyboard focus by default. +* Moved toolbar and editor configurations directly into the widget but still use inherited widgets internally. +* Fixes to some code after the refactoring. + +## 7.9.0 +* Buttons Improvemenets +* Refactor all the button configurations that used in `QuillToolbar.basic()` but there are still few lefts +* **Breaking change**: Remove some configurations from the QuillToolbar and move them to the new `QuillProvider`, please notice this is a development version and this might be changed in the next few days, the stable release will be ready in less than 3 weeks +* Update `flutter_quill_extensions` and it will be published into pub.dev soon. +* Allow you to customize the search dialog by custom callback with child builder + +## 7.8.0 +* **Important note**: this is not test release yet, it works but need more test and changes and breaking changes, we don't have development version and it will help us if you try the latest version and report the issues in Github but if you want a stable version please use `7.4.16`. this refactoring process will not take long and should be done less than three weeks with the testing. +* We managed to refactor most of the buttons configurations and customizations in the `QuillProvider`, only three lefts then will start on refactoring the toolbar configurations +* Code improvemenets + +## 7.7.0 +* **Breaking change**: We have mirgrated more buttons in the toolbar configurations, you can do change them in the `QuillProvider` +* Important bug fixes + +## 7.6.1 +* Bug fixes + +## 7.6.0 +* **Breaking change**: To customize the buttons in the toolbar, you can do that in the `QuillProvider` + +## 7.5.0 +* **Breaking change**: The widgets `QuillEditor` and `QuillToolbar` are no longer have controller parameter, instead you need to make sure in the widget tree you have wrapped them with `QuillProvider` widget and provide the controller and the require configurations + +## 7.4.16 +* Update documentation and README.md + +## 7.4.15 +* Custom style attrbuites for platforms other than mobile (alignment, margin, width, height) +* Bug fixes and other improvemenets + +## 7.4.14 +* Improve performance by reducing the number of widgets rebuilt by listening to media query for only the needed things, for example instead of using `MediaQuery.of(context).size`, now we are using `MediaQuery.sizeOf(context)` +* Add MediaButton for picking the images only since the video one is not ready +* A new feature which allows customizing the text selection in quill editor which is useful for custom theme design system for custom app widget + +## 7.4.13 +* Fixed tab editing when in readOnly mode. + +## 7.4.12 +* Update the minimum version of device_info_plus to 9.1.0. + +## 7.4.11 +* Add sw locale. + +## 7.4.10 +* Update translations. + +## 7.4.9 +* Style recognition fixes. + +## 7.4.8 +* Upgrade dependencies. + +## 7.4.7 +* Add Vietnamese and German translations. + +## 7.4.6 +* Fix more null errors in Leaf.retain [##1394](https://github.com/singerdmx/flutter-quill/issues/1394) and Line.delete [##1395](https://github.com/singerdmx/flutter-quill/issues/1395). + +## 7.4.5 +* Fix null error in Container.insert [##1392](https://github.com/singerdmx/flutter-quill/issues/1392). + +## 7.4.4 +* Fix extra padding on checklists [##1131](https://github.com/singerdmx/flutter-quill/issues/1131). + +## 7.4.3 +* Fixed a space input error on iPad. + +## 7.4.2 +* Fix bug with keepStyleOnNewLine for link. + +## 7.4.1 +* Fix toolbar dividers condition. + +## 7.4.0 +* Support Flutter version 3.13.0. + +## 7.3.3 +* Updated Dependencies conflicting. + +## 7.3.2 +* Added builder for custom button in _LinkDialog. + +## 7.3.1 +* Added case sensitive and whole word search parameters. +* Added wrap around. +* Moved search dialog to the bottom in order not to override the editor and the text found. +* Other minor search dialog enhancements. + +## 7.3.0 +* Add default attributes to basic factory. + +## 7.2.19 +* Feat/link regexp. + +## 7.2.18 +* Fix paste block text in words apply same style. + +## 7.2.17 +* Fix paste text mess up style. +* Add support copy/cut block text. + +## 7.2.16 +* Allow for custom context menu. + +## 7.2.15 +* Add flutter_quill.delta library which only exposes Delta datatype. + +## 7.2.14 +* Fix errors when the editor is used in the `screenshot` package. + +## 7.2.13 +* Fix around image can't delete line break. + +## 7.2.12 +* Add support for copy/cut select image and text together. + +## 7.2.11 +* Add affinity for localPosition. + +## 7.2.10 +* LINE._getPlainText queryChild inclusive=false. + +## 7.2.9 +* Add toPlainText method to `EmbedBuilder`. + +## 7.2.8 +* Add custom button widget in toolbar. + +## 7.2.7 +* Fix language code of Japan. + +## 7.2.6 +* Style custom toolbar buttons like builtins. + +## 7.2.5 +* Always use text cursor for editor on desktop. + +## 7.2.4 +* Fixed keepStyleOnNewLine. + +## 7.2.3 +* Get pixel ratio from view. + +## 7.2.2 +* Prevent operations on stale editor state. + +## 7.2.1 +* Add support for android keyboard content insertion. +* Enhance color picker, enter hex color and color palette option. + +## 7.2.0 +* Checkboxes, bullet points, and number points are now scaled based on the default paragraph font size. + +## 7.1.20 +* Pass linestyle to embedded block. + +## 7.1.19 +* Fix Rtl leading alignment problem. + +## 7.1.18 +* Support flutter latest version. + +## 7.1.17+1 +* Updates `device_info_plus` to version 9.0.0 to benefit from AGP 8 (see [changelog##900](https://pub.dev/packages/device_info_plus/changelog##900)). + +## 7.1.16 +* Fixed subscript key from 'sup' to 'sub'. + +## 7.1.15 +* Fixed a bug introduced in 7.1.7 where each section in `QuillToolbar` was displayed on its own line. + +## 7.1.14 +* Add indents change for multiline selection. + +## 7.1.13 + +* Add custom recognizer. + +## 7.1.12 + +* Add superscript and subscript styles. + +## 7.1.11 + +* Add inserting indents for lines of list if text is selected. + +## 7.1.10 + +* Image embedding tweaks + * Add MediaButton which is intened to superseed the ImageButton and VideoButton. Only image selection is working. + * Implement image insert for web (image as base64) + +## 7.1.9 + +* Editor tweaks PR from bambinoua(https://github.com/bambinoua). + * Shortcuts now working in Mac OS + * QuillDialogTheme is extended with new properties buttonStyle, linkDialogConstraints, imageDialogConstraints, isWrappable, runSpacing, + * Added LinkStyleButton2 with new LinkStyleDialog (similar to Quill implementation + * Conditinally use Row or Wrap for dialog's children. + * Update minimum Dart SDK version to 2.17.0 to use enum extensions. + * Use merging shortcuts and actions correclty (if the key combination is the same) + +## 7.1.8 + +* Dropdown tweaks + * Add itemHeight, itemPadding, defaultItemColor for customization of dropdown items. + * Remove alignment property as useless. + * Fix bugs with max width when width property is null. + +## 7.1.7 + +* Toolbar tweaks. + * Implement tooltips for embed CameraButton, VideoButton, FormulaButton, ImageButton. + * Extends customization for SelectAlignmentButton, QuillFontFamilyButton, QuillFontSizeButton adding padding, text style, alignment, width. + * Add renderFontFamilies to QuillFontFamilyButton to show font faces in dropdown. + * Add AxisDivider and its named constructors for for use in parent project. + * Export ToolbarButtons enum to allow specify tooltips for SelectAlignmentButton. + * Export QuillFontFamilyButton, SearchButton as they were not exported before. + * Deprecate items property in QuillFontFamilyButton, QuillFontSizeButton as the it can be built usinr rawItemsMap. + * Make onSelection QuillFontFamilyButton, QuillFontSizeButton omittable as no need to execute callback outside if controller is passed to widget. + +Now the package is more friendly for web projects. + +## 7.1.6 + +* Add enableUnfocusOnTapOutside field to RawEditor and Editor widgets. + +## 7.1.5 + +* Add tooltips for toolbar buttons. + +## 7.1.4 + +* Fix inserting tab character in lists. + +## 7.1.3 + +* Fix ios cursor bug when word.length==1. + +## 7.1.2 + +* Fix non scrollable editor exception, when tapped under content. + +## 7.1.1 + +* customLinkPrefixes parameter * makes possible to open links with custom protoco. + +## 7.1.0 + +* Fix ordered list numeration with several lists in document. + +## 7.0.9 + +* Use const constructor for EmbedBuilder. + +## 7.0.8 + +* Fix IME position bug with scroller. + +## 7.0.7 + +* Add TextFieldTapRegion for contextMenu. + +## 7.0.6 + +* Fix line style loss on new line from non string. + +## 7.0.5 + +* Fix IME position bug for Mac and Windows. +* Unfocus when tap outside editor. fix the bug that cant refocus in afterButtonPressed after click ToggleStyleButton on Mac. + +## 7.0.4 + +* Have text selection span full line height for uneven sized text. + +## 7.0.3 + +* Fix ordered list numeration for lists with more than one level of list. + +## 7.0.2 + +* Allow widgets to override widget span properties. + +## 7.0.1 + +* Update i18n_extension dependency to version 8.0.0. + +## 7.0.0 + +* Breaking change: Tuples are no longer used. They have been replaced with a number of data classes. + +## 6.4.4 + +* Increased compatibility with Flutter widget tests. + +## 6.4.3 + +* Update dependencies (collection: 1.17.0, flutter_keyboard_visibility: 5.4.0, quiver: 3.2.1, tuple: 2.0.1, url_launcher: 6.1.9, characters: 1.2.1, i18n_extension: 7.0.0, device_info_plus: 8.1.0) + +## 6.4.2 + +* Replace `buildToolbar` with `contextMenuBuilder`. + +## 6.4.1 + +* Control the detect word boundary behaviour. + +## 6.4.0 + +* Use `axis` to make the toolbar vertical. +* Use `toolbarIconCrossAlignment` to align the toolbar icons on the cross axis. +* Breaking change: `QuillToolbar`'s parameter `toolbarHeight` was renamed to `toolbarSize`. + +## 6.3.5 + +* Ability to add custom shortcuts. + +## 6.3.4 + +* Update clipboard status prior to showing selected text overlay. + +## 6.3.3 + +* Fixed handling of mac intents. + +## 6.3.2 + +* Added `unknownEmbedBuilder` to QuillEditor. +* Fix error style when input chinese japanese or korean. + +## 6.3.1 + +* Add color property to the basic factory function. + +## 6.3.0 + +* Support Flutter 3.7. + +## 6.2.2 + +* Fix: nextLine getter null where no assertion. + +## 6.2.1 + +* Revert "Align numerical and bullet lists along with text content". + +## 6.2.0 + +* Align numerical and bullet lists along with text content. + +## 6.1.12 + +* Apply i18n for default font dropdown option labels corresponding to 'Clear'. + +## 6.1.11 + +* Remove iOS hack for delaying focus calculation. + +## 6.1.10 + +* Delay focus calculation for iOS. + +## 6.1.9 + +* Bump keyboard show up wait to 1 sec. + +## 6.1.8 + +* Recalculate focus when showing keyboard. + +## 6.1.7 + +* Add czech localizations. + +## 6.1.6 + +* Upgrade i18n_extension to 6.0.0. + +## 6.1.5 + +* Fix formatting exception. + +## 6.1.4 + +* Add double quotes validation. + +## 6.1.3 + +* Revert "fix order list numbering (##988)". + +## 6.1.2 + +* Add typing shortcuts. + +## 6.1.1 + +* Fix order list numbering. + +## 6.1.0 + +* Add keyboard shortcuts for editor actions. + +## 6.0.10 + +* Upgrade device info plus to ^7.0.0. + +## 6.0.9 + +* Don't throw showAutocorrectionPromptRect not implemented. The function is called with every keystroke as a user is typing. + +## 6.0.8+1 + +* Fixes null pointer when setting documents. + +## 6.0.8 + +* Make QuillController.document mutable. + +## 6.0.7 + +* Allow disabling of selection toolbar. + +## 6.0.6+1 + +* Revert 6.0.6. + +## 6.0.6 + +* Fix wrong custom embed key. + +## 6.0.5 + +* Fixes toolbar buttons stealing focus from editor. + +## 6.0.4 + +* Bug fix for Type 'Uint8List' not found. + +## 6.0.3 + +* Add ability to paste images. + +## 6.0.2 + +* Address Dart Analysis issues. + +## 6.0.1 + +* Changed translation country code (zh_HK -> zh_hk) to lower case, which is required for i18n_extension used in flutter_quill. +* Add localization in example's main to demonstrate translation. +* Issue Windows selection's copy / paste tool bar not shown ##861: add selection's copy / paste toolbar, escape to hide toolbar, mouse right click to show toolbar, ctrl-Y / ctrl-Z to undo / redo. +* Image and video displayed in Windows platform caused screen flickering while selecting text, a sample_data_nomedia.json asset is added for Desktop to demonstrate the added features. +* Known issue: keyboard action sometimes causes exception mentioned in Flutter's issue ##106475 (Windows Keyboard shortcuts stop working after modifier key repeat flutter/flutter##106475). +* Know issue: user needs to click the editor to get focus before toolbar is able to display. + +## 6.0.0 BREAKING CHANGE + +* Removed embed (image, video & formula) blocks from the package to reduce app size. + +These blocks have been moved to the package `flutter_quill_extensions`, migrate by filling the `embedBuilders` and `embedButtons` parameters as follows: + +``` +import 'package:flutter_quill_extensions/flutter_quill_extensions.dart'; + +QuillEditor.basic( + controller: controller, + embedBuilders: FlutterQuillEmbeds.builders(), +); + +QuillToolbar.basic( + controller: controller, + embedButtons: FlutterQuillEmbeds.buttons(), +); +``` + +## 5.4.2 + +* Upgrade i18n_extension. + +## 5.4.1 + +* Update German Translation. + +## 5.4.0 + +* Added Formula Button (for maths support). + +## 5.3.2 + +* Add more font family. + +## 5.3.1 + +* Enable search when text is not empty. + +## 5.3.0 + +* Added search function. + +## 5.2.11 + +* Remove default small color. + +## 5.2.10 + +* Don't wrap the QuillEditor's child in the EditorTextSelectionGestureDetector if selection is disabled. + +## 5.2.9 + +* Added option to modify SelectHeaderStyleButton options. +* Added option to click again on h1, h2, h3 button to go back to normal. + +## 5.2.8 + +* Remove tooltip for LinkStyleButton. +* Make link match regex case insensitive. + +## 5.2.7 + +* Add locale to QuillEditor.basic. + +## 5.2.6 + +* Fix keyboard pops up when resizing the image. + +## 5.2.5 + +* Upgrade youtube_player_flutter_quill to 8.2.2. + +## 5.2.4 + +* Upgrade youtube_player_flutter_quill to 8.2.1. + +## 5.2.3 + +* Flutter Quill Doesn't Work On iOS 16 or Xcode 14 Betas (Stored properties cannot be marked potentially unavailable with '@available'). + +## 5.2.2 + +* Fix Web Unsupported operation: Platform.\_operatingSystem error. + +## 5.2.1 + +* Rename QuillCustomIcon to QuillCustomButton. + +## 5.2.0 + +* Support font family selection. + +## 5.1.1 + +* Update README. + +## 5.1.0 + +* Added CustomBlockEmbed and customElementsEmbedBuilder. + +## 5.0.5 + +* Upgrade device_info_plus to 4.0.0. + +## 5.0.4 + +* Added onVideoInit callback for video documents. + +## 5.0.3 + +* Update dependencies. + +## 5.0.2 + +* Keep cursor position on checkbox tap. + +## 5.0.1 + +* Fix static analysis errors. + +## 5.0.0 + +* Flutter 3.0.0 support. + +## 4.2.3 + +* Ignore color:inherit and convert double to int for level. + +## 4.2.2 + +* Add clear option to font size dropdown. + +## 4.2.1 + +* Refactor font size dropdown. + +## 4.2.0 + +* Ensure selectionOverlay is available for showToolbar. + +## 4.1.9 + +* Using properly iconTheme colors. + +## 4.1.8 + +* Update font size dropdown. + +## 4.1.7 + +* Convert FontSize to a Map to allow for named Font Size. + +## 4.1.6 + +* Update quill_dropdown_button.dart. + +## 4.1.5 + +* Add Font Size dropdown to the toolbar. + +## 4.1.4 + +* New borderRadius for iconTheme. + +## 4.1.3 + +* Fix selection handles show/hide after paste, backspace, copy. + +## 4.1.2 + +* Add full support for hardware keyboards (Chromebook, Android tablets, etc) that don't alter screen UI. + +## 4.1.1 + +* Added textSelectionControls field in QuillEditor. + +## 4.1.0 + +* Added Node to linkActionPickerDelegate. + +## 4.0.12 + +* Add Persian(fa) language. + +## 4.0.11 + +* Fix cut selection error in multi-node line. + +## 4.0.10 + +* Fix vertical caret position bug. + +## 4.0.9 + +* Request keyboard focus when no child is found. + +## 4.0.8 + +* Fix blank lines do not display when **web*renderer=html. + +## 4.0.7 + +* Refactor getPlainText (better handling of blank lines and lines with multiple markups. + +## 4.0.6 + +* Bug fix for copying text with new lines. + +## 4.0.5 + +* Fixed casting null to Tuple2 when link dialog is dismissed without any input (e.g. barrier dismissed). + +## 4.0.4 + +* Bug fix for text direction rtl. + +## 4.0.3 + +* Support text direction rtl. + +## 4.0.2 + +* Clear toggled style on selection change. + +## 4.0.1 + +* Fix copy/cut/paste/selectAll not working. + +## 4.0.0 + +* Upgrade for Flutter 2.10. + +## 3.9.11 + +* Added Indonesian translation. + +## 3.9.10 + +* Fix for undoing a modification ending with an indented line. + +## 3.9.9 + +* iOS: Save image whose filename does not end with image file extension. + +## 3.9.8 + +* Added Urdu translation. + +## 3.9.7 + +* Fix for clicking on the Link button without any text on a new line crashes. + +## 3.9.6 + +* Apply locale to QuillEditor(contents). + +## 3.9.5 + +* Fix image pasting. + +## 3.9.4 + +* Hiding dialog after selecting action for image. + +## 3.9.3 + +* Update ImageResizer for Android. + +## 3.9.2 + +* Copy image with its style. + +## 3.9.1 + +* Support resizing image. + +## 3.9.0 + +* Image menu options for copy/remove. + +## 3.8.8 + +* Update set textEditingValue. + +## 3.8.7 + +* Fix checkbox not toggled correctly in toolbar button. + +## 3.8.6 + +* Fix cursor position changes when checking/unchecking the checkbox. + +## 3.8.5 + +* Fix \_handleDragUpdate in \_TextSelectionHandleOverlayState. + +## 3.8.4 + +* Fix link dialog layout. + +## 3.8.3 + +* Fix for errors on a non scrollable editor. + +## 3.8.2 + +* Fix certain keys not working on web when editor is a child of a scroll view. + +## 3.8.1 + +* Refactor \_QuillEditorState to QuillEditorState. + +## 3.8.0 + +* Support pasting with format. + +## 3.7.3 + +* Fix selection overlay for collapsed selection. + +## 3.7.2 + +* Reverted Embed toPlainText change. + +## 3.7.1 + +* Change Embed toPlainText to be empty string. + +## 3.7.0 + +* Replace Toolbar showHistory group with individual showRedo and showUndo. + +## 3.6.5 + +* Update Link dialogue for image/video. + +## 3.6.4 + +* Link dialogue TextInputType.multiline. + +## 3.6.3 + +* Bug fix for link button text selection. + +## 3.6.2 + +* Improve link button. + +## 3.6.1 + +* Remove SnackBar 'What is entered is not a link'. + +## 3.6.0 -## 0.7.2 -* Fix a bug when opening the link dialog for both video and image buttons -* Update `README.md` +* Allow link button to enter text. -## 0.7.1 -* Update the minimum flutter version to `3.16.0` +## 3.5.3 -## 0.7.0 -* The `FlutterQuillLocalizations.delegate` is no longer a requirement. -* Requiring `flutter_quill` version `8.6.0` as minimum +* Change link button behavior. -## 0.6.11 -* Support for the latest version of `flutter_quill` +## 3.5.2 -## 0.6.10 -* Update deprecated members from `flutter_quill` -* Update doc and `README.md` +* Bug fix for embed. -## 0.6.9 -* Remove duplicated class -* Drop the support for `QuillEditorFormulaEmbedBuilder` for now as it's not usable, we are working on providing fixes -* Fix a bug with the zoom button +## 3.5.1 -## 0.6.8 -* Feature: Allow the developer to override the `assetsPrefix` and the default value is `assets`, you should define this correctly if you planning on using asset images in the `QuillEditor`, take a look at the `QuillSharedExtensionsConfigurations` class for more info +* Bug fix for platform util. -## 0.6.7 -* Support the new localization system of `flutter_quill` +## 3.5.0 -## 0.6.6 -* Add `onImageClicked` in the `QuillEditorImageEmbedConfigurations` -* Fix image resizing on mobile +* Removed redundant classes. -## 0.6.5 -* Support the new improved platform checking of `flutter_quill` -* Update the Image embed builder logic -* Fix the Save image button exception -* Feature: Image cropping for the image embed builder -* Add support for copying the image to the clipboard -* Add a new static method in `FlutterQuillEmbeds` which is `defaultEditorBuilders` for minimal configurations -* Fix the image size logic (it's still missing a lot of things but we will work on that soon) -* Fix the zoom image functionality to support different image providers -* Fix the typo in the function name `editorsWebBuilders`, now it's called `editorWebBuilders` -* Deprecated: The boolean property `forceUseMobileOptionMenuForImageClick` is now deprecated as we will not using it anymore and it will be removed in the next major release -* Update `README.md` +## 3.4.4 -## 0.6.4 -* Update `QuillImageUtilities` -* Add a new extension on `QuillController` to access `QuillImageUtilities` instance easier -* Support the new `iconButtonFactor` property +* Add more translations. -## 0.6.3 -* Update `README.md` +## 3.4.3 -## 0.6.2 -* Add more default exports +* Preset link from attributes. -## 0.6.1 -* Fix a bug on the web that causing the project to not build +## 3.4.2 -## 0.6.0 -* This version is not stable yet as it doesn't have migration guide and missing some things we might introduce more breaking changes very soon but we decided to publish it because the latest stable version is not compatible with the latest stable version of `flutter_quill` +* Fix launch link edit mode. -## 0.6.0-dev.6 -* Better support for web -* Smal fixes and updates +## 3.4.1 -## 0.6.0-dev.5 -* Update the camera button +* Placeholder effective in scrollable. -## 0.6.0-dev.4 -* Add more exports -* Update `README.md`` -* Fix save image bug -* Quick fixes +## 3.4.0 -## 0.6.0-dev.3 -* Disable the camera option by default on the desktop +* Option to save image in read-only mode. -## 0.6.0-dev.2 -* Another breaking change, we will add a migration guide soon. +## 3.3.1 -## 0.6.0-dev.1 -* Breaking Changes, we have refactored most of the functions and it got renamed +* Pass any specified key in QuillEditor constructor to super. -## 0.5.1 +## 3.3.0 -* Provide a way to use a custom image provider for the image widgets -* Provide a way to handle different errors in image widgets -* Two bug fixes related to picking the image and capturing it using the camera -* Add support for image resizing on desktop platforms when forced using the mobile context menu -* Improve performance by reducing the number of widgets rebuilt by listening to media query for only the needed things, for example instead of using `MediaQuery.of(context).size`, now we are using `MediaQuery.sizeOf(context)` -* Fix warning "The platformViewRegistry getter is deprecated and will be removed in a future release. Please import it from dart:ui_web instead." -* Add QuillImageUtilities class -* Small improvements -* Allow to use the mobile context menu on the desktop by force using it -* Add the resizing option to the forced mobile context menu -* Add new custom style attribute for desktop and other platforms +* Fixed Style toggle issue. + +## 3.2.1 + +* Added new translations. + +## 3.2.0 + +* Support multiple links insertion on the go. + +## 3.1.1 + +* Add selection completed callback. + +## 3.1.0 + +* Fixed image ontap functionality. + +## 3.0.4 + +* Add maxContentWidth constraint to editor. + +## 3.0.3 + +* Do not show caret on screen when the editor is not focused. + +## 3.0.2 + +* Fix launch link for read-only mode. + +## 3.0.1 + +* Handle null value of Attribute.link. + +## 3.0.0 + +* Launch link improvements. +* Removed QuillSimpleViewer. + +## 2.5.2 + +* Skip image when pasting. + +## 2.5.1 + +* Bug fix for Desktop `Shift` + `Click` support. + +## 2.5.0 + +* Update checkbox list. + +## 2.4.1 + +* Desktop selection improvements. + +## 2.4.0 + +* Improve inline code style. + +## 2.3.3 + +* Improves selection rects to have consistent height regardless of individual segment text styles. + +## 2.3.2 + +* Allow disabling floating cursor. + +## 2.3.1 + +* Preserve last newline character on delete. + +## 2.3.0 + +* Massive changes to support flutter 2.8. + +## 2.2.2 + +* iOS - floating cursor. + +## 2.2.1 + +* Bug fix for imports supporting flutter 2.8. + +## 2.2.0 + +* Support flutter 2.8. + +## 2.1.1 + +* Add methods of clearing editor and moving cursor. + +## 2.1.0 + +* Add delete handler. + +## 2.0.23 + +* Support custom replaceText handler. + +## 2.0.22 + +* Fix attribute compare and fix font size parsing. + +## 2.0.21 + +* Handle click on embed object. + +## 2.0.20 + +* Improved UX/UI of Image widget. + +## 2.0.19 + +* When uploading a video, applying indicator. + +## 2.0.18 + +* Make toolbar dividers optional. + +## 2.0.17 + +* Allow alignment of the toolbar icons to match WrapAlignment. + +## 2.0.16 + +* Add hide / show alignment buttons. + +## 2.0.15 + +* Implement change cursor to SystemMouseCursors.click when hovering a link styled text. + +## 2.0.14 + +* Enable customize the checkbox widget using DefaultListBlockStyle style. + +## 2.0.13 + +* Improve the scrolling performance by reducing the repaint areas. + +## 2.0.12 + +* Fix the selection effect can't be seen as the textLine with background color. + +## 2.0.11 + +* Fix visibility of text selection handlers on scroll. + +## 2.0.10 + +* cursorConnt.color notify the text_line to repaint if it was disposed. + +## 2.0.9 + +* Improve UX when trying to add a link. + +## 2.0.8 + +* Adding translations to the toolbar. + +## 2.0.7 + +* Added theming options for toolbar icons and LinkDialog. + +## 2.0.6 + +* Avoid runtime error when placed inside TabBarView. + +## 2.0.5 + +* Support inline code formatting. + +## 2.0.4 + +* Enable history shortcuts for desktop. + +## 2.0.3 + +* Fix cursor when line contains image. + +## 2.0.2 + +* Address KeyboardListener class name conflict. + +## 2.0.1 + +* Upgrade flutter_colorpicker to 0.5.0. + +## 2.0.0 + +* Text Alignment functions + Block Format standards. + +## 1.9.6 + +* Support putting QuillEditor inside a Scrollable view. + +## 1.9.5 + +* Skip image when pasting. + +## 1.9.4 + +* Bug fix for cursor position when tapping at the end of line with image(s). + +## 1.9.3 + +* Bug fix when line only contains one image. + +## 1.9.2 + +* Support for building custom inline styles. + +## 1.9.1 + +* Cursor jumps to the most appropriate offset to display selection. + +## 1.9.0 + +* Support inline image. + +## 1.8.3 + +* Updated quill_delta. + +## 1.8.2 + +* Support mobile image alignment. + +## 1.8.1 + +* Support mobile custom size image. + +## 1.8.0 + +* Support entering link for image/video. + +## 1.7.3 + +* Bumps photo_view version. + +## 1.7.2 + +* Fix static analysis error. + +## 1.7.1 + +* Support Youtube video. + +## 1.7.0 + +* Support video. + +## 1.6.4 + +* Bug fix for clear format button. -## 0.5.0 +## 1.6.3 -* Migrated from `gallery_saver` to `gal` for saving images -* Added callbacks for greater control of editing images +* Fixed dragging right handle scrolling issue. -## 0.4.1 +## 1.6.2 -* Updated dependencies to support image_picker 1.0 +* Fixed the position of the selection status drag handle. -## 0.4.0 +## 1.6.1 -* Fix backspace around images [PR #1309](https://github.com/singerdmx/flutter-quill/pull/1309) -* Feat/link regexp [PR #1329](https://github.com/singerdmx/flutter-quill/pull/1329) +* Upgrade image_picker and flutter_colorpicker. + +## 1.6.0 + +* Support Multi Row Toolbar. + +## 1.5.0 + +* Remove file_picker dependency. + +## 1.4.1 + +* Remove filesystem_picker dependency. + +## 1.4.0 + +* Remove path_provider dependency. + +## 1.3.4 + +* Add option to paintCursorAboveText. + +## 1.3.3 + +* Upgrade file_picker version. + +## 1.3.2 + +* Fix copy/paste bug. + +## 1.3.1 + +* New logo. + +## 1.3.0 + +* Support flutter 2.2.0. + +## 1.2.2 + +* Checkbox supports tapping. + +## 1.2.1 + +* Indented position not holding while editing. + +## 1.2.0 + +* Fix image button cancel causes crash. + +## 1.1.8 + +* Fix height of empty line bug. + +## 1.1.7 + +* Fix text selection in read-only mode. + +## 1.1.6 + +* Remove universal_html dependency. + +## 1.1.5 + +* Enable "Select", "Select All" and "Copy" in read-only mode. + +## 1.1.4 + +* Fix text selection issue. + +## 1.1.3 + +* Update example folder. + +## 1.1.2 + +* Add pedantic. + +## 1.1.1 + +* Base64 image support. + +## 1.1.0 + +* Support null safety. + +## 1.0.9 + +* Web support for raw editor and keyboard listener. + +## 1.0.8 + +* Support token attribute. + +## 1.0.7 + +* Fix crash on web (dart:io). + +## 1.0.6 + +* Add desktop support WINDOWS, MACOS and LINUX. + +## 1.0.5 + +* Bug fix: Can not insert newline when Bold is toggled ON. + +## 1.0.4 + +* Upgrade photo_view to ^0.11.0. + +## 1.0.3 + +* Fix issue that text is not displayed while typing WEB. + +## 1.0.2 + +* Update toolbar in sample home page. + +## 1.0.1 + +* Fix static analysis errors. + +## 1.0.0 + +* Support flutter 2.0. + +## 1.0.0-dev.2 + +* Improve link handling for tel, mailto and etc. + +## 1.0.0-dev.1 + +* Upgrade prerelease SDK & Bump for master. + +## 0.3.5 + +* Fix for cursor focus issues when keyboard is on. ## 0.3.4 -* Resolve the deprecated method used in the `video_player` package +* Improve link handling for tel, mailto and etc. ## 0.3.3 -* Fix a prototype bug that was brought by [PR #1230](https://github.com/singerdmx/flutter-quill/pull/1230#issuecomment*1560597099) +* More fix on cursor focus issue when keyboard is on. ## 0.3.2 -* Updated dependencies to support intl 0.18 +* Fix cursor focus issue when keyboard is on. ## 0.3.1 -* Image embedding tweaks - * Add MediaButton which is intended to supersede the ImageButton and VideoButton. Only image selection is working. - * Implement image insert for web (image as base64) +* cursor focus when keyboard is on. ## 0.3.0 -* Added support for adding custom tooltips to toolbar buttons +* Line Height calculated based on font size. + +## 0.2.12 + +* Support placeholder. + +## 0.2.11 + +* Fix static analysis error. + +## 0.2.10 + +* Update TextInputConfiguration autocorrect to true in stable branch. + +## 0.2.9 + +* Update TextInputConfiguration autocorrect to true. + +## 0.2.8 + +* Support display local image besides network image in stable branch. + +## 0.2.7 + +* Support display local image besides network image. + +## 0.2.6 + +* Fix cursor after pasting. + +## 0.2.5 + +* Toggle text/background color button in toolbar. + +## 0.2.4 + +* Support the use of custom icon size in toolbar. + +## 0.2.3 + +* Support custom styles and image on local device storage without uploading. + +## 0.2.2 + +* Update git repo. + +## 0.2.1 + +* Fix static analysis error. ## 0.2.0 -* Allow widgets to override widget span properties [b7951b0](https://github.com/singerdmx/flutter-quill/commit/b7951b02c9086ea42e7aad6d78e6c9b0297562e5) -* Remove tuples [3e9452e](https://github.com/singerdmx/flutter-quill/commit/3e9452e675e8734ff50364c5f7b5d34088d5ff05) -* Remove transparent color of ImageVideoUtils dialog [74544bd](https://github.com/singerdmx/flutter-quill/commit/74544bd945a9d212ca1e8d6b3053dbecee22b720) -* Migrate to `youtube_player_flutter` from `youtube_player_flutter_quill` -* Updates to formula button [5228f38](https://github.com/singerdmx/flutter-quill/commit/5228f389ba6f37d61d445cfe138c19fcf8766d71) +* Add checked/unchecked list button in toolbar. + +## 0.1.8 + +* Support font and size attributes. + +## 0.1.7 + +* Support checked/unchecked list. + +## 0.1.6 + +* Fix getExtentEndpointForSelection. + +## 0.1.5 + +* Support text alignment. + +## 0.1.4 + +* Handle url with trailing spaces. + +## 0.1.3 + +* Handle cursor position change when undo/redo. + +## 0.1.2 + +* Handle more text colors. + +## 0.1.1 + +* Fix cursor issue when undo. ## 0.1.0 -* Initial release +* Fix insert image. + +## 0.0.9 + +* Handle rgba color. + +## 0.0.8 + +* Fix launching url. + +## 0.0.7 + +* Handle multiple image inserts. + +## 0.0.6 + +* More toolbar functionality. + +## 0.0.5 + +* Update example. + +## 0.0.4 + +* Update example. + +## 0.0.3 + +* Update home page meta data. + +## 0.0.2 + +* Support image upload and launch url in read-only mode. + +## 0.0.1 + +* Rich text editor based on Quill Delta. diff --git a/flutter_quill_extensions/README.md b/flutter_quill_extensions/README.md index 400d1e8af..26d2fb02e 100644 --- a/flutter_quill_extensions/README.md +++ b/flutter_quill_extensions/README.md @@ -16,6 +16,7 @@ to support embedding widgets like images, formulas, videos, and more. - [Embed Blocks](#embed-blocks) - [Element properties](#element-properties) - [Custom Element properties](#custom-element-properties) + - [Image Assets](#image-assets) - [Drag and drop feature](#drag-and-drop-feature) - [Features](#features) - [Contributing](#contributing) @@ -52,8 +53,7 @@ dependencies: > > 1. We are using the [`gal`](https://github.com/natsuk4ze/) plugin to save images. -> For this to work, you need to add the appropriate permissions -> to your `Info.plist` and `AndroidManifest.xml` files. +> For this to work, you need to add the appropriate configurations > See to add the needed lines. > > 2. We also use [`image_picker`](https://pub.dev/packages/image_picker) plugin for picking images so please make sure to follow the instructions @@ -165,6 +165,28 @@ Define flutterAlignment` as follows: This works for all platforms except Web +### Image Assets + +If you want to use image assets in the Quill Editor, you need to make sure your assets folder is `assets` otherwise: + +```dart +QuillEditor.basic( + configurations: const QuillEditorConfigurations( + // ... + sharedConfigurations: QuillSharedConfigurations( + extraConfigurations: { + QuillSharedExtensionsConfigurations.key: + QuillSharedExtensionsConfigurations( + assetsPrefix: 'your-assets-folder-name', // Defaults to `assets` + ), + }, + ), + ), +); +``` + +This info is needed by the package to check if it asset image to use the `AssetImage` provider + ### Drag and drop feature Currently, the drag-and-drop feature is not officially supported, but you can achieve this very easily in the following steps: diff --git a/flutter_quill_extensions/lib/embeds/formula/toolbar/formula_button.dart b/flutter_quill_extensions/lib/embeds/formula/toolbar/formula_button.dart index 935389f6b..84dfdb367 100644 --- a/flutter_quill_extensions/lib/embeds/formula/toolbar/formula_button.dart +++ b/flutter_quill_extensions/lib/embeds/formula/toolbar/formula_button.dart @@ -6,7 +6,7 @@ import '../../../models/config/toolbar/buttons/formula.dart'; class QuillToolbarFormulaButton extends StatelessWidget { const QuillToolbarFormulaButton({ required this.controller, - required this.options, + this.options = const QuillToolbarFormulaButtonOptions(), super.key, }); @@ -71,14 +71,11 @@ class QuillToolbarFormulaButton extends StatelessWidget { options.childBuilder ?? baseButtonExtraOptions(context).childBuilder; final iconColor = iconTheme?.iconUnselectedColor ?? theme.iconTheme.color; - final iconFillColor = iconTheme?.iconUnselectedFillColor ?? - (options.fillColor ?? theme.canvasColor); if (childBuilder != null) { return childBuilder( QuillToolbarFormulaButtonOptions( afterButtonPressed: _afterButtonPressed(context), - fillColor: iconFillColor, iconData: iconData, iconSize: iconSize, iconButtonFactor: iconButtonFactor, @@ -94,14 +91,10 @@ class QuillToolbarFormulaButton extends StatelessWidget { } return QuillToolbarIconButton( - icon: Icon(iconData, size: iconSize, color: iconColor), + icon: Icon(iconData, size: iconSize * iconButtonFactor, color: iconColor), tooltip: tooltip, - highlightElevation: 0, - hoverElevation: 0, - size: iconSize * 1.77, - fillColor: iconFillColor, - borderRadius: iconTheme?.borderRadius ?? 2, onPressed: () => _sharedOnPressed(context), + isFilled: false, ); } diff --git a/flutter_quill_extensions/lib/embeds/image/editor/image_embed.dart b/flutter_quill_extensions/lib/embeds/image/editor/image_embed.dart index 2ddc38dbf..ecdefe2d7 100644 --- a/flutter_quill_extensions/lib/embeds/image/editor/image_embed.dart +++ b/flutter_quill_extensions/lib/embeds/image/editor/image_embed.dart @@ -1,4 +1,3 @@ -import 'package:flutter/foundation.dart' show kIsWeb; import 'package:flutter/material.dart'; import 'package:flutter_quill/flutter_quill.dart' hide OptionalSize; import 'package:flutter_quill/translations.dart'; @@ -30,15 +29,19 @@ class QuillEditorImageEmbedBuilder extends EmbedBuilder { bool inline, TextStyle textStyle, ) { - assert(!kIsWeb, 'Please provide image EmbedBuilder for Web'); + // assert(!kIsWeb, 'Please provide image EmbedBuilder for Web'); final imageSource = standardizeImageUrl(node.value.data); - final ((imageSize), margin, alignment) = getElementAttributes(node); + final ((imageSize), margin, alignment) = getElementAttributes( + node, + context, + ); final width = imageSize.width; final height = imageSize.height; final image = getImageWidgetByImageSource( + context: context, imageSource, imageProviderBuilder: configurations.imageProviderBuilder, imageErrorWidgetBuilder: configurations.imageErrorWidgetBuilder, @@ -56,17 +59,14 @@ class QuillEditorImageEmbedBuilder extends EmbedBuilder { onTap: configurations.onImageClicked ?? () => showDialog( context: context, - builder: (_) => QuillProvider.value( - value: context.requireQuillProvider, - child: FlutterQuillLocalizationsWidget( - child: ImageOptionsMenu( - controller: controller, - configurations: configurations, - imageSource: imageSource, - imageSize: imageSize, - isReadOnly: readOnly, - imageSaverService: imageSaverService, - ), + builder: (_) => FlutterQuillLocalizationsWidget( + child: ImageOptionsMenu( + controller: controller, + configurations: configurations, + imageSource: imageSource, + imageSize: imageSize, + isReadOnly: readOnly, + imageSaverService: imageSaverService, ), ), ), diff --git a/flutter_quill_extensions/lib/embeds/image/editor/image_embed_types.dart b/flutter_quill_extensions/lib/embeds/image/editor/image_embed_types.dart index 44d2dfaa1..e2036da14 100644 --- a/flutter_quill_extensions/lib/embeds/image/editor/image_embed_types.dart +++ b/flutter_quill_extensions/lib/embeds/image/editor/image_embed_types.dart @@ -4,7 +4,7 @@ import 'package:flutter/widgets.dart' show BuildContext; import 'package:flutter_quill/flutter_quill.dart'; import 'package:meta/meta.dart' show immutable; -import '../../../extensions/controller.dart'; +import '../../../extensions/controller_ext.dart'; import '../../../services/image_picker/s_image_picker.dart'; /// When request picking an image, for example when the image button toolbar @@ -76,6 +76,7 @@ typedef ImageEmbedBuilderOnRemovedCallback = Future Function( ); typedef ImageEmbedBuilderProviderBuilder = ImageProvider Function( + BuildContext context, String imageUrl, ); diff --git a/flutter_quill_extensions/lib/embeds/image/editor/image_menu.dart b/flutter_quill_extensions/lib/embeds/image/editor/image_menu.dart index 27c83ecef..bc1e1414b 100644 --- a/flutter_quill_extensions/lib/embeds/image/editor/image_menu.dart +++ b/flutter_quill_extensions/lib/embeds/image/editor/image_menu.dart @@ -1,14 +1,10 @@ import 'package:flutter/cupertino.dart' show showCupertinoModalPopup; +import 'package:flutter/foundation.dart' show kIsWeb; import 'package:flutter/material.dart'; import 'package:flutter_quill/flutter_quill.dart' - show - ImageUrl, - QuillController, - QuillProvider, - QuillProviderExt, - StyleAttribute, - getEmbedNode; + show ImageUrl, QuillController, StyleAttribute, getEmbedNode; import 'package:flutter_quill/translations.dart'; +import 'package:super_clipboard/super_clipboard.dart'; import '../../../models/config/editor/image/image.dart'; import '../../../models/config/shared_configurations.dart'; @@ -55,34 +51,31 @@ class ImageOptionsMenu extends StatelessWidget { context: context, builder: (modalContext) { final screenSize = MediaQuery.sizeOf(modalContext); - return QuillProvider.value( - value: context.requireQuillProvider, - child: FlutterQuillLocalizationsWidget( - child: ImageResizer( - onImageResize: (width, height) { - final res = getEmbedNode( - controller, - controller.selection.start, - ); + return FlutterQuillLocalizationsWidget( + child: ImageResizer( + onImageResize: (width, height) { + final res = getEmbedNode( + controller, + controller.selection.start, + ); - final attr = replaceStyleStringWithSize( - getImageStyleString(controller), - width: width, - height: height, + final attr = replaceStyleStringWithSize( + getImageStyleString(controller), + width: width, + height: height, + ); + controller + ..skipRequestKeyboard = true + ..formatText( + res.offset, + 1, + StyleAttribute(attr), ); - controller - ..skipRequestKeyboard = true - ..formatText( - res.offset, - 1, - StyleAttribute(attr), - ); - }, - imageWidth: imageSize.width, - imageHeight: imageSize.height, - maxWidth: screenSize.width, - maxHeight: screenSize.height, - ), + }, + imageWidth: imageSize.width, + imageHeight: imageSize.height, + maxWidth: screenSize.width, + maxHeight: screenSize.height, ), ); }, @@ -96,15 +89,17 @@ class ImageOptionsMenu extends StatelessWidget { final navigator = Navigator.of(context); final imageNode = getEmbedNode(controller, controller.selection.start).value; - final imageUrl = imageNode.value.data; + final image = imageNode.value.data; controller.copiedImageUrl = ImageUrl( - imageUrl, + image, getImageStyleString(controller), ); - // TODO: Implement the copy image - // await Clipboard.setData( - // ClipboardData(), - // ); + + final data = await convertImageToUint8List(image); + if (data != null) { + final item = DataWriterItem()..add(Formats.png(data)); + await ClipboardWriter.instance.write([item]); + } navigator.pop(); }, ), @@ -139,7 +134,7 @@ class ImageOptionsMenu extends StatelessWidget { await configurations.onImageRemovedCallback.call(imageSource); }, ), - ...[ + if (!kIsWeb) ListTile( leading: const Icon(Icons.save), title: Text(context.loc.save), @@ -181,23 +176,22 @@ class ImageOptionsMenu extends StatelessWidget { ); }, ), - ListTile( - leading: const Icon(Icons.zoom_in), - title: Text(context.loc.zoom), - onTap: () => Navigator.pushReplacement( - context, - MaterialPageRoute( - builder: (_) => ImageTapWrapper( - assetsPrefix: QuillSharedExtensionsConfigurations.get( - context: context) - .assetsPrefix, - imageUrl: imageSource, - configurations: configurations, - ), + ListTile( + leading: const Icon(Icons.zoom_in), + title: Text(context.loc.zoom), + onTap: () => Navigator.pushReplacement( + context, + MaterialPageRoute( + builder: (_) => ImageTapWrapper( + assetsPrefix: + QuillSharedExtensionsConfigurations.get(context: context) + .assetsPrefix, + imageUrl: imageSource, + configurations: configurations, ), ), ), - ], + ), ], ), ); diff --git a/flutter_quill_extensions/lib/embeds/image/toolbar/image_button.dart b/flutter_quill_extensions/lib/embeds/image/toolbar/image_button.dart index fb00a6564..f943dabd4 100644 --- a/flutter_quill_extensions/lib/embeds/image/toolbar/image_button.dart +++ b/flutter_quill_extensions/lib/embeds/image/toolbar/image_button.dart @@ -14,7 +14,7 @@ import 'select_image_source.dart'; class QuillToolbarImageButton extends StatelessWidget { const QuillToolbarImageButton({ required this.controller, - required this.options, + this.options = const QuillToolbarImageButtonOptions(), super.key, }); @@ -101,21 +101,15 @@ class QuillToolbarImageButton extends StatelessWidget { final iconTheme = _iconTheme(context); final iconColor = iconTheme?.iconUnselectedColor ?? theme.iconTheme.color; - final iconFillColor = iconTheme?.iconUnselectedFillColor ?? - (options.fillColor ?? theme.canvasColor); return QuillToolbarIconButton( icon: Icon( iconData, - size: iconSize, + size: iconButtonFactor * iconSize, color: iconColor, ), tooltip: tooltip, - highlightElevation: 0, - hoverElevation: 0, - size: iconSize * 1.77, - fillColor: iconFillColor, - borderRadius: iconTheme?.borderRadius ?? 2, + isFilled: false, onPressed: () => _sharedOnPressed(context), ); } @@ -172,14 +166,11 @@ class QuillToolbarImageButton extends StatelessWidget { Future _typeLink(BuildContext context) async { final value = await showDialog( context: context, - builder: (_) => QuillProvider.value( - value: context.requireQuillProvider, - child: FlutterQuillLocalizationsWidget( - child: TypeLinkDialog( - dialogTheme: options.dialogTheme, - linkRegExp: options.linkRegExp, - linkType: LinkType.image, - ), + builder: (_) => FlutterQuillLocalizationsWidget( + child: TypeLinkDialog( + dialogTheme: options.dialogTheme, + linkRegExp: options.linkRegExp, + linkType: LinkType.image, ), ), ); diff --git a/flutter_quill_extensions/lib/embeds/image/toolbar/select_image_source.dart b/flutter_quill_extensions/lib/embeds/image/toolbar/select_image_source.dart index 74de2c819..40b8de5da 100644 --- a/flutter_quill_extensions/lib/embeds/image/toolbar/select_image_source.dart +++ b/flutter_quill_extensions/lib/embeds/image/toolbar/select_image_source.dart @@ -1,6 +1,5 @@ import 'package:flutter/material.dart'; import 'package:flutter_quill/extensions.dart' show isDesktop; -import 'package:flutter_quill/flutter_quill.dart'; import 'package:flutter_quill/translations.dart'; import '../editor/image_embed_types.dart'; @@ -55,11 +54,8 @@ Future showSelectImageSourceDialog({ showDragHandle: true, context: context, constraints: const BoxConstraints(maxWidth: 640), - builder: (_) => QuillProvider.value( - value: context.requireQuillProvider, - child: const FlutterQuillLocalizationsWidget( - child: SelectImageSourceDialog(), - ), + builder: (_) => const FlutterQuillLocalizationsWidget( + child: SelectImageSourceDialog(), ), ); return imageSource; diff --git a/flutter_quill_extensions/lib/embeds/others/camera_button/camera_button.dart b/flutter_quill_extensions/lib/embeds/others/camera_button/camera_button.dart index c62976cb4..ef7ad3da5 100644 --- a/flutter_quill_extensions/lib/embeds/others/camera_button/camera_button.dart +++ b/flutter_quill_extensions/lib/embeds/others/camera_button/camera_button.dart @@ -3,7 +3,7 @@ import 'package:flutter_quill/flutter_quill.dart' show QuillController, QuillIconTheme, - QuillProviderExt, + QuillSimpleToolbarExt, QuillToolbarBaseButtonOptions, QuillToolbarIconButton; import 'package:flutter_quill/translations.dart'; @@ -17,7 +17,7 @@ import 'select_camera_action.dart'; class QuillToolbarCameraButton extends StatelessWidget { const QuillToolbarCameraButton({ required this.controller, - required this.options, + this.options = const QuillToolbarCameraButtonOptions(), super.key, }); @@ -30,6 +30,13 @@ class QuillToolbarCameraButton extends StatelessWidget { return iconSize ?? baseFontSize; } + double _iconButtonFactor(BuildContext context) { + final baseIconFactor = + baseButtonExtraOptions(context).globalIconButtonFactor; + final iconButtonFactor = options.iconButtonFactor; + return iconButtonFactor ?? baseIconFactor; + } + VoidCallback? _afterButtonPressed(BuildContext context) { return options.afterButtonPressed ?? baseButtonExtraOptions(context).afterButtonPressed; @@ -69,6 +76,7 @@ class QuillToolbarCameraButton extends StatelessWidget { final tooltip = _tooltip(context); final iconSize = _iconSize(context); final iconData = _iconData(context); + final iconButtonFactor = _iconButtonFactor(context); final childBuilder = options.childBuilder ?? baseButtonExtraOptions(context).childBuilder; @@ -80,7 +88,7 @@ class QuillToolbarCameraButton extends StatelessWidget { iconData: options.iconData, fillColor: options.fillColor, iconSize: options.iconSize, - iconButtonFactor: options.iconButtonFactor, + iconButtonFactor: iconButtonFactor, iconTheme: options.iconTheme, tooltip: options.tooltip, cameraConfigurations: options.cameraConfigurations, @@ -96,17 +104,11 @@ class QuillToolbarCameraButton extends StatelessWidget { final theme = Theme.of(context); final iconColor = iconTheme?.iconUnselectedColor ?? theme.iconTheme.color; - final iconFillColor = iconTheme?.iconUnselectedFillColor ?? - (options.fillColor ?? theme.canvasColor); return QuillToolbarIconButton( - icon: Icon(iconData, size: iconSize, color: iconColor), + icon: Icon(iconData, size: iconButtonFactor * iconSize, color: iconColor), tooltip: tooltip, - highlightElevation: 0, - hoverElevation: 0, - size: iconSize * 1.77, - fillColor: iconFillColor, - borderRadius: iconTheme?.borderRadius ?? 2, + isFilled: false, // isDesktop(supportWeb: false) ? null : onPressed: () => _sharedOnPressed(context), ); @@ -118,9 +120,8 @@ class QuillToolbarCameraButton extends StatelessWidget { if (customCallback != null) { return await customCallback(context); } - final cameraAction = await showDialog( + final cameraAction = await showSelectCameraActionDialog( context: context, - builder: (ctx) => const SelectCameraActionDialog(), ); return cameraAction; @@ -168,12 +169,5 @@ class QuillToolbarCameraButton extends StatelessWidget { await options.cameraConfigurations.onImageInsertedCallback ?.call(imageFile.path); } - - // final file = await switch (cameraAction) { - // CameraAction.image => - // imagePickerService.pickImage(source: ImageSource.camera), - // CameraAction.video => - // imagePickerService.pickVideo(source: ImageSource.camera), - // }; } } diff --git a/flutter_quill_extensions/lib/embeds/others/camera_button/select_camera_action.dart b/flutter_quill_extensions/lib/embeds/others/camera_button/select_camera_action.dart index bd6527db7..c20670280 100644 --- a/flutter_quill_extensions/lib/embeds/others/camera_button/select_camera_action.dart +++ b/flutter_quill_extensions/lib/embeds/others/camera_button/select_camera_action.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:flutter_quill/extensions.dart'; import 'package:flutter_quill/translations.dart'; import 'camera_types.dart'; @@ -8,27 +9,46 @@ class SelectCameraActionDialog extends StatelessWidget { @override Widget build(BuildContext context) { - return AlertDialog( - contentPadding: EdgeInsets.zero, - content: Column( - mainAxisSize: MainAxisSize.min, - children: [ - TextButton.icon( - icon: const Icon( - Icons.camera, + return SizedBox( + height: 150, + width: double.infinity, + child: SingleChildScrollView( + child: Column( + children: [ + ListTile( + title: Text(context.loc.photo), + subtitle: Text( + context.loc.takeAPhotoUsingYourCamera, + ), + leading: const Icon(Icons.photo_sharp), + enabled: !isDesktop(supportWeb: false), + onTap: () => Navigator.of(context).pop(CameraAction.image), ), - label: Text(context.loc.photo), - onPressed: () => Navigator.pop(context, CameraAction.image), - ), - TextButton.icon( - icon: const Icon( - Icons.video_call, + ListTile( + title: Text(context.loc.video), + subtitle: Text( + context.loc.recordAVideoUsingYourCamera, + ), + leading: const Icon(Icons.camera), + enabled: !isDesktop(supportWeb: false), + onTap: () => Navigator.of(context).pop(CameraAction.video), ), - label: Text(context.loc.video), - onPressed: () => Navigator.pop(context, CameraAction.video), - ) - ], + ], + ), ), ); } } + +Future showSelectCameraActionDialog({ + required BuildContext context, +}) async { + final imageSource = await showModalBottomSheet( + showDragHandle: true, + context: context, + constraints: const BoxConstraints(maxWidth: 640), + builder: (context) => const FlutterQuillLocalizationsWidget( + child: SelectCameraActionDialog()), + ); + return imageSource; +} diff --git a/flutter_quill_extensions/lib/embeds/others/image_video_utils.dart b/flutter_quill_extensions/lib/embeds/others/image_video_utils.dart index 03514f1b3..808f7517f 100644 --- a/flutter_quill_extensions/lib/embeds/others/image_video_utils.dart +++ b/flutter_quill_extensions/lib/embeds/others/image_video_utils.dart @@ -2,6 +2,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_quill/flutter_quill.dart' show QuillDialogTheme; import 'package:flutter_quill/translations.dart'; +import '../../utils/patterns.dart'; + enum LinkType { video, image, @@ -28,7 +30,7 @@ class TypeLinkDialog extends StatefulWidget { class TypeLinkDialogState extends State { late String _link; late TextEditingController _controller; - late RegExp _linkRegExp; + RegExp? _linkRegExp; @override void initState() { @@ -36,15 +38,7 @@ class TypeLinkDialogState extends State { _link = widget.link ?? ''; _controller = TextEditingController(text: _link); - final defaultLinkNonSecureRegExp = RegExp( - r'https?://.*?\.(?:png|jpe?g|gif|bmp|webp|tiff?)', - caseSensitive: false, - ); // Not secure - // final defaultLinkRegExp = RegExp( - // r'https://.*?\.(?:png|jpe?g|gif|bmp|webp|tiff?)', - // caseSensitive: false, - // ); // Secure - _linkRegExp = widget.linkRegExp ?? defaultLinkNonSecureRegExp; + _linkRegExp = widget.linkRegExp; } @override @@ -102,8 +96,28 @@ class TypeLinkDialogState extends State { Navigator.pop(context, _link.trim()); } + RegExp get linkRegExp { + final customRegExp = _linkRegExp; + if (customRegExp != null) { + return customRegExp; + } + switch (widget.linkType) { + case LinkType.video: + if (youtubeRegExp.hasMatch(_link)) { + return youtubeRegExp; + } + return videoRegExp; + case LinkType.image: + return imageRegExp; + } + } + bool _canPress() { - return _link.isNotEmpty && _linkRegExp.hasMatch(_link); + if (_link.isEmpty) { + return false; + } + if (widget.linkType == LinkType.image) {} + return _link.isNotEmpty && linkRegExp.hasMatch(_link); } } diff --git a/flutter_quill_extensions/lib/embeds/others/media_button/media_button.dart b/flutter_quill_extensions/lib/embeds/others/media_button/media_button.dart index 5246e8381..132c7bcd0 100644 --- a/flutter_quill_extensions/lib/embeds/others/media_button/media_button.dart +++ b/flutter_quill_extensions/lib/embeds/others/media_button/media_button.dart @@ -20,7 +20,7 @@ // class QuillToolbarMediaButton extends StatelessWidget { // QuillToolbarMediaButton({ // required this.controller, -// required this.options, +// this.options, // super.key, // }) : assert(options.type == QuillMediaType.image, // 'Video selection is not supported yet'); @@ -135,7 +135,7 @@ // tooltip: tooltip, // highlightElevation: 0, // hoverElevation: 0, -// size: iconSize * 1.77, +// size: iconSize * iconButtonFactor, // fillColor: iconFillColor, // borderRadius: iconTheme?.borderRadius ?? 2, // onPressed: () => _sharedOnPressed(context), diff --git a/flutter_quill_extensions/lib/embeds/video/editor/video_embed.dart b/flutter_quill_extensions/lib/embeds/video/editor/video_embed.dart index 831da538b..fd1cd8fc5 100644 --- a/flutter_quill_extensions/lib/embeds/video/editor/video_embed.dart +++ b/flutter_quill_extensions/lib/embeds/video/editor/video_embed.dart @@ -37,7 +37,10 @@ class QuillEditorVideoEmbedBuilder extends EmbedBuilder { readOnly: readOnly, ); } - final ((elementSize), margin, alignment) = getElementAttributes(node); + final ((elementSize), margin, alignment) = getElementAttributes( + node, + context, + ); final width = elementSize.width; final height = elementSize.height; diff --git a/flutter_quill_extensions/lib/embeds/video/toolbar/select_video_source.dart b/flutter_quill_extensions/lib/embeds/video/toolbar/select_video_source.dart index f4c030c9f..d8ddb7747 100644 --- a/flutter_quill_extensions/lib/embeds/video/toolbar/select_video_source.dart +++ b/flutter_quill_extensions/lib/embeds/video/toolbar/select_video_source.dart @@ -52,7 +52,8 @@ Future showSelectVideoSourceDialog({ showDragHandle: true, context: context, constraints: const BoxConstraints(maxWidth: 640), - builder: (context) => const SelectVideoSourceDialog(), + builder: (context) => + const FlutterQuillLocalizationsWidget(child: SelectVideoSourceDialog()), ); return imageSource; } diff --git a/flutter_quill_extensions/lib/embeds/video/toolbar/video_button.dart b/flutter_quill_extensions/lib/embeds/video/toolbar/video_button.dart index f8b854b3c..748759784 100644 --- a/flutter_quill_extensions/lib/embeds/video/toolbar/video_button.dart +++ b/flutter_quill_extensions/lib/embeds/video/toolbar/video_button.dart @@ -13,8 +13,8 @@ import 'select_video_source.dart'; class QuillToolbarVideoButton extends StatelessWidget { const QuillToolbarVideoButton({ - required this.options, required this.controller, + this.options = const QuillToolbarVideoButtonOptions(), super.key, }); @@ -28,6 +28,13 @@ class QuillToolbarVideoButton extends StatelessWidget { return iconSize ?? baseFontSize; } + double _iconButtonFactor(BuildContext context) { + final baseIconFactor = + baseButtonExtraOptions(context).globalIconButtonFactor; + final iconButtonFactor = options.iconButtonFactor; + return iconButtonFactor ?? baseIconFactor; + } + VoidCallback? _afterButtonPressed(BuildContext context) { return options.afterButtonPressed ?? baseButtonExtraOptions(context).afterButtonPressed; @@ -67,6 +74,7 @@ class QuillToolbarVideoButton extends StatelessWidget { final tooltip = _tooltip(context); final iconSize = _iconSize(context); + final iconButtonFactor = _iconButtonFactor(context); final iconData = _iconData(context); final childBuilder = options.childBuilder ?? baseButtonExtraOptions(context).childBuilder; @@ -83,7 +91,7 @@ class QuillToolbarVideoButton extends StatelessWidget { dialogTheme: options.dialogTheme, fillColor: iconFillColor, iconSize: options.iconSize, - iconButtonFactor: options.iconButtonFactor, + iconButtonFactor: iconButtonFactor, linkRegExp: options.linkRegExp, tooltip: options.tooltip, iconTheme: options.iconTheme, @@ -98,13 +106,9 @@ class QuillToolbarVideoButton extends StatelessWidget { } return QuillToolbarIconButton( - icon: Icon(iconData, size: iconSize, color: iconColor), + icon: Icon(iconData, size: iconSize * iconButtonFactor, color: iconColor), tooltip: tooltip, - highlightElevation: 0, - hoverElevation: 0, - size: iconSize * 1.77, - fillColor: iconFillColor, - borderRadius: iconTheme?.borderRadius ?? 2, + isFilled: false, onPressed: () => _sharedOnPressed(context), ); } @@ -165,13 +169,10 @@ class QuillToolbarVideoButton extends StatelessWidget { Future _typeLink(BuildContext context) async { final value = await showDialog( context: context, - builder: (_) => QuillProvider.value( - value: context.requireQuillProvider, - child: FlutterQuillLocalizationsWidget( - child: TypeLinkDialog( - dialogTheme: options.dialogTheme, - linkType: LinkType.video, - ), + builder: (_) => FlutterQuillLocalizationsWidget( + child: TypeLinkDialog( + dialogTheme: options.dialogTheme, + linkType: LinkType.video, ), ), ); diff --git a/flutter_quill_extensions/lib/embeds/video/video.dart b/flutter_quill_extensions/lib/embeds/video/video.dart index b2e1ca24e..2acd4246c 100644 --- a/flutter_quill_extensions/lib/embeds/video/video.dart +++ b/flutter_quill_extensions/lib/embeds/video/video.dart @@ -2,7 +2,7 @@ import 'package:flutter/widgets.dart' show BuildContext; import 'package:flutter_quill/flutter_quill.dart'; import 'package:meta/meta.dart' show immutable; -import '../../extensions/controller.dart'; +import '../../extensions/controller_ext.dart'; import '../../services/image_picker/s_image_picker.dart'; /// When request picking an video, for example when the video button toolbar diff --git a/flutter_quill_extensions/lib/embeds/widgets/image.dart b/flutter_quill_extensions/lib/embeds/widgets/image.dart index d072f202b..c2d4f240b 100644 --- a/flutter_quill_extensions/lib/embeds/widgets/image.dart +++ b/flutter_quill_extensions/lib/embeds/widgets/image.dart @@ -36,9 +36,10 @@ ImageProvider getImageProviderByImageSource( String imageSource, { required ImageEmbedBuilderProviderBuilder? imageProviderBuilder, required String assetsPrefix, + required BuildContext context, }) { if (imageProviderBuilder != null) { - return imageProviderBuilder(imageSource); + return imageProviderBuilder(context, imageSource); } if (isImageBase64(imageSource)) { @@ -50,7 +51,6 @@ ImageProvider getImageProviderByImageSource( } if (imageSource.startsWith(assetsPrefix)) { - // TODO: This impl could be improved return AssetImage(imageSource); } return FileImage(File(imageSource)); @@ -58,6 +58,7 @@ ImageProvider getImageProviderByImageSource( Image getImageWidgetByImageSource( String imageSource, { + required BuildContext context, required ImageEmbedBuilderProviderBuilder? imageProviderBuilder, required ImageErrorWidgetBuilder? imageErrorWidgetBuilder, required String assetsPrefix, @@ -67,6 +68,7 @@ Image getImageWidgetByImageSource( }) { return Image( image: getImageProviderByImageSource( + context: context, imageSource, imageProviderBuilder: imageProviderBuilder, assetsPrefix: assetsPrefix, @@ -127,6 +129,7 @@ class ImageTapWrapper extends StatelessWidget { children: [ PhotoView( imageProvider: getImageProviderByImageSource( + context: context, imageUrl, imageProviderBuilder: configurations.imageProviderBuilder, assetsPrefix: assetsPrefix, diff --git a/flutter_quill_extensions/lib/extensions/controller.dart b/flutter_quill_extensions/lib/extensions/controller_ext.dart similarity index 100% rename from flutter_quill_extensions/lib/extensions/controller.dart rename to flutter_quill_extensions/lib/extensions/controller_ext.dart diff --git a/flutter_quill_extensions/lib/flutter_quill_extensions.dart b/flutter_quill_extensions/lib/flutter_quill_extensions.dart index b3f7f3bd0..d7492043c 100644 --- a/flutter_quill_extensions/lib/flutter_quill_extensions.dart +++ b/flutter_quill_extensions/lib/flutter_quill_extensions.dart @@ -36,7 +36,7 @@ export 'embeds/video/editor/video_embed.dart'; export 'embeds/video/editor/video_web_embed.dart'; export 'embeds/video/toolbar/video_button.dart'; export 'embeds/video/video.dart'; -export 'extensions/controller.dart'; +export 'extensions/controller_ext.dart'; export 'models/config/editor/image/image.dart'; export 'models/config/editor/image/image_web.dart'; export 'models/config/editor/video/video.dart'; @@ -113,8 +113,8 @@ class FlutterQuillEmbeds { /// videos iframe on the web. /// static List editorWebBuilders( - {QuillEditorWebImageEmbedConfigurations? imageEmbedConfigurations = - const QuillEditorWebImageEmbedConfigurations(), + {QuillEditorImageEmbedConfigurations? imageEmbedConfigurations = + const QuillEditorImageEmbedConfigurations(), QuillEditorWebVideoEmbedConfigurations? videoEmbedConfigurations = const QuillEditorWebVideoEmbedConfigurations()}) { if (!kIsWeb) { @@ -125,7 +125,7 @@ class FlutterQuillEmbeds { } return [ if (imageEmbedConfigurations != null) - QuillEditorWebImageEmbedBuilder( + QuillEditorImageEmbedBuilder( configurations: imageEmbedConfigurations, ), if (videoEmbedConfigurations != null) diff --git a/flutter_quill_extensions/lib/models/config/shared_configurations.dart b/flutter_quill_extensions/lib/models/config/shared_configurations.dart index 1badf5533..402260b81 100644 --- a/flutter_quill_extensions/lib/models/config/shared_configurations.dart +++ b/flutter_quill_extensions/lib/models/config/shared_configurations.dart @@ -5,34 +5,8 @@ import 'package:meta/meta.dart' show immutable; import '../../services/image_picker/s_image_picker.dart'; import '../../services/image_saver/s_image_saver.dart'; -/// Configurations for Flutter Quill Extensions -/// that is shared between the toolbar and editor for the extensions package -/// -/// Example on how to setup it: -/// -/// ```dart -/// QuillProvider( -/// configurations: QuillConfigurations( -/// sharedConfigurations: const QuillSharedConfigurations( -/// extraConfigurations: { -/// QuillSharedExtensionsConfigurations.key: -/// QuillSharedExtensionsConfigurations( -/// // Feel free to explore it -/// ), -/// }, -/// ), -/// controller: _controller, -/// ), -/// child: const Column( -/// children: [ -/// // QuillToolbar -/// // QuillEditor -/// // ... -/// ], -// ), -/// ) -/// ``` -/// +/// Configurations for Flutter Editor Extensions +/// shared between toolbar and editor @immutable class QuillSharedExtensionsConfigurations { const QuillSharedExtensionsConfigurations({ @@ -47,17 +21,15 @@ class QuillSharedExtensionsConfigurations { factory QuillSharedExtensionsConfigurations.get({ required BuildContext context, }) { - final quillSharedExtensionsConfigurations = - context.requireQuillSharedConfigurations.extraConfigurations[key]; - if (quillSharedExtensionsConfigurations != null) { - if (quillSharedExtensionsConfigurations - is! QuillSharedExtensionsConfigurations) { + final value = context.quillSharedConfigurations?.extraConfigurations[key]; + if (value != null) { + if (value is! QuillSharedExtensionsConfigurations) { throw ArgumentError( 'The value of key `$key` should be of type ' - 'QuillSharedExtensionsConfigurations', + '$key', ); } - return quillSharedExtensionsConfigurations; + return value; } return const QuillSharedExtensionsConfigurations(); } @@ -66,8 +38,8 @@ class QuillSharedExtensionsConfigurations { /// which can be found in the [QuillSharedConfigurations] /// which lives in the [QuillConfigurations] /// - /// which exists in the [QuillProvider] - static const String key = 'quillSharedExtensionsConfigurations'; + /// which exists in the [QuillEditorConfigurations] + static const String key = 'QuillSharedExtensionsConfigurations'; /// Defaults to [ImagePickerService.defaultImpl] final ImagePickerService? _imagePickerService; diff --git a/flutter_quill_extensions/lib/utils/element_utils/element_shared_utils.dart b/flutter_quill_extensions/lib/utils/element_utils/element_shared_utils.dart index 927a97e18..8632fba0f 100644 --- a/flutter_quill_extensions/lib/utils/element_utils/element_shared_utils.dart +++ b/flutter_quill_extensions/lib/utils/element_utils/element_shared_utils.dart @@ -1,3 +1,5 @@ +import 'package:flutter/widgets.dart' show BuildContext, MediaQuery; + Map parseCssString(String cssString) { final result = {}; final declarations = cssString.split(';'); @@ -14,16 +16,71 @@ Map parseCssString(String cssString) { return result; } -double? parseCssPropertyAsDouble(String value) { +enum _CssUnit { + px('px'), + percentage('%'), + viewportWidth('vw'), + viewportHeight('vh'), + em('em'), + rem('rem'), + invalid('invalid'); + + const _CssUnit(this.cssName); + + final String cssName; +} + +double? parseCssPropertyAsDouble( + String value, { + required BuildContext context, +}) { if (value.trim().isEmpty) { return null; } - final list = [ - 'px', - // '%', 'vw', 'vh', 'em', 'rem' - ]; - for (final element in list) { - value = value.replaceFirst(element, ''); + + // Try to parse it in case it's a valid double already + var doubleValue = double.tryParse(value); + + if (doubleValue != null) { + return doubleValue; + } + + // If not then if it's a css numberic value then we will try to parse it + final unit = _CssUnit.values + .where((element) => value.endsWith(element.cssName)) + .firstOrNull; + if (unit == null) { + return null; + } + value = value.replaceFirst(unit.cssName, ''); + doubleValue = double.tryParse(value); + if (doubleValue != null) { + switch (unit) { + case _CssUnit.px: + // Do nothing + break; + case _CssUnit.percentage: + // Not supported yet + doubleValue = null; + break; + case _CssUnit.viewportWidth: + doubleValue = (doubleValue / 100) * MediaQuery.sizeOf(context).width; + break; + case _CssUnit.viewportHeight: + doubleValue = (doubleValue / 100) * MediaQuery.sizeOf(context).height; + break; + case _CssUnit.em: + doubleValue = MediaQuery.textScalerOf(context).scale(doubleValue); + break; + case _CssUnit.rem: + // Not fully supported yet + doubleValue = MediaQuery.textScalerOf(context).scale(doubleValue); + break; + case _CssUnit.invalid: + // Ignore + doubleValue = null; + break; + } } - return double.tryParse(value); + return doubleValue; } diff --git a/flutter_quill_extensions/lib/utils/element_utils/element_utils.dart b/flutter_quill_extensions/lib/utils/element_utils/element_utils.dart index 24bd166e7..6be7c1635 100644 --- a/flutter_quill_extensions/lib/utils/element_utils/element_utils.dart +++ b/flutter_quill_extensions/lib/utils/element_utils/element_utils.dart @@ -1,5 +1,5 @@ import 'package:flutter/foundation.dart' show immutable; -import 'package:flutter/widgets.dart' show Alignment; +import 'package:flutter/widgets.dart' show Alignment, BuildContext; import 'package:flutter_quill/extensions.dart'; import 'package:flutter_quill/flutter_quill.dart' show Attribute, Node; @@ -18,15 +18,20 @@ enum ExtraElementProperties { Alignment alignment, ) getElementAttributes( Node node, + BuildContext context, ) { var elementSize = const ElementSize(null, null); var elementAlignment = Alignment.center; double? elementMargin; final heightValue = parseCssPropertyAsDouble( - node.style.attributes[Attribute.height.key]?.value.toString() ?? ''); + node.style.attributes[Attribute.height.key]?.value.toString() ?? '', + context: context, + ); final widthValue = parseCssPropertyAsDouble( - node.style.attributes[Attribute.width.key]?.value.toString() ?? ''); + node.style.attributes[Attribute.width.key]?.value.toString() ?? '', + context: context, + ); if (heightValue != null) { elementSize = elementSize.copyWith( @@ -46,11 +51,14 @@ enum ExtraElementProperties { final cssAttrs = parseCssString(cssStyle.value.toString()); - // todo: This could be improved much better - final cssHeightValue = - parseCssPropertyAsDouble((cssAttrs[Attribute.height.key]) ?? ''); - final cssWidthValue = - parseCssPropertyAsDouble((cssAttrs[Attribute.width.key]) ?? ''); + final cssHeightValue = parseCssPropertyAsDouble( + (cssAttrs[Attribute.height.key]) ?? '', + context: context, + ); + final cssWidthValue = parseCssPropertyAsDouble( + (cssAttrs[Attribute.width.key]) ?? '', + context: context, + ); // cssHeightValue != null && elementSize.height == null if (cssHeightValue != null) { diff --git a/flutter_quill_extensions/lib/utils/patterns.dart b/flutter_quill_extensions/lib/utils/patterns.dart new file mode 100644 index 000000000..53c1650d6 --- /dev/null +++ b/flutter_quill_extensions/lib/utils/patterns.dart @@ -0,0 +1,17 @@ +RegExp base64RegExp = RegExp( + r'^(?:[A-Za-z0-9+\/]{4})*(?:[A-Za-z0-9+\/]{2}==|[A-Za-z0-9+\/]{3}=|[A-Za-z0-9+\/]{4})$', +); + +final imageRegExp = RegExp( + r'https?://.*?\.(?:png|jpe?g|gif|bmp|webp|tiff?)', + caseSensitive: false, +); + +final videoRegExp = RegExp( + r'\bhttps?://\S+\.(mp4|mov|avi|mkv|flv|wmv|webm)\b', + caseSensitive: false, +); +final youtubeRegExp = RegExp( + r'^((?:https?:)?\/\/)?((?:www|m)\.)?((?:youtube(-nocookie)?\.com|youtu.be))(\/(?:[\w\-]+\?v=|embed\/|live\/|v\/)?)([\w\-]+)(\S+)?$', + caseSensitive: false, +); diff --git a/flutter_quill_extensions/lib/utils/utils.dart b/flutter_quill_extensions/lib/utils/utils.dart index 12c802a11..37f5f0205 100644 --- a/flutter_quill_extensions/lib/utils/utils.dart +++ b/flutter_quill_extensions/lib/utils/utils.dart @@ -1,16 +1,15 @@ import 'dart:io' show File; -import 'package:flutter/foundation.dart' show immutable; +import 'package:cross_file/cross_file.dart'; +import 'package:flutter/foundation.dart' show Uint8List, immutable; +import 'package:http/http.dart' as http; import '../embeds/widgets/image.dart'; import '../services/image_saver/s_image_saver.dart'; - -RegExp _base64 = RegExp( - r'^(?:[A-Za-z0-9+\/]{4})*(?:[A-Za-z0-9+\/]{2}==|[A-Za-z0-9+\/]{3}=|[A-Za-z0-9+\/]{4})$', -); +import 'patterns.dart'; bool isBase64(String str) { - return _base64.hasMatch(str); + return base64RegExp.hasMatch(str); } bool isHttpBasedUrl(String url) { @@ -48,6 +47,23 @@ class SaveImageResult { final SaveImageResultMethod method; } +Future convertImageToUint8List(String image) async { + if (isHttpBasedUrl(image)) { + final response = await http.get(Uri.parse(image)); + if (response.statusCode == 200) { + return Uint8List.fromList(response.bodyBytes); + } + return null; + } + // TODO: Add support for all image providers like AssetImage + try { + final file = XFile(image); + return await file.readAsBytes(); + } catch (e) { + return null; + } +} + Future saveImage({ required String imageUrl, required ImageSaverService imageSaverService, diff --git a/flutter_quill_extensions/pubspec.yaml b/flutter_quill_extensions/pubspec.yaml index 254d628a2..8f3cf2068 100644 --- a/flutter_quill_extensions/pubspec.yaml +++ b/flutter_quill_extensions/pubspec.yaml @@ -1,6 +1,6 @@ name: flutter_quill_extensions description: Embed extensions for flutter_quill including image, video, formula and etc. -version: 0.8.0-dev +version: 9.0.0-dev-8 homepage: https://github.com/singerdmx/flutter-quill/tree/master/flutter_quill_extensions/ repository: https://github.com/singerdmx/flutter-quill/tree/master/flutter_quill_extensions/ issue_tracker: https://github.com/singerdmx/flutter-quill/issues/ @@ -35,15 +35,17 @@ dependencies: universal_html: ^2.2.4 cross_file: ^0.3.3+6 - flutter_quill: ^8.6.0 + flutter_quill: ^9.0.0-dev photo_view: ^0.14.0 # Plugins video_player: ^2.8.1 youtube_player_flutter: ^8.1.2 + url_launcher: ^6.2.1 + super_clipboard: ^0.7.3 gal: ^2.1.3 + gal_linux: ^0.0.1-dev-3 image_picker: ^1.0.4 - url_launcher: ^6.2.1 dev_dependencies: flutter_test: diff --git a/flutter_quill_test/CHANGELOG.md b/flutter_quill_test/CHANGELOG.md index 324c4355e..09d629a35 100644 --- a/flutter_quill_test/CHANGELOG.md +++ b/flutter_quill_test/CHANGELOG.md @@ -1,16 +1,1626 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +## 9.0.0-dev-8 +* Better support for pasting HTML contents from external websites to the editor +* The experimental support of converting the HTML from `quill_html_converter` is now built-in in the `flutter_quill` and removed from there (Breaking change for `quill_html_converter`) + +## 9.0.0-dev-7 +* Fix a bug in chaning the background/font color of ol/ul list +* Flutter Quill Extensions: + * Fix link bug in the video url + * Fix patterns + +## 9.0.0-dev-6 +* Move the `child` from `QuillToolbarConfigurations` into `QuillToolbar` directly +* Bug fixes +* Add the ability to change the background and font color of the ol/ul elements dots and numbers +* Flutter Quill Extensions: + * **Breaking Change**: The `imageProviderBuilder`is now providing the context and image url + +## 9.0.0-dev-5 +* The `QuillToolbar` is now accepting only `child` with no configurations so you can customize everything you wants, the `QuillToolbar.simple()` or `QuillSimpleToolbar` implements a simple toolbar that is based on `QuillToolbar`, you are free to use it but it just an example and not standard +* Flutter Quill Extensions: + * Improve the camera button + +## 9.0.0-dev-4 +* The options parameter in all of the buttons is no longer required which can be useful to create custom toolbar with minimal efforts +* Toolbar buttons fixes in both `flutter_quill` and `flutter_quill_extensions` +* The `QuillProvider` has been dropped and no longer used, the providers will be used only internally from now on and we will not using them as much as possible + +## 9.0.0-dev-3 +* Breaking Changes: + * Rename `QuillToolbar` to `QuillSimpleToolbar` + * Rename `QuillBaseToolbar` to `QuillToolbar` + * Replace `pasteboard` with `rich_cliboard` +* Fix a bug in the example when inserting an image from url +* Flutter Quill Extensions: + * Add support for copying the image to the system cliboard + +## 9.0.0-dev-2 +* An attemp to fix CI automated publishing + +## 9.0.0-dev-1 +* An attemp to fix CI automated publishing + +## 9.0.0-dev +* **Major Breaking change**: The `QuillProvider` is now optional, the `controller` parameter has been moved to the `QuillEditor` and `QuillToolbar` once again. +* Flutter Quill Extensions; + * **Breaking Change**: Completly change the way how the source code structured to more basic and simple way, organize folders and file names, if you use the library +from `flutter_quill_extensions.dart` then there is nothing you need to do, but if you are using any other import then you need to re-imports +embed, this won't affect how quill js work + * Improvemenets to the image embed + * Add support for `margin` for web + * Add untranslated strings to the `quill_en.arb` + +## 8.6.4 +* The default value of `keyboardAppearance` for the iOS will be the one from the App/System theme mode instead of always using the `Brightness.light` +* Fix typos in `README.md` + +## 8.6.3 +* Update the minimum flutter version to `3.16.0` + +## 8.6.2 +* Restore use of alternative QuillToolbarLinkStyleButton2 widget + +## 8.6.1 +* Temporary revert style bug fix + +## 8.6.0 +* **Breaking Change** Support [Flutter 3.16](https://medium.com/flutter/whats-new-in-flutter-3-16-dba6cb1015d1), please upgrade to the latest stable version of flutter to use this update +* **Breaking Change**: Remove Deprecated Fields +* **Breaking Change**: Extract the shared things between `QuillToolbarConfigurations` and `QuillBaseToolbarConfigurations` +* **Breaking Change**: You no longer need to use `QuillToolbarProvider` when using custom toolbar buttons, the example has been updated +* Bug fixes + +## 8.5.5 +* Now when opening dialogs by `QuillToolbar` you will not get an exception when you don't use `FlutterQuillLocalizations.delegate` in your `WidgetsApp`, `MaterialApp`, or `CupertinoApp`. The fix is for the `QuillToolbarSearchButton`, `QuillToolbarLinkStyleButton`, and `QuillToolbarColorButton` buttons + +## 8.5.4 +* The `mobileWidth`, `mobileHeight`, `mobileMargin`, and `mobileAlignment` is now deprecated in `flutter_quill`, they are now defined in `flutter_quill_extensions` +* Deprecate `replaceStyleStringWithSize` function which is in `string.dart` +* Deprecate `alignment`, and `margin` as they don't conform to official Quill JS + +## 8.5.3 +* Update doc +* Update `README.md` and `CHANGELOG.md` +* Fix typos +* Use `immutable` when possible +* Update `.pubignore` + +## 8.5.2 +* Updated `README.md`. +* Feature: Added the ability to include a custom callback when the `QuillToolbarColorButton` is pressed. +* The `QuillToolbar` now implements `PreferredSizeWidget`, enabling usage in the AppBar, similar to `QuillBaseToolbar`. + +## 8.5.1 +* Updated `README.md`. + +## 8.5.0 +* Migrated to `flutter_localizations` for translations. +* Fixed: Translated all previously untranslated localizations. +* Fixed: Added translations for missing items. +* Fixed: Introduced default Chinese fallback translation. +* Removed: Unused parameters `items` in `QuillToolbarFontFamilyButtonOptions` and `QuillToolbarFontSizeButtonOptions`. +* Updated: Documentation. + +## 8.4.4 +* Updated `.pubignore` to ignore unnecessary files and folders. + +## 8.4.3 +* Updated `CHANGELOG.md`. + +## 8.4.2 +* **Breaking change**: Configuration for `QuillRawEditor` has been moved to a separate class. Additionally, `readOnly` has been renamed to `isReadOnly`. If using `QuillEditor`, no action is required. +* Introduced the ability for developers to override `TextInputAction` in both `QuillRawEditor` and `QuillEditor`. +* Enabled using `QuillRawEditor` without `QuillEditorProvider`. +* Bug fixes. +* Added image cropping implementation in the example. + +## 8.4.1 +* Added `copyWith` in `OptionalSize` class. + +## 8.4.0 +* **Breaking change**: Updated `QuillCustomButton` to use `QuillCustomButtonOptions`. Moved all properties from `QuillCustomButton` to `QuillCustomButtonOptions`, replacing `iconData` with `icon` widget for increased customization. +* **Breaking change**: `customButtons` in `QuillToolbarConfigurations` is now of type `List`. +* Bug fixes following the `8.0.0` update. +* Updated `README.md`. +* Improved platform checking. + +## 8.3.0 +* Added `iconButtonFactor` property to `QuillToolbarBaseButtonOptions` for customizing button size relative to its icon size (defaults to `kIconButtonFactor`, consistent with previous releases). + +## 8.2.6 +* Organized `QuillRawEditor` code. + +## 8.2.5 +* Added `builder` property in `QuillEditorConfigurations`. + +## 8.2.4 +* Adhered to Flutter best practices. +* Fixed auto-focus bug. + +## 8.2.3 +* Updated `README.md`. + +## 8.2.2 +* Moved `flutter_quill_test` to a separate package: [flutter_quill_test](https://pub.dev/packages/flutter_quill_test). + +## 8.2.1 +* Updated `README.md`. + +## 8.2.0 +* Added the option to add configurations for `flutter_quill_extensions` using `extraConfigurations`. + +## 8.1.11 +* Followed Dart best practices by using `lints` and removed `pedantic` and `platform` since they are not used. +* Fixed text direction bug. +* Updated `README.md`. + +## 8.1.10 +* Secret for automated publishing to pub.dev. + +## 8.1.9 +* Fixed automated publishing to pub.dev. + +## 8.1.8 +* Fixed automated publishing to pub.dev. + +## 8.1.7 +* Automated publishing to pub.dev. + +## 8.1.6 +* Fixed compatibility with `integration_test` by downgrading the minimum version of the platform package to 3.1.0. + +## 8.1.5 +* Reversed background/font color toolbar button icons. + +## 8.1.4 +* Reversed background/font color toolbar button tooltips. + +## 8.1.3 +* Moved images to screenshots instead of `README.md`. + +## 8.1.2 +* Fixed a bug related to the regexp of the insert link dialog. +* Required Dart 3 as the minimum version. +* Code cleanup. +* Added a spacer widget between each button in the `QuillToolbar`. + +## 8.1.1 +* Fixed null error in line.dart #1487(https://github.com/singerdmx/flutter*quill/issues/1487). + +## 8.1.0 +* Fixed a word typo of `mirgration` to `migration` in the readme & migration document. +* Updated migration guide. +* Removed property `enableUnfocusOnTapOutside` in `QuillEditor` configurations and added `isOnTapOutsideEnabled` instead. +* Added a new callback called `onTapOutside` in the `QuillEditorConfigurations` to perform actions when tapping outside the editor. +* Fixed a bug that caused the web platform to not unfocus the editor when tapping outside of it. To override this, please pass a value to the `onTapOutside` callback. +* Removed the old property of `iconTheme`. Instead, pass `iconTheme` in the button options; you will find the `base` property inside it with `iconTheme`. + +## 8.0.0 +* If you have migrated recently, don't be alarmed by this update; it adds documentation, a migration guide, and marks the version as a more stable release. Although there are breaking changes (as reported by some developers), the major version was not changed due to time constraints during development. A single property was also renamed from `code` to `codeBlock` in the `elements` of the new `QuillEditorConfigurations` class. +* Updated the README for better readability. + +## 7.10.2 +* Removed line numbers from code blocks by default. You can still enable this feature thanks to the new configurations in the `QuillEditor`. Find the `elementOptions` property and enable `enableLineNumbers`. + +## 7.10.1 +* Fixed issues and utilized the new parameters. +* No longer need to use `MaterialApp` for most toolbar button child builders. +* Compatibility with [fresh_quill_extensions](https://pub.dev/packages/fresh_quill_extensions), a temporary alternative to [flutter_quill_extensions](https://pub.dev/packages/flutter_quill_extensions). +* Updated most of the documentation in `README.md`. + +## 7.10.0 +* **Breaking change**: `QuillToolbar.basic()` can be accessed directly from `QuillToolbar()`, and the old `QuillToolbar` can be accessed from `QuillBaseToolbar`. +* Refactored Quill editor and toolbar configurations into a single class each. +* After changing checkbox list values, the controller will not request keyboard focus by default. +* Moved toolbar and editor configurations directly into the widget but still use inherited widgets internally. +* Fixes to some code after the refactoring. + +## 7.9.0 +* Buttons Improvemenets +* Refactor all the button configurations that used in `QuillToolbar.basic()` but there are still few lefts +* **Breaking change**: Remove some configurations from the QuillToolbar and move them to the new `QuillProvider`, please notice this is a development version and this might be changed in the next few days, the stable release will be ready in less than 3 weeks +* Update `flutter_quill_extensions` and it will be published into pub.dev soon. +* Allow you to customize the search dialog by custom callback with child builder + +## 7.8.0 +* **Important note**: this is not test release yet, it works but need more test and changes and breaking changes, we don't have development version and it will help us if you try the latest version and report the issues in Github but if you want a stable version please use `7.4.16`. this refactoring process will not take long and should be done less than three weeks with the testing. +* We managed to refactor most of the buttons configurations and customizations in the `QuillProvider`, only three lefts then will start on refactoring the toolbar configurations +* Code improvemenets + +## 7.7.0 +* **Breaking change**: We have mirgrated more buttons in the toolbar configurations, you can do change them in the `QuillProvider` +* Important bug fixes + +## 7.6.1 +* Bug fixes + +## 7.6.0 +* **Breaking change**: To customize the buttons in the toolbar, you can do that in the `QuillProvider` + +## 7.5.0 +* **Breaking change**: The widgets `QuillEditor` and `QuillToolbar` are no longer have controller parameter, instead you need to make sure in the widget tree you have wrapped them with `QuillProvider` widget and provide the controller and the require configurations + +## 7.4.16 +* Update documentation and README.md + +## 7.4.15 +* Custom style attrbuites for platforms other than mobile (alignment, margin, width, height) +* Bug fixes and other improvemenets + +## 7.4.14 +* Improve performance by reducing the number of widgets rebuilt by listening to media query for only the needed things, for example instead of using `MediaQuery.of(context).size`, now we are using `MediaQuery.sizeOf(context)` +* Add MediaButton for picking the images only since the video one is not ready +* A new feature which allows customizing the text selection in quill editor which is useful for custom theme design system for custom app widget + +## 7.4.13 +* Fixed tab editing when in readOnly mode. + +## 7.4.12 +* Update the minimum version of device_info_plus to 9.1.0. + +## 7.4.11 +* Add sw locale. + +## 7.4.10 +* Update translations. + +## 7.4.9 +* Style recognition fixes. + +## 7.4.8 +* Upgrade dependencies. + +## 7.4.7 +* Add Vietnamese and German translations. + +## 7.4.6 +* Fix more null errors in Leaf.retain [##1394](https://github.com/singerdmx/flutter-quill/issues/1394) and Line.delete [##1395](https://github.com/singerdmx/flutter-quill/issues/1395). + +## 7.4.5 +* Fix null error in Container.insert [##1392](https://github.com/singerdmx/flutter-quill/issues/1392). + +## 7.4.4 +* Fix extra padding on checklists [##1131](https://github.com/singerdmx/flutter-quill/issues/1131). + +## 7.4.3 +* Fixed a space input error on iPad. + +## 7.4.2 +* Fix bug with keepStyleOnNewLine for link. + +## 7.4.1 +* Fix toolbar dividers condition. + +## 7.4.0 +* Support Flutter version 3.13.0. + +## 7.3.3 +* Updated Dependencies conflicting. + +## 7.3.2 +* Added builder for custom button in _LinkDialog. + +## 7.3.1 +* Added case sensitive and whole word search parameters. +* Added wrap around. +* Moved search dialog to the bottom in order not to override the editor and the text found. +* Other minor search dialog enhancements. + +## 7.3.0 +* Add default attributes to basic factory. + +## 7.2.19 +* Feat/link regexp. + +## 7.2.18 +* Fix paste block text in words apply same style. + +## 7.2.17 +* Fix paste text mess up style. +* Add support copy/cut block text. + +## 7.2.16 +* Allow for custom context menu. + +## 7.2.15 +* Add flutter_quill.delta library which only exposes Delta datatype. + +## 7.2.14 +* Fix errors when the editor is used in the `screenshot` package. + +## 7.2.13 +* Fix around image can't delete line break. + +## 7.2.12 +* Add support for copy/cut select image and text together. + +## 7.2.11 +* Add affinity for localPosition. + +## 7.2.10 +* LINE._getPlainText queryChild inclusive=false. + +## 7.2.9 +* Add toPlainText method to `EmbedBuilder`. + +## 7.2.8 +* Add custom button widget in toolbar. + +## 7.2.7 +* Fix language code of Japan. + +## 7.2.6 +* Style custom toolbar buttons like builtins. + +## 7.2.5 +* Always use text cursor for editor on desktop. + +## 7.2.4 +* Fixed keepStyleOnNewLine. + +## 7.2.3 +* Get pixel ratio from view. + +## 7.2.2 +* Prevent operations on stale editor state. + +## 7.2.1 +* Add support for android keyboard content insertion. +* Enhance color picker, enter hex color and color palette option. + +## 7.2.0 +* Checkboxes, bullet points, and number points are now scaled based on the default paragraph font size. + +## 7.1.20 +* Pass linestyle to embedded block. + +## 7.1.19 +* Fix Rtl leading alignment problem. + +## 7.1.18 +* Support flutter latest version. + +## 7.1.17+1 +* Updates `device_info_plus` to version 9.0.0 to benefit from AGP 8 (see [changelog##900](https://pub.dev/packages/device_info_plus/changelog##900)). + +## 7.1.16 +* Fixed subscript key from 'sup' to 'sub'. + +## 7.1.15 +* Fixed a bug introduced in 7.1.7 where each section in `QuillToolbar` was displayed on its own line. + +## 7.1.14 +* Add indents change for multiline selection. + +## 7.1.13 + +* Add custom recognizer. + +## 7.1.12 + +* Add superscript and subscript styles. + +## 7.1.11 + +* Add inserting indents for lines of list if text is selected. + +## 7.1.10 + +* Image embedding tweaks + * Add MediaButton which is intened to superseed the ImageButton and VideoButton. Only image selection is working. + * Implement image insert for web (image as base64) + +## 7.1.9 + +* Editor tweaks PR from bambinoua(https://github.com/bambinoua). + * Shortcuts now working in Mac OS + * QuillDialogTheme is extended with new properties buttonStyle, linkDialogConstraints, imageDialogConstraints, isWrappable, runSpacing, + * Added LinkStyleButton2 with new LinkStyleDialog (similar to Quill implementation + * Conditinally use Row or Wrap for dialog's children. + * Update minimum Dart SDK version to 2.17.0 to use enum extensions. + * Use merging shortcuts and actions correclty (if the key combination is the same) + +## 7.1.8 + +* Dropdown tweaks + * Add itemHeight, itemPadding, defaultItemColor for customization of dropdown items. + * Remove alignment property as useless. + * Fix bugs with max width when width property is null. + +## 7.1.7 + +* Toolbar tweaks. + * Implement tooltips for embed CameraButton, VideoButton, FormulaButton, ImageButton. + * Extends customization for SelectAlignmentButton, QuillFontFamilyButton, QuillFontSizeButton adding padding, text style, alignment, width. + * Add renderFontFamilies to QuillFontFamilyButton to show font faces in dropdown. + * Add AxisDivider and its named constructors for for use in parent project. + * Export ToolbarButtons enum to allow specify tooltips for SelectAlignmentButton. + * Export QuillFontFamilyButton, SearchButton as they were not exported before. + * Deprecate items property in QuillFontFamilyButton, QuillFontSizeButton as the it can be built usinr rawItemsMap. + * Make onSelection QuillFontFamilyButton, QuillFontSizeButton omittable as no need to execute callback outside if controller is passed to widget. + +Now the package is more friendly for web projects. + +## 7.1.6 + +* Add enableUnfocusOnTapOutside field to RawEditor and Editor widgets. + +## 7.1.5 + +* Add tooltips for toolbar buttons. + +## 7.1.4 + +* Fix inserting tab character in lists. + +## 7.1.3 + +* Fix ios cursor bug when word.length==1. + +## 7.1.2 + +* Fix non scrollable editor exception, when tapped under content. + +## 7.1.1 + +* customLinkPrefixes parameter * makes possible to open links with custom protoco. + +## 7.1.0 + +* Fix ordered list numeration with several lists in document. + +## 7.0.9 + +* Use const constructor for EmbedBuilder. + +## 7.0.8 + +* Fix IME position bug with scroller. + +## 7.0.7 + +* Add TextFieldTapRegion for contextMenu. + +## 7.0.6 + +* Fix line style loss on new line from non string. + +## 7.0.5 + +* Fix IME position bug for Mac and Windows. +* Unfocus when tap outside editor. fix the bug that cant refocus in afterButtonPressed after click ToggleStyleButton on Mac. + +## 7.0.4 + +* Have text selection span full line height for uneven sized text. + +## 7.0.3 + +* Fix ordered list numeration for lists with more than one level of list. + +## 7.0.2 + +* Allow widgets to override widget span properties. + +## 7.0.1 + +* Update i18n_extension dependency to version 8.0.0. + +## 7.0.0 + +* Breaking change: Tuples are no longer used. They have been replaced with a number of data classes. + +## 6.4.4 + +* Increased compatibility with Flutter widget tests. + +## 6.4.3 + +* Update dependencies (collection: 1.17.0, flutter_keyboard_visibility: 5.4.0, quiver: 3.2.1, tuple: 2.0.1, url_launcher: 6.1.9, characters: 1.2.1, i18n_extension: 7.0.0, device_info_plus: 8.1.0) + +## 6.4.2 + +* Replace `buildToolbar` with `contextMenuBuilder`. + +## 6.4.1 + +* Control the detect word boundary behaviour. + +## 6.4.0 + +* Use `axis` to make the toolbar vertical. +* Use `toolbarIconCrossAlignment` to align the toolbar icons on the cross axis. +* Breaking change: `QuillToolbar`'s parameter `toolbarHeight` was renamed to `toolbarSize`. + +## 6.3.5 + +* Ability to add custom shortcuts. + +## 6.3.4 + +* Update clipboard status prior to showing selected text overlay. + +## 6.3.3 + +* Fixed handling of mac intents. + +## 6.3.2 + +* Added `unknownEmbedBuilder` to QuillEditor. +* Fix error style when input chinese japanese or korean. + +## 6.3.1 + +* Add color property to the basic factory function. + +## 6.3.0 + +* Support Flutter 3.7. + +## 6.2.2 + +* Fix: nextLine getter null where no assertion. + +## 6.2.1 + +* Revert "Align numerical and bullet lists along with text content". + +## 6.2.0 + +* Align numerical and bullet lists along with text content. + +## 6.1.12 + +* Apply i18n for default font dropdown option labels corresponding to 'Clear'. + +## 6.1.11 + +* Remove iOS hack for delaying focus calculation. + +## 6.1.10 + +* Delay focus calculation for iOS. + +## 6.1.9 + +* Bump keyboard show up wait to 1 sec. + +## 6.1.8 + +* Recalculate focus when showing keyboard. + +## 6.1.7 + +* Add czech localizations. + +## 6.1.6 + +* Upgrade i18n_extension to 6.0.0. + +## 6.1.5 + +* Fix formatting exception. + +## 6.1.4 + +* Add double quotes validation. + +## 6.1.3 + +* Revert "fix order list numbering (##988)". + +## 6.1.2 + +* Add typing shortcuts. + +## 6.1.1 + +* Fix order list numbering. + +## 6.1.0 + +* Add keyboard shortcuts for editor actions. + +## 6.0.10 + +* Upgrade device info plus to ^7.0.0. + +## 6.0.9 + +* Don't throw showAutocorrectionPromptRect not implemented. The function is called with every keystroke as a user is typing. + +## 6.0.8+1 + +* Fixes null pointer when setting documents. + +## 6.0.8 + +* Make QuillController.document mutable. + +## 6.0.7 + +* Allow disabling of selection toolbar. + +## 6.0.6+1 + +* Revert 6.0.6. + +## 6.0.6 + +* Fix wrong custom embed key. + +## 6.0.5 + +* Fixes toolbar buttons stealing focus from editor. + +## 6.0.4 + +* Bug fix for Type 'Uint8List' not found. + +## 6.0.3 + +* Add ability to paste images. + +## 6.0.2 + +* Address Dart Analysis issues. + +## 6.0.1 + +* Changed translation country code (zh_HK -> zh_hk) to lower case, which is required for i18n_extension used in flutter_quill. +* Add localization in example's main to demonstrate translation. +* Issue Windows selection's copy / paste tool bar not shown ##861: add selection's copy / paste toolbar, escape to hide toolbar, mouse right click to show toolbar, ctrl-Y / ctrl-Z to undo / redo. +* Image and video displayed in Windows platform caused screen flickering while selecting text, a sample_data_nomedia.json asset is added for Desktop to demonstrate the added features. +* Known issue: keyboard action sometimes causes exception mentioned in Flutter's issue ##106475 (Windows Keyboard shortcuts stop working after modifier key repeat flutter/flutter##106475). +* Know issue: user needs to click the editor to get focus before toolbar is able to display. + +## 6.0.0 BREAKING CHANGE + +* Removed embed (image, video & formula) blocks from the package to reduce app size. + +These blocks have been moved to the package `flutter_quill_extensions`, migrate by filling the `embedBuilders` and `embedButtons` parameters as follows: + +``` +import 'package:flutter_quill_extensions/flutter_quill_extensions.dart'; + +QuillEditor.basic( + controller: controller, + embedBuilders: FlutterQuillEmbeds.builders(), +); + +QuillToolbar.basic( + controller: controller, + embedButtons: FlutterQuillEmbeds.buttons(), +); +``` + +## 5.4.2 + +* Upgrade i18n_extension. + +## 5.4.1 + +* Update German Translation. + +## 5.4.0 + +* Added Formula Button (for maths support). + +## 5.3.2 + +* Add more font family. + +## 5.3.1 + +* Enable search when text is not empty. + +## 5.3.0 + +* Added search function. + +## 5.2.11 + +* Remove default small color. + +## 5.2.10 + +* Don't wrap the QuillEditor's child in the EditorTextSelectionGestureDetector if selection is disabled. + +## 5.2.9 + +* Added option to modify SelectHeaderStyleButton options. +* Added option to click again on h1, h2, h3 button to go back to normal. + +## 5.2.8 + +* Remove tooltip for LinkStyleButton. +* Make link match regex case insensitive. + +## 5.2.7 + +* Add locale to QuillEditor.basic. + +## 5.2.6 + +* Fix keyboard pops up when resizing the image. + +## 5.2.5 + +* Upgrade youtube_player_flutter_quill to 8.2.2. + +## 5.2.4 + +* Upgrade youtube_player_flutter_quill to 8.2.1. + +## 5.2.3 + +* Flutter Quill Doesn't Work On iOS 16 or Xcode 14 Betas (Stored properties cannot be marked potentially unavailable with '@available'). + +## 5.2.2 + +* Fix Web Unsupported operation: Platform.\_operatingSystem error. + +## 5.2.1 + +* Rename QuillCustomIcon to QuillCustomButton. + +## 5.2.0 + +* Support font family selection. + +## 5.1.1 + +* Update README. + +## 5.1.0 + +* Added CustomBlockEmbed and customElementsEmbedBuilder. + +## 5.0.5 + +* Upgrade device_info_plus to 4.0.0. + +## 5.0.4 + +* Added onVideoInit callback for video documents. + +## 5.0.3 + +* Update dependencies. + +## 5.0.2 + +* Keep cursor position on checkbox tap. + +## 5.0.1 + +* Fix static analysis errors. + +## 5.0.0 + +* Flutter 3.0.0 support. + +## 4.2.3 + +* Ignore color:inherit and convert double to int for level. + +## 4.2.2 + +* Add clear option to font size dropdown. + +## 4.2.1 + +* Refactor font size dropdown. + +## 4.2.0 + +* Ensure selectionOverlay is available for showToolbar. + +## 4.1.9 + +* Using properly iconTheme colors. + +## 4.1.8 + +* Update font size dropdown. + +## 4.1.7 + +* Convert FontSize to a Map to allow for named Font Size. + +## 4.1.6 + +* Update quill_dropdown_button.dart. + +## 4.1.5 + +* Add Font Size dropdown to the toolbar. + +## 4.1.4 + +* New borderRadius for iconTheme. + +## 4.1.3 + +* Fix selection handles show/hide after paste, backspace, copy. + +## 4.1.2 + +* Add full support for hardware keyboards (Chromebook, Android tablets, etc) that don't alter screen UI. + +## 4.1.1 + +* Added textSelectionControls field in QuillEditor. + +## 4.1.0 + +* Added Node to linkActionPickerDelegate. + +## 4.0.12 + +* Add Persian(fa) language. + +## 4.0.11 + +* Fix cut selection error in multi-node line. + +## 4.0.10 + +* Fix vertical caret position bug. + +## 4.0.9 + +* Request keyboard focus when no child is found. + +## 4.0.8 + +* Fix blank lines do not display when **web*renderer=html. + +## 4.0.7 + +* Refactor getPlainText (better handling of blank lines and lines with multiple markups. + +## 4.0.6 + +* Bug fix for copying text with new lines. + +## 4.0.5 + +* Fixed casting null to Tuple2 when link dialog is dismissed without any input (e.g. barrier dismissed). + +## 4.0.4 + +* Bug fix for text direction rtl. + +## 4.0.3 + +* Support text direction rtl. + +## 4.0.2 + +* Clear toggled style on selection change. + +## 4.0.1 + +* Fix copy/cut/paste/selectAll not working. + +## 4.0.0 + +* Upgrade for Flutter 2.10. + +## 3.9.11 + +* Added Indonesian translation. + +## 3.9.10 + +* Fix for undoing a modification ending with an indented line. + +## 3.9.9 + +* iOS: Save image whose filename does not end with image file extension. + +## 3.9.8 + +* Added Urdu translation. + +## 3.9.7 + +* Fix for clicking on the Link button without any text on a new line crashes. + +## 3.9.6 + +* Apply locale to QuillEditor(contents). + +## 3.9.5 + +* Fix image pasting. + +## 3.9.4 + +* Hiding dialog after selecting action for image. + +## 3.9.3 + +* Update ImageResizer for Android. + +## 3.9.2 + +* Copy image with its style. + +## 3.9.1 + +* Support resizing image. + +## 3.9.0 + +* Image menu options for copy/remove. + +## 3.8.8 + +* Update set textEditingValue. + +## 3.8.7 + +* Fix checkbox not toggled correctly in toolbar button. + +## 3.8.6 + +* Fix cursor position changes when checking/unchecking the checkbox. + +## 3.8.5 + +* Fix \_handleDragUpdate in \_TextSelectionHandleOverlayState. + +## 3.8.4 + +* Fix link dialog layout. + +## 3.8.3 + +* Fix for errors on a non scrollable editor. + +## 3.8.2 + +* Fix certain keys not working on web when editor is a child of a scroll view. + +## 3.8.1 + +* Refactor \_QuillEditorState to QuillEditorState. + +## 3.8.0 + +* Support pasting with format. + +## 3.7.3 + +* Fix selection overlay for collapsed selection. + +## 3.7.2 + +* Reverted Embed toPlainText change. + +## 3.7.1 + +* Change Embed toPlainText to be empty string. + +## 3.7.0 + +* Replace Toolbar showHistory group with individual showRedo and showUndo. + +## 3.6.5 + +* Update Link dialogue for image/video. + +## 3.6.4 + +* Link dialogue TextInputType.multiline. + +## 3.6.3 + +* Bug fix for link button text selection. + +## 3.6.2 + +* Improve link button. + +## 3.6.1 + +* Remove SnackBar 'What is entered is not a link'. + +## 3.6.0 + +* Allow link button to enter text. + +## 3.5.3 + +* Change link button behavior. + +## 3.5.2 + +* Bug fix for embed. + +## 3.5.1 + +* Bug fix for platform util. + +## 3.5.0 + +* Removed redundant classes. + +## 3.4.4 + +* Add more translations. + +## 3.4.3 + +* Preset link from attributes. + +## 3.4.2 + +* Fix launch link edit mode. + +## 3.4.1 + +* Placeholder effective in scrollable. + +## 3.4.0 + +* Option to save image in read-only mode. + +## 3.3.1 + +* Pass any specified key in QuillEditor constructor to super. + +## 3.3.0 + +* Fixed Style toggle issue. + +## 3.2.1 + +* Added new translations. + +## 3.2.0 + +* Support multiple links insertion on the go. + +## 3.1.1 + +* Add selection completed callback. + +## 3.1.0 + +* Fixed image ontap functionality. + +## 3.0.4 + +* Add maxContentWidth constraint to editor. + +## 3.0.3 + +* Do not show caret on screen when the editor is not focused. + +## 3.0.2 + +* Fix launch link for read-only mode. + +## 3.0.1 + +* Handle null value of Attribute.link. + +## 3.0.0 + +* Launch link improvements. +* Removed QuillSimpleViewer. + +## 2.5.2 + +* Skip image when pasting. + +## 2.5.1 + +* Bug fix for Desktop `Shift` + `Click` support. + +## 2.5.0 + +* Update checkbox list. + +## 2.4.1 + +* Desktop selection improvements. + +## 2.4.0 + +* Improve inline code style. + +## 2.3.3 + +* Improves selection rects to have consistent height regardless of individual segment text styles. + +## 2.3.2 + +* Allow disabling floating cursor. + +## 2.3.1 + +* Preserve last newline character on delete. + +## 2.3.0 + +* Massive changes to support flutter 2.8. + +## 2.2.2 + +* iOS - floating cursor. + +## 2.2.1 + +* Bug fix for imports supporting flutter 2.8. + +## 2.2.0 + +* Support flutter 2.8. + +## 2.1.1 + +* Add methods of clearing editor and moving cursor. + +## 2.1.0 + +* Add delete handler. + +## 2.0.23 + +* Support custom replaceText handler. + +## 2.0.22 + +* Fix attribute compare and fix font size parsing. + +## 2.0.21 + +* Handle click on embed object. + +## 2.0.20 + +* Improved UX/UI of Image widget. + +## 2.0.19 + +* When uploading a video, applying indicator. + +## 2.0.18 + +* Make toolbar dividers optional. + +## 2.0.17 + +* Allow alignment of the toolbar icons to match WrapAlignment. + +## 2.0.16 + +* Add hide / show alignment buttons. + +## 2.0.15 + +* Implement change cursor to SystemMouseCursors.click when hovering a link styled text. + +## 2.0.14 + +* Enable customize the checkbox widget using DefaultListBlockStyle style. + +## 2.0.13 + +* Improve the scrolling performance by reducing the repaint areas. + +## 2.0.12 + +* Fix the selection effect can't be seen as the textLine with background color. + +## 2.0.11 + +* Fix visibility of text selection handlers on scroll. + +## 2.0.10 + +* cursorConnt.color notify the text_line to repaint if it was disposed. + +## 2.0.9 + +* Improve UX when trying to add a link. + +## 2.0.8 + +* Adding translations to the toolbar. + +## 2.0.7 + +* Added theming options for toolbar icons and LinkDialog. + +## 2.0.6 + +* Avoid runtime error when placed inside TabBarView. + +## 2.0.5 + +* Support inline code formatting. + +## 2.0.4 + +* Enable history shortcuts for desktop. + +## 2.0.3 + +* Fix cursor when line contains image. + +## 2.0.2 + +* Address KeyboardListener class name conflict. + +## 2.0.1 + +* Upgrade flutter_colorpicker to 0.5.0. + +## 2.0.0 + +* Text Alignment functions + Block Format standards. + +## 1.9.6 + +* Support putting QuillEditor inside a Scrollable view. + +## 1.9.5 + +* Skip image when pasting. + +## 1.9.4 + +* Bug fix for cursor position when tapping at the end of line with image(s). + +## 1.9.3 + +* Bug fix when line only contains one image. + +## 1.9.2 + +* Support for building custom inline styles. + +## 1.9.1 + +* Cursor jumps to the most appropriate offset to display selection. + +## 1.9.0 + +* Support inline image. + +## 1.8.3 + +* Updated quill_delta. + +## 1.8.2 + +* Support mobile image alignment. + +## 1.8.1 + +* Support mobile custom size image. + +## 1.8.0 + +* Support entering link for image/video. + +## 1.7.3 + +* Bumps photo_view version. + +## 1.7.2 + +* Fix static analysis error. + +## 1.7.1 + +* Support Youtube video. + +## 1.7.0 + +* Support video. + +## 1.6.4 + +* Bug fix for clear format button. + +## 1.6.3 + +* Fixed dragging right handle scrolling issue. + +## 1.6.2 + +* Fixed the position of the selection status drag handle. + +## 1.6.1 + +* Upgrade image_picker and flutter_colorpicker. + +## 1.6.0 + +* Support Multi Row Toolbar. + +## 1.5.0 + +* Remove file_picker dependency. + +## 1.4.1 + +* Remove filesystem_picker dependency. + +## 1.4.0 + +* Remove path_provider dependency. + +## 1.3.4 + +* Add option to paintCursorAboveText. + +## 1.3.3 + +* Upgrade file_picker version. + +## 1.3.2 + +* Fix copy/paste bug. + +## 1.3.1 + +* New logo. + +## 1.3.0 + +* Support flutter 2.2.0. + +## 1.2.2 + +* Checkbox supports tapping. + +## 1.2.1 + +* Indented position not holding while editing. + +## 1.2.0 + +* Fix image button cancel causes crash. + +## 1.1.8 + +* Fix height of empty line bug. + +## 1.1.7 + +* Fix text selection in read-only mode. + +## 1.1.6 + +* Remove universal_html dependency. + +## 1.1.5 + +* Enable "Select", "Select All" and "Copy" in read-only mode. + +## 1.1.4 + +* Fix text selection issue. + +## 1.1.3 + +* Update example folder. + +## 1.1.2 + +* Add pedantic. + +## 1.1.1 + +* Base64 image support. + +## 1.1.0 + +* Support null safety. + +## 1.0.9 + +* Web support for raw editor and keyboard listener. + +## 1.0.8 + +* Support token attribute. + +## 1.0.7 + +* Fix crash on web (dart:io). + +## 1.0.6 + +* Add desktop support WINDOWS, MACOS and LINUX. + +## 1.0.5 + +* Bug fix: Can not insert newline when Bold is toggled ON. + +## 1.0.4 + +* Upgrade photo_view to ^0.11.0. + +## 1.0.3 + +* Fix issue that text is not displayed while typing WEB. + +## 1.0.2 + +* Update toolbar in sample home page. + +## 1.0.1 + +* Fix static analysis errors. + +## 1.0.0 + +* Support flutter 2.0. + +## 1.0.0-dev.2 + +* Improve link handling for tel, mailto and etc. + +## 1.0.0-dev.1 + +* Upgrade prerelease SDK & Bump for master. + +## 0.3.5 + +* Fix for cursor focus issues when keyboard is on. + +## 0.3.4 + +* Improve link handling for tel, mailto and etc. + +## 0.3.3 + +* More fix on cursor focus issue when keyboard is on. + +## 0.3.2 + +* Fix cursor focus issue when keyboard is on. + +## 0.3.1 + +* cursor focus when keyboard is on. + +## 0.3.0 + +* Line Height calculated based on font size. + +## 0.2.12 + +* Support placeholder. + +## 0.2.11 + +* Fix static analysis error. + +## 0.2.10 + +* Update TextInputConfiguration autocorrect to true in stable branch. + +## 0.2.9 + +* Update TextInputConfiguration autocorrect to true. + +## 0.2.8 + +* Support display local image besides network image in stable branch. + +## 0.2.7 + +* Support display local image besides network image. + +## 0.2.6 + +* Fix cursor after pasting. + +## 0.2.5 + +* Toggle text/background color button in toolbar. + +## 0.2.4 + +* Support the use of custom icon size in toolbar. + +## 0.2.3 + +* Support custom styles and image on local device storage without uploading. + +## 0.2.2 + +* Update git repo. + +## 0.2.1 + +* Fix static analysis error. + +## 0.2.0 + +* Add checked/unchecked list button in toolbar. + +## 0.1.8 + +* Support font and size attributes. + +## 0.1.7 + +* Support checked/unchecked list. + +## 0.1.6 + +* Fix getExtentEndpointForSelection. + +## 0.1.5 + +* Support text alignment. + +## 0.1.4 + +* Handle url with trailing spaces. + +## 0.1.3 + +* Handle cursor position change when undo/redo. + +## 0.1.2 + +* Handle more text colors. + +## 0.1.1 + +* Fix cursor issue when undo. + +## 0.1.0 + +* Fix insert image. + +## 0.0.9 + +* Handle rgba color. + +## 0.0.8 + +* Fix launching url. + +## 0.0.7 + +* Handle multiple image inserts. + +## 0.0.6 + +* More toolbar functionality. + ## 0.0.5 -* Update `README.md` + +* Update example. ## 0.0.4 -* Update `README.md` -* Documentation comments. + +* Update example. ## 0.0.3 -* Update the `README.md` and description + +* Update home page meta data. ## 0.0.2 -* Add `.test_config` to mark the package as testing package + +* Support image upload and launch url in read-only mode. ## 0.0.1 -* initial release. +* Rich text editor based on Quill Delta. diff --git a/flutter_quill_test/pubspec.yaml b/flutter_quill_test/pubspec.yaml index 2c18005af..e50efc52b 100644 --- a/flutter_quill_test/pubspec.yaml +++ b/flutter_quill_test/pubspec.yaml @@ -1,6 +1,6 @@ name: flutter_quill_test description: Test utilities for flutter_quill which includes methods to simplify interacting with the editor in test cases. -version: 0.0.5 +version: 9.0.0-dev-8 homepage: https://github.com/singerdmx/flutter-quill/tree/master/flutter_quill_test/ repository: https://github.com/singerdmx/flutter-quill/tree/master/flutter_quill_test/ issue_tracker: https://github.com/singerdmx/flutter-quill/issues/ @@ -28,7 +28,7 @@ environment: dependencies: flutter: sdk: flutter - flutter_quill: ^8.2.5 + flutter_quill: ^9.0.0-dev-6 flutter_test: sdk: flutter diff --git a/lib/flutter_quill.dart b/lib/flutter_quill.dart index 2c3170615..0e14a5a9b 100644 --- a/lib/flutter_quill.dart +++ b/lib/flutter_quill.dart @@ -1,9 +1,9 @@ library flutter_quill; -export 'src/extensions/quill_provider.dart'; +export 'src/extensions/quill_configurations_ext.dart'; export 'src/models/config/quill_configurations.dart'; -export 'src/models/config/raw_editor/configurations.dart'; -export 'src/models/config/toolbar/base_toolbar_configurations.dart'; +export 'src/models/config/raw_editor/raw_editor_configurations.dart'; +export 'src/models/config/toolbar/toolbar_configurations.dart'; export 'src/models/documents/attribute.dart'; export 'src/models/documents/document.dart'; export 'src/models/documents/nodes/block.dart'; @@ -22,15 +22,17 @@ export 'src/models/structs/vertical_spacing.dart'; export 'src/models/themes/quill_dialog_theme.dart'; export 'src/models/themes/quill_icon_theme.dart'; export 'src/utils/embeds.dart'; -export 'src/widgets/controller.dart'; -export 'src/widgets/cursor.dart'; -export 'src/widgets/default_styles.dart'; export 'src/widgets/editor/editor.dart'; -export 'src/widgets/embeds.dart'; -export 'src/widgets/link.dart' show LinkActionPickerDelegate, LinkMenuAction; +export 'src/widgets/others/cursor.dart'; +export 'src/widgets/others/default_styles.dart'; +export 'src/widgets/others/link.dart' + show LinkActionPickerDelegate, LinkMenuAction; +export 'src/widgets/quill/embeds.dart'; +export 'src/widgets/quill/quill_controller.dart'; export 'src/widgets/raw_editor/raw_editor.dart'; export 'src/widgets/raw_editor/raw_editor_state.dart'; export 'src/widgets/style_widgets/style_widgets.dart'; export 'src/widgets/toolbar/base_toolbar.dart'; -export 'src/widgets/toolbar/toolbar.dart'; +export 'src/widgets/toolbar/buttons/select_header_style_button.dart'; +export 'src/widgets/toolbar/simple_toolbar.dart'; export 'src/widgets/utils/provider.dart'; diff --git a/lib/markdown_quill.dart b/lib/markdown_quill.dart new file mode 100644 index 000000000..cca0aa1c9 --- /dev/null +++ b/lib/markdown_quill.dart @@ -0,0 +1,5 @@ +library quill_markdown; + +export 'src/packages/quill_markdown/delta_to_markdown.dart'; +export 'src/packages/quill_markdown/embeddable_table_syntax.dart'; +export 'src/packages/quill_markdown/markdown_to_delta.dart'; diff --git a/lib/src/extensions/quill_configurations_ext.dart b/lib/src/extensions/quill_configurations_ext.dart new file mode 100644 index 000000000..5ca2e5e17 --- /dev/null +++ b/lib/src/extensions/quill_configurations_ext.dart @@ -0,0 +1,93 @@ +import 'package:flutter/widgets.dart' show BuildContext; + +import '../../flutter_quill.dart'; + +extension QuillControllerExt on BuildContext { + /// return nullable [QuillController] + QuillController? get quilController { + return quillSimpleToolbarConfigurations?.controller ?? + quillEditorConfigurations?.controller; + } + + /// return [QuillController] as not null + QuillController get requireQuillController { + return quillSimpleToolbarConfigurations?.controller ?? + quillEditorConfigurations?.controller ?? + (throw ArgumentError( + 'The quill provider is required, you must only call requireQuillController inside the QuillToolbar and QuillEditor')); + } +} + +extension QuillSharedExt on BuildContext { + /// return nullable [QuillSharedConfigurations] + QuillSharedConfigurations? get quillSharedConfigurations { + return quillSimpleToolbarConfigurations?.sharedConfigurations ?? + quillEditorConfigurations?.sharedConfigurations; + } +} + +extension QuillEditorExt on BuildContext { + /// return [QuillEditorConfigurations] as not null + QuillEditorConfigurations get requireQuillEditorConfigurations { + return QuillEditorProvider.of(this).editorConfigurations; + } + + /// return nullable [QuillEditorConfigurations] + QuillEditorConfigurations? get quillEditorConfigurations { + return QuillEditorProvider.maybeOf(this)?.editorConfigurations; + } + + /// return nullable [QuillToolbarBaseButtonOptions]. Since the quill + /// quill editor block options is in the [QuillEditorProvider] then we need to + /// get the provider widget first and then we will return block options + /// throw exception if [QuillEditorProvider] is not in the widget tree + QuillEditorElementOptions? get quillEditorElementOptions { + return quillEditorConfigurations?.elementOptions; + } + + /// return [QuillToolbarBaseButtonOptions] as not null. Since the quill + /// quill editor block options is in the [QuillEditorProvider] then we need to + /// get the provider widget first and then we will return block options + /// don't throw exception if [QuillEditorProvider] is not in the widget tree + QuillEditorElementOptions get requireQuillEditorElementOptions { + return requireQuillEditorConfigurations.elementOptions; + } +} + +extension QuillSimpleToolbarExt on BuildContext { + /// return [QuillSimpleToolbarConfigurations] as not null + QuillSimpleToolbarConfigurations get requireQuillSimpleToolbarConfigurations { + return QuillSimpleToolbarProvider.of(this).toolbarConfigurations; + } + + /// return nullable [QuillSimpleToolbarConfigurations] + QuillSimpleToolbarConfigurations? get quillSimpleToolbarConfigurations { + return QuillSimpleToolbarProvider.maybeOf(this)?.toolbarConfigurations; + } + + /// return nullable [QuillToolbarBaseButtonOptions]. + QuillToolbarBaseButtonOptions? get quillToolbarBaseButtonOptions { + return quillSimpleToolbarConfigurations?.buttonOptions.base; + } + + /// return [QuillToolbarBaseButtonOptions] as not null. + QuillToolbarBaseButtonOptions get requireQuillToolbarBaseButtonOptions { + return quillSimpleToolbarConfigurations?.buttonOptions.base ?? + quillToolbarConfigurations?.buttonOptions.base ?? + (throw ArgumentError( + "The buttonOptions is required and it's null because the toolbar configurations is.", + )); + } +} + +extension QuillToolbarExt on BuildContext { + /// return [QuillToolbarConfigurations] as not null + QuillToolbarConfigurations get requireQuillToolbarConfigurations { + return QuillToolbarProvider.of(this).toolbarConfigurations; + } + + /// return nullable [QuillToolbarConfigurations]. + QuillToolbarConfigurations? get quillToolbarConfigurations { + return QuillToolbarProvider.maybeOf(this)?.toolbarConfigurations; + } +} diff --git a/lib/src/extensions/quill_controller.dart b/lib/src/extensions/quill_controller_ext.dart similarity index 69% rename from lib/src/extensions/quill_controller.dart rename to lib/src/extensions/quill_controller_ext.dart index ca7c36998..49c09f976 100644 --- a/lib/src/extensions/quill_controller.dart +++ b/lib/src/extensions/quill_controller_ext.dart @@ -1,11 +1,11 @@ import 'package:flutter/widgets.dart' show BuildContext; -import '../../flutter_quill.dart' show QuillController, QuillProvider; -import 'quill_provider.dart'; +import '../../flutter_quill.dart' show QuillController; +import 'quill_configurations_ext.dart'; extension QuillControllerNullableExt on QuillController? { /// Simple logic to use the current passed controller if not null - /// if null then we will have to use the default one from [QuillProvider] + /// if null then we will have to use the default one /// using the [context] QuillController notNull(BuildContext context) { final controller = this; diff --git a/lib/src/extensions/quill_provider.dart b/lib/src/extensions/quill_provider.dart deleted file mode 100644 index 364cdf0b8..000000000 --- a/lib/src/extensions/quill_provider.dart +++ /dev/null @@ -1,160 +0,0 @@ -import 'package:flutter/widgets.dart' show BuildContext; - -import '../../flutter_quill.dart'; - -// TODO: The comments of this file is outdated and needs to be updated - -/// Public shared extension -extension QuillProviderExt on BuildContext { - /// return [QuillProvider] as not null - /// throw exception if it's not in the widget tree - QuillProvider get requireQuillProvider { - return QuillProvider.ofNotNull(this); - } - - /// return nullable [QuillProvider] - /// don't throw exception if it's not in the widget tree - /// instead it will be null - QuillProvider? get quillProvider { - return QuillProvider.of(this); - } - - /// return nullable [QuillController] - /// since the quill controller is in the [QuillProvider] then we need to get - /// the provider widget first and then we will return the controller - /// don't throw exception if [QuillProvider] is not in the widget tree - /// instead it will be null - QuillController? get quilController { - return quillProvider?.configurations.controller; - } - - /// return [QuillController] as not null - /// since the quill controller is in the [QuillProvider] then we need to get - /// the provider widget first and then we will return the controller - /// throw exception if [QuillProvider] is not in the widget tree - QuillController get requireQuillController { - return requireQuillProvider.configurations.controller; - } - - /// return [QuillConfigurations] as not null - /// since the quill configurations is in the [QuillProvider] then we need to - /// get the provider widget first and then we will return quill configurations - /// throw exception if [QuillProvider] is not in the widget tree - QuillConfigurations get requireQuillConfigurations { - return requireQuillProvider.configurations; - } - - /// return nullable [QuillConfigurations] - /// since the quill configurations is in the [QuillProvider] then we need to - /// get the provider widget first and then we will return quill configurations - /// don't throw exception if [QuillProvider] is not in the widget tree - QuillConfigurations? get quillConfigurations { - return quillProvider?.configurations; - } - - /// return [QuillSharedConfigurations] as not null. Since the quill - /// shared configurations is in the [QuillProvider] then we need to get the - /// provider widget first and then we will return shared configurations - /// throw exception if [QuillProvider] is not in the widget tree - QuillSharedConfigurations get requireQuillSharedConfigurations { - return requireQuillConfigurations.sharedConfigurations; - } - - /// return nullable [QuillSharedConfigurations] . Since the quill - /// shared configurations is in the [QuillProvider] then we need to get the - /// provider widget first and then we will return shared configurations - /// don't throw exception if [QuillProvider] is not in the widget tree - QuillSharedConfigurations? get quillSharedConfigurations { - return quillConfigurations?.sharedConfigurations; - } - - /// return [QuillEditorConfigurations] as not null . Since the quill - /// editor configurations is in the [QuillEditorProvider] - /// then we need to get the - /// provider widget first and then we will return editor configurations - /// throw exception if [QuillProvider] is not in the widget tree - QuillEditorConfigurations get requireQuillEditorConfigurations { - return QuillEditorProvider.ofNotNull(this).editorConfigurations; - } - - /// return nullable [QuillEditorConfigurations]. Since the quill - /// editor configurations is in the [QuillEditorProvider] - /// then we need to get the - /// provider widget first and then we will return editor configurations - /// don't throw exception if [QuillProvider] is not in the widget tree - QuillEditorConfigurations? get quillEditorConfigurations { - return QuillEditorProvider.of(this)?.editorConfigurations; - } - - /// return [QuillToolbarConfigurations] as not null . Since the quill - /// toolbar configurations is in the [QuillToolbarProvider] - /// then we need to get the - /// provider widget first and then we will return toolbar configurations - /// throw exception if [QuillProvider] is not in the widget tree - QuillToolbarConfigurations get requireQuillToolbarConfigurations { - return QuillToolbarProvider.ofNotNull(this).toolbarConfigurations; - } - - /// return nullable [QuillToolbarConfigurations]. Since the quill - /// toolbar configurations is in the [QuillToolbarProvider] - /// then we need to get the - /// provider widget first and then we will return toolbar configurations - /// don't throw exception if [QuillProvider] is not in the widget tree - QuillToolbarConfigurations? get quillToolbarConfigurations { - return QuillToolbarProvider.of(this)?.toolbarConfigurations; - } - - /// return [QuillBaseToolbarConfigurations] as not null . Since the quill - /// toolbar configurations is in the [QuillBaseToolbarProvider] - /// then we need to get the - /// provider widget first and then we will return toolbar configurations - /// throw exception if [QuillBaseToolbarProvider] is not in the widget tree - QuillBaseToolbarConfigurations get requireQuillBaseToolbarConfigurations { - return QuillBaseToolbarProvider.ofNotNull(this).toolbarConfigurations; - } - - /// return nullable [QuillBaseToolbarConfigurations]. Since the quill - /// toolbar configurations is in the [QuillBaseToolbarProvider] - /// then we need to get the - /// provider widget first and then we will return toolbar configurations - /// don't throw exception if [QuillBaseToolbarProvider] is not in the widget tree - QuillBaseToolbarConfigurations? get quillBaseToolbarConfigurations { - return QuillBaseToolbarProvider.of(this)?.toolbarConfigurations; - } - - /// return nullable [QuillToolbarBaseButtonOptions]. Since the quill - /// toolbar base button options is in the [QuillProvider] then we need to - /// get the provider widget first and then we will return base button - /// don't throw exception if [QuillProvider] is not in the widget tree - QuillToolbarBaseButtonOptions? get quillToolbarBaseButtonOptions { - return quillToolbarConfigurations?.buttonOptions.base; - } - - /// return [QuillToolbarBaseButtonOptions] as not null. Since the quill - /// toolbar base button options is in the [QuillProvider] then we need to - /// get the provider widget first and then we will return base button - /// throw exception if [QuillProvider] is not in the widget tree - QuillToolbarBaseButtonOptions get requireQuillToolbarBaseButtonOptions { - return quillToolbarConfigurations?.buttonOptions.base ?? - quillBaseToolbarConfigurations?.buttonOptions.base ?? - (throw ArgumentError( - "The buttonOptions is required and it's null because the toolbar configurations is.", - )); - } - - /// return nullable [QuillToolbarBaseButtonOptions]. Since the quill - /// quill editor block options is in the [QuillEditorProvider] then we need to - /// get the provider widget first and then we will return block options - /// throw exception if [QuillEditorProvider] is not in the widget tree - QuillEditorElementOptions? get quillEditorElementOptions { - return quillEditorConfigurations?.elementOptions; - } - - /// return [QuillToolbarBaseButtonOptions] as not null. Since the quill - /// quill editor block options is in the [QuillEditorProvider] then we need to - /// get the provider widget first and then we will return block options - /// don't throw exception if [QuillEditorProvider] is not in the widget tree - QuillEditorElementOptions get requireQuillEditorElementOptions { - return requireQuillEditorConfigurations.elementOptions; - } -} diff --git a/lib/src/extensions/uri_ext.dart b/lib/src/extensions/uri_ext.dart new file mode 100644 index 000000000..b548a29c6 --- /dev/null +++ b/lib/src/extensions/uri_ext.dart @@ -0,0 +1,11 @@ +extension UriExt on Uri { + bool isHttpBasedUrl() { + final uri = this; + return uri.isScheme('HTTP') || uri.isScheme('HTTPS'); + } + + bool isHttpsBasedUrl() { + final uri = this; + return uri.isScheme('HTTPS'); + } +} diff --git a/lib/src/l10n/generated/quill_localizations.dart b/lib/src/l10n/generated/quill_localizations.dart index f4425f811..abd411abf 100644 --- a/lib/src/l10n/generated/quill_localizations.dart +++ b/lib/src/l10n/generated/quill_localizations.dart @@ -421,6 +421,30 @@ abstract class FlutterQuillLocalizations { /// **'Header style'** String get headerStyle; + /// No description provided for @normal. + /// + /// In en, this message translates to: + /// **'Normal'** + String get normal; + + /// No description provided for @heading1. + /// + /// In en, this message translates to: + /// **'Heading 1'** + String get heading1; + + /// No description provided for @heading2. + /// + /// In en, this message translates to: + /// **'Heading 2'** + String get heading2; + + /// No description provided for @heading3. + /// + /// In en, this message translates to: + /// **'Heading 3'** + String get heading3; + /// No description provided for @numberedList. /// /// In en, this message translates to: @@ -610,7 +634,7 @@ abstract class FlutterQuillLocalizations { /// No description provided for @takeAPhotoUsingYourCamera. /// /// In en, this message translates to: - /// **'Take a photo using your phone camera'** + /// **'Take a photo using your camera'** String get takeAPhotoUsingYourCamera; /// No description provided for @pasteAPhotoUsingALink. @@ -628,7 +652,7 @@ abstract class FlutterQuillLocalizations { /// No description provided for @recordAVideoUsingYourCamera. /// /// In en, this message translates to: - /// **'Record a video using your phone camera'** + /// **'Record a video using your camera'** String get recordAVideoUsingYourCamera; /// No description provided for @pasteAVideoUsingALink. diff --git a/lib/src/l10n/generated/quill_localizations_ar.dart b/lib/src/l10n/generated/quill_localizations_ar.dart index fc7b2dfc4..b7211f5c4 100644 --- a/lib/src/l10n/generated/quill_localizations_ar.dart +++ b/lib/src/l10n/generated/quill_localizations_ar.dart @@ -136,6 +136,18 @@ class FlutterQuillLocalizationsAr extends FlutterQuillLocalizations { @override String get headerStyle => 'ستايل العنوان'; + @override + String get normal => 'Normal'; + + @override + String get heading1 => 'Heading 1'; + + @override + String get heading2 => 'Heading 2'; + + @override + String get heading3 => 'Heading 3'; + @override String get numberedList => 'قائمة مرقمة'; @@ -231,8 +243,7 @@ class FlutterQuillLocalizationsAr extends FlutterQuillLocalizations { String get pickAPhotoFromYourGallery => 'Pick a photo from your gallery'; @override - String get takeAPhotoUsingYourCamera => - 'Take a photo using your phone camera'; + String get takeAPhotoUsingYourCamera => 'Take a photo using your camera'; @override String get pasteAPhotoUsingALink => 'Paste a photo using a link'; @@ -241,8 +252,7 @@ class FlutterQuillLocalizationsAr extends FlutterQuillLocalizations { String get pickAVideoFromYourGallery => 'Pick a video from your gallery'; @override - String get recordAVideoUsingYourCamera => - 'Record a video using your phone camera'; + String get recordAVideoUsingYourCamera => 'Record a video using your camera'; @override String get pasteAVideoUsingALink => 'Paste a video using a link'; diff --git a/lib/src/l10n/generated/quill_localizations_bg.dart b/lib/src/l10n/generated/quill_localizations_bg.dart index a75b6ffab..dd9a1bfec 100644 --- a/lib/src/l10n/generated/quill_localizations_bg.dart +++ b/lib/src/l10n/generated/quill_localizations_bg.dart @@ -136,6 +136,18 @@ class FlutterQuillLocalizationsBg extends FlutterQuillLocalizations { @override String get headerStyle => 'Стил на заглавието'; + @override + String get normal => 'Normal'; + + @override + String get heading1 => 'Heading 1'; + + @override + String get heading2 => 'Heading 2'; + + @override + String get heading3 => 'Heading 3'; + @override String get numberedList => 'Номериран списък'; @@ -233,8 +245,7 @@ class FlutterQuillLocalizationsBg extends FlutterQuillLocalizations { String get pickAPhotoFromYourGallery => 'Pick a photo from your gallery'; @override - String get takeAPhotoUsingYourCamera => - 'Take a photo using your phone camera'; + String get takeAPhotoUsingYourCamera => 'Take a photo using your camera'; @override String get pasteAPhotoUsingALink => 'Paste a photo using a link'; @@ -243,8 +254,7 @@ class FlutterQuillLocalizationsBg extends FlutterQuillLocalizations { String get pickAVideoFromYourGallery => 'Pick a video from your gallery'; @override - String get recordAVideoUsingYourCamera => - 'Record a video using your phone camera'; + String get recordAVideoUsingYourCamera => 'Record a video using your camera'; @override String get pasteAVideoUsingALink => 'Paste a video using a link'; diff --git a/lib/src/l10n/generated/quill_localizations_bn.dart b/lib/src/l10n/generated/quill_localizations_bn.dart index 7ef340ef6..fb41d3c2c 100644 --- a/lib/src/l10n/generated/quill_localizations_bn.dart +++ b/lib/src/l10n/generated/quill_localizations_bn.dart @@ -136,6 +136,18 @@ class FlutterQuillLocalizationsBn extends FlutterQuillLocalizations { @override String get headerStyle => 'হেডার স্টাইল'; + @override + String get normal => 'Normal'; + + @override + String get heading1 => 'Heading 1'; + + @override + String get heading2 => 'Heading 2'; + + @override + String get heading3 => 'Heading 3'; + @override String get numberedList => 'সংখ্যাযুক্ত তালিকা'; @@ -233,8 +245,7 @@ class FlutterQuillLocalizationsBn extends FlutterQuillLocalizations { String get pickAPhotoFromYourGallery => 'Pick a photo from your gallery'; @override - String get takeAPhotoUsingYourCamera => - 'Take a photo using your phone camera'; + String get takeAPhotoUsingYourCamera => 'Take a photo using your camera'; @override String get pasteAPhotoUsingALink => 'Paste a photo using a link'; @@ -243,8 +254,7 @@ class FlutterQuillLocalizationsBn extends FlutterQuillLocalizations { String get pickAVideoFromYourGallery => 'Pick a video from your gallery'; @override - String get recordAVideoUsingYourCamera => - 'Record a video using your phone camera'; + String get recordAVideoUsingYourCamera => 'Record a video using your camera'; @override String get pasteAVideoUsingALink => 'Paste a video using a link'; diff --git a/lib/src/l10n/generated/quill_localizations_cs.dart b/lib/src/l10n/generated/quill_localizations_cs.dart index a74f67564..375fe13f4 100644 --- a/lib/src/l10n/generated/quill_localizations_cs.dart +++ b/lib/src/l10n/generated/quill_localizations_cs.dart @@ -136,6 +136,18 @@ class FlutterQuillLocalizationsCs extends FlutterQuillLocalizations { @override String get headerStyle => 'Styl záhlaví'; + @override + String get normal => 'Normal'; + + @override + String get heading1 => 'Heading 1'; + + @override + String get heading2 => 'Heading 2'; + + @override + String get heading3 => 'Heading 3'; + @override String get numberedList => 'Číslovaný seznam'; @@ -233,8 +245,7 @@ class FlutterQuillLocalizationsCs extends FlutterQuillLocalizations { String get pickAPhotoFromYourGallery => 'Pick a photo from your gallery'; @override - String get takeAPhotoUsingYourCamera => - 'Take a photo using your phone camera'; + String get takeAPhotoUsingYourCamera => 'Take a photo using your camera'; @override String get pasteAPhotoUsingALink => 'Paste a photo using a link'; @@ -243,8 +254,7 @@ class FlutterQuillLocalizationsCs extends FlutterQuillLocalizations { String get pickAVideoFromYourGallery => 'Pick a video from your gallery'; @override - String get recordAVideoUsingYourCamera => - 'Record a video using your phone camera'; + String get recordAVideoUsingYourCamera => 'Record a video using your camera'; @override String get pasteAVideoUsingALink => 'Paste a video using a link'; diff --git a/lib/src/l10n/generated/quill_localizations_da.dart b/lib/src/l10n/generated/quill_localizations_da.dart index 6e8825fc5..fc7926e87 100644 --- a/lib/src/l10n/generated/quill_localizations_da.dart +++ b/lib/src/l10n/generated/quill_localizations_da.dart @@ -136,6 +136,18 @@ class FlutterQuillLocalizationsDa extends FlutterQuillLocalizations { @override String get headerStyle => 'Header style'; + @override + String get normal => 'Normal'; + + @override + String get heading1 => 'Heading 1'; + + @override + String get heading2 => 'Heading 2'; + + @override + String get heading3 => 'Heading 3'; + @override String get numberedList => 'Numbered list'; @@ -231,8 +243,7 @@ class FlutterQuillLocalizationsDa extends FlutterQuillLocalizations { String get pickAPhotoFromYourGallery => 'Pick a photo from your gallery'; @override - String get takeAPhotoUsingYourCamera => - 'Take a photo using your phone camera'; + String get takeAPhotoUsingYourCamera => 'Take a photo using your camera'; @override String get pasteAPhotoUsingALink => 'Paste a photo using a link'; @@ -241,8 +252,7 @@ class FlutterQuillLocalizationsDa extends FlutterQuillLocalizations { String get pickAVideoFromYourGallery => 'Pick a video from your gallery'; @override - String get recordAVideoUsingYourCamera => - 'Record a video using your phone camera'; + String get recordAVideoUsingYourCamera => 'Record a video using your camera'; @override String get pasteAVideoUsingALink => 'Paste a video using a link'; diff --git a/lib/src/l10n/generated/quill_localizations_de.dart b/lib/src/l10n/generated/quill_localizations_de.dart index 384b8ea33..79e847b96 100644 --- a/lib/src/l10n/generated/quill_localizations_de.dart +++ b/lib/src/l10n/generated/quill_localizations_de.dart @@ -136,6 +136,18 @@ class FlutterQuillLocalizationsDe extends FlutterQuillLocalizations { @override String get headerStyle => 'Überschrift-Stil'; + @override + String get normal => 'Normal'; + + @override + String get heading1 => 'Heading 1'; + + @override + String get heading2 => 'Heading 2'; + + @override + String get heading3 => 'Heading 3'; + @override String get numberedList => 'Nummerierte Liste'; @@ -232,8 +244,7 @@ class FlutterQuillLocalizationsDe extends FlutterQuillLocalizations { String get pickAPhotoFromYourGallery => 'Pick a photo from your gallery'; @override - String get takeAPhotoUsingYourCamera => - 'Take a photo using your phone camera'; + String get takeAPhotoUsingYourCamera => 'Take a photo using your camera'; @override String get pasteAPhotoUsingALink => 'Paste a photo using a link'; @@ -242,8 +253,7 @@ class FlutterQuillLocalizationsDe extends FlutterQuillLocalizations { String get pickAVideoFromYourGallery => 'Pick a video from your gallery'; @override - String get recordAVideoUsingYourCamera => - 'Record a video using your phone camera'; + String get recordAVideoUsingYourCamera => 'Record a video using your camera'; @override String get pasteAVideoUsingALink => 'Paste a video using a link'; diff --git a/lib/src/l10n/generated/quill_localizations_en.dart b/lib/src/l10n/generated/quill_localizations_en.dart index 1233f1cf3..192d8c6cf 100644 --- a/lib/src/l10n/generated/quill_localizations_en.dart +++ b/lib/src/l10n/generated/quill_localizations_en.dart @@ -136,6 +136,18 @@ class FlutterQuillLocalizationsEn extends FlutterQuillLocalizations { @override String get headerStyle => 'Header style'; + @override + String get normal => 'Normal'; + + @override + String get heading1 => 'Heading 1'; + + @override + String get heading2 => 'Heading 2'; + + @override + String get heading3 => 'Heading 3'; + @override String get numberedList => 'Numbered list'; @@ -233,8 +245,7 @@ class FlutterQuillLocalizationsEn extends FlutterQuillLocalizations { String get pickAPhotoFromYourGallery => 'Pick a photo from your gallery'; @override - String get takeAPhotoUsingYourCamera => - 'Take a photo using your phone camera'; + String get takeAPhotoUsingYourCamera => 'Take a photo using your camera'; @override String get pasteAPhotoUsingALink => 'Paste a photo using a link'; @@ -243,8 +254,7 @@ class FlutterQuillLocalizationsEn extends FlutterQuillLocalizations { String get pickAVideoFromYourGallery => 'Pick a video from your gallery'; @override - String get recordAVideoUsingYourCamera => - 'Record a video using your phone camera'; + String get recordAVideoUsingYourCamera => 'Record a video using your camera'; @override String get pasteAVideoUsingALink => 'Paste a video using a link'; diff --git a/lib/src/l10n/generated/quill_localizations_es.dart b/lib/src/l10n/generated/quill_localizations_es.dart index 3c63865d1..63c9a7669 100644 --- a/lib/src/l10n/generated/quill_localizations_es.dart +++ b/lib/src/l10n/generated/quill_localizations_es.dart @@ -136,6 +136,18 @@ class FlutterQuillLocalizationsEs extends FlutterQuillLocalizations { @override String get headerStyle => 'Header style'; + @override + String get normal => 'Normal'; + + @override + String get heading1 => 'Heading 1'; + + @override + String get heading2 => 'Heading 2'; + + @override + String get heading3 => 'Heading 3'; + @override String get numberedList => 'Numbered list'; @@ -232,8 +244,7 @@ class FlutterQuillLocalizationsEs extends FlutterQuillLocalizations { String get pickAPhotoFromYourGallery => 'Pick a photo from your gallery'; @override - String get takeAPhotoUsingYourCamera => - 'Take a photo using your phone camera'; + String get takeAPhotoUsingYourCamera => 'Take a photo using your camera'; @override String get pasteAPhotoUsingALink => 'Paste a photo using a link'; @@ -242,8 +253,7 @@ class FlutterQuillLocalizationsEs extends FlutterQuillLocalizations { String get pickAVideoFromYourGallery => 'Pick a video from your gallery'; @override - String get recordAVideoUsingYourCamera => - 'Record a video using your phone camera'; + String get recordAVideoUsingYourCamera => 'Record a video using your camera'; @override String get pasteAVideoUsingALink => 'Paste a video using a link'; diff --git a/lib/src/l10n/generated/quill_localizations_fa.dart b/lib/src/l10n/generated/quill_localizations_fa.dart index 32040119a..759674bfe 100644 --- a/lib/src/l10n/generated/quill_localizations_fa.dart +++ b/lib/src/l10n/generated/quill_localizations_fa.dart @@ -136,6 +136,18 @@ class FlutterQuillLocalizationsFa extends FlutterQuillLocalizations { @override String get headerStyle => 'سبک هدر'; + @override + String get normal => 'Normal'; + + @override + String get heading1 => 'Heading 1'; + + @override + String get heading2 => 'Heading 2'; + + @override + String get heading3 => 'Heading 3'; + @override String get numberedList => 'لیست شماره‌دار'; @@ -234,8 +246,7 @@ class FlutterQuillLocalizationsFa extends FlutterQuillLocalizations { String get pickAPhotoFromYourGallery => 'Pick a photo from your gallery'; @override - String get takeAPhotoUsingYourCamera => - 'Take a photo using your phone camera'; + String get takeAPhotoUsingYourCamera => 'Take a photo using your camera'; @override String get pasteAPhotoUsingALink => 'Paste a photo using a link'; @@ -244,8 +255,7 @@ class FlutterQuillLocalizationsFa extends FlutterQuillLocalizations { String get pickAVideoFromYourGallery => 'Pick a video from your gallery'; @override - String get recordAVideoUsingYourCamera => - 'Record a video using your phone camera'; + String get recordAVideoUsingYourCamera => 'Record a video using your camera'; @override String get pasteAVideoUsingALink => 'Paste a video using a link'; diff --git a/lib/src/l10n/generated/quill_localizations_fr.dart b/lib/src/l10n/generated/quill_localizations_fr.dart index b94451ff1..f0644c76a 100644 --- a/lib/src/l10n/generated/quill_localizations_fr.dart +++ b/lib/src/l10n/generated/quill_localizations_fr.dart @@ -136,6 +136,18 @@ class FlutterQuillLocalizationsFr extends FlutterQuillLocalizations { @override String get headerStyle => "Style d'en-tête"; + @override + String get normal => 'Normal'; + + @override + String get heading1 => 'Heading 1'; + + @override + String get heading2 => 'Heading 2'; + + @override + String get heading3 => 'Heading 3'; + @override String get numberedList => 'Liste numérotée'; @@ -235,8 +247,7 @@ class FlutterQuillLocalizationsFr extends FlutterQuillLocalizations { String get pickAPhotoFromYourGallery => 'Pick a photo from your gallery'; @override - String get takeAPhotoUsingYourCamera => - 'Take a photo using your phone camera'; + String get takeAPhotoUsingYourCamera => 'Take a photo using your camera'; @override String get pasteAPhotoUsingALink => 'Paste a photo using a link'; @@ -245,8 +256,7 @@ class FlutterQuillLocalizationsFr extends FlutterQuillLocalizations { String get pickAVideoFromYourGallery => 'Pick a video from your gallery'; @override - String get recordAVideoUsingYourCamera => - 'Record a video using your phone camera'; + String get recordAVideoUsingYourCamera => 'Record a video using your camera'; @override String get pasteAVideoUsingALink => 'Paste a video using a link'; diff --git a/lib/src/l10n/generated/quill_localizations_he.dart b/lib/src/l10n/generated/quill_localizations_he.dart index 956c10f99..5fea7ff51 100644 --- a/lib/src/l10n/generated/quill_localizations_he.dart +++ b/lib/src/l10n/generated/quill_localizations_he.dart @@ -136,6 +136,18 @@ class FlutterQuillLocalizationsHe extends FlutterQuillLocalizations { @override String get headerStyle => 'סגנון הכותרת'; + @override + String get normal => 'Normal'; + + @override + String get heading1 => 'Heading 1'; + + @override + String get heading2 => 'Heading 2'; + + @override + String get heading3 => 'Heading 3'; + @override String get numberedList => 'רשימה ממוספרת'; @@ -233,8 +245,7 @@ class FlutterQuillLocalizationsHe extends FlutterQuillLocalizations { String get pickAPhotoFromYourGallery => 'Pick a photo from your gallery'; @override - String get takeAPhotoUsingYourCamera => - 'Take a photo using your phone camera'; + String get takeAPhotoUsingYourCamera => 'Take a photo using your camera'; @override String get pasteAPhotoUsingALink => 'Paste a photo using a link'; @@ -243,8 +254,7 @@ class FlutterQuillLocalizationsHe extends FlutterQuillLocalizations { String get pickAVideoFromYourGallery => 'Pick a video from your gallery'; @override - String get recordAVideoUsingYourCamera => - 'Record a video using your phone camera'; + String get recordAVideoUsingYourCamera => 'Record a video using your camera'; @override String get pasteAVideoUsingALink => 'Paste a video using a link'; diff --git a/lib/src/l10n/generated/quill_localizations_hi.dart b/lib/src/l10n/generated/quill_localizations_hi.dart index 3e2e738aa..103ded145 100644 --- a/lib/src/l10n/generated/quill_localizations_hi.dart +++ b/lib/src/l10n/generated/quill_localizations_hi.dart @@ -136,6 +136,18 @@ class FlutterQuillLocalizationsHi extends FlutterQuillLocalizations { @override String get headerStyle => 'हेडर शैली'; + @override + String get normal => 'Normal'; + + @override + String get heading1 => 'Heading 1'; + + @override + String get heading2 => 'Heading 2'; + + @override + String get heading3 => 'Heading 3'; + @override String get numberedList => 'संख्याबद्ध सूची'; @@ -234,8 +246,7 @@ class FlutterQuillLocalizationsHi extends FlutterQuillLocalizations { String get pickAPhotoFromYourGallery => 'Pick a photo from your gallery'; @override - String get takeAPhotoUsingYourCamera => - 'Take a photo using your phone camera'; + String get takeAPhotoUsingYourCamera => 'Take a photo using your camera'; @override String get pasteAPhotoUsingALink => 'Paste a photo using a link'; @@ -244,8 +255,7 @@ class FlutterQuillLocalizationsHi extends FlutterQuillLocalizations { String get pickAVideoFromYourGallery => 'Pick a video from your gallery'; @override - String get recordAVideoUsingYourCamera => - 'Record a video using your phone camera'; + String get recordAVideoUsingYourCamera => 'Record a video using your camera'; @override String get pasteAVideoUsingALink => 'Paste a video using a link'; diff --git a/lib/src/l10n/generated/quill_localizations_id.dart b/lib/src/l10n/generated/quill_localizations_id.dart index 4d4d38c53..fbd1a0e36 100644 --- a/lib/src/l10n/generated/quill_localizations_id.dart +++ b/lib/src/l10n/generated/quill_localizations_id.dart @@ -136,6 +136,18 @@ class FlutterQuillLocalizationsId extends FlutterQuillLocalizations { @override String get headerStyle => 'Gaya Header'; + @override + String get normal => 'Normal'; + + @override + String get heading1 => 'Heading 1'; + + @override + String get heading2 => 'Heading 2'; + + @override + String get heading3 => 'Heading 3'; + @override String get numberedList => 'Daftar Bernomor'; @@ -235,8 +247,7 @@ class FlutterQuillLocalizationsId extends FlutterQuillLocalizations { String get pickAPhotoFromYourGallery => 'Pick a photo from your gallery'; @override - String get takeAPhotoUsingYourCamera => - 'Take a photo using your phone camera'; + String get takeAPhotoUsingYourCamera => 'Take a photo using your camera'; @override String get pasteAPhotoUsingALink => 'Paste a photo using a link'; @@ -245,8 +256,7 @@ class FlutterQuillLocalizationsId extends FlutterQuillLocalizations { String get pickAVideoFromYourGallery => 'Pick a video from your gallery'; @override - String get recordAVideoUsingYourCamera => - 'Record a video using your phone camera'; + String get recordAVideoUsingYourCamera => 'Record a video using your camera'; @override String get pasteAVideoUsingALink => 'Paste a video using a link'; diff --git a/lib/src/l10n/generated/quill_localizations_it.dart b/lib/src/l10n/generated/quill_localizations_it.dart index 2b8fe0703..bf90ba95d 100644 --- a/lib/src/l10n/generated/quill_localizations_it.dart +++ b/lib/src/l10n/generated/quill_localizations_it.dart @@ -136,6 +136,18 @@ class FlutterQuillLocalizationsIt extends FlutterQuillLocalizations { @override String get headerStyle => 'Stile intestazione'; + @override + String get normal => 'Normal'; + + @override + String get heading1 => 'Heading 1'; + + @override + String get heading2 => 'Heading 2'; + + @override + String get heading3 => 'Heading 3'; + @override String get numberedList => 'Elenco numerato'; @@ -235,8 +247,7 @@ class FlutterQuillLocalizationsIt extends FlutterQuillLocalizations { String get pickAPhotoFromYourGallery => 'Pick a photo from your gallery'; @override - String get takeAPhotoUsingYourCamera => - 'Take a photo using your phone camera'; + String get takeAPhotoUsingYourCamera => 'Take a photo using your camera'; @override String get pasteAPhotoUsingALink => 'Paste a photo using a link'; @@ -245,8 +256,7 @@ class FlutterQuillLocalizationsIt extends FlutterQuillLocalizations { String get pickAVideoFromYourGallery => 'Pick a video from your gallery'; @override - String get recordAVideoUsingYourCamera => - 'Record a video using your phone camera'; + String get recordAVideoUsingYourCamera => 'Record a video using your camera'; @override String get pasteAVideoUsingALink => 'Paste a video using a link'; diff --git a/lib/src/l10n/generated/quill_localizations_ja.dart b/lib/src/l10n/generated/quill_localizations_ja.dart index 7a55d6b53..65569dd0e 100644 --- a/lib/src/l10n/generated/quill_localizations_ja.dart +++ b/lib/src/l10n/generated/quill_localizations_ja.dart @@ -136,6 +136,18 @@ class FlutterQuillLocalizationsJa extends FlutterQuillLocalizations { @override String get headerStyle => 'タイトルスタイル'; + @override + String get normal => 'Normal'; + + @override + String get heading1 => 'Heading 1'; + + @override + String get heading2 => 'Heading 2'; + + @override + String get heading3 => 'Heading 3'; + @override String get numberedList => '順序付きリスト'; @@ -230,8 +242,7 @@ class FlutterQuillLocalizationsJa extends FlutterQuillLocalizations { String get pickAPhotoFromYourGallery => 'Pick a photo from your gallery'; @override - String get takeAPhotoUsingYourCamera => - 'Take a photo using your phone camera'; + String get takeAPhotoUsingYourCamera => 'Take a photo using your camera'; @override String get pasteAPhotoUsingALink => 'Paste a photo using a link'; @@ -240,8 +251,7 @@ class FlutterQuillLocalizationsJa extends FlutterQuillLocalizations { String get pickAVideoFromYourGallery => 'Pick a video from your gallery'; @override - String get recordAVideoUsingYourCamera => - 'Record a video using your phone camera'; + String get recordAVideoUsingYourCamera => 'Record a video using your camera'; @override String get pasteAVideoUsingALink => 'Paste a video using a link'; diff --git a/lib/src/l10n/generated/quill_localizations_ko.dart b/lib/src/l10n/generated/quill_localizations_ko.dart index c8a6b5669..8cf56ecbb 100644 --- a/lib/src/l10n/generated/quill_localizations_ko.dart +++ b/lib/src/l10n/generated/quill_localizations_ko.dart @@ -136,6 +136,18 @@ class FlutterQuillLocalizationsKo extends FlutterQuillLocalizations { @override String get headerStyle => 'Header style'; + @override + String get normal => 'Normal'; + + @override + String get heading1 => 'Heading 1'; + + @override + String get heading2 => 'Heading 2'; + + @override + String get heading3 => 'Heading 3'; + @override String get numberedList => 'Numbered list'; @@ -230,8 +242,7 @@ class FlutterQuillLocalizationsKo extends FlutterQuillLocalizations { String get pickAPhotoFromYourGallery => 'Pick a photo from your gallery'; @override - String get takeAPhotoUsingYourCamera => - 'Take a photo using your phone camera'; + String get takeAPhotoUsingYourCamera => 'Take a photo using your camera'; @override String get pasteAPhotoUsingALink => 'Paste a photo using a link'; @@ -240,8 +251,7 @@ class FlutterQuillLocalizationsKo extends FlutterQuillLocalizations { String get pickAVideoFromYourGallery => 'Pick a video from your gallery'; @override - String get recordAVideoUsingYourCamera => - 'Record a video using your phone camera'; + String get recordAVideoUsingYourCamera => 'Record a video using your camera'; @override String get pasteAVideoUsingALink => 'Paste a video using a link'; diff --git a/lib/src/l10n/generated/quill_localizations_ms.dart b/lib/src/l10n/generated/quill_localizations_ms.dart index c443712f5..e561d2a42 100644 --- a/lib/src/l10n/generated/quill_localizations_ms.dart +++ b/lib/src/l10n/generated/quill_localizations_ms.dart @@ -136,6 +136,18 @@ class FlutterQuillLocalizationsMs extends FlutterQuillLocalizations { @override String get headerStyle => 'Header style'; + @override + String get normal => 'Normal'; + + @override + String get heading1 => 'Heading 1'; + + @override + String get heading2 => 'Heading 2'; + + @override + String get heading3 => 'Heading 3'; + @override String get numberedList => 'Numbered list'; @@ -233,8 +245,7 @@ class FlutterQuillLocalizationsMs extends FlutterQuillLocalizations { String get pickAPhotoFromYourGallery => 'Pick a photo from your gallery'; @override - String get takeAPhotoUsingYourCamera => - 'Take a photo using your phone camera'; + String get takeAPhotoUsingYourCamera => 'Take a photo using your camera'; @override String get pasteAPhotoUsingALink => 'Paste a photo using a link'; @@ -243,8 +254,7 @@ class FlutterQuillLocalizationsMs extends FlutterQuillLocalizations { String get pickAVideoFromYourGallery => 'Pick a video from your gallery'; @override - String get recordAVideoUsingYourCamera => - 'Record a video using your phone camera'; + String get recordAVideoUsingYourCamera => 'Record a video using your camera'; @override String get pasteAVideoUsingALink => 'Paste a video using a link'; diff --git a/lib/src/l10n/generated/quill_localizations_nl.dart b/lib/src/l10n/generated/quill_localizations_nl.dart index 65bc5b47c..3ddd6a551 100644 --- a/lib/src/l10n/generated/quill_localizations_nl.dart +++ b/lib/src/l10n/generated/quill_localizations_nl.dart @@ -136,6 +136,18 @@ class FlutterQuillLocalizationsNl extends FlutterQuillLocalizations { @override String get headerStyle => 'Header style'; + @override + String get normal => 'Normal'; + + @override + String get heading1 => 'Heading 1'; + + @override + String get heading2 => 'Heading 2'; + + @override + String get heading3 => 'Heading 3'; + @override String get numberedList => 'Numbered list'; @@ -235,8 +247,7 @@ class FlutterQuillLocalizationsNl extends FlutterQuillLocalizations { String get pickAPhotoFromYourGallery => 'Pick a photo from your gallery'; @override - String get takeAPhotoUsingYourCamera => - 'Take a photo using your phone camera'; + String get takeAPhotoUsingYourCamera => 'Take a photo using your camera'; @override String get pasteAPhotoUsingALink => 'Paste a photo using a link'; @@ -245,8 +256,7 @@ class FlutterQuillLocalizationsNl extends FlutterQuillLocalizations { String get pickAVideoFromYourGallery => 'Pick a video from your gallery'; @override - String get recordAVideoUsingYourCamera => - 'Record a video using your phone camera'; + String get recordAVideoUsingYourCamera => 'Record a video using your camera'; @override String get pasteAVideoUsingALink => 'Paste a video using a link'; diff --git a/lib/src/l10n/generated/quill_localizations_no.dart b/lib/src/l10n/generated/quill_localizations_no.dart index 087c69971..665761059 100644 --- a/lib/src/l10n/generated/quill_localizations_no.dart +++ b/lib/src/l10n/generated/quill_localizations_no.dart @@ -136,6 +136,18 @@ class FlutterQuillLocalizationsNo extends FlutterQuillLocalizations { @override String get headerStyle => 'Overskriftsstil'; + @override + String get normal => 'Normal'; + + @override + String get heading1 => 'Heading 1'; + + @override + String get heading2 => 'Heading 2'; + + @override + String get heading3 => 'Heading 3'; + @override String get numberedList => 'Nummerert liste'; @@ -235,8 +247,7 @@ class FlutterQuillLocalizationsNo extends FlutterQuillLocalizations { String get pickAPhotoFromYourGallery => 'Pick a photo from your gallery'; @override - String get takeAPhotoUsingYourCamera => - 'Take a photo using your phone camera'; + String get takeAPhotoUsingYourCamera => 'Take a photo using your camera'; @override String get pasteAPhotoUsingALink => 'Paste a photo using a link'; @@ -245,8 +256,7 @@ class FlutterQuillLocalizationsNo extends FlutterQuillLocalizations { String get pickAVideoFromYourGallery => 'Pick a video from your gallery'; @override - String get recordAVideoUsingYourCamera => - 'Record a video using your phone camera'; + String get recordAVideoUsingYourCamera => 'Record a video using your camera'; @override String get pasteAVideoUsingALink => 'Paste a video using a link'; diff --git a/lib/src/l10n/generated/quill_localizations_pl.dart b/lib/src/l10n/generated/quill_localizations_pl.dart index 677ff5c82..348521404 100644 --- a/lib/src/l10n/generated/quill_localizations_pl.dart +++ b/lib/src/l10n/generated/quill_localizations_pl.dart @@ -136,6 +136,18 @@ class FlutterQuillLocalizationsPl extends FlutterQuillLocalizations { @override String get headerStyle => 'Header style'; + @override + String get normal => 'Normal'; + + @override + String get heading1 => 'Heading 1'; + + @override + String get heading2 => 'Heading 2'; + + @override + String get heading3 => 'Heading 3'; + @override String get numberedList => 'Numbered list'; @@ -232,8 +244,7 @@ class FlutterQuillLocalizationsPl extends FlutterQuillLocalizations { String get pickAPhotoFromYourGallery => 'Pick a photo from your gallery'; @override - String get takeAPhotoUsingYourCamera => - 'Take a photo using your phone camera'; + String get takeAPhotoUsingYourCamera => 'Take a photo using your camera'; @override String get pasteAPhotoUsingALink => 'Paste a photo using a link'; @@ -242,8 +253,7 @@ class FlutterQuillLocalizationsPl extends FlutterQuillLocalizations { String get pickAVideoFromYourGallery => 'Pick a video from your gallery'; @override - String get recordAVideoUsingYourCamera => - 'Record a video using your phone camera'; + String get recordAVideoUsingYourCamera => 'Record a video using your camera'; @override String get pasteAVideoUsingALink => 'Paste a video using a link'; diff --git a/lib/src/l10n/generated/quill_localizations_pt.dart b/lib/src/l10n/generated/quill_localizations_pt.dart index bec9b366c..048cddec6 100644 --- a/lib/src/l10n/generated/quill_localizations_pt.dart +++ b/lib/src/l10n/generated/quill_localizations_pt.dart @@ -136,6 +136,18 @@ class FlutterQuillLocalizationsPt extends FlutterQuillLocalizations { @override String get headerStyle => 'Header style'; + @override + String get normal => 'Normal'; + + @override + String get heading1 => 'Heading 1'; + + @override + String get heading2 => 'Heading 2'; + + @override + String get heading3 => 'Heading 3'; + @override String get numberedList => 'Numbered list'; @@ -233,8 +245,7 @@ class FlutterQuillLocalizationsPt extends FlutterQuillLocalizations { String get pickAPhotoFromYourGallery => 'Pick a photo from your gallery'; @override - String get takeAPhotoUsingYourCamera => - 'Take a photo using your phone camera'; + String get takeAPhotoUsingYourCamera => 'Take a photo using your camera'; @override String get pasteAPhotoUsingALink => 'Paste a photo using a link'; @@ -243,8 +254,7 @@ class FlutterQuillLocalizationsPt extends FlutterQuillLocalizations { String get pickAVideoFromYourGallery => 'Pick a video from your gallery'; @override - String get recordAVideoUsingYourCamera => - 'Record a video using your phone camera'; + String get recordAVideoUsingYourCamera => 'Record a video using your camera'; @override String get pasteAVideoUsingALink => 'Paste a video using a link'; diff --git a/lib/src/l10n/generated/quill_localizations_ru.dart b/lib/src/l10n/generated/quill_localizations_ru.dart index 96a9314b8..c2afd16fa 100644 --- a/lib/src/l10n/generated/quill_localizations_ru.dart +++ b/lib/src/l10n/generated/quill_localizations_ru.dart @@ -136,6 +136,18 @@ class FlutterQuillLocalizationsRu extends FlutterQuillLocalizations { @override String get headerStyle => 'Header style'; + @override + String get normal => 'Normal'; + + @override + String get heading1 => 'Heading 1'; + + @override + String get heading2 => 'Heading 2'; + + @override + String get heading3 => 'Heading 3'; + @override String get numberedList => 'Numbered list'; @@ -232,8 +244,7 @@ class FlutterQuillLocalizationsRu extends FlutterQuillLocalizations { String get pickAPhotoFromYourGallery => 'Pick a photo from your gallery'; @override - String get takeAPhotoUsingYourCamera => - 'Take a photo using your phone camera'; + String get takeAPhotoUsingYourCamera => 'Take a photo using your camera'; @override String get pasteAPhotoUsingALink => 'Paste a photo using a link'; @@ -242,8 +253,7 @@ class FlutterQuillLocalizationsRu extends FlutterQuillLocalizations { String get pickAVideoFromYourGallery => 'Pick a video from your gallery'; @override - String get recordAVideoUsingYourCamera => - 'Record a video using your phone camera'; + String get recordAVideoUsingYourCamera => 'Record a video using your camera'; @override String get pasteAVideoUsingALink => 'Paste a video using a link'; diff --git a/lib/src/l10n/generated/quill_localizations_sr.dart b/lib/src/l10n/generated/quill_localizations_sr.dart index 1e89073e7..4b2e8c4e7 100644 --- a/lib/src/l10n/generated/quill_localizations_sr.dart +++ b/lib/src/l10n/generated/quill_localizations_sr.dart @@ -136,6 +136,18 @@ class FlutterQuillLocalizationsSr extends FlutterQuillLocalizations { @override String get headerStyle => 'Stil zaglavlja'; + @override + String get normal => 'Normal'; + + @override + String get heading1 => 'Heading 1'; + + @override + String get heading2 => 'Heading 2'; + + @override + String get heading3 => 'Heading 3'; + @override String get numberedList => 'Numerisana lista'; @@ -234,8 +246,7 @@ class FlutterQuillLocalizationsSr extends FlutterQuillLocalizations { String get pickAPhotoFromYourGallery => 'Pick a photo from your gallery'; @override - String get takeAPhotoUsingYourCamera => - 'Take a photo using your phone camera'; + String get takeAPhotoUsingYourCamera => 'Take a photo using your camera'; @override String get pasteAPhotoUsingALink => 'Paste a photo using a link'; @@ -244,8 +255,7 @@ class FlutterQuillLocalizationsSr extends FlutterQuillLocalizations { String get pickAVideoFromYourGallery => 'Pick a video from your gallery'; @override - String get recordAVideoUsingYourCamera => - 'Record a video using your phone camera'; + String get recordAVideoUsingYourCamera => 'Record a video using your camera'; @override String get pasteAVideoUsingALink => 'Paste a video using a link'; diff --git a/lib/src/l10n/generated/quill_localizations_sw.dart b/lib/src/l10n/generated/quill_localizations_sw.dart index 640403607..53d038871 100644 --- a/lib/src/l10n/generated/quill_localizations_sw.dart +++ b/lib/src/l10n/generated/quill_localizations_sw.dart @@ -136,6 +136,18 @@ class FlutterQuillLocalizationsSw extends FlutterQuillLocalizations { @override String get headerStyle => 'Mtindo wa Mada'; + @override + String get normal => 'Normal'; + + @override + String get heading1 => 'Heading 1'; + + @override + String get heading2 => 'Heading 2'; + + @override + String get heading3 => 'Heading 3'; + @override String get numberedList => 'Orodha ya Nambari'; @@ -232,8 +244,7 @@ class FlutterQuillLocalizationsSw extends FlutterQuillLocalizations { String get pickAPhotoFromYourGallery => 'Pick a photo from your gallery'; @override - String get takeAPhotoUsingYourCamera => - 'Take a photo using your phone camera'; + String get takeAPhotoUsingYourCamera => 'Take a photo using your camera'; @override String get pasteAPhotoUsingALink => 'Paste a photo using a link'; @@ -242,8 +253,7 @@ class FlutterQuillLocalizationsSw extends FlutterQuillLocalizations { String get pickAVideoFromYourGallery => 'Pick a video from your gallery'; @override - String get recordAVideoUsingYourCamera => - 'Record a video using your phone camera'; + String get recordAVideoUsingYourCamera => 'Record a video using your camera'; @override String get pasteAVideoUsingALink => 'Paste a video using a link'; diff --git a/lib/src/l10n/generated/quill_localizations_tk.dart b/lib/src/l10n/generated/quill_localizations_tk.dart index 13e7b1d3c..4018d3335 100644 --- a/lib/src/l10n/generated/quill_localizations_tk.dart +++ b/lib/src/l10n/generated/quill_localizations_tk.dart @@ -136,6 +136,18 @@ class FlutterQuillLocalizationsTk extends FlutterQuillLocalizations { @override String get headerStyle => 'Sözbaşy stili'; + @override + String get normal => 'Normal'; + + @override + String get heading1 => 'Heading 1'; + + @override + String get heading2 => 'Heading 2'; + + @override + String get heading3 => 'Heading 3'; + @override String get numberedList => 'Sanly sanaw'; @@ -231,8 +243,7 @@ class FlutterQuillLocalizationsTk extends FlutterQuillLocalizations { String get pickAPhotoFromYourGallery => 'Pick a photo from your gallery'; @override - String get takeAPhotoUsingYourCamera => - 'Take a photo using your phone camera'; + String get takeAPhotoUsingYourCamera => 'Take a photo using your camera'; @override String get pasteAPhotoUsingALink => 'Paste a photo using a link'; @@ -241,8 +252,7 @@ class FlutterQuillLocalizationsTk extends FlutterQuillLocalizations { String get pickAVideoFromYourGallery => 'Pick a video from your gallery'; @override - String get recordAVideoUsingYourCamera => - 'Record a video using your phone camera'; + String get recordAVideoUsingYourCamera => 'Record a video using your camera'; @override String get pasteAVideoUsingALink => 'Paste a video using a link'; diff --git a/lib/src/l10n/generated/quill_localizations_tr.dart b/lib/src/l10n/generated/quill_localizations_tr.dart index e6c52e9a5..fa6da123d 100644 --- a/lib/src/l10n/generated/quill_localizations_tr.dart +++ b/lib/src/l10n/generated/quill_localizations_tr.dart @@ -136,6 +136,18 @@ class FlutterQuillLocalizationsTr extends FlutterQuillLocalizations { @override String get headerStyle => 'Başlık Stili'; + @override + String get normal => 'Normal'; + + @override + String get heading1 => 'Heading 1'; + + @override + String get heading2 => 'Heading 2'; + + @override + String get heading3 => 'Heading 3'; + @override String get numberedList => 'Numaralı Liste'; @@ -232,8 +244,7 @@ class FlutterQuillLocalizationsTr extends FlutterQuillLocalizations { String get pickAPhotoFromYourGallery => 'Pick a photo from your gallery'; @override - String get takeAPhotoUsingYourCamera => - 'Take a photo using your phone camera'; + String get takeAPhotoUsingYourCamera => 'Take a photo using your camera'; @override String get pasteAPhotoUsingALink => 'Paste a photo using a link'; @@ -242,8 +253,7 @@ class FlutterQuillLocalizationsTr extends FlutterQuillLocalizations { String get pickAVideoFromYourGallery => 'Pick a video from your gallery'; @override - String get recordAVideoUsingYourCamera => - 'Record a video using your phone camera'; + String get recordAVideoUsingYourCamera => 'Record a video using your camera'; @override String get pasteAVideoUsingALink => 'Paste a video using a link'; diff --git a/lib/src/l10n/generated/quill_localizations_uk.dart b/lib/src/l10n/generated/quill_localizations_uk.dart index f7a273a5a..0c79a5700 100644 --- a/lib/src/l10n/generated/quill_localizations_uk.dart +++ b/lib/src/l10n/generated/quill_localizations_uk.dart @@ -136,6 +136,18 @@ class FlutterQuillLocalizationsUk extends FlutterQuillLocalizations { @override String get headerStyle => 'Стиль заголовка'; + @override + String get normal => 'Normal'; + + @override + String get heading1 => 'Heading 1'; + + @override + String get heading2 => 'Heading 2'; + + @override + String get heading3 => 'Heading 3'; + @override String get numberedList => 'Нумерований список'; @@ -234,8 +246,7 @@ class FlutterQuillLocalizationsUk extends FlutterQuillLocalizations { String get pickAPhotoFromYourGallery => 'Pick a photo from your gallery'; @override - String get takeAPhotoUsingYourCamera => - 'Take a photo using your phone camera'; + String get takeAPhotoUsingYourCamera => 'Take a photo using your camera'; @override String get pasteAPhotoUsingALink => 'Paste a photo using a link'; @@ -244,8 +255,7 @@ class FlutterQuillLocalizationsUk extends FlutterQuillLocalizations { String get pickAVideoFromYourGallery => 'Pick a video from your gallery'; @override - String get recordAVideoUsingYourCamera => - 'Record a video using your phone camera'; + String get recordAVideoUsingYourCamera => 'Record a video using your camera'; @override String get pasteAVideoUsingALink => 'Paste a video using a link'; diff --git a/lib/src/l10n/generated/quill_localizations_ur.dart b/lib/src/l10n/generated/quill_localizations_ur.dart index 79c665f56..c984f3187 100644 --- a/lib/src/l10n/generated/quill_localizations_ur.dart +++ b/lib/src/l10n/generated/quill_localizations_ur.dart @@ -136,6 +136,18 @@ class FlutterQuillLocalizationsUr extends FlutterQuillLocalizations { @override String get headerStyle => 'ہیڈر کا انداز'; + @override + String get normal => 'Normal'; + + @override + String get heading1 => 'Heading 1'; + + @override + String get heading2 => 'Heading 2'; + + @override + String get heading3 => 'Heading 3'; + @override String get numberedList => 'مرقم فہرست'; @@ -236,8 +248,7 @@ class FlutterQuillLocalizationsUr extends FlutterQuillLocalizations { String get pickAPhotoFromYourGallery => 'Pick a photo from your gallery'; @override - String get takeAPhotoUsingYourCamera => - 'Take a photo using your phone camera'; + String get takeAPhotoUsingYourCamera => 'Take a photo using your camera'; @override String get pasteAPhotoUsingALink => 'Paste a photo using a link'; @@ -246,8 +257,7 @@ class FlutterQuillLocalizationsUr extends FlutterQuillLocalizations { String get pickAVideoFromYourGallery => 'Pick a video from your gallery'; @override - String get recordAVideoUsingYourCamera => - 'Record a video using your phone camera'; + String get recordAVideoUsingYourCamera => 'Record a video using your camera'; @override String get pasteAVideoUsingALink => 'Paste a video using a link'; diff --git a/lib/src/l10n/generated/quill_localizations_vi.dart b/lib/src/l10n/generated/quill_localizations_vi.dart index 6f26695b0..817a9701a 100644 --- a/lib/src/l10n/generated/quill_localizations_vi.dart +++ b/lib/src/l10n/generated/quill_localizations_vi.dart @@ -136,6 +136,18 @@ class FlutterQuillLocalizationsVi extends FlutterQuillLocalizations { @override String get headerStyle => 'Kiểu tiêu đề'; + @override + String get normal => 'Normal'; + + @override + String get heading1 => 'Heading 1'; + + @override + String get heading2 => 'Heading 2'; + + @override + String get heading3 => 'Heading 3'; + @override String get numberedList => 'Danh sách có số'; @@ -233,8 +245,7 @@ class FlutterQuillLocalizationsVi extends FlutterQuillLocalizations { String get pickAPhotoFromYourGallery => 'Pick a photo from your gallery'; @override - String get takeAPhotoUsingYourCamera => - 'Take a photo using your phone camera'; + String get takeAPhotoUsingYourCamera => 'Take a photo using your camera'; @override String get pasteAPhotoUsingALink => 'Paste a photo using a link'; @@ -243,8 +254,7 @@ class FlutterQuillLocalizationsVi extends FlutterQuillLocalizations { String get pickAVideoFromYourGallery => 'Pick a video from your gallery'; @override - String get recordAVideoUsingYourCamera => - 'Record a video using your phone camera'; + String get recordAVideoUsingYourCamera => 'Record a video using your camera'; @override String get pasteAVideoUsingALink => 'Paste a video using a link'; diff --git a/lib/src/l10n/generated/quill_localizations_zh.dart b/lib/src/l10n/generated/quill_localizations_zh.dart index 59ba0ce1e..ca4c41d3b 100644 --- a/lib/src/l10n/generated/quill_localizations_zh.dart +++ b/lib/src/l10n/generated/quill_localizations_zh.dart @@ -136,6 +136,18 @@ class FlutterQuillLocalizationsZh extends FlutterQuillLocalizations { @override String get headerStyle => '标题样式'; + @override + String get normal => 'Normal'; + + @override + String get heading1 => 'Heading 1'; + + @override + String get heading2 => 'Heading 2'; + + @override + String get heading3 => 'Heading 3'; + @override String get numberedList => '编号列表'; @@ -230,8 +242,7 @@ class FlutterQuillLocalizationsZh extends FlutterQuillLocalizations { String get pickAPhotoFromYourGallery => 'Pick a photo from your gallery'; @override - String get takeAPhotoUsingYourCamera => - 'Take a photo using your phone camera'; + String get takeAPhotoUsingYourCamera => 'Take a photo using your camera'; @override String get pasteAPhotoUsingALink => 'Paste a photo using a link'; @@ -240,8 +251,7 @@ class FlutterQuillLocalizationsZh extends FlutterQuillLocalizations { String get pickAVideoFromYourGallery => 'Pick a video from your gallery'; @override - String get recordAVideoUsingYourCamera => - 'Record a video using your phone camera'; + String get recordAVideoUsingYourCamera => 'Record a video using your camera'; @override String get pasteAVideoUsingALink => 'Paste a video using a link'; diff --git a/lib/src/l10n/quill_en.arb b/lib/src/l10n/quill_en.arb index 0d4d737f0..58c6884c2 100644 --- a/lib/src/l10n/quill_en.arb +++ b/lib/src/l10n/quill_en.arb @@ -44,6 +44,10 @@ "justifyWinWidth": "Justify win width", "textDirection": "Text direction", "headerStyle": "Header style", + "normal": "Normal", + "heading1": "Heading 1", + "heading2": "Heading 2", + "heading3": "Heading 3", "numberedList": "Numbered list", "bulletList": "Bullet list", "checkedList": "Checked list", @@ -75,9 +79,9 @@ "caseSensitivityAndWholeWordSearch": "Case sensitivity and whole word search", "insertImage": "Insert image", "pickAPhotoFromYourGallery": "Pick a photo from your gallery", - "takeAPhotoUsingYourCamera": "Take a photo using your phone camera", + "takeAPhotoUsingYourCamera": "Take a photo using your camera", "pasteAPhotoUsingALink": "Paste a photo using a link", "pickAVideoFromYourGallery": "Pick a video from your gallery", - "recordAVideoUsingYourCamera": "Record a video using your phone camera", + "recordAVideoUsingYourCamera": "Record a video using your camera", "pasteAVideoUsingALink": "Paste a video using a link" } diff --git a/lib/src/l10n/untranslated.json b/lib/src/l10n/untranslated.json index d6c2ff3d2..d6a5a21ac 100644 --- a/lib/src/l10n/untranslated.json +++ b/lib/src/l10n/untranslated.json @@ -1,5 +1,9 @@ { "ar": [ + "normal", + "heading1", + "heading2", + "heading3", "pickAPhotoFromYourGallery", "takeAPhotoUsingYourCamera", "pasteAPhotoUsingALink", @@ -9,6 +13,10 @@ ], "bg": [ + "normal", + "heading1", + "heading2", + "heading3", "pickAPhotoFromYourGallery", "takeAPhotoUsingYourCamera", "pasteAPhotoUsingALink", @@ -18,6 +26,10 @@ ], "bn": [ + "normal", + "heading1", + "heading2", + "heading3", "pickAPhotoFromYourGallery", "takeAPhotoUsingYourCamera", "pasteAPhotoUsingALink", @@ -27,6 +39,10 @@ ], "cs": [ + "normal", + "heading1", + "heading2", + "heading3", "pickAPhotoFromYourGallery", "takeAPhotoUsingYourCamera", "pasteAPhotoUsingALink", @@ -36,6 +52,10 @@ ], "da": [ + "normal", + "heading1", + "heading2", + "heading3", "pickAPhotoFromYourGallery", "takeAPhotoUsingYourCamera", "pasteAPhotoUsingALink", @@ -45,6 +65,10 @@ ], "de": [ + "normal", + "heading1", + "heading2", + "heading3", "pickAPhotoFromYourGallery", "takeAPhotoUsingYourCamera", "pasteAPhotoUsingALink", @@ -54,6 +78,10 @@ ], "en_US": [ + "normal", + "heading1", + "heading2", + "heading3", "pickAPhotoFromYourGallery", "takeAPhotoUsingYourCamera", "pasteAPhotoUsingALink", @@ -63,6 +91,10 @@ ], "es": [ + "normal", + "heading1", + "heading2", + "heading3", "pickAPhotoFromYourGallery", "takeAPhotoUsingYourCamera", "pasteAPhotoUsingALink", @@ -72,6 +104,10 @@ ], "fa": [ + "normal", + "heading1", + "heading2", + "heading3", "pickAPhotoFromYourGallery", "takeAPhotoUsingYourCamera", "pasteAPhotoUsingALink", @@ -81,6 +117,10 @@ ], "fr": [ + "normal", + "heading1", + "heading2", + "heading3", "pickAPhotoFromYourGallery", "takeAPhotoUsingYourCamera", "pasteAPhotoUsingALink", @@ -90,6 +130,10 @@ ], "he": [ + "normal", + "heading1", + "heading2", + "heading3", "pickAPhotoFromYourGallery", "takeAPhotoUsingYourCamera", "pasteAPhotoUsingALink", @@ -99,6 +143,10 @@ ], "hi": [ + "normal", + "heading1", + "heading2", + "heading3", "pickAPhotoFromYourGallery", "takeAPhotoUsingYourCamera", "pasteAPhotoUsingALink", @@ -108,6 +156,10 @@ ], "id": [ + "normal", + "heading1", + "heading2", + "heading3", "pickAPhotoFromYourGallery", "takeAPhotoUsingYourCamera", "pasteAPhotoUsingALink", @@ -117,6 +169,10 @@ ], "it": [ + "normal", + "heading1", + "heading2", + "heading3", "pickAPhotoFromYourGallery", "takeAPhotoUsingYourCamera", "pasteAPhotoUsingALink", @@ -126,6 +182,10 @@ ], "ja": [ + "normal", + "heading1", + "heading2", + "heading3", "pickAPhotoFromYourGallery", "takeAPhotoUsingYourCamera", "pasteAPhotoUsingALink", @@ -135,6 +195,10 @@ ], "ko": [ + "normal", + "heading1", + "heading2", + "heading3", "pickAPhotoFromYourGallery", "takeAPhotoUsingYourCamera", "pasteAPhotoUsingALink", @@ -144,6 +208,10 @@ ], "ms": [ + "normal", + "heading1", + "heading2", + "heading3", "pickAPhotoFromYourGallery", "takeAPhotoUsingYourCamera", "pasteAPhotoUsingALink", @@ -153,6 +221,10 @@ ], "nl": [ + "normal", + "heading1", + "heading2", + "heading3", "pickAPhotoFromYourGallery", "takeAPhotoUsingYourCamera", "pasteAPhotoUsingALink", @@ -162,6 +234,10 @@ ], "no": [ + "normal", + "heading1", + "heading2", + "heading3", "pickAPhotoFromYourGallery", "takeAPhotoUsingYourCamera", "pasteAPhotoUsingALink", @@ -171,6 +247,10 @@ ], "pl": [ + "normal", + "heading1", + "heading2", + "heading3", "pickAPhotoFromYourGallery", "takeAPhotoUsingYourCamera", "pasteAPhotoUsingALink", @@ -180,6 +260,10 @@ ], "pt": [ + "normal", + "heading1", + "heading2", + "heading3", "pickAPhotoFromYourGallery", "takeAPhotoUsingYourCamera", "pasteAPhotoUsingALink", @@ -189,6 +273,10 @@ ], "pt_BR": [ + "normal", + "heading1", + "heading2", + "heading3", "pickAPhotoFromYourGallery", "takeAPhotoUsingYourCamera", "pasteAPhotoUsingALink", @@ -198,6 +286,10 @@ ], "ru": [ + "normal", + "heading1", + "heading2", + "heading3", "pickAPhotoFromYourGallery", "takeAPhotoUsingYourCamera", "pasteAPhotoUsingALink", @@ -207,6 +299,10 @@ ], "sr": [ + "normal", + "heading1", + "heading2", + "heading3", "pickAPhotoFromYourGallery", "takeAPhotoUsingYourCamera", "pasteAPhotoUsingALink", @@ -216,6 +312,10 @@ ], "sw": [ + "normal", + "heading1", + "heading2", + "heading3", "pickAPhotoFromYourGallery", "takeAPhotoUsingYourCamera", "pasteAPhotoUsingALink", @@ -225,6 +325,10 @@ ], "tk": [ + "normal", + "heading1", + "heading2", + "heading3", "pickAPhotoFromYourGallery", "takeAPhotoUsingYourCamera", "pasteAPhotoUsingALink", @@ -234,6 +338,10 @@ ], "tr": [ + "normal", + "heading1", + "heading2", + "heading3", "pickAPhotoFromYourGallery", "takeAPhotoUsingYourCamera", "pasteAPhotoUsingALink", @@ -243,6 +351,10 @@ ], "uk": [ + "normal", + "heading1", + "heading2", + "heading3", "pickAPhotoFromYourGallery", "takeAPhotoUsingYourCamera", "pasteAPhotoUsingALink", @@ -252,6 +364,10 @@ ], "ur": [ + "normal", + "heading1", + "heading2", + "heading3", "pickAPhotoFromYourGallery", "takeAPhotoUsingYourCamera", "pasteAPhotoUsingALink", @@ -261,6 +377,10 @@ ], "vi": [ + "normal", + "heading1", + "heading2", + "heading3", "pickAPhotoFromYourGallery", "takeAPhotoUsingYourCamera", "pasteAPhotoUsingALink", @@ -270,6 +390,10 @@ ], "zh": [ + "normal", + "heading1", + "heading2", + "heading3", "pickAPhotoFromYourGallery", "takeAPhotoUsingYourCamera", "pasteAPhotoUsingALink", @@ -279,6 +403,10 @@ ], "zh_CN": [ + "normal", + "heading1", + "heading2", + "heading3", "pickAPhotoFromYourGallery", "takeAPhotoUsingYourCamera", "pasteAPhotoUsingALink", @@ -288,6 +416,10 @@ ], "zh_HK": [ + "normal", + "heading1", + "heading2", + "heading3", "pickAPhotoFromYourGallery", "takeAPhotoUsingYourCamera", "pasteAPhotoUsingALink", diff --git a/lib/src/l10n/widgets/localizations.dart b/lib/src/l10n/widgets/localizations.dart index 7e40894da..57277b6b0 100644 --- a/lib/src/l10n/widgets/localizations.dart +++ b/lib/src/l10n/widgets/localizations.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; -import '../../extensions/quill_provider.dart'; +import '../../extensions/quill_configurations_ext.dart'; import '../extensions/localizations.dart'; class FlutterQuillLocalizationsWidget extends StatelessWidget { @@ -19,7 +19,7 @@ class FlutterQuillLocalizationsWidget extends StatelessWidget { return child; } return Localizations( - locale: context.requireQuillSharedConfigurations.locale ?? + locale: context.quillSharedConfigurations?.locale ?? Localizations.localeOf(context), delegates: FlutterQuillLocalizations.localizationsDelegates, child: child, diff --git a/lib/src/models/config/editor/configurations.dart b/lib/src/models/config/editor/editor_configurations.dart similarity index 95% rename from lib/src/models/config/editor/configurations.dart rename to lib/src/models/config/editor/editor_configurations.dart index c6e0f5de5..d6a4ecb10 100644 --- a/lib/src/models/config/editor/configurations.dart +++ b/lib/src/models/config/editor/editor_configurations.dart @@ -6,14 +6,15 @@ import 'package:flutter/material.dart' import 'package:flutter/widgets.dart'; import 'package:meta/meta.dart' show experimental; -import '../../../widgets/default_styles.dart'; -import '../../../widgets/delegate.dart'; -import '../../../widgets/editor/editor.dart'; import '../../../widgets/editor/editor_builder.dart'; -import '../../../widgets/embeds.dart'; -import '../../../widgets/link.dart'; +import '../../../widgets/others/default_styles.dart'; +import '../../../widgets/others/delegate.dart'; +import '../../../widgets/others/link.dart'; +import '../../../widgets/quill/embeds.dart'; +import '../../../widgets/quill/quill_controller.dart'; import '../../../widgets/raw_editor/raw_editor.dart'; import '../../themes/quill_dialog_theme.dart'; +import '../quill_shared_configurations.dart'; import 'element_options.dart'; export 'element_options.dart'; @@ -24,6 +25,8 @@ class QuillEditorConfigurations extends Equatable { /// Important note for the maintainers /// When editing this class please update the [copyWith] function too. const QuillEditorConfigurations({ + required this.controller, + this.sharedConfigurations = const QuillSharedConfigurations(), this.scrollable = true, this.padding = EdgeInsets.zero, this.autoFocus = false, @@ -74,6 +77,10 @@ class QuillEditorConfigurations extends Equatable { this.textInputAction = TextInputAction.newline, }); + final QuillSharedConfigurations sharedConfigurations; + + final QuillController controller; + /// The text placeholder in the quill editor final String? placeholder; @@ -331,6 +338,8 @@ class QuillEditorConfigurations extends Equatable { // regenerate this function using extension in vs code or plugin in intellij QuillEditorConfigurations copyWith({ + QuillSharedConfigurations? sharedConfigurations, + QuillController? controller, String? placeholder, bool? readOnly, bool? scrollable, @@ -376,6 +385,8 @@ class QuillEditorConfigurations extends Equatable { TextInputAction? textInputAction, }) { return QuillEditorConfigurations( + sharedConfigurations: sharedConfigurations ?? this.sharedConfigurations, + controller: controller ?? this.controller, placeholder: placeholder ?? this.placeholder, readOnly: readOnly ?? this.readOnly, scrollable: scrollable ?? this.scrollable, diff --git a/lib/src/models/config/editor/element_options.dart b/lib/src/models/config/editor/element_options.dart index a2c20171e..f083f692e 100644 --- a/lib/src/models/config/editor/element_options.dart +++ b/lib/src/models/config/editor/element_options.dart @@ -2,16 +2,25 @@ import 'package:equatable/equatable.dart'; import 'package:flutter/foundation.dart' show immutable; import 'elements/code_block.dart'; +import 'elements/list/ordered_list.dart'; +import 'elements/list/unordered_list.dart'; export 'elements/code_block.dart'; +export 'elements/list/ordered_list.dart'; +export 'elements/list/unordered_list.dart'; @immutable class QuillEditorElementOptions extends Equatable { const QuillEditorElementOptions({ this.codeBlock = const QuillEditorCodeBlockElementOptions(), + this.orderedList = const QuillEditorOrderedListElementOptions(), + this.unorderedList = const QuillEditorUnOrderedListElementOptions(), }); final QuillEditorCodeBlockElementOptions codeBlock; + + final QuillEditorOrderedListElementOptions orderedList; + final QuillEditorUnOrderedListElementOptions unorderedList; @override List get props => [ codeBlock, diff --git a/lib/src/models/config/editor/elements/list/ordered_list.dart b/lib/src/models/config/editor/elements/list/ordered_list.dart new file mode 100644 index 000000000..b48252991 --- /dev/null +++ b/lib/src/models/config/editor/elements/list/ordered_list.dart @@ -0,0 +1,14 @@ +import 'package:equatable/equatable.dart'; +import 'package:flutter/foundation.dart' show immutable; +import 'package:flutter/widgets.dart' show Color; + +@immutable +class QuillEditorOrderedListElementOptions extends Equatable { + const QuillEditorOrderedListElementOptions( + {this.backgroundColor, this.fontColor}); + + final Color? backgroundColor; + final Color? fontColor; + @override + List get props => []; +} diff --git a/lib/src/models/config/editor/elements/list/unordered_list.dart b/lib/src/models/config/editor/elements/list/unordered_list.dart new file mode 100644 index 000000000..3fe570eb2 --- /dev/null +++ b/lib/src/models/config/editor/elements/list/unordered_list.dart @@ -0,0 +1,14 @@ +import 'package:equatable/equatable.dart'; +import 'package:flutter/foundation.dart' show immutable; +import 'package:flutter/widgets.dart' show Color; + +@immutable +class QuillEditorUnOrderedListElementOptions extends Equatable { + const QuillEditorUnOrderedListElementOptions( + {this.backgroundColor, this.fontColor}); + + final Color? backgroundColor; + final Color? fontColor; + @override + List get props => []; +} diff --git a/lib/src/models/config/others/animations.dart b/lib/src/models/config/others/animations.dart index ce6bcb0cb..42e082796 100644 --- a/lib/src/models/config/others/animations.dart +++ b/lib/src/models/config/others/animations.dart @@ -1,28 +1,28 @@ -import 'package:equatable/equatable.dart'; -import 'package:meta/meta.dart' show experimental, immutable; +// import 'package:equatable/equatable.dart'; +// import 'package:meta/meta.dart' show experimental, immutable; -@immutable -@experimental -class QuillAnimationConfigurations extends Equatable { - const QuillAnimationConfigurations({ - required this.checkBoxPointItem, - }); +// @immutable +// @experimental +// class QuillAnimationConfigurations extends Equatable { +// const QuillAnimationConfigurations({ +// required this.checkBoxPointItem, +// }); - factory QuillAnimationConfigurations.disableAll() => - const QuillAnimationConfigurations( - checkBoxPointItem: false, - ); +// factory QuillAnimationConfigurations.disableAll() => +// const QuillAnimationConfigurations( +// checkBoxPointItem: false, +// ); - factory QuillAnimationConfigurations.enableAll() => - const QuillAnimationConfigurations( - checkBoxPointItem: true, - ); +// factory QuillAnimationConfigurations.enableAll() => +// const QuillAnimationConfigurations( +// checkBoxPointItem: true, +// ); - /// This currently has issue which the whole checkbox list will rebuilt - /// and the animation will replay when some value changes - /// which is why disabled by default - final bool checkBoxPointItem; +// /// This currently has issue which the whole checkbox list will rebuilt +// /// and the animation will replay when some value changes +// /// which is why disabled by default +// final bool checkBoxPointItem; - @override - List get props => []; -} +// @override +// List get props => []; +// } diff --git a/lib/src/models/config/quill_configurations.dart b/lib/src/models/config/quill_configurations.dart index fd9d985f3..b673ba39e 100644 --- a/lib/src/models/config/quill_configurations.dart +++ b/lib/src/models/config/quill_configurations.dart @@ -3,27 +3,26 @@ import 'package:flutter/foundation.dart' show immutable; import '../../../flutter_quill.dart'; -export './editor/configurations.dart'; +export 'editor/editor_configurations.dart'; export 'quill_shared_configurations.dart'; -export 'toolbar/toolbar_configurations.dart'; +export 'toolbar/simple_toolbar_configurations.dart'; @immutable class QuillConfigurations extends Equatable { const QuillConfigurations({ - required this.controller, this.sharedConfigurations = const QuillSharedConfigurations(), }); - /// Controller object which establishes a link between a rich text document - /// and this editor. - /// - /// The controller is shared between [QuillEditorConfigurations] and - /// [QuillToolbarConfigurations] but to simplify things we will defined it - /// here, it should not be null - final QuillController controller; + // /// Controller object which establishes a link between a rich text document + // /// and this editor. + // /// + // /// The controller is shared between [QuillEditorConfigurations] and + // /// [QuillToolbarConfigurations] but to simplify things we will defined it + // /// here, it should not be null + // final QuillController controller; /// The shared configurations between [QuillEditorConfigurations] and - /// [QuillToolbarConfigurations] so we don't duplicate things + /// [QuillSimpleToolbarConfigurations] so we don't duplicate things final QuillSharedConfigurations sharedConfigurations; @override diff --git a/lib/src/models/config/quill_shared_configurations.dart b/lib/src/models/config/quill_shared_configurations.dart index fd34e8dd0..beadc4327 100644 --- a/lib/src/models/config/quill_shared_configurations.dart +++ b/lib/src/models/config/quill_shared_configurations.dart @@ -2,22 +2,19 @@ import 'package:equatable/equatable.dart'; import 'package:flutter/material.dart' show Color, Colors, Locale; import '../themes/quill_dialog_theme.dart'; -import './editor/configurations.dart' show QuillEditorConfigurations; -import 'others/animations.dart'; -import 'toolbar/toolbar_configurations.dart' show QuillToolbarConfigurations; +import 'editor/editor_configurations.dart' show QuillEditorConfigurations; +import 'toolbar/simple_toolbar_configurations.dart' + show QuillSimpleToolbarConfigurations; export './others/animations.dart'; /// The shared configurations between [QuillEditorConfigurations] and -/// [QuillToolbarConfigurations] so we don't duplicate things +/// [QuillSimpleToolbarConfigurations] so we don't duplicate things class QuillSharedConfigurations extends Equatable { const QuillSharedConfigurations({ this.dialogBarrierColor = Colors.black54, this.dialogTheme, this.locale, - this.animationConfigurations = const QuillAnimationConfigurations( - checkBoxPointItem: false, - ), this.extraConfigurations = const {}, }); @@ -37,9 +34,6 @@ class QuillSharedConfigurations extends Equatable { /// `MaterialApp` or `WidgetsApp` final Locale? locale; - /// To configure which animations you want to be enabled - final QuillAnimationConfigurations animationConfigurations; - /// Store custom configurations in here and use it in the widget tree final Map extraConfigurations; @@ -48,6 +42,5 @@ class QuillSharedConfigurations extends Equatable { dialogBarrierColor, dialogTheme, locale, - animationConfigurations, ]; } diff --git a/lib/src/models/config/raw_editor/configurations.dart b/lib/src/models/config/raw_editor/raw_editor_configurations.dart similarity index 96% rename from lib/src/models/config/raw_editor/configurations.dart rename to lib/src/models/config/raw_editor/raw_editor_configurations.dart index 2fe2866b8..abe92a04a 100644 --- a/lib/src/models/config/raw_editor/configurations.dart +++ b/lib/src/models/config/raw_editor/raw_editor_configurations.dart @@ -25,11 +25,11 @@ import 'package:flutter/widgets.dart' Widget; import 'package:meta/meta.dart' show immutable; -import '../../../widgets/controller.dart'; -import '../../../widgets/cursor.dart'; -import '../../../widgets/default_styles.dart'; -import '../../../widgets/delegate.dart'; -import '../../../widgets/link.dart'; +import '../../../widgets/others/cursor.dart'; +import '../../../widgets/others/default_styles.dart'; +import '../../../widgets/others/delegate.dart'; +import '../../../widgets/others/link.dart'; +import '../../../widgets/quill/quill_controller.dart'; import '../../../widgets/raw_editor/raw_editor.dart'; import '../../../widgets/raw_editor/raw_editor_state.dart'; import '../../themes/quill_dialog_theme.dart'; @@ -49,7 +49,7 @@ class QuillRawEditorConfigurations extends Equatable { this.showCursor = true, this.scrollable = true, this.padding = EdgeInsets.zero, - this.isReadOnly = false, + this.readOnly = false, this.placeholder, this.onLaunchUrl, this.contextMenuBuilder = defaultContextMenuBuilder, @@ -97,7 +97,7 @@ class QuillRawEditorConfigurations extends Equatable { /// by any shortcut or keyboard operation. The text is still selectable. /// /// Defaults to false. Must not be null. - final bool isReadOnly; + final bool readOnly; final String? placeholder; @@ -296,7 +296,7 @@ class QuillRawEditorConfigurations extends Equatable { @override List get props => [ - isReadOnly, + readOnly, placeholder, ]; } diff --git a/lib/src/models/config/toolbar/base_toolbar_configurations.dart b/lib/src/models/config/toolbar/base_toolbar_configurations.dart deleted file mode 100644 index 3450a9ded..000000000 --- a/lib/src/models/config/toolbar/base_toolbar_configurations.dart +++ /dev/null @@ -1,33 +0,0 @@ -import 'package:flutter/widgets.dart' - show Axis, WrapAlignment, WrapCrossAlignment, immutable; - -import '../../../widgets/toolbar/base_toolbar.dart'; -import 'toolbar_shared_configurations.dart'; - -@immutable -class QuillBaseToolbarConfigurations extends QuillSharedToolbarProperties { - const QuillBaseToolbarConfigurations({ - required this.childrenBuilder, - super.axis = Axis.horizontal, - super.toolbarSize = kDefaultIconSize * 2, - super.toolbarSectionSpacing = kToolbarSectionSpacing, - super.toolbarIconAlignment = WrapAlignment.center, - super.toolbarIconCrossAlignment = WrapCrossAlignment.center, - super.color, - super.customButtons = const [], - super.sectionDividerColor, - super.sectionDividerSpace, - super.linkDialogAction, - super.multiRowsDisplay = true, - super.decoration, - - /// Note this only used when you using the quill toolbar buttons like - /// `QuillToolbarHistoryButton` inside it - super.buttonOptions = const QuillToolbarButtonOptions(), - }); - - final QuillBaseToolbarChildrenBuilder childrenBuilder; - - @override - List get props => []; -} diff --git a/lib/src/models/config/toolbar/buttons/base.dart b/lib/src/models/config/toolbar/buttons/base_configurations.dart similarity index 94% rename from lib/src/models/config/toolbar/buttons/base.dart rename to lib/src/models/config/toolbar/buttons/base_configurations.dart index 4b7b8ab12..91058591c 100644 --- a/lib/src/models/config/toolbar/buttons/base.dart +++ b/lib/src/models/config/toolbar/buttons/base_configurations.dart @@ -1,9 +1,8 @@ -// ignore_for_file: public_member_api_docs, sort_constructors_first import 'package:equatable/equatable.dart'; import 'package:flutter/foundation.dart' show VoidCallback, immutable; import 'package:flutter/widgets.dart' show BuildContext, IconData, Widget; -import '../../../../../flutter_quill.dart' show QuillController, QuillProvider; +import '../../../../../flutter_quill.dart' show QuillController; import '../../../themes/quill_icon_theme.dart' show QuillIconTheme; import '../../quill_configurations.dart' show kDefaultIconSize, kIconButtonFactor; @@ -82,7 +81,7 @@ class QuillToolbarBaseButtonOptions extends Equatable { /// If you want to dispaly a differnet widget based using a builder final QuillToolbarButtonOptionsChildBuilder childBuilder; - /// By default it will be from the one in [QuillProvider] + /// By default it will be from the one in [QuillEditor] or [QuillToolbar] /// To override it you must pass not null controller /// if you wish to use the controller in the [childBuilder], please use the /// one from the extraOptions since it will be not null and will be the one diff --git a/lib/src/models/config/toolbar/buttons/clear_format.dart b/lib/src/models/config/toolbar/buttons/clear_format_configurations.dart similarity index 100% rename from lib/src/models/config/toolbar/buttons/clear_format.dart rename to lib/src/models/config/toolbar/buttons/clear_format_configurations.dart diff --git a/lib/src/models/config/toolbar/buttons/color.dart b/lib/src/models/config/toolbar/buttons/color_configurations.dart similarity index 94% rename from lib/src/models/config/toolbar/buttons/color.dart rename to lib/src/models/config/toolbar/buttons/color_configurations.dart index 7c722d98e..b3c08c251 100644 --- a/lib/src/models/config/toolbar/buttons/color.dart +++ b/lib/src/models/config/toolbar/buttons/color_configurations.dart @@ -1,8 +1,8 @@ import 'package:flutter/widgets.dart' show Color; -import '../../../../widgets/controller.dart'; +import '../../../../widgets/quill/quill_controller.dart'; import '../../quill_shared_configurations.dart' show QuillSharedConfigurations; -import 'base.dart'; +import 'base_configurations.dart'; class QuillToolbarColorButtonExtraOptions extends QuillToolbarBaseButtonExtraOptions { diff --git a/lib/src/models/config/toolbar/buttons/custom_button.dart b/lib/src/models/config/toolbar/buttons/custom_button_configurations.dart similarity index 95% rename from lib/src/models/config/toolbar/buttons/custom_button.dart rename to lib/src/models/config/toolbar/buttons/custom_button_configurations.dart index 9a862e837..3c6f3d7b8 100644 --- a/lib/src/models/config/toolbar/buttons/custom_button.dart +++ b/lib/src/models/config/toolbar/buttons/custom_button_configurations.dart @@ -1,6 +1,6 @@ import 'package:flutter/widgets.dart' show VoidCallback, Widget; -import 'base.dart'; +import 'base_configurations.dart'; class QuillToolbarCustomButtonExtraOptions extends QuillToolbarBaseButtonExtraOptions { diff --git a/lib/src/models/config/toolbar/buttons/font_family.dart b/lib/src/models/config/toolbar/buttons/font_family_configurations.dart similarity index 98% rename from lib/src/models/config/toolbar/buttons/font_family.dart rename to lib/src/models/config/toolbar/buttons/font_family_configurations.dart index 41319bbb9..5674232cf 100644 --- a/lib/src/models/config/toolbar/buttons/font_family.dart +++ b/lib/src/models/config/toolbar/buttons/font_family_configurations.dart @@ -61,7 +61,7 @@ class QuillToolbarFontFamilyButtonOptions extends QuillToolbarBaseButtonOptions< final double hoverElevation; final double highlightElevation; - /// By default it will be [fontFamilyValues] from [QuillToolbarConfigurations] + /// By default it will be [fontFamilyValues] from [QuillSimpleToolbarConfigurations] /// You can override this if you want final Map? rawItemsMap; final ValueChanged? onSelected; diff --git a/lib/src/models/config/toolbar/buttons/font_size.dart b/lib/src/models/config/toolbar/buttons/font_size_configurations.dart similarity index 96% rename from lib/src/models/config/toolbar/buttons/font_size.dart rename to lib/src/models/config/toolbar/buttons/font_size_configurations.dart index 45464ecf8..ec3c66825 100644 --- a/lib/src/models/config/toolbar/buttons/font_size.dart +++ b/lib/src/models/config/toolbar/buttons/font_size_configurations.dart @@ -6,7 +6,7 @@ import 'package:flutter/material.dart' import 'package:flutter/widgets.dart' show Color, EdgeInsets, EdgeInsetsGeometry, TextOverflow, TextStyle; -import '../../../../widgets/controller.dart'; +import '../../../../widgets/quill/quill_controller.dart'; import '../../../documents/attribute.dart'; import '../../../themes/quill_icon_theme.dart'; import '../../quill_configurations.dart'; @@ -58,7 +58,7 @@ class QuillToolbarFontSizeButtonOptions extends QuillToolbarBaseButtonOptions< final double hoverElevation; final double highlightElevation; - /// By default it will be [fontSizesValues] from [QuillToolbarConfigurations] + /// By default it will be [fontSizesValues] from [QuillSimpleToolbarConfigurations] /// You can override this if you want final Map? rawItemsMap; final ValueChanged? onSelected; diff --git a/lib/src/models/config/toolbar/buttons/history.dart b/lib/src/models/config/toolbar/buttons/history_configurations.dart similarity index 87% rename from lib/src/models/config/toolbar/buttons/history.dart rename to lib/src/models/config/toolbar/buttons/history_configurations.dart index 1f1bfaabf..5766c3802 100644 --- a/lib/src/models/config/toolbar/buttons/history.dart +++ b/lib/src/models/config/toolbar/buttons/history_configurations.dart @@ -20,7 +20,6 @@ class QuillToolbarHistoryButtonExtraOptions class QuillToolbarHistoryButtonOptions extends QuillToolbarBaseButtonOptions< QuillToolbarHistoryButtonOptions, QuillToolbarHistoryButtonExtraOptions> { const QuillToolbarHistoryButtonOptions({ - required this.isUndo, super.iconData, super.controller, super.iconTheme, @@ -31,10 +30,6 @@ class QuillToolbarHistoryButtonOptions extends QuillToolbarBaseButtonOptions< this.iconButtonFactor, }); - /// If this true then it will be the undo button - /// otherwise it will be redo - final bool isUndo; - /// By default will use [globalIconSize] final double? iconSize; final double? iconButtonFactor; diff --git a/lib/src/models/config/toolbar/buttons/indent.dart b/lib/src/models/config/toolbar/buttons/indent_configurations.dart similarity index 83% rename from lib/src/models/config/toolbar/buttons/indent.dart rename to lib/src/models/config/toolbar/buttons/indent_configurations.dart index 66700c71d..559217e7b 100644 --- a/lib/src/models/config/toolbar/buttons/indent.dart +++ b/lib/src/models/config/toolbar/buttons/indent_configurations.dart @@ -1,6 +1,6 @@ import 'package:flutter/foundation.dart'; -import 'base.dart'; +import 'base_configurations.dart'; class QuillToolbarIndentButtonExtraOptions extends QuillToolbarBaseButtonExtraOptions { @@ -12,7 +12,8 @@ class QuillToolbarIndentButtonExtraOptions } @immutable -class QuillToolbarIndentButtonOptions extends QuillToolbarBaseButtonOptions { +class QuillToolbarIndentButtonOptions extends QuillToolbarBaseButtonOptions< + QuillToolbarIndentButtonOptions, QuillToolbarIndentButtonExtraOptions> { const QuillToolbarIndentButtonOptions({ super.iconData, super.afterButtonPressed, diff --git a/lib/src/models/config/toolbar/buttons/link_style2.dart b/lib/src/models/config/toolbar/buttons/link_style2_configurations.dart similarity index 97% rename from lib/src/models/config/toolbar/buttons/link_style2.dart rename to lib/src/models/config/toolbar/buttons/link_style2_configurations.dart index 750141438..6c977f643 100644 --- a/lib/src/models/config/toolbar/buttons/link_style2.dart +++ b/lib/src/models/config/toolbar/buttons/link_style2_configurations.dart @@ -1,7 +1,7 @@ import 'package:flutter/widgets.dart'; import '../../../themes/quill_dialog_theme.dart'; -import 'base.dart'; +import 'base_configurations.dart'; class QuillToolbarLinkStyleButton2ExtraOptions extends QuillToolbarBaseButtonExtraOptions { diff --git a/lib/src/models/config/toolbar/buttons/link_style.dart b/lib/src/models/config/toolbar/buttons/link_style_configurations.dart similarity index 100% rename from lib/src/models/config/toolbar/buttons/link_style.dart rename to lib/src/models/config/toolbar/buttons/link_style_configurations.dart diff --git a/lib/src/models/config/toolbar/buttons/search.dart b/lib/src/models/config/toolbar/buttons/search_configurations.dart similarity index 100% rename from lib/src/models/config/toolbar/buttons/search.dart rename to lib/src/models/config/toolbar/buttons/search_configurations.dart diff --git a/lib/src/models/config/toolbar/buttons/select_alignment.dart b/lib/src/models/config/toolbar/buttons/select_alignment_configurations.dart similarity index 98% rename from lib/src/models/config/toolbar/buttons/select_alignment.dart rename to lib/src/models/config/toolbar/buttons/select_alignment_configurations.dart index 0a90d350b..7870e9a15 100644 --- a/lib/src/models/config/toolbar/buttons/select_alignment.dart +++ b/lib/src/models/config/toolbar/buttons/select_alignment_configurations.dart @@ -1,5 +1,5 @@ import 'package:flutter/widgets.dart' show IconData, immutable; -import 'base.dart'; +import 'base_configurations.dart'; class QuillToolbarSelectAlignmentButtonExtraOptions extends QuillToolbarBaseButtonExtraOptions { diff --git a/lib/src/models/config/toolbar/buttons/select_header_style.dart b/lib/src/models/config/toolbar/buttons/select_header_style_configurations.dart similarity index 93% rename from lib/src/models/config/toolbar/buttons/select_header_style.dart rename to lib/src/models/config/toolbar/buttons/select_header_style_configurations.dart index 6e70c9eb6..975b47702 100644 --- a/lib/src/models/config/toolbar/buttons/select_header_style.dart +++ b/lib/src/models/config/toolbar/buttons/select_header_style_configurations.dart @@ -37,7 +37,7 @@ class QuillToolbarSelectHeaderStyleButtonsOptions /// ] final List? attributes; - /// By default we will the toolbar axis from [QuillToolbarConfigurations] + /// By default we will the toolbar axis from [QuillSimpleToolbarConfigurations] final Axis? axis; final double? iconSize; final double? iconButtonFactor; diff --git a/lib/src/models/config/toolbar/buttons/toggle_check_list.dart b/lib/src/models/config/toolbar/buttons/toggle_check_list_configurations.dart similarity index 100% rename from lib/src/models/config/toolbar/buttons/toggle_check_list.dart rename to lib/src/models/config/toolbar/buttons/toggle_check_list_configurations.dart diff --git a/lib/src/models/config/toolbar/buttons/toggle_style.dart b/lib/src/models/config/toolbar/buttons/toggle_style_configurations.dart similarity index 96% rename from lib/src/models/config/toolbar/buttons/toggle_style.dart rename to lib/src/models/config/toolbar/buttons/toggle_style_configurations.dart index 0d58fdca9..4ea2ea0f0 100644 --- a/lib/src/models/config/toolbar/buttons/toggle_style.dart +++ b/lib/src/models/config/toolbar/buttons/toggle_style_configurations.dart @@ -2,7 +2,7 @@ import 'package:flutter/foundation.dart' show immutable; import 'package:flutter/widgets.dart' show Color; -import 'base.dart'; +import 'base_configurations.dart'; class QuillToolbarToggleStyleButtonExtraOptions extends QuillToolbarBaseButtonExtraOptions diff --git a/lib/src/models/config/toolbar/simple_toolbar_configurations.dart b/lib/src/models/config/toolbar/simple_toolbar_configurations.dart new file mode 100644 index 000000000..c9f0ffeb5 --- /dev/null +++ b/lib/src/models/config/toolbar/simple_toolbar_configurations.dart @@ -0,0 +1,322 @@ +// ignore_for_file: public_member_api_docs, sort_constructors_first +import 'package:equatable/equatable.dart'; +import 'package:flutter/foundation.dart' show immutable; +import 'package:flutter/widgets.dart' + show Axis, Widget, WrapAlignment, WrapCrossAlignment; + +import '../../../widgets/quill/embeds.dart'; +import '../../../widgets/quill/quill_controller.dart'; +import '../../themes/quill_dialog_theme.dart'; +import '../../themes/quill_icon_theme.dart'; +import 'buttons/base_configurations.dart'; +import 'buttons/clear_format_configurations.dart'; +import 'buttons/color_configurations.dart'; +import 'buttons/custom_button_configurations.dart'; +import 'buttons/font_family_configurations.dart'; +import 'buttons/font_size_configurations.dart'; +import 'buttons/history_configurations.dart'; +import 'buttons/indent_configurations.dart'; +import 'buttons/link_style2_configurations.dart'; +import 'buttons/link_style_configurations.dart'; +import 'buttons/search_configurations.dart'; +import 'buttons/select_alignment_configurations.dart'; +import 'buttons/select_header_style_configurations.dart'; +import 'buttons/toggle_check_list_configurations.dart'; +import 'buttons/toggle_style_configurations.dart'; +import 'toolbar_shared_configurations.dart'; + +export './../../../widgets/toolbar/buttons/search/search_dialog.dart'; +export 'buttons/base_configurations.dart'; +export 'buttons/clear_format_configurations.dart'; +export 'buttons/color_configurations.dart'; +export 'buttons/custom_button_configurations.dart'; +export 'buttons/font_family_configurations.dart'; +export 'buttons/font_size_configurations.dart'; +export 'buttons/history_configurations.dart'; +export 'buttons/indent_configurations.dart'; +export 'buttons/link_style2_configurations.dart'; +export 'buttons/link_style_configurations.dart'; +export 'buttons/search_configurations.dart'; +export 'buttons/select_alignment_configurations.dart'; +export 'buttons/select_header_style_configurations.dart'; +export 'buttons/toggle_check_list_configurations.dart'; +export 'buttons/toggle_style_configurations.dart'; + +/// The default size of the icon of a button. +const double kDefaultIconSize = 18; + +/// The default size for the toolbar (width, height) +const double defaultToolbarSize = kDefaultIconSize * 2; + +/// The factor of how much larger the button is in relation to the icon. +const double kIconButtonFactor = 1.6; + +/// The horizontal margin between the contents of each toolbar section. +const double kToolbarSectionSpacing = 4; + +enum LinkStyleType { + /// Defines the original [QuillToolbarLinkStyleButton]. + original, + + /// Defines the alternative [QuillToolbarLinkStyleButton2]. + alternative; + + bool get isOriginal => this == LinkStyleType.original; + bool get isAlternative => this == LinkStyleType.alternative; +} + +/// The configurations for the toolbar widget of flutter quill +@immutable +class QuillSimpleToolbarConfigurations extends QuillSharedToolbarProperties { + const QuillSimpleToolbarConfigurations({ + required this.controller, + super.sharedConfigurations, + super.toolbarSectionSpacing = kToolbarSectionSpacing, + super.toolbarIconAlignment = WrapAlignment.center, + super.toolbarIconCrossAlignment = WrapCrossAlignment.center, + super.buttonOptions = const QuillToolbarButtonOptions(), + this.customButtons = const [], + this.fontFamilyValues, + super.multiRowsDisplay = true, + this.fontSizesValues, + this.showDividers = true, + this.showFontFamily = true, + this.showFontSize = true, + this.showBoldButton = true, + this.showItalicButton = true, + this.showSmallButton = false, + this.showUnderLineButton = true, + this.showStrikeThrough = true, + this.showInlineCode = true, + this.showColorButton = true, + this.showBackgroundColorButton = true, + this.showClearFormat = true, + this.showAlignmentButtons = false, + this.showLeftAlignment = true, + this.showCenterAlignment = true, + this.showRightAlignment = true, + this.showJustifyAlignment = true, + this.showHeaderStyle = true, + this.showListNumbers = true, + this.showListBullets = true, + this.showListCheck = true, + this.showCodeBlock = true, + this.showQuote = true, + this.showIndent = true, + this.showLink = true, + this.showUndo = true, + this.showRedo = true, + this.showDirection = false, + this.showSearchButton = true, + this.showSubscript = true, + this.showSuperscript = true, + this.linkStyleType = LinkStyleType.original, + + /// The decoration to use for the toolbar. + super.decoration, + + /// Toolbar items to display for controls of embed blocks + this.embedButtons, + super.linkDialogAction, + + ///The theme to use for the icons in the toolbar, uses type [QuillIconTheme] + // this.iconTheme, + this.dialogTheme, + super.axis = Axis.horizontal, + super.color, + super.sectionDividerColor, + super.sectionDividerSpace, + this.spacerWidget, + + /// By default it will calculated based on the [globalIconSize] from + /// [base] in [QuillToolbarButtonOptions] + /// You can change it but the the change only apply if + /// the [multiRowsDisplay] is false, if [multiRowsDisplay] then the value + /// will be [kDefaultIconSize] * 2 + super.toolbarSize, + }) : _toolbarSize = toolbarSize; + + final double? _toolbarSize; + + /// The toolbar size, by default it will be `baseButtonOptions.iconSize * 2` + @override + double get toolbarSize { + final alternativeToolbarSize = _toolbarSize; + if (alternativeToolbarSize != null) { + return alternativeToolbarSize; + } + return buttonOptions.base.globalIconSize * 2; + } + + final Map? fontFamilyValues; + + final QuillController controller; + + /// A widget that will placed between each button in the toolbar + /// can be used as a spacer + /// it will not used before the first button + /// it will not used after the last button + /// it will also not used in the toolbar dividers + /// Default value will be [SizedBox.shrink()] + /// some widgets like the header styles will be considered as one widget + final Widget? spacerWidget; + + /// By default it will be + /// ``` + /// { + /// 'Small'.i18n: 'small', + /// 'Large'.i18n: 'large', + /// 'Huge'.i18n: 'huge', + /// 'Clear'.loc: '0' + /// } + /// ``` + final Map? fontSizesValues; + + /// List of custom buttons + final List customButtons; + + final bool showDividers; + final bool showFontFamily; + final bool showFontSize; + final bool showBoldButton; + final bool showItalicButton; + final bool showSmallButton; + final bool showUnderLineButton; + final bool showStrikeThrough; + final bool showInlineCode; + final bool showColorButton; + final bool showBackgroundColorButton; + final bool showClearFormat; + + final bool showAlignmentButtons; + final bool showLeftAlignment; + final bool showCenterAlignment; + final bool showRightAlignment; + final bool showJustifyAlignment; + final bool showHeaderStyle; + final bool showListNumbers; + final bool showListBullets; + final bool showListCheck; + final bool showCodeBlock; + final bool showQuote; + final bool showIndent; + final bool showLink; + final bool showUndo; + final bool showRedo; + final bool showDirection; + final bool showSearchButton; + final bool showSubscript; + final bool showSuperscript; + + /// Toolbar items to display for controls of embed blocks + final List? embedButtons; + + // ///The theme to use for the icons in the toolbar, uses type [QuillIconTheme] + // final QuillIconTheme? iconTheme; + + ///The theme to use for the theming of the [LinkDialog()], + ///shown when embedding an image, for example + final QuillDialogTheme? dialogTheme; + + /// Defines which dialog is used for applying link attribute. + final LinkStyleType linkStyleType; + + @override + List get props => [ + buttonOptions, + multiRowsDisplay, + fontSizesValues, + toolbarSize, + axis, + ]; +} + +/// The configurations for the buttons of the toolbar widget of flutter quill +@immutable +class QuillToolbarButtonOptions extends Equatable { + const QuillToolbarButtonOptions({ + this.base = const QuillToolbarBaseButtonOptions(), + this.undoHistory = const QuillToolbarHistoryButtonOptions(), + this.redoHistory = const QuillToolbarHistoryButtonOptions(), + this.fontFamily = const QuillToolbarFontFamilyButtonOptions(), + this.fontSize = const QuillToolbarFontSizeButtonOptions(), + this.bold = const QuillToolbarToggleStyleButtonOptions(), + this.subscript = const QuillToolbarToggleStyleButtonOptions(), + this.superscript = const QuillToolbarToggleStyleButtonOptions(), + this.italic = const QuillToolbarToggleStyleButtonOptions(), + this.small = const QuillToolbarToggleStyleButtonOptions(), + this.underLine = const QuillToolbarToggleStyleButtonOptions(), + this.strikeThrough = const QuillToolbarToggleStyleButtonOptions(), + this.inlineCode = const QuillToolbarToggleStyleButtonOptions(), + this.direction = const QuillToolbarToggleStyleButtonOptions(), + this.listNumbers = const QuillToolbarToggleStyleButtonOptions(), + this.listBullets = const QuillToolbarToggleStyleButtonOptions(), + this.codeBlock = const QuillToolbarToggleStyleButtonOptions(), + this.quote = const QuillToolbarToggleStyleButtonOptions(), + this.toggleCheckList = const QuillToolbarToggleCheckListButtonOptions(), + this.indentIncrease = const QuillToolbarIndentButtonOptions(), + this.indentDecrease = const QuillToolbarIndentButtonOptions(), + this.color = const QuillToolbarColorButtonOptions(), + this.backgroundColor = const QuillToolbarColorButtonOptions(), + this.clearFormat = const QuillToolbarClearFormatButtonOptions(), + this.selectAlignmentButtons = + const QuillToolbarSelectAlignmentButtonOptions(), + this.search = const QuillToolbarSearchButtonOptions(), + this.selectHeaderStyleButtons = + const QuillToolbarSelectHeaderStyleButtonsOptions(), + this.linkStyle = const QuillToolbarLinkStyleButtonOptions(), + this.linkStyle2 = const QuillToolbarLinkStyleButton2Options(), + this.customButtons = const QuillToolbarCustomButtonOptions(), + }); + + /// The base configurations for all the buttons which will apply to all + /// but if the options overrided in the spesefic button options + /// then it will use that instead + final QuillToolbarBaseButtonOptions base; + final QuillToolbarHistoryButtonOptions undoHistory; + final QuillToolbarHistoryButtonOptions redoHistory; + final QuillToolbarFontFamilyButtonOptions fontFamily; + final QuillToolbarFontSizeButtonOptions fontSize; + final QuillToolbarToggleStyleButtonOptions bold; + final QuillToolbarToggleStyleButtonOptions subscript; + final QuillToolbarToggleStyleButtonOptions superscript; + final QuillToolbarToggleStyleButtonOptions italic; + final QuillToolbarToggleStyleButtonOptions small; + final QuillToolbarToggleStyleButtonOptions underLine; + final QuillToolbarToggleStyleButtonOptions strikeThrough; + final QuillToolbarToggleStyleButtonOptions inlineCode; + final QuillToolbarToggleStyleButtonOptions direction; + final QuillToolbarToggleStyleButtonOptions listNumbers; + final QuillToolbarToggleStyleButtonOptions listBullets; + final QuillToolbarToggleStyleButtonOptions codeBlock; + final QuillToolbarToggleStyleButtonOptions quote; + final QuillToolbarToggleCheckListButtonOptions toggleCheckList; + final QuillToolbarIndentButtonOptions indentIncrease; + final QuillToolbarIndentButtonOptions indentDecrease; + final QuillToolbarColorButtonOptions color; + final QuillToolbarColorButtonOptions backgroundColor; + final QuillToolbarClearFormatButtonOptions clearFormat; + + /// The reason we call this buttons in the end because this is responsible + /// for all the alignment buttons and not just one, you still + /// can customize the icons and tooltips + /// and you have child builder + final QuillToolbarSelectAlignmentButtonOptions selectAlignmentButtons; + + final QuillToolbarSearchButtonOptions search; + + /// The reason we call this buttons in the end because this is responsible + /// for all the header style buttons and not just one, you still + /// can customize it and you also have child builder + final QuillToolbarSelectHeaderStyleButtonsOptions selectHeaderStyleButtons; + + final QuillToolbarLinkStyleButtonOptions linkStyle; + final QuillToolbarLinkStyleButton2Options linkStyle2; + + final QuillToolbarCustomButtonOptions customButtons; + + @override + List get props => [ + base, + ]; +} diff --git a/lib/src/models/config/toolbar/toolbar_configurations.dart b/lib/src/models/config/toolbar/toolbar_configurations.dart index e4ccf4ad2..4e318c5c9 100644 --- a/lib/src/models/config/toolbar/toolbar_configurations.dart +++ b/lib/src/models/config/toolbar/toolbar_configurations.dart @@ -1,313 +1,18 @@ -import 'package:equatable/equatable.dart'; -import 'package:flutter/foundation.dart' show immutable; -import 'package:flutter/widgets.dart' - show Axis, Widget, WrapAlignment, WrapCrossAlignment; +import 'package:flutter/widgets.dart' show immutable; -import '../../../widgets/embeds.dart'; -import '../../themes/quill_dialog_theme.dart'; -import '../../themes/quill_icon_theme.dart'; -import 'buttons/base.dart'; -import 'buttons/clear_format.dart'; -import 'buttons/color.dart'; -import 'buttons/custom_button.dart'; -import 'buttons/font_family.dart'; -import 'buttons/font_size.dart'; -import 'buttons/history.dart'; -import 'buttons/indent.dart'; -import 'buttons/link_style.dart'; -import 'buttons/link_style2.dart'; -import 'buttons/search.dart'; -import 'buttons/select_alignment.dart'; -import 'buttons/select_header_style.dart'; -import 'buttons/toggle_check_list.dart'; -import 'buttons/toggle_style.dart'; +import '../../../widgets/toolbar/base_toolbar.dart'; import 'toolbar_shared_configurations.dart'; -export './../../../widgets/toolbar/buttons/search/search_dialog.dart'; -export './buttons/base.dart'; -export './buttons/clear_format.dart'; -export './buttons/color.dart'; -export './buttons/custom_button.dart'; -export './buttons/font_family.dart'; -export './buttons/font_size.dart'; -export './buttons/history.dart'; -export './buttons/indent.dart'; -export './buttons/link_style.dart'; -export './buttons/link_style2.dart'; -export './buttons/search.dart'; -export './buttons/select_alignment.dart'; -export './buttons/select_header_style.dart'; -export './buttons/toggle_check_list.dart'; -export './buttons/toggle_style.dart'; - -/// The default size of the icon of a button. -const double kDefaultIconSize = 18; - -/// The default size for the toolbar (width, height) -const double defaultToolbarSize = kDefaultIconSize * 2; - -/// The factor of how much larger the button is in relation to the icon. -const double kIconButtonFactor = 1.77; - -/// The horizontal margin between the contents of each toolbar section. -const double kToolbarSectionSpacing = 4; - -enum LinkStyleType { - /// Defines the original [QuillToolbarLinkStyleButton]. - original, - - /// Defines the alternative [QuillToolbarLinkStyleButton2]. - alternative; - - bool get isOriginal => this == LinkStyleType.original; - bool get isAlternative => this == LinkStyleType.alternative; -} - -/// The configurations for the toolbar widget of flutter quill @immutable class QuillToolbarConfigurations extends QuillSharedToolbarProperties { const QuillToolbarConfigurations({ - super.toolbarSectionSpacing = kToolbarSectionSpacing, - super.toolbarIconAlignment = WrapAlignment.center, - super.toolbarIconCrossAlignment = WrapCrossAlignment.center, - super.buttonOptions = const QuillToolbarButtonOptions(), - super.multiRowsDisplay = true, - this.fontSizesValues, - this.showDividers = true, - this.showFontFamily = true, - this.showFontSize = true, - this.showBoldButton = true, - this.showItalicButton = true, - this.showSmallButton = false, - this.showUnderLineButton = true, - this.showStrikeThrough = true, - this.showInlineCode = true, - this.showColorButton = true, - this.showBackgroundColorButton = true, - this.showClearFormat = true, - this.showAlignmentButtons = false, - this.showLeftAlignment = true, - this.showCenterAlignment = true, - this.showRightAlignment = true, - this.showJustifyAlignment = true, - this.showHeaderStyle = true, - this.showListNumbers = true, - this.showListBullets = true, - this.showListCheck = true, - this.showCodeBlock = true, - this.showQuote = true, - this.showIndent = true, - this.showLink = true, - this.showUndo = true, - this.showRedo = true, - this.showDirection = false, - this.showSearchButton = true, - this.showSubscript = true, - this.showSuperscript = true, - this.linkStyleType = LinkStyleType.original, - super.customButtons = const [], - - /// The decoration to use for the toolbar. - super.decoration, - - /// Toolbar items to display for controls of embed blocks - this.embedButtons, - super.linkDialogAction, - - ///The theme to use for the icons in the toolbar, uses type [QuillIconTheme] - // this.iconTheme, - this.dialogTheme, - super.axis = Axis.horizontal, - super.color, - super.sectionDividerColor, - super.sectionDividerSpace, - this.spacerWidget, - - /// By default it will calculated based on the [globalIconSize] from - /// [base] in [QuillToolbarButtonOptions] - /// You can change it but the the change only apply if - /// the [multiRowsDisplay] is false, if [multiRowsDisplay] then the value - /// will be [kDefaultIconSize] * 2 - super.toolbarSize, - }) : _toolbarSize = toolbarSize; - - final double? _toolbarSize; - - /// The toolbar size, by default it will be `baseButtonOptions.iconSize * 2` - @override - double get toolbarSize { - final alternativeToolbarSize = _toolbarSize; - if (alternativeToolbarSize != null) { - return alternativeToolbarSize; - } - return buttonOptions.base.globalIconSize * 2; - } - - /// A widget that will placed between each button in the toolbar - /// can be used as a spacer - /// it will not used before the first button - /// it will not used after the last button - /// it will also not used in the toolbar dividers - /// Default value will be [SizedBox.shrink()] - /// some widgets like the header styles will be considered as one widget - final Widget? spacerWidget; + super.sharedConfigurations, - /// By default it will be - /// ``` - /// { - /// 'Small'.i18n: 'small', - /// 'Large'.i18n: 'large', - /// 'Huge'.i18n: 'huge', - /// 'Clear'.loc: '0' - /// } - /// ``` - final Map? fontSizesValues; - - final bool showDividers; - final bool showFontFamily; - final bool showFontSize; - final bool showBoldButton; - final bool showItalicButton; - final bool showSmallButton; - final bool showUnderLineButton; - final bool showStrikeThrough; - final bool showInlineCode; - final bool showColorButton; - final bool showBackgroundColorButton; - final bool showClearFormat; - final bool showAlignmentButtons; - final bool showLeftAlignment; - final bool showCenterAlignment; - final bool showRightAlignment; - final bool showJustifyAlignment; - final bool showHeaderStyle; - final bool showListNumbers; - final bool showListBullets; - final bool showListCheck; - final bool showCodeBlock; - final bool showQuote; - final bool showIndent; - final bool showLink; - final bool showUndo; - final bool showRedo; - final bool showDirection; - final bool showSearchButton; - final bool showSubscript; - final bool showSuperscript; - - /// Toolbar items to display for controls of embed blocks - final List? embedButtons; - - // ///The theme to use for the icons in the toolbar, uses type [QuillIconTheme] - // final QuillIconTheme? iconTheme; - - ///The theme to use for the theming of the [LinkDialog()], - ///shown when embedding an image, for example - final QuillDialogTheme? dialogTheme; - - /// Defines which dialog is used for applying link attribute. - final LinkStyleType linkStyleType; - - @override - List get props => [ - buttonOptions, - multiRowsDisplay, - fontSizesValues, - toolbarSize, - axis, - ]; -} - -/// The configurations for the buttons of the toolbar widget of flutter quill -@immutable -class QuillToolbarButtonOptions extends Equatable { - const QuillToolbarButtonOptions({ - this.base = const QuillToolbarBaseButtonOptions(), - this.undoHistory = const QuillToolbarHistoryButtonOptions( - isUndo: true, - ), - this.redoHistory = const QuillToolbarHistoryButtonOptions( - isUndo: false, - ), - this.fontFamily = const QuillToolbarFontFamilyButtonOptions(), - this.fontSize = const QuillToolbarFontSizeButtonOptions(), - this.bold = const QuillToolbarToggleStyleButtonOptions(), - this.subscript = const QuillToolbarToggleStyleButtonOptions(), - this.superscript = const QuillToolbarToggleStyleButtonOptions(), - this.italic = const QuillToolbarToggleStyleButtonOptions(), - this.small = const QuillToolbarToggleStyleButtonOptions(), - this.underLine = const QuillToolbarToggleStyleButtonOptions(), - this.strikeThrough = const QuillToolbarToggleStyleButtonOptions(), - this.inlineCode = const QuillToolbarToggleStyleButtonOptions(), - this.direction = const QuillToolbarToggleStyleButtonOptions(), - this.listNumbers = const QuillToolbarToggleStyleButtonOptions(), - this.listBullets = const QuillToolbarToggleStyleButtonOptions(), - this.codeBlock = const QuillToolbarToggleStyleButtonOptions(), - this.quote = const QuillToolbarToggleStyleButtonOptions(), - this.toggleCheckList = const QuillToolbarToggleCheckListButtonOptions(), - this.indentIncrease = const QuillToolbarIndentButtonOptions(), - this.indentDecrease = const QuillToolbarIndentButtonOptions(), - this.color = const QuillToolbarColorButtonOptions(), - this.backgroundColor = const QuillToolbarColorButtonOptions(), - this.clearFormat = const QuillToolbarClearFormatButtonOptions(), - this.selectAlignmentButtons = - const QuillToolbarSelectAlignmentButtonOptions(), - this.search = const QuillToolbarSearchButtonOptions(), - this.selectHeaderStyleButtons = - const QuillToolbarSelectHeaderStyleButtonsOptions(), - this.linkStyle = const QuillToolbarLinkStyleButtonOptions(), - this.linkStyle2 = const QuillToolbarLinkStyleButton2Options(), - this.customButtons = const QuillToolbarCustomButtonOptions(), + /// Note this only used when you using the quill toolbar buttons like + /// `QuillToolbarHistoryButton` inside it + super.buttonOptions = const QuillToolbarButtonOptions(), }); - /// The base configurations for all the buttons which will apply to all - /// but if the options overrided in the spesefic button options - /// then it will use that instead - final QuillToolbarBaseButtonOptions base; - final QuillToolbarHistoryButtonOptions undoHistory; - final QuillToolbarHistoryButtonOptions redoHistory; - final QuillToolbarFontFamilyButtonOptions fontFamily; - final QuillToolbarFontSizeButtonOptions fontSize; - final QuillToolbarToggleStyleButtonOptions bold; - final QuillToolbarToggleStyleButtonOptions subscript; - final QuillToolbarToggleStyleButtonOptions superscript; - final QuillToolbarToggleStyleButtonOptions italic; - final QuillToolbarToggleStyleButtonOptions small; - final QuillToolbarToggleStyleButtonOptions underLine; - final QuillToolbarToggleStyleButtonOptions strikeThrough; - final QuillToolbarToggleStyleButtonOptions inlineCode; - final QuillToolbarToggleStyleButtonOptions direction; - final QuillToolbarToggleStyleButtonOptions listNumbers; - final QuillToolbarToggleStyleButtonOptions listBullets; - final QuillToolbarToggleStyleButtonOptions codeBlock; - final QuillToolbarToggleStyleButtonOptions quote; - final QuillToolbarToggleCheckListButtonOptions toggleCheckList; - final QuillToolbarIndentButtonOptions indentIncrease; - final QuillToolbarIndentButtonOptions indentDecrease; - final QuillToolbarColorButtonOptions color; - final QuillToolbarColorButtonOptions backgroundColor; - final QuillToolbarClearFormatButtonOptions clearFormat; - - /// The reason we call this buttons in the end because this is responsible - /// for all the alignment buttons and not just one, you still - /// can customize the icons and tooltips - /// and you have child builder - final QuillToolbarSelectAlignmentButtonOptions selectAlignmentButtons; - - final QuillToolbarSearchButtonOptions search; - - /// The reason we call this buttons in the end because this is responsible - /// for all the header style buttons and not just one, you still - /// can customize it and you also have child builder - final QuillToolbarSelectHeaderStyleButtonsOptions selectHeaderStyleButtons; - - final QuillToolbarLinkStyleButtonOptions linkStyle; - final QuillToolbarLinkStyleButton2Options linkStyle2; - - final QuillToolbarCustomButtonOptions customButtons; - @override - List get props => [ - base, - ]; + List get props => []; } diff --git a/lib/src/models/config/toolbar/toolbar_shared_configurations.dart b/lib/src/models/config/toolbar/toolbar_shared_configurations.dart index d563f4dfd..aff603a52 100644 --- a/lib/src/models/config/toolbar/toolbar_shared_configurations.dart +++ b/lib/src/models/config/toolbar/toolbar_shared_configurations.dart @@ -4,16 +4,17 @@ import 'package:flutter/widgets.dart' import '../../../widgets/toolbar/base_toolbar.dart'; import '../../structs/link_dialog_action.dart'; +import '../quill_shared_configurations.dart'; abstract class QuillSharedToolbarProperties extends Equatable { const QuillSharedToolbarProperties({ + this.sharedConfigurations = const QuillSharedConfigurations(), this.toolbarSize, this.axis = Axis.horizontal, this.toolbarSectionSpacing = kToolbarSectionSpacing, this.toolbarIconAlignment = WrapAlignment.center, this.toolbarIconCrossAlignment = WrapCrossAlignment.center, this.color, - this.customButtons = const [], this.sectionDividerColor, this.sectionDividerSpace, this.linkDialogAction, @@ -36,9 +37,6 @@ abstract class QuillSharedToolbarProperties extends Equatable { /// is given. final Color? color; - /// List of custom buttons - final List customButtons; - /// The color to use when painting the toolbar section divider. /// /// If this is null, then the [DividerThemeData.color] is used. If that is @@ -57,4 +55,6 @@ abstract class QuillSharedToolbarProperties extends Equatable { /// If you want change spesefic buttons or all of them /// then you came to the right place final QuillToolbarButtonOptions buttonOptions; + + final QuillSharedConfigurations sharedConfigurations; } diff --git a/lib/src/models/documents/document.dart b/lib/src/models/documents/document.dart index 4649765ff..e0efbdf8e 100644 --- a/lib/src/models/documents/document.dart +++ b/lib/src/models/documents/document.dart @@ -1,6 +1,6 @@ import 'dart:async'; -import '../../widgets/embeds.dart'; +import '../../widgets/quill/embeds.dart'; import '../quill_delta.dart'; import '../rules/rule.dart'; import '../structs/doc_change.dart'; @@ -402,6 +402,7 @@ class Document { throw ArgumentError.value(doc, 'Document Delta cannot be empty.'); } + // print(doc.last.data.runtimeType); assert((doc.last.data as String).endsWith('\n')); var offset = 0; diff --git a/lib/src/models/documents/nodes/container.dart b/lib/src/models/documents/nodes/container.dart index 605e472e4..f5f1900d3 100644 --- a/lib/src/models/documents/nodes/container.dart +++ b/lib/src/models/documents/nodes/container.dart @@ -1,6 +1,6 @@ import 'dart:collection'; -import '../../../widgets/embeds.dart'; +import '../../../widgets/quill/embeds.dart'; import '../style.dart'; import 'leaf.dart'; import 'line.dart'; diff --git a/lib/src/models/documents/nodes/leaf.dart b/lib/src/models/documents/nodes/leaf.dart index 4f1fd3b0f..115f7058b 100644 --- a/lib/src/models/documents/nodes/leaf.dart +++ b/lib/src/models/documents/nodes/leaf.dart @@ -1,6 +1,6 @@ import 'dart:math' as math; -import '../../../widgets/embeds.dart'; +import '../../../widgets/quill/embeds.dart'; import '../../quill_delta.dart'; import '../style.dart'; import 'embeddable.dart'; diff --git a/lib/src/models/documents/nodes/line.dart b/lib/src/models/documents/nodes/line.dart index 75dfbde06..d202ba08a 100644 --- a/lib/src/models/documents/nodes/line.dart +++ b/lib/src/models/documents/nodes/line.dart @@ -2,7 +2,7 @@ import 'dart:math' as math; import 'package:collection/collection.dart'; -import '../../../widgets/embeds.dart'; +import '../../../widgets/quill/embeds.dart'; import '../../quill_delta.dart'; import '../../structs/offset_value.dart'; import '../attribute.dart'; diff --git a/lib/src/models/documents/nodes/node.dart b/lib/src/models/documents/nodes/node.dart index c196b47f8..3b78603c6 100644 --- a/lib/src/models/documents/nodes/node.dart +++ b/lib/src/models/documents/nodes/node.dart @@ -1,6 +1,6 @@ import 'dart:collection'; -import '../../../widgets/embeds.dart'; +import '../../../widgets/quill/embeds.dart'; import '../../quill_delta.dart'; import '../attribute.dart'; import '../style.dart'; diff --git a/lib/src/models/rules/insert.dart b/lib/src/models/rules/insert.dart index daf3c1100..f57cb910b 100644 --- a/lib/src/models/rules/insert.dart +++ b/lib/src/models/rules/insert.dart @@ -1,5 +1,6 @@ import 'package:meta/meta.dart' show immutable; +import '../../extensions/uri_ext.dart'; import '../../models/documents/document.dart'; import '../documents/attribute.dart'; import '../documents/nodes/embeddable.dart'; @@ -520,8 +521,7 @@ class AutoFormatLinksRule extends InsertRule { try { final cand = (prev.data as String).split('\n').last.split(' ').last; final link = Uri.parse(cand); - // TODO: Can be improved a little - if (!['https', 'http'].contains(link.scheme)) { + if (!link.isHttpBasedUrl()) { return null; } final attributes = prev.attributes ?? {}; diff --git a/lib/src/packages/quill_markdown/custom_quill_attributes.dart b/lib/src/packages/quill_markdown/custom_quill_attributes.dart new file mode 100644 index 000000000..fef8a953e --- /dev/null +++ b/lib/src/packages/quill_markdown/custom_quill_attributes.dart @@ -0,0 +1,11 @@ +import '../../../flutter_quill.dart'; + +/// Custom attribute to save the language of codeblock +class CodeBlockLanguageAttribute extends Attribute { + /// @nodoc + const CodeBlockLanguageAttribute(String? value) + : super(attrKey, AttributeScope.ignore, value); + + /// attribute key + static const attrKey = 'x-md-codeblock-lang'; +} diff --git a/lib/src/packages/quill_markdown/delta_to_markdown.dart b/lib/src/packages/quill_markdown/delta_to_markdown.dart new file mode 100644 index 000000000..ba1518b7d --- /dev/null +++ b/lib/src/packages/quill_markdown/delta_to_markdown.dart @@ -0,0 +1,359 @@ +import 'dart:convert'; +import 'dart:ui'; + +import 'package:collection/collection.dart'; +import '../../../flutter_quill.dart'; +import './custom_quill_attributes.dart'; +import './utils.dart'; + +class _AttributeHandler { + _AttributeHandler({ + this.beforeContent, + this.afterContent, + }); + + final void Function( + Attribute attribute, + Node node, + StringSink output, + )? beforeContent; + + final void Function( + Attribute attribute, + Node node, + StringSink output, + )? afterContent; +} + +/// Outputs [Embed] element as markdown. +typedef EmbedToMarkdown = void Function(Embed embed, StringSink out); + +extension on Object? { + T? asNullable() { + final self = this; + return self == null ? null : self as T; + } +} + +/// Convertor from [Delta] to quill Markdown string. +class DeltaToMarkdown extends Converter + implements _NodeVisitor { + /// + DeltaToMarkdown({ + Map? customEmbedHandlers, + }) { + if (customEmbedHandlers != null) { + _embedHandlers.addAll(customEmbedHandlers); + } + } + + @override + String convert(Delta input) { + final newDelta = transform(input); + + final quillDocument = Document.fromDelta(newDelta); + + final outBuffer = quillDocument.root.accept(this); + + return outBuffer.toString(); + } + + final Map _blockAttrsHandlers = { + Attribute.codeBlock.key: _AttributeHandler( + beforeContent: (attribute, node, output) { + var infoString = ''; + if (node.containsAttr(CodeBlockLanguageAttribute.attrKey)) { + infoString = node.getAttrValueOr( + CodeBlockLanguageAttribute.attrKey, + '', + ); + } + if (infoString.isEmpty) { + final linesWithLang = (node as Block).children.where((child) => + child.containsAttr(CodeBlockLanguageAttribute.attrKey)); + if (linesWithLang.isNotEmpty) { + infoString = linesWithLang.first.getAttrValueOr( + CodeBlockLanguageAttribute.attrKey, + 'or', + ); + } + } + + output.writeln('```$infoString'); + }, + afterContent: (attribute, node, output) => output.writeln('```'), + ), + }; + + final Map _lineAttrsHandlers = { + Attribute.header.key: _AttributeHandler( + beforeContent: (attribute, node, output) { + output + ..write('#' * (attribute.value.asNullable() ?? 1)) + ..write(' '); + }, + ), + Attribute.blockQuote.key: _AttributeHandler( + beforeContent: (attribute, node, output) => output.write('> '), + ), + Attribute.list.key: _AttributeHandler( + beforeContent: (attribute, node, output) { + final indentLevel = node.getAttrValueOr(Attribute.indent.key, 0); + final isNumbered = attribute.value == 'ordered'; + output + ..write((isNumbered ? ' ' : ' ') * indentLevel) + ..write('${isNumbered ? '1.' : '-'} '); + }, + ), + }; + + final Map _textAttrsHandlers = { + Attribute.italic.key: _AttributeHandler( + beforeContent: (attribute, node, output) { + if (node.previous?.containsAttr(attribute.key) != true) { + output.write('_'); + } + }, + afterContent: (attribute, node, output) { + if (node.next?.containsAttr(attribute.key) != true) { + output.write('_'); + } + }, + ), + Attribute.bold.key: _AttributeHandler( + beforeContent: (attribute, node, output) { + if (node.previous?.containsAttr(attribute.key) != true) { + output.write('**'); + } + }, + afterContent: (attribute, node, output) { + if (node.next?.containsAttr(attribute.key) != true) { + output.write('**'); + } + }, + ), + Attribute.strikeThrough.key: _AttributeHandler( + beforeContent: (attribute, node, output) { + if (node.previous?.containsAttr(attribute.key) != true) { + output.write('~~'); + } + }, + afterContent: (attribute, node, output) { + if (node.next?.containsAttr(attribute.key) != true) { + output.write('~~'); + } + }, + ), + Attribute.inlineCode.key: _AttributeHandler( + beforeContent: (attribute, node, output) { + if (node.previous?.containsAttr(attribute.key) != true) { + output.write('`'); + } + }, + afterContent: (attribute, node, output) { + if (node.next?.containsAttr(attribute.key) != true) { + output.write('`'); + } + }, + ), + Attribute.link.key: _AttributeHandler( + beforeContent: (attribute, node, output) { + if (node.previous?.containsAttr(attribute.key, attribute.value) != + true) { + output.write('['); + } + }, + afterContent: (attribute, node, output) { + if (node.next?.containsAttr(attribute.key, attribute.value) != true) { + output.write('](${attribute.value.asNullable() ?? ''})'); + } + }, + ), + }; + + final Map _embedHandlers = { + BlockEmbed.imageType: (embed, out) => out.write('![](${embed.value.data})'), + horizontalRuleType: (embed, out) { + // adds new line after it + // make --- separated so it doesn't get rendered as header + out.writeln('- - -'); + }, + }; + + @override + StringSink visitRoot(Root root, [StringSink? output]) { + final out = output ??= StringBuffer(); + for (final container in root.children) { + container.accept(this, out); + } + return out; + } + + @override + StringSink visitBlock(Block block, [StringSink? output]) { + final out = output ??= StringBuffer(); + _handleAttribute(_blockAttrsHandlers, block, output, () { + for (final line in block.children) { + line.accept(this, out); + } + }); + return out; + } + + @override + StringSink visitLine(Line line, [StringSink? output]) { + final out = output ??= StringBuffer(); + final style = line.style; + _handleAttribute(_lineAttrsHandlers, line, output, () { + for (final leaf in line.children) { + leaf.accept(this, out); + } + }); + if (style.isEmpty || + style.values.every((item) => item.scope != AttributeScope.block)) { + out.writeln(); + } + if (style.containsKey(Attribute.list.key) && + line.nextLine?.style.containsKey(Attribute.list.key) != true) { + out.writeln(); + } + out.writeln(); + return out; + } + + @override + StringSink visitText(QuillText text, [StringSink? output]) { + final out = output ??= StringBuffer(); + final style = text.style; + _handleAttribute( + _textAttrsHandlers, + text, + output, + () { + var content = text.value; + if (!(style.containsKey(Attribute.codeBlock.key) || + style.containsKey(Attribute.inlineCode.key) || + (text.parent?.style.containsKey(Attribute.codeBlock.key) ?? + false))) { + content = content.replaceAllMapped( + RegExp(r'[\\\`\*\_\{\}\[\]\(\)\#\+\-\.\!\>\<]'), (match) { + return '\\${match[0]}'; + }); + } + out.write(content); + }, + sortedAttrsBySpan: true, + ); + return out; + } + + @override + StringSink visitEmbed(Embed embed, [StringSink? output]) { + final out = output ??= StringBuffer(); + + final type = embed.value.type; + + _embedHandlers[type]!.call(embed, out); + + return out; + } + + void _handleAttribute( + Map handlers, + Node node, + StringSink output, + VoidCallback contentHandler, { + bool sortedAttrsBySpan = false, + }) { + final attrs = sortedAttrsBySpan + ? node.attrsSortedByLongestSpan() + : node.style.attributes.values.toList(); + final handlersToUse = attrs + .where((attr) => handlers.containsKey(attr.key)) + .map((attr) => MapEntry(attr.key, handlers[attr.key]!)) + .toList(); + for (final handlerEntry in handlersToUse) { + handlerEntry.value.beforeContent?.call( + node.style.attributes[handlerEntry.key]!, + node, + output, + ); + } + contentHandler(); + for (final handlerEntry in handlersToUse.reversed) { + handlerEntry.value.afterContent?.call( + node.style.attributes[handlerEntry.key]!, + node, + output, + ); + } + } +} + +//// AST with visitor + +abstract class _NodeVisitor { + const _NodeVisitor._(); + + T visitRoot(Root root, [T? context]); + + T visitBlock(Block block, [T? context]); + + T visitLine(Line line, [T? context]); + + T visitText(QuillText text, [T? context]); + + T visitEmbed(Embed embed, [T? context]); +} + +extension _NodeX on Node { + T accept(_NodeVisitor visitor, [T? context]) { + switch (runtimeType) { + case Root _: + return visitor.visitRoot(this as Root, context); + case Block _: + return visitor.visitBlock(this as Block, context); + case Line _: + return visitor.visitLine(this as Line, context); + case QuillText _: + return visitor.visitText(this as QuillText, context); + case Embed _: + return visitor.visitEmbed(this as Embed, context); + } + throw Exception('Container of type $runtimeType cannot be visited'); + } + + bool containsAttr(String attributeKey, [Object? value]) { + if (!style.containsKey(attributeKey)) { + return false; + } + if (value == null) { + return true; + } + return style.attributes[attributeKey]!.value == value; + } + + T getAttrValueOr(String attributeKey, T or) { + final attrs = style.attributes; + final attrValue = attrs[attributeKey]?.value as T?; + return attrValue ?? or; + } + + List> attrsSortedByLongestSpan() { + final attrCount = , int>{}; + var node = this; + // get the first node + while (node.previous != null) { + node = node.previous!; + node.style.attributes.forEach((key, value) { + attrCount[value] = (attrCount[value] ?? 0) + 1; + }); + node = node.next!; + } + + final attrs = style.attributes.values.sorted( + (attr1, attr2) => attrCount[attr2]!.compareTo(attrCount[attr1]!)); + + return attrs; + } +} diff --git a/lib/src/packages/quill_markdown/embeddable_table_syntax.dart b/lib/src/packages/quill_markdown/embeddable_table_syntax.dart new file mode 100644 index 000000000..d6375cd4e --- /dev/null +++ b/lib/src/packages/quill_markdown/embeddable_table_syntax.dart @@ -0,0 +1,117 @@ +import 'package:charcode/charcode.dart'; +import 'package:markdown/markdown.dart'; + +import '../../../flutter_quill.dart' hide Node; + +/// Parses markdown table and saves the table markdown content into the element attributes. +class EmbeddableTableSyntax extends BlockSyntax { + /// @nodoc + const EmbeddableTableSyntax(); + static const _base = TableSyntax(); + + @override + bool canEndBlock(BlockParser parser) => false; + + @override + RegExp get pattern => _base.pattern; + + @override + bool canParse(BlockParser parser) => _base.canParse(parser); + + /// Parses a table into its three parts: + /// + /// * a head row of head cells (`` cells) + /// * a divider of hyphens and pipes (not rendered) + /// * many body rows of body cells (`` cells) + @override + Node? parse(BlockParser parser) { + final columnCount = _columnCount(parser.next!.content); + final headCells = _columnCount(parser.current.content); + final valBuf = + StringBuffer('${parser.current.content}\n${parser.next!.content}'); + parser.advance(); + if (columnCount != headCells) { + return null; + } + + // advance header and divider of hyphens. + parser.advance(); + + while (!parser.isDone && !BlockSyntax.isAtBlockEnd(parser)) { + valBuf.write('\n${parser.current.content}'); + parser.advance(); + } + + return Element.empty(EmbeddableTable.tableType) + ..attributes['data'] = valBuf.toString(); + } + + int _columnCount(String line) { + final startIndex = _walkPastOpeningPipe(line); + + var endIndex = line.length - 1; + while (endIndex > 0) { + final ch = line.codeUnitAt(endIndex); + if (ch == $pipe) { + endIndex--; + break; + } + if (ch != $space && ch != $tab) { + break; + } + endIndex--; + } + + return line.substring(startIndex, endIndex + 1).split('|').length; + } + + int _walkPastWhitespace(String line, int index) { + while (index < line.length) { + final ch = line.codeUnitAt(index); + if (ch != $space && ch != $tab) { + break; + } + //ignore: parameter_assignments + index++; + } + return index; + } + + int _walkPastOpeningPipe(String line) { + var index = 0; + while (index < line.length) { + final ch = line.codeUnitAt(index); + if (ch == $pipe) { + index++; + index = _walkPastWhitespace(line, index); + } + if (ch != $space && ch != $tab) { + // No leading pipe. + break; + } + index++; + } + return index; + } +} + +/// An [Embeddable] table that can used to render a table in quill_editor +class EmbeddableTable extends BlockEmbed { + /// @nodoc + EmbeddableTable(String data) : super(tableType, data); + + /// [Embeddable] type + static const tableType = 'x-embed-table'; + + /// Create from markdown. + //ignore: prefer_constructors_over_static_methods + static EmbeddableTable fromMdSyntax(Map attributes) => + EmbeddableTable(attributes['data']!); + + /// Outputs table markdown to output. + static void toMdSyntax(Embed embed, StringSink out) { + out + ..writeln(embed.value.data) + ..writeln(); + } +} diff --git a/lib/src/packages/quill_markdown/markdown_to_delta.dart b/lib/src/packages/quill_markdown/markdown_to_delta.dart new file mode 100644 index 000000000..76f5b4ba0 --- /dev/null +++ b/lib/src/packages/quill_markdown/markdown_to_delta.dart @@ -0,0 +1,424 @@ +import 'dart:collection'; +import 'dart:convert'; + +import 'package:collection/collection.dart'; +import 'package:markdown/markdown.dart' as md; + +import '../../../flutter_quill.dart'; +import './custom_quill_attributes.dart'; +import './embeddable_table_syntax.dart'; +import './utils.dart'; + +/// Converts markdown [md.Element] to list of [Attribute]. +typedef ElementToAttributeConvertor = List> Function( + md.Element element, +); + +/// Converts markdown [md.Element] to [Embeddable]. +typedef ElementToEmbeddableConvertor = Embeddable Function( + Map elAttrs, +); + +/// Convertor from Markdown string to quill [Delta]. +class MarkdownToDelta extends Converter + implements md.NodeVisitor { + /// + MarkdownToDelta({ + required this.markdownDocument, + this.customElementToInlineAttribute = const {}, + this.customElementToBlockAttribute = const {}, + this.customElementToEmbeddable = const {}, + this.softLineBreak = false, + }); + + final md.Document markdownDocument; + final Map customElementToInlineAttribute; + final Map customElementToBlockAttribute; + final Map customElementToEmbeddable; + final bool softLineBreak; + + // final _blockTags = [ + // 'p', + // 'h1', + // 'h2', + // 'h3', + // 'h4', + // 'h5', + // 'h6', + // 'li', + // 'blockquote', + // 'pre', + // 'ol', + // 'ul', + // 'hr', + // 'table', + // 'thead', + // 'tbody', + // 'tr' + // ]; + + final _elementToBlockAttr = { + 'ul': (_) => [Attribute.ul], + 'ol': (_) => [Attribute.ol], + 'pre': (element) { + final codeChild = element.children!.first as md.Element; + final language = (codeChild.attributes['class'] ?? '') + .split(' ') + .where((class_) => class_.startsWith('language-')) + .firstOrNull + ?.split('-') + .lastOrNull; + return [ + Attribute.codeBlock, + if (language != null) CodeBlockLanguageAttribute(language), + ]; + }, + 'blockquote': (_) => [Attribute.blockQuote], + 'h1': (_) => [Attribute.h1], + 'h2': (_) => [Attribute.h2], + 'h3': (_) => [Attribute.h3], + }; + + final _elementToInlineAttr = { + 'em': (_) => [Attribute.italic], + 'strong': (_) => [Attribute.bold], + 'del': (_) => [Attribute.strikeThrough], + 'a': (element) => [LinkAttribute(element.attributes['href'])], + 'code': (_) => [Attribute.inlineCode], + }; + + final _elementToEmbed = { + 'hr': (_) => horizontalRule, + 'img': (elAttrs) => BlockEmbed.image(elAttrs['src'] ?? ''), + }; + + var _delta = Delta(); + final _activeInlineAttributes = Queue>>(); + final _activeBlockAttributes = Queue>>(); + final _topLevelNodes = []; + bool _isInBlockQuote = false; + bool _isInCodeblock = false; + bool _justPreviousBlockExit = false; + String? _lastTag; + String? _currentBlockTag; + int _listItemIndent = -1; + + @override + Delta convert(String input) { + _delta = Delta(); + _activeInlineAttributes.clear(); + _activeBlockAttributes.clear(); + _topLevelNodes.clear(); + _lastTag = null; + _currentBlockTag = null; + _isInBlockQuote = false; + _isInCodeblock = false; + _justPreviousBlockExit = false; + _listItemIndent = -1; + + final lines = const LineSplitter().convert(input); + final mdNodes = markdownDocument.parseLines(lines); + + _topLevelNodes.addAll(mdNodes); + + for (final node in mdNodes) { + node.accept(this); + } + + // Ensure the delta ends with a newline. + _appendLastNewLineIfNeeded(); + + return _delta; + } + + void _appendLastNewLineIfNeeded() { + if (_delta.isEmpty) return; + final dynamic lastValue = _delta.last.value; + if (!(lastValue is String && lastValue.endsWith('\n'))) { + _delta.insert('\n', _effectiveBlockAttrs()); + } + } + + @override + void visitText(md.Text text) { + String renderedText; + if (_isInBlockQuote) { + renderedText = text.text; + } else if (_isInCodeblock) { + renderedText = text.text.endsWith('\n') + ? text.text.substring(0, text.text.length - 1) + : text.text; + } else { + renderedText = _trimTextToMdSpec(text.text); + } + + if (renderedText.contains('\n')) { + var lines = renderedText.split('\n'); + if (renderedText.endsWith('\n')) { + lines = lines.sublist(0, lines.length - 1); + } + for (var i = 0; i < lines.length; i++) { + final isLastItem = i == lines.length - 1; + final line = lines[i]; + _delta.insert(line, _effectiveInlineAttrs()); + if (!isLastItem) { + _delta.insert('\n', _effectiveBlockAttrs()); + } + } + } else { + _delta.insert(renderedText, _effectiveInlineAttrs()); + } + _lastTag = null; + _justPreviousBlockExit = false; + } + + @override + bool visitElementBefore(md.Element element) { + _insertNewLineBeforeElementIfNeeded(element); + + final tag = element.tag; + _currentBlockTag ??= tag; + _lastTag = tag; + + if (_haveBlockAttrs(element)) { + _activeBlockAttributes.addLast(_toBlockAttributes(element)); + } + if (_haveInlineAttrs(element)) { + _activeInlineAttributes.addLast(_toInlineAttributes(element)); + } + + if (tag == 'blockquote') { + _isInBlockQuote = true; + } + + if (tag == 'pre') { + _isInCodeblock = true; + } + + if (tag == 'li') { + _listItemIndent++; + } + + return true; + } + + @override + void visitElementAfter(md.Element element) { + final tag = element.tag; + + if (_isEmbedElement(element)) { + _delta.insert(_toEmbeddable(element).toJson()); + } + + if (tag == 'br') { + _delta.insert('\n'); + } + + // exit block with new line + // hr need to be followed by new line + _insertNewLineAfterElementIfNeeded(element); + + if (tag == 'blockquote') { + _isInBlockQuote = false; + } + + if (tag == 'pre') { + _isInCodeblock = false; + } + + if (tag == 'li') { + _listItemIndent--; + } + + if (_haveBlockAttrs(element)) { + _activeBlockAttributes.removeLast(); + } + + if (_haveInlineAttrs(element)) { + _activeInlineAttributes.removeLast(); + } + + if (_currentBlockTag == tag) { + _currentBlockTag = null; + } + _lastTag = tag; + } + + void _insertNewLine() { + _delta.insert('\n', _effectiveBlockAttrs()); + } + + void _insertNewLineBeforeElementIfNeeded(md.Element element) { + if (!_isInBlockQuote && + _lastTag == 'blockquote' && + element.tag == 'blockquote') { + _insertNewLine(); + return; + } + + if (!_isInCodeblock && _lastTag == 'pre' && element.tag == 'pre') { + _insertNewLine(); + return; + } + + if (_listItemIndent >= 0 && (element.tag == 'ul' || element.tag == 'ol')) { + _insertNewLine(); + return; + } + } + + void _insertNewLineAfterElementIfNeeded(md.Element element) { + // TODO: refactor this to allow embeds to specify if they require + // new line after them + if (element.tag == 'hr' || element.tag == EmbeddableTable.tableType) { + // Always add new line after divider + _justPreviousBlockExit = true; + _insertNewLine(); + return; + } + + // if all the p children are embeddable add a new line + // example: images in a single line + if (element.tag == 'p' && + (element.children?.every( + (child) => child is md.Element && _isEmbedElement(child), + ) ?? + false)) { + _justPreviousBlockExit = true; + _insertNewLine(); + return; + } + + if (!_justPreviousBlockExit && + (_isTopLevelNode(element) || + _haveBlockAttrs(element) || + element.tag == 'li')) { + _justPreviousBlockExit = true; + _insertNewLine(); + return; + } + } + + bool _isTopLevelNode(md.Node node) => _topLevelNodes.contains(node); + + Map? _effectiveBlockAttrs() { + if (_activeBlockAttributes.isEmpty) return null; + final attrsRespectingExclusivity = >[ + if (_listItemIndent > 0) IndentAttribute(level: _listItemIndent), + ]; + + for (final attr in _activeBlockAttributes.expand((e) => e)) { + final isExclusiveAttr = Attribute.exclusiveBlockKeys.contains( + attr.key, + ); + final isThereAlreadyExclusiveAttr = attrsRespectingExclusivity.any( + (element) => Attribute.exclusiveBlockKeys.contains(element.key), + ); + + if (!(isExclusiveAttr && isThereAlreadyExclusiveAttr)) { + attrsRespectingExclusivity.add(attr); + } + } + + return { + for (final a in attrsRespectingExclusivity) ...a.toJson(), + }; + } + + Map? _effectiveInlineAttrs() { + if (_activeInlineAttributes.isEmpty) return null; + return { + for (final attrs in _activeInlineAttributes) + for (final a in attrs) ...a.toJson(), + }; + } + + // Define trim text function to remove spaces from text elements in + // accordance with Markdown specifications. + String _trimTextToMdSpec(String text) { + var result = text; + // The leading spaces pattern is used to identify spaces + // at the beginning of a line of text. + final leadingSpacesPattern = RegExp('^ *'); + + // The soft line break is used to identify the spaces at the end of a line + // of text and the leading spaces in the immediately following the line + // of text. These spaces are removed in accordance with the Markdown + // specification on soft line breaks when lines of text are joined. + final softLineBreak = RegExp(r' ?\n *'); + + // Leading spaces following a hard line break are ignored. + // https://github.github.com/gfm/#example-657 + if (const ['p', 'ol', 'li', 'br'].contains(_lastTag)) { + result = result.replaceAll(leadingSpacesPattern, ''); + } + + if (softLineBreak.hasMatch(result)) { + return result; + } + return result.replaceAll(softLineBreak, ' '); + } + + Map _effectiveElementToInlineAttr() { + return { + ...customElementToInlineAttribute, + ..._elementToInlineAttr, + }; + } + + bool _haveInlineAttrs(md.Element element) { + if (_isInCodeblock && element.tag == 'code') return false; + return _effectiveElementToInlineAttr().containsKey(element.tag); + } + + List> _toInlineAttributes(md.Element element) { + List>? result; + if (!(_isInCodeblock && element.tag == 'code')) { + result = _effectiveElementToInlineAttr()[element.tag]?.call(element); + } + if (result == null) { + throw Exception( + 'Element $element cannot be converted to inline attribute'); + } + return result; + } + + Map _effectiveElementToBlockAttr() { + return { + ...customElementToBlockAttribute, + ..._elementToBlockAttr, + }; + } + + bool _haveBlockAttrs(md.Element element) { + return _effectiveElementToBlockAttr().containsKey(element.tag); + } + + List> _toBlockAttributes(md.Element element) { + final result = _effectiveElementToBlockAttr()[element.tag]?.call(element); + if (result == null) { + throw Exception( + 'Element $element cannot be converted to block attribute'); + } + return result; + } + + Map _effectiveElementToEmbed() { + return { + ...customElementToEmbeddable, + ..._elementToEmbed, + }; + } + + bool _isEmbedElement(md.Element element) => + _effectiveElementToEmbed().containsKey(element.tag); + + Embeddable _toEmbeddable(md.Element element) { + final result = + _effectiveElementToEmbed()[element.tag]?.call(element.attributes); + if (result == null) { + throw Exception('Element $element cannot be converted to Embeddable'); + } + return result; + } +} diff --git a/lib/src/packages/quill_markdown/utils.dart b/lib/src/packages/quill_markdown/utils.dart new file mode 100644 index 000000000..ba4c6b859 --- /dev/null +++ b/lib/src/packages/quill_markdown/utils.dart @@ -0,0 +1,59 @@ +//ignore_for_file: cast_nullable_to_non_nullable +import '../../../flutter_quill.dart'; + +import './embeddable_table_syntax.dart'; + +/// To allow embedding images/videos in horizontal mode. +const BlockEmbed horizontalRule = BlockEmbed(horizontalRuleType, 'hr'); + +/// Necessary for [horizontalRule] BlockEmbed. +const String horizontalRuleType = 'divider'; + +/// Format the passed delta to ensure that there is new line +/// after embeds +Delta transform(Delta delta) { + final res = Delta(); + final ops = delta.toList(); + for (var i = 0; i < ops.length; i++) { + final op = ops[i]; + res.push(op); + autoAppendNewlineAfterEmbeddable(i, ops, op, res, [ + 'hr', + EmbeddableTable.tableType, + ]); + } + return res; +} + +/// Appends new line after embeds if needed +void autoAppendNewlineAfterEmbeddable( + int i, + List ops, + Operation op, + Delta res, + List types, +) { + final nextOpIsEmbed = i + 1 < ops.length && + ops[i + 1].isInsert && + ops[i + 1].data is Map && + types.any((type) => (ops[i + 1].data as Map).containsKey(type)); + + if (nextOpIsEmbed && + op.data is String && + (op.data as String).isNotEmpty && + !(op.data as String).endsWith('\n')) { + res.push(Operation.insert('\n')); + } + // embed could be image or video + final opInsertEmbed = op.isInsert && + op.data is Map && + types.any((type) => (op.data as Map).containsKey(type)); + final nextOpIsLineBreak = i + 1 < ops.length && + ops[i + 1].isInsert && + ops[i + 1].data is String && + (ops[i + 1].data as String).startsWith('\n'); + if (opInsertEmbed && (i + 1 == ops.length - 1 || !nextOpIsLineBreak)) { + // automatically append '\n' for embeddable + res.push(Operation.insert('\n')); + } +} diff --git a/lib/src/utils/embeds.dart b/lib/src/utils/embeds.dart index 7164f1d5a..4b3e2de38 100644 --- a/lib/src/utils/embeds.dart +++ b/lib/src/utils/embeds.dart @@ -2,7 +2,7 @@ import 'dart:math'; import '../models/documents/nodes/leaf.dart'; import '../models/structs/offset_value.dart'; -import '../widgets/controller.dart'; +import '../widgets/quill/quill_controller.dart'; OffsetValue getEmbedNode(QuillController controller, int offset) { var offset = controller.selection.start; diff --git a/lib/src/widgets/editor/editor.dart b/lib/src/widgets/editor/editor.dart index f0dfdf9a5..df8040e11 100644 --- a/lib/src/widgets/editor/editor.dart +++ b/lib/src/widgets/editor/editor.dart @@ -8,51 +8,23 @@ import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter/services.dart'; -import '../../extensions/quill_provider.dart'; import '../../l10n/widgets/localizations.dart'; -import '../../models/config/editor/configurations.dart'; -import '../../models/config/raw_editor/configurations.dart'; +import '../../models/config/editor/editor_configurations.dart'; +import '../../models/config/raw_editor/raw_editor_configurations.dart'; import '../../models/documents/document.dart'; import '../../models/documents/nodes/container.dart' as container_node; import '../../models/documents/nodes/leaf.dart'; -import '../../models/structs/offset_value.dart'; import '../../utils/platform.dart'; -import '../box.dart'; -import '../cursor.dart'; -import '../delegate.dart'; -import '../embeds.dart'; -import '../float_cursor.dart'; +import '../others/box.dart'; +import '../others/cursor.dart'; +import '../others/delegate.dart'; +import '../others/float_cursor.dart'; +import '../others/text_selection.dart'; +import '../quill/embeds.dart'; import '../raw_editor/raw_editor.dart'; -import '../text_selection.dart'; import '../utils/provider.dart'; import 'editor_builder.dart'; -/// Base interface for the editor state which defines contract used by -/// various mixins. -abstract class EditorState extends State - implements TextSelectionDelegate { - ScrollController get scrollController; - - RenderEditor get renderEditor; - - EditorTextSelectionOverlay? get selectionOverlay; - - List get pasteStyleAndEmbed; - - String get pastePlainText; - - /// Controls the floating cursor animation when it is released. - /// The floating cursor is animated to merge with the regular cursor. - AnimationController get floatingCursorResetController; - - /// Returns true if the editor has been marked as needing to be rebuilt. - bool get dirty; - - bool showToolbar(); - - void requestKeyboard(); -} - /// Base interface for editable render objects. abstract class RenderAbstractEditor implements TextLayoutMetrics { TextSelection selectWordAtPosition(TextPosition position); @@ -155,8 +127,7 @@ class QuillEditor extends StatefulWidget { factory QuillEditor.basic({ /// The configurations for the quill editor widget of flutter quill - QuillEditorConfigurations configurations = - const QuillEditorConfigurations(), + required QuillEditorConfigurations configurations, FocusNode? focusNode, ScrollController? scrollController, }) { @@ -257,13 +228,13 @@ class QuillEditorState extends State child: QuillRawEditor( key: _editorKey, configurations: QuillRawEditorConfigurations( - controller: context.requireQuillController, + controller: configurations.controller, focusNode: widget.focusNode, scrollController: widget.scrollController, scrollable: configurations.scrollable, scrollBottomInset: configurations.scrollBottomInset, padding: configurations.padding, - isReadOnly: configurations.readOnly, + readOnly: configurations.readOnly, placeholder: configurations.placeholder, onLaunchUrl: configurations.onLaunchUrl, contextMenuBuilder: showSelectionToolbar @@ -442,7 +413,7 @@ class _QuillEditorSelectionGestureDetectorBuilder } bool _isPositionSelected(TapUpDetails details) { - if (_state.context.requireQuillController.document.isEmpty()) { + if (_state.configurations.controller.document.isEmpty()) { return false; } final pos = renderEditor!.getPositionForOffset(details.globalPosition); @@ -1238,28 +1209,6 @@ class RenderEditor extends RenderEditableContainerBox @override Rect getLocalRectForCaret(TextPosition position) { final targetChild = childAtPosition(position); - // TODO: There is a bug here - // The provided text position is not in the current node - // 'package:flutter_quill/src/widgets/text_block.dart': - // text_block.dart:1 - // Failed assertion: line 604 pos 12: - // 'container.containsOffset(position.offset)' - // When the exception was thrown, this was the stack - // #2 RenderEditableTextBlock.globalToLocalPosition - // text_block.dart:604 - // #3 RenderEditor.getLocalRectForCaret - // editor.dart:1230 - // #4 RawEditorStateTextInputClientMixin._updateComposingRectIfNeeded - // raw_editor_state_text_input_client_mixin.dart:85 - // #5 RawEditorStateTextInputClientMixin.openConnectionIfNeeded - // raw_editor_state_text_input_client_mixin.dart:70 - // #6 RawEditorState.requestKeyboard - // raw_editor.dart:1428 - // #7 QuillEditorState._requestKeyboard - // editor.dart:379 - // #8 _QuillEditorSelectionGestureDetectorBuilder.onSingleTapUp - // editor.dart:538 - // #9 _EditorTextSelectionGestureDetectorState._handleTapUp final localPosition = targetChild.globalToLocalPosition(position); final childLocalRect = targetChild.getLocalRectForCaret(localPosition); diff --git a/lib/src/widgets/box.dart b/lib/src/widgets/others/box.dart similarity index 98% rename from lib/src/widgets/box.dart rename to lib/src/widgets/others/box.dart index 8b4f5f007..85dc8dff0 100644 --- a/lib/src/widgets/box.dart +++ b/lib/src/widgets/others/box.dart @@ -1,6 +1,6 @@ import 'package:flutter/rendering.dart'; -import '../models/documents/nodes/container.dart'; +import '../../models/documents/nodes/container.dart'; /// A common interface to render boxes which represent a piece of rich text /// content. diff --git a/lib/src/widgets/cursor.dart b/lib/src/widgets/others/cursor.dart similarity index 99% rename from lib/src/widgets/cursor.dart rename to lib/src/widgets/others/cursor.dart index 929118b16..41513308b 100644 --- a/lib/src/widgets/cursor.dart +++ b/lib/src/widgets/others/cursor.dart @@ -2,7 +2,7 @@ import 'dart:async'; import 'package:flutter/widgets.dart'; -import '../utils/platform.dart'; +import '../../utils/platform.dart'; import 'box.dart'; /// Style properties of editing cursor. diff --git a/lib/src/widgets/default_styles.dart b/lib/src/widgets/others/default_styles.dart similarity index 97% rename from lib/src/widgets/default_styles.dart rename to lib/src/widgets/others/default_styles.dart index 5d5949f8b..575a52730 100644 --- a/lib/src/widgets/default_styles.dart +++ b/lib/src/widgets/others/default_styles.dart @@ -2,11 +2,11 @@ import 'dart:ui'; import 'package:flutter/material.dart'; -import '../models/documents/attribute.dart'; -import '../models/documents/style.dart'; -import '../models/structs/vertical_spacing.dart'; -import '../utils/platform.dart'; -import 'style_widgets/checkbox_point.dart'; +import '../../models/documents/attribute.dart'; +import '../../models/documents/style.dart'; +import '../../models/structs/vertical_spacing.dart'; +import '../../utils/platform.dart'; +import '../style_widgets/checkbox_point.dart'; class QuillStyles extends InheritedWidget { const QuillStyles({ diff --git a/lib/src/widgets/delegate.dart b/lib/src/widgets/others/delegate.dart similarity index 97% rename from lib/src/widgets/delegate.dart rename to lib/src/widgets/others/delegate.dart index 5d0216065..f22074abe 100644 --- a/lib/src/widgets/delegate.dart +++ b/lib/src/widgets/others/delegate.dart @@ -3,11 +3,12 @@ import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter/scheduler.dart'; -import '../models/documents/attribute.dart'; -import '../models/documents/nodes/leaf.dart'; -import '../utils/platform.dart'; -import 'editor/editor.dart'; -import 'embeds.dart'; +import '../../models/documents/attribute.dart'; +import '../../models/documents/nodes/leaf.dart'; +import '../../utils/platform.dart'; +import '../editor/editor.dart'; +import '../quill/embeds.dart'; +import '../raw_editor/raw_editor.dart'; import 'text_selection.dart'; typedef EmbedsBuilder = EmbedBuilder Function(Embed node); @@ -331,8 +332,6 @@ class EditorTextSelectionGestureDetectorBuilder { @protected void onDragSelectionEnd(DragEndDetails details) { renderEditor!.handleDragEnd(details); - // TODO: Should we care if the platform is desktop using native desktop app - // or the flutter app is running using web app?? if (isDesktop(supportWeb: true) && delegate.selectionEnabled && shouldShowSelectionToolbar) { diff --git a/lib/src/widgets/float_cursor.dart b/lib/src/widgets/others/float_cursor.dart similarity index 100% rename from lib/src/widgets/float_cursor.dart rename to lib/src/widgets/others/float_cursor.dart diff --git a/lib/src/widgets/keyboard_listener.dart b/lib/src/widgets/others/keyboard_listener.dart similarity index 100% rename from lib/src/widgets/keyboard_listener.dart rename to lib/src/widgets/others/keyboard_listener.dart diff --git a/lib/src/widgets/link.dart b/lib/src/widgets/others/link.dart similarity index 97% rename from lib/src/widgets/link.dart rename to lib/src/widgets/others/link.dart index 9e4ee354b..93d8c685e 100644 --- a/lib/src/widgets/link.dart +++ b/lib/src/widgets/others/link.dart @@ -2,9 +2,9 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; -import '../l10n/extensions/localizations.dart'; -import '../models/documents/attribute.dart'; -import '../models/documents/nodes/node.dart'; +import '../../l10n/extensions/localizations.dart'; +import '../../models/documents/attribute.dart'; +import '../../models/documents/nodes/node.dart'; const linkPrefixes = [ 'mailto:', // email diff --git a/lib/src/widgets/proxy.dart b/lib/src/widgets/others/proxy.dart similarity index 98% rename from lib/src/widgets/proxy.dart rename to lib/src/widgets/others/proxy.dart index 698497f44..6039a009b 100644 --- a/lib/src/widgets/proxy.dart +++ b/lib/src/widgets/others/proxy.dart @@ -136,8 +136,7 @@ class RichTextProxy extends SingleChildRenderObjectWidget { required this.textDirection, required this.locale, required this.strutStyle, - // TODO: This might needs to be updated, previous value was 1.0 using `textScaleFactor` - this.textScaler = const TextScaler.linear(1), + required this.textScaler, this.textWidthBasis = TextWidthBasis.parent, this.textHeightBehavior, super.key, diff --git a/lib/src/widgets/text_selection.dart b/lib/src/widgets/others/text_selection.dart similarity index 99% rename from lib/src/widgets/text_selection.dart rename to lib/src/widgets/others/text_selection.dart index c51d414df..8016201f0 100644 --- a/lib/src/widgets/text_selection.dart +++ b/lib/src/widgets/others/text_selection.dart @@ -6,8 +6,8 @@ import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter/scheduler.dart'; -import '../models/documents/nodes/node.dart'; -import 'editor/editor.dart'; +import '../../models/documents/nodes/node.dart'; +import '../editor/editor.dart'; TextSelection localSelection(Node node, TextSelection selection, fromParent) { final base = fromParent ? node.offset : node.documentOffset; diff --git a/lib/src/widgets/embeds.dart b/lib/src/widgets/quill/embeds.dart similarity index 73% rename from lib/src/widgets/embeds.dart rename to lib/src/widgets/quill/embeds.dart index 87511204a..d575f2fe1 100644 --- a/lib/src/widgets/embeds.dart +++ b/lib/src/widgets/quill/embeds.dart @@ -1,10 +1,10 @@ import 'package:flutter/material.dart'; -import '../../extensions.dart'; -import '../models/documents/nodes/leaf.dart' as leaf; -import '../models/themes/quill_dialog_theme.dart'; -import '../models/themes/quill_icon_theme.dart'; -import 'controller.dart'; +import '../../../extensions.dart'; +import '../../models/documents/nodes/leaf.dart' as leaf; +import '../../models/themes/quill_dialog_theme.dart'; +import '../../models/themes/quill_icon_theme.dart'; +import 'quill_controller.dart'; abstract class EmbedBuilder { const EmbedBuilder(); diff --git a/lib/src/widgets/controller.dart b/lib/src/widgets/quill/quill_controller.dart similarity index 87% rename from lib/src/widgets/controller.dart rename to lib/src/widgets/quill/quill_controller.dart index b48ab9ebc..fd3403003 100644 --- a/lib/src/widgets/controller.dart +++ b/lib/src/widgets/quill/quill_controller.dart @@ -1,18 +1,21 @@ import 'dart:math' as math; -import 'package:flutter/cupertino.dart'; import 'package:flutter/services.dart'; - -import '../models/documents/attribute.dart'; -import '../models/documents/document.dart'; -import '../models/documents/nodes/embeddable.dart'; -import '../models/documents/nodes/leaf.dart'; -import '../models/documents/style.dart'; -import '../models/quill_delta.dart'; -import '../models/structs/doc_change.dart'; -import '../models/structs/image_url.dart'; -import '../models/structs/offset_value.dart'; -import '../utils/delta.dart'; +import 'package:flutter/widgets.dart'; +import 'package:html2md/html2md.dart' as html2md; +import 'package:markdown/markdown.dart' as md; + +import '../../../markdown_quill.dart'; +import '../../models/documents/attribute.dart'; +import '../../models/documents/document.dart'; +import '../../models/documents/nodes/embeddable.dart'; +import '../../models/documents/nodes/leaf.dart'; +import '../../models/documents/style.dart'; +import '../../models/quill_delta.dart'; +import '../../models/structs/doc_change.dart'; +import '../../models/structs/image_url.dart'; +import '../../models/structs/offset_value.dart'; +import '../../utils/delta.dart'; typedef ReplaceTextCallback = bool Function(int index, int len, Object? data); typedef DeleteCallback = void Function(int cursorPosition, bool forward); @@ -42,7 +45,7 @@ class QuillController extends ChangeNotifier { Document get document => _document; - set document(doc) { + set document(Document doc) { _document = doc; // Prevent the selection from @@ -51,6 +54,11 @@ class QuillController extends ChangeNotifier { notifyListeners(); } + void updateDocument(Document newDocument) { + _document = newDocument; + notifyListeners(); + } + /// Tells whether to keep or reset the [toggledStyle] /// when user adds a new line. final bool _keepStyleOnNewLine; @@ -439,4 +447,38 @@ class QuillController extends ChangeNotifier { // Notify toolbar buttons directly with attributes Map toolbarButtonToggler = const {}; + + /// Convert the HTML Raw string to [Delta] + /// + /// It will run using the following steps: + /// + /// 1. Convert the html to markdown string using `html2md` package + /// 2. Convert the markdown string to quill delta json string + /// 3. Decode the delta json string to [Delta] + /// + /// for more [info](https://github.com/singerdmx/flutter-quill/issues/1100) + static Delta fromHtml(String html) { + final markdown = html2md + .convert( + html, + ) + .replaceAll('unsafe:', ''); + + final mdDocument = md.Document(encodeHtml: false); + + final mdToDelta = MarkdownToDelta(markdownDocument: mdDocument); + + return mdToDelta.convert(markdown); + + // final deltaJsonString = markdownToDelta(markdown); + // final deltaJson = jsonDecode(deltaJsonString); + // if (deltaJson is! List) { + // throw ArgumentError( + // 'The delta json string should be of type list when jsonDecode() it', + // ); + // } + // return Delta.fromJson( + // deltaJson, + // ); + } } diff --git a/lib/src/widgets/text_block.dart b/lib/src/widgets/quill/text_block.dart similarity index 96% rename from lib/src/widgets/text_block.dart rename to lib/src/widgets/quill/text_block.dart index 1f70c9af7..6510357d6 100644 --- a/lib/src/widgets/text_block.dart +++ b/lib/src/widgets/quill/text_block.dart @@ -1,24 +1,24 @@ import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; -import '../extensions/quill_provider.dart'; -import '../models/documents/attribute.dart'; -import '../models/documents/nodes/block.dart'; -import '../models/documents/nodes/line.dart'; -import '../models/structs/vertical_spacing.dart'; -import '../utils/delta.dart'; -import 'box.dart'; -import 'controller.dart'; -import 'cursor.dart'; -import 'default_styles.dart'; -import 'delegate.dart'; -import 'editor/editor.dart'; -import 'link.dart'; -import 'style_widgets/bullet_point.dart'; -import 'style_widgets/checkbox_point.dart'; -import 'style_widgets/number_point.dart'; +import '../../extensions/quill_configurations_ext.dart'; +import '../../models/documents/attribute.dart'; +import '../../models/documents/nodes/block.dart'; +import '../../models/documents/nodes/line.dart'; +import '../../models/structs/vertical_spacing.dart'; +import '../../utils/delta.dart'; +import '../editor/editor.dart'; +import '../others/box.dart'; +import '../others/cursor.dart'; +import '../others/default_styles.dart'; +import '../others/delegate.dart'; +import '../others/link.dart'; +import '../others/text_selection.dart'; +import '../style_widgets/bullet_point.dart'; +import '../style_widgets/checkbox_point.dart'; +import '../style_widgets/number_point.dart'; +import 'quill_controller.dart'; import 'text_line.dart'; -import 'text_selection.dart'; const List arabianRomanNumbers = [ 1000, diff --git a/lib/src/widgets/text_line.dart b/lib/src/widgets/quill/text_line.dart similarity index 97% rename from lib/src/widgets/text_line.dart rename to lib/src/widgets/quill/text_line.dart index c79e8da33..c1b68f42d 100644 --- a/lib/src/widgets/text_line.dart +++ b/lib/src/widgets/quill/text_line.dart @@ -8,27 +8,27 @@ import 'package:flutter/rendering.dart'; import 'package:flutter/services.dart'; import 'package:url_launcher/url_launcher.dart'; -import '../models/documents/attribute.dart'; -import '../models/documents/nodes/container.dart' as container_node; -import '../models/documents/nodes/embeddable.dart'; -import '../models/documents/nodes/leaf.dart'; -import '../models/documents/nodes/leaf.dart' as leaf; -import '../models/documents/nodes/line.dart'; -import '../models/documents/nodes/node.dart'; -import '../models/documents/style.dart'; -import '../models/structs/vertical_spacing.dart'; -import '../utils/color.dart'; -import '../utils/font.dart'; -import '../utils/platform.dart'; -import 'box.dart'; -import 'controller.dart'; -import 'cursor.dart'; -import 'default_styles.dart'; -import 'delegate.dart'; -import 'keyboard_listener.dart'; -import 'link.dart'; -import 'proxy.dart'; -import 'text_selection.dart'; +import '../../models/documents/attribute.dart'; +import '../../models/documents/nodes/container.dart' as container_node; +import '../../models/documents/nodes/embeddable.dart'; +import '../../models/documents/nodes/leaf.dart' as leaf; +import '../../models/documents/nodes/leaf.dart'; +import '../../models/documents/nodes/line.dart'; +import '../../models/documents/nodes/node.dart'; +import '../../models/documents/style.dart'; +import '../../models/structs/vertical_spacing.dart'; +import '../../utils/color.dart'; +import '../../utils/font.dart'; +import '../../utils/platform.dart'; +import '../others/box.dart'; +import '../others/cursor.dart'; +import '../others/default_styles.dart'; +import '../others/delegate.dart'; +import '../others/keyboard_listener.dart'; +import '../others/link.dart'; +import '../others/proxy.dart'; +import '../others/text_selection.dart'; +import 'quill_controller.dart'; class TextLine extends StatefulWidget { const TextLine({ @@ -163,7 +163,7 @@ class _TextLineState extends State { ); } } - final textSpan = _getTextSpanForWholeLine(context); + final textSpan = _getTextSpanForWholeLine(); final strutStyle = StrutStyle.fromTextStyle(textSpan.style!); final textAlign = _getTextAlign(); final child = RichText( @@ -175,16 +175,17 @@ class _TextLineState extends State { textScaler: MediaQuery.textScalerOf(context), ); return RichTextProxy( - textStyle: textSpan.style!, + textStyle: textSpan.style ?? const TextStyle(), textAlign: textAlign, textDirection: widget.textDirection!, strutStyle: strutStyle, locale: Localizations.localeOf(context), + textScaler: MediaQuery.textScalerOf(context), child: child, ); } - InlineSpan _getTextSpanForWholeLine(BuildContext context) { + InlineSpan _getTextSpanForWholeLine() { final lineStyle = _getLineStyle(widget.styles); if (!widget.line.hasEmbed) { return _buildTextSpan(widget.styles, widget.line.children, lineStyle); diff --git a/lib/src/widgets/quill_single_child_scroll_view.dart b/lib/src/widgets/raw_editor/quill_single_child_scroll_view.dart similarity index 100% rename from lib/src/widgets/quill_single_child_scroll_view.dart rename to lib/src/widgets/raw_editor/quill_single_child_scroll_view.dart diff --git a/lib/src/widgets/raw_editor/raw_editor.dart b/lib/src/widgets/raw_editor/raw_editor.dart index c0ecf52db..b69b75154 100644 --- a/lib/src/widgets/raw_editor/raw_editor.dart +++ b/lib/src/widgets/raw_editor/raw_editor.dart @@ -1,8 +1,18 @@ import 'package:flutter/widgets.dart' - show BuildContext, State, StatefulWidget, Widget; + show + AnimationController, + BuildContext, + ScrollController, + State, + StatefulWidget, + TextSelectionDelegate, + Widget; import 'package:meta/meta.dart' show immutable; -import '../../models/config/raw_editor/configurations.dart'; +import '../../models/config/raw_editor/raw_editor_configurations.dart'; +import '../../models/structs/offset_value.dart'; +import '../editor/editor.dart'; +import '../others/text_selection.dart'; import 'raw_editor_state.dart'; class QuillRawEditor extends StatefulWidget { @@ -49,3 +59,29 @@ class QuillEditorGlyphHeights { final double startGlyphHeight; final double endGlyphHeight; } + +/// Base interface for the editor state which defines contract used by +/// various mixins. +abstract class EditorState extends State + implements TextSelectionDelegate { + ScrollController get scrollController; + + RenderEditor get renderEditor; + + EditorTextSelectionOverlay? get selectionOverlay; + + List get pasteStyleAndEmbed; + + String get pastePlainText; + + /// Controls the floating cursor animation when it is released. + /// The floating cursor is animated to merge with the regular cursor. + AnimationController get floatingCursorResetController; + + /// Returns true if the editor has been marked as needing to be rebuilt. + bool get dirty; + + bool showToolbar(); + + void requestKeyboard(); +} diff --git a/lib/src/widgets/raw_editor/raw_editor_actions.dart b/lib/src/widgets/raw_editor/raw_editor_actions.dart index 54a7eba7a..40580d70e 100644 --- a/lib/src/widgets/raw_editor/raw_editor_actions.dart +++ b/lib/src/widgets/raw_editor/raw_editor_actions.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import '../../models/documents/attribute.dart'; import '../editor/editor.dart'; -import '../toolbar/buttons/link_style2.dart'; +import '../toolbar/buttons/link_style2_button.dart'; import '../toolbar/buttons/search/search_dialog.dart'; import 'raw_editor_state.dart'; import 'raw_editor_text_boundaries.dart'; @@ -76,7 +76,7 @@ class QuillEditorDeleteTextAction @override bool get isActionEnabled => - !state.widget.configurations.isReadOnly && + !state.widget.configurations.readOnly && state.textEditingValue.selection.isValid; } diff --git a/lib/src/widgets/raw_editor/raw_editor_render_object.dart b/lib/src/widgets/raw_editor/raw_editor_render_object.dart index 3338c1f22..af99671fa 100644 --- a/lib/src/widgets/raw_editor/raw_editor_render_object.dart +++ b/lib/src/widgets/raw_editor/raw_editor_render_object.dart @@ -2,8 +2,8 @@ import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart' show ViewportOffset; import '../../models/documents/document.dart'; -import '../cursor.dart'; import '../editor/editor.dart'; +import '../others/cursor.dart'; class QuilRawEditorMultiChildRenderObject extends MultiChildRenderObjectWidget { const QuilRawEditorMultiChildRenderObject({ diff --git a/lib/src/widgets/raw_editor/raw_editor_state.dart b/lib/src/widgets/raw_editor/raw_editor_state.dart index dc13082f5..3a9dfd608 100644 --- a/lib/src/widgets/raw_editor/raw_editor_state.dart +++ b/lib/src/widgets/raw_editor/raw_editor_state.dart @@ -10,15 +10,16 @@ import 'package:flutter/rendering.dart' show RenderAbstractViewport; import 'package:flutter/scheduler.dart' show SchedulerBinding; import 'package:flutter/services.dart' show - LogicalKeyboardKey, - RawKeyDownEvent, - HardwareKeyboard, Clipboard, ClipboardData, + HardwareKeyboard, + LogicalKeyboardKey, + RawKeyDownEvent, + SystemChannels, TextInputControl; import 'package:flutter_keyboard_visibility/flutter_keyboard_visibility.dart' show KeyboardVisibilityController; -import 'package:pasteboard/pasteboard.dart' show Pasteboard; +import 'package:super_clipboard/super_clipboard.dart'; import '../../models/documents/attribute.dart'; import '../../models/documents/document.dart'; @@ -27,23 +28,24 @@ import '../../models/documents/nodes/embeddable.dart'; import '../../models/documents/nodes/leaf.dart' as leaf; import '../../models/documents/nodes/line.dart'; import '../../models/documents/nodes/node.dart'; +import '../../models/quill_delta.dart'; import '../../models/structs/offset_value.dart'; import '../../models/structs/vertical_spacing.dart'; import '../../utils/cast.dart'; import '../../utils/delta.dart'; import '../../utils/embeds.dart'; import '../../utils/platform.dart'; -import '../controller.dart'; -import '../cursor.dart'; -import '../default_styles.dart'; import '../editor/editor.dart'; -import '../keyboard_listener.dart'; -import '../link.dart'; -import '../proxy.dart'; -import '../quill_single_child_scroll_view.dart'; -import '../text_block.dart'; -import '../text_line.dart'; -import '../text_selection.dart'; +import '../others/cursor.dart'; +import '../others/default_styles.dart'; +import '../others/keyboard_listener.dart'; +import '../others/link.dart'; +import '../others/proxy.dart'; +import '../others/text_selection.dart'; +import '../quill/quill_controller.dart'; +import '../quill/text_block.dart'; +import '../quill/text_line.dart'; +import 'quill_single_child_scroll_view.dart'; import 'raw_editor.dart'; import 'raw_editor_actions.dart'; import 'raw_editor_render_object.dart'; @@ -115,14 +117,240 @@ class QuillRawEditorState extends EditorState .call(content); } + // List get contextMenuButtonItems { + // return EditableText.getEditableButtonItems( + // clipboardStatus: _clipboardStatus.value, + // onLiveTextInput: null, + // onCopy: copyEnabled + // ? () => copySelection(SelectionChangedCause.toolbar) + // : null, + // onCut: + // cutEnabled ? () => cutSelection(SelectionChangedCause.toolbar) : null, + // onPaste: + // pasteEnabled ? () => pasteText(SelectionChangedCause.toolbar) : null, + // onSelectAll: selectAllEnabled + // ? () => selectAll(SelectionChangedCause.toolbar) + // : null, + // onLookUp: null, + // onSearchWeb: null, + // onShare: null, + // ); + // } + + /// Copy current selection to [Clipboard]. + @override + void copySelection(SelectionChangedCause cause) { + controller.copiedImageUrl = null; + _pastePlainText = controller.getPlainText(); + _pasteStyleAndEmbed = controller.getAllIndividualSelectionStylesAndEmbed(); + + final selection = textEditingValue.selection; + final text = textEditingValue.text; + if (selection.isCollapsed) { + return; + } + Clipboard.setData(ClipboardData(text: selection.textInside(text))); + + if (cause == SelectionChangedCause.toolbar) { + bringIntoView(textEditingValue.selection.extent); + + // Collapse the selection and hide the toolbar and handles. + userUpdateTextEditingValue( + TextEditingValue( + text: textEditingValue.text, + selection: + TextSelection.collapsed(offset: textEditingValue.selection.end), + ), + SelectionChangedCause.toolbar, + ); + } + } + + /// Cut current selection to [Clipboard]. + @override + void cutSelection(SelectionChangedCause cause) { + controller.copiedImageUrl = null; + _pastePlainText = controller.getPlainText(); + _pasteStyleAndEmbed = controller.getAllIndividualSelectionStylesAndEmbed(); + + if (widget.configurations.readOnly) { + return; + } + final selection = textEditingValue.selection; + final text = textEditingValue.text; + if (selection.isCollapsed) { + return; + } + Clipboard.setData(ClipboardData(text: selection.textInside(text))); + _replaceText(ReplaceTextIntent(textEditingValue, '', selection, cause)); + + if (cause == SelectionChangedCause.toolbar) { + bringIntoView(textEditingValue.selection.extent); + hideToolbar(); + } + } + + /// Paste text from [Clipboard]. + @override + Future pasteText(SelectionChangedCause cause) async { + if (widget.configurations.readOnly) { + return; + } + + if (controller.copiedImageUrl != null) { + final index = textEditingValue.selection.baseOffset; + final length = textEditingValue.selection.extentOffset - index; + final copied = controller.copiedImageUrl!; + controller.replaceText( + index, + length, + BlockEmbed.image(copied.url), + null, + ); + if (copied.styleString.isNotEmpty) { + controller.formatText( + getEmbedNode(controller, index + 1).offset, + 1, + StyleAttribute(copied.styleString), + ); + } + controller.copiedImageUrl = null; + await Clipboard.setData( + const ClipboardData(text: ''), + ); + return; + } + + final selection = textEditingValue.selection; + if (!selection.isValid) { + return; + } + + // TODO: Could be improved + Delta? deltaFromCliboard; + final reader = await ClipboardReader.readClipboard(); + if (reader.canProvide(Formats.htmlText)) { + final html = await reader.readValue(Formats.htmlText); + if (html == null) { + return; + } + deltaFromCliboard = QuillController.fromHtml(html); + } + if (deltaFromCliboard != null) { + // final index = selection.baseOffset; + // final length = selection.extentOffset - index; + + final list = controller.document.toDelta().toList() + ..insertAll(controller.document.toDelta().toList().length - 1, + deltaFromCliboard.toList()); + + final delta = controller.document.toDelta(); + for (final operation in list) { + delta.push(operation); + } + + controller + ..updateDocument( + Document.fromDelta(delta), + ) + ..updateSelection( + TextSelection.collapsed( + offset: controller.document.length, + ), + ChangeSource.local, + ); + + bringIntoView(textEditingValue.selection.extent); + + // Collapse the selection and hide the toolbar and handles. + userUpdateTextEditingValue( + TextEditingValue( + text: textEditingValue.text, + selection: TextSelection.collapsed( + offset: textEditingValue.selection.end, + ), + ), + cause, + ); + + return; + } + + // Snapshot the input before using `await`. + // See https://github.com/flutter/flutter/issues/11427 + final plainText = await Clipboard.getData(Clipboard.kTextPlain); + if (plainText != null) { + _replaceText( + ReplaceTextIntent( + textEditingValue, + plainText.text!, + selection, + cause, + ), + ); + + bringIntoView(textEditingValue.selection.extent); + + // Collapse the selection and hide the toolbar and handles. + userUpdateTextEditingValue( + TextEditingValue( + text: textEditingValue.text, + selection: TextSelection.collapsed( + offset: textEditingValue.selection.end, + ), + ), + cause, + ); + + return; + } + + final onImagePaste = widget.configurations.onImagePaste; + if (onImagePaste != null) { + final reader = await ClipboardReader.readClipboard(); + if (!reader.canProvide(Formats.png)) { + return; + } + reader.getFile(Formats.png, (value) async { + final image = value; + + final imageUrl = await onImagePaste(await image.readAll()); + if (imageUrl == null) { + return; + } + + controller.replaceText( + textEditingValue.selection.end, + 0, + BlockEmbed.image(imageUrl), + null, + ); + }); + } + } + + /// Select the entire text value. + @override + void selectAll(SelectionChangedCause cause) { + userUpdateTextEditingValue( + textEditingValue.copyWith( + selection: TextSelection( + baseOffset: 0, extentOffset: textEditingValue.text.length), + ), + cause, + ); + + if (cause == SelectionChangedCause.toolbar) { + bringIntoView(textEditingValue.selection.extent); + } + } + /// Returns the [ContextMenuButtonItem]s representing the buttons in this /// platform's default selection menu for [QuillRawEditor]. - /// /// Copied from [EditableTextState]. List get contextMenuButtonItems { return EditableText.getEditableButtonItems( clipboardStatus: _clipboardStatus.value, - onLiveTextInput: null, onCopy: copyEnabled ? () => copySelection(SelectionChangedCause.toolbar) : null, @@ -133,12 +361,70 @@ class QuillRawEditorState extends EditorState onSelectAll: selectAllEnabled ? () => selectAll(SelectionChangedCause.toolbar) : null, - onLookUp: null, - onSearchWeb: null, - onShare: null, + onLookUp: lookUpEnabled + ? () => lookUpSelection(SelectionChangedCause.toolbar) + : null, + onSearchWeb: searchWebEnabled + ? () => searchWebForSelection(SelectionChangedCause.toolbar) + : null, + onShare: shareEnabled + ? () => shareSelection(SelectionChangedCause.toolbar) + : null, + onLiveTextInput: liveTextInputEnabled ? () {} : null, + ); + } + + /// Look up the current selection, + /// as in the "Look Up" edit menu button on iOS. + /// + /// Currently this is only implemented for iOS. + /// + /// Throws an error if the selection is empty or collapsed. + Future lookUpSelection(SelectionChangedCause cause) async { + final text = textEditingValue.selection.textInside(textEditingValue.text); + if (text.isEmpty) { + return; + } + await SystemChannels.platform.invokeMethod( + 'LookUp.invoke', + text, ); } + /// Launch a web search on the current selection, + /// as in the "Search Web" edit menu button on iOS. + /// + /// Currently this is only implemented for iOS. + /// + /// When 'obscureText' is true or the selection is empty, + /// this function will not do anything + Future searchWebForSelection(SelectionChangedCause cause) async { + final text = textEditingValue.selection.textInside(textEditingValue.text); + if (text.isNotEmpty) { + await SystemChannels.platform.invokeMethod( + 'SearchWeb.invoke', + text, + ); + } + } + + /// Launch the share interface for the current selection, + /// as in the "Share" edit menu button on iOS. + /// + /// Currently this is only implemented for iOS. + /// + /// When 'obscureText' is true or the selection is empty, + /// this function will not do anything + Future shareSelection(SelectionChangedCause cause) async { + final text = textEditingValue.selection.textInside(textEditingValue.text); + if (text.isNotEmpty) { + await SystemChannels.platform.invokeMethod( + 'Share.invoke', + text, + ); + } + } + /// Returns the anchor points for the default context menu. /// /// Copied from [EditableTextState]. @@ -552,7 +838,7 @@ class QuillRawEditorState extends EditorState controller.document.queryChild(controller.selection.baseOffset); KeyEventResult insertTabCharacter() { - if (widget.configurations.isReadOnly) { + if (widget.configurations.readOnly) { return KeyEventResult.ignored; } controller.replaceText(controller.selection.baseOffset, 0, '\t', null); @@ -662,7 +948,7 @@ class QuillRawEditorState extends EditorState void _handleCheckboxTap(int offset, bool value) { final requestKeyboardFocusOnCheckListChanged = widget.configurations.requestKeyboardFocusOnCheckListChanged; - if (!widget.configurations.isReadOnly) { + if (!widget.configurations.readOnly) { _disableScrollControllerAnimateOnce = true; final currentSelection = controller.selection.copyWith(); final attribute = value ? Attribute.checked : Attribute.unchecked; @@ -737,7 +1023,7 @@ class QuillRawEditorState extends EditorState indentLevelCounts: indentLevelCounts, clearIndents: clearIndents, onCheckboxTap: _handleCheckboxTap, - readOnly: widget.configurations.isReadOnly, + readOnly: widget.configurations.readOnly, customStyleBuilder: widget.configurations.customStyleBuilder, customLinkPrefixes: widget.configurations.customLinkPrefixes, ); @@ -767,7 +1053,7 @@ class QuillRawEditorState extends EditorState customStyleBuilder: widget.configurations.customStyleBuilder, customRecognizerBuilder: widget.configurations.customRecognizerBuilder, styles: _styles!, - readOnly: widget.configurations.isReadOnly, + readOnly: widget.configurations.readOnly, controller: controller, linkActionPicker: _linkActionPicker, onLaunchUrl: widget.configurations.onLaunchUrl, @@ -968,7 +1254,7 @@ class QuillRawEditorState extends EditorState if (!shouldCreateInputConnection) { closeConnectionIfNeeded(); } else { - if (oldWidget.configurations.isReadOnly && _hasFocus) { + if (oldWidget.configurations.readOnly && _hasFocus) { openConnectionIfNeeded(); } } @@ -1262,161 +1548,6 @@ class QuillRawEditorState extends EditorState ); } - /// Copy current selection to [Clipboard]. - @override - void copySelection(SelectionChangedCause cause) { - controller.copiedImageUrl = null; - _pastePlainText = controller.getPlainText(); - _pasteStyleAndEmbed = controller.getAllIndividualSelectionStylesAndEmbed(); - - final selection = textEditingValue.selection; - final text = textEditingValue.text; - if (selection.isCollapsed) { - return; - } - Clipboard.setData(ClipboardData(text: selection.textInside(text))); - - if (cause == SelectionChangedCause.toolbar) { - bringIntoView(textEditingValue.selection.extent); - - // Collapse the selection and hide the toolbar and handles. - userUpdateTextEditingValue( - TextEditingValue( - text: textEditingValue.text, - selection: - TextSelection.collapsed(offset: textEditingValue.selection.end), - ), - SelectionChangedCause.toolbar, - ); - } - } - - /// Cut current selection to [Clipboard]. - @override - void cutSelection(SelectionChangedCause cause) { - controller.copiedImageUrl = null; - _pastePlainText = controller.getPlainText(); - _pasteStyleAndEmbed = controller.getAllIndividualSelectionStylesAndEmbed(); - - if (widget.configurations.isReadOnly) { - return; - } - final selection = textEditingValue.selection; - final text = textEditingValue.text; - if (selection.isCollapsed) { - return; - } - Clipboard.setData(ClipboardData(text: selection.textInside(text))); - _replaceText(ReplaceTextIntent(textEditingValue, '', selection, cause)); - - if (cause == SelectionChangedCause.toolbar) { - bringIntoView(textEditingValue.selection.extent); - hideToolbar(); - } - } - - /// Paste text from [Clipboard]. - @override - Future pasteText(SelectionChangedCause cause) async { - if (widget.configurations.isReadOnly) { - return; - } - - if (controller.copiedImageUrl != null) { - final index = textEditingValue.selection.baseOffset; - final length = textEditingValue.selection.extentOffset - index; - final copied = controller.copiedImageUrl!; - controller.replaceText( - index, - length, - BlockEmbed.image(copied.url), - null, - ); - if (copied.styleString.isNotEmpty) { - controller.formatText( - getEmbedNode(controller, index + 1).offset, - 1, - StyleAttribute(copied.styleString), - ); - } - controller.copiedImageUrl = null; - await Clipboard.setData( - const ClipboardData(text: ''), - ); - return; - } - - final selection = textEditingValue.selection; - if (!selection.isValid) { - return; - } - // Snapshot the input before using `await`. - // See https://github.com/flutter/flutter/issues/11427 - final text = await Clipboard.getData(Clipboard.kTextPlain); - if (text != null) { - _replaceText( - ReplaceTextIntent( - textEditingValue, - text.text!, - selection, - cause, - ), - ); - - bringIntoView(textEditingValue.selection.extent); - - // Collapse the selection and hide the toolbar and handles. - userUpdateTextEditingValue( - TextEditingValue( - text: textEditingValue.text, - selection: TextSelection.collapsed( - offset: textEditingValue.selection.end, - ), - ), - cause, - ); - - return; - } - - final onImagePaste = widget.configurations.onImagePaste; - if (onImagePaste != null) { - final image = await Pasteboard.image; - - if (image == null) { - return; - } - - final imageUrl = await onImagePaste(image); - if (imageUrl == null) { - return; - } - - controller.replaceText( - textEditingValue.selection.end, - 0, - BlockEmbed.image(imageUrl), - null, - ); - } - } - - /// Select the entire text value. - @override - void selectAll(SelectionChangedCause cause) { - userUpdateTextEditingValue( - textEditingValue.copyWith( - selection: TextSelection( - baseOffset: 0, extentOffset: textEditingValue.text.length), - ), - cause, - ); - - if (cause == SelectionChangedCause.toolbar) { - bringIntoView(textEditingValue.selection.extent); - } - } - @override bool get wantKeepAlive => widget.configurations.focusNode.hasFocus; @@ -1614,6 +1745,8 @@ class QuillRawEditorState extends EditorState } } + // TODO: Review those + @override bool get liveTextInputEnabled => false; diff --git a/lib/src/widgets/raw_editor/raw_editor_state_selection_delegate_mixin.dart b/lib/src/widgets/raw_editor/raw_editor_state_selection_delegate_mixin.dart index 466175732..2b5bfa10c 100644 --- a/lib/src/widgets/raw_editor/raw_editor_state_selection_delegate_mixin.dart +++ b/lib/src/widgets/raw_editor/raw_editor_state_selection_delegate_mixin.dart @@ -8,7 +8,7 @@ import '../../models/documents/nodes/embeddable.dart'; import '../../models/documents/nodes/leaf.dart'; import '../../models/documents/style.dart'; import '../../utils/delta.dart'; -import '../editor/editor.dart'; +import 'raw_editor.dart'; mixin RawEditorStateSelectionDelegateMixin on EditorState implements TextSelectionDelegate { @@ -171,7 +171,7 @@ mixin RawEditorStateSelectionDelegateMixin on EditorState @override bool get cutEnabled => widget.configurations.contextMenuBuilder != null && - !widget.configurations.isReadOnly; + !widget.configurations.readOnly; @override bool get copyEnabled => widget.configurations.contextMenuBuilder != null; @@ -179,7 +179,7 @@ mixin RawEditorStateSelectionDelegateMixin on EditorState @override bool get pasteEnabled => widget.configurations.contextMenuBuilder != null && - !widget.configurations.isReadOnly; + !widget.configurations.readOnly; @override bool get selectAllEnabled => widget.configurations.contextMenuBuilder != null; diff --git a/lib/src/widgets/raw_editor/raw_editor_state_text_input_client_mixin.dart b/lib/src/widgets/raw_editor/raw_editor_state_text_input_client_mixin.dart index f9698bd21..2c6a08daa 100644 --- a/lib/src/widgets/raw_editor/raw_editor_state_text_input_client_mixin.dart +++ b/lib/src/widgets/raw_editor/raw_editor_state_text_input_client_mixin.dart @@ -10,6 +10,7 @@ import 'package:flutter/services.dart'; import '../../models/documents/document.dart'; import '../../utils/delta.dart'; import '../editor/editor.dart'; +import 'raw_editor.dart'; mixin RawEditorStateTextInputClientMixin on EditorState implements TextInputClient { @@ -30,7 +31,7 @@ mixin RawEditorStateTextInputClientMixin on EditorState /// - cmd/ctrl+a to select all. /// - Changing the selection using a physical keyboard. bool get shouldCreateInputConnection => - kIsWeb || !widget.configurations.isReadOnly; + kIsWeb || !widget.configurations.readOnly; /// Returns `true` if there is open input connection. bool get hasConnection => @@ -58,9 +59,9 @@ mixin RawEditorStateTextInputClientMixin on EditorState this, TextInputConfiguration( inputType: TextInputType.multiline, - readOnly: widget.configurations.isReadOnly, + readOnly: widget.configurations.readOnly, inputAction: widget.configurations.textInputAction, - enableSuggestions: !widget.configurations.isReadOnly, + enableSuggestions: !widget.configurations.readOnly, keyboardAppearance: widget.configurations.keyboardAppearance ?? CupertinoTheme.maybeBrightnessOf(context) ?? Theme.of(context).brightness, diff --git a/lib/src/widgets/style_widgets/bullet_point.dart b/lib/src/widgets/style_widgets/bullet_point.dart index 1dd1b4cf5..629c68e6d 100644 --- a/lib/src/widgets/style_widgets/bullet_point.dart +++ b/lib/src/widgets/style_widgets/bullet_point.dart @@ -1,5 +1,7 @@ import 'package:flutter/material.dart'; +import '../../extensions/quill_configurations_ext.dart'; + class QuillEditorBulletPoint extends StatelessWidget { const QuillEditorBulletPoint({ required this.style, @@ -18,7 +20,13 @@ class QuillEditorBulletPoint extends StatelessWidget { alignment: AlignmentDirectional.topEnd, width: width, padding: EdgeInsetsDirectional.only(end: padding), - child: Text('•', style: style), + color: context.quillEditorElementOptions?.unorderedList.backgroundColor, + child: Text( + '•', + style: style.copyWith( + color: context.quillEditorElementOptions?.unorderedList.fontColor, + ), + ), ); } } diff --git a/lib/src/widgets/style_widgets/checkbox_point.dart b/lib/src/widgets/style_widgets/checkbox_point.dart index 5eb826863..860d5696f 100644 --- a/lib/src/widgets/style_widgets/checkbox_point.dart +++ b/lib/src/widgets/style_widgets/checkbox_point.dart @@ -1,7 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:flutter_animate/flutter_animate.dart'; - -import '../../extensions/quill_provider.dart'; class QuillEditorCheckboxPoint extends StatefulWidget { const QuillEditorCheckboxPoint({ @@ -76,20 +73,6 @@ class QuillEditorCheckboxPointState extends State { ), ), ); - if (context.requireQuillSharedConfigurations.animationConfigurations - .checkBoxPointItem) { - return Animate( - effects: const [ - SlideEffect( - duration: Duration(milliseconds: 70), - ), - ScaleEffect( - duration: Duration(milliseconds: 70), - ) - ], - child: child, - ); - } return child; } } diff --git a/lib/src/widgets/style_widgets/number_point.dart b/lib/src/widgets/style_widgets/number_point.dart index 30b5590ec..ed5d96c8c 100644 --- a/lib/src/widgets/style_widgets/number_point.dart +++ b/lib/src/widgets/style_widgets/number_point.dart @@ -1,7 +1,8 @@ import 'package:flutter/material.dart'; +import '../../extensions/quill_configurations_ext.dart'; import '../../models/documents/attribute.dart'; -import '../text_block.dart'; +import '../quill/text_block.dart'; class QuillEditorNumberPoint extends StatelessWidget { const QuillEditorNumberPoint({ @@ -36,7 +37,13 @@ class QuillEditorNumberPoint extends StatelessWidget { alignment: AlignmentDirectional.topEnd, width: width, padding: EdgeInsetsDirectional.only(end: padding), - child: Text(withDot ? '$s.' : s, style: style), + color: context.quillEditorElementOptions?.orderedList.backgroundColor, + child: Text( + withDot ? '$s.' : s, + style: style.copyWith( + color: context.quillEditorElementOptions?.orderedList.fontColor, + ), + ), ); } if (attrs.containsKey(Attribute.indent.key)) { @@ -67,7 +74,13 @@ class QuillEditorNumberPoint extends StatelessWidget { alignment: AlignmentDirectional.topEnd, width: width, padding: EdgeInsetsDirectional.only(end: padding), - child: Text(withDot ? '$s.' : s, style: style), + color: context.quillEditorElementOptions?.orderedList.backgroundColor, + child: Text( + withDot ? '$s.' : s, + style: style.copyWith( + color: context.quillEditorElementOptions?.orderedList.fontColor, + ), + ), ); } diff --git a/lib/src/widgets/toolbar/base_toolbar.dart b/lib/src/widgets/toolbar/base_toolbar.dart index 5767f0f2f..c849eda72 100644 --- a/lib/src/widgets/toolbar/base_toolbar.dart +++ b/lib/src/widgets/toolbar/base_toolbar.dart @@ -1,40 +1,50 @@ import 'package:flutter/material.dart'; import '../../../flutter_quill.dart' - show QuillBaseToolbarProvider, defaultToolbarSize; + show QuillToolbarProvider, defaultToolbarSize; import '../../l10n/widgets/localizations.dart'; -import '../../models/config/toolbar/base_toolbar_configurations.dart'; -import 'buttons/arrow_indicated_list.dart'; +import '../../models/config/toolbar/simple_toolbar_configurations.dart'; +import '../../models/config/toolbar/toolbar_configurations.dart'; +import 'simple_toolbar.dart'; -export '../../models/config/toolbar/buttons/base.dart'; -export '../../models/config/toolbar/toolbar_configurations.dart'; -export 'buttons/clear_format.dart'; -export 'buttons/color/color.dart'; -export 'buttons/custom_button.dart'; -export 'buttons/font_family.dart'; -export 'buttons/font_size.dart'; -export 'buttons/history.dart'; -export 'buttons/indent.dart'; -export 'buttons/link_style.dart'; -export 'buttons/link_style2.dart'; -export 'buttons/quill_icon.dart'; -export 'buttons/search/search.dart'; -export 'buttons/select_alignment.dart'; -export 'buttons/select_header_style.dart'; -export 'buttons/toggle_check_list.dart'; -export 'buttons/toggle_style.dart'; +export '../../models/config/toolbar/buttons/base_configurations.dart'; +export '../../models/config/toolbar/simple_toolbar_configurations.dart'; +export 'buttons/clear_format_button.dart'; +export 'buttons/color/color_button.dart'; +export 'buttons/custom_button_button.dart'; +export 'buttons/font_family_button.dart'; +export 'buttons/font_size_button.dart'; +export 'buttons/history_button.dart'; +export 'buttons/indent_button.dart'; +export 'buttons/link_style2_button.dart'; +export 'buttons/link_style_button.dart'; +export 'buttons/quill_icon_button.dart'; +export 'buttons/search/search_button.dart'; +export 'buttons/select_header_style_buttons.dart'; +export 'buttons/toggle_check_list_button.dart'; +export 'buttons/toggle_style_button.dart'; typedef QuillBaseToolbarChildrenBuilder = List Function( BuildContext context, ); -class QuillBaseToolbar extends StatelessWidget implements PreferredSizeWidget { - const QuillBaseToolbar({ - required this.configurations, +class QuillToolbar extends StatelessWidget implements PreferredSizeWidget { + const QuillToolbar({ + required this.child, + this.configurations = const QuillToolbarConfigurations(), super.key, }); - final QuillBaseToolbarConfigurations configurations; + static QuillSimpleToolbar simple( + {required QuillSimpleToolbarConfigurations configurations}) { + return QuillSimpleToolbar( + configurations: configurations, + ); + } + + final Widget child; + + final QuillToolbarConfigurations configurations; // We can't get the modified [toolbarSize] by the developer // but we tested the [QuillToolbar] on the [appBar] and I didn't notice @@ -47,90 +57,11 @@ class QuillBaseToolbar extends StatelessWidget implements PreferredSizeWidget { @override Widget build(BuildContext context) { - final toolbarSize = configurations.toolbarSize; return FlutterQuillLocalizationsWidget( - child: QuillBaseToolbarProvider( + child: QuillToolbarProvider( toolbarConfigurations: configurations, - child: Builder( - builder: (context) { - if (configurations.multiRowsDisplay) { - return Wrap( - direction: configurations.axis, - alignment: configurations.toolbarIconAlignment, - crossAxisAlignment: configurations.toolbarIconCrossAlignment, - runSpacing: 4, - spacing: configurations.toolbarSectionSpacing, - children: configurations.childrenBuilder(context), - ); - } - return Container( - decoration: configurations.decoration ?? - BoxDecoration( - color: - configurations.color ?? Theme.of(context).canvasColor, - ), - constraints: BoxConstraints.tightFor( - height: - configurations.axis == Axis.horizontal ? toolbarSize : null, - width: - configurations.axis == Axis.vertical ? toolbarSize : null, - ), - child: QuillToolbarArrowIndicatedButtonList( - axis: configurations.axis, - buttons: configurations.childrenBuilder(context), - ), - ); - }, - ), + child: child, ), ); } } - -/// The divider which is used for separation of buttons in the toolbar. -/// -/// It can be used outside of this package, for example when user does not use -/// [QuillBaseToolbar.basic] and compose toolbar's children on its own. -class QuillToolbarDivider extends StatelessWidget { - const QuillToolbarDivider( - this.axis, { - super.key, - this.color, - this.space, - }); - - /// Provides a horizontal divider for vertical toolbar. - const QuillToolbarDivider.horizontal({Key? key, Color? color, double? space}) - : this(Axis.horizontal, color: color, space: space, key: key); - - /// Provides a horizontal divider for horizontal toolbar. - const QuillToolbarDivider.vertical({Key? key, Color? color, double? space}) - : this(Axis.vertical, color: color, space: space, key: key); - - /// The axis along which the toolbar is. - final Axis axis; - - /// The color to use when painting this divider's line. - final Color? color; - - /// The divider's space (width or height) depending of [axis]. - final double? space; - - @override - Widget build(BuildContext context) { - // Vertical toolbar requires horizontal divider, and vice versa - return axis == Axis.vertical - ? Divider( - height: space, - color: color, - indent: 12, - endIndent: 12, - ) - : VerticalDivider( - width: space, - color: color, - indent: 12, - endIndent: 12, - ); - } -} diff --git a/lib/src/widgets/toolbar/buttons/arrow_indicated_list.dart b/lib/src/widgets/toolbar/buttons/arrow_indicated_list_button.dart similarity index 100% rename from lib/src/widgets/toolbar/buttons/arrow_indicated_list.dart rename to lib/src/widgets/toolbar/buttons/arrow_indicated_list_button.dart diff --git a/lib/src/widgets/toolbar/buttons/clear_format.dart b/lib/src/widgets/toolbar/buttons/clear_format_button.dart similarity index 89% rename from lib/src/widgets/toolbar/buttons/clear_format.dart rename to lib/src/widgets/toolbar/buttons/clear_format_button.dart index e5f4afc7a..751474116 100644 --- a/lib/src/widgets/toolbar/buttons/clear_format.dart +++ b/lib/src/widgets/toolbar/buttons/clear_format_button.dart @@ -1,16 +1,16 @@ import 'package:flutter/material.dart'; -import '../../../extensions/quill_provider.dart'; +import '../../../extensions/quill_configurations_ext.dart'; import '../../../l10n/extensions/localizations.dart'; import '../../../models/documents/attribute.dart'; import '../../../models/themes/quill_icon_theme.dart'; -import '../../controller.dart'; +import '../../quill/quill_controller.dart'; import '../base_toolbar.dart'; class QuillToolbarClearFormatButton extends StatelessWidget { const QuillToolbarClearFormatButton({ required QuillController controller, - required this.options, + this.options = const QuillToolbarClearFormatButtonOptions(), super.key, }) : _controller = controller; @@ -108,16 +108,11 @@ class QuillToolbarClearFormatButton extends StatelessWidget { final theme = Theme.of(context); final iconColor = iconTheme?.iconUnselectedColor ?? theme.iconTheme.color; - final fillColor = iconTheme?.iconUnselectedFillColor ?? theme.canvasColor; return QuillToolbarIconButton( tooltip: tooltip, - highlightElevation: 0, - hoverElevation: 0, - size: iconSize * iconButtonFactor, - icon: Icon(iconData, size: iconSize, color: iconColor), - fillColor: fillColor, - borderRadius: iconTheme?.borderRadius ?? 2, + icon: Icon(iconData, size: iconSize * iconButtonFactor, color: iconColor), + isFilled: false, onPressed: _sharedOnPressed, afterPressed: afterButtonPressed, ); diff --git a/lib/src/widgets/toolbar/buttons/color/color.dart b/lib/src/widgets/toolbar/buttons/color/color_button.dart similarity index 88% rename from lib/src/widgets/toolbar/buttons/color/color.dart rename to lib/src/widgets/toolbar/buttons/color/color_button.dart index cd45880db..2140afc40 100644 --- a/lib/src/widgets/toolbar/buttons/color/color.dart +++ b/lib/src/widgets/toolbar/buttons/color/color_button.dart @@ -1,16 +1,15 @@ import 'package:flutter/material.dart'; -import '../../../../extensions/quill_provider.dart'; +import '../../../../extensions/quill_configurations_ext.dart'; import '../../../../l10n/extensions/localizations.dart'; import '../../../../l10n/widgets/localizations.dart'; import '../../../../models/documents/attribute.dart'; import '../../../../models/documents/style.dart'; import '../../../../models/themes/quill_icon_theme.dart'; import '../../../../utils/color.dart'; -import '../../../controller.dart'; -import '../../../utils/provider.dart'; +import '../../../quill/quill_controller.dart'; import '../../base_toolbar.dart'; -import 'dialog.dart'; +import 'color_dialog.dart'; /// Controls color styles. /// @@ -194,18 +193,14 @@ class QuillToolbarColorButtonState extends State { ); } - return QuillToolbarIconButton( + return IconButton( tooltip: tooltip, - highlightElevation: 0, - hoverElevation: 0, - size: iconSize * iconButtonFactor, - icon: Icon(iconData, - size: iconSize, - color: widget.isBackground ? iconColorBackground : iconColor), - fillColor: widget.isBackground ? fillColorBackground : fillColor, - borderRadius: iconTheme?.borderRadius ?? 2, + iconSize: iconSize * iconButtonFactor, + icon: Icon( + iconData, + color: widget.isBackground ? iconColorBackground : iconColor, + ), onPressed: _showColorPicker, - afterPressed: afterButtonPressed, ); } @@ -226,16 +221,14 @@ class QuillToolbarColorButtonState extends State { showDialog( context: context, barrierColor: options.dialogBarrierColor ?? - context.requireQuillSharedConfigurations.dialogBarrierColor, - builder: (_) => QuillProvider.value( - value: context.requireQuillProvider, - child: FlutterQuillLocalizationsWidget( - child: ColorPickerDialog( - isBackground: widget.isBackground, - onRequestChangeColor: _changeColor, - isToggledColor: _isToggledColor, - selectionStyle: _selectionStyle, - ), + context.quillSharedConfigurations?.dialogBarrierColor ?? + Colors.black54, + builder: (_) => FlutterQuillLocalizationsWidget( + child: ColorPickerDialog( + isBackground: widget.isBackground, + onRequestChangeColor: _changeColor, + isToggledColor: _isToggledColor, + selectionStyle: _selectionStyle, ), ), ); diff --git a/lib/src/widgets/toolbar/buttons/color/dialog.dart b/lib/src/widgets/toolbar/buttons/color/color_dialog.dart similarity index 99% rename from lib/src/widgets/toolbar/buttons/color/dialog.dart rename to lib/src/widgets/toolbar/buttons/color/color_dialog.dart index a9806f51c..3ce6bf814 100644 --- a/lib/src/widgets/toolbar/buttons/color/dialog.dart +++ b/lib/src/widgets/toolbar/buttons/color/color_dialog.dart @@ -4,7 +4,7 @@ import 'package:flutter_colorpicker/flutter_colorpicker.dart' import '../../../../../translations.dart'; import '../../../../models/documents/style.dart'; -import 'color.dart' show hexToColor; +import 'color_button.dart' show hexToColor; class ColorPickerDialog extends StatefulWidget { const ColorPickerDialog({ diff --git a/lib/src/widgets/toolbar/buttons/custom_button.dart b/lib/src/widgets/toolbar/buttons/custom_button_button.dart similarity index 88% rename from lib/src/widgets/toolbar/buttons/custom_button.dart rename to lib/src/widgets/toolbar/buttons/custom_button_button.dart index f306a440b..825b3d2f9 100644 --- a/lib/src/widgets/toolbar/buttons/custom_button.dart +++ b/lib/src/widgets/toolbar/buttons/custom_button_button.dart @@ -1,14 +1,14 @@ import 'package:flutter/material.dart'; -import '../../../extensions/quill_provider.dart'; +import '../../../extensions/quill_configurations_ext.dart'; import '../../../models/themes/quill_icon_theme.dart'; -import '../../controller.dart'; +import '../../quill/quill_controller.dart'; import '../base_toolbar.dart'; class QuillToolbarCustomButton extends StatelessWidget { const QuillToolbarCustomButton({ - required this.options, required this.controller, + this.options = const QuillToolbarCustomButtonOptions(), super.key, }); @@ -80,15 +80,12 @@ class QuillToolbarCustomButton extends StatelessWidget { ); } - final theme = Theme.of(context); return QuillToolbarIconButton( - size: iconSize * iconButtonFactor, - icon: options.icon, + icon: options.icon ?? const SizedBox.shrink(), + isFilled: false, tooltip: tooltip, - borderRadius: iconTheme?.borderRadius ?? 2, onPressed: () => _onPressed(context), afterPressed: afterButtonPressed, - fillColor: iconTheme?.iconUnselectedFillColor ?? theme.canvasColor, ); } } diff --git a/lib/src/widgets/toolbar/buttons/font_family.dart b/lib/src/widgets/toolbar/buttons/font_family_button.dart similarity index 91% rename from lib/src/widgets/toolbar/buttons/font_family.dart rename to lib/src/widgets/toolbar/buttons/font_family_button.dart index fbfa954ea..8714d6fa3 100644 --- a/lib/src/widgets/toolbar/buttons/font_family.dart +++ b/lib/src/widgets/toolbar/buttons/font_family_button.dart @@ -1,19 +1,19 @@ import 'package:flutter/material.dart'; import '../../../../extensions.dart'; -import '../../../extensions/quill_provider.dart'; +import '../../../extensions/quill_configurations_ext.dart'; import '../../../l10n/extensions/localizations.dart'; -import '../../../models/config/toolbar/buttons/font_family.dart'; +import '../../../models/config/toolbar/buttons/font_family_configurations.dart'; import '../../../models/documents/attribute.dart'; import '../../../models/documents/style.dart'; import '../../../models/themes/quill_icon_theme.dart'; -import '../../controller.dart'; +import '../../quill/quill_controller.dart'; class QuillToolbarFontFamilyButton extends StatefulWidget { QuillToolbarFontFamilyButton({ - required this.options, required this.controller, required this.defaultDispalyText, + this.options = const QuillToolbarFontFamilyButtonOptions(), super.key, }) : assert(options.rawItemsMap?.isNotEmpty ?? (true)), assert( @@ -86,19 +86,20 @@ class QuillToolbarFontFamilyButtonState } Map get rawItemsMap { - // context.requireQuillToolbarConfigurations.buttonOptions; - final rawItemsMap = options.rawItemsMap ?? - { - 'Sans Serif': 'sans-serif', - 'Serif': 'serif', - 'Monospace': 'monospace', - 'Ibarra Real Nova': 'ibarra-real-nova', - 'SquarePeg': 'square-peg', - 'Nunito': 'nunito', - 'Pacifico': 'pacifico', - 'Roboto Mono': 'roboto-mono', - context.loc.clear: 'Clear' - }; + final rawItemsMap = + context.quillSimpleToolbarConfigurations?.fontFamilyValues ?? + options.rawItemsMap ?? + { + 'Sans Serif': 'sans-serif', + 'Serif': 'serif', + 'Monospace': 'monospace', + 'Ibarra Real Nova': 'ibarra-real-nova', + 'SquarePeg': 'square-peg', + 'Nunito': 'nunito', + 'Pacifico': 'pacifico', + 'Roboto Mono': 'roboto-mono', + context.loc.clear: 'Clear' + }; return rawItemsMap; } diff --git a/lib/src/widgets/toolbar/buttons/font_size.dart b/lib/src/widgets/toolbar/buttons/font_size_button.dart similarity index 95% rename from lib/src/widgets/toolbar/buttons/font_size.dart rename to lib/src/widgets/toolbar/buttons/font_size_button.dart index 621fc5c7f..d1ee7752e 100644 --- a/lib/src/widgets/toolbar/buttons/font_size.dart +++ b/lib/src/widgets/toolbar/buttons/font_size_button.dart @@ -1,20 +1,20 @@ import 'package:flutter/material.dart'; import '../../../../extensions.dart'; -import '../../../extensions/quill_provider.dart'; +import '../../../extensions/quill_configurations_ext.dart'; import '../../../l10n/extensions/localizations.dart'; import '../../../models/config/quill_configurations.dart'; import '../../../models/documents/attribute.dart'; import '../../../models/documents/style.dart'; import '../../../models/themes/quill_icon_theme.dart'; import '../../../utils/font.dart'; -import '../../controller.dart'; +import '../../quill/quill_controller.dart'; class QuillToolbarFontSizeButton extends StatefulWidget { QuillToolbarFontSizeButton({ - required this.options, required this.controller, required this.defaultDisplayText, + this.options = const QuillToolbarFontSizeButtonOptions(), super.key, }) : assert(options.rawItemsMap?.isNotEmpty ?? true), assert(options.initialValue == null || @@ -43,7 +43,7 @@ class QuillToolbarFontSizeButtonState Map get rawItemsMap { final fontSizes = options.rawItemsMap ?? - context.requireQuillToolbarConfigurations.fontSizesValues ?? + context.quillSimpleToolbarConfigurations?.fontSizesValues ?? { context.loc.small: 'small', context.loc.large: 'large', @@ -112,10 +112,9 @@ class QuillToolbarFontSizeButtonState } double get iconSize { - final baseFontSize = - context.requireQuillToolbarBaseButtonOptions.globalIconSize; + final baseFontSize = context.quillToolbarBaseButtonOptions?.globalIconSize; final iconSize = options.iconSize; - return iconSize ?? baseFontSize; + return iconSize ?? baseFontSize ?? kDefaultIconSize; } double get iconButtonFactor { diff --git a/lib/src/widgets/toolbar/buttons/history.dart b/lib/src/widgets/toolbar/buttons/history_button.dart similarity index 85% rename from lib/src/widgets/toolbar/buttons/history.dart rename to lib/src/widgets/toolbar/buttons/history_button.dart index bbf7c54f8..5436e74cf 100644 --- a/lib/src/widgets/toolbar/buttons/history.dart +++ b/lib/src/widgets/toolbar/buttons/history_button.dart @@ -1,17 +1,22 @@ import 'package:flutter/material.dart'; -import '../../../extensions/quill_provider.dart'; +import '../../../extensions/quill_configurations_ext.dart'; import '../../../l10n/extensions/localizations.dart'; -import '../../controller.dart'; +import '../../quill/quill_controller.dart'; import '../base_toolbar.dart'; class QuillToolbarHistoryButton extends StatefulWidget { const QuillToolbarHistoryButton({ - required this.options, required this.controller, + required this.isUndo, + this.options = const QuillToolbarHistoryButtonOptions(), super.key, }); + /// If this true then it will be the undo button + /// otherwise it will be redo + final bool isUndo; + final QuillToolbarHistoryButtonOptions options; final QuillController controller; @@ -53,10 +58,10 @@ class QuillToolbarHistoryButtonState extends State { context.requireQuillToolbarBaseButtonOptions; final tooltip = options.tooltip ?? baseButtonConfigurations.tooltip ?? - (options.isUndo ? context.loc.undo : context.loc.redo); + (widget.isUndo ? context.loc.undo : context.loc.redo); final iconData = options.iconData ?? baseButtonConfigurations.iconData ?? - (options.isUndo ? Icons.undo_outlined : Icons.redo_outlined); + (widget.isUndo ? Icons.undo_outlined : Icons.redo_outlined); final childBuilder = options.childBuilder ?? baseButtonConfigurations.childBuilder; final iconSize = @@ -71,7 +76,6 @@ class QuillToolbarHistoryButtonState extends State { if (childBuilder != null) { return childBuilder( QuillToolbarHistoryButtonOptions( - isUndo: options.isUndo, afterButtonPressed: afterButtonPressed, controller: controller, iconData: iconData, @@ -93,22 +97,16 @@ class QuillToolbarHistoryButtonState extends State { } theme = Theme.of(context); - - final fillColor = iconTheme?.iconUnselectedFillColor ?? theme.canvasColor; return QuillToolbarIconButton( tooltip: tooltip, - highlightElevation: 0, - hoverElevation: 0, - size: iconSize * iconButtonFactor, icon: Icon( iconData, - size: iconSize, + size: iconSize * iconButtonFactor, color: _canPressed ? iconTheme?.iconUnselectedColor ?? theme.iconTheme.color : iconTheme?.disabledIconColor ?? theme.disabledColor, ), - fillColor: fillColor, - borderRadius: iconTheme?.borderRadius ?? 2, + isFilled: false, onPressed: _updateHistory, afterPressed: afterButtonPressed, ); @@ -121,7 +119,7 @@ class QuillToolbarHistoryButtonState extends State { } void _updateCanPressed() { - if (options.isUndo) { + if (widget.isUndo) { _canPressed = controller.hasUndo; return; } @@ -129,7 +127,7 @@ class QuillToolbarHistoryButtonState extends State { } void _updateHistory() { - if (options.isUndo) { + if (widget.isUndo) { if (controller.hasUndo) { controller.undo(); } diff --git a/lib/src/widgets/toolbar/buttons/indent.dart b/lib/src/widgets/toolbar/buttons/indent_button.dart similarity index 86% rename from lib/src/widgets/toolbar/buttons/indent.dart rename to lib/src/widgets/toolbar/buttons/indent_button.dart index 22c3eb29a..654b3d472 100644 --- a/lib/src/widgets/toolbar/buttons/indent.dart +++ b/lib/src/widgets/toolbar/buttons/indent_button.dart @@ -1,10 +1,10 @@ import 'package:flutter/material.dart'; -import '../../../extensions/quill_provider.dart'; +import '../../../extensions/quill_configurations_ext.dart'; import '../../../l10n/extensions/localizations.dart'; -import '../../../models/config/toolbar/buttons/indent.dart'; +import '../../../models/config/toolbar/buttons/indent_configurations.dart'; import '../../../models/themes/quill_icon_theme.dart'; -import '../../controller.dart'; +import '../../quill/quill_controller.dart'; import '../base_toolbar.dart' show QuillToolbarBaseButtonOptions, QuillToolbarIconButton; @@ -12,7 +12,7 @@ class QuillToolbarIndentButton extends StatefulWidget { const QuillToolbarIndentButton({ required this.controller, required this.isIncrease, - required this.options, + this.options = const QuillToolbarIndentButtonOptions(), super.key, }); @@ -107,16 +107,10 @@ class QuillToolbarIndentButtonState extends State { final theme = Theme.of(context); final iconColor = iconTheme?.iconUnselectedColor ?? theme.iconTheme.color; - final iconFillColor = - iconTheme?.iconUnselectedFillColor ?? theme.canvasColor; return QuillToolbarIconButton( tooltip: tooltip, - highlightElevation: 0, - hoverElevation: 0, - size: iconSize * iconButtonFactor, - icon: Icon(iconData, size: iconSize, color: iconColor), - fillColor: iconFillColor, - borderRadius: iconTheme?.borderRadius ?? 2, + icon: Icon(iconData, size: iconSize * iconButtonFactor, color: iconColor), + isFilled: false, onPressed: _sharedOnPressed, afterPressed: afterButtonPressed, ); diff --git a/lib/src/widgets/toolbar/buttons/link_style2.dart b/lib/src/widgets/toolbar/buttons/link_style2_button.dart similarity index 91% rename from lib/src/widgets/toolbar/buttons/link_style2.dart rename to lib/src/widgets/toolbar/buttons/link_style2_button.dart index 517f7b5b1..8fec6c3a6 100644 --- a/lib/src/widgets/toolbar/buttons/link_style2.dart +++ b/lib/src/widgets/toolbar/buttons/link_style2_button.dart @@ -4,15 +4,14 @@ import 'package:url_launcher/link.dart'; import '../../../../extensions.dart' show UtilityWidgets, AutoFormatMultipleLinksRule; -import '../../../extensions/quill_provider.dart'; +import '../../../extensions/quill_configurations_ext.dart'; import '../../../l10n/extensions/localizations.dart'; import '../../../l10n/widgets/localizations.dart'; import '../../../models/documents/attribute.dart'; import '../../../models/themes/quill_dialog_theme.dart'; import '../../../models/themes/quill_icon_theme.dart'; -import '../../controller.dart'; -import '../../link.dart'; -import '../../utils/provider.dart'; +import '../../others/link.dart'; +import '../../quill/quill_controller.dart'; import '../base_toolbar.dart'; /// Alternative version of [QuillToolbarLinkStyleButton]. This widget has more @@ -21,7 +20,7 @@ import '../base_toolbar.dart'; class QuillToolbarLinkStyleButton2 extends StatefulWidget { QuillToolbarLinkStyleButton2({ required this.controller, - required this.options, + this.options = const QuillToolbarLinkStyleButton2Options(), super.key, }) : assert(options.addLinkLabel == null || (options.addLinkLabel?.isNotEmpty ?? true)), @@ -107,7 +106,8 @@ class _QuillToolbarLinkStyleButton2State Color get dialogBarrierColor { return options.dialogBarrierColor ?? - context.requireQuillSharedConfigurations.dialogBarrierColor; + context.quillSharedConfigurations?.dialogBarrierColor ?? + Colors.black54; } @override @@ -148,20 +148,14 @@ class _QuillToolbarLinkStyleButton2State final isToggled = _getLinkAttributeValue() != null; return QuillToolbarIconButton( tooltip: tooltip, - highlightElevation: 0, - hoverElevation: 0, - size: iconSize * iconButtonFactor, icon: Icon( iconData, - size: iconSize, + size: iconSize * iconButtonFactor, color: isToggled ? (iconTheme?.iconSelectedColor ?? theme.primaryIconTheme.color) : (iconTheme?.iconUnselectedColor ?? theme.iconTheme.color), ), - fillColor: isToggled - ? (iconTheme?.iconSelectedFillColor ?? theme.primaryColor) - : (iconTheme?.iconUnselectedFillColor ?? theme.canvasColor), - borderRadius: iconTheme?.borderRadius ?? 2, + isFilled: isToggled, onPressed: _openLinkDialog, afterPressed: afterButtonPressed, ); @@ -173,22 +167,19 @@ class _QuillToolbarLinkStyleButton2State final textLink = await showDialog( context: context, barrierColor: dialogBarrierColor, - builder: (_) => QuillProvider.value( - value: context.requireQuillProvider, - child: FlutterQuillLocalizationsWidget( - child: LinkStyleDialog( - dialogTheme: options.dialogTheme, - text: initialTextLink.text, - link: initialTextLink.link, - constraints: options.constraints, - addLinkLabel: options.addLinkLabel, - editLinkLabel: options.editLinkLabel, - linkColor: options.linkColor, - childrenSpacing: options.childrenSpacing, - autovalidateMode: options.autovalidateMode, - validationMessage: options.validationMessage, - buttonSize: options.buttonSize, - ), + builder: (_) => FlutterQuillLocalizationsWidget( + child: LinkStyleDialog( + dialogTheme: options.dialogTheme, + text: initialTextLink.text, + link: initialTextLink.link, + constraints: options.constraints, + addLinkLabel: options.addLinkLabel, + editLinkLabel: options.editLinkLabel, + linkColor: options.linkColor, + childrenSpacing: options.childrenSpacing, + autovalidateMode: options.autovalidateMode, + validationMessage: options.validationMessage, + buttonSize: options.buttonSize, ), ), ); diff --git a/lib/src/widgets/toolbar/buttons/link_style.dart b/lib/src/widgets/toolbar/buttons/link_style_button.dart similarity index 91% rename from lib/src/widgets/toolbar/buttons/link_style.dart rename to lib/src/widgets/toolbar/buttons/link_style_button.dart index c87eb0e92..33ae60e9c 100644 --- a/lib/src/widgets/toolbar/buttons/link_style.dart +++ b/lib/src/widgets/toolbar/buttons/link_style_button.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; -import '../../../extensions/quill_provider.dart'; +import '../../../extensions/quill_configurations_ext.dart'; import '../../../l10n/extensions/localizations.dart'; import '../../../l10n/widgets/localizations.dart'; import '../../../models/documents/attribute.dart'; @@ -8,15 +8,14 @@ import '../../../models/rules/insert.dart'; import '../../../models/structs/link_dialog_action.dart'; import '../../../models/themes/quill_dialog_theme.dart'; import '../../../models/themes/quill_icon_theme.dart'; -import '../../controller.dart'; -import '../../link.dart'; -import '../../utils/provider.dart'; +import '../../others/link.dart'; +import '../../quill/quill_controller.dart'; import '../base_toolbar.dart'; class QuillToolbarLinkStyleButton extends StatefulWidget { const QuillToolbarLinkStyleButton({ required this.controller, - required this.options, + this.options = const QuillToolbarLinkStyleButtonOptions(), super.key, }); @@ -100,7 +99,8 @@ class QuillToolbarLinkStyleButtonState Color get dialogBarrierColor { return options.dialogBarrierColor ?? - context.requireQuillSharedConfigurations.dialogBarrierColor; + context.quillSharedConfigurations?.dialogBarrierColor ?? + Colors.black54; } RegExp? get linkRegExp { @@ -141,27 +141,21 @@ class QuillToolbarLinkStyleButtonState final theme = Theme.of(context); return QuillToolbarIconButton( tooltip: tooltip, - highlightElevation: 0, - hoverElevation: 0, - size: iconSize * iconButtonFactor, icon: Icon( iconData, - size: iconSize, + size: iconSize * iconButtonFactor, color: isToggled ? (iconTheme?.iconSelectedColor ?? theme.primaryIconTheme.color) : (iconTheme?.iconUnselectedColor ?? theme.iconTheme.color), ), - fillColor: isToggled - ? (iconTheme?.iconSelectedFillColor ?? theme.primaryColor) - : (iconTheme?.iconUnselectedFillColor ?? theme.canvasColor), - borderRadius: iconTheme?.borderRadius ?? 2, + isFilled: isToggled, onPressed: () => _openLinkDialog(context), afterPressed: afterButtonPressed, ); } Future _openLinkDialog(BuildContext context) async { - // TODO: Add a custom call back to customize this just like in the search + // TODO: Add a custom call back to customize this just like in the search dialog // button final value = await showDialog<_TextLink>( context: context, @@ -181,16 +175,13 @@ class QuillToolbarLinkStyleButtonState final len = controller.selection.end - index; text ??= len == 0 ? '' : controller.document.getPlainText(index, len); - return QuillProvider.value( - value: context.requireQuillProvider, - child: FlutterQuillLocalizationsWidget( - child: _LinkDialog( - dialogTheme: options.dialogTheme, - link: link, - text: text, - linkRegExp: linkRegExp, - action: options.linkDialogAction, - ), + return FlutterQuillLocalizationsWidget( + child: _LinkDialog( + dialogTheme: options.dialogTheme, + link: link, + text: text, + linkRegExp: linkRegExp, + action: options.linkDialogAction, ), ); }, diff --git a/lib/src/widgets/toolbar/buttons/quill_icon.dart b/lib/src/widgets/toolbar/buttons/quill_icon.dart deleted file mode 100644 index d78494d0f..000000000 --- a/lib/src/widgets/toolbar/buttons/quill_icon.dart +++ /dev/null @@ -1,54 +0,0 @@ -import 'package:flutter/material.dart'; - -import '../../../utils/widgets.dart'; - -class QuillToolbarIconButton extends StatelessWidget { - const QuillToolbarIconButton({ - required this.onPressed, - this.afterPressed, - this.icon, - this.size = 40, - this.fillColor, - this.hoverElevation = 1, - this.highlightElevation = 1, - this.borderRadius = 2, - this.tooltip, - super.key, - }); - - final VoidCallback? onPressed; - final VoidCallback? afterPressed; - final Widget? icon; - - final double size; - final Color? fillColor; - final double hoverElevation; - final double highlightElevation; - final double borderRadius; - final String? tooltip; - - @override - Widget build(BuildContext context) { - return ConstrainedBox( - constraints: BoxConstraints.tightFor(width: size, height: size), - child: UtilityWidgets.maybeTooltip( - message: tooltip, - child: RawMaterialButton( - visualDensity: VisualDensity.compact, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(borderRadius), - ), - fillColor: fillColor, - elevation: 0, - hoverElevation: hoverElevation, - highlightElevation: hoverElevation, - onPressed: () { - onPressed?.call(); - afterPressed?.call(); - }, - child: icon, - ), - ), - ); - } -} diff --git a/lib/src/widgets/toolbar/buttons/quill_icon_button.dart b/lib/src/widgets/toolbar/buttons/quill_icon_button.dart new file mode 100644 index 000000000..78c4eaf54 --- /dev/null +++ b/lib/src/widgets/toolbar/buttons/quill_icon_button.dart @@ -0,0 +1,33 @@ +import 'package:flutter/material.dart'; + +class QuillToolbarIconButton extends StatelessWidget { + const QuillToolbarIconButton({ + required this.onPressed, + required this.icon, + required this.isFilled, + this.afterPressed, + this.tooltip, + super.key, + }); + + final VoidCallback? onPressed; + final VoidCallback? afterPressed; + final Widget icon; + + final String? tooltip; + final bool isFilled; + + @override + Widget build(BuildContext context) { + if (isFilled) { + return IconButton.filled(onPressed: onPressed, icon: icon); + } + return IconButton( + onPressed: () { + onPressed?.call(); + afterPressed?.call(); + }, + icon: icon, + ); + } +} diff --git a/lib/src/widgets/toolbar/buttons/search/search.dart b/lib/src/widgets/toolbar/buttons/search/search_button.dart similarity index 81% rename from lib/src/widgets/toolbar/buttons/search/search.dart rename to lib/src/widgets/toolbar/buttons/search/search_button.dart index f800566b2..78936d35c 100644 --- a/lib/src/widgets/toolbar/buttons/search/search.dart +++ b/lib/src/widgets/toolbar/buttons/search/search_button.dart @@ -1,18 +1,17 @@ import 'package:flutter/material.dart'; -import '../../../../extensions/quill_provider.dart'; +import '../../../../extensions/quill_configurations_ext.dart'; import '../../../../l10n/extensions/localizations.dart'; import '../../../../l10n/widgets/localizations.dart'; import '../../../../models/themes/quill_dialog_theme.dart'; import '../../../../models/themes/quill_icon_theme.dart'; -import '../../../controller.dart'; -import '../../../utils/provider.dart'; +import '../../../quill/quill_controller.dart'; import '../../base_toolbar.dart'; class QuillToolbarSearchButton extends StatelessWidget { const QuillToolbarSearchButton({ required QuillController controller, - required this.options, + this.options = const QuillToolbarSearchButtonOptions(), super.key, }) : _controller = controller; @@ -63,12 +62,13 @@ class QuillToolbarSearchButton extends StatelessWidget { Color _dialogBarrierColor(BuildContext context) { return options.dialogBarrierColor ?? - context.requireQuillSharedConfigurations.dialogBarrierColor; + context.quillSharedConfigurations?.dialogBarrierColor ?? + Colors.black54; } QuillDialogTheme? _dialogTheme(BuildContext context) { return options.dialogTheme ?? - context.requireQuillSharedConfigurations.dialogTheme; + context.quillSharedConfigurations?.dialogTheme; } @override @@ -111,21 +111,15 @@ class QuillToolbarSearchButton extends StatelessWidget { final theme = Theme.of(context); final iconColor = iconTheme?.iconUnselectedColor ?? theme.iconTheme.color; - final iconFillColor = iconTheme?.iconUnselectedFillColor ?? - (options.fillColor ?? theme.canvasColor); return QuillToolbarIconButton( tooltip: tooltip, icon: Icon( iconData, - size: iconSize, + size: iconSize * iconButtonFactor, color: iconColor, ), - highlightElevation: 0, - hoverElevation: 0, - size: iconSize * iconButtonFactor, - fillColor: iconFillColor, - borderRadius: iconTheme?.borderRadius ?? 2, + isFilled: false, onPressed: () => _sharedOnPressed(context), afterPressed: afterButtonPressed, ); @@ -142,14 +136,11 @@ class QuillToolbarSearchButton extends StatelessWidget { await showDialog( barrierColor: _dialogBarrierColor(context), context: context, - builder: (_) => QuillProvider.value( - value: context.requireQuillProvider, - child: FlutterQuillLocalizationsWidget( - child: QuillToolbarSearchDialog( - controller: controller, - dialogTheme: _dialogTheme(context), - text: '', - ), + builder: (_) => FlutterQuillLocalizationsWidget( + child: QuillToolbarSearchDialog( + controller: controller, + dialogTheme: _dialogTheme(context), + text: '', ), ), ); diff --git a/lib/src/widgets/toolbar/buttons/search/search_dialog.dart b/lib/src/widgets/toolbar/buttons/search/search_dialog.dart index 2aa268c5a..337059f4f 100644 --- a/lib/src/widgets/toolbar/buttons/search/search_dialog.dart +++ b/lib/src/widgets/toolbar/buttons/search/search_dialog.dart @@ -3,7 +3,7 @@ import 'package:flutter/material.dart'; import '../../../../l10n/extensions/localizations.dart'; import '../../../../models/documents/document.dart'; import '../../../../models/themes/quill_dialog_theme.dart'; -import '../../../controller.dart'; +import '../../../quill/quill_controller.dart'; @immutable class QuillToolbarSearchDialogChildBuilderExtraOptions { diff --git a/lib/src/widgets/toolbar/buttons/select_alignment.dart b/lib/src/widgets/toolbar/buttons/select_alignment.dart deleted file mode 100644 index c81dc1e1d..000000000 --- a/lib/src/widgets/toolbar/buttons/select_alignment.dart +++ /dev/null @@ -1,281 +0,0 @@ -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; - -import '../../../extensions/quill_provider.dart'; -import '../../../l10n/extensions/localizations.dart'; -import '../../../models/documents/attribute.dart'; -import '../../../models/documents/style.dart'; -import '../../../models/themes/quill_icon_theme.dart'; -import '../../../utils/widgets.dart'; -import '../../controller.dart'; -import '../base_toolbar.dart'; - -class QuillToolbarSelectAlignmentButton extends StatefulWidget { - const QuillToolbarSelectAlignmentButton({ - required this.controller, - required this.options, - this.showLeftAlignment, - this.showCenterAlignment, - this.showRightAlignment, - this.showJustifyAlignment, - this.padding, - super.key, - }); - - final QuillController controller; - final QuillToolbarSelectAlignmentButtonOptions options; - - final bool? showLeftAlignment; - final bool? showCenterAlignment; - final bool? showRightAlignment; - final bool? showJustifyAlignment; - final EdgeInsetsGeometry? padding; - - @override - QuillToolbarSelectAlignmentButtonState createState() => - QuillToolbarSelectAlignmentButtonState(); -} - -class QuillToolbarSelectAlignmentButtonState - extends State { - Attribute? _value; - - Style get _selectionStyle => controller.getSelectionStyle(); - - @override - void initState() { - super.initState(); - setState(() { - _value = _selectionStyle.attributes[Attribute.align.key] ?? - Attribute.leftAlignment; - }); - controller.addListener(_didChangeEditingValue); - } - - QuillToolbarSelectAlignmentButtonOptions get options { - return widget.options; - } - - QuillController get controller { - return widget.controller; - } - - double get _iconSize { - final baseFontSize = baseButtonExtraOptions.globalIconSize; - final iconSize = options.iconSize; - return iconSize ?? baseFontSize; - } - - double get _iconButtonFactor { - final baseIconFactor = baseButtonExtraOptions.globalIconButtonFactor; - final iconButtonFactor = options.iconButtonFactor; - return iconButtonFactor ?? baseIconFactor; - } - - VoidCallback? get _afterButtonPressed { - return options.afterButtonPressed ?? - baseButtonExtraOptions.afterButtonPressed; - } - - QuillIconTheme? get _iconTheme { - return options.iconTheme ?? baseButtonExtraOptions.iconTheme; - } - - QuillToolbarBaseButtonOptions get baseButtonExtraOptions { - return context.requireQuillToolbarBaseButtonOptions; - } - - QuillSelectAlignmentValues get _iconsData { - final iconsData = options.iconsData; - if (iconsData != null) { - return iconsData; - } - final baseIconData = baseButtonExtraOptions.iconData; - if (baseIconData != null) { - return QuillSelectAlignmentValues( - leftAlignment: baseIconData, - centerAlignment: baseIconData, - rightAlignment: baseIconData, - justifyAlignment: baseIconData, - ); - } - return const QuillSelectAlignmentValues( - leftAlignment: Icons.format_align_left, - centerAlignment: Icons.format_align_center, - rightAlignment: Icons.format_align_right, - justifyAlignment: Icons.format_align_justify, - ); - } - - QuillSelectAlignmentValues get _tooltips { - final tooltips = options.tooltips; - if (tooltips != null) { - return tooltips; - } - final baseToolTip = baseButtonExtraOptions.tooltip; - if (baseToolTip != null) { - return QuillSelectAlignmentValues( - leftAlignment: baseToolTip, - centerAlignment: baseToolTip, - rightAlignment: baseToolTip, - justifyAlignment: baseToolTip, - ); - } - return QuillSelectAlignmentValues( - leftAlignment: context.loc.alignLeft, - centerAlignment: context.loc.alignCenter, - rightAlignment: context.loc.alignRight, - justifyAlignment: context.loc.justifyWinWidth, - ); - } - - void _didChangeEditingValue() { - setState(() { - _value = _selectionStyle.attributes[Attribute.align.key] ?? - Attribute.leftAlignment; - }); - } - - @override - void didUpdateWidget(covariant QuillToolbarSelectAlignmentButton oldWidget) { - super.didUpdateWidget(oldWidget); - if (oldWidget.controller != controller) { - oldWidget.controller.removeListener(_didChangeEditingValue); - controller.addListener(_didChangeEditingValue); - _value = _selectionStyle.attributes[Attribute.align.key] ?? - Attribute.leftAlignment; - } - } - - @override - void dispose() { - controller.removeListener(_didChangeEditingValue); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - final valueToText = { - if (widget.showLeftAlignment!) - Attribute.leftAlignment: Attribute.leftAlignment.value!, - if (widget.showCenterAlignment!) - Attribute.centerAlignment: Attribute.centerAlignment.value!, - if (widget.showRightAlignment!) - Attribute.rightAlignment: Attribute.rightAlignment.value!, - if (widget.showJustifyAlignment!) - Attribute.justifyAlignment: Attribute.justifyAlignment.value!, - }; - - final valueAttribute = [ - if (widget.showLeftAlignment!) Attribute.leftAlignment, - if (widget.showCenterAlignment!) Attribute.centerAlignment, - if (widget.showRightAlignment!) Attribute.rightAlignment, - if (widget.showJustifyAlignment!) Attribute.justifyAlignment - ]; - final valueString = [ - if (widget.showLeftAlignment!) Attribute.leftAlignment.value!, - if (widget.showCenterAlignment!) Attribute.centerAlignment.value!, - if (widget.showRightAlignment!) Attribute.rightAlignment.value!, - if (widget.showJustifyAlignment!) Attribute.justifyAlignment.value!, - ]; - // final _valueToButtons = { - // if (widget.showLeftAlignment!) - // Attribute.leftAlignment: ToolbarButtons.leftAlignment, - // if (widget.showCenterAlignment!) - // Attribute.centerAlignment: ToolbarButtons.centerAlignment, - // if (widget.showRightAlignment!) - // Attribute.rightAlignment: ToolbarButtons.rightAlignment, - // if (widget.showJustifyAlignment!) - // Attribute.justifyAlignment: ToolbarButtons.justifyAlignment, - // }; - - final buttonCount = ((widget.showLeftAlignment!) ? 1 : 0) + - ((widget.showCenterAlignment!) ? 1 : 0) + - ((widget.showRightAlignment!) ? 1 : 0) + - ((widget.showJustifyAlignment!) ? 1 : 0); - - final childBuilder = - options.childBuilder ?? baseButtonExtraOptions.childBuilder; - - void sharedOnPressed(int index) { - valueAttribute[index] == Attribute.leftAlignment - ? controller.formatSelection( - Attribute.clone(Attribute.align, null), - ) - : controller.formatSelection(valueAttribute[index]); - _afterButtonPressed?.call(); - } - - return Row( - mainAxisSize: MainAxisSize.min, - children: List.generate(buttonCount, (index) { - if (childBuilder != null) { - return childBuilder( - QuillToolbarSelectAlignmentButtonOptions( - afterButtonPressed: _afterButtonPressed, - iconSize: _iconSize, - iconButtonFactor: _iconButtonFactor, - iconTheme: _iconTheme, - tooltips: _tooltips, - iconsData: _iconsData, - ), - QuillToolbarSelectAlignmentButtonExtraOptions( - context: context, - controller: controller, - onPressed: () => sharedOnPressed(index), - ), - ); - } - final theme = Theme.of(context); - return Padding( - padding: widget.padding ?? - const EdgeInsets.symmetric(horizontal: !kIsWeb ? 1.0 : 5.0), - child: ConstrainedBox( - constraints: BoxConstraints.tightFor( - width: _iconSize * _iconButtonFactor, - height: _iconSize * _iconButtonFactor, - ), - child: UtilityWidgets.maybeTooltip( - message: valueString[index] == Attribute.leftAlignment.value - ? _tooltips.leftAlignment - : valueString[index] == Attribute.centerAlignment.value - ? _tooltips.centerAlignment - : valueString[index] == Attribute.rightAlignment.value - ? _tooltips.rightAlignment - : _tooltips.justifyAlignment, - child: RawMaterialButton( - hoverElevation: 0, - highlightElevation: 0, - elevation: 0, - visualDensity: VisualDensity.compact, - shape: RoundedRectangleBorder( - borderRadius: - BorderRadius.circular(_iconTheme?.borderRadius ?? 2)), - fillColor: valueToText[_value] == valueString[index] - ? (_iconTheme?.iconSelectedFillColor ?? theme.primaryColor) - : (_iconTheme?.iconUnselectedFillColor ?? - theme.canvasColor), - onPressed: () => sharedOnPressed(index), - child: Icon( - valueString[index] == Attribute.leftAlignment.value - ? _iconsData.leftAlignment - : valueString[index] == Attribute.centerAlignment.value - ? _iconsData.centerAlignment - : valueString[index] == Attribute.rightAlignment.value - ? _iconsData.rightAlignment - : _iconsData.justifyAlignment, - size: _iconSize, - color: valueToText[_value] == valueString[index] - ? (_iconTheme?.iconSelectedColor ?? - theme.primaryIconTheme.color) - : (_iconTheme?.iconUnselectedColor ?? - theme.iconTheme.color), - ), - ), - ), - ), - ); - }), - ); - } -} diff --git a/lib/src/widgets/toolbar/buttons/select_alignment_buttons.dart b/lib/src/widgets/toolbar/buttons/select_alignment_buttons.dart new file mode 100644 index 000000000..7259a2c59 --- /dev/null +++ b/lib/src/widgets/toolbar/buttons/select_alignment_buttons.dart @@ -0,0 +1,52 @@ +import 'package:flutter/material.dart'; + +import '../../../models/config/toolbar/buttons/select_alignment_configurations.dart'; +import '../../../models/documents/attribute.dart'; +import '../../quill/quill_controller.dart'; +import 'toggle_style_button.dart'; + +enum _AlignmentOptions { + left(attribute: Attribute.leftAlignment), + center(attribute: Attribute.centerAlignment), + right(attribute: Attribute.rightAlignment), + justifyMinWidth(attribute: Attribute.justifyAlignment); + + const _AlignmentOptions({required this.attribute}); + + final Attribute attribute; +} + +class QuillToolbarSelectAlignmentButtons extends StatelessWidget { + const QuillToolbarSelectAlignmentButtons({ + required this.controller, + this.options = const QuillToolbarSelectAlignmentButtonOptions(), + this.showLeftAlignment, + this.showCenterAlignment, + this.showRightAlignment, + this.showJustifyAlignment, + this.padding, + super.key, + }); + + final QuillController controller; + final QuillToolbarSelectAlignmentButtonOptions options; + + final bool? showLeftAlignment; + final bool? showCenterAlignment; + final bool? showRightAlignment; + final bool? showJustifyAlignment; + final EdgeInsetsGeometry? padding; + + @override + Widget build(BuildContext context) { + return Row( + mainAxisSize: MainAxisSize.min, + children: _AlignmentOptions.values + .map((e) => QuillToolbarToggleStyleButton( + controller: controller, + attribute: e.attribute, + )) + .toList(), + ); + } +} diff --git a/lib/src/widgets/toolbar/buttons/select_header_style_button.dart b/lib/src/widgets/toolbar/buttons/select_header_style_button.dart new file mode 100644 index 000000000..1e92c84fd --- /dev/null +++ b/lib/src/widgets/toolbar/buttons/select_header_style_button.dart @@ -0,0 +1,116 @@ +import 'package:flutter/material.dart'; + +import '../../../../translations.dart'; +import '../../../models/config/toolbar/buttons/select_header_style_configurations.dart'; +import '../../../models/documents/attribute.dart'; +import '../../quill/quill_controller.dart'; + +enum _HeaderStyleOptions { + normal, + headingOne, + headingTwo, + headingThree, +} + +class QuillToolbarSelectHeaderStyleButton extends StatefulWidget { + const QuillToolbarSelectHeaderStyleButton({ + required this.controller, + this.options = const QuillToolbarSelectHeaderStyleButtonsOptions(), + super.key, + }); + + final QuillController controller; + // TODO: Needs to be reviewed + final QuillToolbarSelectHeaderStyleButtonsOptions options; + + @override + State createState() => + _QuillToolbarSelectHeaderStyleButtonState(); +} + +class _QuillToolbarSelectHeaderStyleButtonState + extends State { + var _selectedItem = _HeaderStyleOptions.normal; + @override + void initState() { + super.initState(); + widget.controller.addListener(_didChangeEditingValue); + } + + void _didChangeEditingValue() { + final newSelectedItem = _getOptionsItemByAttribute(_getHeaderValue()); + if (newSelectedItem == _selectedItem) { + return; + } + setState(() { + _selectedItem = newSelectedItem; + }); + } + + Attribute _getHeaderValue() { + final attr = widget.controller.toolbarButtonToggler[Attribute.header.key]; + if (attr != null) { + // checkbox tapping causes controller.selection to go to offset 0 + widget.controller.toolbarButtonToggler.remove(Attribute.header.key); + return attr; + } + return widget.controller + .getSelectionStyle() + .attributes[Attribute.header.key] ?? + Attribute.header; + } + + String _label(_HeaderStyleOptions value) { + final label = switch (value) { + _HeaderStyleOptions.normal => context.loc.normal, + _HeaderStyleOptions.headingOne => context.loc.heading1, + _HeaderStyleOptions.headingTwo => context.loc.heading2, + _HeaderStyleOptions.headingThree => context.loc.heading3, + }; + return label; + } + + Attribute? getAttributeByOptionsItem(_HeaderStyleOptions option) { + return switch (option) { + _HeaderStyleOptions.normal => Attribute.header, + _HeaderStyleOptions.headingOne => Attribute.h1, + _HeaderStyleOptions.headingTwo => Attribute.h2, + _HeaderStyleOptions.headingThree => Attribute.h3, + }; + } + + _HeaderStyleOptions _getOptionsItemByAttribute( + Attribute? attribute) { + return switch (attribute) { + Attribute.h1 => _HeaderStyleOptions.headingOne, + Attribute.h2 => _HeaderStyleOptions.headingTwo, + Attribute.h2 => _HeaderStyleOptions.headingThree, + Attribute() => _HeaderStyleOptions.normal, + null => _HeaderStyleOptions.normal, + }; + } + + @override + Widget build(BuildContext context) { + return DropdownButton<_HeaderStyleOptions>( + value: _selectedItem, + items: _HeaderStyleOptions.values + .map( + (e) => DropdownMenuItem<_HeaderStyleOptions>( + value: e, + child: Text(_label(e)), + onTap: () { + widget.controller.formatSelection(getAttributeByOptionsItem(e)); + }, + ), + ) + .toList(), + onChanged: (newItem) { + if (newItem == null) { + return; + } + setState(() => _selectedItem = newItem); + }, + ); + } +} diff --git a/lib/src/widgets/toolbar/buttons/select_header_style.dart b/lib/src/widgets/toolbar/buttons/select_header_style_buttons.dart similarity index 96% rename from lib/src/widgets/toolbar/buttons/select_header_style.dart rename to lib/src/widgets/toolbar/buttons/select_header_style_buttons.dart index 791b81320..a49151860 100644 --- a/lib/src/widgets/toolbar/buttons/select_header_style.dart +++ b/lib/src/widgets/toolbar/buttons/select_header_style_buttons.dart @@ -2,18 +2,18 @@ import 'package:flutter/foundation.dart' show kIsWeb; import 'package:flutter/material.dart'; import '../../../../extensions.dart'; -import '../../../extensions/quill_provider.dart'; +import '../../../extensions/quill_configurations_ext.dart'; import '../../../l10n/extensions/localizations.dart'; import '../../../models/documents/attribute.dart'; import '../../../models/documents/style.dart'; import '../../../models/themes/quill_icon_theme.dart'; -import '../../controller.dart'; +import '../../quill/quill_controller.dart'; import '../base_toolbar.dart'; class QuillToolbarSelectHeaderStyleButtons extends StatefulWidget { const QuillToolbarSelectHeaderStyleButtons({ required this.controller, - required this.options, + this.options = const QuillToolbarSelectHeaderStyleButtonsOptions(), super.key, }); @@ -88,8 +88,8 @@ class QuillToolbarSelectHeaderStyleButtonsState Axis get axis { return options.axis ?? + context.quillSimpleToolbarConfigurations?.axis ?? context.quillToolbarConfigurations?.axis ?? - context.quillBaseToolbarConfigurations?.axis ?? (throw ArgumentError( 'There is no default value for the Axis of the toolbar')); } diff --git a/lib/src/widgets/toolbar/buttons/toggle_check_list.dart b/lib/src/widgets/toolbar/buttons/toggle_check_list_button.dart similarity index 94% rename from lib/src/widgets/toolbar/buttons/toggle_check_list.dart rename to lib/src/widgets/toolbar/buttons/toggle_check_list_button.dart index 52ee2f1d9..7c2ba94f9 100644 --- a/lib/src/widgets/toolbar/buttons/toggle_check_list.dart +++ b/lib/src/widgets/toolbar/buttons/toggle_check_list_button.dart @@ -1,20 +1,20 @@ import 'package:flutter/material.dart'; -import '../../../extensions/quill_provider.dart'; +import '../../../extensions/quill_configurations_ext.dart'; import '../../../l10n/extensions/localizations.dart'; -import '../../../models/config/toolbar/buttons/base.dart'; -import '../../../models/config/toolbar/buttons/toggle_check_list.dart'; +import '../../../models/config/toolbar/buttons/base_configurations.dart'; +import '../../../models/config/toolbar/buttons/toggle_check_list_configurations.dart'; import '../../../models/documents/attribute.dart'; import '../../../models/documents/style.dart'; import '../../../models/themes/quill_icon_theme.dart'; import '../../../utils/widgets.dart'; -import '../../controller.dart'; -import 'toggle_style.dart'; +import '../../quill/quill_controller.dart'; +import 'toggle_style_button.dart'; class QuillToolbarToggleCheckListButton extends StatefulWidget { const QuillToolbarToggleCheckListButton({ - required this.options, required this.controller, + this.options = const QuillToolbarToggleCheckListButtonOptions(), super.key, }); diff --git a/lib/src/widgets/toolbar/buttons/toggle_style.dart b/lib/src/widgets/toolbar/buttons/toggle_style_button.dart similarity index 83% rename from lib/src/widgets/toolbar/buttons/toggle_style.dart rename to lib/src/widgets/toolbar/buttons/toggle_style_button.dart index dabf225e0..fd558ebb5 100644 --- a/lib/src/widgets/toolbar/buttons/toggle_style.dart +++ b/lib/src/widgets/toolbar/buttons/toggle_style_button.dart @@ -1,12 +1,12 @@ import 'package:flutter/material.dart'; -import '../../../extensions/quill_provider.dart'; +import '../../../extensions/quill_configurations_ext.dart'; import '../../../l10n/extensions/localizations.dart'; import '../../../models/documents/attribute.dart'; import '../../../models/documents/style.dart'; import '../../../models/themes/quill_icon_theme.dart'; import '../../../utils/widgets.dart'; -import '../../controller.dart'; +import '../../quill/quill_controller.dart'; import '../base_toolbar.dart'; typedef ToggleStyleButtonBuilder = Widget Function( @@ -23,9 +23,9 @@ typedef ToggleStyleButtonBuilder = Widget Function( class QuillToolbarToggleStyleButton extends StatefulWidget { const QuillToolbarToggleStyleButton({ - required this.options, required this.controller, required this.attribute, + this.options = const QuillToolbarToggleStyleButtonOptions(), super.key, }); @@ -62,10 +62,9 @@ class QuillToolbarToggleStyleButtonState } double get iconSize { - final baseFontSize = - context.requireQuillToolbarBaseButtonOptions.globalIconSize; + final baseFontSize = context.quillToolbarBaseButtonOptions?.globalIconSize; final iconSize = options.iconSize; - return iconSize ?? baseFontSize; + return iconSize ?? baseFontSize ?? kDefaultIconSize; } double get iconButtonFactor { @@ -77,15 +76,15 @@ class QuillToolbarToggleStyleButtonState VoidCallback? get afterButtonPressed { return options.afterButtonPressed ?? - context.requireQuillToolbarBaseButtonOptions.afterButtonPressed; + context.quillToolbarBaseButtonOptions?.afterButtonPressed; } QuillIconTheme? get iconTheme { return options.iconTheme ?? - context.requireQuillToolbarBaseButtonOptions.iconTheme; + context.quillToolbarBaseButtonOptions?.iconTheme; } - (String?, IconData) get _defaultTooltipAndIconData { + (String, IconData) get _defaultTooltipAndIconData { switch (widget.attribute.key) { case 'bold': return (context.loc.bold, Icons.format_bold); @@ -115,6 +114,14 @@ class QuillToolbarToggleStyleButtonState return (context.loc.codeBlock, Icons.code); case 'blockquote': return (context.loc.quote, Icons.format_quote); + case 'align': + return switch (widget.attribute.value) { + 'left' => (context.loc.alignLeft, Icons.format_align_left), + 'right' => (context.loc.alignRight, Icons.format_align_right), + 'center' => (context.loc.alignCenter, Icons.format_align_center), + Object() => (context.loc.alignCenter, Icons.format_align_center), + null => (context.loc.alignCenter, Icons.format_align_center), + }; default: throw ArgumentError( 'Could not find the default tooltip for ' @@ -125,13 +132,13 @@ class QuillToolbarToggleStyleButtonState String? get tooltip { return options.tooltip ?? - context.requireQuillToolbarBaseButtonOptions.tooltip ?? + context.quillToolbarBaseButtonOptions?.tooltip ?? _defaultTooltipAndIconData.$1; } IconData get iconData { return options.iconData ?? - context.requireQuillToolbarBaseButtonOptions.iconData ?? + context.quillToolbarBaseButtonOptions?.iconData ?? _defaultTooltipAndIconData.$2; } @@ -143,7 +150,7 @@ class QuillToolbarToggleStyleButtonState @override Widget build(BuildContext context) { final childBuilder = options.childBuilder ?? - context.requireQuillToolbarBaseButtonOptions.childBuilder; + context.quillToolbarBaseButtonOptions?.childBuilder; if (childBuilder != null) { return childBuilder( QuillToolbarToggleStyleButtonOptions( @@ -203,7 +210,8 @@ class QuillToolbarToggleStyleButtonState bool _getIsToggled(Map attrs) { if (widget.attribute.key == Attribute.list.key || - widget.attribute.key == Attribute.script.key) { + widget.attribute.key == Attribute.script.key || + widget.attribute.key == Attribute.align.key) { final attribute = attrs[widget.attribute.key]; if (attribute == null) { return false; @@ -241,22 +249,10 @@ Widget defaultToggleStyleButtonBuilder( .primaryIconTheme.color) //You can specify your own icon color : (iconTheme?.iconUnselectedColor ?? theme.iconTheme.color) : (iconTheme?.disabledIconColor ?? theme.disabledColor); - final fill = isEnabled - ? isToggled == true - ? (iconTheme?.iconSelectedFillColor ?? - Theme.of(context).primaryColor) //Selected icon fill color - : (iconTheme?.iconUnselectedFillColor ?? - theme.canvasColor) //Unselected icon fill color : - : (iconTheme?.disabledIconFillColor ?? - (fillColor ?? theme.canvasColor)); //Disabled icon fill color return QuillToolbarIconButton( - highlightElevation: 0, - hoverElevation: 0, - size: iconSize * iconButtonFactor, - icon: Icon(icon, size: iconSize, color: iconColor), - fillColor: fill, + icon: Icon(icon, size: iconSize * iconButtonFactor, color: iconColor), + isFilled: isEnabled ? isToggled == true : false, onPressed: onPressed, afterPressed: afterPressed, - borderRadius: iconTheme?.borderRadius ?? 2, ); } diff --git a/lib/src/widgets/toolbar/simple_toolbar.dart b/lib/src/widgets/toolbar/simple_toolbar.dart new file mode 100644 index 000000000..9794b3127 --- /dev/null +++ b/lib/src/widgets/toolbar/simple_toolbar.dart @@ -0,0 +1,521 @@ +import 'package:flutter/material.dart'; + +import '../../../translations.dart'; +import '../../extensions/quill_configurations_ext.dart'; +import '../../models/config/toolbar/toolbar_configurations.dart'; +import '../../models/documents/attribute.dart'; +import '../utils/provider.dart'; +import 'base_toolbar.dart'; +import 'buttons/arrow_indicated_list_button.dart'; +import 'buttons/select_alignment_buttons.dart'; +import 'buttons/select_header_style_button.dart'; + +class QuillSimpleToolbar extends StatelessWidget + implements PreferredSizeWidget { + const QuillSimpleToolbar({ + required this.configurations, + super.key, + }); + + /// The configurations for the toolbar widget of flutter quill + final QuillSimpleToolbarConfigurations configurations; + + @override + Widget build(BuildContext context) { + final theEmbedButtons = configurations.embedButtons; + + final isButtonGroupShown = [ + configurations.showFontFamily || + configurations.showFontSize || + configurations.showBoldButton || + configurations.showItalicButton || + configurations.showSmallButton || + configurations.showUnderLineButton || + configurations.showStrikeThrough || + configurations.showInlineCode || + configurations.showColorButton || + configurations.showBackgroundColorButton || + configurations.showClearFormat || + theEmbedButtons?.isNotEmpty == true, + configurations.showLeftAlignment || + configurations.showCenterAlignment || + configurations.showRightAlignment || + configurations.showJustifyAlignment || + configurations.showDirection, + configurations.showHeaderStyle, + configurations.showListNumbers || + configurations.showListBullets || + configurations.showListCheck || + configurations.showCodeBlock, + configurations.showQuote || configurations.showIndent, + configurations.showLink || configurations.showSearchButton + ]; + + List childrenBuilder(BuildContext context) { + final toolbarConfigurations = + context.requireQuillSimpleToolbarConfigurations; + + final globalIconSize = + toolbarConfigurations.buttonOptions.base.globalIconSize; + + final axis = toolbarConfigurations.axis; + final globalController = configurations.controller; + + final spacerWidget = + configurations.spacerWidget ?? const SizedBox.shrink(); + + return [ + if (configurations.showUndo) ...[ + QuillToolbarHistoryButton( + isUndo: true, + options: toolbarConfigurations.buttonOptions.undoHistory, + controller: + toolbarConfigurations.buttonOptions.undoHistory.controller ?? + globalController, + ), + spacerWidget, + ], + if (configurations.showRedo) ...[ + QuillToolbarHistoryButton( + isUndo: false, + options: toolbarConfigurations.buttonOptions.redoHistory, + controller: + toolbarConfigurations.buttonOptions.redoHistory.controller ?? + globalController, + ), + spacerWidget, + ], + if (configurations.showFontFamily) ...[ + QuillToolbarFontFamilyButton( + options: toolbarConfigurations.buttonOptions.fontFamily, + controller: + toolbarConfigurations.buttonOptions.fontFamily.controller ?? + globalController, + defaultDispalyText: context.loc.font, + ), + spacerWidget, + ], + if (configurations.showFontSize) ...[ + QuillToolbarFontSizeButton( + options: toolbarConfigurations.buttonOptions.fontSize, + controller: + toolbarConfigurations.buttonOptions.fontFamily.controller ?? + globalController, + defaultDisplayText: context.loc.fontSize, + ), + spacerWidget, + ], + if (configurations.showBoldButton) ...[ + QuillToolbarToggleStyleButton( + attribute: Attribute.bold, + options: toolbarConfigurations.buttonOptions.bold, + controller: toolbarConfigurations.buttonOptions.bold.controller ?? + globalController, + ), + spacerWidget, + ], + if (configurations.showItalicButton) ...[ + QuillToolbarToggleStyleButton( + attribute: Attribute.italic, + options: toolbarConfigurations.buttonOptions.italic, + controller: toolbarConfigurations.buttonOptions.italic.controller ?? + globalController, + ), + spacerWidget, + ], + if (configurations.showUnderLineButton) ...[ + QuillToolbarToggleStyleButton( + attribute: Attribute.underline, + options: toolbarConfigurations.buttonOptions.underLine, + controller: + toolbarConfigurations.buttonOptions.underLine.controller ?? + globalController, + ), + spacerWidget, + ], + if (configurations.showInlineCode) ...[ + QuillToolbarToggleStyleButton( + attribute: Attribute.inlineCode, + options: toolbarConfigurations.buttonOptions.inlineCode, + controller: + toolbarConfigurations.buttonOptions.inlineCode.controller ?? + globalController, + ), + spacerWidget, + ], + if (configurations.showSubscript) ...[ + QuillToolbarToggleStyleButton( + attribute: Attribute.subscript, + options: toolbarConfigurations.buttonOptions.subscript, + controller: + toolbarConfigurations.buttonOptions.subscript.controller ?? + globalController, + ), + spacerWidget, + ], + if (configurations.showSuperscript) ...[ + QuillToolbarToggleStyleButton( + attribute: Attribute.superscript, + options: toolbarConfigurations.buttonOptions.superscript, + controller: + toolbarConfigurations.buttonOptions.superscript.controller ?? + globalController, + ), + spacerWidget, + ], + if (configurations.showSmallButton) ...[ + QuillToolbarToggleStyleButton( + attribute: Attribute.small, + options: toolbarConfigurations.buttonOptions.small, + controller: toolbarConfigurations.buttonOptions.small.controller ?? + globalController, + ), + spacerWidget, + ], + if (configurations.showStrikeThrough) ...[ + QuillToolbarToggleStyleButton( + attribute: Attribute.strikeThrough, + options: toolbarConfigurations.buttonOptions.strikeThrough, + controller: + toolbarConfigurations.buttonOptions.strikeThrough.controller ?? + globalController, + ), + spacerWidget, + ], + if (configurations.showColorButton) ...[ + QuillToolbarColorButton( + controller: toolbarConfigurations.buttonOptions.color.controller ?? + globalController, + isBackground: false, + options: toolbarConfigurations.buttonOptions.color, + ), + spacerWidget, + ], + if (configurations.showBackgroundColorButton) ...[ + QuillToolbarColorButton( + options: toolbarConfigurations.buttonOptions.backgroundColor, + controller: toolbarConfigurations.buttonOptions.color.controller ?? + globalController, + isBackground: true, + ), + spacerWidget, + ], + if (configurations.showClearFormat) ...[ + QuillToolbarClearFormatButton( + controller: + toolbarConfigurations.buttonOptions.clearFormat.controller ?? + globalController, + options: toolbarConfigurations.buttonOptions.clearFormat, + ), + spacerWidget, + ], + if (theEmbedButtons != null) + for (final builder in theEmbedButtons) + builder( + globalController, + globalIconSize, + context.requireQuillToolbarBaseButtonOptions.iconTheme, + configurations.dialogTheme), + if (configurations.showDividers && + isButtonGroupShown[0] && + (isButtonGroupShown[1] || + isButtonGroupShown[2] || + isButtonGroupShown[3] || + isButtonGroupShown[4] || + isButtonGroupShown[5])) + QuillToolbarDivider( + axis, + color: configurations.sectionDividerColor, + space: configurations.sectionDividerSpace, + ), + if (configurations.showAlignmentButtons) ...[ + QuillToolbarSelectAlignmentButtons( + controller: toolbarConfigurations + .buttonOptions.selectAlignmentButtons.controller ?? + globalController, + options: toolbarConfigurations.buttonOptions.selectAlignmentButtons, + showLeftAlignment: configurations.showLeftAlignment, + showCenterAlignment: configurations.showCenterAlignment, + showRightAlignment: configurations.showRightAlignment, + showJustifyAlignment: configurations.showJustifyAlignment, + ), + spacerWidget, + ], + if (configurations.showDirection) ...[ + QuillToolbarToggleStyleButton( + attribute: Attribute.rtl, + options: toolbarConfigurations.buttonOptions.direction, + controller: + toolbarConfigurations.buttonOptions.direction.controller ?? + globalController, + ), + spacerWidget, + ], + if (configurations.showDividers && + isButtonGroupShown[1] && + (isButtonGroupShown[2] || + isButtonGroupShown[3] || + isButtonGroupShown[4] || + isButtonGroupShown[5])) + QuillToolbarDivider( + axis, + color: configurations.sectionDividerColor, + space: configurations.sectionDividerSpace, + ), + if (configurations.showHeaderStyle) ...[ + QuillToolbarSelectHeaderStyleButton( + controller: toolbarConfigurations + .buttonOptions.selectHeaderStyleButtons.controller ?? + globalController, + options: + toolbarConfigurations.buttonOptions.selectHeaderStyleButtons, + ), + spacerWidget, + ], + if (configurations.showDividers && + configurations.showHeaderStyle && + isButtonGroupShown[2] && + (isButtonGroupShown[3] || + isButtonGroupShown[4] || + isButtonGroupShown[5])) + QuillToolbarDivider( + axis, + color: configurations.sectionDividerColor, + space: configurations.sectionDividerSpace, + ), + if (configurations.showListNumbers) ...[ + QuillToolbarToggleStyleButton( + attribute: Attribute.ol, + options: toolbarConfigurations.buttonOptions.listNumbers, + controller: + toolbarConfigurations.buttonOptions.listNumbers.controller ?? + globalController, + ), + spacerWidget, + ], + if (configurations.showListBullets) ...[ + QuillToolbarToggleStyleButton( + attribute: Attribute.ul, + options: toolbarConfigurations.buttonOptions.listBullets, + controller: + toolbarConfigurations.buttonOptions.listBullets.controller ?? + globalController, + ), + spacerWidget, + ], + if (configurations.showListCheck) ...[ + QuillToolbarToggleCheckListButton( + options: toolbarConfigurations.buttonOptions.toggleCheckList, + controller: toolbarConfigurations + .buttonOptions.toggleCheckList.controller ?? + globalController, + ), + spacerWidget, + ], + if (configurations.showCodeBlock) ...[ + QuillToolbarToggleStyleButton( + attribute: Attribute.codeBlock, + options: toolbarConfigurations.buttonOptions.codeBlock, + controller: + toolbarConfigurations.buttonOptions.codeBlock.controller ?? + globalController, + ), + spacerWidget, + ], + if (configurations.showDividers && + isButtonGroupShown[3] && + (isButtonGroupShown[4] || isButtonGroupShown[5])) ...[ + QuillToolbarDivider( + axis, + color: configurations.sectionDividerColor, + space: configurations.sectionDividerSpace, + ), + ], + if (configurations.showQuote) ...[ + QuillToolbarToggleStyleButton( + options: toolbarConfigurations.buttonOptions.quote, + controller: toolbarConfigurations.buttonOptions.quote.controller ?? + globalController, + attribute: Attribute.blockQuote, + ), + spacerWidget, + ], + if (configurations.showIndent) ...[ + QuillToolbarIndentButton( + controller: + toolbarConfigurations.buttonOptions.indentIncrease.controller ?? + globalController, + isIncrease: true, + options: toolbarConfigurations.buttonOptions.indentIncrease, + ), + spacerWidget, + ], + if (configurations.showIndent) ...[ + QuillToolbarIndentButton( + controller: + toolbarConfigurations.buttonOptions.indentDecrease.controller ?? + globalController, + isIncrease: false, + options: toolbarConfigurations.buttonOptions.indentDecrease, + ), + spacerWidget, + ], + if (configurations.showDividers && + isButtonGroupShown[4] && + isButtonGroupShown[5]) + QuillToolbarDivider( + axis, + color: configurations.sectionDividerColor, + space: configurations.sectionDividerSpace, + ), + if (configurations.showLink) ...[ + toolbarConfigurations.linkStyleType.isOriginal + ? QuillToolbarLinkStyleButton( + controller: toolbarConfigurations + .buttonOptions.linkStyle.controller ?? + globalController, + options: toolbarConfigurations.buttonOptions.linkStyle, + ) + : QuillToolbarLinkStyleButton2( + controller: toolbarConfigurations + .buttonOptions.linkStyle2.controller ?? + globalController, + options: toolbarConfigurations.buttonOptions.linkStyle2, + ), + spacerWidget, + ], + if (configurations.showSearchButton) ...[ + QuillToolbarSearchButton( + controller: toolbarConfigurations.buttonOptions.search.controller ?? + globalController, + options: toolbarConfigurations.buttonOptions.search, + ), + spacerWidget, + ], + if (configurations.customButtons.isNotEmpty) ...[ + if (configurations.showDividers) + QuillToolbarDivider( + axis, + color: configurations.sectionDividerColor, + space: configurations.sectionDividerSpace, + ), + for (final customButton in configurations.customButtons) + QuillToolbarCustomButton( + options: customButton, + controller: customButton.controller ?? globalController, + ), + // if (customButton.child != null) ...[ + // InkWell( + // onTap: customButton.onTap, + // child: customButton.child, + // ), + // ] else ...[ + // QuillToolbarCustomButton( + // options: + // toolbarConfigurations.buttonOptions.customButtons, + // controller: toolbarConfigurations + // .buttonOptions.customButtons.controller ?? + // globalController, + // ), + // ], + spacerWidget, + ], + ]; + } + + return QuillSimpleToolbarProvider( + toolbarConfigurations: configurations, + child: QuillToolbar( + configurations: QuillToolbarConfigurations( + buttonOptions: configurations.buttonOptions, + ), + child: Builder( + builder: (context) { + if (configurations.multiRowsDisplay) { + return Wrap( + direction: configurations.axis, + alignment: configurations.toolbarIconAlignment, + crossAxisAlignment: configurations.toolbarIconCrossAlignment, + runSpacing: 4, + spacing: configurations.toolbarSectionSpacing, + children: childrenBuilder(context), + ); + } + return Container( + decoration: configurations.decoration ?? + BoxDecoration( + color: + configurations.color ?? Theme.of(context).canvasColor, + ), + constraints: BoxConstraints.tightFor( + height: configurations.axis == Axis.horizontal + ? configurations.toolbarSize + : null, + width: configurations.axis == Axis.vertical + ? configurations.toolbarSize + : null, + ), + child: QuillToolbarArrowIndicatedButtonList( + axis: configurations.axis, + buttons: childrenBuilder(context), + ), + ); + }, + ), + ), + ); + } + + @override + Size get preferredSize => configurations.axis == Axis.horizontal + ? const Size.fromHeight(defaultToolbarSize) + : const Size.fromWidth(defaultToolbarSize); +} + +/// The divider which is used for separation of buttons in the toolbar. +/// +/// It can be used outside of this package, for example when user does not use +/// [QuillToolbar.basic] and compose toolbar's children on its own. +class QuillToolbarDivider extends StatelessWidget { + const QuillToolbarDivider( + this.axis, { + super.key, + this.color, + this.space, + }); + + /// Provides a horizontal divider for vertical toolbar. + const QuillToolbarDivider.horizontal({Key? key, Color? color, double? space}) + : this(Axis.horizontal, color: color, space: space, key: key); + + /// Provides a horizontal divider for horizontal toolbar. + const QuillToolbarDivider.vertical({Key? key, Color? color, double? space}) + : this(Axis.vertical, color: color, space: space, key: key); + + /// The axis along which the toolbar is. + final Axis axis; + + /// The color to use when painting this divider's line. + final Color? color; + + /// The divider's space (width or height) depending of [axis]. + final double? space; + + @override + Widget build(BuildContext context) { + // Vertical toolbar requires horizontal divider, and vice versa + return axis == Axis.vertical + ? Divider( + height: space, + color: color, + indent: 12, + endIndent: 12, + ) + : VerticalDivider( + width: space, + color: color, + indent: 12, + endIndent: 12, + ); + } +} diff --git a/lib/src/widgets/toolbar/toolbar.dart b/lib/src/widgets/toolbar/toolbar.dart deleted file mode 100644 index 8d7a6fc8d..000000000 --- a/lib/src/widgets/toolbar/toolbar.dart +++ /dev/null @@ -1,459 +0,0 @@ -import 'package:flutter/material.dart'; - -import '../../extensions/quill_provider.dart'; -import '../../l10n/extensions/localizations.dart'; -import '../../models/config/toolbar/base_toolbar_configurations.dart'; -import '../../models/documents/attribute.dart'; -import '../utils/provider.dart'; -import 'base_toolbar.dart'; - -class QuillToolbar extends StatelessWidget implements PreferredSizeWidget { - const QuillToolbar({ - super.key, - this.configurations = const QuillToolbarConfigurations(), - }); - - /// The configurations for the toolbar widget of flutter quill - final QuillToolbarConfigurations configurations; - - @override - Widget build(BuildContext context) { - final theEmbedButtons = configurations.embedButtons; - - final isButtonGroupShown = [ - configurations.showFontFamily || - configurations.showFontSize || - configurations.showBoldButton || - configurations.showItalicButton || - configurations.showSmallButton || - configurations.showUnderLineButton || - configurations.showStrikeThrough || - configurations.showInlineCode || - configurations.showColorButton || - configurations.showBackgroundColorButton || - configurations.showClearFormat || - theEmbedButtons?.isNotEmpty == true, - configurations.showLeftAlignment || - configurations.showCenterAlignment || - configurations.showRightAlignment || - configurations.showJustifyAlignment || - configurations.showDirection, - configurations.showHeaderStyle, - configurations.showListNumbers || - configurations.showListBullets || - configurations.showListCheck || - configurations.showCodeBlock, - configurations.showQuote || configurations.showIndent, - configurations.showLink || configurations.showSearchButton - ]; - - return QuillToolbarProvider( - toolbarConfigurations: configurations, - child: QuillBaseToolbar( - configurations: QuillBaseToolbarConfigurations( - color: configurations.color, - decoration: configurations.decoration, - toolbarSectionSpacing: configurations.toolbarSectionSpacing, - toolbarIconAlignment: configurations.toolbarIconAlignment, - toolbarIconCrossAlignment: configurations.toolbarIconCrossAlignment, - customButtons: configurations.customButtons, - linkDialogAction: configurations.linkDialogAction, - multiRowsDisplay: configurations.multiRowsDisplay, - sectionDividerColor: configurations.sectionDividerColor, - axis: configurations.axis, - sectionDividerSpace: configurations.sectionDividerSpace, - toolbarSize: configurations.toolbarSize, - childrenBuilder: (context) { - final toolbarConfigurations = - context.requireQuillToolbarConfigurations; - - final globalIconSize = - toolbarConfigurations.buttonOptions.base.globalIconSize; - - final axis = toolbarConfigurations.axis; - final globalController = context.requireQuillController; - - final spacerWidget = - configurations.spacerWidget ?? const SizedBox.shrink(); - - return [ - if (configurations.showUndo) ...[ - QuillToolbarHistoryButton( - options: toolbarConfigurations.buttonOptions.undoHistory, - controller: toolbarConfigurations - .buttonOptions.undoHistory.controller ?? - globalController, - ), - spacerWidget, - ], - if (configurations.showRedo) ...[ - QuillToolbarHistoryButton( - options: toolbarConfigurations.buttonOptions.redoHistory, - controller: toolbarConfigurations - .buttonOptions.redoHistory.controller ?? - globalController, - ), - spacerWidget, - ], - if (configurations.showFontFamily) ...[ - QuillToolbarFontFamilyButton( - options: toolbarConfigurations.buttonOptions.fontFamily, - controller: toolbarConfigurations - .buttonOptions.fontFamily.controller ?? - globalController, - defaultDispalyText: context.loc.font, - ), - spacerWidget, - ], - if (configurations.showFontSize) ...[ - QuillToolbarFontSizeButton( - options: toolbarConfigurations.buttonOptions.fontSize, - controller: toolbarConfigurations - .buttonOptions.fontFamily.controller ?? - globalController, - defaultDisplayText: context.loc.fontSize, - ), - spacerWidget, - ], - if (configurations.showBoldButton) ...[ - QuillToolbarToggleStyleButton( - attribute: Attribute.bold, - options: toolbarConfigurations.buttonOptions.bold, - controller: - toolbarConfigurations.buttonOptions.bold.controller ?? - globalController, - ), - spacerWidget, - ], - if (configurations.showSubscript) ...[ - QuillToolbarToggleStyleButton( - attribute: Attribute.subscript, - options: toolbarConfigurations.buttonOptions.subscript, - controller: toolbarConfigurations - .buttonOptions.subscript.controller ?? - globalController, - ), - spacerWidget, - ], - if (configurations.showSuperscript) ...[ - QuillToolbarToggleStyleButton( - attribute: Attribute.superscript, - options: toolbarConfigurations.buttonOptions.superscript, - controller: toolbarConfigurations - .buttonOptions.superscript.controller ?? - globalController, - ), - spacerWidget, - ], - if (configurations.showItalicButton) ...[ - QuillToolbarToggleStyleButton( - attribute: Attribute.italic, - options: toolbarConfigurations.buttonOptions.italic, - controller: - toolbarConfigurations.buttonOptions.italic.controller ?? - globalController, - ), - spacerWidget, - ], - if (configurations.showSmallButton) ...[ - QuillToolbarToggleStyleButton( - attribute: Attribute.small, - options: toolbarConfigurations.buttonOptions.small, - controller: - toolbarConfigurations.buttonOptions.small.controller ?? - globalController, - ), - spacerWidget, - ], - if (configurations.showUnderLineButton) ...[ - QuillToolbarToggleStyleButton( - attribute: Attribute.underline, - options: toolbarConfigurations.buttonOptions.underLine, - controller: toolbarConfigurations - .buttonOptions.underLine.controller ?? - globalController, - ), - spacerWidget, - ], - if (configurations.showStrikeThrough) ...[ - QuillToolbarToggleStyleButton( - attribute: Attribute.strikeThrough, - options: toolbarConfigurations.buttonOptions.strikeThrough, - controller: toolbarConfigurations - .buttonOptions.strikeThrough.controller ?? - globalController, - ), - spacerWidget, - ], - if (configurations.showInlineCode) ...[ - QuillToolbarToggleStyleButton( - attribute: Attribute.inlineCode, - options: toolbarConfigurations.buttonOptions.inlineCode, - controller: toolbarConfigurations - .buttonOptions.inlineCode.controller ?? - globalController, - ), - spacerWidget, - ], - if (configurations.showColorButton) ...[ - QuillToolbarColorButton( - controller: - toolbarConfigurations.buttonOptions.color.controller ?? - globalController, - isBackground: false, - options: toolbarConfigurations.buttonOptions.color, - ), - spacerWidget, - ], - if (configurations.showBackgroundColorButton) ...[ - QuillToolbarColorButton( - options: toolbarConfigurations.buttonOptions.backgroundColor, - controller: - toolbarConfigurations.buttonOptions.color.controller ?? - globalController, - isBackground: true, - ), - spacerWidget, - ], - if (configurations.showClearFormat) ...[ - QuillToolbarClearFormatButton( - controller: toolbarConfigurations - .buttonOptions.clearFormat.controller ?? - globalController, - options: toolbarConfigurations.buttonOptions.clearFormat, - ), - spacerWidget, - ], - if (theEmbedButtons != null) - for (final builder in theEmbedButtons) - builder( - globalController, - globalIconSize, - context.requireQuillToolbarBaseButtonOptions.iconTheme, - configurations.dialogTheme), - if (configurations.showDividers && - isButtonGroupShown[0] && - (isButtonGroupShown[1] || - isButtonGroupShown[2] || - isButtonGroupShown[3] || - isButtonGroupShown[4] || - isButtonGroupShown[5])) - QuillToolbarDivider( - axis, - color: configurations.sectionDividerColor, - space: configurations.sectionDividerSpace, - ), - if (configurations.showAlignmentButtons) ...[ - QuillToolbarSelectAlignmentButton( - controller: toolbarConfigurations - .buttonOptions.selectAlignmentButtons.controller ?? - globalController, - options: toolbarConfigurations - .buttonOptions.selectAlignmentButtons, - // tooltips: Map.of(buttonTooltips) - // ..removeWhere((key, value) => ![ - // ToolbarButtons.leftAlignment, - // ToolbarButtons.centerAlignment, - // ToolbarButtons.rightAlignment, - // ToolbarButtons.justifyAlignment, - // ].contains(key)), - showLeftAlignment: configurations.showLeftAlignment, - showCenterAlignment: configurations.showCenterAlignment, - showRightAlignment: configurations.showRightAlignment, - showJustifyAlignment: configurations.showJustifyAlignment, - ), - spacerWidget, - ], - if (configurations.showDirection) ...[ - QuillToolbarToggleStyleButton( - attribute: Attribute.rtl, - options: toolbarConfigurations.buttonOptions.direction, - controller: toolbarConfigurations - .buttonOptions.direction.controller ?? - context.requireQuillController, - ), - spacerWidget, - ], - if (configurations.showDividers && - isButtonGroupShown[1] && - (isButtonGroupShown[2] || - isButtonGroupShown[3] || - isButtonGroupShown[4] || - isButtonGroupShown[5])) - QuillToolbarDivider( - axis, - color: configurations.sectionDividerColor, - space: configurations.sectionDividerSpace, - ), - if (configurations.showHeaderStyle) ...[ - QuillToolbarSelectHeaderStyleButtons( - controller: toolbarConfigurations - .buttonOptions.selectHeaderStyleButtons.controller ?? - globalController, - options: toolbarConfigurations - .buttonOptions.selectHeaderStyleButtons, - ), - spacerWidget, - ], - if (configurations.showDividers && - configurations.showHeaderStyle && - isButtonGroupShown[2] && - (isButtonGroupShown[3] || - isButtonGroupShown[4] || - isButtonGroupShown[5])) - QuillToolbarDivider( - axis, - color: configurations.sectionDividerColor, - space: configurations.sectionDividerSpace, - ), - if (configurations.showListNumbers) ...[ - QuillToolbarToggleStyleButton( - attribute: Attribute.ol, - options: toolbarConfigurations.buttonOptions.listNumbers, - controller: toolbarConfigurations - .buttonOptions.listNumbers.controller ?? - globalController, - ), - spacerWidget, - ], - if (configurations.showListBullets) ...[ - QuillToolbarToggleStyleButton( - attribute: Attribute.ul, - options: toolbarConfigurations.buttonOptions.listBullets, - controller: toolbarConfigurations - .buttonOptions.listBullets.controller ?? - globalController, - ), - spacerWidget, - ], - if (configurations.showListCheck) ...[ - QuillToolbarToggleCheckListButton( - options: toolbarConfigurations.buttonOptions.toggleCheckList, - controller: toolbarConfigurations - .buttonOptions.toggleCheckList.controller ?? - globalController, - ), - spacerWidget, - ], - if (configurations.showCodeBlock) ...[ - QuillToolbarToggleStyleButton( - attribute: Attribute.codeBlock, - options: toolbarConfigurations.buttonOptions.codeBlock, - controller: toolbarConfigurations - .buttonOptions.codeBlock.controller ?? - globalController, - ), - spacerWidget, - ], - if (configurations.showDividers && - isButtonGroupShown[3] && - (isButtonGroupShown[4] || isButtonGroupShown[5])) ...[ - QuillToolbarDivider( - axis, - color: configurations.sectionDividerColor, - space: configurations.sectionDividerSpace, - ), - ], - if (configurations.showQuote) ...[ - QuillToolbarToggleStyleButton( - options: toolbarConfigurations.buttonOptions.quote, - controller: - toolbarConfigurations.buttonOptions.quote.controller ?? - globalController, - attribute: Attribute.blockQuote, - ), - spacerWidget, - ], - if (configurations.showIndent) ...[ - QuillToolbarIndentButton( - controller: toolbarConfigurations - .buttonOptions.indentIncrease.controller ?? - globalController, - isIncrease: true, - options: toolbarConfigurations.buttonOptions.indentIncrease, - ), - spacerWidget, - ], - if (configurations.showIndent) ...[ - QuillToolbarIndentButton( - controller: toolbarConfigurations - .buttonOptions.indentDecrease.controller ?? - globalController, - isIncrease: false, - options: toolbarConfigurations.buttonOptions.indentDecrease, - ), - spacerWidget, - ], - if (configurations.showDividers && - isButtonGroupShown[4] && - isButtonGroupShown[5]) - QuillToolbarDivider( - axis, - color: configurations.sectionDividerColor, - space: configurations.sectionDividerSpace, - ), - if (configurations.showLink) ...[ - toolbarConfigurations.linkStyleType.isOriginal - ? QuillToolbarLinkStyleButton( - controller: toolbarConfigurations - .buttonOptions.linkStyle.controller ?? - globalController, - options: toolbarConfigurations.buttonOptions.linkStyle, - ) - : QuillToolbarLinkStyleButton2( - controller: toolbarConfigurations - .buttonOptions.linkStyle2.controller ?? - globalController, - options: toolbarConfigurations.buttonOptions.linkStyle2, - ), - spacerWidget, - ], - if (configurations.showSearchButton) ...[ - QuillToolbarSearchButton( - controller: - toolbarConfigurations.buttonOptions.search.controller ?? - globalController, - options: toolbarConfigurations.buttonOptions.search, - ), - spacerWidget, - ], - if (configurations.customButtons.isNotEmpty) ...[ - if (configurations.showDividers) - QuillToolbarDivider( - axis, - color: configurations.sectionDividerColor, - space: configurations.sectionDividerSpace, - ), - for (final customButton in configurations.customButtons) - QuillToolbarCustomButton( - options: customButton, - controller: customButton.controller ?? globalController, - ), - // if (customButton.child != null) ...[ - // InkWell( - // onTap: customButton.onTap, - // child: customButton.child, - // ), - // ] else ...[ - // QuillToolbarCustomButton( - // options: - // toolbarConfigurations.buttonOptions.customButtons, - // controller: toolbarConfigurations - // .buttonOptions.customButtons.controller ?? - // globalController, - // ), - // ], - spacerWidget, - ], - ]; - }, - ), - ), - ); - } - - @override - Size get preferredSize => configurations.axis == Axis.horizontal - ? const Size.fromHeight(defaultToolbarSize) - : const Size.fromWidth(defaultToolbarSize); -} diff --git a/lib/src/widgets/utils/provider.dart b/lib/src/widgets/utils/provider.dart index 773cbba33..c0d40e73a 100644 --- a/lib/src/widgets/utils/provider.dart +++ b/lib/src/widgets/utils/provider.dart @@ -3,89 +3,31 @@ import 'package:flutter/widgets.dart' show BuildContext, InheritedWidget, Widget; import '../../models/config/quill_configurations.dart'; -import '../../models/config/toolbar/base_toolbar_configurations.dart'; +import '../../models/config/toolbar/toolbar_configurations.dart'; -class QuillProvider extends InheritedWidget { - const QuillProvider({ - required this.configurations, - required super.child, - super.key, - }); - - /// Controller object which establishes a link between a rich text document - /// and this editor. - /// - /// Must not be null. - final QuillConfigurations configurations; - - @override - bool updateShouldNotify(covariant QuillProvider oldWidget) { - return oldWidget.configurations != configurations; - } - - static QuillProvider? of(BuildContext context) { - return context.dependOnInheritedWidgetOfExactType(); - } - - static QuillProvider ofNotNull(BuildContext context) { - final provider = of(context); - if (provider == null) { - if (kDebugMode) { - debugPrint( - 'The quill provider must be provided in the widget tree.', - ); - } - throw ArgumentError.checkNotNull( - 'You are using a widget in the Flutter quill library that require ' - 'The Quill provider widget to be in the parent widget tree ' - 'because ' - 'The provider is $provider. Please make sure to wrap this widget' - ' with' - ' QuillProvider widget. ' - 'You might using QuillEditor and QuillToolbar so make sure to' - ' wrap them with the quill provider widget and setup the required ' - 'configurations', - 'QuillProvider', - ); - } - return provider; - } - - /// To pass the [QuillProvider] instance as value instead of creating new - /// widget - static QuillProvider value({ - required QuillProvider value, - required Widget child, - }) { - return QuillProvider( - configurations: value.configurations, - child: child, - ); - } -} - -class QuillToolbarProvider extends InheritedWidget { - const QuillToolbarProvider({ +class QuillSimpleToolbarProvider extends InheritedWidget { + const QuillSimpleToolbarProvider({ required super.child, required this.toolbarConfigurations, super.key, }); /// The configurations for the toolbar widget of flutter quill - final QuillToolbarConfigurations toolbarConfigurations; + final QuillSimpleToolbarConfigurations toolbarConfigurations; @override - bool updateShouldNotify(covariant QuillToolbarProvider oldWidget) { + bool updateShouldNotify(covariant QuillSimpleToolbarProvider oldWidget) { return oldWidget.toolbarConfigurations != toolbarConfigurations; } - static QuillToolbarProvider? of(BuildContext context) { + static QuillSimpleToolbarProvider? maybeOf(BuildContext context) { /// The configurations for the quill editor widget of flutter quill - return context.dependOnInheritedWidgetOfExactType(); + return context + .dependOnInheritedWidgetOfExactType(); } - static QuillToolbarProvider ofNotNull(BuildContext context) { - final provider = of(context); + static QuillSimpleToolbarProvider of(BuildContext context) { + final provider = maybeOf(context); if (provider == null) { if (kDebugMode) { debugPrint( @@ -102,19 +44,19 @@ class QuillToolbarProvider extends InheritedWidget { 'You might using QuillToolbar so make sure to' ' wrap them with the quill provider widget and setup the required ' 'configurations', - 'QuillProvider', + 'QuillSimpleToolbarProvider', ); } return provider; } - /// To pass the [QuillToolbarProvider] instance as value instead of creating + /// To pass the [QuillSimpleToolbarProvider] instance as value instead of creating /// new widget - static QuillToolbarProvider value({ - required QuillToolbarProvider value, + static QuillSimpleToolbarProvider value({ + required QuillSimpleToolbarProvider value, required Widget child, }) { - return QuillToolbarProvider( + return QuillSimpleToolbarProvider( toolbarConfigurations: value.toolbarConfigurations, child: child, ); @@ -122,29 +64,28 @@ class QuillToolbarProvider extends InheritedWidget { } // Not really needed -class QuillBaseToolbarProvider extends InheritedWidget { - const QuillBaseToolbarProvider({ +class QuillToolbarProvider extends InheritedWidget { + const QuillToolbarProvider({ required super.child, required this.toolbarConfigurations, super.key, }); /// The configurations for the toolbar widget of flutter quill - final QuillBaseToolbarConfigurations toolbarConfigurations; + final QuillToolbarConfigurations toolbarConfigurations; @override - bool updateShouldNotify(covariant QuillBaseToolbarProvider oldWidget) { + bool updateShouldNotify(covariant QuillToolbarProvider oldWidget) { return oldWidget.toolbarConfigurations != toolbarConfigurations; } - static QuillBaseToolbarProvider? of(BuildContext context) { + static QuillToolbarProvider? maybeOf(BuildContext context) { /// The configurations for the quill editor widget of flutter quill - return context - .dependOnInheritedWidgetOfExactType(); + return context.dependOnInheritedWidgetOfExactType(); } - static QuillBaseToolbarProvider ofNotNull(BuildContext context) { - final provider = of(context); + static QuillToolbarProvider of(BuildContext context) { + final provider = maybeOf(context); if (provider == null) { if (kDebugMode) { debugPrint( @@ -161,19 +102,19 @@ class QuillBaseToolbarProvider extends InheritedWidget { 'You might using QuillBaseToolbar so make sure to' ' wrap them with the quill provider widget and setup the required ' 'configurations', - 'QuillProvider', + 'QuillToolbarProvider', ); } return provider; } - /// To pass the [QuillBaseToolbarConfigurations] instance as value + /// To pass the [QuillToolbarConfigurations] instance as value /// instead of creating new widget - static QuillBaseToolbarProvider value({ - required QuillBaseToolbarConfigurations value, + static QuillToolbarProvider value({ + required QuillToolbarConfigurations value, required Widget child, }) { - return QuillBaseToolbarProvider( + return QuillToolbarProvider( toolbarConfigurations: value, child: child, ); @@ -195,13 +136,13 @@ class QuillEditorProvider extends InheritedWidget { return oldWidget.editorConfigurations != editorConfigurations; } - static QuillEditorProvider? of(BuildContext context) { + static QuillEditorProvider? maybeOf(BuildContext context) { /// The configurations for the quill editor widget of flutter quill return context.dependOnInheritedWidgetOfExactType(); } - static QuillEditorProvider ofNotNull(BuildContext context) { - final provider = of(context); + static QuillEditorProvider of(BuildContext context) { + final provider = maybeOf(context); if (provider == null) { if (kDebugMode) { debugPrint( @@ -218,7 +159,7 @@ class QuillEditorProvider extends InheritedWidget { 'You might using QuillEditor so make sure to' ' wrap them with the quill provider widget and setup the required ' 'configurations', - 'QuillProvider', + 'QuillEditorProvider', ); } return provider; diff --git a/packages/quill_html_converter/CHANGELOG.md b/packages/quill_html_converter/CHANGELOG.md index f3ed5bc79..09d629a35 100644 --- a/packages/quill_html_converter/CHANGELOG.md +++ b/packages/quill_html_converter/CHANGELOG.md @@ -1,3 +1,1626 @@ -## 0.0.1-experimental.1 +# Changelog -* initial release. +All notable changes to this project will be documented in this file. + +## 9.0.0-dev-8 +* Better support for pasting HTML contents from external websites to the editor +* The experimental support of converting the HTML from `quill_html_converter` is now built-in in the `flutter_quill` and removed from there (Breaking change for `quill_html_converter`) + +## 9.0.0-dev-7 +* Fix a bug in chaning the background/font color of ol/ul list +* Flutter Quill Extensions: + * Fix link bug in the video url + * Fix patterns + +## 9.0.0-dev-6 +* Move the `child` from `QuillToolbarConfigurations` into `QuillToolbar` directly +* Bug fixes +* Add the ability to change the background and font color of the ol/ul elements dots and numbers +* Flutter Quill Extensions: + * **Breaking Change**: The `imageProviderBuilder`is now providing the context and image url + +## 9.0.0-dev-5 +* The `QuillToolbar` is now accepting only `child` with no configurations so you can customize everything you wants, the `QuillToolbar.simple()` or `QuillSimpleToolbar` implements a simple toolbar that is based on `QuillToolbar`, you are free to use it but it just an example and not standard +* Flutter Quill Extensions: + * Improve the camera button + +## 9.0.0-dev-4 +* The options parameter in all of the buttons is no longer required which can be useful to create custom toolbar with minimal efforts +* Toolbar buttons fixes in both `flutter_quill` and `flutter_quill_extensions` +* The `QuillProvider` has been dropped and no longer used, the providers will be used only internally from now on and we will not using them as much as possible + +## 9.0.0-dev-3 +* Breaking Changes: + * Rename `QuillToolbar` to `QuillSimpleToolbar` + * Rename `QuillBaseToolbar` to `QuillToolbar` + * Replace `pasteboard` with `rich_cliboard` +* Fix a bug in the example when inserting an image from url +* Flutter Quill Extensions: + * Add support for copying the image to the system cliboard + +## 9.0.0-dev-2 +* An attemp to fix CI automated publishing + +## 9.0.0-dev-1 +* An attemp to fix CI automated publishing + +## 9.0.0-dev +* **Major Breaking change**: The `QuillProvider` is now optional, the `controller` parameter has been moved to the `QuillEditor` and `QuillToolbar` once again. +* Flutter Quill Extensions; + * **Breaking Change**: Completly change the way how the source code structured to more basic and simple way, organize folders and file names, if you use the library +from `flutter_quill_extensions.dart` then there is nothing you need to do, but if you are using any other import then you need to re-imports +embed, this won't affect how quill js work + * Improvemenets to the image embed + * Add support for `margin` for web + * Add untranslated strings to the `quill_en.arb` + +## 8.6.4 +* The default value of `keyboardAppearance` for the iOS will be the one from the App/System theme mode instead of always using the `Brightness.light` +* Fix typos in `README.md` + +## 8.6.3 +* Update the minimum flutter version to `3.16.0` + +## 8.6.2 +* Restore use of alternative QuillToolbarLinkStyleButton2 widget + +## 8.6.1 +* Temporary revert style bug fix + +## 8.6.0 +* **Breaking Change** Support [Flutter 3.16](https://medium.com/flutter/whats-new-in-flutter-3-16-dba6cb1015d1), please upgrade to the latest stable version of flutter to use this update +* **Breaking Change**: Remove Deprecated Fields +* **Breaking Change**: Extract the shared things between `QuillToolbarConfigurations` and `QuillBaseToolbarConfigurations` +* **Breaking Change**: You no longer need to use `QuillToolbarProvider` when using custom toolbar buttons, the example has been updated +* Bug fixes + +## 8.5.5 +* Now when opening dialogs by `QuillToolbar` you will not get an exception when you don't use `FlutterQuillLocalizations.delegate` in your `WidgetsApp`, `MaterialApp`, or `CupertinoApp`. The fix is for the `QuillToolbarSearchButton`, `QuillToolbarLinkStyleButton`, and `QuillToolbarColorButton` buttons + +## 8.5.4 +* The `mobileWidth`, `mobileHeight`, `mobileMargin`, and `mobileAlignment` is now deprecated in `flutter_quill`, they are now defined in `flutter_quill_extensions` +* Deprecate `replaceStyleStringWithSize` function which is in `string.dart` +* Deprecate `alignment`, and `margin` as they don't conform to official Quill JS + +## 8.5.3 +* Update doc +* Update `README.md` and `CHANGELOG.md` +* Fix typos +* Use `immutable` when possible +* Update `.pubignore` + +## 8.5.2 +* Updated `README.md`. +* Feature: Added the ability to include a custom callback when the `QuillToolbarColorButton` is pressed. +* The `QuillToolbar` now implements `PreferredSizeWidget`, enabling usage in the AppBar, similar to `QuillBaseToolbar`. + +## 8.5.1 +* Updated `README.md`. + +## 8.5.0 +* Migrated to `flutter_localizations` for translations. +* Fixed: Translated all previously untranslated localizations. +* Fixed: Added translations for missing items. +* Fixed: Introduced default Chinese fallback translation. +* Removed: Unused parameters `items` in `QuillToolbarFontFamilyButtonOptions` and `QuillToolbarFontSizeButtonOptions`. +* Updated: Documentation. + +## 8.4.4 +* Updated `.pubignore` to ignore unnecessary files and folders. + +## 8.4.3 +* Updated `CHANGELOG.md`. + +## 8.4.2 +* **Breaking change**: Configuration for `QuillRawEditor` has been moved to a separate class. Additionally, `readOnly` has been renamed to `isReadOnly`. If using `QuillEditor`, no action is required. +* Introduced the ability for developers to override `TextInputAction` in both `QuillRawEditor` and `QuillEditor`. +* Enabled using `QuillRawEditor` without `QuillEditorProvider`. +* Bug fixes. +* Added image cropping implementation in the example. + +## 8.4.1 +* Added `copyWith` in `OptionalSize` class. + +## 8.4.0 +* **Breaking change**: Updated `QuillCustomButton` to use `QuillCustomButtonOptions`. Moved all properties from `QuillCustomButton` to `QuillCustomButtonOptions`, replacing `iconData` with `icon` widget for increased customization. +* **Breaking change**: `customButtons` in `QuillToolbarConfigurations` is now of type `List`. +* Bug fixes following the `8.0.0` update. +* Updated `README.md`. +* Improved platform checking. + +## 8.3.0 +* Added `iconButtonFactor` property to `QuillToolbarBaseButtonOptions` for customizing button size relative to its icon size (defaults to `kIconButtonFactor`, consistent with previous releases). + +## 8.2.6 +* Organized `QuillRawEditor` code. + +## 8.2.5 +* Added `builder` property in `QuillEditorConfigurations`. + +## 8.2.4 +* Adhered to Flutter best practices. +* Fixed auto-focus bug. + +## 8.2.3 +* Updated `README.md`. + +## 8.2.2 +* Moved `flutter_quill_test` to a separate package: [flutter_quill_test](https://pub.dev/packages/flutter_quill_test). + +## 8.2.1 +* Updated `README.md`. + +## 8.2.0 +* Added the option to add configurations for `flutter_quill_extensions` using `extraConfigurations`. + +## 8.1.11 +* Followed Dart best practices by using `lints` and removed `pedantic` and `platform` since they are not used. +* Fixed text direction bug. +* Updated `README.md`. + +## 8.1.10 +* Secret for automated publishing to pub.dev. + +## 8.1.9 +* Fixed automated publishing to pub.dev. + +## 8.1.8 +* Fixed automated publishing to pub.dev. + +## 8.1.7 +* Automated publishing to pub.dev. + +## 8.1.6 +* Fixed compatibility with `integration_test` by downgrading the minimum version of the platform package to 3.1.0. + +## 8.1.5 +* Reversed background/font color toolbar button icons. + +## 8.1.4 +* Reversed background/font color toolbar button tooltips. + +## 8.1.3 +* Moved images to screenshots instead of `README.md`. + +## 8.1.2 +* Fixed a bug related to the regexp of the insert link dialog. +* Required Dart 3 as the minimum version. +* Code cleanup. +* Added a spacer widget between each button in the `QuillToolbar`. + +## 8.1.1 +* Fixed null error in line.dart #1487(https://github.com/singerdmx/flutter*quill/issues/1487). + +## 8.1.0 +* Fixed a word typo of `mirgration` to `migration` in the readme & migration document. +* Updated migration guide. +* Removed property `enableUnfocusOnTapOutside` in `QuillEditor` configurations and added `isOnTapOutsideEnabled` instead. +* Added a new callback called `onTapOutside` in the `QuillEditorConfigurations` to perform actions when tapping outside the editor. +* Fixed a bug that caused the web platform to not unfocus the editor when tapping outside of it. To override this, please pass a value to the `onTapOutside` callback. +* Removed the old property of `iconTheme`. Instead, pass `iconTheme` in the button options; you will find the `base` property inside it with `iconTheme`. + +## 8.0.0 +* If you have migrated recently, don't be alarmed by this update; it adds documentation, a migration guide, and marks the version as a more stable release. Although there are breaking changes (as reported by some developers), the major version was not changed due to time constraints during development. A single property was also renamed from `code` to `codeBlock` in the `elements` of the new `QuillEditorConfigurations` class. +* Updated the README for better readability. + +## 7.10.2 +* Removed line numbers from code blocks by default. You can still enable this feature thanks to the new configurations in the `QuillEditor`. Find the `elementOptions` property and enable `enableLineNumbers`. + +## 7.10.1 +* Fixed issues and utilized the new parameters. +* No longer need to use `MaterialApp` for most toolbar button child builders. +* Compatibility with [fresh_quill_extensions](https://pub.dev/packages/fresh_quill_extensions), a temporary alternative to [flutter_quill_extensions](https://pub.dev/packages/flutter_quill_extensions). +* Updated most of the documentation in `README.md`. + +## 7.10.0 +* **Breaking change**: `QuillToolbar.basic()` can be accessed directly from `QuillToolbar()`, and the old `QuillToolbar` can be accessed from `QuillBaseToolbar`. +* Refactored Quill editor and toolbar configurations into a single class each. +* After changing checkbox list values, the controller will not request keyboard focus by default. +* Moved toolbar and editor configurations directly into the widget but still use inherited widgets internally. +* Fixes to some code after the refactoring. + +## 7.9.0 +* Buttons Improvemenets +* Refactor all the button configurations that used in `QuillToolbar.basic()` but there are still few lefts +* **Breaking change**: Remove some configurations from the QuillToolbar and move them to the new `QuillProvider`, please notice this is a development version and this might be changed in the next few days, the stable release will be ready in less than 3 weeks +* Update `flutter_quill_extensions` and it will be published into pub.dev soon. +* Allow you to customize the search dialog by custom callback with child builder + +## 7.8.0 +* **Important note**: this is not test release yet, it works but need more test and changes and breaking changes, we don't have development version and it will help us if you try the latest version and report the issues in Github but if you want a stable version please use `7.4.16`. this refactoring process will not take long and should be done less than three weeks with the testing. +* We managed to refactor most of the buttons configurations and customizations in the `QuillProvider`, only three lefts then will start on refactoring the toolbar configurations +* Code improvemenets + +## 7.7.0 +* **Breaking change**: We have mirgrated more buttons in the toolbar configurations, you can do change them in the `QuillProvider` +* Important bug fixes + +## 7.6.1 +* Bug fixes + +## 7.6.0 +* **Breaking change**: To customize the buttons in the toolbar, you can do that in the `QuillProvider` + +## 7.5.0 +* **Breaking change**: The widgets `QuillEditor` and `QuillToolbar` are no longer have controller parameter, instead you need to make sure in the widget tree you have wrapped them with `QuillProvider` widget and provide the controller and the require configurations + +## 7.4.16 +* Update documentation and README.md + +## 7.4.15 +* Custom style attrbuites for platforms other than mobile (alignment, margin, width, height) +* Bug fixes and other improvemenets + +## 7.4.14 +* Improve performance by reducing the number of widgets rebuilt by listening to media query for only the needed things, for example instead of using `MediaQuery.of(context).size`, now we are using `MediaQuery.sizeOf(context)` +* Add MediaButton for picking the images only since the video one is not ready +* A new feature which allows customizing the text selection in quill editor which is useful for custom theme design system for custom app widget + +## 7.4.13 +* Fixed tab editing when in readOnly mode. + +## 7.4.12 +* Update the minimum version of device_info_plus to 9.1.0. + +## 7.4.11 +* Add sw locale. + +## 7.4.10 +* Update translations. + +## 7.4.9 +* Style recognition fixes. + +## 7.4.8 +* Upgrade dependencies. + +## 7.4.7 +* Add Vietnamese and German translations. + +## 7.4.6 +* Fix more null errors in Leaf.retain [##1394](https://github.com/singerdmx/flutter-quill/issues/1394) and Line.delete [##1395](https://github.com/singerdmx/flutter-quill/issues/1395). + +## 7.4.5 +* Fix null error in Container.insert [##1392](https://github.com/singerdmx/flutter-quill/issues/1392). + +## 7.4.4 +* Fix extra padding on checklists [##1131](https://github.com/singerdmx/flutter-quill/issues/1131). + +## 7.4.3 +* Fixed a space input error on iPad. + +## 7.4.2 +* Fix bug with keepStyleOnNewLine for link. + +## 7.4.1 +* Fix toolbar dividers condition. + +## 7.4.0 +* Support Flutter version 3.13.0. + +## 7.3.3 +* Updated Dependencies conflicting. + +## 7.3.2 +* Added builder for custom button in _LinkDialog. + +## 7.3.1 +* Added case sensitive and whole word search parameters. +* Added wrap around. +* Moved search dialog to the bottom in order not to override the editor and the text found. +* Other minor search dialog enhancements. + +## 7.3.0 +* Add default attributes to basic factory. + +## 7.2.19 +* Feat/link regexp. + +## 7.2.18 +* Fix paste block text in words apply same style. + +## 7.2.17 +* Fix paste text mess up style. +* Add support copy/cut block text. + +## 7.2.16 +* Allow for custom context menu. + +## 7.2.15 +* Add flutter_quill.delta library which only exposes Delta datatype. + +## 7.2.14 +* Fix errors when the editor is used in the `screenshot` package. + +## 7.2.13 +* Fix around image can't delete line break. + +## 7.2.12 +* Add support for copy/cut select image and text together. + +## 7.2.11 +* Add affinity for localPosition. + +## 7.2.10 +* LINE._getPlainText queryChild inclusive=false. + +## 7.2.9 +* Add toPlainText method to `EmbedBuilder`. + +## 7.2.8 +* Add custom button widget in toolbar. + +## 7.2.7 +* Fix language code of Japan. + +## 7.2.6 +* Style custom toolbar buttons like builtins. + +## 7.2.5 +* Always use text cursor for editor on desktop. + +## 7.2.4 +* Fixed keepStyleOnNewLine. + +## 7.2.3 +* Get pixel ratio from view. + +## 7.2.2 +* Prevent operations on stale editor state. + +## 7.2.1 +* Add support for android keyboard content insertion. +* Enhance color picker, enter hex color and color palette option. + +## 7.2.0 +* Checkboxes, bullet points, and number points are now scaled based on the default paragraph font size. + +## 7.1.20 +* Pass linestyle to embedded block. + +## 7.1.19 +* Fix Rtl leading alignment problem. + +## 7.1.18 +* Support flutter latest version. + +## 7.1.17+1 +* Updates `device_info_plus` to version 9.0.0 to benefit from AGP 8 (see [changelog##900](https://pub.dev/packages/device_info_plus/changelog##900)). + +## 7.1.16 +* Fixed subscript key from 'sup' to 'sub'. + +## 7.1.15 +* Fixed a bug introduced in 7.1.7 where each section in `QuillToolbar` was displayed on its own line. + +## 7.1.14 +* Add indents change for multiline selection. + +## 7.1.13 + +* Add custom recognizer. + +## 7.1.12 + +* Add superscript and subscript styles. + +## 7.1.11 + +* Add inserting indents for lines of list if text is selected. + +## 7.1.10 + +* Image embedding tweaks + * Add MediaButton which is intened to superseed the ImageButton and VideoButton. Only image selection is working. + * Implement image insert for web (image as base64) + +## 7.1.9 + +* Editor tweaks PR from bambinoua(https://github.com/bambinoua). + * Shortcuts now working in Mac OS + * QuillDialogTheme is extended with new properties buttonStyle, linkDialogConstraints, imageDialogConstraints, isWrappable, runSpacing, + * Added LinkStyleButton2 with new LinkStyleDialog (similar to Quill implementation + * Conditinally use Row or Wrap for dialog's children. + * Update minimum Dart SDK version to 2.17.0 to use enum extensions. + * Use merging shortcuts and actions correclty (if the key combination is the same) + +## 7.1.8 + +* Dropdown tweaks + * Add itemHeight, itemPadding, defaultItemColor for customization of dropdown items. + * Remove alignment property as useless. + * Fix bugs with max width when width property is null. + +## 7.1.7 + +* Toolbar tweaks. + * Implement tooltips for embed CameraButton, VideoButton, FormulaButton, ImageButton. + * Extends customization for SelectAlignmentButton, QuillFontFamilyButton, QuillFontSizeButton adding padding, text style, alignment, width. + * Add renderFontFamilies to QuillFontFamilyButton to show font faces in dropdown. + * Add AxisDivider and its named constructors for for use in parent project. + * Export ToolbarButtons enum to allow specify tooltips for SelectAlignmentButton. + * Export QuillFontFamilyButton, SearchButton as they were not exported before. + * Deprecate items property in QuillFontFamilyButton, QuillFontSizeButton as the it can be built usinr rawItemsMap. + * Make onSelection QuillFontFamilyButton, QuillFontSizeButton omittable as no need to execute callback outside if controller is passed to widget. + +Now the package is more friendly for web projects. + +## 7.1.6 + +* Add enableUnfocusOnTapOutside field to RawEditor and Editor widgets. + +## 7.1.5 + +* Add tooltips for toolbar buttons. + +## 7.1.4 + +* Fix inserting tab character in lists. + +## 7.1.3 + +* Fix ios cursor bug when word.length==1. + +## 7.1.2 + +* Fix non scrollable editor exception, when tapped under content. + +## 7.1.1 + +* customLinkPrefixes parameter * makes possible to open links with custom protoco. + +## 7.1.0 + +* Fix ordered list numeration with several lists in document. + +## 7.0.9 + +* Use const constructor for EmbedBuilder. + +## 7.0.8 + +* Fix IME position bug with scroller. + +## 7.0.7 + +* Add TextFieldTapRegion for contextMenu. + +## 7.0.6 + +* Fix line style loss on new line from non string. + +## 7.0.5 + +* Fix IME position bug for Mac and Windows. +* Unfocus when tap outside editor. fix the bug that cant refocus in afterButtonPressed after click ToggleStyleButton on Mac. + +## 7.0.4 + +* Have text selection span full line height for uneven sized text. + +## 7.0.3 + +* Fix ordered list numeration for lists with more than one level of list. + +## 7.0.2 + +* Allow widgets to override widget span properties. + +## 7.0.1 + +* Update i18n_extension dependency to version 8.0.0. + +## 7.0.0 + +* Breaking change: Tuples are no longer used. They have been replaced with a number of data classes. + +## 6.4.4 + +* Increased compatibility with Flutter widget tests. + +## 6.4.3 + +* Update dependencies (collection: 1.17.0, flutter_keyboard_visibility: 5.4.0, quiver: 3.2.1, tuple: 2.0.1, url_launcher: 6.1.9, characters: 1.2.1, i18n_extension: 7.0.0, device_info_plus: 8.1.0) + +## 6.4.2 + +* Replace `buildToolbar` with `contextMenuBuilder`. + +## 6.4.1 + +* Control the detect word boundary behaviour. + +## 6.4.0 + +* Use `axis` to make the toolbar vertical. +* Use `toolbarIconCrossAlignment` to align the toolbar icons on the cross axis. +* Breaking change: `QuillToolbar`'s parameter `toolbarHeight` was renamed to `toolbarSize`. + +## 6.3.5 + +* Ability to add custom shortcuts. + +## 6.3.4 + +* Update clipboard status prior to showing selected text overlay. + +## 6.3.3 + +* Fixed handling of mac intents. + +## 6.3.2 + +* Added `unknownEmbedBuilder` to QuillEditor. +* Fix error style when input chinese japanese or korean. + +## 6.3.1 + +* Add color property to the basic factory function. + +## 6.3.0 + +* Support Flutter 3.7. + +## 6.2.2 + +* Fix: nextLine getter null where no assertion. + +## 6.2.1 + +* Revert "Align numerical and bullet lists along with text content". + +## 6.2.0 + +* Align numerical and bullet lists along with text content. + +## 6.1.12 + +* Apply i18n for default font dropdown option labels corresponding to 'Clear'. + +## 6.1.11 + +* Remove iOS hack for delaying focus calculation. + +## 6.1.10 + +* Delay focus calculation for iOS. + +## 6.1.9 + +* Bump keyboard show up wait to 1 sec. + +## 6.1.8 + +* Recalculate focus when showing keyboard. + +## 6.1.7 + +* Add czech localizations. + +## 6.1.6 + +* Upgrade i18n_extension to 6.0.0. + +## 6.1.5 + +* Fix formatting exception. + +## 6.1.4 + +* Add double quotes validation. + +## 6.1.3 + +* Revert "fix order list numbering (##988)". + +## 6.1.2 + +* Add typing shortcuts. + +## 6.1.1 + +* Fix order list numbering. + +## 6.1.0 + +* Add keyboard shortcuts for editor actions. + +## 6.0.10 + +* Upgrade device info plus to ^7.0.0. + +## 6.0.9 + +* Don't throw showAutocorrectionPromptRect not implemented. The function is called with every keystroke as a user is typing. + +## 6.0.8+1 + +* Fixes null pointer when setting documents. + +## 6.0.8 + +* Make QuillController.document mutable. + +## 6.0.7 + +* Allow disabling of selection toolbar. + +## 6.0.6+1 + +* Revert 6.0.6. + +## 6.0.6 + +* Fix wrong custom embed key. + +## 6.0.5 + +* Fixes toolbar buttons stealing focus from editor. + +## 6.0.4 + +* Bug fix for Type 'Uint8List' not found. + +## 6.0.3 + +* Add ability to paste images. + +## 6.0.2 + +* Address Dart Analysis issues. + +## 6.0.1 + +* Changed translation country code (zh_HK -> zh_hk) to lower case, which is required for i18n_extension used in flutter_quill. +* Add localization in example's main to demonstrate translation. +* Issue Windows selection's copy / paste tool bar not shown ##861: add selection's copy / paste toolbar, escape to hide toolbar, mouse right click to show toolbar, ctrl-Y / ctrl-Z to undo / redo. +* Image and video displayed in Windows platform caused screen flickering while selecting text, a sample_data_nomedia.json asset is added for Desktop to demonstrate the added features. +* Known issue: keyboard action sometimes causes exception mentioned in Flutter's issue ##106475 (Windows Keyboard shortcuts stop working after modifier key repeat flutter/flutter##106475). +* Know issue: user needs to click the editor to get focus before toolbar is able to display. + +## 6.0.0 BREAKING CHANGE + +* Removed embed (image, video & formula) blocks from the package to reduce app size. + +These blocks have been moved to the package `flutter_quill_extensions`, migrate by filling the `embedBuilders` and `embedButtons` parameters as follows: + +``` +import 'package:flutter_quill_extensions/flutter_quill_extensions.dart'; + +QuillEditor.basic( + controller: controller, + embedBuilders: FlutterQuillEmbeds.builders(), +); + +QuillToolbar.basic( + controller: controller, + embedButtons: FlutterQuillEmbeds.buttons(), +); +``` + +## 5.4.2 + +* Upgrade i18n_extension. + +## 5.4.1 + +* Update German Translation. + +## 5.4.0 + +* Added Formula Button (for maths support). + +## 5.3.2 + +* Add more font family. + +## 5.3.1 + +* Enable search when text is not empty. + +## 5.3.0 + +* Added search function. + +## 5.2.11 + +* Remove default small color. + +## 5.2.10 + +* Don't wrap the QuillEditor's child in the EditorTextSelectionGestureDetector if selection is disabled. + +## 5.2.9 + +* Added option to modify SelectHeaderStyleButton options. +* Added option to click again on h1, h2, h3 button to go back to normal. + +## 5.2.8 + +* Remove tooltip for LinkStyleButton. +* Make link match regex case insensitive. + +## 5.2.7 + +* Add locale to QuillEditor.basic. + +## 5.2.6 + +* Fix keyboard pops up when resizing the image. + +## 5.2.5 + +* Upgrade youtube_player_flutter_quill to 8.2.2. + +## 5.2.4 + +* Upgrade youtube_player_flutter_quill to 8.2.1. + +## 5.2.3 + +* Flutter Quill Doesn't Work On iOS 16 or Xcode 14 Betas (Stored properties cannot be marked potentially unavailable with '@available'). + +## 5.2.2 + +* Fix Web Unsupported operation: Platform.\_operatingSystem error. + +## 5.2.1 + +* Rename QuillCustomIcon to QuillCustomButton. + +## 5.2.0 + +* Support font family selection. + +## 5.1.1 + +* Update README. + +## 5.1.0 + +* Added CustomBlockEmbed and customElementsEmbedBuilder. + +## 5.0.5 + +* Upgrade device_info_plus to 4.0.0. + +## 5.0.4 + +* Added onVideoInit callback for video documents. + +## 5.0.3 + +* Update dependencies. + +## 5.0.2 + +* Keep cursor position on checkbox tap. + +## 5.0.1 + +* Fix static analysis errors. + +## 5.0.0 + +* Flutter 3.0.0 support. + +## 4.2.3 + +* Ignore color:inherit and convert double to int for level. + +## 4.2.2 + +* Add clear option to font size dropdown. + +## 4.2.1 + +* Refactor font size dropdown. + +## 4.2.0 + +* Ensure selectionOverlay is available for showToolbar. + +## 4.1.9 + +* Using properly iconTheme colors. + +## 4.1.8 + +* Update font size dropdown. + +## 4.1.7 + +* Convert FontSize to a Map to allow for named Font Size. + +## 4.1.6 + +* Update quill_dropdown_button.dart. + +## 4.1.5 + +* Add Font Size dropdown to the toolbar. + +## 4.1.4 + +* New borderRadius for iconTheme. + +## 4.1.3 + +* Fix selection handles show/hide after paste, backspace, copy. + +## 4.1.2 + +* Add full support for hardware keyboards (Chromebook, Android tablets, etc) that don't alter screen UI. + +## 4.1.1 + +* Added textSelectionControls field in QuillEditor. + +## 4.1.0 + +* Added Node to linkActionPickerDelegate. + +## 4.0.12 + +* Add Persian(fa) language. + +## 4.0.11 + +* Fix cut selection error in multi-node line. + +## 4.0.10 + +* Fix vertical caret position bug. + +## 4.0.9 + +* Request keyboard focus when no child is found. + +## 4.0.8 + +* Fix blank lines do not display when **web*renderer=html. + +## 4.0.7 + +* Refactor getPlainText (better handling of blank lines and lines with multiple markups. + +## 4.0.6 + +* Bug fix for copying text with new lines. + +## 4.0.5 + +* Fixed casting null to Tuple2 when link dialog is dismissed without any input (e.g. barrier dismissed). + +## 4.0.4 + +* Bug fix for text direction rtl. + +## 4.0.3 + +* Support text direction rtl. + +## 4.0.2 + +* Clear toggled style on selection change. + +## 4.0.1 + +* Fix copy/cut/paste/selectAll not working. + +## 4.0.0 + +* Upgrade for Flutter 2.10. + +## 3.9.11 + +* Added Indonesian translation. + +## 3.9.10 + +* Fix for undoing a modification ending with an indented line. + +## 3.9.9 + +* iOS: Save image whose filename does not end with image file extension. + +## 3.9.8 + +* Added Urdu translation. + +## 3.9.7 + +* Fix for clicking on the Link button without any text on a new line crashes. + +## 3.9.6 + +* Apply locale to QuillEditor(contents). + +## 3.9.5 + +* Fix image pasting. + +## 3.9.4 + +* Hiding dialog after selecting action for image. + +## 3.9.3 + +* Update ImageResizer for Android. + +## 3.9.2 + +* Copy image with its style. + +## 3.9.1 + +* Support resizing image. + +## 3.9.0 + +* Image menu options for copy/remove. + +## 3.8.8 + +* Update set textEditingValue. + +## 3.8.7 + +* Fix checkbox not toggled correctly in toolbar button. + +## 3.8.6 + +* Fix cursor position changes when checking/unchecking the checkbox. + +## 3.8.5 + +* Fix \_handleDragUpdate in \_TextSelectionHandleOverlayState. + +## 3.8.4 + +* Fix link dialog layout. + +## 3.8.3 + +* Fix for errors on a non scrollable editor. + +## 3.8.2 + +* Fix certain keys not working on web when editor is a child of a scroll view. + +## 3.8.1 + +* Refactor \_QuillEditorState to QuillEditorState. + +## 3.8.0 + +* Support pasting with format. + +## 3.7.3 + +* Fix selection overlay for collapsed selection. + +## 3.7.2 + +* Reverted Embed toPlainText change. + +## 3.7.1 + +* Change Embed toPlainText to be empty string. + +## 3.7.0 + +* Replace Toolbar showHistory group with individual showRedo and showUndo. + +## 3.6.5 + +* Update Link dialogue for image/video. + +## 3.6.4 + +* Link dialogue TextInputType.multiline. + +## 3.6.3 + +* Bug fix for link button text selection. + +## 3.6.2 + +* Improve link button. + +## 3.6.1 + +* Remove SnackBar 'What is entered is not a link'. + +## 3.6.0 + +* Allow link button to enter text. + +## 3.5.3 + +* Change link button behavior. + +## 3.5.2 + +* Bug fix for embed. + +## 3.5.1 + +* Bug fix for platform util. + +## 3.5.0 + +* Removed redundant classes. + +## 3.4.4 + +* Add more translations. + +## 3.4.3 + +* Preset link from attributes. + +## 3.4.2 + +* Fix launch link edit mode. + +## 3.4.1 + +* Placeholder effective in scrollable. + +## 3.4.0 + +* Option to save image in read-only mode. + +## 3.3.1 + +* Pass any specified key in QuillEditor constructor to super. + +## 3.3.0 + +* Fixed Style toggle issue. + +## 3.2.1 + +* Added new translations. + +## 3.2.0 + +* Support multiple links insertion on the go. + +## 3.1.1 + +* Add selection completed callback. + +## 3.1.0 + +* Fixed image ontap functionality. + +## 3.0.4 + +* Add maxContentWidth constraint to editor. + +## 3.0.3 + +* Do not show caret on screen when the editor is not focused. + +## 3.0.2 + +* Fix launch link for read-only mode. + +## 3.0.1 + +* Handle null value of Attribute.link. + +## 3.0.0 + +* Launch link improvements. +* Removed QuillSimpleViewer. + +## 2.5.2 + +* Skip image when pasting. + +## 2.5.1 + +* Bug fix for Desktop `Shift` + `Click` support. + +## 2.5.0 + +* Update checkbox list. + +## 2.4.1 + +* Desktop selection improvements. + +## 2.4.0 + +* Improve inline code style. + +## 2.3.3 + +* Improves selection rects to have consistent height regardless of individual segment text styles. + +## 2.3.2 + +* Allow disabling floating cursor. + +## 2.3.1 + +* Preserve last newline character on delete. + +## 2.3.0 + +* Massive changes to support flutter 2.8. + +## 2.2.2 + +* iOS - floating cursor. + +## 2.2.1 + +* Bug fix for imports supporting flutter 2.8. + +## 2.2.0 + +* Support flutter 2.8. + +## 2.1.1 + +* Add methods of clearing editor and moving cursor. + +## 2.1.0 + +* Add delete handler. + +## 2.0.23 + +* Support custom replaceText handler. + +## 2.0.22 + +* Fix attribute compare and fix font size parsing. + +## 2.0.21 + +* Handle click on embed object. + +## 2.0.20 + +* Improved UX/UI of Image widget. + +## 2.0.19 + +* When uploading a video, applying indicator. + +## 2.0.18 + +* Make toolbar dividers optional. + +## 2.0.17 + +* Allow alignment of the toolbar icons to match WrapAlignment. + +## 2.0.16 + +* Add hide / show alignment buttons. + +## 2.0.15 + +* Implement change cursor to SystemMouseCursors.click when hovering a link styled text. + +## 2.0.14 + +* Enable customize the checkbox widget using DefaultListBlockStyle style. + +## 2.0.13 + +* Improve the scrolling performance by reducing the repaint areas. + +## 2.0.12 + +* Fix the selection effect can't be seen as the textLine with background color. + +## 2.0.11 + +* Fix visibility of text selection handlers on scroll. + +## 2.0.10 + +* cursorConnt.color notify the text_line to repaint if it was disposed. + +## 2.0.9 + +* Improve UX when trying to add a link. + +## 2.0.8 + +* Adding translations to the toolbar. + +## 2.0.7 + +* Added theming options for toolbar icons and LinkDialog. + +## 2.0.6 + +* Avoid runtime error when placed inside TabBarView. + +## 2.0.5 + +* Support inline code formatting. + +## 2.0.4 + +* Enable history shortcuts for desktop. + +## 2.0.3 + +* Fix cursor when line contains image. + +## 2.0.2 + +* Address KeyboardListener class name conflict. + +## 2.0.1 + +* Upgrade flutter_colorpicker to 0.5.0. + +## 2.0.0 + +* Text Alignment functions + Block Format standards. + +## 1.9.6 + +* Support putting QuillEditor inside a Scrollable view. + +## 1.9.5 + +* Skip image when pasting. + +## 1.9.4 + +* Bug fix for cursor position when tapping at the end of line with image(s). + +## 1.9.3 + +* Bug fix when line only contains one image. + +## 1.9.2 + +* Support for building custom inline styles. + +## 1.9.1 + +* Cursor jumps to the most appropriate offset to display selection. + +## 1.9.0 + +* Support inline image. + +## 1.8.3 + +* Updated quill_delta. + +## 1.8.2 + +* Support mobile image alignment. + +## 1.8.1 + +* Support mobile custom size image. + +## 1.8.0 + +* Support entering link for image/video. + +## 1.7.3 + +* Bumps photo_view version. + +## 1.7.2 + +* Fix static analysis error. + +## 1.7.1 + +* Support Youtube video. + +## 1.7.0 + +* Support video. + +## 1.6.4 + +* Bug fix for clear format button. + +## 1.6.3 + +* Fixed dragging right handle scrolling issue. + +## 1.6.2 + +* Fixed the position of the selection status drag handle. + +## 1.6.1 + +* Upgrade image_picker and flutter_colorpicker. + +## 1.6.0 + +* Support Multi Row Toolbar. + +## 1.5.0 + +* Remove file_picker dependency. + +## 1.4.1 + +* Remove filesystem_picker dependency. + +## 1.4.0 + +* Remove path_provider dependency. + +## 1.3.4 + +* Add option to paintCursorAboveText. + +## 1.3.3 + +* Upgrade file_picker version. + +## 1.3.2 + +* Fix copy/paste bug. + +## 1.3.1 + +* New logo. + +## 1.3.0 + +* Support flutter 2.2.0. + +## 1.2.2 + +* Checkbox supports tapping. + +## 1.2.1 + +* Indented position not holding while editing. + +## 1.2.0 + +* Fix image button cancel causes crash. + +## 1.1.8 + +* Fix height of empty line bug. + +## 1.1.7 + +* Fix text selection in read-only mode. + +## 1.1.6 + +* Remove universal_html dependency. + +## 1.1.5 + +* Enable "Select", "Select All" and "Copy" in read-only mode. + +## 1.1.4 + +* Fix text selection issue. + +## 1.1.3 + +* Update example folder. + +## 1.1.2 + +* Add pedantic. + +## 1.1.1 + +* Base64 image support. + +## 1.1.0 + +* Support null safety. + +## 1.0.9 + +* Web support for raw editor and keyboard listener. + +## 1.0.8 + +* Support token attribute. + +## 1.0.7 + +* Fix crash on web (dart:io). + +## 1.0.6 + +* Add desktop support WINDOWS, MACOS and LINUX. + +## 1.0.5 + +* Bug fix: Can not insert newline when Bold is toggled ON. + +## 1.0.4 + +* Upgrade photo_view to ^0.11.0. + +## 1.0.3 + +* Fix issue that text is not displayed while typing WEB. + +## 1.0.2 + +* Update toolbar in sample home page. + +## 1.0.1 + +* Fix static analysis errors. + +## 1.0.0 + +* Support flutter 2.0. + +## 1.0.0-dev.2 + +* Improve link handling for tel, mailto and etc. + +## 1.0.0-dev.1 + +* Upgrade prerelease SDK & Bump for master. + +## 0.3.5 + +* Fix for cursor focus issues when keyboard is on. + +## 0.3.4 + +* Improve link handling for tel, mailto and etc. + +## 0.3.3 + +* More fix on cursor focus issue when keyboard is on. + +## 0.3.2 + +* Fix cursor focus issue when keyboard is on. + +## 0.3.1 + +* cursor focus when keyboard is on. + +## 0.3.0 + +* Line Height calculated based on font size. + +## 0.2.12 + +* Support placeholder. + +## 0.2.11 + +* Fix static analysis error. + +## 0.2.10 + +* Update TextInputConfiguration autocorrect to true in stable branch. + +## 0.2.9 + +* Update TextInputConfiguration autocorrect to true. + +## 0.2.8 + +* Support display local image besides network image in stable branch. + +## 0.2.7 + +* Support display local image besides network image. + +## 0.2.6 + +* Fix cursor after pasting. + +## 0.2.5 + +* Toggle text/background color button in toolbar. + +## 0.2.4 + +* Support the use of custom icon size in toolbar. + +## 0.2.3 + +* Support custom styles and image on local device storage without uploading. + +## 0.2.2 + +* Update git repo. + +## 0.2.1 + +* Fix static analysis error. + +## 0.2.0 + +* Add checked/unchecked list button in toolbar. + +## 0.1.8 + +* Support font and size attributes. + +## 0.1.7 + +* Support checked/unchecked list. + +## 0.1.6 + +* Fix getExtentEndpointForSelection. + +## 0.1.5 + +* Support text alignment. + +## 0.1.4 + +* Handle url with trailing spaces. + +## 0.1.3 + +* Handle cursor position change when undo/redo. + +## 0.1.2 + +* Handle more text colors. + +## 0.1.1 + +* Fix cursor issue when undo. + +## 0.1.0 + +* Fix insert image. + +## 0.0.9 + +* Handle rgba color. + +## 0.0.8 + +* Fix launching url. + +## 0.0.7 + +* Handle multiple image inserts. + +## 0.0.6 + +* More toolbar functionality. + +## 0.0.5 + +* Update example. + +## 0.0.4 + +* Update example. + +## 0.0.3 + +* Update home page meta data. + +## 0.0.2 + +* Support image upload and launch url in read-only mode. + +## 0.0.1 + +* Rich text editor based on Quill Delta. diff --git a/packages/quill_html_converter/lib/quill_html_converter.dart b/packages/quill_html_converter/lib/quill_html_converter.dart index 72645e997..2e8d080fa 100644 --- a/packages/quill_html_converter/lib/quill_html_converter.dart +++ b/packages/quill_html_converter/lib/quill_html_converter.dart @@ -1,11 +1,6 @@ library quill_html_converter; -import 'dart:convert' show jsonDecode; - -import 'package:delta_markdown_converter/delta_markdown_converter.dart' - as delta_markdown show markdownToDelta; import 'package:flutter_quill/flutter_quill.dart' show Delta; -import 'package:html2md/html2md.dart' as html2md; import 'package:vsc_quill_delta_to_html/vsc_quill_delta_to_html.dart' as conventer show ConverterOptions, QuillDeltaToHtmlConverter; @@ -30,31 +25,4 @@ extension DeltaHtmlExt on Delta { ).convert(); return html; } - - /// Convert the HTML Raw string to [Delta] - /// - /// It will run using the following steps: - /// - /// 1. Convert the html to markdown string using `html2md` package - /// 2. Convert the markdown string to quill delta json string - /// 3. Decode the delta json string to [Delta] - /// - /// for more [info](https://github.com/singerdmx/flutter-quill/issues/1100) - static Delta fromHtml(String html) { - final markdown = html2md - .convert( - html, - ) - .replaceAll('unsafe:', ''); - final deltaJsonString = delta_markdown.markdownToDelta(markdown); - final deltaJson = jsonDecode(deltaJsonString); - if (deltaJson is! List) { - throw ArgumentError( - 'The delta json string should be of type list when jsonDecode() it', - ); - } - return Delta.fromJson( - deltaJson, - ); - } } diff --git a/packages/quill_html_converter/pubspec.yaml b/packages/quill_html_converter/pubspec.yaml index f2e41adac..a5aa9589f 100644 --- a/packages/quill_html_converter/pubspec.yaml +++ b/packages/quill_html_converter/pubspec.yaml @@ -1,6 +1,6 @@ name: quill_html_converter description: A extension for flutter_quill package to add support for dealing with conversion to/from html -version: 0.0.1-experimental.1 +version: 9.0.0-dev-8 homepage: https://github.com/singerdmx/flutter-quill/tree/master/packages/quill_html_converter/ repository: https://github.com/singerdmx/flutter-quill/tree/master/packages/quill_html_converter/ issue_tracker: https://github.com/singerdmx/flutter-quill/issues/ @@ -20,11 +20,14 @@ environment: dependencies: flutter: sdk: flutter - flutter_quill: ^8.5.1 + flutter_quill: ^9.0.0-dev-6 vsc_quill_delta_to_html: ^1.0.3 html2md: ^1.3.1 # markdown: ^7.1.1 - delta_markdown_converter: ^0.0.2 + # delta_markdown_converter: ^0.0.3-dev + markdown: ^7.1.1 + charcode: ^1.3.1 + collection: ^1.18.0 dev_dependencies: flutter_test: diff --git a/pubspec.yaml b/pubspec.yaml index bcd2b3f22..7d396197b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: flutter_quill description: A rich text editor built for the modern Android, iOS, web and desktop platforms. It is the WYSIWYG editor and a Quill component for Flutter. -version: 8.6.4 +version: 9.0.0-dev-8 homepage: https://1o24bbs.com/c/bulletjournal/108/ repository: https://github.com/singerdmx/flutter-quill/ issue_tracker: https://github.com/singerdmx/flutter-quill/issues/ @@ -50,22 +50,27 @@ dependencies: characters: ^1.3.0 diff_match_patch: ^0.4.1 equatable: ^2.0.5 - flutter_animate: ^4.2.0+1 meta: ^1.9.1 + # For Quill HTML + markdown: ^7.1.1 + html2md: ^1.3.1 + # Plugins url_launcher: ^6.1.14 flutter_keyboard_visibility: ^5.4.1 device_info_plus: ^9.1.0 - pasteboard: ^0.2.0 + super_clipboard: ^0.7.3 + charcode: ^1.3.1 dev_dependencies: flutter_lints: ^3.0.1 flutter_test: sdk: flutter - flutter_quill_test: ^0.0.4 + flutter_quill_test: ^9.0.0-dev-6 test: ^1.24.3 intl_translation: ^0.18.2 + yaml_edit: ^2.1.1 flutter: uses-material-design: true diff --git a/scripts/before-push.sh b/scripts/before_push.sh similarity index 100% rename from scripts/before-push.sh rename to scripts/before_push.sh diff --git a/scripts/regenerate-translations.sh b/scripts/regenerate_translations.sh similarity index 93% rename from scripts/regenerate-translations.sh rename to scripts/regenerate_translations.sh index 1f7c26e52..8e1583bf0 100755 --- a/scripts/regenerate-translations.sh +++ b/scripts/regenerate_translations.sh @@ -1,7 +1,7 @@ #!/bin/bash # Important: make sure to run the script in the root folder of the repo: -# ./scripts/regenerate-translations.sh +# ./scripts/regenerate_translations.sh # otherwise the script could delete the wrong folder in rare cases echo "" diff --git a/scripts/regenerate_versions.dart b/scripts/regenerate_versions.dart new file mode 100644 index 000000000..ed73e523a --- /dev/null +++ b/scripts/regenerate_versions.dart @@ -0,0 +1,40 @@ +// ignore_for_file: avoid_print + +import 'dart:io' show File; + +import 'package:yaml_edit/yaml_edit.dart'; + +// You must run this script in the root folder of the repo and not inside the scripts + +// ignore: unused_import +import '../version.dart'; + +final packages = [ + './', + './flutter_quill_extensions', + './flutter_quill_test', + './packages/quill_html_converter' +]; + +Future main(List args) async { + for (final element in packages) { + await updatePubspecYamlFile('$element/pubspec.yaml'); + if (element != packages.first) { + updateChangelogMD(element); + } + } +} + +Future updatePubspecYamlFile(String path) async { + final file = File(path); + final yaml = await file.readAsString(); + final yamlEditor = YamlEditor(yaml)..update(['version'], version); + await file.writeAsString(yamlEditor.toString()); + print(yamlEditor.toString()); +} + +Future updateChangelogMD(String path) async { + final changeLog = await File('./CHANGELOG.md').readAsString(); + final currentFile = File('$path/CHANGELOG.md'); + await currentFile.writeAsString(changeLog); +} diff --git a/scripts/regenerate_versions.sh b/scripts/regenerate_versions.sh new file mode 100755 index 000000000..379020442 --- /dev/null +++ b/scripts/regenerate_versions.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +dart ./scripts/regenerate_versions.dart \ No newline at end of file diff --git a/test/bug_fix_test.dart b/test/bug_fix_test.dart index 818494592..fb135f127 100644 --- a/test/bug_fix_test.dart +++ b/test/bug_fix_test.dart @@ -16,14 +16,12 @@ void main() { await tester.pumpWidget( MaterialApp( - home: QuillProvider( - configurations: QuillConfigurations( - controller: controller, - ), - child: const QuillToolbar( - configurations: QuillToolbarConfigurations( + home: Scaffold( + body: QuillSimpleToolbar( + configurations: QuillSimpleToolbarConfigurations( + controller: controller, showRedo: false, - customButtons: [ + customButtons: const [ QuillToolbarCustomButtonOptions( tooltip: tooltip, ) @@ -40,19 +38,19 @@ void main() { matchRoot: true, ); expect(builtinFinder, findsOneWidget); - final builtinButton = - builtinFinder.evaluate().first.widget as QuillToolbarIconButton; + // final builtinButton = + // builtinFinder.evaluate().first.widget as QuillToolbarIconButton; final customFinder = find.descendant( - of: find.byType(QuillBaseToolbar), + of: find.byType(QuillToolbar), matching: find.byWidgetPredicate((widget) => widget is QuillToolbarIconButton && widget.tooltip == tooltip), matchRoot: true); expect(customFinder, findsOneWidget); - final customButton = - customFinder.evaluate().first.widget as QuillToolbarIconButton; + // final customButton = + // customFinder.evaluate().first.widget as QuillToolbarIconButton; - expect(customButton.fillColor, equals(builtinButton.fillColor)); + // expect(customButton.fillColor, equals(builtinButton.fillColor)); }); }); @@ -64,7 +62,8 @@ void main() { controller = QuillController.basic(); editor = QuillEditor.basic( // ignore: avoid_redundant_argument_values - configurations: const QuillEditorConfigurations( + configurations: QuillEditorConfigurations( + controller: controller, // ignore: avoid_redundant_argument_values readOnly: false, ), @@ -78,12 +77,9 @@ void main() { testWidgets('Refocus editor after controller clears document', (tester) async { await tester.pumpWidget( - QuillProvider( - configurations: QuillConfigurations(controller: controller), - child: MaterialApp( - home: Column( - children: [editor], - ), + MaterialApp( + home: Column( + children: [editor], ), ), ); @@ -99,12 +95,9 @@ void main() { testWidgets('Refocus editor after removing block attribute', (tester) async { - await tester.pumpWidget(QuillProvider( - configurations: QuillConfigurations(controller: controller), - child: MaterialApp( - home: Column( - children: [editor], - ), + await tester.pumpWidget(MaterialApp( + home: Column( + children: [editor], ), )); await tester.quillEnterText(find.byType(QuillEditor), 'test\n'); @@ -120,12 +113,9 @@ void main() { testWidgets('Tap checkbox in unfocused editor', (tester) async { await tester.pumpWidget( - QuillProvider( - configurations: QuillConfigurations(controller: controller), - child: MaterialApp( - home: Column( - children: [editor], - ), + MaterialApp( + home: Column( + children: [editor], ), ), ); diff --git a/test/widgets/editor_test.dart b/test/widgets/editor_test.dart index f063e4291..72db9c2b5 100644 --- a/test/widgets/editor_test.dart +++ b/test/widgets/editor_test.dart @@ -21,15 +21,13 @@ void main() { group('QuillEditor', () { testWidgets('Keyboard entered text is stored in document', (tester) async { await tester.pumpWidget( - QuillProvider( - configurations: QuillConfigurations(controller: controller), - child: MaterialApp( - home: QuillEditor.basic( + MaterialApp( + home: QuillEditor.basic( + // ignore: avoid_redundant_argument_values + configurations: QuillEditorConfigurations( + controller: controller, // ignore: avoid_redundant_argument_values - configurations: const QuillEditorConfigurations( - // ignore: avoid_redundant_argument_values - readOnly: false, - ), + readOnly: false, ), ), ), @@ -43,24 +41,20 @@ void main() { String? latestUri; await tester.pumpWidget( MaterialApp( - home: QuillProvider( - configurations: QuillConfigurations( + home: QuillEditor( + focusNode: FocusNode(), + scrollController: ScrollController(), + configurations: QuillEditorConfigurations( controller: controller, - ), - child: QuillEditor( - focusNode: FocusNode(), - scrollController: ScrollController(), - configurations: QuillEditorConfigurations( - // ignore: avoid_redundant_argument_values - readOnly: false, - autoFocus: true, - expands: true, - contentInsertionConfiguration: ContentInsertionConfiguration( - onContentInserted: (content) { - latestUri = content.uri; - }, - allowedMimeTypes: ['image/gif'], - ), + // ignore: avoid_redundant_argument_values + readOnly: false, + autoFocus: true, + expands: true, + contentInsertionConfiguration: ContentInsertionConfiguration( + onContentInserted: (content) { + latestUri = content.uri; + }, + allowedMimeTypes: ['image/gif'], ), ), ), @@ -120,21 +114,17 @@ void main() { testWidgets('custom context menu builder', (tester) async { await tester.pumpWidget( MaterialApp( - home: QuillProvider( - configurations: QuillConfigurations( + home: QuillEditor( + focusNode: FocusNode(), + scrollController: ScrollController(), + // ignore: avoid_redundant_argument_values + configurations: QuillEditorConfigurations( controller: controller, - ), - child: QuillEditor( - focusNode: FocusNode(), - scrollController: ScrollController(), // ignore: avoid_redundant_argument_values - configurations: QuillEditorConfigurations( - // ignore: avoid_redundant_argument_values - readOnly: false, - autoFocus: true, - expands: true, - contextMenuBuilder: customBuilder, - ), + readOnly: false, + autoFocus: true, + expands: true, + contextMenuBuilder: customBuilder, ), ), ), diff --git a/version.dart b/version.dart new file mode 100644 index 000000000..27a78c104 --- /dev/null +++ b/version.dart @@ -0,0 +1 @@ +const version = '9.0.0-dev-8';