• Stars
    star
    148
  • Rank 249,983 (Top 5 %)
  • Language
    TypeScript
  • License
    MIT License
  • Created over 4 years ago
  • Updated 8 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Manipulating tree data structure for user interface

treemate · Coverage Status

https://treemate.vercel.app

All in one solution for tree structure in component developing.

It helps you manipulate tree data structure for user interface. (Can be used in Tree, Select, Dropdown, Table, Menu components and ...)

  1. check & uncheck nodes in the tree
  2. expand & collapse nodes in the tree
  3. move along tree nodes
  4. get flattened nodes
  5. query nodes
  6. support group node (group a set of nodes in the same tree level)
  7. support ignored node (ignored by move)
  8. meta info of nodes
  9. get the original node data ref
  10. async check
  11. ...

Installation

npm i -D treemate

Basic Concepts

Before you started, I strongly recommend you to read the section to get the basic concepts of treemate.

Node, Group Node and Ignored Node

In treemate, a tree is composed of node (optional group node and optional ignored).

A node contains a key, and maybe a children prop which includes its child node.

Node

Node {
  key,
  children?
}

Group Node

If you don't need group node, you can pass the section.

A group node contains a key, a type prop with value group and a children of its child node (can be a group node any more).

GroupNode {
  key,
  type: 'group',
  children
}

The group node itself will be ignored when moving along the nodes, and the children of the group node is view as the same level of the group node.

For example, in the following tree, the group child 1 will be viewed as node 1's next node. node 2 will be viewed as group child 2's next node.

- node 1
- group node 1
  - group child 1
  - group child 2
- node 2

Ignored Node

Some time's you may want to put some render only nodes in the tree. For example:

- node 1
- divider (render only)
- node 2

In data aspect, the divider node is meaning less. You can make it a ignored node. The ignored node will be ignored when moving along the nodes. (node 2 will be view the next node as node 1.) Also, getNode method won't return the ignored node.

An ignored node should also contains a key (for modern frontend framework to do efficient diff)

IgnoredNode {
  key,
  type: 'ignored'
}

Usage

Create a Treemate

createTreeMate method accepts a array of node as data. It returns a treemate instance.

In javascript:

import { createTreeMate } from 'treemate'

const data = [
  // non-leaf node
  {
    key: 1,
    children: [
      {
        key: 2
      }
    ]
  },
  // leaf node
  {
    key: 3
  },
  // ignored node
  {
    key: 4,
    type: 'ignored'
  },
  // group node
  {
    key: 5,
    type: 'group',
    children: [
      {
        key: 6
      }
    ]
  }
]

const treeMate = createTreeMate(data)

In typescript, the data looks almost same. However createTreeMate accepts 3 optional generic parameter to specify the types of node, group node and ignored node.

interface BaseNode {
  key: string | number
  children?: Array<BaseNode | GroupNode | IgnoredNode>
}

interface GroupNode {
  key: string | number
  type: 'group'
  children: Array<BaseNode | IgnoredNode>
}

interface IgnoredNode {
  key: string | number
  type: 'ignored'
}

// 1. specify all node types
const treeMate = createTreeMate<BaseNode, GroupNode, IgnoredNode>(data)

// 2. equals to createTreeMate<BaseNode, GroupNode, BaseNode>()
//    which mean no ignored node is in the data.
const treeMate = createTreeMate<BaseNode, GroupNode>(data)

// 3. equals to createTreeMate<BaseNode, BaseNode, BaseNode>()
//    which mean no ignored node and group node is in the data.
const treeMate = createTreeMate<BaseNode>(data)

// 4. without generic parameter
//    it will use a builtin node type as basic node type.
//    RawNode {
//      key: string | number
//      children?: RawNode[]
//      disabled?: boolean
//      isLeaf? boolean
//    }
//    ignored node and group node should be in the data.
const treeMate = createTreeMate(data)

Custom createTreeMate Options

If you want another way to determine a way to specify if a node's key or its disabled, group, ignored status. You can pass an option when create treemate.

const treeMate = createTreeMate(data, {
  getKey: (node) => Key,
  getDisabled: (node) => boolean,
  getIsGroup: (node) => boolean,
  getIgnored: (node) => boolean
})

Get a Node from Tree

Now suppose we have a treemate instance.

const tmNode = treeMate.getNode(key) // if not exist return null

