View on GitHub

vue-tree

Yet another Vue treeview implementation.

Overview

License: MIT Build status

vue-tree is a Vue component that implements a TreeView control. Its aim is to provide common tree options in a way that is easy to use and easy to customize.

Features include:

Planned:

Installation

Install the component with your favorite package manager:

yarn add @grapoza/vue-tree

or

npm install --save @grapoza/vue-tree

The default import from this package is the components from the .vue files. In addition to this, pre-compiled versions of the TreeView component and CSS are also available in the package but you will need to reference them manually from your own project.

Usage

If you’re using it in a .vue file:

<template>
    <tree-view  id="my-tree" :initial-model="dataModel"></tree-view>
  </template>
  
  <script>
  import TreeView from "@grapoza/vue-tree"
  
  export default {
    components: {
      TreeView
    },
    data() {
      return {
        dataModel: [
          {id: "numberOrString", label: "Root Node", children: [
            {id: 1, label: "Child Node"},
            {id: "node2", label: "Second Child"}]
          }]
      }
    }
  }
  </script>

Or, import it into your application:

import TreeView from "@grapoza/vue-tree"
  Vue.use(TreeView)

Then add the component:

<tree-view id="my-tree" :initial-model="dataModel"></tree-view>
export default {
    data() {
      return {
        dataModel: [
          { id: "numberOrString", label: "Root Node", children: [
            { id: 1, label: "Child Node" },
            { id: "node2", label: "Second Child" }]
          }]
      }
    }
  }

Demos

To see it in action, try out the demos.

Tree Props

Prop Type Description Default value Required
initialModel Array The data model containing model data - Yes
customAriaKeyMap Object An object, the properties of which are arrays to keyCodes for various actions See Aria
modelDefaults Object An object containing defaults for all nodes that do not specify the given properties {}
selectionMode String How selection should operate (see Selection Mode) null (cannot select nodes)
skinClass String A class name to apply to the tree that specifies a skin to use (see Skins) "default-tree-view-skin"

Selection Mode

The selectionMode property defines how nodes should be selected within the treeview. Allowed values are null, single, selectionFollowsFocus, and multiple. Only nodes with the selectable model property set to true can be selected.

When clicking on a node, it is only selected if the click target was not interactive (e.g., clicking a checkbox or expander won’t select the node, but clicking a label will).

Model Data

The data passed to the treeview’s initialModel prop should be an array of nodes, where each node should have:

The treeNodeSpec of the objects in the data model passed to the treeview’s initialModel property will be updated by the treeview nodes themselves on creation to include missing properties.

{
    id: "node0",
    label: "A checkbox node",
    children: [],
    treeNodeSpec: {
      title: "This will be the value of the node text/label's 'title' attribute.",
      expandable: true,
      selectable: false,
      focusable: true,
      input: {
        type: 'checkbox'
      },
      state: {
        expanded: true,
        selected: false,
        input: {
          value: false,
          disabled: false
        }
      }
    }
  },
  {
    otherIdProp: "node1",
    textProp: "A radio button node",
    subThings: [],
    extraIrrelevantData: "February",
    treeNodeSpec: {
      idProperty: "otherIdProp", // Customize what model props are checked for the id, label, and children
      labelProperty: "textProp",
      childrenProperty: "subThings",
      expandable: true,
      selectable: false,
      input: {
        type: 'radio',
        name: 'rbGroup1',   // Used as the name attribute for the radio button
        value: 'thisValue', // Used as the value attribute for the radio button
        isInitialRadioGroupValue: true // Indicates this should be the initially selected value for the group
      },
      state: {
        expanded: true,
        selected: false
        // No input.value here; to let complex radio button groupings work, state value is
        // bound to an internal tree-level property. input.disabled, however, is valid here for radio buttons.
      },
      addChildCallback: () => Promise.resolve({ id: '1', label: 'label' })
    }
  }

The properties below can be specified for each node. Note that id, label, and children are the default properties nodes look for to get data, but the names of the properties can be overridden using the treeNodeSpec.

