html_editor_enhanced/lib/utils/options.dart
2022-10-30 15:25:33 +07:00

430 lines
16 KiB
Dart

import 'dart:async';
import 'dart:collection';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import 'package:html_editor_enhanced/html_editor.dart';
/// Options that modify the editor and its behavior
class HtmlEditorOptions {
const HtmlEditorOptions({
this.autoAdjustHeight = true,
this.androidUseHybridComposition = true,
this.adjustHeightForKeyboard = true,
this.characterLimit,
this.customOptions = '',
this.darkMode,
this.disabled = false,
this.filePath,
this.hint,
this.initialText,
this.inputType = HtmlInputType.text,
this.mobileContextMenu,
this.mobileLongPressDuration,
this.mobileInitialScripts,
this.webInitialScripts,
this.shouldEnsureVisible = false,
this.spellCheck = false,
});
/// The editor will automatically adjust its height when the keyboard is active
/// to prevent the keyboard overlapping the editor.
///
/// The default value is true. It is recommended to leave this as true because
/// it significantly improves the UX.
final bool adjustHeightForKeyboard;
/// ALlows devs to set hybrid composition off in case they would like to
/// prioritize animation smoothness over text input experience.
///
/// The recommended value is `true`.
final bool androidUseHybridComposition;
/// The editor will automatically adjust its height once the page is loaded to
/// ensure there is no vertical scrolling or empty space. It will only perform
/// the adjustment when the summernote editor is the loaded page.
///
/// It will also disable vertical scrolling on the webview, so scrolling on
/// the webview will actually scroll the rest of the page rather than doing
/// nothing because it is trying to scroll the webview container.
///
/// The default value is true. It is recommended to leave this as true because
/// it significantly improves the UX.
final bool autoAdjustHeight;
/// Adds a character limit to the editor.
///
/// NOTE: ONLY WORKS ON iOS AND WEB PLATFORMS!!
final int? characterLimit;
/// Set custom options for the summernote editor by using their syntax.
///
/// Please ensure your syntax is correct (and add a comma at the end of your
/// string!) otherwise the editor may not load.
final String customOptions;
/// Sets the editor to dark mode. `null` - switches with system, `false` -
/// always light, `true` - always dark.
///
/// The default value is null (switches with system).
final bool? darkMode;
/// Disable the editor immediately after startup. You can re-enable the editor
/// by calling [controller.enable()].
final bool disabled;
/// Specify the file path to your custom html editor code.
///
/// Make sure to set the editor's HTML ID to be 'summernote-2'.
///
/// If you plan to use this on Web, you must add comments in your HTML so the
/// package can insert the relevant JS code to communicate between Dart and JS.
/// See the README for more details on this.
final String? filePath;
/// Sets the Html editor's hint (text displayed when there is no text in the
/// editor).
final String? hint;
/// The initial text that is be supplied to the Html editor.
final String? initialText;
/// Changes the display of the virtual keyboard on mobile devices.
///
/// See [HtmlInputType] for the supported modes.
///
/// The default value is [HtmlInputType.text] (the standard virtual keyboard)
final HtmlInputType inputType;
/// Customize the context menu for selected text on mobile
final ContextMenu? mobileContextMenu;
/// Set the duration until a long-press is recognized.
///
/// The default value is 500ms.
final Duration? mobileLongPressDuration;
/// Initial JS to inject into the editor.
final UnmodifiableListView<UserScript>? mobileInitialScripts;
/// Initial JS to add to the editor. These can be called at any time using
/// [controller.evaluateJavascriptWeb]
final UnmodifiableListView<WebScript>? webInitialScripts;
/// Specifies whether the widget should scroll to reveal the HTML editor when
/// it is focused or the text content is changed.
/// See the README examples for the best way to implement this.
///
/// Note: Your editor *must* be in a Scrollable type widget (e.g. ListView,
/// SingleChildScrollView, etc.) for this to work. Otherwise, nothing will
/// happen.
final bool shouldEnsureVisible;
/// Specify whether or not the editor should spellcheck its contents.
///
/// Default value is false.
final bool spellCheck;
}
/// Options that modify the toolbar and its behavior
class HtmlToolbarOptions {
const HtmlToolbarOptions({
this.audioExtensions,
this.customToolbarButtons = const [],
this.customToolbarInsertionIndices = const [],
this.defaultToolbarButtons = const [
StyleButtons(),
FontSettingButtons(fontSizeUnit: false),
FontButtons(clearAll: false),
ColorButtons(),
ListButtons(listStyles: false),
ParagraphButtons(
textDirection: false, lineHeight: false, caseConverter: false),
InsertButtons(
video: false,
audio: false,
table: false,
hr: false,
otherFile: false),
],
this.otherFileExtensions,
this.imageExtensions,
this.initiallyExpanded = false,
this.linkInsertInterceptor,
this.mediaLinkInsertInterceptor,
this.mediaUploadInterceptor,
this.onButtonPressed,
this.onDropdownChanged,
this.onOtherFileLinkInsert,
this.onOtherFileUpload,
this.toolbarType = ToolbarType.nativeScrollable,
this.toolbarPosition = ToolbarPosition.aboveEditor,
this.videoExtensions,
this.dropdownElevation = 8,
this.dropdownIcon,
this.dropdownIconColor,
this.dropdownIconSize = 24,
this.dropdownItemHeight = kMinInteractiveDimension,
this.dropdownFocusColor,
this.dropdownBackgroundColor,
this.dropdownMenuDirection,
this.dropdownMenuMaxHeight,
this.dropdownBoxDecoration,
this.buttonColor,
this.buttonSelectedColor,
this.buttonFillColor,
this.buttonFocusColor,
this.buttonHighlightColor,
this.buttonHoverColor,
this.buttonSplashColor,
this.buttonBorderColor,
this.buttonSelectedBorderColor,
this.buttonBorderRadius,
this.buttonBorderWidth,
this.renderBorder = false,
this.textStyle,
this.separatorWidget =
const VerticalDivider(indent: 2, endIndent: 2, color: Colors.grey),
this.renderSeparatorWidget = true,
this.toolbarItemHeight = 36,
this.gridViewHorizontalSpacing = 5,
this.gridViewVerticalSpacing = 5,
});
/// Allows you to set the allowed extensions when a user inserts an audio file
///
/// By default any audio extension is allowed.
final List<String>? audioExtensions;
/// Allows you to create your own buttons that are added to the end of the
/// default buttons list
final List<Widget> customToolbarButtons;
/// Allows you to set where each custom toolbar button is inserted into the
/// toolbar buttons.
///
/// Notes: 1) This list should have the same length as the [customToolbarButtons]
///
/// 2) If any indices > [defaultToolbarButtons.length] then the plugin will
/// automatically account for this and insert the buttons at the end of the
/// [defaultToolbarButtons]
///
/// 3) If any indices < 0 then the plugin will automatically account for this
/// and insert the buttons at the beginning of the [defaultToolbarButtons]
final List<int> customToolbarInsertionIndices;
/// Sets which options are visible in the toolbar for the editor.
final List<Toolbar> defaultToolbarButtons;
/// Allows you to set the allowed extensions when a user inserts an image
///
/// By default any image extension is allowed.
final List<String>? imageExtensions;
/// Allows you to set whether the toolbar starts out expanded (in gridview)
/// or contracted (in scrollview).
///
/// By default it starts out contracted.
///
/// This option only works when you have set [toolbarType] to
/// [ToolbarType.nativeExpandable].
final bool initiallyExpanded;
/// Allows you to intercept any links being inserted into the editor. The
/// function passes the display text, the URL itself, and whether the
/// URL should open a new tab.
///
/// Return a bool to tell the plugin if it should continue with its own handler
/// or if you want to handle the link by yourself.
/// (true = continue with internal handler, false = do not use internal handler)
///
/// If no interceptor is set, the plugin uses the internal handler.
final FutureOr<bool> Function(String, String, bool)? linkInsertInterceptor;
/// Allows you to intercept any image/video/audio inserted as a link into the editor.
/// The function passes the URL of the media inserted.
///
/// Return a bool to tell the plugin if it should continue with its own handler
/// or if you want to handle the image/video link by yourself.
/// (true = continue with internal handler, false = do not use internal handler)
///
/// If no interceptor is set, the plugin uses the internal handler.
final FutureOr<bool> Function(String, InsertFileType)?
mediaLinkInsertInterceptor;
/// Allows you to intercept any image/video/audio files being inserted into the editor.
/// The function passes the PlatformFile class, which contains all the file data
/// including name, size, type, Uint8List bytes, etc.
///
/// Return a bool to tell the plugin if it should continue with its own handler
/// or if you want to handle the image/video/audio upload by yourself.
/// (true = continue with internal handler, false = do not use internal handler)
///
/// If no interceptor is set, the plugin uses the internal handler.
final FutureOr<bool> Function(PlatformFile, InsertFileType)?
mediaUploadInterceptor;
/// Allows you to intercept any button press. The function passes the ButtonType
/// enum, which tells you which button was pressed, the current selected status of
/// the button, and a function to reverse the status (in case you decide to handle
/// the button press yourself).
///
/// Note: In some cases, the button is never active (e.g. copy/paste buttons)
/// so null will be returned for both the selected status and the function.
///
/// Return a bool to tell the plugin if it should continue with its own handler
/// or if you want to handle the button press by yourself.
/// (true = continue with internal handler, false = do not use internal handler)
///
/// If no interceptor is set, the plugin uses the internal handler.
final FutureOr<bool> Function(ButtonType, bool?, Function?)? onButtonPressed;
/// Allows you to intercept any dropdown changes. The function passes the
/// DropdownType enum, which tells you which dropdown was changed,
/// the changed value to indicate what the dropdown was changed to, and the
/// function to update the changed value (in case you decide to handle the
/// dropdown change yourself). The function is null in some cases because
/// the dropdown does not update its value.
///
/// Return a bool to tell the plugin if it should continue with its own handler
/// or if you want to handle the dropdown change by yourself.
/// (true = continue with internal handler, false = do not use internal handler)
///
/// If no interceptor is set, the plugin uses the internal handler.
final FutureOr<bool> Function(DropdownType, dynamic, void Function(dynamic)?)?
onDropdownChanged;
/// Called when a link is inserted for a file using the "other file" button.
///
/// The package does not have a built in handler for these files, so you should
/// provide this callback when using the button.
///
/// The function passes the URL of the file inserted.
final void Function(String)? onOtherFileLinkInsert;
/// Called when a file is uploaded using the "other file" button.
///
/// The package does not have a built in handler for these files, so if you use
/// the button you should provide this callback.
///
/// The function passes the PlatformFile class, which contains all the file data
/// including name, size, type, Uint8List bytes, etc.
final void Function(PlatformFile)? onOtherFileUpload;
/// Allows you to set the allowed extensions when a user inserts a file other
/// than image/audio/video
///
/// By default any other extension is allowed.
final List<String>? otherFileExtensions;
/// Controls how the toolbar displays. See [ToolbarType] for more details.
///
/// By default the toolbar is rendered as a scrollable one-line list.
final ToolbarType toolbarType;
/// Controls where the toolbar is positioned. See [ToolbarPosition] for more details.
///
/// By default the toolbar is above the editor.
final ToolbarPosition toolbarPosition;
/// Allows you to set the allowed extensions when a user inserts a video.
///
/// By default any video extension is allowed.
final List<String>? videoExtensions;
/// Styling options for the toolbar:
/// Determines whether a border is rendered around all toolbar widgets
///
/// The default value is false. True is recommended for [ToolbarType.nativeGrid].
final bool renderBorder;
/// Sets the text style for all toolbar widgets
final TextStyle? textStyle;
/// Sets the separator widget between toolbar sections. This widget is only
/// used in [ToolbarType.nativeScrollable].
///
/// The default widget is [VerticalDivider(indent: 2, endIndent: 2, color: Colors.grey)]
final Widget separatorWidget;
/// Determines whether the separator widget is rendered
///
/// The default value is true
final bool renderSeparatorWidget;
/// Sets the height of the toolbar items
///
/// Button width is affected by this parameter, however dropdown widths are
/// not affected. The plugin will maintain a square shape for all buttons.
///
/// The default value is 36
final double toolbarItemHeight;
/// Sets the vertical spacing between rows when using [ToolbarType.nativeGrid]
///
/// The default value is 5
final double gridViewVerticalSpacing;
/// Sets the horizontal spacing between items when using [ToolbarType.nativeGrid]
///
/// The default value is 5
final double gridViewHorizontalSpacing;
/// Styling options that only apply to dropdowns:
/// (See the [DropdownButton] class for more information)
final int dropdownElevation;
final Widget? dropdownIcon;
final Color? dropdownIconColor;
final double dropdownIconSize;
final double dropdownItemHeight;
final Color? dropdownFocusColor;
final Color? dropdownBackgroundColor;
/// Set the menu opening direction for the dropdown. Only useful when using
/// [ToolbarPosition.custom] since the toolbar otherwise automatically
/// determines the correct direction.
final DropdownMenuDirection? dropdownMenuDirection;
final double? dropdownMenuMaxHeight;
final BoxDecoration? dropdownBoxDecoration;
/// Styling options that only apply to the buttons:
/// (See the [ToggleButtons] class for more information)
final Color? buttonColor;
final Color? buttonSelectedColor;
final Color? buttonFillColor;
final Color? buttonFocusColor;
final Color? buttonHighlightColor;
final Color? buttonHoverColor;
final Color? buttonSplashColor;
final Color? buttonBorderColor;
final Color? buttonSelectedBorderColor;
final BorderRadius? buttonBorderRadius;
final double? buttonBorderWidth;
}
/// Other options such as the height of the widget and the decoration surrounding it
class OtherOptions {
const OtherOptions({
this.decoration = const BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(4)),
border:
Border.fromBorderSide(BorderSide(color: Color(0xffececec), width: 1)),
),
this.height = 400,
});
/// The BoxDecoration to use around the Html editor. By default, the widget
/// uses a thin, dark, rounded rectangle border around the widget.
final BoxDecoration decoration;
/// Sets the height of the Html editor widget. This takes the toolbar into
/// account (i.e. this sets the height of the entire widget rather than the
/// editor space)
///
/// The default value is 400.
final double height;
}