# Tree API

## Introducing

EyzyTree includes a [basic API](basic) from under the hood. Much can be done with it. But sometimes you need to do something more complicated. In this case, you can use the **external API**.

A external API has to be included from separately file (module). It is similar to [Connection](../basic#connection) way the Basic API.

## Connection

CDN:

```markup
<script src="https://cdn.jsdelivr.net/npm/eyzy-tree/dist/api.js"></script>
```

As ES6 module:

```javascript
import EyzyTreeApi from "eyzy-tree/api"
```

Using:

```javascript
import React from 'react'
import EyzyTree from 'eyzy-tree'
import EyzyTreeApi from "eyzy-tree/api"

class AwesomeComponent extends React.PureComponent {
    api = null
    
    handleTreeReady = (treeApi) => {
        // it is important to pass the Basic API as an argument
        this.api = new EyzyTreeApi(treeApi)
    }
    
    render() {
        return <EyzyTree 
            data={data}
            onReady={this.handleTreeReady}
        />
    }
}
```

## Methods

### find

* **Description**: Finds nodes by specified [Query](query)
* **Arguments**:&#x20;
  * `query`: [Query](query)
* **Returns**:&#x20;
  * [NodeAPI](node-api)
* **Usage:**&#x20;

```javascript
const awesomeNode = this.api.find({text: 'Awesome Node'})

awesomeNode.addClass('really')
```

###

### findAll

* **Description**: Similar to `find` method. But it returns all found nodes.
* **Arguments**:&#x20;
  * `query`: [Query](query)
* **Returns**:&#x20;
  * [NodeAPI](node-api)
* **Usage:**&#x20;

```javascript
const awesomeNode = this.api.findAll({text: 'Awesome Node'})
```

###

### remove

* **Description**: Removes matched node.
* **Arguments:**
  * `query`: [Query](query)
  * `multiple`**:** boolean
* **Returns:**&#x20;
  * `TreeNode`
  * `TreeNode[]`
  * `null`
* **Usage:**

```javascript
const removedNode = this.api.remove(/bad nodes/)

if (isNodeRemoved) {
    alert(`Congrat... node "${removedNode.text}" has removed!`)
}

// it will remove all `checked` nodes
this.api.remove({ checked: true }, true)
```

###

### empty

* **Description**: It removes all children nodes. Also it removes `isBatch` property (see [Acync](../../guides/async#nodes-property))
* **Arguments:**
  * `query`: [Query](query)
  * `multiple`**:** boolean
* **Returns:**&#x20;
  * `boolean`
  * `null`
* **Usage:**

```javascript
const isNodeCleared = this.api.empty(/__test node__/)

if (isNodeCleared) {
    alert('Congrat... node has removed!')
} else {
    alert('Node doesn\'t has children')
}
```

###

### selected

{% hint style="info" %}
This method doesn't has a **multiple** argument. It depends on tree **multiple** prop.
{% endhint %}

* **Description**: It the same as [Basic:selected()](../basic#selected), but it returns a [NodeAPI](node-api)
* **Returns:**&#x20;
  * [NodeAPI](node-api)
* **Usage:**

```javascript
// remove all selected node(s)
this.api.selected().remove()
```

###

### select

{% hint style="info" %}
When using **extendSelection** argument it will not extend selection for single mode.

In case if you want to to **select** multiple nodes and **extend** selection you need to use next hook: [Multiple Selection](../useful-hooks#multiple-selection)
{% endhint %}

* **Description**: Selects matched node.
* **Arguments:**
  * `criteria`**:** [Find criteria](query)
  * `extendSelection`**:** boolean not required. For multiple mode adds the matched node to the list of selected items (it's like a selecting node via `Ctrl`)
  * `expandOnSelect` boolean, not required. `select` method will try to read this property from the tree props if **expandOnSelect** argument is not pass
* **Returns:**&#x20;
  * `boolean`
  * `null`
* **Usage:**

```javascript
this.api.select('Item 1')
this.api.select('Item 2', true) // will be selected: Item 1, Item 2
```

`expandOnSelect` example

```jsx
<EyzyTree 
    data={data}
    expandOnSelect={true}
/>

this.api.select(
    /node/,
    /* extend selection */ false,  
    /* expandOnSelect will be true (read from the tree) */
)

this.api.select(
    /node/,
    false,
    /* expandOnSelect is overrided */ false
)
```

###

### unselect

* **Description**: Removes Selection for matched node.
* **Arguments:**
  * `query`: [Query](query)
  * `multiple`**:** boolean
* **Returns:**&#x20;
  * **`boolean`**
* **Usage:**

```javascript
// will be returned false if node is not selected overwise true
this.api.unselect('Item 1')

// yeah, u can do it
this.api.unselect({ selected: true })

// unselect ALL selected nodes (similar to unselectAll method)
this.api.unselect({ selected: true }, true)
```

###

### unselectAll

* **Description**: Removes selection for all selected nodes

###

### checked

* **Description**: For `checkable` mode the library will combine checked nodes into an array. There are different ways to select "checked" nodes by using `valueConsistsOf` argument.
  * `ALL` (default) it will gather all "checked" nodes
  * `BRANCH` if node has children and it is checked (and all of the children nodes are checked) then all children nodes will be excluded from the result
  * `LEAF` returns nodes which doesn't has children nodes
  * `WITH_INDETERMINATE` the same as `ALL`plus indeterminate nodes
* **Arguments**:&#x20;
  * `valueConsistsOf`: 'ALL' | 'BRANCH' | 'LEAF' | 'WITH\_INDETERMINATE'
  * `showDisabled`: boolean (default **false**)
* **Returns**:&#x20;
  * [NodeAPI](node-api)
* **Usage:**&#x20;

```javascript
this.api.checked('LEAF').addClass(['className'])
```

###

### check

* **Description**: Sets matched node as ticked(checked).
* **Arguments:**
  * `query`: [Query](query)
  * `multiple`**:** boolean
* **Returns:**&#x20;
  * **`boolean`**
* **Usage:**

```javascript
this.api.check('Item 1') // -> true
this.api.check({ disabledCheckbox: true }) // -> false

this.api.check(/item/, true) // multiple search
```

###

### uncheck

* **Description**: Sets matched node as **not** ticked(checked).
* **Arguments:**
  * `query`: [Query](query)
  * `multiple`**:** boolean
* **Returns:**&#x20;
  * **`boolean`**
* **Usage:**

```javascript
this.api.uncheck('Item 1') // -> false, because node was unchecked


this.api.uncheck({ checked: true }, true)
```

###

### uncheckAll

* **Description**: Uncheck all ticked nodes.

###

### disable

* **Description**: Sets matched node as disabled.
* **Arguments:**
  * `query`: [Query](query)
  * `multiple`**:** boolean
* **Returns:**&#x20;
  * **`boolean`**
* **Usage:**

```javascript
this.api.disable('Item 1') // -> true
this.api.disable({ disabled: true }) // -> false, node already disabled
```

###

### enable

* **Description**: Sets matched node as enabled.
* **Arguments:**
  * `query`: [Query](query)
  * `multiple`**:** boolean
* **Returns:**&#x20;
  * **`boolean`**
* **Usage:**

```javascript
this.api.enable('Item 1') // -> false, node already enabled
this.api.enable({ disabled: true }, true) // -> true
```



### enableCheckbox

* **Description**: Sets checkbox state for matched node as enabled.
* **Arguments:**
  * `query`: [Query](query)
  * `multiple`**:** boolean
* **Returns:**&#x20;
  * **`boolean`**
* **Usage:**

```javascript
this.api.enableCheckbox('Item 1')
this.api.enableCheckbox({ disabledCheckbox: true }, true) // -> true
```



### disableCheckbox

* **Description**: Sets checkbox state for matched node as disabled.
* **Arguments:**
  * `query`: [Query](query)
  * `multiple`**:** boolean
* **Returns:**&#x20;
  * **`boolean`**
* **Usage:**

```javascript
this.api.disableCheckbox('Item 1')
this.api.disableCheckbox({ checkbable: true }, true)
```



### expand

* **Description**: Expands matched node.
* **Arguments:**
  * `query`: [Query](query)&#x20;
  * `multiple`**:** boolean
  * `includingDisabled`: boolean (default **false**).
* **Returns:**&#x20;
  * **`boolean`**
* **Usage:**

```javascript
this.api.expand('Leaf Node') // -> false, node doesn't has children
this.api.expand({ expanded: false }, true) // -> all expanded
```



### collapse

* **Description**: Collapses matched node.
* **Arguments:**
  * `query`: [Query](query)
  * `multiple`**:** boolean
  * `includingDisabled`: boolean (default **false**).&#x20;
* **Returns:**&#x20;
  * **`boolean`**
* **Usage:**

```javascript
this.api.collapse('Leaf Node') // -> false, node doesn't has children
this.api.collapse({ expanded: true}, true, true) // -> all nodes including disabled
```



### data

{% hint style="info" %}
You can store here whatever you want.&#x20;

**It is impossible to set/get data for multiple nodes via this method. But it very easy to do it via** [**NodeAPI::data**](../node-api#data)
{% endhint %}

* **Description**: It is getter/setter for the `data` property of the node.
* **Arguments:**
  * `query`: [Query](query)
  * `key`: string | object
  * `value` : any (not required)
* **Returns:**&#x20;
  * `undefined`
  * `data object`
  * [NodeAPI](node-api)
* **Usage:**

```jsx
// tree data
const data = [
    { text: 'Item 1', data: { isSuper: true } },
    { text: 'Item 2' },
    { text: 'Item 3' }
]

// it will find the first item (!)
this.api.data(/Item/, 'isSuper', false)
// or
// data is instead extended by shallow merge
// node.data will be { isSuper: false, isSuper2: false }
this.api.data(/Item/, { isSuper2: false })
```

{% hint style="info" %}
This method has different return results
{% endhint %}

```jsx
// tree data
const data = [
    { text: 'Item 1', data: { isSuper: true } },
    { text: 'Item 2' },
    { text: 'Item 3' }
]

// return a value: value
this.api.data(/Item/, 'isSuper')

// get full data object: { isSuper: true, isSuper2: 'string' }
this.api.data(/Item/)

// setter: return NodeAPI
this.api.data(/Item/, { isSuper2: false })

// setter: return NodeAPI
this.api.data(/Item/, 'isSuper2', 'string')

// as you can see it's possible you use API after setting the data
this.api
    .data(/Item/, 'isSuper3', 'string3')
    .expand()
```



### hasClass

* **Description**: Checks if a node has a `className`
* **Arguments:**
  * `query`: [Query](query)
  * `className`: string
* **Returns:**&#x20;
  * `boolean`
* **Usage:**

```javascript
const hasClass = this.api.hasClass(/excess/, 'highligted')
```



### addClass

* **Description**: It will add classNames for the node. This class will be added to the node with the class `.node-content` (check [Node's structure](../../customization#nodes-strcutrure))
* **Arguments:**
  * `query`: [Query](query)
  * `Array<className>`: string\[]
  * `multiple`**:** boolean
* **Returns:**&#x20;
  * `TreeNode` if node is found
  * `null` if node is not found
* **Usage:**

```javascript
this.api.addClass(/excess/, ['excess-node', 'you-can', 'pass', 'many', 'names'])
this.api.addClass(/excess/, ['red-border'], true)
```

###

### removeClass

* **Description**: The same behavior as `addClass` method, but it removes classNames
* **Arguments:**
  * `query`: [Query](query)
  * `Array<className>`: string\[]
  * `multiple`**:** boolean
* **Returns:**&#x20;
  * `TreeNode` if node is found
  * `null` if node is not found
* **Usage:**

```javascript
this.api.removeClass(/excess/, ['excess-node', 'you-can', 'pass', 'many', 'names'])
this.api.removeClass(/focused/, ['focused'], true)
```



### append

* **Description**: Inserts nodes to the end of the matched node
* **Arguments:**
  * `query`: [Query](query)
  * `nodes`: _string | object | Promise_
  * `opts`: [Insertion options](../../guides/async#insertion-options)
* **Returns:**&#x20;
  * `Promiselike<TreeNode[] | null>` added nodes (always an array)
* **Usage:**

```javascript
const appendedNodes = this.api.append(
    'Item 1', // find criteria
    ['Item 1.1.', 'Item 1.2'], // nodes can be as string
    { expand: true } // expand node with text 'Item 1' (if node was found)
)

this.api.append(
    { selected: true },
    [
        { text: 'Selected children 1', disabled: true },
        { text: 'Selected children 2' }        
    ]
)

// it also can be a function, but it must return a Promise
this.api.append(
    'Selected children 2',
    (node) => {
        // node.text === 'Selected children 2'
        return fetch(`/api?childName=${node.text}`)
            .then(response => response.json())
    }
)

this.api
    .append('Item 1', 'Item 1.3')
    .then(node => { // see Node API page
        node.select()
    })
```

###

### prepend

* **Description**: This method is similar to `append`. But nodes inserting to the beginning of children list. (well ... you know, as well as jQuery :) )
* **Example:**

```javascript
// tree structure
/**
item 1
item 2
 item 2.1
 item 2.2
item 3
*/

this.api.append('item 2', [
 'item 2.3', 'item 2.4', { text: 'item 2.5', checked: true }
])

// will be:
/**
item 1
 item 2.3    << new node
 item 2.4    << new node
 item 2.5    << new node
 item 2.1
 item 2.2
item 2
item 3
*/
```

###

### before

* **Description**: This method is similar to `append`. But nodes inserting **before** matched node.
* **Example:**

```javascript
// tree structure
/**
item 1
item 2
item 3
*/

this.api.before(/item 2/, 'item n')

// will be:
/**
item 1
item n <<<<<<< -- added node
item 2
item 3
*/
```

###

### after

* **Description**: This method is similar to `append`. But nodes inserting **after** matched node.

```javascript
// tree structure
/**
item 1
item 2
item 3
*/

this.api.before(/item 2/, 'item n')

// will be:
/**
item 1
item 2
item n <<<<<<< -- added node
item 3
*/
```
