Dashboard
The Dashboard
class represents a complete dashboard. The Dashboard
manages
the dashboard model, owns the various services required by a dashboard, and
exposes methods for setting the model and signalling model dirtiness.
Dashboard
uses the following classes:
PartManager
(1:1)LayoutManager
(1:1)PartFactory
(1:1, derived from a higher level factory)GlobalsService
(1:1)BindingsProvider
(1:1)Dashboard
works with the following classes:
IRenderMimeRegistry
IClientSession
IExpressionEvaluator
IExternalPartRenderer
The Layout engine is one of the most important components of a Dashboard, along with the PartManager. The Engine is built using Phosphor, and thus uses Phosphor constructs extensively.
It uses a top-down approach, where constraints are evaluated at the root and then passed down to solve the whole tree. This approach works well for dashboards, as it greatly simplifies the framework and enables analytical, linear-time algorithms for otherwise-complex layout schemes.
The engine also includes a drag-n’-drop docking framework. This framework is largely bolt-on, and exposes ways for regions to customize docking behavior. Note that while PhosphorJS includes a DockPanel, the DockPanel is insufficient for dashboards. DockPanels lack the flexibility demanded of comprehensive, production-ready dashboards and are designed instead for an IDE dock layout.
LayoutManager
The LayoutManager
handles the visual layout of the dashboard. Parts are
organized into a tree of containers, such as a Stack Panel or a Tab Panel.
The LayoutManager
exposes some basic facilities for working with these
regions, while LayoutActions
exposes tree manipulation helpers.
The LayoutManager
also tracks the UX state of the layout. For instane, it will
track which region most recently had focus, and report that as the focused
region.
The LayoutManager
can be used independently of a dashboard, it just needs
a part provider and some factory for creating new parts.
One caveat is that the LayoutManager
must always have a root region. This must
be a RegionWithChildren
,
LayoutManager
uses the following classes:
DockPreview
(1:1)LayoutManager
works with the following classes:
LayoutManager.IPartManager
LayoutManager
does not require Part
instances, just
Widget
instances.LayoutManager.IFactory
LayoutManager.IPartManager
, as well as describing the types of “parts”
available to instantiate.LayoutSerializer
LayoutSerializer
instantiates a Layout tree from a given JSON model,
and serializes a given layout tree back to JSON.LayoutActions
DashboardLayoutRegion
The DashboardLayoutRegion
is the base class for all layout regions used by the
LayoutManager
. A layout region implementation must subclass this class, and
may override the following methods:
public static GetMetadata()
super.GetMetadata()
to get a metadata object pre-populated with the
superclass layout properties.public sizeContentToFit(bounds)
public updateFromProperties()
sizeContentToFit
, apply them to the
DOM.onBeforeFocus
/onAfterFocus
onBeforeBlur
/onAfterBlur
By convention, layout properties are described with two brackets in the
documentation (eg, [[showOverlays]]
).
RegionWithChildren
RegionWithChildren
is a subclass of DashboardLayoutRegion
that implements
methods for working with children.
In addition to the methods exposed by DashboardLayoutRegion
,
RegionWithChildren
subclassers may override the following methods:
public layoutChildren()
public createDragShadow(child, clientX, clientY)
onChildRegionAdded
/onChildRegionRemoved
Some layout properties are per-child, but do not make sense globally.
Subclassers can define “Attached Properties” for these cases, to define a
per-part property that is only respected by a particular RegionWithChildren
subclass. Eg, [[StackPanelLayoutRegion.FixedSize]]
. Declare the existence of
these properties in GetMetadata()
, using metadata#addAttachedMetdata()
.
Regions may also have Chroming
; These are widgets added by the parent layout
to children, and are cleared whenever the widget is moved in the tree. Use these
for things like resizer-grips.
Parts are the bread-and-butter of Maven dashboards, and comprise an execution
framework and a view. Views communicate using a set
of “Options”, which are managed by the PartManager
and can be bound together
using bindings.
PartManager
The PartManager
controls the instantiation and execution of Part
instances.
PartManager
works with the following classes:
- PartFactory
- Used to instantiate new parts
- GlobalsService
- Used to enable 2-way bindings via globals
- BindingsProvider
- Used to evaluate all other types of bindings
The flow of execution is as follows:
addPart
RefreshRequested
-> cancelPartEvaluations
, evaluateOrWaitForUser
CancelRequested
-> cancelPart
disposed
-> onPartDisposed
OnOptionChanged
-> evaluateOrWaitForUser
initializePart
trySetOption
initializePart
part#initialize()
init-error
and bail.evaluateOrWaitForUser
evaluateOrWaitForUser
[[WaitForUser]]
is true and that the user wasn’t the one to
trigger the refresh:
evaluateOptions
evaluateOptions
renderPart
and bail.BeforeCalculate
.evaluateOptionForPart
, then handleOptionFinished
evaluateOptionForPart
before-option-calc
lifecycle messageOptionsBag#setBindingValue
.option-error
lifecycle message, and report the error to the
console.handlePartFinished
AfterCalculate
lifecycle message.renderPart
.renderPart
BeforeRender
lifecycle message.part#render()
AfterRender
lifecycle messagerender-error
lifecycle messagePartFactory
A PartFactory is where part constructors are registered. Parts are associated
with a string name that is used in serialization. Consumers of PartFactory
can retrieve all the registered parts, and use that information to do things
like populating lists in UI designers.
Part factories are hierarchal- there is a global PartFactory
that all built-in
parts are registered to. Each Dashboard’s part factory derives from a given
PartFactory
, which is used for Local Parts (UDPs that are saved alongside the
dashboard). Implementations may define whatever levels of hierarchy between the
global factory and the one provided to the Dashboard. (Eg, The JupyterLab plugin
leverages this to scope KernelParts to a given kernel)
Part
A Part represents an individual view in a Dashboard. Parts do not interact with the rest of the dashboard, and are isolated from one another. They instead use Options, which the framework will manage. When these options change, the part will be re-rendered.
Part options are declared by overwriting the static method GetMetadata()
, in
a similar fashion to layout regions. Options named “Input Table” are special-
cased in the UI, and recieve additional integrations (such as a binding editor
and a “Copy/Paste Table Binding” command).
Parts primarily consist of two methods:
initialize
render
It’s good practice to front-load as much work onto initialize
as possible-
the more lightweight you can make render
, the snappier the view will be.
Part
may be subclassed directly, which will work for most implementations. For
views using React, you may find the ReactPart
subclass helpful.
The Part
base class manages some lifecycle methods, and handles the stateful
part overlay that appears over the part when the part is calculating options,
rendering, initalizing, or awaiting a command from the user to refresh.
Bindings define how part options interact with each other. They glue a dashboard together into a cohesive, maintainable unit.
Dashboards normally have the following types of bindings:
None
: A dummy binding that does nothing (default)Global
: A two-way binding to a named globalMql
: A binding to the result of an MQL queryJavascript
: A binding to the result of a Javascript functionEval
: A binding to the result of a kernel function (only in JupyterLab)BindingsProvider
The BindingsProvider
manages the binding evaluators, and owns the shared
thread pool that the MQL and JS binding evaluators use. It offers a layer of
indirection to the binding types, so that in the future we can make this more
dynamic and pluggable.
BindingsProvider
uses the following class:
- MqlWorkerPool
(1:1)
- Hard-coded 8 threads, with a cancel timeout of 15 seconds.
BindingsProvider
works with the following classes:
- GlobalsService
- The globals service is provided to all binding evaluators, so that
they can retrieve the globals referenced in a binding just prior to
evaluation.
- IExpressionEvaluator
- Optional.
- If provided, an EvalBindingsEvaluator will be added to the evaluators
list and made available to consumers.
GlobalsService
The GlobalsService tracks changes in dashboard-level global variables, and includes methods for manipulating and retrieving the globals. It also exposes an Observable for global deltas, so that consumers can react to new globals, updated globals, renamed globals, and deleted globals.
Globals are typed using the serializer’s annotation system. It does not check
type validity, it merely exposes the types for consumers to check against. A
future update may add type validity checking to GlobalsService#set
.