Configuring Widgets
Using the editingConfig
option in your package.json
, you can change how WebWriter treats your widget.
editingConfig["./widgets/my-widget"]
Extra editing preferences (matching NodeSpec
) to be passed along to the editor. Does not support the functional properties (toDOM
, parseDOM
, toDebugString
or leafText
). Supports the additional parts
, cssVariables
, and slots
properties.
editingConfig[...].group
The group or space-separated groups to which this widget belongs. Can be one of the content categories of "heading"
, "sectioning"
, "interactive"
, "embedded"
, "phrasing"
, "palpable"
, "formassociated"
, "listed"
, "labelable"
, "submittable"
, "resettable"
, or "scriptsupporting"
, or any custom group name. Always implicitly includes the group widget
. If not set, defaults to flow widget
.
{
// ...
"editingConfig": {
// ...
"./widgets/awesome-widget": {
// ...
"group": "flow simulation" // we add a custom group name which other widgets may also use in their group or content
}
}
}
editingConfig[...].inline
Should be set to true
for inline widgets. Defaults to false
. Warning: Currently poorly supported, may cause issues.
{
// ...
"editingConfig": {
// ...
"./widgets/awesome-widget": {
// ...
"inline": true
}
}
}
editingConfig[...].selectable
Controls whether the widget can be selected as a node selection. Defaults to true
.
{
// ...
"editingConfig": {
// ...
"./widgets/awesome-widget": {
// ...
"selectable": false
}
}
}
editingConfig[...].draggable
Determines whether the widget can be dragged without being selected. Defaults to false
.
{
// ...
"editingConfig": {
// ...
"./widgets/awesome-widget": {
// ...
"draggable": true
}
}
}
editingConfig[...].code
Can be used to indicate that the widget contains code, which causes some commands to behave differently. Defaults to false
.
{
// ...
"editingConfig": {
// ...
"./widgets/awesome-widget": {
// ...
"code": true
}
}
}
editingConfig[...].whitespace
Controls the way whitespace in the widget is parsed. "normal"
causes the DOM parser to collapse whitespace in normal mode, and normalize it (replacing newlines and such with spaces) otherwise. “pre” causes the parser to preserve spaces inside the widget. When this option isn’t given, but code is true
, whitespace will default to "pre"
. Note that this option doesn’t influence the way the widget is rendered—that should be handled by styling. Defaults to "normal"
.
{
// ...
"editingConfig": {
// ...
"./widgets/awesome-widget": {
// ...
"whitespace": "pre"
}
}
}
editingConfig[...].definingAsContext
Determines whether the widget is considered an important parent node during replace operations (such as paste). Non-defining widgets get dropped when their entire content is replaced, whereas defining nodes persist and wrap the inserted content. Defaults to false
.
{
// ...
"editingConfig": {
// ...
"./widgets/awesome-widget": {
// ...
"definingAsContext": true
}
}
}
editingConfig[...].definingForContent
In inserted content the defining parents of the content are preserved when possible. Typically, non-default-paragraph textblock types, and possibly list items, are marked as defining. Defaults to false
.
{
// ...
"editingConfig": {
// ...
"./widgets/awesome-widget": {
// ...
"definingForContent": true
}
}
}
editingConfig[...].defining
When enabled, enables both definingAsContext
and definingForContent
. Defaults to false
.
{
// ...
"editingConfig": {
// ...
"./widgets/awesome-widget": {
// ...
"defining": true
}
}
}
editingConfig[...].isolating
When enabled, the sides of the widget count as boundaries that regular editing operations, like backspacing or lifting, won’t cross. Defaults to false
.
{
// ...
"editingConfig": {
// ...
"./widgets/awesome-widget": {
// ...
"isolating": true
}
}
}
editingConfig[...].content
The allowed content of the widget. Should be a content expression (see below). To allow zero or more flow elements, use the value "flow*"
(Flow content is a broad category for most elements allowed in the <body>
). To define the default (unnamed) slot, use the empty string ""
as the key. Defaults to undefined
, allowing no content.
{
// ...
"editingConfig": {
// ...
"./widgets/awesome-widget": {
// ...
"content": "(h1 p)* | flow*"
}
}
}
Content expressions
The value of the content
field should be a ProseMirror content expression. These use a similar syntax to regular expressions:
[ELEMENT NAME]
means a single element, e.g.p
is a content expression meaning the widget must have exactly one child element which is<p>
.[EXPR]?
,[EXPR]*
,[EXPR]+
,[EXPR]{min, max}
are quantifier operators.?
means zero or one,*
means zero or more,+
means one or more, and{min, max}
means betweenmin
and (including)max
. If only one number is provided ([EXPR]{n}
), it means exactlyn
. For example:p*
means zero or more<p>
elements, andp{0, 3}
means between 0 and (including) 3<p>
elements.[EXPR1] [EXPR2] ...
and[EXPR1] | [EXPR2] | ...
are connecting operators. The space (p div
means first a<p>
, then a<div>
). The pipe (|
) means “either the first or the second, third, … expression”. The order of expressions is significant: When WebWriter auto-fills required content (e.g.(p | div)+
), it would use the first expression (in the example:p
-><p>
).([EXPR])
Expressions can be grouped with parantheses()
to change precedence. This can be used to apply quantifiers to connected expressions, for example(h1 p)*
would mean zero or more pairs of<h1>
and<p>
elements.- The tag names of all built-in elements are supported (
p
,div
, etc.), excluding some of the phrasing elements (a
,abbr
,b
,bdi
,bdo
,cite
,code
,data
,del
,dfn
,em
,i
,ins
,kbd
,q
,ruby
,s
,samp
,small
,span
,strong
,sub
,sup
,time
,u
,var
). - Additionally, all content groups except
phrasing
andmetadata
are supported (flow
,interactive
,embedded
,heading
,sectioning
,formassociated
,listed
,labelable
,submittable
,resettable
,scriptsupporting
). A special content categorywidget
which includes all widgets is also supported. Using groups in content expressions is often useful to allow whole categories of content. For example, the expressionflow*
is useful to allow essentially any content (since all elements that can go directly into the<body>
are flow elements).
editingConfig[...].marks
The marks that are allowed inside of the widget. Should be a space-separated list referring to mark names or groups, ”_” to explicitly allow all marks, or "" to disallow marks. Possible mark names are: "abbr"
, "b"
, "bdi"
, "bdo"
, "cite"
, "code"
, "data"
, "dfn"
, "em"
, "i"
, "kbd"
, "mark"
, "q"
, "ruby"
, "s"
, "samp"
, "small"
, "span"
, "sub"
, "sup"
, "u"
, "var"
. If an object, each key should be the name of slot, and each value a space-separated list of mark names. Defaults to ”_”, allowing all marks.
{
// ...
"editingConfig": {
// ...
"./widgets/awesome-widget": {
// ...
"marks": "i b u s"
}
}
}
editingConfig[...].parts
The CSS parts defined in the widget. Should be an object where each key is the name of the part, and the value is a config object (currently empty, available for future compatibility).
{
// ...
"editingConfig": {
// ...
"./widgets/awesome-widget": {
// ...
"parts": {
"sidepanel": {},
"main": {}
}
}
}
}
editingConfig[...].customProperties
The CSS custom properties defined in the widget. Should be an object where each key is the name of the CSS custom property (including the ”—” prefix), and the value is a CSS value definition syntax expression.
{
// ...
"editingConfig": {
// ...
"./widgets/awesome-widget": {
// ...
"customProperties": {
"--canvas-background-color": "<color>"
}
}
}
}
editingConfig[...].label
The localized name of the widget shown in the editor. Should be an object where each key is a unicode locale and each value a string.
{
// ...
"editingConfig": {
// ...
"./widgets/awesome-widget": {
// ...
"label": {
"de": "Fantastisches Widget"
}
}
}
}
editingConfig[...].noDefaultSnippet
By default, if no matching snippet is found, a default snippet of the form <my-widget></my-widget>
is generated. In case you want to define widgets that are not directly insertable, use this option to opt out.
{
// ...
"editingConfig": {
// ...
"./widgets/awesome-widget": {
// ...
"noDefaultSnippet": true
}
}
}