1tree 中文文档教程

发布于 8年前 浏览 29 项目主页 更新于 3年前

1tree

1tree

一棵树统领一切,步行即可找到它们, 一棵树将它们全部带到节点图中并绑定它们。

A functional API for traversing and manipulating tree structures

  • extensive traversal and manipulation API (comparable to DOM or jQuery)
  • provide an adapter to any other tree structure by implementing a few simple functions and get the rest of the API for free
  • supports plugins to extend or modify functionality
  • small footprint, no external dependencies, fast
  • optimised for large graphs, full test suite

Quick start

npm install 1tree --save

创建一个基本树,其中每个节点只是一个字符串:

const Tree = require( '1tree' )

const root = Tree.createRoot( 'Animalia' )
const chordata = Tree.createNode( 'Chordata' )

root.append( chordata )

root.walk( n => console.log( n.value() ) )

API

Basics

创建一个树:

const root = Tree.createRoot( rootValue )

创建一个节点:

const node = Tree.createNode( value )

获取节点的底层实现:

const rawNode = node.get()

获取一个节点的值:

const value = node.value()

设置一个节点的值:

node.value( 'something' )

注意你给节点的值可以是任何东西,但我高度 建议您仅使用 JSON 可序列化对象,它使您的结果 图表更有用,因为您可以轻松地将其通过电线传递,将其存储在 数据库等。

对于大多数示例,我们只使用字符串作为节点的值。

使用对象文字的人为示例:

const root = Tree.createRoot({
  fileType: 'folder'
  name: 'Photos'
})

const selfie = Tree.createNode({
  fileType: 'file',
  name: 'selfie.jpg'
})

root.append( selfie )

Traversal

每个 API 方法示例上方的注释是函数签名 rtype notation

getChildren

获取当前节点

// getChildren() => [Node]

const children = node.getChildren()

childAt

的子节点 获取指定索引处的子

// childAt( index: Integer ) => childNode: Node

const second = node.childAt( 2 )

index

节点 获取该节点相对于其兄弟节点的索引

// index() => childIndex: Integer

const index = node.index()

firstChild

获取当前节点的第一个孩子获取当前节点

// firstChild() => childNode: Node

const first = node.firstChild()

lastChild

的最后一个孩子从当前节点

// lastChild() => childNode: Node

const last = node.lastChild()

walk

开始对树进行深度优先遍历

// walk( callback: Function ) => Void

node.walk( n => console.log( n.value() ) )

回调传递当前节点,当前节点的父节点和 相对于初始节点的深度。 您可以通过返回 truthy 来停止步行 来自回调的值。

// callback( current: Node, parent: Node, depth: Integer ) => stop: Boolean

node.walk( ( current, parent, depth ) => {
  console.log( current.value(), parent.value() )

  if( depth > 5 ) return true
})

walkUp

从当前节点向上遍历树,对每个节点执行回调 parent 直到它到达树的根或者回调返回一个 truthy 价值。 遍历从当前节点开始。

// walkUp( callback: Function ) => Void

node.walkUp( n => console.log( n.value() ) )

callback is passed only the current node:

// callback( current: Node ) => stop: Boolean

node.walkUp( n => {
  const value = n.value()

  console.log( value )

  if( value === 'Some Value' ) return true
})

find

从当前节点开始遍历树,返回匹配a的第一个节点 传入谓词函数。

// find( test: Predicate ) => Node

const target = node.find( n => n.value() === 'Some Value' )

console.log( target.value() )

findAll

从当前节点开始遍历树,返回所有匹配a的节点 传入谓词函数。

// findAll( test: Predicate ) => [Node]

const targets = node.findAll( n => n.value() === 'Some Value' )

targets.forEach( n => console.log( n.value() ) )

getParent

获取当前节点的父节点

// getParent() => parentNode: Node

const parent = node.getParent()

closest

查找匹配传入谓词的第一个节点,向上遍历 当前节点

// closest( test: Predicate ) => Node

const target = node.closest( n => n.value() === 'Some Value' )

console.log( target.value() )

ancestors

