Add Editor component (#1851)
This commit is contained in:
parent
7df3f2f621
commit
0a196693a3
@ -100,6 +100,7 @@ editable = Editable.create
|
||||
editable_input = EditableInput.create
|
||||
editable_preview = EditablePreview.create
|
||||
editable_textarea = EditableTextarea.create
|
||||
editor = Editor.create
|
||||
form = Form.create
|
||||
form_control = FormControl.create
|
||||
form_error_message = FormErrorMessage.create
|
||||
|
@ -12,6 +12,7 @@ from .date_picker import DatePicker
|
||||
from .date_time_picker import DateTimePicker
|
||||
from .debounce import DebounceInput
|
||||
from .editable import Editable, EditableInput, EditablePreview, EditableTextarea
|
||||
from .editor import Editor, EditorButtonList, EditorOptions
|
||||
from .email import Email
|
||||
from .form import Form, FormControl, FormErrorMessage, FormHelperText, FormLabel
|
||||
from .iconbutton import IconButton
|
||||
|
206
reflex/components/forms/editor.py
Normal file
206
reflex/components/forms/editor.py
Normal file
@ -0,0 +1,206 @@
|
||||
"""A Rich Text Editor based on SunEditor."""
|
||||
from __future__ import annotations
|
||||
|
||||
import enum
|
||||
from typing import Any, Dict, List, Optional, Union
|
||||
|
||||
from reflex.base import Base
|
||||
from reflex.components.component import Component, NoSSRComponent
|
||||
from reflex.constants import EventTriggers
|
||||
from reflex.utils.format import to_camel_case
|
||||
from reflex.vars import ImportVar, Var
|
||||
|
||||
|
||||
class EditorButtonList(list, enum.Enum):
|
||||
"""List enum that provides three predefined button lists."""
|
||||
|
||||
BASIC = [
|
||||
["font", "fontSize"],
|
||||
["fontColor"],
|
||||
["horizontalRule"],
|
||||
["link", "image"],
|
||||
]
|
||||
FORMATTING = [
|
||||
["undo", "redo"],
|
||||
["bold", "underline", "italic", "strike", "subscript", "superscript"],
|
||||
["removeFormat"],
|
||||
["outdent", "indent"],
|
||||
["fullScreen", "showBlocks", "codeView"],
|
||||
["preview", "print"],
|
||||
]
|
||||
COMPLEX = [
|
||||
["undo", "redo"],
|
||||
["font", "fontSize", "formatBlock"],
|
||||
["bold", "underline", "italic", "strike", "subscript", "superscript"],
|
||||
["removeFormat"],
|
||||
"/",
|
||||
["fontColor", "hiliteColor"],
|
||||
["outdent", "indent"],
|
||||
["align", "horizontalRule", "list", "table"],
|
||||
["link", "image", "video"],
|
||||
["fullScreen", "showBlocks", "codeView"],
|
||||
["preview", "print"],
|
||||
["save", "template"],
|
||||
]
|
||||
|
||||
|
||||
class EditorOptions(Base):
|
||||
"""Some of the additional options to configure the Editor.
|
||||
Complete list of options found here:
|
||||
https://github.com/JiHong88/SunEditor/blob/master/README.md#options.
|
||||
"""
|
||||
|
||||
# Specifies default tag name of the editor.
|
||||
# default: 'p' {String}
|
||||
default_tag: Optional[str] = None
|
||||
|
||||
# The mode of the editor ('classic', 'inline', 'balloon', 'balloon-always').
|
||||
# default: 'classic' {String}
|
||||
mode: Optional[str] = None
|
||||
|
||||
# If true, the editor is set to RTL(Right To Left) mode.
|
||||
# default: false {Boolean}
|
||||
rtl: Optional[bool] = None
|
||||
|
||||
# List of buttons to use in the toolbar.
|
||||
button_list: Optional[List[Union[List[str], str]]]
|
||||
|
||||
|
||||
class Editor(NoSSRComponent):
|
||||
"""A Rich Text Editor component based on SunEditor.
|
||||
Not every JS prop is listed here (some are not easily usable from python),
|
||||
refer to the library docs for a complete list.
|
||||
"""
|
||||
|
||||
library = "suneditor-react"
|
||||
|
||||
tag = "SunEditor"
|
||||
|
||||
is_default = True
|
||||
|
||||
lib_dependencies: List[str] = ["suneditor"]
|
||||
|
||||
# Language of the editor.
|
||||
# Alternatively to a string, a dict of your language can be passed to this prop.
|
||||
# Please refer to the library docs for this.
|
||||
# options: "en" | "da" | "de" | "es" | "fr" | "ja" | "ko" | "pt_br" |
|
||||
# "ru" | "zh_cn" | "ro" | "pl" | "ckb" | "lv" | "se" | "ua" | "he" | "it"
|
||||
# default : "en"
|
||||
lang: Var[Union[str, dict]]
|
||||
|
||||
# This is used to set the HTML form name of the editor.
|
||||
# This means on HTML form submission,
|
||||
# it will be submitted together with contents of the editor by the name provided.
|
||||
name: Var[str]
|
||||
|
||||
# Sets the default value of the editor.
|
||||
# This is useful if you don't want the on_change method to be called on render.
|
||||
# If you want the on_change method to be called on render please use the set_contents prop
|
||||
default_value: Var[str]
|
||||
|
||||
# Sets the width of the editor.
|
||||
# px and percentage values are accepted, eg width="100%" or width="500px"
|
||||
# default: 100%
|
||||
width: Var[str]
|
||||
|
||||
# Sets the height of the editor.
|
||||
# px and percentage values are accepted, eg height="100%" or height="100px"
|
||||
height: Var[str]
|
||||
|
||||
# Sets the placeholder of the editor.
|
||||
placeholder: Var[str]
|
||||
|
||||
# Should the editor receive focus when initialized?
|
||||
auto_focus: Var[bool]
|
||||
|
||||
# Pass an EditorOptions instance to modify the behaviour of Editor even more.
|
||||
set_options: Var[Dict]
|
||||
|
||||
# Whether all SunEditor plugins should be loaded.
|
||||
# default: True
|
||||
set_all_plugins: Var[bool]
|
||||
|
||||
# Set the content of the editor.
|
||||
# Note: To set the initial contents of the editor
|
||||
# without calling the on_change event,
|
||||
# please use the default_value prop.
|
||||
# set_contents is used to set the contents of the editor programmatically.
|
||||
# You must be aware that, when the set_contents's prop changes,
|
||||
# the on_change event is triggered.
|
||||
set_contents: Var[str]
|
||||
|
||||
# Append editor content
|
||||
append_contents: Var[str]
|
||||
|
||||
# Sets the default style of the editor's edit area
|
||||
set_default_style: Var[str]
|
||||
|
||||
# Disable the editor
|
||||
# default: False
|
||||
disable: Var[bool]
|
||||
|
||||
# Hide the editor
|
||||
# default: False
|
||||
hide: Var[bool]
|
||||
|
||||
# Hide the editor toolbar
|
||||
# default: False
|
||||
hide_toolbar: Var[bool]
|
||||
|
||||
# Disable the editor toolbar
|
||||
# default: False
|
||||
disable_toolbar: Var[bool]
|
||||
|
||||
def _get_imports(self):
|
||||
imports = super()._get_imports()
|
||||
imports[""] = {
|
||||
ImportVar(tag="suneditor/dist/css/suneditor.min.css", install=False)
|
||||
}
|
||||
return imports
|
||||
|
||||
def get_event_triggers(self) -> Dict[str, Any]:
|
||||
"""Get the event triggers that pass the component's value to the handler.
|
||||
|
||||
Returns:
|
||||
A dict mapping the event trigger to the var that is passed to the handler.
|
||||
"""
|
||||
return {
|
||||
**super().get_event_triggers(),
|
||||
EventTriggers.ON_CHANGE: lambda content: [content],
|
||||
"on_input": lambda _e: [_e],
|
||||
EventTriggers.ON_BLUR: lambda _e, content: [content],
|
||||
"on_load": lambda reload: [reload],
|
||||
"on_resize_editor": lambda height, prev_height: [height, prev_height],
|
||||
"on_copy": lambda _e, clipboard_data: [clipboard_data],
|
||||
"on_cut": lambda _e, clipboard_data: [clipboard_data],
|
||||
"on_paste": lambda _e, clean_data, max_char_count: [
|
||||
clean_data,
|
||||
max_char_count,
|
||||
],
|
||||
"toggle_code_view": lambda is_code_view: [is_code_view],
|
||||
"toggle_full_screen": lambda is_full_screen: [is_full_screen],
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def create(cls, set_options: Optional[EditorOptions] = None, **props) -> Component:
|
||||
"""Create an instance of Editor. No children allowed.
|
||||
|
||||
Args:
|
||||
set_options(Optional[EditorOptions]): Configuration object to further configure the instance.
|
||||
**props: Any properties to be passed to the Editor
|
||||
|
||||
Returns:
|
||||
An Editor instance.
|
||||
|
||||
Raises:
|
||||
ValueError: If set_options is a state Var.
|
||||
"""
|
||||
if set_options is not None:
|
||||
if isinstance(set_options, Var):
|
||||
raise ValueError("EditorOptions cannot be a state Var")
|
||||
props["set_options"] = {
|
||||
to_camel_case(k): v
|
||||
for k, v in set_options.dict().items()
|
||||
if v is not None
|
||||
}
|
||||
return super().create(*[], **props)
|
33
reflex/components/forms/editor.pyi
Normal file
33
reflex/components/forms/editor.pyi
Normal file
@ -0,0 +1,33 @@
|
||||
"""Stub file for editor.py"""
|
||||
# ------------------- DO NOT EDIT ----------------------
|
||||
# This file was generated by `scripts/pyi_generator.py`!
|
||||
# ------------------------------------------------------
|
||||
|
||||
import enum
|
||||
from typing import Any, Dict, List, Optional, Union, overload
|
||||
from reflex.base import Base
|
||||
from reflex.components.component import Component
|
||||
from reflex.components.component import NoSSRComponent
|
||||
from reflex.vars import Var, BaseVar, ComputedVar
|
||||
from reflex.event import EventHandler, EventChain, EventSpec
|
||||
|
||||
class EditorButtonList(list, enum.Enum): ...
|
||||
class EditorOptions(Base): ...
|
||||
|
||||
class Editor(NoSSRComponent):
|
||||
@overload
|
||||
@classmethod
|
||||
def create(cls, *children, lib_dependencies: Optional[List[str]] = None, lang: Optional[Union[Var[Union[str, dict]], Union[str, dict]]] = None, name: Optional[Union[Var[str], str]] = None, default_value: Optional[Union[Var[str], str]] = None, width: Optional[Union[Var[str], str]] = None, height: Optional[Union[Var[str], str]] = None, placeholder: Optional[Union[Var[str], str]] = None, auto_focus: Optional[Union[Var[bool], bool]] = None, set_options: Optional[Union[Var[Dict], Dict]] = None, set_all_plugins: Optional[Union[Var[bool], bool]] = None, set_contents: Optional[Union[Var[str], str]] = None, append_contents: Optional[Union[Var[str], str]] = None, set_default_style: Optional[Union[Var[str], str]] = None, disable: Optional[Union[Var[bool], bool]] = None, hide: Optional[Union[Var[bool], bool]] = None, hide_toolbar: Optional[Union[Var[bool], bool]] = None, disable_toolbar: Optional[Union[Var[bool], bool]] = None, on_blur: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_change: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_click: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_context_menu: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_copy: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_cut: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_double_click: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_focus: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_input: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_load: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mount: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_down: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_enter: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_leave: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_move: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_out: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_over: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_mouse_up: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_paste: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_resize_editor: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_scroll: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, on_unmount: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, toggle_code_view: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, toggle_full_screen: Optional[Union[EventHandler, EventSpec, List, function, BaseVar]] = None, **props) -> "Editor": # type: ignore
|
||||
"""Create an instance of Editor. No children allowed.
|
||||
|
||||
Args:
|
||||
set_options(Optional[EditorOptions]): Configuration object to further configure the instance.
|
||||
**props: Any properties to be passed to the Editor
|
||||
|
||||
Returns:
|
||||
An Editor instance.
|
||||
|
||||
Raises:
|
||||
ValueError: If set_options is a state Var.
|
||||
"""
|
||||
...
|
@ -100,6 +100,9 @@ def _issubclass(cls: GenericType, cls_check: GenericType) -> bool:
|
||||
|
||||
Returns:
|
||||
Whether the class is a subclass of the other class.
|
||||
|
||||
Raises:
|
||||
TypeError: If the base class is not valid for issubclass.
|
||||
"""
|
||||
# Special check for Any.
|
||||
if cls_check == Any:
|
||||
@ -116,7 +119,12 @@ def _issubclass(cls: GenericType, cls_check: GenericType) -> bool:
|
||||
return False
|
||||
|
||||
# Check if the types match.
|
||||
return cls_check_base == Any or issubclass(cls_base, cls_check_base)
|
||||
try:
|
||||
return cls_check_base == Any or issubclass(cls_base, cls_check_base)
|
||||
except TypeError as te:
|
||||
# These errors typically arise from bad annotations and are hard to
|
||||
# debug without knowing the type that we tried to compare.
|
||||
raise TypeError(f"Invalid type for issubclass: {cls_base}") from te
|
||||
|
||||
|
||||
def _isinstance(obj: Any, cls: GenericType) -> bool:
|
||||
|
Loading…
Reference in New Issue
Block a user