Tabs
With this widget you can show different widgets in the same space by switching between tabs in a given browser tab. Each tab holds a complete 'sub' compilation.
Once you are familiar with the model details, be sure to also refer to the creating and editing tab content section.
Model
{
"type": "tabs",
"appearance" : {},
"options" : {},
"tabs": [ // List of tabs. View order depends on the index in the array.
{
"id": "", // Unique id of the tab.
"name": "", // Name of the tab as shown on the tab when there is no indicator title.
"indicator": {}, // Configure the appearance of the tab's indicator. see details below
"compilation": {} // Like the a normal WebStudio compilation.
}
]
}
More details for the nested properties are provided below:
Appearance
This property controls the appearance of tab content to be either docked or floating. It has the following fields:
Name | Description |
---|---|
|
Switches the tabs widget appearance between:
|
|
Determines the compilation overall scroll behavior while a floating tab is visible. The options are:
|
Additional considerations for floating tabs
-
Showing a tab panel: A floating tab is displayed when it is activated, either by clicked on its indicator or it receiving an activate message. Only one tab in the tabs-set can be visible at a time. Consequently, the currently visible tab is replaced by the next one selected.
-
Hiding a tab panel: A visible tab panel can be hidden in the following ways:
-
Click on its indicator a second time.
-
Send the tabs a
setActiveTab
message using theid
of the active tab. This is the equivalent of clicking on the indicator. -
Send the tabs a
setActiveTab
message setting theactivate
property to "none"
-
-
Panel size: The size of the floating tab panel is determined by setting the
width
andheight
in each tab’scompilation.options
property to the desired number of pixels. The panel size can be be different for each tab. -
Popup location: All floating tabs are drawn next to the first tab indictor at the side opposite to the
tabAlignment
. For example, if thetabAlignment
is set to top, the pop-up panel is shown below the first tab, with the left edges aligned.
Options
The options property controls the overall appearance of the tabs indicator:
{
"options": {
"tabAlignment": "top", // Edge to show the tab indicator at
"showTabBar": true, // Show or hide the tab indicator bar
"indicator": {}, // General indicator settings
"tabBar": {} // Tab bar style setting
}
}
Name | Description |
---|---|
|
Show the tab indicators at any of the widgets edges ("top" = default, "bottom", "left", "right" ). |
|
The tab-bar can be explicitly hidden. This is useful to maximize the available screen area available to the contained compilations. If the tab bar is hidden, different tab instances can still be selected using send actions as explained below |
|
The indicator settings provide a means to configure the appearance of the tab selector in the tab bar.
Overall settings can be defined at the |
|
Use the |
Tab Indicator
The appearance of the indicator, used to show which tab is selected, can be configured at the tabs
level, in which
case it applies to all tab
instances, or at the individual tab
level.
Note: The indicator
element of the tab
model is optional. If omitted, the indicator title will be set to the value
of the tab
name field.
{
"indicator": {
"title": "", // Title shown on the tab indicator.
"icon": {}, // Icon config object.
"style": {}, // Sets additional style for this indicator. (e.g. Different font or color)
"selector": { // Settings relating to the selected tab
"line": {
"color": "red",
"alignment": "bottom",
"hidden": false
},
"style": {
"color": "yellow"
}
}
}
}
Name | Description |
---|---|
|
Title of the tab in the indicator bar. Only relevant at |
|
Only relevant at Tab specific icons can be provided in addition to, or instead of indicator titles. Set the |
|
Set the "base" appearance of indicators independent of their selected state. Setting defined in the |
|
Configure the appearance of the selected tab’s indicator. When defined at the
|
data sources
As with other widgets, model content can be loaded from a data source. The tabs
widget is somewhat unique in this regard, since it provides
dataSource
properties at both the tabs
level and at individual tab
level.
Typically the data source will be an advanced endpoint used to call a Lua function in the backend. The script needs to return a Lua table conforming to the model expected by the widget. As always, the returned Lua table is transparently translated to json.
-
Tabs dataSource: The number of tabs returned and their appearance will be under the control of the
dataSource
. The data returned must be one of the following:-
Tabs array: An array of objects each element of which defines a
tab
object as shown above. The compilation content of the tab can be included in the returned dataset, but may be left blank{}
, if thetab
in question has its owndataSource
binding. If the model returned contains data sources at tab level, these are evaluated immediately after the top level update has been done. -
Tabs object: A single object, containing a field called
type
with value tabs. In this scenario, valid root level properties present in the data-source are added into the work model. Existing work model settings will be overwritten. Exceptions are theid
,captionBar
andlayout
properties, which cannot be overwritten in the work model.
-
-
Tab dataSource: The
tab
leveldataSource
can return one of the following:-
Compilation object: A single object containing a valid compilation. WebStudio looks for the presence of the
version
(string) andwidgets
(array) properties to determine if the returned object is a compilation. If either of these is not present, WebStudio will treat the object as atab
object. For example, if the following JSON is returned, WebStudio knows to assign the data to thecompilation
property of the tab:{ "version": "1", "widgets": [ { "type" : "text", ... } ] }
-
Tab object: An object with named fields of a
tab
. Any of the fields can be provided except for theid
which cannot be overwritten by the data source attab
level. In other words, this option applies when the returned data is determined not to be a compilation object. Fields that are invalid for a tab instance are ignored. The example below does essentially the same at the previous one, but in this case the compilation field is explicitly set and we have the option to override some of the other tab properties:{ "indicator": { "title": "New title" }, "compilation" : { "version": "1", "widgets": [ { "type" : "text", ... } ] } }
Note: The parent
tabs
data source can define any field of atab
including the tab-id.
-
Actions
Supported action hooks:
-
onActiveTabChanged
: Triggers when:-
Clicking on an unselected indicator or activation from an action in a pipeline.
-
For fixed tabs, that is non-floating ones, the hook is also invoked immediately after the widget is loaded, since these always have one tab selected.
-
For floating tabs, the hook is invoked both when the tab panel is shown or hidden.
-
Example opening a link:
{
"type": "tabs",
"name": "Tabs",
"description": "Empty Tabs",
"tabs": [
{
"id": "tab01",
"name": "Tab01",
"indicator": {
"title": "Tab 01"
},
"compilation": {}
},
{
"id": "tab02",
"name": "Tab02",
"indicator": {
"title": "Tab 02"
},
"compilation": {}
}
],
"actions": {
"onActiveTabChanged": {
"type": "notify",
"text": "The active tab changed"
}
},
"id": "tabs01"
}
Changing the selected tab in a pipeline
Modify actions
A tab can be dynamically changed by means of a modify action. Depending on the route
the behavior is:
route contains | results in |
---|---|
ID of the tabs |
refresh of all the tabs |
ID of the tabs and the tab |
refresh of that particular tab |
ID of the tabs, the tab and the widget |
refresh of the widget on that particular tab |
This pattern for the route
continues for nested Tabs widget
within tabs.
Examples:
-
Change the tab indicator alignment:
{ "type": "modify", "id": { // even though we are not modifying anything on the tab instances, // they are all refreshed by modifying a property at tabs level! "route": [ "tabs01" ] }, "set": [ { "name": "model.options.tabAlignment", "value": "left" } ] }
-
Change the indicator color of "tab01". Only the targeted tab will be updated.
{ "type": "modify", "id": { // only tab01 will be refreshed. "route": [ "tabs01", "tab01" ] }, "set": [ { "name": "model.indicator.style.color", "value": "yellow" } ] }
-
To change the property of a widget inside the compilation of a tab, say the color of a
text
widget, you might be tempted to do something like this:{ "type": "modify", "id": { "route": [ "tabs01", "tab01" ] }, "set": [ { // Access a widget by its array index in the compilation. // This is not very convenient and can lead to unpredictable // results if the position of the widget were to change in the array. "name": "model.compilation.widgets.0.options.style.color", "value": "yellow" } ] }
A much better way of achieving the same is by specifying the id of the target widget in the
route
expression, which automatically interprets any id following the tab id as that of a widget in its compilation. Not only does it make the expression simpler and more robust, but it also ensures that only the targeted widget is updated instead of the whole tab compilation.{ "type": "modify", "id": { "route": [ "tabs01", "tab01", "text01" ] }, "set": [ { "name": "model.options.style.color", "value": "yellow" } ] }
Send Actions
The tabs
widget responds to the following message topics in a send action:
-
setActiveTab
: Activate a specific tab based on the messagepayload
parameters provided:Field Description activate
Optional field used to control which tab should be selected. Options are:
-
first or last
-
next or previous: Activate the tab after/before the currently active one. The order is defined by the position of the tab in the tabs list of the model.
-
nextWithRotate or previousWithRotate: Wrap round if the current active tab is the last or first one in the list.
-
none: Only affects floating tabs, causing the active tab to be deactivated/hidden. An alternative way to hide the active floating tab is to use a
dismiss
action triggered from a widget inside the floating tab.
id
Causes the tab with the specified
id
to be activated when used without providing anactivate
value. When used in conjunction withactivate
, determines the tab from which the relative position is calculated.Examples:
Move to the next tab in the tabs array:
{ "type": "send", "to": "tabs01", "message": { "topic": "setActiveTab", "payload": { "activate": "next" // move relative to the current active tab } } }
Active a specific tab:
{ "type": "send", "to": "tabs01", "message": { "topic": "setActiveTab", "payload": { "id": "tab02" // activate is not provided } } }
-
-
addTab
: Add a new tab to the tabs-array. By using asend
rather than amodify
action ensures that only the added instance is refreshed and not the tabs widget as a whole. The model for the new tab must be provided in thepayload
of the message received by the action. For example:{ "type": "send", "to": "tabs01", "message": { "topic": "addTab", "payload": { "tab": { "name": "New Tab", "indicator": { "title": "New Tab" }, "compilation": { "version": "1", "widgets": [ { "type": "text", "text": "Text", "captionBar": true, "layout": { "x": 1, "w": 7, "h": 4, "y": 1 } } ] } }, "activateTab": true } } }
The
payload
fields used by the send topic are:Field Description tab
Compulsory field used to define the tab model.
activateTab
Optional field used to immediately activate the newly added tab. The default value for this field is false.
Note that if theappearance.type
of the tabs widget is "docked" and the newly added tab is the first one to be added then the tab gets activated, regardless of the value ofactivateTab
.Note: The selected tab is exposed in the work model by the
state.activeTabId
field
Creating and editing tab content
While being able to create and display nested compilations in the tabs
widget adds a lot of flexibility to WebStudio, it also introduces some complexities that the "root" compilation doesn’t need to contend with. What these are and how we deal with them is the subject of this section.
As you will have seen when adding a new template tabs
widget to your compilation, it start with two empty tab instances. The obvious question now arises "How do I configure the compilation content for each tab?", since at present, widgets can only be added interactively at root level. The best answer to this depends on how you intend to use the tabs widget. Let’s consider two scenarios:
Creating a static tab compilation
If the content of each tab will be static, in that it is not dependent on run-time state and should be loaded with the main compilation rather than by the dataSource
, the easiest way to get going is to create the content in a new browser tab and then copy the JSON to the tab.compilation
property.
Once loaded, the nested widgets can be edited as usual, by clicking on the the edit icon {} in the widget caption bar, dragging them to the desired position in the panel or resizing them using the resize handle in the bottom right corner. Changes made in this way are written back to the initial model.
If you need to add additional widgets to the tab at some later stage, you can either:
-
Manually add the widget JSON to the
widgets
array in thetab.compilation
property:-
Optionally, copy JSON for the new widget from a template or some other existing widget of the same type.
-
Locate the
widgets
array in thecompilation
property of thetab
and insert the copied JSON as a new array element. Alternatively create a new blank element and set thetype
property to the desired value, then use code completion to further configure the model. -
Update the
id
for the new widget.
-
-
Edit the compilation interactively in a new browser window:
-
Copy the JSON of the
compilation
property into a new blank WebStudio browser tab. -
Add new widgets interactively
-
Copy the modified JSON back
-
Dynamic tabs
When the content of a tab
instance is loaded using a dataSource
, the compilation value is typically sourced from the back end where it may be dynamically created or read from an object property. In the latter case, the compilation JSON will often have been created in WebStudio, like any other "root" compilation.
Widgets added at runtime, whether by a dataSource or via a modify action, cannot be interactively edited since they do not reside in the initial model. The widget JSON is still shown when the {} icon is clicked, but the editor will be in "read-only" mode.
|
Nested widgets that have their layout.static property set to false, whether they are in the initial model or were dynamically loaded, can be moved and resized, but the changes are only persisted for the ones that are in the initial model.
|
showDevTools
As is the case with the root compilation, the caption bar edit-buttons {} of widgets in a tab panel, can be shown or hidden using the options.showDevTools
property.
{
"type": "tabs",
"tabs": [
{
"id": "tab01",
"indicator": {
"title": "Tab 01"
},
"compilation": {
"version": "1",
"widgets": [],
"options": {
"showDevTools": true
}
}
}
],
"id": "tabs01"
}
In the context of tab compilations there are two scenarios to consider:
-
showDevTools is not set: This is the recommended option, since it allows the
showDevTools
value to be inherited from the parent compilation the tabs widget resides in. -
showDevTools is true or false: The parent setting is ignored in favour of the locally applied value. If the tab in turn contains its own tabs widgets, this values is pushed down to the nested compilations, where the same logic as described here is applied.