// Caveat: getNode won't return group node & ignored node!
// If you do need to get them, you can use the treeNodeMap, eg:
treeMate.treeNodeMap.get(key)

Props of a TreeNode

TreeNode {
  key,
  rawNode, // hold the ref to the original data node, can be very useful
  level, // from 0
  index, // its index in its parent node (or root array)
  siblings,
  isFirstChild,
  isLastChild,
  parent, // its parent TreeNode (not data node)
  isShallowLoaded, // use when on partial data is loaded
  isLeaf,
  isGroup,
  ignored, // boolean
  disabled, // disabled
  children?, // its child TreeNodes (not data node)
  getPrev, // method
  getNext, // method
  getParent, // method
  getChild // method
}

Do Check and Uncheck in the Tree

TreeMate.getCheckedKeys(checkedKeys, options?)

Get checked status of the tree.

Node has disabled = true will be block cascade check's propagation.

Param checkedKeys has two forms:

Key[] // 1. currently checked keys

// 2. merged checked status
interface InputMergedKeys {
  checkedKeys?: Key[] | null
  indeterminateKeys?: Key[] | null // half checked
}

// it can also be
null | undefined
// viewed as an empty array

Param options looks like

interface CheckOptions {
  cascade?: boolean // cascade check status, default is true
  leafOnly?: boolean // whether only allow leaf node being checked, default is false
  checkStrategy?: string // set show strategy when checked 'all' | 'parent' | 'child'
}

Return value looks like

interface MergedKeys {
  checkedKeys: Key[]
  indeterminateKeys: Key[] // half checked
}
Usage
const { checkedKeys, indeterminateKeys } = treeMate.getCheckedKeys([1])
const { checkedKeys, indeterminateKeys } = treeMate.getCheckedKeys([1], {
  cascade: false
})
const { checkedKeys, indeterminateKeys } = treeMate.getCheckedKeys({
  checkedKeys: [1],
  indeterminateKeys: [2]
})
// ...

TreeMate.check(keysToCheck, checkedKeys, options?)

Get checked status of the tree after some nodes are checked.

keysToCheck could be Key | Key[] | null | undefined.

For checkedKeys, options and return value, see getCheckedKeys(checkedKeys, options?).

TreeMate.uncheck(keysToUncheck, checkedKeys, options?)

Get checked status of the tree after some nodes are unchecked.

keysToCheck could be Key | Key[] | null | undefined.

For checkedKeys, options and return value, see getCheckedKeys(checkedKeys, options?).

Do Move in the Tree

TreeMate.getPrev(key, options?)

Get the first previous not disabled sibling TreeMateNode of the key's corresponding node. In the traverse process, the group | ignored node itself will be dismissed. If node doesn't exist, return null.

options look like { loop?: boolean }. By default, loop is false, it won't loop when touches the last node.

TreeMate.getNext(key, options?)

Get the first next not disabled sibling TreeMateNode of the key's corresponding node. In the traverse process, the group | ignored node itself will be dismissed. If node doesn't exist, return null.

options look like { loop?: boolean }. By default, loop is false, it won't loop when touches the last node.

TreeMate.getParent(key)

Get the parent node of the key's corresponding node. In the traverse process, the group node itself will be dismissed. If node doesn't exist, return null.

TreeMate.getChild(key)

Get the first not disabled child node of the key's corresponding node. In the traverse process, the group | ignored node itself will be dismissed. If node doesn't exist, return null.

TreeNode.getPrev(options?)

Get the first previous not disabled sibling TreeMateNode. In the traverse process, the group node itself will be dismissed. If node doesn't exist, return null.

options look like { loop?: boolean }. By default, loop is false, it won't loop when touches the last node.

TreeNode.getNext(options?)

Get the first next not disabled sibling TreeMateNode. In the traverse process, the group | ignored node itself will be dismissed. If node doesn't exist, return null.

options look like { loop?: boolean }. By default, loop is false, it won't loop when touches the last node.

TreeNode.getParent()

Get the parent node of TreeMateNode. In the traverse process, the group node itself will be dismissed. If node doesn't exist, return null.

TreeNode.getChild()

Get the first not disabled child TreeMateNode. In the traverse process, the group | ignored node itself will be dismissed. If node doesn't exist, return null.

Do Expand & Collapsed on the Tree

Expand status is will influence the flattened nodes of the tree. The flattened nodes is crucial for virtual list.