返回当前节点的所有祖先的列表,其中列表的头部 是当前节点的父节点,tail 是根节点。

// ancestors() => [Node]

const ancestors = node.ancestors()

ancestors.forEach( n => console.log( n.value() ) )

nextSibling

返回当前节点的下一个兄弟节点,或者 undefined 如果当前节点 是最后一个孩子。

// nextSibling() => siblingNode: Node

const next = node.nextSibling()

if( next ){
  console.log( 'Next sibling value is', next.value() )
} else {
  console.log( 'Current node is last child' )
}

previousSibling

返回当前节点的前一个兄弟节点,或者 undefined 如果当前节点 是第一个孩子。

// previousSibling() => siblingNode: Node

const prev = node.previousSibling()

if( prev ){
  console.log( 'Previous sibling value is', prev.value() )
} else {
  console.log( 'Current node is first child' )
}

siblings

返回当前节点的所有兄弟节点,不包括当前节点。 如果 当前节点是其父节点的唯一子节点,返回一个空数组。

// siblings() => [Node]

const siblings = node.siblings()

siblings.forEach( n => console.log( n.value() ) )

descendents

返回一个节点的所有子节点,它们的子节点等作为深度的平面数组 第一个订单。 如果节点没有子节点,则返回一个空数组。

// descendents() => [Node]

const descendents = node.descendents()

descendents.forEach( n => console.log( n.value() ) )

contains

返回一个布尔值,指示是否找到与谓词匹配的节点, 从当前节点向下搜索。

// contains( test: Predicate ) => Boolean

const hasValue = node.contains( n => n.value() === 'Some Value' )

hasChildren

返回一个布尔值,指示当前节点是否有任何子节点, 如果不是,则为 false(是叶节点)。

// hasChildren() => Boolean

const hasChildren = node.hasChildren()

isEmpty

返回一个布尔值,表示当前节点可以有 children - 注意,与hasChildren 不同,后者是节点是否存在 确实有孩子。 默认实现总是返回 false,但是 您可以覆盖它以制作仅叶节点。

// isEmpty() => Boolean

const isEmpty = node.isEmpty()

accepts

返回一个布尔值,指示当前节点是否可以接受另一个 节点。 还扩展了 insertBefore(并且根据定义所有其他插入 方法,因为它们都是基于 insertBefore) 构建的,因此会引发错误 如果您尝试添加父母无法接受的孩子。 默认值 实现只检查节点不是 isEmpty 但它可以被扩展 用于自定义行为。

// accepts( child: Node ) => Boolean

const accepts = node.accepts( child )

slug

返回一个节点的字符串,该字符串在其兄弟节点中是唯一的。 默认的 实现使用节点的 index 转换为字符串。

// slug() => String

const slug = node.slug()

getPath

返回表示从根到节点的路径的字符串。 路径是 由路径的 slug 中的每个节点与可选的连接在一起构成 分隔符字符串。 默认的分隔符字符串是 /。 鼻涕虫都不能 包含分隔符字符串,否则会抛出错误。

// getPath( separator: String = "/" ) => String

const path = node.getPath()
const path2 = node.getPath( '_' )

atPath

查找与指定路径匹配的节点。 默认的分隔符字符串是 /。 如果不使用默认分隔符,则必须使用与默认分隔符相同的分隔符 最初用于创建路径。 如果路径无效或没有节点匹配 path, undefined 将被返回。

// atPath( path: String, separator: String = "/" ) => Node | Undefined

const node = root.atPath( '/0/1/0/3/2' )

Manipulation

append

将新节点添加到当前节点的子列表的末尾。 如果 节点已经有一个父节点,它将从该父节点的子列表中删除。 退货 附加的节点。

// append( newNode: Node ) => newNode: Node

node.append( newNode )

insertBefore

在当前节点的子列表中插入一个新节点,在节点之前 作为参考节点提供。 如果新节点已经有父节点,则它是 从该父项的子项列表中删除。 返回插入的节点。

// insertBefore( newNode: Node, referenceNode: Node ) => Node

node.insertBefore( newNode, referenceNode )

remove

