-
-
Notifications
You must be signed in to change notification settings - Fork 536
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add quill.js based TextEditor widget (#2875)
- Loading branch information
1 parent
afc8d1e
commit d13e5f6
Showing
10 changed files
with
411 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,181 @@ | ||
{ | ||
"cells": [ | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"import panel as pn\n", | ||
"import param\n", | ||
"\n", | ||
"pn.extension('texteditor')" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"The ``TextEditor`` widget allows provides a WYSIWYG (what-you-see-is-what-you-get) rich text editor into a Panel application which outputs HTML. The editor is built on top of the [Quill.js](https://quilljs.com/) library.\n", | ||
"\n", | ||
"#### Parameters:\n", | ||
"\n", | ||
"For layout and styling related parameters see the [customization user guide](../../user_guide/Customization.ipynb).\n", | ||
"\n", | ||
"* **``disabled``** (boolean): Whether the editor is disabled\n", | ||
"* **``mode``** (str): Whether to display a `'toolbar'` or a `'bubble'` menu on highlight.\n", | ||
"* **``placeholder``** (str): Placeholder output to render when the editor is empty.\n", | ||
"* **``toolbar``** (boolean or list): Toolbar configuration either as a boolean toggle or a configuration specified as a list.\n", | ||
"* **``value``** (str): The current HTML output of the widget\n", | ||
"\n", | ||
"___" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"To construct a `TextEditor` editor widget we must declare it explicitly and may provide a `placeholder` as pure text or a `value` as HTML:" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"wysiwyg = pn.widgets.TextEditor(placeholder='Enter some text')\n", | ||
"wysiwyg" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"The current state of the editor output is reflected on the `value` parameter:" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"wysiwyg.value" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"The `value` may also be set:" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"wysiwyg.value = '<h1>A title</h1>'" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"### Toolbar\n", | ||
"\n", | ||
"The toolbar of the editor can be configured in a variety of ways, the simplest of which is simply toggling it on and off by setting `toolbar=True/False`. Beyond that we can provide the formatting options to display in a number of configurations which are explained in the [quill.js documentation](https://quilljs.com/docs/modules/toolbar/#container). The examples below" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"pn.config.sizing_mode = 'stretch_width'\n", | ||
"\n", | ||
"# Flat list of options\n", | ||
"flat = pn.widgets.TextEditor(toolbar=['bold', 'italic', 'underline'])\n", | ||
"\n", | ||
"# Grouped options\n", | ||
"grouped = pn.widgets.TextEditor(toolbar=[['bold', 'italic'], ['link', 'image']])\n", | ||
"\n", | ||
"# Dropdown of options\n", | ||
"dropdown = pn.widgets.TextEditor(toolbar=[{'size': [ 'small', False, 'large', 'huge' ]}])\n", | ||
"\n", | ||
"# Full configuration\n", | ||
"full_config = pn.widgets.TextEditor(toolbar=[\n", | ||
" ['bold', 'italic', 'underline', 'strike'], # toggled buttons\n", | ||
" ['blockquote', 'code-block'],\n", | ||
"\n", | ||
" [{ 'header': 1 }, { 'header': 2 }], # custom button values\n", | ||
" [{ 'list': 'ordered'}, { 'list': 'bullet' }],\n", | ||
" [{ 'script': 'sub'}, { 'script': 'super' }], # superscript/subscript\n", | ||
" [{ 'indent': '-1'}, { 'indent': '+1' }], # outdent/indent\n", | ||
" [{ 'direction': 'rtl' }], # text direction\n", | ||
"\n", | ||
" [{ 'size': ['small', False, 'large', 'huge'] }], # custom dropdown\n", | ||
" [{ 'header': [1, 2, 3, 4, 5, 6, False] }],\n", | ||
"\n", | ||
" [{ 'color': [] }, { 'background': [] }], # dropdown with defaults from theme\n", | ||
" [{ 'font': [] }],\n", | ||
" [{ 'align': [] }],\n", | ||
"\n", | ||
" ['clean'] # remove formatting button\n", | ||
"])\n", | ||
"\n", | ||
"pn.Column(\n", | ||
" pn.Row(flat, grouped, dropdown),\n", | ||
" full_config\n", | ||
")" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"Instead of a toolbar we can also switch to `'bubble'` mode which displays a hover menu when highlighting the text:" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"pn.widgets.TextEditor(mode='bubble', value='Highlight me to edit formatting', margin=(40, 0, 0, 0), height=200, width=400)" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"### Controls\n", | ||
"\n", | ||
"The `TextEditor` widget exposes a number of options which can be changed from both Python and Javascript. Try out the effect of these parameters interactively:" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"editor = pn.widgets.TextEditor(placeholder='Enter some text')\n", | ||
"\n", | ||
"pn.Row(editor.controls(jslink=True, sizing_mode='fixed'), editor)" | ||
] | ||
} | ||
], | ||
"metadata": { | ||
"language_info": { | ||
"name": "python", | ||
"pygments_lexer": "ipython3" | ||
} | ||
}, | ||
"nbformat": 4, | ||
"nbformat_minor": 4 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
from bokeh.core.properties import Any, Bool, Enum, Either, List, String | ||
from bokeh.models import HTMLBox | ||
|
||
from ..io.resources import bundled_files | ||
from ..util import classproperty | ||
|
||
|
||
class QuillInput(HTMLBox): | ||
""" | ||
WYSIWYG text editor based on Quill.js | ||
""" | ||
|
||
__css_raw__ = [ | ||
'https://cdn.quilljs.com/1.3.6/quill.bubble.css', | ||
'https://cdn.quilljs.com/1.3.6/quill.snow.css' | ||
] | ||
|
||
__javascript_raw__ = [ | ||
'https://cdn.quilljs.com/1.3.6/quill.js', | ||
] | ||
|
||
@classproperty | ||
def __javascript__(cls): | ||
return bundled_files(cls) | ||
|
||
@classproperty | ||
def __css__(cls): | ||
return bundled_files(cls, 'css') | ||
|
||
@classproperty | ||
def __js_skip__(cls): | ||
return {'Quill': cls.__javascript__} | ||
|
||
__js_require__ = { | ||
'paths': { | ||
'Quill': 'https://cdn.quilljs.com/1.3.6/quill', | ||
}, | ||
'exports': { | ||
'Quill': 'Quill' | ||
} | ||
} | ||
|
||
mode = Enum("bubble", "toolbar", default='toolbar') | ||
|
||
placeholder = String() | ||
|
||
readonly = Bool(False) | ||
|
||
text = String() | ||
|
||
toolbar = Either(List(Any), Bool) |
Oops, something went wrong.