Adaptive Layouts
An Adaptive Layout uses primary input sources, namely:
- Data and its Metadata
- Layout Instructions
- Metadata Layout Components
- Registered Layout Components
An Adaptive Layout uses those inputs and produces a composition of Adaptive Layout Components. Not all inputs are required, and the degree to which the inputs are used depends on the use case, described later in this document. Adaptive Layouts understand how to take data and map, or translate them, into meaningful component parameters. Adaptive Layouts also understand how to take changes from the components and store them back into the corresponding object adaptor. Some layouts are built into the core library and are used by default when no other layout is applicable. Some layouts can be created, stored and retrieved to augment the core with additional, registered layouts.
Adaptive Layout Components
These are the building blocks for creating user interfaces. They are simple units for building up more complex UI patterns, while maintaining some degree of generality, left to the implementation details. This slight abstraction allows for the Layout Component to be portable between UI implementation frameworks. For example, a Text Field Layout Component may suggest that it has a label, a description and a value, but it doesn't specify how the inputs are arranged out and rendered. It contains no direct influence over, say, the font style or spacing, or whether the field has an outline or not. The implementation may even have more complex features than the generic Layout Component definition requires.
Layout Components are also “dumb”, so to speak. They are unaware of where data comes from or where it needs to be stored. So, to produce something meaningful, a composite collection of components need to work together and a layer placed on top to handle the data synchronization. This is where Layouts and Metadata Components come in.
The following table is a complete list of Layout Components:
Layout Component | Description |
---|---|
Autocomplete | Component to display a list of options that may be selected or can be autocompleted by typing. |
Box | A component that renders a flexible Box of content. |
Breadcrumb | A component that renders a Breadcrumb path with links |
Button | Component to display a Button. |
Callout | A layout container that displays content inside a popup. |
Chart | Component to display data in a Chart. |
Checkbox | A component that displays a checkbox. |
ChoiceGroup | A component to display a selectable choice of options. |
CodeEditor | A component that renders a rich text editor. |
Collapsible | A layout container that displays content that can be hidden by a collapsible control. |
DatePicker | A component that allows you to select a date from a calendar. |
DateTimePicker | A component that allows you to select a date and time. |
Dialog | A layout container that displays content inside a popup, with a confirmation response. |
DiffEditor | A component that compares two objects. |
Divider | A component that displays a Divider line to separate content. |
Dropdown | A component that displays a set of options that can be selected from a dropdown. |
DropdownEditor | A component that manages a list of options in a Dropdown picker. |
EditableCallout | A component that calls out with an editable multiline text area. |
EditableLabel | A component that renders an label, which can be edited when clicked. |
Hidden | A component that hides content under screen breakpoint conditions. |
Icon | A component that displays an icon. |
Image | A component that displays an image. |
Link | A component that renders a url link. |
List | A layout container that renders a list of components. |
ListEditor | A component that allows the user to manage a list of text items. |
Menu | A component that renders a Menu to be displayed. |
Message | A component that renders a message to be displayed. |
Modal | A layout container that displays content inside a popup. |
Native | A component that displays a native HTML element. |
Nav | A navigational component that displays a list of links. |
Paper | A component that allows contents to be displayed on a Paper like surface. |
Picker | A component that allows one or more items to be picked from a list. |
Responsive | A layout container that renders content in a grid format, with breakpoints defined for a variety of screen sizes. |
Skeleton | A component that displays a skeleton, or shape, of a component that may shimmer to indicate that an actual component is waiting to be loaded. |
Slider | A component that displays numeric values in a slider control. |
Snackbar | A component that renders a Snackbar to be displayed. |
SpinButton | A component that renders a numeric field that an be incremented or decremented. |
Spinner | A component that displays a spinner progress indicator. |
Stepper | A component that displays a Stepper indicating a list of steps and the current one you are on. |
Table | A component that displays data in a table format. |
Tabs | A layout container that renders a series of tabs. |
TextField | A component that renders a text field with a label and description. |
TimeDuration | A component that renders a time duration. |
TimePicker | A component that allows a time to be chosen. |
TimezonePicker | A component that allows a timezone to be chosen. |
Toggle | A component that renders a boolean value as a toggleable switch. |
ToggleButtons | A component to display a selectable choice of options, presented as buttons that can be toggled on or off. |
Toolbar | A container component that arranges a series of buttons or other minor content in a horizontal bar. |
Tooltip | A component that surrounds content with a tooltip. |
Tree | A component that renders data in a Tree structure. |
Typography | A component that renders a text label. |
Generating Adaptive Layouts
There are a variety of methods for building Adaptive Layouts. Some require very little effort, while others require varying degrees of programming. This range of flexibility resembles the patterns of Declarative vs Imperative design. We will visit each method of creating layouts, beginning with the most declarative way and move towards more imperative methods.
Method 1
By simply defining objects and their metadata - objectType and propertyType definitions - we have already given the Adaptive Layout processor much of what it needs to know to leverage built-in components and parameter mappings to generate some simple layouts. For example, suppose we have an object containing:
obj = {
“givenName”: “Robert”,
“surname”: “Smith”,
“registered”: true,
}
And supposed we also define an Object Type to handle those properties:
objectType = {
“propertyTypes”: {
“givenName”: {
“dataType”: “string”,
“label”: “Given Name”,
“description”: “Your first name”
},
“surname”: {
“dataType”: “string”,
“label”: “Surname”,
"description": “Your last name”
},
“registered”: {
“dataType”: “boolean”
}
}
}
This may not seem like much, but already, we have enough to render the object with some appropriate default components. The properties givenName and surname would use TextField components, while registered would use a Checkbox component. Using metadata this way allows you to use declarative efforts to create your layouts. By changing the metadata and adding in constraints and dataTypeParameters, you can influence the way a layout is created. In this method, we are not explicitly defining any real Layout designs.
Results
When we are finished, we may see something like:
Pros
Layouts are implicitly generated simply by declaring metadata. This eliminates code and boilerplates, necessary for influencing the final output. Great for simple designs, or for end-users with little to no time or programming experience.
Cons
You are limited to what can be added to metadata, and therefore layouts auto-generated in this way may seem cookie-cutter like in their final output.
Method 2
This method piggy-backs the first Method, but introduces the ability to declare some Layout Parameters. These parameters are instructions that the Adaptive Layout processor uses to influence which components to select, and how to handle parameter mappings to individual Components. If we reuse the example from Method 1, we may decide to tweak the design slightly so that boolean dataTypes are represented by Switch components instead of Checkbox components, and TextFields should ignore descriptions. To do this, we may create some Layout Parameters, such as:
layoutParameters = {
“preferredComponents”: [
{
“dataType”: “boolean”,
“componentType”: “Switch”
},
{
“dataType”: “string”,
“componentType”: “TextField”,
“componentProps”: {
“showDescriptions”: false
}
}
]
}
Results
When we're finished, we may see something like:
Pros
This method still uses metadata for much of its rendering, but with some parameter declaration, we can produce much more flexible outputs than previously.
Cons
In addition to object metadata, we will have to actually create Adaptive Layouts to declare our Layout Parameters.
Method 3
Now, we're going to have to start declaring our layouts much more directly. This method takes the Layouts that we created via Method 2 and enhances them with some explicit instructions on how to actually layout the data we have to work with. In addition, we no longer need to tether the layout to object data or metadata, if we don't really want to. We are free to add spontaneous boxes, headers, text labels or icons.
layout = {
“componentType”: “Box”,
“parameters”: {
“contains”: [
{
“componentType”: “Typography”,
“parameters”: {
“text”: “Register User Form”
}
},
{
“componentType”: “Box”,
“parameters”: {
“contains”: [
{
“componentType”: “TextField”,
“parameters”: {
“label”: “Given Name”
}
},
{
“componentType”: “TextField”,
“parameters”: {
“label”: “Surname”
}
},
{
“componentType”: “Switch”,
“parameters”: {
“label”: “registered”
}
}
]
}
}
]
}
}
Results
When we're finished, we may see something like:
Pros
The rendered output will be very customizable, while still maintaining the flexibility and portability offered by keeping the designs inside Adaptive Layouts.
Cons
More effort will be required, using the Adaptive Layout design tool to generate the layout instructions and any additional layout parameters. For a programmer with more experience, they may wish to simply use the underlying components directly inside an IDE.
Method 4
At this point, we are breaking away from Adaptive Layouts and directly importing and using layouts that have been implemented by a technology, such as Javascript/ReactJS. In this use-case, you are a programmer and may be writing a full-blown application that contains data and components that are outside of Adaptive Layouts or their purpose, but still want to pull in some Adaptive Layout code for an easier integration with Adaptive Framework data. You may also want to massage the data going in and out of the components in order to store and present them in different ways.
Results
A complete application, independent of Adaptive Framework, but importing common layouts:
Pros
As a programmer, you get to work with tools and libraries that you may be more familiar with, while still extracting some of the work done by Adaptive Layouts. Entire apps can integrate parts of the framework without too much effort.
Cons
You must be an experienced programmer.
Method 5
From here on out, we're not really talking about Adaptive Layouts at all, but more interested in the direct interfaces and APIs that can be used to integrate Adaptive Framework data. Perhaps, you import and use the Javascript bindings, or even hit the RESTful interfaces to access and use data. All of the UI is up to you and you may choose to use third-party component libraries to present the UI.
Results
The sky's the limit!
Pros
You have the ultimate flexibility here. Use what you need and discard the rest.
Cons
Some assembly required.
Metadata Components
In the previous section, the more declarative methods for creating Adaptive Layouts did some hand-waving to suggest the Layout processor that generated the layouts understood the best way to do so, given the data, metadata, instructions. This requires there be some default behavior when those instructions are not present. That's where Metadata Components come in. In general, there are three categories of built-ins. Not surprisingly, they are:
- Data Type Components
- Property Type Components
- Object Type Components
The first set of Metadata Components, Data Type Components are fairly simple. They take a dataType and dataType parameter, along with any operational property, such as "editable", and return a Layout Component, such as a TextField, with its parameters appropriately mapped from the object and its object metadata. These Metadata Components map one-to-one with dataTypes:
- anyURI
- Base64Binary
- Boolean
- Date
- DateTime
- DayTimeDuration
- DnsName
- Double
- Expression
- HexBinary
- Hybrid
- Ia5String
- Integer
- IpAddress
- List
- Null
- Object
- ObjectId
- ObjectPath
- Password
- Rfc822Name
- String
- Template
- Time
- X500Name
- XpathExpression
- YearMonthDuration
The next set of Metadata Components handle specific Property Types. These aren't as obvious, but some of the common ones are:
- AdaptorDropdown
- DataTypeDropdown
- PropertyTypeDropdown
- ObjectTypeDrodown
- CustomVariables
Finally, some Metadata Components handle specific Object Types. For example, _AdaptiveObjectType_ objects are specially handled in order to display them in an easy-to-consume way.
Customization
Custom Adaptive Layouts
Adaptive Layouts are intended to be created by end-users and developers in order to provide layout instructions for the Object Types and Property Types that are unique to your data. This section describes the process of creating and maintaining custom Adaptive Layouts.
Custom Adaptive Components
There's a lot of flexibility and options that have been provided with the Adaptive Components provided out of the box. However, just like everything else in Adaptive Framework, these Adaptive Components are intended to be extended, so developers can create and add new Adaptive Components to be used and shared. This section provides steps to being creating your own custom Adaptive Components.