从其父节点中删除当前节点。 返回当前节点。

// remove() => removedNode: Node

const parent = node.getParent()

console.log( parent.getChildren().length )

node.remove()

console.log( parent.getChildren().length )

replaceChild

用新节点替换父节点的子列表中的引用节点。 如果 新节点已经有一个父节点,它将从该父节点的子列表中删除。 返回被替换的节点。

// replaceChild( newNode: Node, referenceNode: Node ) => replacedNode: Node

parentNode.replaceChild( newNode, referenceNode )

insertAt

在当前节点的子列表中的索引处插入一个新节点 指定的。 如果新节点已经有父节点,则将其从该节点中删除 父母的孩子名单。 返回插入的节点。

// insertAt( newNode: Node, index: Integer ) => newNode: Node

parentNode.insertAt( newNode, 2 )

insertAfter

在节点之后插入一个新节点到当前节点的子列表中 作为参考节点提供。 如果新节点已经有父节点,则它是 从该父项的子项列表中删除。 返回插入的节点。

// insertAfter( newNode: Node, referenceNode: Node ) => newNode: Node

node.insertAfter( newNode, referenceNode )

removeAt

删除指定索引处当前节点的子节点。 返回 删除节点。

// removeAt( index: Integer ) => removedNode: Node

const removedNode = node.removeAt( 2 )

empty

删除当前节点的所有子节点。 返回删除的数组 孩子们。

// empty() => removedNodes: [Node]

console.log( node.getChildren().length )

const removed = node.empty()

console.log( node.getChildren().length )

prepend

将新节点添加到当前节点的子列表的头部。 返回新的 节点。

// prepend( newNode: Node ) => newNode: Node

node.prepend( newNode )

unwrap

用当前节点及其兄弟节点替换当前节点的父节点。 返回移除的父节点。

// unwrap() => removedParentNode: Node

const oldParent = node.unwrap()

wrap

用新节点替换当前节点,然后将当前节点添加到 新节点的子列表。 返回新节点。

// wrap( newParent: Node ) => newParent: Node

node.wrap( newParentNode )

Miscellaneous

get

获取节点的底层实现——例如在使用 DOM 时 adapter 它可能指的是 HTMLElement 或类似

// get() => Any
const divElement = div.get()

console.log( divElement.tagName ) // "div"

serialize

的 返回包含当前节点及其所有子节点的单个对象, 每个节点看起来像:

{
  "value": ...,
  "children": [...]
}

在可能的情况下,您应该确保分配给节点的值是 JSON 可序列化对象,它会让您的生活变得更轻松很多

// serialize() => Object

const myTree = node.serialize()

db.save( 'myTree', myTree )

deserialize

采用以下形式的对象并返回一个节点:

{
  "value": ...,
  "children": [...]
}
// deserialize( obj: Object ) => Node

const obj = db.load( 'myTree' )
const node = Tree.deserialize( obj )

clone

克隆一个节点 - 要求 value 是 JSON 可序列化的。 返回一个新节点 具有与原始节点相同的值和子节点,但整个图是 一个新实例。

// clone() => clonedNode: Node

const cloned = node.clone()

meta

存储或检索有关未持久化的节点的元数据,例如不会更改 节点的基础值。 用于存储临时运行时 信息,例如当您在浏览器中可视化一棵树并允许 用户折叠或展开节点

// meta( name:string, value?:any ) => value:any

//set
node.meta( 'isCollapsed', true )

//get
console.log( node.meta( 'isCollapsed' ) )

adapters

const Dom = Tree.adapter( domAdapter )
const root = Dom.createRoot( 'My page title' )

creating an adapter

您可以创建适配器,允许您在任何树状支持上使用 API 结构。

您需要提供 1 到 5 个函数才能使适配器工作。 如果你 只提供getChildren,你会得到整个遍历API,但是 操作 API 需要 insertBeforeremove。 序列化器 函数需要 valuecreateNode

您还可以提供通常由 API,例如你的底层数据结构可能已经有一个更高效的 比 API 获取节点父节点的方式。

