diff --git a/lib/src/models/documents/document.dart b/lib/src/models/documents/document.dart index c20ab27d..d6fa7f30 100644 --- a/lib/src/models/documents/document.dart +++ b/lib/src/models/documents/document.dart @@ -59,6 +59,7 @@ class Document { final StreamController documentChangeObserver = StreamController.broadcast(); + // TODO: See why we made this public when it was private field final History history = History(); /// Stream of [DocChange]s applied to this document. diff --git a/lib/src/utils/platform.dart b/lib/src/utils/platform.dart index d248b369..e2ccc1bc 100644 --- a/lib/src/utils/platform.dart +++ b/lib/src/utils/platform.dart @@ -4,14 +4,6 @@ import 'package:device_info_plus/device_info_plus.dart'; import 'package:flutter/foundation.dart' show TargetPlatform, defaultTargetPlatform, kIsWeb, visibleForTesting; -/// If you want to override the [kIsWeb] use [overrideIsWeb] but it's only -/// for testing -bool isWeb({ - @visibleForTesting bool? overrideIsWeb, -}) { - return overrideIsWeb ?? kIsWeb; -} - /// [supportWeb] is a parameter that ask you if we should care about web support /// if the value is true then we will return the result no matter if we are /// on web or using a native app to run the flutter app @@ -128,6 +120,16 @@ Future isIOSSimulator({ return false; } +// TODO: This is only used for tests, we might remove it or keep it + +/// If you want to override the [kIsWeb] use [overrideIsWeb] but it's only +/// for testing +bool isWeb({ + @visibleForTesting bool? overrideIsWeb, +}) { + return overrideIsWeb ?? kIsWeb; +} + bool isFlutterTest({ bool? overrideIsWeb, }) { diff --git a/lib/src/widgets/quill/quill_controller.dart b/lib/src/widgets/quill/quill_controller.dart index bf21c8f4..25d6d3e5 100644 --- a/lib/src/widgets/quill/quill_controller.dart +++ b/lib/src/widgets/quill/quill_controller.dart @@ -1,9 +1,12 @@ +import 'dart:async' show StreamSubscription; import 'dart:math' as math; +import 'package:flutter/foundation.dart' show kIsWeb; import 'package:flutter/services.dart' show ClipboardData, Clipboard; import 'package:flutter/widgets.dart'; import 'package:html/parser.dart' as html_parser; import 'package:meta/meta.dart'; +import 'package:web/web.dart' as web; import '../../../flutter_quill.dart'; import '../../../quill_delta.dart'; @@ -27,7 +30,20 @@ class QuillController extends ChangeNotifier { this.readOnly = false, this.editorFocusNode, }) : _document = document, - _selection = selection; + _selection = selection { + if (kIsWeb) { + _webPasteEventSubscription = web.EventStreamProviders.pasteEvent + .forTarget(web.window.document) + .listen((e) { + // TODO: See if we can support markdown as well + final html = e.clipboardData?.getData('text/html'); + if (html == null) { + return; + } + _pasteHTML(html: html); + }); + } + } factory QuillController.basic( {QuillControllerConfigurations configurations = @@ -97,8 +113,8 @@ class QuillController extends ChangeNotifier { bool ignoreFocusOnTextChange = false; - /// Skip requestKeyboard being called in - /// RawEditorState#_didChangeTextEditingValue + /// Skip requestKeyboard being called + /// in [QuillRawEditorState._didChangeTextEditingValue] bool skipRequestKeyboard = false; /// True when this [QuillController] instance has been disposed. @@ -114,6 +130,13 @@ class QuillController extends ChangeNotifier { selection: selection, ); + /// Paste event for the web. + /// + /// Will be `null` for non-web platforms. + /// + /// See: https://developer.mozilla.org/en-US/docs/Web/API/Element/paste_event + StreamSubscription? _webPasteEventSubscription; + /// Only attributes applied to all characters within this range are /// included in the result. Style getSelectionStyle() { @@ -431,6 +454,8 @@ class QuillController extends ChangeNotifier { } _isDisposed = true; + + _webPasteEventSubscription?.cancel(); super.dispose(); } @@ -566,11 +591,21 @@ class QuillController extends ChangeNotifier { ); } - /// Return true if can paste using HTML - Future _pasteHTML() async { + /// Paste the HTML into the document from [html] if not null, otherwise + /// will read it from the Clipboard in case the [ClipboardServiceProvider.instance] + /// support it on the current platform. + /// + /// The argument [html] allow to override the HTML that's being pasted, + /// mainly to support pasting HTML on the web in [_webPasteEventSubscription]. + /// + /// Return `true` if can paste or have pasted using HTML. + Future _pasteHTML({String? html}) async { final clipboardService = ClipboardServiceProvider.instance; Future getHTML() async { + if (html != null) { + return html; + } if (await clipboardService.canProvideHtmlTextFromFile()) { return await clipboardService.getHtmlTextFromFile(); } @@ -593,7 +628,7 @@ class QuillController extends ChangeNotifier { return false; } - /// Return true if can paste using Markdown + /// Return `true` if can paste or have pasted using Markdown. Future _pasteMarkdown() async { final clipboardService = ClipboardServiceProvider.instance; diff --git a/lib/src/widgets/raw_editor/raw_editor_state.dart b/lib/src/widgets/raw_editor/raw_editor_state.dart index 7b7b6646..cc3d972f 100644 --- a/lib/src/widgets/raw_editor/raw_editor_state.dart +++ b/lib/src/widgets/raw_editor/raw_editor_state.dart @@ -540,10 +540,7 @@ class QuillRawEditorState extends EditorState maxHeight: widget.configurations.maxHeight ?? double.infinity, ); - // Please notice that this change will make the check fixed - // so if we ovveride the platform in material app theme data - // it will not depend on it and doesn't change here but I don't think - // we need to + // TODO: Review this and decide if we should for macOS on the browser or not final isDesktopMacOS = isMacOS(supportWeb: true); return TextFieldTapRegion( diff --git a/pubspec.yaml b/pubspec.yaml index 89716e62..82287b03 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -53,6 +53,8 @@ dependencies: flutter_colorpicker: ^1.1.0 + web: ^0.5.1 + # For converting HTML to Quill delta flutter_quill_delta_from_html: ^1.3.12 markdown: ^7.2.1 diff --git a/test/bug_fix_test.dart b/test/bug_fix_test.dart index 0be8a1e0..3631ee77 100644 --- a/test/bug_fix_test.dart +++ b/test/bug_fix_test.dart @@ -62,10 +62,8 @@ void main() { setUp(() { controller = QuillController.basic(); editor = QuillEditor.basic( - // ignore: avoid_redundant_argument_values configurations: QuillEditorConfigurations( controller: controller, - // ignore: avoid_redundant_argument_values ), ); }); @@ -150,10 +148,8 @@ void main() { home: QuillEditor( focusNode: FocusNode(), scrollController: ScrollController(), - // ignore: avoid_redundant_argument_values configurations: QuillEditorConfigurations( controller: controller, - // ignore: avoid_redundant_argument_values autoFocus: true, expands: true, ), diff --git a/test/widgets/controller_test.dart b/test/widgets/controller_test.dart index 7c04b274..88679290 100644 --- a/test/widgets/controller_test.dart +++ b/test/widgets/controller_test.dart @@ -412,5 +412,8 @@ void main() { reason: 'line 1 extends into line3 which is not block'); } }); + + // TODO: Implement this, check of kIsWeb is not testable in QuillController + test('the paste event should be not null on web', () {}); }); } diff --git a/test/widgets/editor_test.dart b/test/widgets/editor_test.dart index 4e3a79f6..a1739e9e 100644 --- a/test/widgets/editor_test.dart +++ b/test/widgets/editor_test.dart @@ -24,10 +24,8 @@ void main() { await tester.pumpWidget( MaterialApp( home: QuillEditor.basic( - // ignore: avoid_redundant_argument_values configurations: QuillEditorConfigurations( controller: controller, - // ignore: avoid_redundant_argument_values ), ), ), @@ -45,16 +43,15 @@ void main() { focusNode: FocusNode(), scrollController: ScrollController(), configurations: QuillEditorConfigurations( - controller: controller, - // ignore: avoid_redundant_argument_values - autoFocus: true, - expands: true, contentInsertionConfiguration: ContentInsertionConfiguration( onContentInserted: (content) { latestUri = content.uri; }, allowedMimeTypes: ['image/gif'], ), + autoFocus: true, + expands: true, + controller: controller, ), ), ), @@ -116,10 +113,8 @@ void main() { home: QuillEditor( focusNode: FocusNode(), scrollController: ScrollController(), - // ignore: avoid_redundant_argument_values configurations: QuillEditorConfigurations( controller: controller, - // ignore: avoid_redundant_argument_values autoFocus: true, expands: true, contextMenuBuilder: customBuilder,