-
Notifications
You must be signed in to change notification settings - Fork 27
Tree Component
A tree component to display hierarchical data.
Displays several TreeItem
entries recursively. This recursion approach was based on the FreeCodeCamp Tree Browser implementation.
It keeps three cache collections (Set
s), one for the TreeItem
entries, one for the entries that are expanded, and one for the entries that are active.
The cache collections are used to avoid having to iterate all the entries for operations such as "Expand All" and "Collapse All", and were based on the Vuetify VTreeView
component implementation.
This component utilizes the Cylc TreeItem
component, as well as Vuetify components like VLayout
, VFlex
, etc.
Cache of TreeItem
entries. Updated whenever a TreeItem
is added to the Tree
(or recursively added to another TreeItem
).
Cache of active entries (see prop activable
). Updated whenever the active
data property of a TreeItem
changes.
Cache of entries that were expanded (i.e. its children are now displayed to the user) (see data (expanded
)[expanded
]). Updated whenever the expanded
prop property of a TreeItem
changes.
Whether the TreeItem
s added to the Tree
must be initially expanded (true
) or collapsed (false
).
A Function
that is used by the method expandAll
(#expandAll). When it is not null
, expandAll
uses this function to filter what entries are expanded or not.
A Function
that is used by the method collapseAll
(#collapseAll). When it is not null
, collapseAll
uses this function to filter what entries are collapsed or not.
An object with structure
tasksFilter: {
name: '',
states: []
}
that is used for filtering the tree. This object is bound to UI elements, such as text input field for the name, and a combobox for the filtered states.
But the filter may or may not be applied automatically. At the moment of writing, the user needs to press a button for the filter to be applied, to reduce the impact on the performance of the app.
This has a structure similar to tasksFilter
. When the user enter values in the UI for the filters, those values are applied to tasksFilter
. But when the user press the button to apply the filters, we move the filters over to this activeFilters
data.
This way, the user can modify the filter parameters without changing the currently filtered tree. Until the button to apply the filters is pressed again.
The main data structure. It is based on the response of a GraphQL query to retrieve workflows, but with a few modifications to the result.
In order to support recursion in the component, we need a fixed entry with the children nodes. Plus, in order to allow for customization based on the type of the node, it also requires a custom field __type
.
- type:
Array
- required:
true
- example:
[
{
__type: 'workflow',
id: 'user/workflow1',
name: 'workflow1',
status: 'running',
children: [
{
__type: 'checkpoint',
id: '20100101T0000Z',
name: '20100101T0000Z',
state: 'failed',
children: [
{
__type: 'task',
id: 'user/workflow1/20100101T0000Z/foo',
name: 'foo',
state: 'failed',
children: [
{
__type: 'job',
id: 'user/workflow1/20100101T0000Z/foo/01',
name: '#1',
startedTime: '2019-08-19T22:44:42Z',
state: 'failed',
submitNum: 1
}
]
}
]
}
]
}
]
Whether each entry in the tree should be highlighted or not when the user hovers the mouse over it.
- type:
Boolean
- default:
false
Whether each entry in the tree should be highlighted or not when the user hovers the mouse over it.
- type:
Boolean
- default:
false
Whether clicking on the entry in the tree should mark it as active, changing its background color permanently until it is clicked again.
- type:
Boolean
- default:
false
When enabled, users will be able to mark multiple entries in the tree as active, instead of a single one when activable
is set to true
.
- type:
Boolean
- default:
false
We use only the default slot.
See src/styles/cylc/_tree.scss
These methods are used internally by the component. But can be used externally if necessary. There are multiple ways of accessing methods of a component externally, but one of the simplest is to assign a ref
ID to the component when created. And then simply call the method.
Example.
<tree
ref="treeComponent1"
:workflows="workflowTree"
:hoverable="hoverable"
:activable="activable"
:multiple-active="multipleActive"
></tree>
And then somewhere in your caller code, you can simply use something similar to <v-btn small @click="$refs.treeComponent1.expandAll()">Expand All</v-btn>
.
A method that returns true
if the component is configured to filter by names, false
otherwise.
A method that returns true
if the component is configured to filter by task states, false
otherwise.
Uses filterByTaskName
and filterByTaskState
to check if filters are set. If so, it will clone the filters over to activeFilters
data varaible, and call filterNodes
method.
If the filters are not set, and this method is called, it will remove all filters and reset activeFilters
data.
Will iterate the list of nodes and call filterNode
.
A recursive function. For a given node, will keep iterating down its children until it finds a task-proxy
node.
When that happens, it will filter using the activeFilters
data. If the task-proxy
node is filtered, then it will return recursively and trim down branches of the tree that have been completely filtered. In other words, if a branch has cyclepoint
and/or family-proxy
nodes whose children task-proxy
nodes have been removed with the filter, then we remove this branch too.
With that, only the items that pass the filter are displayed. And we don't get empty branches with only family-proxy
and/or cyclepoint
nodes.
The filtered nodes are marked in the treeItemCache
data. This way we can quickly access it again to change the .filtered
property if necessary.
For each item in treeItemCache
data, we change its property .filtered
to true
. This makes the value to pass the filter()
function, which means the element is displayed in the UI (default behaviour unless filters applied).
Expands all the entries of the Tree
. If a filter
is provided, the filter is applied first, and the expandedFilter
is updated. The expandedCache
is properly updated too.
Collapses all the entries of the Tree
. If a filter
is provided, the filter is applied first, and the collapsedFilter
is updated. The expandedCache
is properly updated too (we only need to keep one cache, as we have the complete set domain values in treeItemCache
).
The component has other methods such as onTreeItemExpanded
, onTreeItemCollapsed
, onTreeItemCreated
, and onTreeItemClicked
.
These methods react to events from TreeItem
entries, and are not supposed to be extended or called directly.
There are unit tests in the project for basic features, and for expanding and collapsing entries.
Some of these screen shots/casts have been recorded during development, so they may differ a little.
This screen shot displays the Tree
component rendering test data. Note that the workflow entry is not displayed.
Also note that cyclepoints use the Cylc Task
component, but have a certain logic to choose what state represents the whole group (TODO: add link to page explaining logic).
Tasks use the same Task
component. And jobs use the Cylc Job
component. Jobs are displayed from the most recent submitted to the oldest. And the host name appears near the job icon (which has a color indicating its state).
What the component looks like when rendered in a browser with mobile viewport.
In this example we have added a custom view with controls to customize the component (similar to Vuetify's show case sections).
When the user collapses a TreeItem
that represents a task, the user is presented with the summary of the task jobs. As the jobs are now hidden (were collapsed), we show them aligned side-by-side next to the task entry.