这些函数与 1tree API 中的消费者版本略有不同 他们接受更多的参数(API curries 额外的参数)—— 签名在这里以 rtype 格式显示:

fn 参数将在树 API 中传递给您,这样你可以调用其他API 来自适配器

getChildren( node: Node ) => [Node]
/*
  children should be an array, even if the underlying implementation is not, for
  example the DOM returns a variety of array-like objects, you should convert
  these to an array
*/

insertBefore( fn: Object[Function], rootNode: Node, currentNode: Node, newNode: Node, referenceNode: Node ) => newNode: Node
/*
  If referenceNode is not provided you should append the new node to the tail
  end of the current node's children instead. You should remove the new node
  from it's current parent if it already has one.
  (eg. fn.remove( fn, root, newNode ) ).
*/

remove( fn: Object[Function], rootNode: Node, currentNode: Node ) => removedNode: Node
/*
  fn is provided in case you need to for example, find the parent via
  fn.getParent or etc.
*/

value( currentNode: Node, value?: Any ) => value: Any
/*
  If called without the value parameter, it should return the "value" of the
  current node.

  If the value parameter is provided, it should set the "value" of the current
  node.

  It is best to have "value" be an abstraction of the underlying data stucture,
  and it is also wise to have that abstraction be JSON-serializable.

  For example, rather than returning an underlying DOM node directly, I would
  abstract it as:

  {
    "nodeType": 1,
    "tagName": "div",
    "attributes": [
      {
        "name": "id",
        "value": "myDiv"
      }
    ]
  }
*/

的原语:例如,查看 DOM 适配器

plugins

到它,从中删除,包装现有函数等。

using a plugin

const Tree = require( '1tree' )
const logPlugin = require( './log-plugin.js' )

Tree.plugin( logPlugin )

const root = Tree.createRoot( 'Animalia' )

root.log()

implementing a plugin

如果您的插件将函数附加到 fn 对象,您还应该附加一个 def 对象到每个提供一些元数据的函数,以便 您的插件可以从包装节点使用。 查看 defs 文件夹 对于内置函数中的 def 示例。

const logPlugin = fn => {
  const log = node => {
    console.log( fn.value( node ) )

    return node
  }

  log.def = {
    argTypes: [ 'node' ],
    returnType: 'node',
    requires: [ 'value' ],
    categories: [ 'log-plugin', 'plugin' ]
  }

  fn.log = log

  return fn
}

future

当节点可能需要异步或事件时如何遍历?

是否可以构建将所有函数调用包装为异步的适配器或插件 必要时使用与 wrap-nodes 插件类似的技术?

1tree

1tree

One tree to rule them all, one walk to find them, One tree to bring them all and in the node graph bind them.

A functional API for traversing and manipulating tree structures

  • extensive traversal and manipulation API (comparable to DOM or jQuery)
  • provide an adapter to any other tree structure by implementing a few simple functions and get the rest of the API for free
  • supports plugins to extend or modify functionality
  • small footprint, no external dependencies, fast
  • optimised for large graphs, full test suite

Quick start

npm install 1tree --save

Create a basic tree where each node is just a string:

const Tree = require( '1tree' )

const root = Tree.createRoot( 'Animalia' )
const chordata = Tree.createNode( 'Chordata' )

root.append( chordata )

root.walk( n => console.log( n.value() ) )

API

Basics

Create a tree:

const root = Tree.createRoot( rootValue )

Create a node:

const node = Tree.createNode( value )

Get the node's underlying implementation:

const rawNode = node.get()

Get a node's value:

const value = node.value()

Set a node's value:

node.value( 'something' )

Note that the value that you give a node can be anything, but I highly recommend that you use only JSON-serializable objects, it makes your resultant graph more useful as you can easily pass it over the wire, store it in a database etc.

For most of the examples we just use a string as the node's value.

A contrived example using object literals:

const root = Tree.createRoot({
  fileType: 'folder'
  name: 'Photos'
})

const selfie = Tree.createNode({
  fileType: 'file',
  name: 'selfie.jpg'
})

root.append( selfie )

Traversal

