Web Designer Themes and Pluggable Objects: Introduction
- 1. Environment
- 2. Known limitations
- 3. Templates
- 4. Themes
- 5. Pluggable objects
- 5.1. Introduction
- 5.2. Manage Pluggable Objects
- 5.3. Creating a custom pluggable object
- 5.4. Custom styling
- 5.5. Language sensitive User interface
- 5.6. Output profiles (Experimental)
- 5.7. Designer provided variables
- 5.8. Designer content
- 5.9. Designer fragments (Experimental)
- 5.10. Javascript API
- 5.11. Helpers
- 5.12. Focusable outline (Experimental)
1. Environment
The Web Designer environment currently contains following web applications:
Web App | URL | Main purpose |
---|---|---|
Web Designer |
Template design |
|
Resource Browser |
Management of Scriptura resources such as templates, themes and pluggable objects |
2. Known limitations
-
Spaces and tabs inside of your Pluggable Object or theme are not removed from the output.
3. Templates
The Web Designer web application can be used to link the following to a template:
-
1 or more themes to control the look and feel of the template.
-
1 data model providing data fields and sample data to the template.
-
1 or more languages for supporting multilanguage templates.
Web Designer templates support two classes of object types:
-
Built-in objects provided as part of the standard Web Designer product.
-
Customer specific pluggable objects that extend the Web Designer object collection.
4. Themes
Themes define the look and feel of a template but they can also include content. Content included in a template by applying a theme cannot be deleted and/or edited in Web Designer.
A theme can for example include a company logo and a standard header and footer as content. A template designer applying this theme to a template would not be able to change or delete these objects from the template using Web Designer.
4.1. Manage Themes
The Manage Themes page lists all recognized themes in the current environment. Unrecognized themes are not included. For every theme its status, name, location and author are listed. The page is useful in case of theme errors. If a theme contains an error, the theme is marked with and an error message is shown inline. Note that in order to access this page, the user needs to have the webdesigner:themes:manage permission.
4.2. Creating themes
A theme is defined by an HTML document containing specific metadata tags. The metadata tags should be the first children of the <head>
element.
Name | Description |
---|---|
scriptura:wbd:type |
Identifies the HTML file as a theme and should have value html-theme |
scriptura:wbd:name |
Name of the theme |
scriptura:wbd:author |
Author of the theme |
<html>
<head>
<meta name="scriptura:wbd:type" content="html-theme">
<meta name="scriptura:wbd:name" content="My theme">
<meta name="scriptura:wbd:author" content="John Doe">
</head>
<body>
<p>This is my first theme</p>
</body>
</html>
When designing themes you have to take into account a number of restrictions:
|
When you are done designing your theme, you can upload it to the Design Studio through the Resource Browser. The theme will automatically be listed on the Manage Themes page and also be available in the template designer.
4.2.1. Adding designer content in your theme
In order to integrate designer content into your theme, you can mark insertion points using the {{designer-content}} handlebars instruction.
<html>
<head>
<meta name="scriptura:wbd:type" content="html-theme">
<meta name="scriptura:wbd:name" content="My theme">
<meta name="scriptura:wbd:author" content="John Doe">
</head>
<body>
{{designer-content}}
</body>
</html>
It is possible to add multiple designer content insertion points in your theme, provided you specify a unique name attribute for each of them. When there is only one insertion point, the name attribute can be omitted.
<html>
<head>
<meta name="scriptura:wbd:type" content="html-theme">
<meta name="scriptura:wbd:name" content="My theme">
<meta name="scriptura:wbd:author" content="John Doe">
</head>
<body>
<div class="banner">
{{designer-content name="banner"}}
</div>
<div class="body">
{{designer-content name="body"}}
</div>
<div class="footer">
{{designer-content name="footer"}}
</div>
</body>
</html>
4.2.2. Using data in your theme
Themes can also refer to data within the currently linked data model. To do this, simply add references to data fields between {{ and }}. As soon as the theme is loaded, the data will be resolved relative to the root of the chosen data model.
However, note that the Web Designer does not enforce a coupling between the theme and the chosen data model. It is possible that template designers choose an incompatible data model which will result in the data fields being resolved to empty data.
<html>
<head>
<meta name="scriptura:wbd:type" content="html-theme">
<meta name="scriptura:wbd:name" content="My theme">
<meta name="scriptura:wbd:author" content="John Doe">
</head>
<body>
<div class="banner">
<img src="{{company.logo}}">
</div>
<div class="body">
{{designer-content name="body"}}
</div>
<div class="footer">
<p>{{company.address.street}}</p>
<p>{{company.address.zip}} {{company.address.city}}</p>
</div>
</body>
</html>
4.2.3. Defining styles in your theme
A theme could define a set of styles. The template designers can then choose a style to change the look and feel of the template. If your theme does not provide any styles, the default list is used.
You define a style by adding a comment to your CSS in the following format:
Name | Description |
---|---|
@scriptura:wbd:style:name |
Human readable name for the style |
@scriptura:wbd:style:requires (optional) |
Comma separated list of valid objects types. Possible values: Text, Container, Image, ReusableComponent, <Pluggable object id from component definition> |
@scriptura:wbd:style:class (optional) |
You can optionally define a class. When no class is provided we will extract it from the following rule provided it is a simple class selector. |
<html>
<head>
<style>
/*
@scriptura:wbd:style:name = Header 1
@scriptura:wbd:style:requires = Text
@scriptura:wbd:style:class = header-1
@scriptura:wbd:style:name = Header 2
@scriptura:wbd:style:requires = Text
@scriptura:wbd:style:class = header-2
@scriptura:wbd:style:name = Header 3
@scriptura:wbd:style:requires = Text
@scriptura:wbd:style:class = header-3
*/
/*
@scriptura:wbd:style:name = Footer
@scriptura:wbd:style:requires = Container, pluggable-object-section
*/
.footer {
color: blue;
}
</style>
</head>
<body>
{{designer-content}}
</body>
</html>
We only parse embedded styles, so this will not work with style sheets hosted on a remote server. You can work around this by hosting the style sheet on the remote server and adding a style tag with all your styles defined in one comment. |
4.2.4. Using responsive images
HTML5 offers a number of features to support responsive images such as srcset, sizes and picture. See https://developer.mozilla.org/en-US/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images for more information.
The srcset feature of HTML5 and image-set CSS function can be leveraged to select images with the correct DPI when generating PDF output. This can be useful when for example you want to generate previews with low DPI images and the final output with high DPI images. To do this, first enable the required feature (HTML or CSS version) in the theme definition. Then define the set of images using the srcset attribute and set the Device pixel ratio property to the corresponding value when generating PDF output depending on which image you want.
This works for both the Xribe Output API and the Web Designer output step.
Example usage:
...
<head>
<script id="theme-definition" type="application/json">
{
"profile": {
"pdf": {
"src-set": true,
"image-set": true
}
}
}
</script>
<meta charset="utf-8">
<meta name="scriptura:wbd:type" content="html-theme">
<meta name="scriptura:wbd:name" content="My theme">
...
<style>
body {
background-image: image-set(
url('http://example.com/img/lores.png') 1x,
url('http://example.com/img/hires.png') 2x
);
}
</style>
</head>
<body>
...
<picture>
<img
srcset="http://example.com/img/lores.png 1x, http://example.com/img/hires.png 2x"
src="http://example.com/img/lores.png"
alt="My image" />
</picture>
...
You can also use the browser specific versions -webkit-image-set() and -moz-image-set().
The sizes attribute, dpi-descriptors, w-descriptors and other absolute units are not supported. Only x-descriptors to indicate display resolution are supported.
For more information on how to set the Device pixel ratio property, refer to the Xribe Output API documentation or the Scriptura documentation that came with the Web Designer output step extension. |
We recommend to only enable the option that you need as these can have a performance impact when generating PDF output for larger templates. |
4.3. Styling built-in objects
4.3.1. Text Object
A Text object is created to support styling the text object. It is allowed to style the Text object by using the text-object class in a theme. For more information that how the text object work on the template designer, please check Text objects.
.text-span {
/*styling color and font-size of the paragraph*/
color: purple;
font-size: 60px;
}
.text-object p {
/*styling color and font-size of the paragraph*/
color: red;
font-size: 70px;
}
.firepad-b {
/*styling color and font-size of the bold text */
color: blue;
font-size: 20px;
}
.text-object strong {
/*styling color and font-size of the bold text */
color: green;
font-size: 90px;
}
4.3.2. Spacer
A Spacer is useful for creating space between objects. To provide support in email clients, the Spacer will be rendered using a table element when creating email output.
It is allowed to style the Spacer object by using the spacer-object
class in a theme, but styles relying on the corresponding element type and its possible child elements are not supported. Those change depending on output type. Additionally, this is implementation.
.spacer-object {
/* providing extra margin */
margin: 16px;
}
4.3.3. Divider
A Divider is useful for dividing several objects. The Divider object consists of a hr element wrapped in a div. It is supported to style both the div and the containing hr element using the divider-object
class in a theme.
.divider-object hr {
/* making the Divider a red line */
border-top: 1px solid red;
}
4.3.4. QR Code
A QR code object allows template designers to encode data in the form of a QR code. The actual QR Code is generated as an SVG image, wrapped with a div element.
Because of the SVG format, a QR code will not be rendered when creating email output.
Styling of the svg element is not supported, but the surrounding container can be styled by using the qr-code-object
class name. As an example, making a QR code centered on the page:
.qr-code-object {
/* centering the QR code */
text-align: center;
}
4.3.5. Table
A table object is a built-in object that allows template designers to represent information in the form of a table. The table object does not contain any styling apart from a margin and a 100% width. Since we render the table object as a regular HTML table, you can use regular CSS rules to style the rendered table.
table,
th,
td {
border: 1px solid #c6c8cb;
}
table {
border-spacing: 0;
border-collapse: collapse;
width: 100%;
table-layout: fixed;
}
table tr {
margin: 0;
padding: 0;
}
table th,
table td {
border-right-width: 0;
border-bottom-width: 0;
vertical-align: top;
padding: 8px;
}
4.3.6. Using headless content theme
Design Studio supports defining headless output properties which can be used to generate headless content that can be consumed by backend systems. In order to define headless output properties, add a new script tag to your existing HTML theme with the special marker ID output-properties-definition
. It is best practice to mark the type
of this script element’s contents as application/json
.
An example of the headless output properties definition can be found below.
Example of the headless theme (include some comments to explain the structure).
<html> <head> <meta name="scriptura:wbd:type" content="html-theme"> <meta name="scriptura:wbd:name" content="headless_theme_1"> <meta name="scriptura:wbd:author" content="John Doe"> <script id="output-properties-definition" type="application/json"> { "name": "Headless", "output": { /* The target will be used to determine the ID of the final output containing the headless output properties. */ /* When generating output using the Compose Runtime, the target will be the name of the output in the deliverable. */ /* On the other hand, when generating output using the Scriptura Engage Document Flow step, */ /* the target will be available in the property data source of the output flow object containing the output properties. */ "target": "headless" }, "definition": [ { /* A unique ID identifying the spec entry */ "id": "title", /* Label supports multilingual labels or single string */ "label": { "en": "Title (required)", "nl": "Titel (gewenste)" }, /* 1 or more targets specify the location(s) in the final output where the rendered*/ /* content for this entry will be injected*/ "targets": [ "message.title", "push.title" ], /* The resulting JSON type. Possible values are “string”, “boolean”, “number” or “array”.*/ /* In the case of an “array” type, the item type must be specified as well. */ /* When omitted, the resulting type will fallback to string.*/ "resultType": "string", /* Priority specifies whether the field is "required", "important" (shown by default) or*/ /* "optional" (hidden by default but shows when expanded)*/ "priority": "required", /* Specifies whether the entry is language sensitive. The default value is true.*/ "languageSensitive": false, /* Support multiline content. When omitted or false, the entry will be single line*/ "multiline": false, /* Specifies how many text lines will be showing if "multiline": true else*/ /* default text object is showing with 1 text line*/ "rows": 6 }, { "id": "remark", "label": { "en": "Remark (optional)", "nl": "Opmerking (optioneel)" }, "targets": [ "message.remark", "push.remark" ], "resultType": { "type": "array", "items": { "type": "string" } }, "priority": "optional", "languageSensitive": false, "multiline": true, "rows": 6 } ] } </script> </head> <body> {{designer-content name="abc"}} </body> </html>
The actual example of the headless theme.
<html> <head> <meta name="scriptura:wbd:type" content="html-theme"> <meta name="scriptura:wbd:name" content="headless_theme_1"> <meta name="scriptura:wbd:author" content="John Doe"> <script id="output-properties-definition" type="application/json"> { "name": "Headless", "output": { "target": "headless" }, "definition": [ { "id": "title", "label": { "en": "Title (required)", "nl": "Titel (gewenste)" }, "targets": [ "message.title", "push.title" ], "resultType": "string", "priority": "required", "languageSensitive": false, "multiline": false, "rows": 6 }, { "id": "remark", "label": { "en": "Remark (optional)", "nl": "Opmerking (optioneel)" }, "targets": [ "message.remark", "push.remark" ], "resultType": { "type": "array", "items": { "type": "string" } }, "priority": "optional", "languageSensitive": false, "multiline": true, "rows": 6 } ] } </script> </head> <body> {{designer-content name="abc"}} </body> </html>
After the theme is used in a template, the template designer will show additional text fields above the template allowing the user to specify the output properties content.
Additional input fields are rendered based on the headless ouptut-properties-definition script tag.
When generating output with the Compose Runtime API, JSON output result are generated, for example:
{ "message":{ "title":"abc", "remark": ["xyz1"] }, "push":{ "title":"abc", "remark": ["xyz1"] } }
5. Pluggable objects
5.1. Introduction
Web Designer includes four builtin object types:
-
Text objects.
-
Image objects.
-
Container objects.
-
Table objects
Other object types listed in the Add Object panel are customer developed pluggable objects, highlighted in the following figure.
5.2. Manage Pluggable Objects
The Manage Pluggable Objects page lists all recognized pluggable objects in the current environment. Unrecognized pluggable objects are not included. For every pluggable objects its status, title, category and location are listed. The page is useful in case of pluggable object errors. If a pluggable object contains an error, the pluggable object is marked with and an error message is shown inline. Note that in order to access this page, the user needs to have the webdesigner:pluggableobjects:manage permission.
5.3. Creating a custom pluggable object
Pluggable objects behave in a way similar to the built-in objects. Like the built-in objects they are used to add content to the template that is being designed. They are created by combining HTML, CSS and javascript. In the following chapters we will go over de different steps needed to make one.
When designing pluggable objects you have to take into account a number of restrictions:
|
5.3.1. Basic usage
A pluggable object contains 2 mandatory parts. It always starts with a script tag followed by the content of the pluggable object. The script tag needs to have the attribute id with the value of component-definition. Inside, you will need to specify the name, icon and category in a JSON format. See below for an example.
Following the script tag you can add as many style or script elements as you need. These will be hoisted to the head element of the HTML and are only included once per pluggable object so you don’t need to worry about using the same pluggable object multiple times within a template.
After that you need to add 1 HTML root element. All other root or text elements will be ignored.
<script type="text/json" id="component-definition">
{
"id": "2-columns", (1)
"name": "2 Columns", (2)
"icon": "columns", (3)
"category": "Layout" (4)
}
</script>
<style>
& {
color: blue;
}
</style>
<script>
console.log('Hello pluggable object');
</script>
<button>My first pluggable object</button>
<div>This won't be in the output</div> (5)
1 | The id for the pluggable object (optionally) |
2 | The name |
3 | The icon based on fontawesome or an absolute url to the image. |
4 | Category of the pluggable object |
5 | Node won’t be rendered by the designer |
5.3.2. Adding custom properties
In a lot of cases you will need to add dynamic data to the pluggable object. You can do this by defining properties. These properties will be visible in the right pane of the webdesigner.
You can add custom properties by implicitly using them or defining them inside of the component-definition.
To use a property within the component add the curly braces around the name like so {{propertyName}}
.
In the following example we implicitly define the variable color by placing it between curly braces. The default property type for variables will be string. If you want to have a different type, you will need to explicitly define the property which we will cover next.
<script type="text/json" id="component-definition">
{
"name": "Button",
"icon": "square",
"category": "Buttons"
}
</script>
<style>
& {
color: blue;
}
</style>
<button class="{{color}}">My first pluggable object</button>
1 | Color property with a plain input field. |
We can improve upon our last example by giving the template designer a pre-defined list of colors to use. This can be done by explicitly defining the properties in the component-definition.
<script type="text/json" id="component-definition">
{
"name": "Button",
"icon": "square",
"category": "Buttons",
"properties": {
"color": {
"defaultValue": "red",
"label": "Color",
"supportedValues": ["red", "green"]
}
}
}
</script>
<style>
& {
color: blue;
}
</style>
<button class="{{color}}">My first pluggable object</button>
1 | Color property with a list input field. |
The table below lists all the different optional options you have for property definitions.
Item | Description | Remarks | Default value |
---|---|---|---|
type |
Used by Web Designer to select the user interface property editing component. |
URL, boolean, string, bind |
string |
label |
The label used in the Properties panel in Web Designer. |
No default |
|
scope |
The scope property allows you to limit them to a certain fragment scope. The Value of the property should be the name of the fragment. |
Only available on properties of type |
None (Pluggable Object scope) |
supportedValues |
When the type is string, specifying this property will change the control to edit this property from an input box to a dropdown list of which the display values are converted using the same rules as for labels. |
None |
|
defaultValue |
The value the property gets when a new instance of the pluggable object is used in a template. |
None |
5.3.3. Requiring capabilities
Pluggable objects can define a set of required capabilities that should be injected into the document and be made available to the pluggable object at runtime. This can be done by extending the component-definition with the requiredCapabilities
field:
<script type="text/json" id="component-definition">
{
"name": "HighchartsDemo",
"icon": "bar-chart",
"category": "Charts",
"requiredCapabilities": [{
"highcharts": "9.3.2"
}],
}
</script>
<script>
window.initializeChart = function(element) {
// the on-mount handler function has access to the HighCharts object since it was specified in the requiredCapabilities.
console.log(HighCharts);
}
</script>
<div onmount="window.initializeChart(this)"></div>
The current set of possible capabilities that can be injected can be found in the following table:
Capability | Supported Versions |
---|---|
HighCharts |
9.3.2 |
When having multiple pluggable objects requiring the same capabilities, it is recommended to make sure they all specify the same version. Specifying different versions across different pluggable objects will lead to undefined behaviour of which version you will end up receiving. Specifying an unsupported version will lead to the pluggable object not appearing in the objects pane.
5.4. Custom styling
The css for all pluggable objects is scoped so it cannot leak into other parts of the template. There is however one caveat for selectors on root elements. To understand this you need to know how scoping works within the webdesigner.
The Web Designer does not introduce additional HTML elements in between objects from the webdesigner and pluggable objects. In order to make sure that all pluggable objects are properly scoped we will add a unique class to the root element of your pluggable object. Afterwards we will rewrite all css selectors to contain this unique class. See the example below what would happen if we would create a class red which sets the background color to red.
.red {
background-color: red;
}
.unique .red {
background-color: red;
}
Since both unique
and red
are both classes of button the selector won’t match and the background color will not change.
To solve this you should use the pseudo class :root
. The following example shows how you could write selectors to change the background color of the button.
<script type="text/json" id="component-definition">
{
"name": "Button",
"icon": "square",
"category": "Buttons",
"properties": {
"color": {
"supportedValues": ["red", "green"]
}
}
}
</script>
<style>
& {
color: blue;
}
.red:root {
background-color: red;
}
.green:root {
background-color: green;
}
</style>
<button class="{{color}}">Button</button>
5.5. Language sensitive User interface
You can optionally provide translations for the name, category and property labels in your Pluggable object. This is done by assigning a translation object to those properties instead of a string. A translation object will have ISO-3166 keys and the translation as the value like so.
{
"nl": "Kleur",
"en-GB": "Colour",
"en-US": "Color"
}
In a Pluggable object it will look as followed:
<script type="text/json" id="component-definition">
{
"name": {"en": "Button"},
"icon": "square",
"category": {"en": "Buttons"},
"properties": {
"color": {
"label": {"en": "Color", "en-GB": "Colour"},
"supportedValues": ["red", "green"]
}
}
}
</script>
<button class="{{color}}">Button</button>
5.6. Output profiles (Experimental)
The designer supports generating output based on several output profiles, such as web, application or email. Depending on this profile, certain capabilities can be available or unavailable. For example, it is not possible to use form elements in the email output profile. You can use these output profiles to restrict the availabilitty of your pluggable object by specifying the required capabilities needed by the pluggable object. If the chosen output profile does not support these capabilities, the pluggable object will not be shown in the Objects Pane.
To specify the capabilities of the pluggable object, simply add a requiredCapabilities
propoerty to the component definition as follows:
<script type="text/json" id="component-definition">
{
"name": {"en": "Button"},
"icon": "square",
"category": {"en": "Buttons"},
"requiredCapabilities": [{"html:form": true}],
"properties": {
"color": {
"label": {"en": "Color", "en-GB": "Colour"},
"supportedValues": ["red", "green"]
}
}
}
</script>
<button class="{{color}}">Button</button>
The list of possible capabilities per output profile can be found in the table below.
Capability | Web | Application | |
---|---|---|---|
html:iframe |
yes |
no |
yes |
html:internal-css |
yes |
yes |
yes |
html:style-element-css |
yes |
yes |
yes |
html:inline-css |
yes |
yes |
yes |
html:external-css |
yes |
yes |
yes |
css:margin |
yes |
yes |
yes |
css:padding |
yes |
yes |
yes |
html:video |
yes |
no |
yes |
html:img |
yes |
yes |
yes |
html:table > css:width:percent |
yes |
no |
yes |
html:form |
yes |
no |
yes |
5.6.1. Output profile application
The output profile application needs javascript in order to render your document dynamically. The officially supported browsers for this are:
Browser | Minimum version |
---|---|
Chrome |
57 |
Safari |
10.1 |
Internet Explorer |
11 |
Firefox |
52 |
Documents created with output profile application may work on older browsers but this is not tested or maintained. |
5.7. Designer provided variables
The designer provides useful variables by default which can be used in your pluggable object.
All designer provided variables are under the object _scriptura
.
Variable |
Type |
Description |
id |
string |
The id of the object. This is useful when used in combination with the Javascript API |
repeatID |
string |
The repeatID for an object. The id of an object is not unique when working with repeats. This id is needed if you want to use the selection api. |
designMode |
boolean |
Indicates if the designer is in design modus or not. This can be used to render additional information while designing the template. |
selected |
boolean |
Indicates if the object is selected or not |
selectedChildrenInfo |
string[] |
The ids for the selected children. This is optional and if you want it, you need to specify it via the |
<script type="text/json" id="component-definition">
{
"name": "Image",
"icon": "object",
"category": "Object"
}
</script>
<div>
{{#if _scriptura.designMode}}
Image source: {{imageURL}}
{{/if}}
<img src="{{imageURL}}"/>
</div>
5.8. Designer content
You can optionally add one or more designer managed areas to your pluggable object. This will allow the template designer to add other objects to that section.
These areas are specified by adding the {{designer-content}}
variable in the HTML.
You can specify following properties on Designer content.
Property | Type | Description |
---|---|---|
Name |
string |
Name for the designer content. This will also be in the content outline. |
objectInsertionMode |
'blank' or '' (default: '') |
Determines the way of inserting objects into the Designer content. |
If we take the button from the previous examples and make the text of the button a designer managed content, it will look like this.
<script type="text/json" id="component-definition">
{
"name": "Button",
"icon": "square",
"category": "Buttons",
"properties": {
"color": {
"supportedValues": ["red", "green"]
}
}
}
</script>
<style>
& {
color: blue;
}
</style>
<button class="{{color}}">{{designer-content name="Text"}}</button>
5.8.1. Default content (Experimental)
It is possible to specify a default object for designer-content blocks. The default object will be inserted into the designer-content when the Pluggable Object is added to the template.
The default object can either be a native object or reference another Pluggable Object.
Native objects are referenced with the native:
prefix. Supported objects are native:text
, native:image
, native:container
, native:spacer
, native:divider
and native:qrcode
.
Pluggable objects can be specified with a path relative to the location of the current file. To prevent the universe from collapsing, avoid circular references in default objects, as they would lead to infinite loops.
An optional attribute defaultObjectLocked
can be set to true to prevent users from deleting the default object.
<script type="text/json" id="component-definition">
{
"name": "DefaultObject",
"icon": "square",
}
</script>
<div>
<p>There is a wrapper component with default pluggable object:</p>
<!-- Built-in objects are referenced by with the "native:" prefix. -->
{{designer-content name="Head" defaultObject="native:text" }}
<!-- Pluggable objects are referenced with a relative path to this document -->
{{designer-content name="Body" defaultObject="../bodyObject.html" defaultObjectLocked=true }}
</div>
5.9. Designer fragments (Experimental)
Designer fragments are block helpers that allow you to specify a section of a pluggable object which can occur multiple times and specify custom properties.
You specify fragments by adding the block helper {{#designer-fragment}}{{/designer-fragment}}
. Fragments can be nested and have support for Designer content. It is however not possible to add additional fragments by default via the Web Designer UI.
It is up to you to provide a convenient way to create new fragments via the Javascript API.
Following properties are available on designer fragments.
Property | Type | Description |
---|---|---|
Name |
string |
Name for the designer fragment. This will also be in the content outline. |
icon |
string |
Icon for the object. This is shown in the content outline and in the selection outline. |
initialCount |
number (default: 1) |
Specify the amount of objects instantiated when inserting the pluggable object. |
locked |
boolean (default: false) |
When set to true, the user will not be able to delete this object. You can only delete this object via the javascript API once created. |
repeat |
boolean (default: false) |
Allows this fragment to be repeated. This will change the structure of Designer provided variables for fragments |
Designer fragments need to contain at least 1 HTML element |
5.9.1. Custom Properties
You can specify properties on fragments in the same way as you do for normal pluggable objects.
The only difference is that you change scope underneath a fragment to that of the fragment.
It is possible to access the parent scope via ../
.
<ul>
<li>Category: {{category}}</li>
{{#designer-fragment name="Product" icon="product" initialCount=1}}
<li>
<span>Name: {{name}} - {{../category}}</span>
<span>Price: {{price}}</span>
</li>
{{/designer-fragment}}
</ul>
-
Properties Pluggable object:
category
-
Properties Product fragment:
name, price
5.9.2. Designer provided variables for fragments
You can find the general Designer provided variables underneath each fragment plus one additional variable. This variable contains the child fragments, and it’s properties. Designer fragment variables follow the same rule as other properties and changes scope underneath each fragment.
<ul>
<li>Category: {{category}}</li>
{{#designer-fragment name="Product" icon="product" initialCount=1}}
<li>
<span>Name: {{name}} - {{../category}}</span>
<span>Price: {{price}}</span>
</li>
{{/designer-fragment}}
</ul>
{
category: "Clothing"
_scriptura: {
designerFragments: {
Product: [
{name: "Pants", price: 100},
{name: "Shirt", price: 50}
]
}
}
}
Designer provided variables for fragments with repeats
If you allow repeats on fragments, the internal structure changes from a 1-dimensional array to a 2-dimensional array. Following example based on the previous one will highlight the key difference in the structure.
<ul>
{{#designer-fragment name="Product" icon="product" initialCount=1 repeat=true}}
<li>
<span>Name: {{name}}</span>
<span>Price: {{price}}</span>
</li>
{{/designer-fragment}}
</ul>
{
_scriptura: {
designerFragments: {
Product: [
[
{name: "Pants", price: 100},
{name: "Shirt", price: 50}
],
[
{name: "Socks", price: 10},
{name: "Sweater", price: 100}
]
]
}
}
}
5.10. Javascript API
The Javascript API is in an experimental state. Breaking changes may occur, and backward compatibility is not guaranteed. The Javascript API is only available in the Web Designer unless otherwise specified. |
5.10.1. Context menu (Experimental)
The context menu only works in design mode. You can add a context menu by placing a data-id-context-menu
attribute on an HTML element.
The recommended way of working with the context menu is via a function you place on the window object.
An example of how you could do this is below.
<script type="text/json" id="component-definition">
{
"name": "Table",
"icon": "table",
"category": "Tables"
}
</script>
<script>
window.createContextMenu = () => {
return [
{
label: 'Insert Left',
onClick: () => console.log('Insert Left'),
disabled: true,
},
{
label: 'Insert Right',
onClick: () => console.log('Insert Right'),
},
];
}
</script>
<div>
<table>
<tr>
<td data-id-context-menu="this.ownerDocument.defaultView.createContextMenu()">
You can insert rows by clicking the row Pluggable object.
</td>
</tr>
</table>
</div>
The result of the function is an array of context menu items. Each item has the following properties.
Property | Type | Description |
---|---|---|
label |
String Or { [langCode: string]: string } |
The label of the context menu item. This will be visible to the user. You can add translation support by creating a map of translations. The keys should be in ISO-3166, and the values are the translations. |
onClick |
Function |
This function will be called when the user clicks the context menu item. |
disabled |
boolean |
Optional parameter to mark certain items as optional. |
5.10.2. OnLoad, OnUpdate
Many libraries wait for the DOM content to be loaded to execute a piece of javascript. Pluggable objects are loaded in dynamically as a result of this many of these libraries will not work out of the box. To resolve this issue, we provide three different attributes which are available on the root element. The only one of the 3 which will work on the actual output is onmount. The remaining 2 will never be called after generating output.
Attribute | Type | Description |
---|---|---|
onmount |
Function |
This will be evaluated each time a pluggable object is loaded into the document. |
onupdate |
Function |
This will be evaluated each time the pluggable object is updated. E.g. change sample, update property,… |
onunmount |
Function |
This will be evaluated just before the item is removed. This is usually the place to do some clean-up. |
<script type="text/json" id="component-definition">
{
"name": "Table",
"icon": "table",
"category": "Tables"
}
</script>
<script>
let elements = [];
window.onmount = (element) => elements.push(element);
window.onupdate = (element) => console.log('Updated ', element);
window.onunmount = (element) => elements.filter(e => e !== element)
</script>
<div onmount="this.ownerDocument.defaultView.onmount(this)"
onupdate="this.ownerDocument.defaultView.onupdate(this)"
onunmount="this.ownerDocument.defaultView.onunmount(this)"
>
Hello world
</div>
5.10.3. Editor (Experimental)
select(objectID, repeatID, omitCrumb) ⇒ void
Allows you to programmatically select an object based on the objectID and the repeatID.
Parameter | Type | Description |
---|---|---|
objectID |
string |
The id of the object |
repeatID |
string |
The repeatID of the object |
omitCrumb |
boolean (default: false) |
Optionally omit the crumb of the selection outline. |
window.id.wbd.api.editor.select(objectID, repeatID);
window.id.wbd.api.editor.select(objectID, repeatID, true);
setProperty(objectID, propertyName, newValue) ⇒ Delta
This method returns a delta object on which you can chain any of these methods.
This method does not do anything until you call apply()
on the delta.
The delta is an atomic operation which enables the template designer to undo this with one undo action.
Parameter | Type | Description |
---|---|---|
objectID |
string |
The id of the object where you want to change the property |
propertyName |
string |
The name of the property you want to change |
newValue |
any |
The new value for the property |
window.id.wbd.api.editor
.setProperty(objectID, 'show', true)
.setProperty(objectID, 'price', 50)
.setProperty(objectID, 'name', 'Shirt')
.apply()
5.10.4. Fragments API (Experimental)
All these methods return a delta object on which you can chain any of these methods.
The methods themselves do not do anything until you call apply()
on the delta.
The delta is an atomic operation which enables the template designer to undo this with a single undo action.
add(objectID, position) ⇒ Delta
Add a new designer fragment before or after the current one. As a result, you always need to have one designer fragment.
Parameter | Type | Description |
---|---|---|
objectID |
string |
The id of the fragment where you want to add an additional one. |
position |
string (default: 'after') |
Optionally define where the fragment needs to be added. Possible values are |
window.id.wbd.api.designerFragments
.add(fragmentID, 'after')
.add(fragmentID, 'before')
.apply()
Delete(ObjectID) ⇒ delta
Delete a designer fragment based on the objectID.
Parameter | Type | Description |
---|---|---|
objectID |
string |
The id of the fragment you want to delete. |
window.id.wbd.api.designerFragments
.delete(fragmentID)
.apply()
5.10.5. Data API (Experimental)
The Data API allows you to retrieve and modify data within your pluggable object. The functions contributed by this api all take a property of type bind
as parameter.
This bind
property will reference a datafield within the data model.
Properties of type |
The following example creates a counter object which will provide a bind
property allowing the user to choose a data field in the data model.
When the user presses the button, the onclick
event handler is fired. The event handler is executed with the following arguments in scope:
Parameter | Description |
---|---|
this |
The element receiving the event. |
_scriptura |
The Scriptura object providing the api. |
event |
The event that triggered the event handler. |
<script type="text/json" id="component-definition">
{
"name": "counter",
"category": "Counter",
"properties": {
"amount": {
"type": "bind",
"label": "Amount"
}
}
}
</script>
<script>
window.button = {
increment: (element, api, event) => {
const current = api.po.getDataValue('amount') || 0;
api.po.setDataValue('amount', current + 1);
}
}
</script>
<div>
<button onclick="button.increment(this, _scriptura.api, event)">+</button>
{{amount}}
</div>
_scriptura.api.po.getDataValue(bindPropertyName) ⇒ string | number
Retrieves a value from the data model, corresponding to the datafield associated with the bind property.
Parameter | Type | Description |
---|---|---|
bindPropertyName |
string |
The name of the bind property. |
_scriptura.api.po.getDataValue('amount')
_scriptura.api.po.setDataValue(bindPropertyName, newValue) ⇒ void
Sets the value in the datamodel associated with the bind property to the new value.
Parameter | Type | Description |
---|---|---|
bindPropertyName |
string |
The name of the bind property. |
newValue |
string, number |
The new value to set in the data model. |
_scriptura.api.po.setDataValue('amount', newAmount)
_scriptura.api.po.submitData(url, options?) ⇒ void
Submits the data to the given url and optionally replaces either the entire document, the data model or nothing.
Parameter | Type | Description |
---|---|---|
url |
string |
The URL to which to submit the data. |
options |
object |
The optional options for the submission request. |
Property | Values | Default Value | Description |
---|---|---|---|
replace |
'all', 'data', 'none' |
'none' |
Indicates what to do with the response data. |
method |
'GET', 'POST', 'PUT', 'DELETE' |
'POST' |
The HTTP method for the request. |
Type | Description |
---|---|
'all' |
Replaces the entire document with the response contents |
'data' |
Replaces the instance data of the document with the response contents. This assumes the response data is of type |
'none' |
Does not replace anything. |
_scriptura.api.po.submitData('https://example.com');
_scriptura.api.po.submitData('https://example.com', { method: 'PUT', replace: 'data' });
_scriptura.api.po.submitData('https://example.com')
.then(data => console.log('Received: ', data))
.catch(error => console.error('Received error: ', error.message));
_scriptura.api.po.validate() ⇒ boolean
Validates the form and returns the result of the validation.
Form validation is only supported in pluggable objects specifying the required capability |
_scriptura.api.po.validate();
5.11. Helpers
5.11.1. Comparison
{{if}}, {{unless}}
Variables in these expressions are recognized by the Web Designer as boolean properties.
Property | type |
---|---|
a |
any |
{{#if hasButton}}
<button>Click me</button>
{{else}}
<span>Text</span>
{{/if}}
{{and}}
Helper that renders the block if both of the given values are truthy. If an inverse block is specified it will be rendered when falsy. Works as a block helper, inline helper or subexpression.
Property | type |
---|---|
a |
any |
b |
any |
returns |
string |
<!-- {great: true, magnificent: true} -->
{{#and great magnificent}}A{{else}}B{{/and}}
<!-- results in: 'A' -->
{{or}}
Block helper that renders a block if any of the given values is truthy. If an inverse block is specified it will be rendered when falsy.
Property | type | description |
---|---|---|
arguments |
…any |
variable number of arguments |
returns |
string |
{{#or a b c}}
If any value is true this will be rendered.
{{/or}}
{{compare}}
Render a block when a comparison of the first and third arguments returns true. The second argument is the arithemetic operator to use. You may also optionally specify an inverse block to render when falsy.
Property | type | description |
---|---|---|
a |
any |
|
operator |
string |
The operator to use. Operators must be enclosed in quotes: ">", "=", "<=", and so on. |
be |
any |
{{#compare productCode '===' 1}}
<button>Click me</button>
{{else}}
<span>Text</span>
{{/if}}
5.11.2. Array
{{each}}
Iterates over each item in an array and exposes the current item in the array as context to the inner block.
Property | type | description |
---|---|---|
array |
Array |
The array to iterator over |
<ul class="people_list">
{{#each people}}
<li>{{this}}</li>
{{/each}}
</ul>
{{withLast}}
se the last item or n items in an array as context inside a block. Opposite of [withFirst].
Property | type | description |
---|---|---|
array |
Array |
|
idx |
number (default: 0) |
The starting index |
<!-- array: ['a', 'b', 'c'] -->
{{#withLast array}}
{{this}}
{{/withLast}}
<!-- results in: 'c' -->
{{withFirst}}
Use the first item in a collection inside a handlebars block expression. Opposite of [withLast].
Property | type | description |
---|---|---|
array |
Array |
|
idx |
number (default: 0) |
The starting index |
<!-- array: ['a', 'b', 'c'] -->
{{#withFirst array}}
{{this}}
{{/withFirst}}
<!-- results in: 'a' -->
5.11.3. Math
{{min}}
Returns the smallest number of the two.
Property | type |
---|---|
a |
number |
b |
number |
returns |
number |
<!-- a: 1, b: 2 -->
{{min a b}}
<!-- results in: 1 -->
{{max}}
Returns the largest number of the two.
Property | type |
---|---|
a |
number |
b |
number |
returns |
number |
<!-- a: 1, b: 2 -->
{{max a b}}
<!-- results in: 2 -->
{{add}}
Return the sum of a plus b.
Property | type |
---|---|
a |
number |
b |
number |
returns |
number |
<!-- a: 1, b: 2 -->
{{add a b}}
<!-- results in: 3 -->
{{subtract}}
Return the product of a minus b.
Property | type |
---|---|
a |
number |
b |
number |
returns |
number |
<!-- a: 1, b: 2 -->
{{subtract a b}}
<!-- results in: -1 -->
{{sum}}
Returns the sum of all numbers in the given array.
Property | type |
---|---|
array |
Array |
returns |
number |
{{sum "[1, 2, 3, 4, 5]"}}
<!-- results in: '15' -->
{{avg}}
Returns the average of all numbers in the given array.
Property | type |
---|---|
array |
Array |
returns |
number |
{{avg "[1, 2, 3, 4, 5]"}}
<!-- results in: '3' -->
5.12. Focusable outline (Experimental)
It can be beneficial for the end user to hide the pluggable object tree in the content outline when he/she is not working on it.
To do this add the option collapseOutline
to the component-definition
section as shown below. The default value is false.
<script type="text/json" id="component-definition">
{
"name": "Table",
"icon": "table",
"category": "Tables",
"collapseOutline": true
}
</script>
1 | Go back to global outline |