Prop Type Description Default value Required
id Number/String An ID that uniquely identifies this node within the tree - Yes
label String The text to show in the treeview - Yes
children Array<Object> The child nodes of this node []
treeNodeSpec Object The object containing data about the node’s capabilities and initial state See next table

The treeNodeSpec property contains any data about the node’s capabilities and its initial state. This keeps the tree-specific data separate from the data of the model itself. This makes it more convenient to drop data into the treeview as-is, potentially with a modelDefaults specified on the treeview to define common values used in the treeNodeSpec of every node.

Prop Type Description Default value Required
idProperty String The name of the property with a value that will be used as the node’s ID id
labelProperty String The name of the property with a value that will be used as the node’s label label
childrenProperty String The name of the property with a value that will be used as the node’s subnodes children
title String The text of the node’s text or label’s title attribute null
expandable Boolean True to show a toggle for expanding nodes’ subnode lists true
selectable Boolean True to allow the node to be selected false
deletable Boolean True to allow the node to be deleted false
focusable Boolean True to make the node the focus when the treeview is focused See Aria for details
expanderTitle String The text to use as the title for the expander button null
addChildTitle String The text to use as the title for the Add Child button null
deleteTitle String The text to use as the title for the Delete button null
input Object Contains data specific to the node’s input element null
input.type String The type of input; valid values are checkbox or radio - Yes*
input.name String The name attribute of the input; used with radio type 'unspecifiedRadioName'
input.value String The value attribute of the input; used with radio type label’s value**
input.isInitialRadioGroupValue Boolean Indicates this should be the initially selected value for the group null
state Object Contains the current state of the node -
state.expanded Boolean True if this node’s subnode list is expanded false
state.selected Boolean True if the node is selected false
state.input Object Contains any state related to the input field {} for checkbox, otherwise -
state.input.value Boolean Contains the value of the input false for checkbox, otherwise -
state.input.disabled Boolean True if the node’s input field is disabled false
customizations Object A customizations object {}
addChildCallback Function An async function that resolves to a new node model null

* If input.type is not supplied, input is forced to null.

