Introduction
This library allows you to present hierarchically organized data in a nice and logical manner based on the VueJS framework.
There are lots of libraries but there was always something from each (in my humble opinion). It is an attempt to make a perfect tree.
Just try it. The tree you were waiting for!
Features
- drag&drop
- mobile friendly
- events for every action
- flexible configuration
- any number of instances per page
- multi selection
- keyboard navigation
- filtering
- sorting
- integration with Vuex
Getting Started
Installation
- npm:
$ npm install liquor-tree
- Yarn:
$ yarn add liquor-tree
It has to be installed to the VueJS instance. Please take a look at the official documentation to understand how to use VueJS components (if needed, of course).
You don’t need to care about styles, they are automatically appended to the document.
When used with a module system there are 3 ways to register the component (maybe more… I don’t know).
Okay. It’s our ways:
1 | import Vue from 'Vue' |
1 | import LiquorTree from 'liquor-tree' |
To register the library you can choose between the 3 methods I mention above.
When used directly in browser you can include liquor-tree
via CND (it is a latest version of the library):
1 | <script src="https://cdn.jsdelivr.net/npm/liquor-tree/dist/liquor-tree.umd.js"></script> |
Component Options
Name | Type | Default | Description |
---|---|---|---|
multiple | Boolean | true | Allows to select more than one node. |
checkbox | Boolean | false | checkbox mode. It shows checkboxes for every node |
checkOnSelect | Boolean | false | For checkbox mode only. Node will have checked state when user clicks either text or checkbox |
autoCheckChildren | Boolean | true | For checkbox mode only. Children will have the same checked state as their parent. |
parentSelect | Boolean | false | By clicking node which has children it expands node. i.e we have two ways to expand/collapse node: by clicking on arrow and on text |
keyboardNavigation | Boolean | true | Allows user to navigate tree using keyboard |
propertyNames | Object | - | This options allows the default tree’s structure to be redefined. See example |
deletion | Boolean | Function | false | If keyboardNavigation is false this property is ignored. This property defines deletion behaviour. See example |
fetchData | Object | - | See guide |
dnd | Object | - | See guide |
editing | Object | - | See guide |
Structure
The component has only two props: data and options. For more information about props read VueJS documentation.
Additionally, see filtering.
- property options - This property defines tree behavior. See Component Options
- property data - Array-like object that defines tree nodes
Property data has its own structure for every node:
1 | { |
id
: By default if node didn’t have anid
it will be generated randomlytext
: Label for Nodedata
: Intermediate data for each node. It can be anything you want. This object is created for every node and VueJS makes this property reactive.children
: List of child nodes.state
: Allows user to set Node’s state.
By default a Node has the following states:
1
2
3
4
5
6
7
8
9
10
11
12
13
14{
"selected": false,
"selectable": true,
"checked": false,
"expanded": false,
"disabled": false,
"visible": true,
"indeterminate": false,
"matched": false,
"editable": true,
"dragging": false,
"draggable": true,
"dropable": true
}
It is not necessary to pass all the states for every Node. It will automatically merged with the default states object.
Initial data example:
1 | const treeData = [ |
Basic Usage
ES6 (using vue-loader)
1 | <template> |
If you are using CDN that’s all you need to build your first app (I mean app that using VueTree
library)
The following example illustrates linking to the VueTree
library without having to register the library as a component:
1 |
|
Guides
Basic Features
This example demonstrates default behaviour of tree without any configurations. Each node from received data has its own states properties (view full list)
You able to select multiple nodes with Ctrl
key. The same behavior as we are used to ;)
Checkboxes
The example above is default mode. You can switch it to checkbox
mode. To do it just add the tree’s option checkbox
:
1 | <tree |
States of node like checked and selected are not interchangeable. They can be used together.
Redefine Structure
This component has strict
structure. But! You can easily redefine this format. Yeah… Sometimes you don’t want to change your server-side code and you have very different format for tree. To do this you just need to send the object:
For instance you have data:
1 | [ |
You just need to add propertyNames
options to Component Options:
1 | { |
Then your data will be transformed to a readable tree format. Awesome! See example below.
Keyboard Navigation
By default keyboardNavigation options is true. It allows user to navigate the tree using a keyboard. Navigation is implemented in the usual way (i.e Windows Navigation pane). Disabled Nodes are ignored.
You also have the abillity to define condition to remove Node. To do this, determine the deletion component option.
It receives Boolean object (default is false) or Function.
- If this property received a
true
it will remove selected Node - If this property revieved a
function
it will remove Node IF thefunction
returntrue
Ohh, too hard. See example:
- In this case Node will be removed
1 | <tree |
- In this case if Node doesn’t have children it will be removed
1 | <tree |
The example above removes ONLY nodes that has checked
state (use DEL code on your keyboard).
Filtering
We do not know where to show the field for filtering, how to stylize it and so on… It depends on a situation.
So we decided to provide a powerfull API to handle it (the library don’t know about other components on the page and this is not necessary). See examples to understand ;)
Default props:
1 | { |
- emptyText - shows when nothing is found
- showChildren - this property hides the children of the node if they are available
- matcher - this function determines whether the node is suitable for condition
- plainList - this property breaks the tree structure and shows a plain list of matched results (but it easy to fix the structure by clearing filter :-D)
Async Data
There are two ways to set an async data:
- data property as a Promise. You can pass it as an array (a lot of examples above) or as a Promise-like object (object that has the then method)
- fetchData options. This option is flexible. See examples below.
minFetchDelay option - this option provides a minimum delay before rendering the children list. For example request takes 15 ms and you will not see the loading indicator. To see the indicator you need to set this property to 1000 (for example).
fetchData options:
- As a string. It is like a pattern. The construction in curly brackets is replaced with similar values in the node object
1 | { |
- As a function that returns a string. The same as above. It is run for each request.
1 | { |
- As a function that returns a promise-like object. You can do a request to server as you wish
1 | { |
Inline Editing
By default, when editing, a text box appears with the value of the node.
When you press Esc, the changes are canceled. When you press Enter or click on any area of the page, the changes are applied.
Events: check below
Manual editing (calls node API)
Editing via options
Just add an option editing
to the tree options.
You can add a state editable
for node to prevent editing:
1 | { |
Integration with Vuex
The library is allow to pass store
options. The tree is not update partially. It updates the whole tree items.
You must implement dispatcher
to update the tree.
Working with modules is identical. Vuex forbids to have more than 1 getter
with same name.
Example:
1 | const Store = new Vuex.Store({ |
Drag & Drop
Now there is only basic functionality of DND includes events (dragging:start
, dragging:finish
). Just add dnd
property to the tree options.
To more details see the Issue
Examples
JSON Viewer
I did this in 40 min … do not judge me strictly :)
In plans:
- online editor
- to reveal all the possibilities of slots
- process in real time
Custom Node
This example shows how to replace default content. It allows you to control content in any way. It is possible thanks to VueJS scoped slots.
Redefine Structure Example
For instance: we had a super-tree-component with its own structure (never mind, just for test). And we have a lot of dependencies to that component. This example shows how to apply data from server without a headache.
In this example we have structure:1
2
3[
MY_TEXT: 'some text', KIDS: []
]
A library doesn’t know this format. But we can add propertyNames
options and redefine structure. See example
Custom Theme
This example shows how to use tree in real life
Accordion
The library doesn’t have a such property. It uses provided API.
API
Tree API
To have directly access to the Tree API you have to use ref
property of component. See more Child-Component-Refs.
Tree.find(criteria, [multiple = false])
Arguments:
{ Object | String } criteria
{ Boolean } multiple
Returns:
Usage:
This method uses in every methods where you can find node by criteria. If
criteria
is passed as string it will be transformed like{ text: criteria }
.
Examples:1
2
3
4
5
6this.$refs.tree.find('Node Text') // It will find Node that has text 'Node Text'
this.$refs.tree.find(/Node Text/) // Using RegExp. It will find: Node Text ATAT, ATATA Node Text and so on...
this.$refs.tree.find({
text: /^Item 2/,
state: { checked: true, selected: true }
})
By default this method finds the first found node. You can add the second parameter and this function will return all found nodes.
Tree.findAll(criteria)
Arguments:
{ Object | String } criteria
Returns:
Usage:
This method is “syntactic sugar” of Tree.find(criteria, true)
Tree.selected()
Returns:
Usage:
You can get access to
selected
nodes and do everything you want with NodeAPI
Tree.checked()
Returns:
Usage:
You can get access to
checked
nodes and do everything you want with NodeAPI
Tree.append(criteria, node)
Arguments:
{ Object | Node } criteria
(see find method){ Object | String } node
Returns:
- Appended Node
- null
Usage:
This method allows you to append (add to the end of the list) new Node to the Tree.
There are 2 types of insertion:Set criteria and new Node. It will try to find Node (using criteria) and append as children of this Node. Example:
1
2
3
4this.$refs.tree.append(
{ text: 'My super Text' }, // search criteria
'New CHILD Node for "My super Text"' // this string will be converted to Node object with default state parameters
)Set only one argument (Node). In this way it will add new Node as
root
element. (Yeah, we are able to have more than one root element)1
2
3
4this.$refs.tree.append({
text: 'My NEW Node',
state: { selected: true }
})
Tree.prepend(criteria, node)
Usage:
This method has the same behaviour as
Tree.append
, but the point of insertion will be different (in the start of the list).
Tree.before(criteria, node)
Usage:
This method has behaviour the same as
Tree.append
, but the point of insertion will be different (before found node or the start of the list (as root)).
Tree.after(criteria, node)
Usage:
This method has behaviour the same as
Tree.append
, but the point of insertion will be different (after found node or the end of the list (as root)).
Tree.remove(criteria, multiple = false)
Arguments:
{ Object | String } criteria
(see find method){ Boolean } multiple
Returns:
Usage:
Remove Node by criteria.
Selection API
This array-like object has all array methods (forEach, map and so on) because it inherits Array
object. This collection has very similar behaviour with jQuery. All actions apply to all items in the collection. I’m going to show one example in more details and other methods have similar logic.
Selection.select(extendList)
Arguments:
{ Boolean } extendList
- inmultiple
mode it will add selected Node
Returns:
Usage:
It calls method
select
forall Nodes
in the collection. For instance:
1 | // Let's find Nodes which text starts with 'Java' and it's not disabled |
I think I should not explain behaviour of all methods. I hope it clear how it works.
Methods list:
- Selection.select() - select all nodes
- Selection.unselect() - unselect all nodes
- Selection.check() - check all nodes (if
checkbox
mode) - Selection.uncheck() - uncheck all nodes (if
checkbox
mode) - Selection.expand() - expand nodes (if node has children)
- Selection.collapse() - collapse nodes (if node has children)
- Selection.remove() - remove nodes
Node API
A Tree component consists of Nodes. Every Node is a VueJS component, which has a node
property linked to the Node class.
It is desirable not to work with the VueJS Node component directly. You have an API that returns a Node (not VueJS compoenent).
The tree mapping is based on the node states. Here are list of default states:
1 | const nodeStates = { |
I hope that every state speaks for itself.
To check state you can use:
Node.selected()
Node.checked()
Node.hidden()
Node.visible()
Node.enabled()
Node.disabled()
Node.expanded()
Node.collapsed()
Node.indeterminate()
Node.indeterminate() - If a Node has more than one checked Node it will return true
. Otherwise it returns false
We have reverse state checks and this is done purely for convenience:
Node.hidden()
andNode.visible()
Node.enabled()
andNode.disabled()
Node.collapsed()
andNode.expanded()
To change Node state:
Node.select(extendList)
Node.unselect()
Node.check()
Node.uncheck()
Node.show()
Node.hide()
Node.expand()
Node.disable()
Node.collapse()
Node.enable()
Node.toggleCollapse()
Node.toggleExpand()
For instance Node is checked. When you call Node.check()
it will not check Node again and not call node.checked
event. This condition applies to all of the above methods. You don’t need to:
1 | ... |
We have only 1 method which receives a single parameter. It is a Node.select(extendList)
. It this case if tree option multiple
is true it will be added to selectionNodes
list and user will see more than one selected Nodes. Example:
1 | // In our example we have one selected node. Using API we are able to find Node: |
Adding, removing, finding Nodes
You probably know jQuery and how it works with DOM objects. It is very similar.
Node.append(node)
//Node.addChild()
is an aliasNode.prepend(node)
Node.after(node)
Node.before(node)
These methods add children or insert nodes before/after.
Argument node
can be:
- simple text - it will be Node name with default states
- structured node object ( See Node Structure )
- Node object
Examples:
1 |
|
Node properties
Name | Type | Description |
---|---|---|
Node.parent | { Object | null } | Link to parent ** For root node it will be null |
Node.text | String | Node text. It can be HTML |
Node.depth | Int | Node depth. Depth for root nodes is 0 |
Node.tree | Object | Link to a Tree instance (not Vue component) |
Node.vm | Object | Link to a VueJS component |
Events
This example shows every possible event for the tree.