The comment above each API method example is the function signature in rtype notation

getChildren

Get the child nodes of the current node

// getChildren() => [Node]

const children = node.getChildren()

childAt

Gets the child at the specified index

// childAt( index: Integer ) => childNode: Node

const second = node.childAt( 2 )

index

Gets the index of the node relative to its siblings

// index() => childIndex: Integer

const index = node.index()

firstChild

Get the first child of the current node

// firstChild() => childNode: Node

const first = node.firstChild()

lastChild

Get the last child of the current node

// lastChild() => childNode: Node

const last = node.lastChild()

walk

Do a depth-first traversal of the tree starting at the current node

// walk( callback: Function ) => Void

node.walk( n => console.log( n.value() ) )

The callback is passed the current node, the parent of the current node, and the depth relative to the inital node. You can halt the walk by returning a truthy value from your callback.

// callback( current: Node, parent: Node, depth: Integer ) => stop: Boolean

node.walk( ( current, parent, depth ) => {
  console.log( current.value(), parent.value() )

  if( depth > 5 ) return true
})

walkUp

Traverses the tree upwards from the current node, performing a callback on each parent until it reaches the root of the tree or the callback returns a truthy value. The traversal starts from the current node.

// walkUp( callback: Function ) => Void

node.walkUp( n => console.log( n.value() ) )

The callback is passed only the current node:

// callback( current: Node ) => stop: Boolean

node.walkUp( n => {
  const value = n.value()

  console.log( value )

  if( value === 'Some Value' ) return true
})

find

Traverses the tree from the current node and returns the first node matching a passed in predicate function.

// find( test: Predicate ) => Node

const target = node.find( n => n.value() === 'Some Value' )

console.log( target.value() )

findAll

Traverses the tree from the current node and returns all nodes matching a passed in predicate function.

// findAll( test: Predicate ) => [Node]

const targets = node.findAll( n => n.value() === 'Some Value' )

targets.forEach( n => console.log( n.value() ) )

getParent

Gets the parent of the current node

// getParent() => parentNode: Node

const parent = node.getParent()

closest

Finds the first node matching the passed in predicate, traversing upwards from the current node

// closest( test: Predicate ) => Node

const target = node.closest( n => n.value() === 'Some Value' )

console.log( target.value() )

ancestors

Returns a list of all ancestors of the current node, where the head of the list is the current node's parent and the tail is the root node.

// ancestors() => [Node]

const ancestors = node.ancestors()

ancestors.forEach( n => console.log( n.value() ) )

nextSibling

Returns the next sibling of the current node, or undefined if the current node is the last child.

// nextSibling() => siblingNode: Node

const next = node.nextSibling()

if( next ){
  console.log( 'Next sibling value is', next.value() )
} else {
  console.log( 'Current node is last child' )
}

previousSibling

Returns the previous sibling of the current node, or undefined if the current node is the first child.

// previousSibling() => siblingNode: Node

const prev = node.previousSibling()

if( prev ){
  console.log( 'Previous sibling value is', prev.value() )
} else {
  console.log( 'Current node is first child' )
}

siblings

Returns all siblings of the current node, excluding the current node. If the current node is the only child of its parent, an empty array is returned.

// siblings() => [Node]

const siblings = node.siblings()

siblings.forEach( n => console.log( n.value() ) )

descendents

Returns all of a node's children, their children etc. as a flat array in depth first order. Returns an empty array if the node has no children.

// descendents() => [Node]

const descendents = node.descendents()

descendents.forEach( n => console.log( n.value() ) )

contains

Returns a boolean indicating whether a node matching the predicate was found, searching from the current node downwards.

// contains( test: Predicate ) => Boolean

const hasValue = node.contains( n => n.value() === 'Some Value' )

hasChildren

Returns a boolean indicating whether or not the current node has any children, or false if it doesn't (is a leaf node).

// hasChildren() => Boolean

const hasChildren = node.hasChildren()

isEmpty

Returns a boolean indicating whether or not the current node can have children - note, not the same as hasChildren, which is whether or not the node does have children. The default implementation always returns false, but you can override it to make leaf-only nodes.

