The Perspective Dashboard component is finally here! Allow your users to add, remove, resize, move, and configure widgets pre-defined by you, the designer, all during runtime.
Overview:
The dashboard component uses a grid system based off of CSS grid specifications to position and place widgets. As the designer, you control the general layout of the grid by specifying the responsive mode, row and column count, cell size (fixed mode only), corresponding row and column gaps, and the widgets available to the user. A user has the ability to toggle a dashboardâs editing state at runtime, and when in edit mode, decide what widgets they want, where they want them, and how they want them configured. Truly, the magic of the dashboard component lies in the runtime user interaction and a widgetâs isConfigurable
** property. Both are described in detail below.
Properties:
-
grid:
fixed | stretch
- A mode which defines the responsive behavior of the grid and its cells. Instretch
mode, the gridâs dimensions are restricted to the full dimensions of its containing element, and its cells consume one free unit of space, effectively growing and shrinking with the containing element. Infixed
mode, the gridâs dimensions can be greater or less than the full dimensions of its containing element, and its cells are given a static size, effectively creating a scrollable grid when cells overflow beyond the containing elements dimensions. -
isEditing:
boolean
- Controls the runtime edit mode of the dashboard component. Stays in sync with the edit/play toggle control located at the bottom of the component. -
editingToggle:
boolean
- When disabled, hides the built in edit/play toggle control located at the bottom of the component. Disable this if youâd like to implement your own toggle that updates theisEditing
prop in a controlled fashion. -
fixed:
FixedModeConfig
- Visible when the grid mode isfixed
.-
cellSize:
number
- Width and height of a grid cell. Exclusively for fixed mode. -
rowCount:
number
- The number of rows in the grid. -
columnCount:
number
- The number of columns in the grid. -
rowGutterSize:
number
- The gap size between grid rows. -
columnGutterSize:
number
- The gap size between grid columns.
-
cellSize:
-
stretch:
StretchModeConfig
- Visible when the grid mode isstretch
.-
rowCount:
number
- The number of rows in the grid. -
columnCount:
number
- The number of columns in the grid. -
rowGutterSize:
number
- The gap size between grid rows. -
columnGutterSize:
number
- The gap size between grid columns.
-
rowCount:
-
widgets:
Array<WidgetInUseConfig>
- An array of configuration objects for widgets currently in use.-
WidgetInUseConfig
-- name - A unique widget name.
- viewPath - Path to a view.
- viewParams - Parameters passed to the view at the specified path.
-
isConfigurable** - Whether this widget is configurable during runtime. If enabled, and in dashboard edit mode, and the widget is selected, presents the user with a configure icon (pencil) in which when active sets a
configuring
boolean view parameter passed to the specified view totrue
. The view can then use this configuring parameter to go into âconfiguringâ mode (designed by you), allowing users to configure the widget. The purpose of thisconfiguring
parameter is to avoid having to make a separate widget for various configurations of the same view. (See configuring a widget below under interaction) -
header:
WidgetHeaderConfig
- Configuration object for the widget header.-
enabled:
boolean
- When enabled renders the widget header. -
title:
string
- The header title to display. -
style:
StyleObject
- Style to be applied the widget header.
-
enabled:
-
body:
WidgetBodyConfig
- Configuration object for the widget body.-
style:
StyleObject
- Style to be applied the widget body.
-
style:
-
minSize:
WidgetSizeConfig
- Specifies the widgets minimum allowable size. Users may not resize widgets below these dimensions.-
columnSpan:
number
- The minimum allowable columns that this widget may span. -
rowSpan:
number
- The minimum allowable rows that this widget may span.
-
columnSpan:
-
position:
WidgetPositionConfig
- A widget position object that is automatically updated whenever a widget is added, resized, or moved.-
rowStart:
number
- The top position of the widget. -
rowEnd:
number
- The bottom position of the widget. -
columnStart:
number
- The left position of the widget. -
columnEnd:
number
- The right position of the widget.
-
rowStart:
-
style:
StyleObject
- Style to be applied the widget.
-
-
availableWidgets:
Array<AvailableWidgetConfig>
- An array of widgets as configuration objects that are available to the user. When a widget is added to the dashboard via the add widget modal, this configuration object is copied to the widgets in use array, and act as the widgets defaults.-
AvailableWidgetConfig
-- viewPath - Path to a view.
- viewParams - Parameters passed to the view at the specified path.
-
isConfigurable** - Whether this widget is configurable during runtime. If enabled, and in dashboard edit mode, and the widget is selected, presents the user with a configure icon (pencil) in which when active sets a
configuring
boolean view parameter passed to the specified view totrue
. The view can then use this configuring parameter to go into âconfiguringâ mode (designed by you), allowing users to configure the widget. The purpose of thisconfiguring
parameter is to avoid having to make a separate widget for various configurations of the same view. (See configuring a widget below under interaction) -
defaultSize:
WidgetSizeConfig
- Specifies the widgets default size adding a widget with no size specified.-
columnSpan:
number
- The default columns that this widget will span. -
rowSpan:
number
- The default rows that this widget will span.
-
columnSpan:
-
minSize:
WidgetSizeConfig
- Specifies the widgets minimum allowable size. Users may not resize widgets below these dimensions.-
columnSpan:
number
- The minimum allowable columns that this widget may span. -
rowSpan:
number
- The minimum allowable rows that this widget may span.
-
columnSpan:
-
category:
string | number
- Groups this widget in the specified category in the add widget modal. -
name:
string | number
- A unique name to provide this widget. This name is used in the add widget modal. If no name is specified, its value will be blank. It is essentially a required property. -
header:
WidgetHeaderConfig
- Configuration object for the widget header.-
enabled:
boolean
- When enabled renders the widget header. -
title:
string
- The header title to display. -
style:
StyleObject
- Style to be applied the widget header.
-
enabled:
-
body:
WidgetBodyConfig
- Configuration object for the widget body.-
style:
StyleObject
- Style to be applied the widget body.
-
style:
-
style:
StyleObject
- Style to be applied the widget.
-
Interaction:
Arguably the âfun partâ of the dashboard component. Users can add, remove, move, resize, and configure pre-defined widgets at runtime both on desktop and mobile devices. There may be some minor variances in how a user can interact with their dashboard between desktop and mobile devices (explained below), but the principle is still the same. We use a bin packing algorithm to do our best to ensure widgets do not overlap when being added, resized, and moved. If there happens to be no space for a widget, it will overlap others so that it is placed within the grid.
Adding a widget
There are two ways a user can add a widget, first, by effectively clicking a single grid cell, and second, by dragging a grid cell to create an add widget overlay. Both result in displaying the add widget modal which provides a searchable list of all of the available widgets a user may add. Effectively dragging a grid cell will create an add widget overlay that specifies the desired dimensions of the widget to add. If the desired widget position overlaps other widgets. The overlapped widgets will be moved to any available space.
Note that when adding a widget, if the desired dimensions are less than the configured minimum dimensions, the desired dimensions will get overridden by the minimum dimensions. If a single grid cell is effectively clicked, the configured default dimensions will be applied, if and only if, the default meet the required minimum dimensions, otherwise the minimum dimensions are applied. By default, the minimum and default dimensions for a widget are 1x1.
On mobile devices, activating a grid cell requires a long-press of about a second. Once a grid cell is activated, you can then drag to create the add widget overlay.
Removing a widget
To remove a widget, select the widget and effectively click the x
icon in the top right corner of the widget. You will then be prompted with a confirmation modal.
Moving a widget
To move a widget, first select (effectively click) the widget so that it becomes active (indicated by the dashed blue border). After selecting the widget, drag the widget to the desired position. As you move the widget any overlapped widgets will be packed into the first available space.
Resizing a widget
To resize a widget, simply select the widget youâd like to resize and drag one of the resize handles. If, while resizing, the widget overlaps other widgets, the overlapped widgets will be packed into the first available space.
Configuring a widget
To configure a configurable widget, select the widget youâd like to configure. Then, effectively click the edit icon (pencil) in the top right corner of the widget. When the widget is configuring, the widgets border will change colors from blue to orange, and the configuring
view param passed to the specified view will be true
.
Toggling editing mode
The dashboard component has a built in edit/play toggle located at the bottom of the component. Users can enter into and out of editing mode by effectively clicking this control. You can also remove this control entirely and implement your own by configuring the editingToggle
component property. See the properties section above for more details.
Populating Widgets and Saving Runtime Edits
You may notice that the runtime edits a user makes to their dashboard do not persist when the session restarts. The current solution here is to add a property change script on the widgets
prop to listen for changes and then write that value back to a database along with any user information derived from the active session. The value of the widgets
prop will be an array of QualifiedValues, which youâll need to handle accordingly.
In similar fashion, consider adding an onStartup
event action that will query the database and then populate the widgets
prop with the users last saved configuration and optionally populate the availableWidgets
prop (possibly for varying user roles).
Itâs apparent that this solution requires some effort, so as always we are open to any ideas or suggestions you may have to help make this processes easier. Please, feel free to comment below.
Wrapping Up
Hopefully thatâs enough information to get you started. We still have some work to do with this component, including the refinement of the bin packing algorithm, improving user interaction, and preventing widgets from jumping beyond the boundaries of the grid, and fixing any bugs you may encounter. We appreciate your patience as we work towards incremental improvements. As always, If you have any questions or suggestions, feel free to post them here.