TreeMate.getFlattenedNodes(expandedKeys?)

Returns the flattened tree nodes with corresponding expandedKeys. If expandedKeys is not provided, treemate will treat it as all expanded.

createIndexGetter(flattenedNodes)

Create an index getter from the flattenedNodes.

import { createIndexGetter } from 'treemate'

const getIndex = createIndexGetter(flattenedNodes)

getIndex(flattenedNodes[0].key) === 0

Get Path of a Node

TreeMate.getPath(key, options?)

Get the path from root to the node corresponding to the key. The return value looks like

interface MergedPath {
  keyPath: Key[]
  treeNodePath: TreeMateNode[]
  treeNode: TreeMateNode | null
}

The keyPath is the key of the nodes in path. The treeNodePath is the node path. treeNode is the TreeMateNode corresponding to the key.

options looks like { includeSelf?: boolean, includeGroup?: boolean }, by default includeSelf is true and includeGroup is false.

Get First Available Node of the Tree

Can be used to get the default pending status of a select menu.

TreeMate.getFirstAvailableNode()

Get the first not disabled TreeMateNode of the tree. In the traverse process, the group | ignored node itself will be dismissed. If node doesn't exist, returns null.

Other Props in TreeMate Instance

TreeMate.treeNodes

Corresponding TreeMateNode Array of original data. The tree structure is identical to the original data.

TreeMate.treeNodeMap

A map of key to tree node. Contains all nodes, including group | ignored node.

More Repositories

1

xicons

SVG Vue/React components integrated from fluentui-system-icons, ionicons, ant-design-icons, material-design-icons, Font-Awesome, tabler-icons and carbon icons. (Vue3, Vue2, React, SVG)
JavaScript
1,264
star
2

vueuc

Util Components for Vue
TypeScript
266
star
3

css-render

Generating CSS using JS with considerable flexibility and extensibility, at both server side and client side.
TypeScript
203
star
4

vfonts

CSS
72
star
5

lyla

A fully typed HTTP client with explicit behavior & error handling.
TypeScript
64
star
6

naive-ui-nuxt-demo

Vue
52
star
7

vooks

Utils Composable for Vue
TypeScript
47
star
8

naive-ui-vite-ssr

An example of naive-ui vite ssr.
JavaScript
36
star
9

evtd

Event delegation with native events and extended events.
TypeScript
23
star
10

component-tree-data-share

关于组件库树形数据的一次分享
22
star
11

seemly

TypeScript
21
star
12

nuxtjs-naive-ui

The official nuxt module for naive-ui that supports Nuxt.js SSR.
TypeScript
17
star
13

event-tracker-wrapper

Event tracker wrapper.
TypeScript
15
star
14

vdirs

Helper directives for Vue
TypeScript
14
star
15

vue3-programming-share

CSS
7
star
16

misclint

TypeScript
6
star
17

maintainable-vue-project-share

Vue
5
star
18

vue3-programming-share-2

CSS
5
star
19

naive-ui-vitepress-demo

This is a demo for using `naive-ui` in `vitepress` with SSR enabled.
TypeScript
4
star
20

demoup

TypeScript
3
star
21

irw

TypeScript
3
star
22

store4mp

A very basic data store manager library for mini program (mainly for cross-page usage)
TypeScript
3
star
23

v2s

Convert vue to script.
JavaScript
3
star
24

sstimer

A super simple timer that can start, stop & fire finish callback.
TypeScript
3
star
25

boom

JavaScript
2
star
26

07akioni

2
star
27

naming-convention-types

TypeScript
2
star
28

figma-plugin-image-info

JavaScript
2
star
29

create-merged-request

Merge scattered requests into one, distribute corresponding result to requestor.
TypeScript
2
star
30

maybe-a-bug-of-volar

Vue
1
star
31

dag-maker

A frontend for dagre & dagre-d3 that draws elegant DAG.
JavaScript
1
star
32

karma-boilerplate

JavaScript
1
star
33

taro-example

JavaScript
1
star
34

vue-create-ssr-app-issue

JavaScript
1
star
35

pkuhole-fetcher-pwa

An PWA for pkuhole-fetcher.
JavaScript
1
star
36

tailwind-color-interpolation

Linear interpolation color set for tailwind CSS
HTML
1
star
37

tex-assignment-template

TeX
1
star