** If input.value is not supplied, it defaults to the node’s label value replaced with the regular expression /[\s&<>"'\/]/g, ''

Default Data

If specified, the modelDefaults property of the treeview will be merged with node model’s treeNodeSpec data such that any data not explicitly specified for the node will be set to the value from modelDefaults. This is useful for situations where all (or most) nodes will use the same values. For instance, in a treeview that is all enabled, collapsed, unchecked checkboxes the user could use a modelDefaults of

{
    expandable: true,
    selectable: true,
    input: {
      type: 'checkbox',
    },
    state: {
      expanded: false,
      selected: false,
      input: {
        value: false,
        disabled: false
      }
    }
  }

Public Methods

Method Description Parameters Returns
getCheckedCheckboxes Gets models for checked checkbox nodes An Array<Object> of models for checked checkbox nodes
getCheckedRadioButtons Gets models for checked radio nodes An Array<Object> of models for checked radio button nodes
getSelected Gets models for selected nodes An Array<Object> of models for selected nodes
getMatching Gets models for nodes that match a function matcherFunction: A function that takes a node model and returns a boolean indicating whether that node should be returned. An Array<Object> of models for matched nodes

Events

Event Description Handler Parameters
treeViewNodeAdd Emitted when a node is added target The model of the target (child) node
parent The model of the parent node
event The original event
treeViewNodeClick Emitted when a node is clicked target The model of the target node
event The original event
treeViewNodeDblclick Emitted when a node is double clicked target The model of the target node
event The original event
treeViewNodeDelete Emitted when a node is deleted target The model of the target node
event The original event
treeViewNodeCheckboxChange Emitted when a node’s checkbox emits a change event target The model of the target node
event The original event
treeViewNodeRadioChange Emitted when a node’s radio button emits a change event target The model of the target node
event The original event
treeViewNodeExpandedChange Emitted when a node is expanded or collapsed target The model of the target node
event The original event
treeViewNodeSelectedChange Emitted when a node is selected or deselected target The model of the target node
event The original event

CSS Classes

The display of the treeview can be customized via CSS using the following classes. Class names are organized in a hierarchy, so a containing node’s class is the prefix of its child classes.

Class Affects
tree-view The top-level tree view list
tree-view-node A single node’s list item
tree-view-node-self-selected A selected node
tree-view-node-self The div containing the current node’s UI
tree-view-node-self-expander The button used to expand the children
tree-view-node-self-expanded Applied to the expander button when the node is expanded
tree-view-node-self-expanded-indicator The <i> element containing the expansion indicator
tree-view-node-self-spacer An empty spacer to replace fixed-width elements, e.g. the expander or checkbox
tree-view-node-self-label The label for the checkbox of checkable nodes
tree-view-node-self-input Any type of input field within the tree node
tree-view-node-self-checkbox The checkbox
tree-view-node-self-radio The radio button
tree-view-node-self-text The text for a non-input node
tree-view-node-self-action The action buttons (e.g., add child or delete)
tree-view-node-self-add-child-icon The <i> element containing the add child icon
tree-view-node-self-delete-icon The <i> element containing the delete icon
tree-view-node-children The list of child nodes

Customizing the TreeView

Customizations Property

It’s often helpful to be able to make adjustments to the markup or styles for the tree. You can provide an object to the modelDefaults.customizations property of the tree to set a customization affecting all nodes, or to the treeNodeSpec.customizations property of a single node. Node-specific customizations will override modelDefault customizations.

A customizations object may have the following properties:

Prop Type Description
classes Object Properties are classes to add for various parts of a node
classes.treeViewNode String Classes to add to a node’s list item
classes.treeViewNodeSelf String Classes to add to the div containing the current node’s UI
classes.treeViewNodeSelfSelected String Classes to add to the tree-view-node-self div if the node is selected
classes.treeViewNodeSelfExpander String Classes to add to the button used to expand the children
classes.treeViewNodeSelfExpanded String Classes to add to the expander button when the node is expanded
classes.treeViewNodeSelfExpandedIndicator String Classes to add to the <i> element containing the expansion indicator
classes.treeViewNodeSelfSpacer String Classes to add to the fixed-width spacer
classes.treeViewNodeSelfLabel String Classes to add to the label for the checkbox of checkable nodes
classes.treeViewNodeSelfInput String Classes to add to an input field
classes.treeViewNodeSelfCheckbox String Classes to add to the checkbox
classes.treeViewNodeSelfRadio String Classes to add to the radio button
classes.treeViewNodeSelfText String Classes to add to the text for a non-input node
classes.treeViewNodeSelfAction String Classes to add to the action buttons
classes.treeViewNodeSelfAddChild String Classes to add to the add child buttons
classes.treeViewNodeSelfAddChildIcon String Classes to add to the <i> element containing the add child icon
classes.treeViewNodeSelfDelete String Classes to add to the delete button
classes.treeViewNodeSelfDeleteIcon String Classes to add to the <i> element containing the delete icon
classes.treeViewNodeChildren String Classes to add to the list of child nodes

Skins

If adding classes isn’t enough, the entire default styles of the TreeView can be overridden using the skinClass property of the TreeView. When this property is set, the TreeView’s default class of default-tree-view-skin is replaced with your own class name, causing all of the built-in style selectors to not match the tree. Instead, you can create your own stylesheet or modify a copy of the default styles to achieve complete control over the tree styling.

Slots

Sometimes the entire content of a node (e.g., the checkbox or text) needs customization beyond what is available through classes. In this case, some slots are available in the TreeView to allow this customization.

Slot Name Description Props
text Replaces the span used for non-input content model - The TreeViewNode’s model
customClasses - Any custom classes specified in treeNodeSpec.customizations
checkbox Replaces the label and content used for checkboxes model - The TreeViewNode’s model
customClasses - Any custom classes specified in treeNodeSpec.customizations
inputId - The ID for the input (as generated by the TreeViewNode)
checkboxChangeHandler - The handler for checkbox change events. You should fire this on change.
radio Replaces the label and content used for radio buttons model - The TreeViewNode’s model
customClasses - Any custom classes specified in treeNodeSpec.customizations
inputId - The ID for the input (as generated by the TreeViewNode)
radioChangeHandler - The handler for radio button change events. You should fire this on change.

Example usage:

<tree-view id="customtree" :initial-model="model">
    <template #text="{ model, customClasses }">
      <marquee :title="model.treeNodeSpec.title" <!-- The tree view node's model is available -->
                class="tree-view-node-self-text" <!-- Built in classes and overrides are available -->
                :class="customClasses.treeViewNodeSelfText">
            {{ model[model.treeNodeSpec.labelProperty] }}
      <marquee>
    </template>
  
    <template #checkbox="{ model, customClasses, inputId, checkboxChangeHandler }">
      <label :for="inputId"
          :title="model.treeNodeSpec.title"
          class="tree-view-node-self-label"
          :class="customClasses.treeViewNodeSelfLabel">
  
        <input :id="inputId" <!-- The generated inputId for the node is available -->
               class="my-awesome-checkbox-class"
               type="checkbox"
               :disabled="model.treeNodeSpec.state.input.disabled"
               v-model="model.treeNodeSpec.state.input.value"
               @change="checkboxChangeHandler" /> <!-- The TreeViewNode change handler is available -->
  
        <blink>{{ "Slotted Content for " + model[model.treeNodeSpec.labelProperty] }}</blink>
      </label>
    </template>
  </tree-view>

Aria

ARIA Accessibility recommendations have been implemented at a basic level. This means keyboard navigation follows ARIA recommendations, but the component has not been tested with a screen reader and, since many screen readers exhibit different behaviors for treeview controls anyway, it would be expected to fail articulation checks in many cases. Additionally, some recommended keyboard controls are not implemented (e.g., Expand All Nodes, Type-ahead Focusing). When using the component, there are only a couple of things you need to know.

Setting Key Bindings

The keys used to navigate the treeview can be customized using the customAriaKeyMap prop of the TreeView component. The value of the prop is an object, with each attribute named for a type of action and its value as an Array of integer key codes that trigger that action.

Attribute Description Default value
activateItem Triggers the default action for input nodes (generally click) [32] (Space)
selectItem Selects the currently focused item [13] (Enter)
focusLastItem Sets focus to the last visible item in the tree [35] (End)
focusFirstItem Sets focus to the first visible item in the tree [36] (Home)
collapseFocusedItem Collapses the currently focused item, if expanded; otherwise focuses the parent node if one exists [37] (Left)
expandFocusedItem Expands the currently focused item, if collapsed; otherwise focuses the first child node if one exists [39] (Right)
focusPreviousItem Focuses the previous visible node* in the tree if one exists. [38] (Up)
focusNextItem Focuses the next visible node** in the tree if one exists. [40] (Down)
insertItem Fires the callback to add a new node if the callback exists [45] (Insert)
deleteItem Deletes the current node, if deletable [46] (Delete)

* The previous visible node is a)the focused node’s previous sibling’s last child if visible, b)the previous sibling if it has no children or is collapsed, or c)the parent node.

** The next visible node is a)the focused node’s first child if one exists and the focused node is expanded, b)the focused node’s next sibling if one exists, or c)the focused node’s parent’s next sibling if one exists.

Focusable

The treeview uses a roaming tab index to maintain focusability in the tree. A node model can specify a treeNodeSpec.focusable property of true in order for that node to be used as the initial target of keyboard focus within the treeview. If multiple node models specify this then only the first will have treeNodeSpec.focusable set to true once the treeview is intialized. If no models have it specified then the first selected node in the treeview is given a treeNodeSpec.focusable of true. If there are no selected nodes then the first node in the treeview is given a treeNodeSpec.focusable of true.

More about ARIA TreeViews

WAI-ARIA Authoring Best Practices, Tree View