From a41a1bb51363832cd419a50b24815fba484b39ae Mon Sep 17 00:00:00 2001 From: kmh Date: Sat, 29 Jul 2023 01:20:15 +0800 Subject: [PATCH] Allow for custom regular expression validation of links. (#1048) --- .../lib/embeds/toolbar/image_button.dart | 7 +++++- .../lib/embeds/toolbar/image_video_utils.dart | 13 ++++++++--- .../lib/embeds/toolbar/video_button.dart | 4 ++++ .../lib/flutter_quill_extensions.dart | 6 ++++- lib/src/widgets/toolbar.dart | 5 ++++ .../widgets/toolbar/link_style_button.dart | 23 +++++++++++++++---- 6 files changed, 48 insertions(+), 10 deletions(-) diff --git a/flutter_quill_extensions/lib/embeds/toolbar/image_button.dart b/flutter_quill_extensions/lib/embeds/toolbar/image_button.dart index d05d851d..dffbb387 100644 --- a/flutter_quill_extensions/lib/embeds/toolbar/image_button.dart +++ b/flutter_quill_extensions/lib/embeds/toolbar/image_button.dart @@ -18,6 +18,7 @@ class ImageButton extends StatelessWidget { this.iconTheme, this.dialogTheme, this.tooltip, + this.linkRegExp, Key? key, }) : super(key: key); @@ -40,6 +41,7 @@ class ImageButton extends StatelessWidget { final QuillDialogTheme? dialogTheme; final String? tooltip; + final RegExp? linkRegExp; @override Widget build(BuildContext context) { @@ -90,7 +92,10 @@ class ImageButton extends StatelessWidget { void _typeLink(BuildContext context) { showDialog( context: context, - builder: (_) => LinkDialog(dialogTheme: dialogTheme), + builder: (_) => LinkDialog( + dialogTheme: dialogTheme, + linkRegExp: linkRegExp, + ), ).then(_linkSubmitted); } diff --git a/flutter_quill_extensions/lib/embeds/toolbar/image_video_utils.dart b/flutter_quill_extensions/lib/embeds/toolbar/image_video_utils.dart index aef7c9e7..77b1eb60 100644 --- a/flutter_quill_extensions/lib/embeds/toolbar/image_video_utils.dart +++ b/flutter_quill_extensions/lib/embeds/toolbar/image_video_utils.dart @@ -10,10 +10,16 @@ import 'package:image_picker/image_picker.dart'; import '../embed_types.dart'; class LinkDialog extends StatefulWidget { - const LinkDialog({this.dialogTheme, this.link, Key? key}) : super(key: key); + const LinkDialog({ + this.dialogTheme, + this.link, + this.linkRegExp, + Key? key, + }) : super(key: key); final QuillDialogTheme? dialogTheme; final String? link; + final RegExp? linkRegExp; @override LinkDialogState createState() => LinkDialogState(); @@ -22,12 +28,14 @@ class LinkDialog extends StatefulWidget { class LinkDialogState extends State { late String _link; late TextEditingController _controller; + late RegExp _linkRegExp; @override void initState() { super.initState(); _link = widget.link ?? ''; _controller = TextEditingController(text: _link); + _linkRegExp = widget.linkRegExp ?? AutoFormatMultipleLinksRule.linkRegExp; } @override @@ -48,8 +56,7 @@ class LinkDialogState extends State { ), actions: [ TextButton( - onPressed: _link.isNotEmpty && - AutoFormatMultipleLinksRule.linkRegExp.hasMatch(_link) + onPressed: _link.isNotEmpty && _linkRegExp.hasMatch(_link) ? _applyLink : null, child: Text( diff --git a/flutter_quill_extensions/lib/embeds/toolbar/video_button.dart b/flutter_quill_extensions/lib/embeds/toolbar/video_button.dart index e5c1ab73..eb31f28d 100644 --- a/flutter_quill_extensions/lib/embeds/toolbar/video_button.dart +++ b/flutter_quill_extensions/lib/embeds/toolbar/video_button.dart @@ -18,6 +18,7 @@ class VideoButton extends StatelessWidget { this.iconTheme, this.dialogTheme, this.tooltip, + this.linkRegExp, Key? key, }) : super(key: key); @@ -39,8 +40,11 @@ class VideoButton extends StatelessWidget { final QuillIconTheme? iconTheme; final QuillDialogTheme? dialogTheme; + final String? tooltip; + final RegExp? linkRegExp; + @override Widget build(BuildContext context) { final theme = Theme.of(context); diff --git a/flutter_quill_extensions/lib/flutter_quill_extensions.dart b/flutter_quill_extensions/lib/flutter_quill_extensions.dart index fdbc54b2..a1945ab7 100644 --- a/flutter_quill_extensions/lib/flutter_quill_extensions.dart +++ b/flutter_quill_extensions/lib/flutter_quill_extensions.dart @@ -49,6 +49,8 @@ class FlutterQuillEmbeds { FilePickImpl? filePickImpl, WebImagePickImpl? webImagePickImpl, WebVideoPickImpl? webVideoPickImpl, + RegExp? imageLinkRegExp, + RegExp? videoLinkRegExp, }) => [ if (showImageButton) @@ -63,6 +65,7 @@ class FlutterQuillEmbeds { mediaPickSettingSelector: mediaPickSettingSelector, iconTheme: iconTheme, dialogTheme: dialogTheme, + linkRegExp: imageLinkRegExp, ), if (showVideoButton) (controller, toolbarIconSize, iconTheme, dialogTheme) => VideoButton( @@ -76,7 +79,8 @@ class FlutterQuillEmbeds { mediaPickSettingSelector: mediaPickSettingSelector, iconTheme: iconTheme, dialogTheme: dialogTheme, - ), + linkRegExp: videoLinkRegExp, + ), if ((onImagePickCallback != null || onVideoPickCallback != null) && showCameraButton) (controller, toolbarIconSize, iconTheme, dialogTheme) => CameraButton( diff --git a/lib/src/widgets/toolbar.dart b/lib/src/widgets/toolbar.dart index fea5b420..5d103590 100644 --- a/lib/src/widgets/toolbar.dart +++ b/lib/src/widgets/toolbar.dart @@ -154,6 +154,10 @@ class QuillToolbar extends StatelessWidget implements PreferredSizeWidget { /// The space occupied by toolbar divider double? sectionDividerSpace, + + /// Validate the legitimacy of hyperlinks + RegExp? linkRegExp, + Key? key, }) { final isButtonGroupShown = [ @@ -551,6 +555,7 @@ class QuillToolbar extends StatelessWidget implements PreferredSizeWidget { iconTheme: iconTheme, dialogTheme: dialogTheme, afterButtonPressed: afterButtonPressed, + linkRegExp: linkRegExp, ), if (showSearchButton) SearchButton( diff --git a/lib/src/widgets/toolbar/link_style_button.dart b/lib/src/widgets/toolbar/link_style_button.dart index a0865941..f3b20ccb 100644 --- a/lib/src/widgets/toolbar/link_style_button.dart +++ b/lib/src/widgets/toolbar/link_style_button.dart @@ -18,6 +18,7 @@ class LinkStyleButton extends StatefulWidget { this.dialogTheme, this.afterButtonPressed, this.tooltip, + this.linkRegExp, Key? key, }) : super(key: key); @@ -28,6 +29,7 @@ class LinkStyleButton extends StatefulWidget { final QuillDialogTheme? dialogTheme; final VoidCallback? afterButtonPressed; final String? tooltip; + final RegExp? linkRegExp; @override _LinkStyleButtonState createState() => _LinkStyleButtonState(); @@ -108,7 +110,11 @@ class _LinkStyleButtonState extends State { text ??= len == 0 ? '' : widget.controller.document.getPlainText(index, len); return _LinkDialog( - dialogTheme: widget.dialogTheme, link: link, text: text); + dialogTheme: widget.dialogTheme, + link: link, + text: text, + linkRegExp: widget.linkRegExp, + ); }, ).then( (value) { @@ -143,12 +149,18 @@ class _LinkStyleButtonState extends State { } class _LinkDialog extends StatefulWidget { - const _LinkDialog({this.dialogTheme, this.link, this.text, Key? key}) - : super(key: key); + const _LinkDialog({ + this.dialogTheme, + this.link, + this.text, + this.linkRegExp, + Key? key, + }) : super(key: key); final QuillDialogTheme? dialogTheme; final String? link; final String? text; + final RegExp? linkRegExp; @override _LinkDialogState createState() => _LinkDialogState(); @@ -157,6 +169,7 @@ class _LinkDialog extends StatefulWidget { class _LinkDialogState extends State<_LinkDialog> { late String _link; late String _text; + late RegExp linkRegExp; late TextEditingController _linkController; late TextEditingController _textController; @@ -165,6 +178,7 @@ class _LinkDialogState extends State<_LinkDialog> { super.initState(); _link = widget.link ?? ''; _text = widget.text ?? ''; + linkRegExp = widget.linkRegExp ?? AutoFormatMultipleLinksRule.linkRegExp; _linkController = TextEditingController(text: _link); _textController = TextEditingController(text: _text); } @@ -218,8 +232,7 @@ class _LinkDialogState extends State<_LinkDialog> { if (_text.isEmpty || _link.isEmpty) { return false; } - - if (!AutoFormatMultipleLinksRule.linkRegExp.hasMatch(_link)) { + if (!linkRegExp.hasMatch(_link)) { return false; }