// isEmpty() => Boolean

const isEmpty = node.isEmpty()

accepts

Returns a boolean indicating whether or not the current node can accept another node. Also extends insertBefore (and by definition all of the other insertion methods, as they are all built on insertBefore) so that an error is thrown if you try to add a child that the parent cannot accept. The default implementation only checks that the node is not isEmpty but it can be extended for custom behavior.

// accepts( child: Node ) => Boolean

const accepts = node.accepts( child )

slug

Returns a string for a node which is unique amongst its siblings. The default implementation uses the node's index converted to a string.

// slug() => String

const slug = node.slug()

getPath

Returns a string representing the path from the root to the node. The path is constructed of each node in the path's slug joined together with an optional separator string. The default separator string is /. None of the slugs may contain the separator string, or an error will be thrown.

// getPath( separator: String = "/" ) => String

const path = node.getPath()
const path2 = node.getPath( '_' )

atPath

Finds the node matching the specified path. The default separator string is /. If not using the default separator, the same separator must be used that was initially used to create the path. If the path is invalid or no node matched the path, undefined will be returned.

// atPath( path: String, separator: String = "/" ) => Node | Undefined

const node = root.atPath( '/0/1/0/3/2' )

Manipulation

append

Adds the new node to the tail end of the current node's child list. If the node already has a parent, it is removed from that parent's child list. Returns the node that was appended.

// append( newNode: Node ) => newNode: Node

node.append( newNode )

insertBefore

Inserts a new node into the current node's child list, before the node provided as a reference node. If the new node already has a parent, it is removed from that parent's child list. Returns the node that was inserted.

// insertBefore( newNode: Node, referenceNode: Node ) => Node

node.insertBefore( newNode, referenceNode )

remove

Removes the current node from its parent. Returns the current node.

// remove() => removedNode: Node

const parent = node.getParent()

console.log( parent.getChildren().length )

node.remove()

console.log( parent.getChildren().length )

replaceChild

Replaces the reference node in the parent's child list with the new node. If the new node already has a parent, it is removed from that parent's child list. Returns the node that was replaced.

// replaceChild( newNode: Node, referenceNode: Node ) => replacedNode: Node

parentNode.replaceChild( newNode, referenceNode )

insertAt

Inserts a new node into the current node's child list, at the index specified. If the new node already has a parent, it is removed from that parent's child list. Returns the node that was inserted.

// insertAt( newNode: Node, index: Integer ) => newNode: Node

parentNode.insertAt( newNode, 2 )

insertAfter

Inserts a new node into the current node's child list, after the node provided as a reference node. If the new node already has a parent, it is removed from that parent's child list. Returns the node that was inserted.

// insertAfter( newNode: Node, referenceNode: Node ) => newNode: Node

node.insertAfter( newNode, referenceNode )

removeAt

Removes the child of the current node at the specified index. Returns the removed node.

// removeAt( index: Integer ) => removedNode: Node

const removedNode = node.removeAt( 2 )

empty

Removes all of the current node's children. Returns an array of the removed children.

// empty() => removedNodes: [Node]

console.log( node.getChildren().length )

const removed = node.empty()

console.log( node.getChildren().length )

prepend

Adds the new node to the head of the current node's child list. Returns the new node.

// prepend( newNode: Node ) => newNode: Node

node.prepend( newNode )

unwrap

Replaces the parent of the current node with the current node and its siblings. Returns the removed parent node.

// unwrap() => removedParentNode: Node

const oldParent = node.unwrap()

wrap

Replaces the current node with the new node, and then adds the current node to the new node's child list. Returns the new node.

// wrap( newParent: Node ) => newParent: Node

node.wrap( newParentNode )

Miscellaneous

get

Gets the node's underlying implementation - for example when using the DOM adapter it would probably refer to an HTMLElement or similar

// get() => Any
const divElement = div.get()

console.log( divElement.tagName ) // "div"

serialize

Returns a single object containing the current node and all of its children, where each node looks like:

{
  "value": ...,
  "children": [...]
}

Where possible you should ensure that the values you assign to nodes are JSON-serializable objects, it will make your life a lot easier.

// serialize() => Object

const myTree = node.serialize()

db.save( 'myTree', myTree )

deserialize

Takes an object of the following form and returns a node:

{
  "value": ...,
  "children": [...]
}
// deserialize( obj: Object ) => Node

const obj = db.load( 'myTree' )
const node = Tree.deserialize( obj )

clone

Clones a node - requires that value is JSON-serializable. Returns a new node with the same value and children as the original node, but the entire graph is a new instance.

// clone() => clonedNode: Node

const cloned = node.clone()

meta

Store or retrieve metadata about a node that isn't persisted, eg doesn't change the underlying value of the node. Useful for storing temporary runtime information, like when you're visualising a tree in the browser and allow the user to collapse or expand nodes

// meta( name:string, value?:any ) => value:any

//set
node.meta( 'isCollapsed', true )

//get
console.log( node.meta( 'isCollapsed' ) )

adapters

const Dom = Tree.adapter( domAdapter )
const root = Dom.createRoot( 'My page title' )

creating an adapter

You can create adapters that allow you to use the API over any tree-like backing structure.

You need to provide between 1 and 5 functions for the adapter to work. If you only provide getChildren, you will get the whole traversal API, but the manipulation API requires insertBefore and remove. The serializer functions require value and createNode.

You can also provide implementations of other functions normally provided by the API, for example your underlying data structure may already have a more efficient way of getting the parent of a node than the API does.

These functions differ slightly from the consumer versions in the 1tree API in that they take more arguments (the API curries the extra arguments) - the signatures are shown here in rtype format:

The fn argument will pass you in the tree API, so that you can call other API primitives from your adapter:

getChildren( node: Node ) => [Node]
/*
  children should be an array, even if the underlying implementation is not, for
  example the DOM returns a variety of array-like objects, you should convert
  these to an array
*/

insertBefore( fn: Object[Function], rootNode: Node, currentNode: Node, newNode: Node, referenceNode: Node ) => newNode: Node
/*
  If referenceNode is not provided you should append the new node to the tail
  end of the current node's children instead. You should remove the new node
  from it's current parent if it already has one.
  (eg. fn.remove( fn, root, newNode ) ).
*/

remove( fn: Object[Function], rootNode: Node, currentNode: Node ) => removedNode: Node
/*
  fn is provided in case you need to for example, find the parent via
  fn.getParent or etc.
*/

value( currentNode: Node, value?: Any ) => value: Any
/*
  If called without the value parameter, it should return the "value" of the
  current node.

  If the value parameter is provided, it should set the "value" of the current
  node.

  It is best to have "value" be an abstraction of the underlying data stucture,
  and it is also wise to have that abstraction be JSON-serializable.

  For example, rather than returning an underlying DOM node directly, I would
  abstract it as:

  {
    "nodeType": 1,
    "tagName": "div",
    "attributes": [
      {
        "name": "id",
        "value": "myDiv"
      }
    ]
  }
*/

For an example, see the DOM adapter

plugins

A plugin is implemented as a function that takes the current tree API and adds to it, deletes from it, wraps an existing function etc.

using a plugin

const Tree = require( '1tree' )
const logPlugin = require( './log-plugin.js' )

Tree.plugin( logPlugin )

const root = Tree.createRoot( 'Animalia' )

root.log()

implementing a plugin

If your plugin attaches functions to the fn object, you should also attach a def object to each of those functions which provides some metadata so that your plugin can be used from a wrapped node. See the defs folder for examples of def in the built in functions.

const logPlugin = fn => {
  const log = node => {
    console.log( fn.value( node ) )

    return node
  }

  log.def = {
    argTypes: [ 'node' ],
    returnType: 'node',
    requires: [ 'value' ],
    categories: [ 'log-plugin', 'plugin' ]
  }

  fn.log = log

  return fn
}

future

How to traverse when nodes may require async or events?

Can an adapter or plugin be built that wraps all function calls to be async where necessary using similar technique to the wrap-nodes plugin?

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文