包详细信息

inspire-tree

helion310kMIT7.0.16

Inspired JavaScript Tree UI Component

自述文件

Badge

Inspire Tree

Inspire Tree is a performance-driven UI tree component. This core library is "headless" (just an API, no DOM) but we do provide a DOM rendering library. We also have some basic example implementations in other popular libraries/frameworks: AngularJS, Angular2, React, and Inferno.

8.0 Overhaul & Modernization

Progress is currently underway to completely overhaul this repository for typescript support, monorepo design, better deployments, better tests, etc.

I have no plans to overhaul the API, only to modernize the code and improve it.

I don't have a timeline.

Features:

  • Detailed and fluent API.
  • Typescript Support.
  • Events everywhere.
  • Load data directly, via promises, or callbacks.
  • Load child nodes directly or dynamically (or a mix of both).
  • Tri-state checkboxes (optional).
  • Multiselect (optional).
  • Inline editing (optional).
  • Search by plain string, RegExp, custom matcher, or external resources (optional).
  • Sorting (optional).
  • AMD and CommonJS support (RequireJS, Node/Webpack).
  • Supports multiple instances on a single page.
  • API methods to simplify moving nodes between instances.
  • Solid coverage by automated tests.

Installation

  • Yarn: yarn add --dev inspire-tree or
  • NPM npm install --save-dev inspire-tree

If you're using InspireTree in a browser environment, and are not using a module bundler like Webpack, Browserify, rollup, etc, you'll need to ensure lodash is available.

Usage

At the very least you must provide data source

var tree = new InspireTree({
    data: [{
        text: 'A node'
    }]
});

Node objects must have at least a text property. Additional properties are listed below in "Node Configuration".

Usage with Inspire Tree DOM

If you're using our DOM rendering, you need to pass in two arguments: the tree instance, and a DOM target (css selector):

new InspireTreeDOM(tree, {
    target: '.tree'
});

For more information regarding InspireTreeDOM, see the README.

Data Loading and Initialization Errors

InspireTree will try to load nodes on initialization. If an error occurs, it will be caught in a promise that's cached as tree._loader.promise. This allows you to track the promise returned, which can't be returned from the InspireTree constructor. This behavior may change in future major releases.

Any calls to load-related methods can use the returned promise normally.

Tree Configuration

  • allowLoadEvents - Array of state-change events to fire for pre-set states.
  • checkbox
    • autoCheckChildren - Automatically check/uncheck children when parent toggled.
  • data - An array, promise, or callback function.
  • deferredLoading - Enable deferred loading. (See "Deferrals" section below.)
  • editable - Allow inline editing (requires inspire-tree-dom).
  • editing (defaults to true if editable is true)
    • add - Allow user to add nodes.
    • edit - Allow user to edit existing nodes.
    • remove - Allow user to remove nodes.
  • nodes
    • resetStateOnRestore - Reset node state to defaults when restored.
  • pagination
    • limit - How many nodes are rendered/loaded at once. Used with deferrals. Defaults to nodes which fit in the container.
  • search
    • matcher - Custom search executor (for custom/external handling of entire search). Must be a function which accepts a query and resolve/reject arguments.
    • matchProcessor - Custom handler for search matches. Must be a function which accepts a TreeNodes argument.
  • selection
    • allow - Dynamically determine selectable boolean for a node.
    • autoDeselect - Prevent automatic deselection.
    • autoSelectChildren - Auto-select children when a parent node is selected, regardless of their visibility.
    • disableDirectDeselection - Disallow deselecting a node by clicking on it while selected.
    • mode - default or checkbox. Checkbox mode auto-selects children, doesn't auto deselect.
    • multiple - Allow multiple nodes to be selected at a time.
    • require - Require at least one selected node.
  • sort - Property to sort by, or a custom sort function.

Node Configuration

  • text - Text used in display.
  • id - Unique ID. If missing, one will be generated.
  • children - An array of child nodes.
  • itree - An object used to describe initial tree values:

    • a.attributes - Custom attributes for this node's a.
    • icon - Custom icon for the anchor.
    • li.attributes - Custom attributes for this node's root li.
    • state.checked - Set checked state.
    • state.collapsed - Set initial collapsed state.
    • state.draggable - Allows this node to be dragged, if supported*.
    • state.drop-target - Allows node to be a drop target, if supported*.
    • state.editable - Allow user editing of node text, if supported*.
    • state.focused - Node has focus, if supported*.
    • state.hidden - Set initial visibility.
    • state.indeterminate - Set indeterminate state. May be overridden if all or zero children checked.
    • state.loading - Dynamic load of children in progress.
    • state.matched - Node was matched by a search.
    • state.removed - Soft removed. Never shown until restored.
    • state.rendered - Whether node has been rendered, if supported*.
    • state.selectable - Allow selection.
    • state.selected - Set initial selection.
  • Value applies natively to InspireTreeDOM, and will require added support if you're using a custom DOM renderer.

Some internal-use-only states are not listed.

Events

Events are triggered to inform you of changes or user interaction. Listeners are always registered on tree.on. Methods available in our event system are described at EventEmitter2.

tree.on('node.added', function(event, node) {
    // node added!
});

Event List

  • changes.applied - (InspireTree | TreeNode context) - Indicates batched changes are complete for the context.
  • children.loaded - (TreeNode node) - Children were dynamically loaded for a node.
  • data.loaded - (Array nodes) - Data has been loaded successfully (only for data loaded via xhr/callbacks).
  • data.loaderror - (Error err) - Loading failed.
  • model.loaded - (Array nodes) - Data has been parsed into an internal model.
  • node.added - (TreeNode node) - Node added.
  • node.blurred - (TreeNode node, bool isLoadEvent) - Node lost focus.
  • node.checked - (TreeNode node, bool isLoadEvent) - Node checked.
  • node.collapsed - (TreeNode node) - Node collapsed.
  • node.deselected - (TreeNode node) - Node deselected.
  • node.edited - (TreeNode node), (string oldValue), (string newValue) - Node text was altered via inline editing.
  • node.expanded - (TreeNode node, bool isLoadEvent) - Node expanded.
  • node.focused - (TreeNode node, bool isLoadEvent) - Node focused.
  • node.hidden - (TreeNode node, bool isLoadEvent) - Node hidden.
  • node.moved - (TreeNode node, TreeNodes source, int oldIndex, TreeNodes target, int newIndex) - Node moved.
  • node.paginated - (TreeNode context), (Object pagination) (Event event) - Nodes were paginated. Context is undefined when for the root level.
  • node.property.changed - (TreeNode node), (String property), (Mixed oldValue), (Mixed) newValue) - A node's root property has changed.
  • node.removed - (object node) - Node removed.
  • node.restored - (TreeNode node) - Node restored.
  • node.selected - (TreeNode node, bool isLoadEvent) - Node selected.
  • node.state.changed - (TreeNode node), (String property), (Mixed oldValue), (Mixed) newValue) - A node state boolean has changed.
  • node.shown - (TreeNode node) - Node shown.
  • node.softremoved - (TreeNode node, bool isLoadEvent) - Node soft removed.
  • node.unchecked - (TreeNode node) - Node unchecked.

API Basics

Each Inspire Tree instance returns an API object.

All methods not specific to existing node/s are found directly on the API:

tree.addNode({ text: 'Example' });

TreeNode

Each incoming javascript object is wrapped as a TreeNode, which aliases methods useful on a single node.

tree.node('a-unique-id').select();

TreeNodes

Multiple TreeNodes are contained within an Array-like object. The TreeNodes class extends Array, but we currently only provide official tests/support for Array prototype methods available in all of our target browsers.

However, there are a few exceptions:

  • lastIndexOf cannot be applied because nodes may not be duplicated. Use indexOf.

TreeNodes maps several TreeNode methods so you can invoke them on all nodes in the collection, or recursively down the nodes and their children.

Expands only root nodes inside this collection:

tree.nodes().expand();

Expands root nodes and their children inside this collection:

tree.nodes().expandDeep();

Global TreeNodes Methods

Most TreeNodes methods are mapped to the tree instance to ease working with all nodes. Instead of using tree.nodes().someMethod() you can use tree.someMethod().

Deferrals

For those working with massive datasets, InspireTree offers several additional features to help reduce initial load burdens.

For Deferred Rendering, see the InspireTreeDOM package.

Deferred Loading

Deferred Loading works exactly like deferred rendering, except nodes are loaded in paginated chunks.

  1. Enable deferredLoading in the config.
  2. Pass a count of total nodes to the data callback. This way, inspire tree knows how many nodes remain.
data: function(node, resolve, reject, pagination) {
    // nodes = subset of total nodes, 1000 = total rows
    resolve(nodes, 1000);
}
  1. Set pagination.limit in the config if the default doesn't suit you.
  2. A fourth object, pagination is passed to the data callback. It contains the current limit/total for the context being loaded. If node is undefined, the pagination object refers to root level nodes, otherwise it refers to children of the node.

Note: Deferred rendering and loading may be used together but there's no reason to.

Custom Rendering

While Inspire Tree provides a fast virtual DOM engine for element rendering, there are times when you need your own. Useful for integrating with existing engines like Angular, React, etc.

All you need is this core API package, and to listen to the changes.applied event.

Inspire Tree offers data change batching and fires the changes.applied event when multiple changes are complete.

When the event emits, you may trigger an updated render through your existing view layer.

Terminology

  • available - Node is not soft-removed or hidden.
  • deepest - Nodes without any children.
  • visible - Node is visible to the user. It's ancestors are not hidden/collapsed/removed.

Troubleshooting

Recursing Flattened Arrays

Methods which return flattened arrays intentionally leave their hierarchy pointers intact. This means that while you have a flat array, you also have reference to parent/child elements. This can interfere with recursive methods because they'll iterate both the array elements and their children.

This will only impact a small number of methods. For example:

tree.available().deepest();

... will duplicate nodes because deepest iterates the array and recurses through children.

Possible solutions:

  • Reverse the order: tree.deepest().available()
  • Avoid flattened arrays: tree.available(true).deepest()

更新日志

Changes to Inspire Tree

7.0.16

  • Added auto selection on search/clearSearch when selection required.

7.0.15

  • Added selection.autoSelectOnNodeRemoval configuration option.

7.0.14

  • Fixed selecting a node when selection is required and the only selected node is removed.

7.0.13

  • Fixed distribution build.

7.0.12

  • Added tree.cacheSelectedNodes() and tree.previouslySelectedNodes() to return nodes selected before the most recent.
  • Fixed defineProperty use on clone when parent key is excluded.
  • Removed a console.log.

7.0.11

  • Fixed itree.parent being lost on tree node clone.

7.0.10

  • Additional fixes for recursive keys, and fixed recursion test oversight.

7.0.9

  • Made TreeNode#context and itree#parent fields unenumerable to prevent recursion errors from libraries that deeply iterate object properties.

7.0.8

  • Fixed selectFirstAvailableNode to look deeply.

7.0.7

  • Fixed selectFirstAvailableNode trying to select unselectable nodes.

7.0.6

  • Fixed typo in method type.

7.0.5

  • Fixed careless mistake placing unshiftChild method on the wrong interface.

7.0.4

  • Added missed type definition for unshiftChild.

7.0.3

  • Added TreeNode.unshiftChild(child).

7.0.2

  • Fixed inconsistent TreeNode.tree return.

7.0.1

  • Added cancelEditOnBlur config.

7.0.0

  • Clears lastSelectedNode if that node is deselected.
  • Fixed tree.splice not mapping nodes splice correctly.
  • Updated dependencies.

Breaking Changes

v7 drops Promise polyfills, so older browser without native Promise support are no longer supported.

v7 will be the LTS version pre-v8. It will effectively be maintenance work only.

7.0.0 Beta.9

  • Added TreeNode#indexList for an array of context array indices.
  • Reworked Tree#boundingNodes to fix issues with sorting.
  • Fixed miscellaneous types.

7.0.0 Beta.8

  • Fixed miscellaneous types.

7.0.0 Beta.7

  • Fixed miscellaneous types.

7.0.0 Beta.6

  • Fixed miscellaneous types.

7.0.0 Beta.5

  • Fixed miscellaneous types.

7.0.0 Beta.4

  • Fixed dist files never being updated in any 7.0.0 builds. I forgot to run yarn deploy first...

7.0.0 Beta.3

  • Improved lodash imports.
  • Added node.contextmenu event to type definitions.

7.0.0 Beta.2

  • Improved node resolve type definitions.

7.0.0 Beta.1

Improvements to the typescript definition file:

  • Added TreeNode.hasLoadedChildren().
  • Added TreeNode.isOnlyRenderable().
  • Added support for user-defined state properties on itree in NodeConfig.
  • Fixed TreeNode.children definitions.

7.0.0 Beta.0

  • Fixed typescript definitions for data config property.
  • Added typescript entry for TreeNode.children.
  • Dropped Promise polyfill (Internet Explorer support).
  • Updated dependencies.

6.0.1

  • Fixes copy not cloning exported object, sharing object references.

6.0.0

  • Added TreeNodes.first and TreeNodes.last shallow search functions.
  • Added TreeNode.renderable, TreeNode.isFirstRenderable, TreeNode.isLastRenderable, and TreeNode.isOnlyRenderable check functions.
  • Moved batch/end/applyChanges to the TreeNodes level. Calling this on the tree as usual aliases the root-level model.
  • Updated TreeNodes method to call batch/end on their context in most cases, not the entire tree.
  • Added TreeNodes.context() as a parameter for changes.applied events. Some renderers could possibly ignore these if the context is a TreeNode.
  • Added a config object argument to the TreeNodes constructor. Internally, calculateRenderablePositions is only used for "renderable" TreeNodes.
  • Added pass-through of includeState parameter for copy/copyHierarchy methods which rely on toObject.
  • Minor cleanup.

Breaking Changes

Dirty

Internally, first/last/only "renderable" nodes are calculated on add/remove or position change operations (addNode, push, splice, sort, etc).

These are useful to rendering engines, and in order to communicate the change, nodes are marked as dirty when their first/last/only position changes.

This will cause dirty flags to be true for some nodes when it wasn't previously.

changes.applied

Batching is now handled contextually. Each TreeNodes collection may fire its own changes.applied event. The event now includes a context argument which will be the result of TreeNodes.context(). The context will either be the InspireTree object, or a parent TreeNode.

If your entire tree is rendered on this event, you can either ignore the event when fired for a specific node, or debounce it.

5.0.2

  • Fixed incorrect find method typescript definition.
  • Fixed minor spelling/linting errors.
  • Fixed duplicate rerender call on model reset.

5.0.1

  • Fixed parent checkbox not overriding indeterminate children.

5.0.0

  • Added TreeNode.assign.
  • Added TreeNodes.find.
  • Added TreeNodes.sortDeep.
  • Added state object argument support to TreeNode.state.
  • Fixed argument support for TreeNodes.invokeDeep.
  • Fixed typescript definition issues.
  • Switched API doc generator from docdown to jsdoc.

Breaking Changes

  • Removed { to() } return of copy methods. For example nodes.copy(dest) instead of nodes.copy().to(dest).

4.3.1

  • Fixed crypto error resulting from build tool regression.

4.3.0

  • Typescript definition file cleanup and improvements:
  • Added NodeConfig interface.
  • Imports EventEmitter2 definitions.
  • Various cleanup.

4.2.1

  • Improves typescript definitions.
  • Moves four npm dependencies to the dependencies array to support ES6 users.

4.2.0

  • Added includeState parameter to TreeNode.remove.
  • Added draggable and drop-target to node state objects.
  • Improved logic for calculating pagination totals when using arrays.
  • Fixed batch end error in TreeNode.states.

4.1.0

  • Added TreeNode.states which sets multiple states to a single value.
  • toObject now supports optionally exporting node state.
  • Fixed pagination totals for nested data loaded via array.

4.0.1

  • Added TreeNode@hasAncestor.

4.0.0

Breaking Changes

  • Removes string typing of node ID.

This means that you must query your nodes by ID using the same type as the ID was given to the tree. Using id: 1 means you must query by a number, tree.node(1). Previously, querying by a number or string would work. This change allows your data to remain typed, if you need it later.

Anything except numbers or strings will still be converted to a string using toString.

3.0.1

  • Added TreeNode.hasOrWillHaveChildren. Returns true when children are present or children property is true.

3.0.0

  • Added createNode method for creating a node, without adding it.
  • Added TreeNode.tree method.
  • Implemented tests and support for inherited Array prototype methods (excluding those not available in IE10+).
  • Updated typescript definition file to include Array methods.

Breaking Changes

  • Removed isNode alias of isTreeNode.
  • isTreeNode and isTreeNodes are now static methods.
  • Renamed filter to filterBy to avoid overriding Array.filter.
  • Renamed sort to sortBy to avoid overriding Array.sort.

2.0.6

  • Added isLoadEvent boolean to state events. true only when event fires on load and is in allowLoadEvents config.

2.0.5

  • Added TreeNodes.move.
  • Added TreeNodes.swap and TreeNode.swap.
  • Added event-related methods to typescript definition file.
  • Fixed empty arrays not being converted to TreeNodes instances.
  • Cached initial load promise on the tree rather than throwing its error.
  • Added safety check for invalid arrays on data load.

2.0.4

  • Clears existing data on load when not using deferred loading. Was broken with 1.11.

2.0.3

  • Addressed incorrect version in distribution file banners.

2.0.2

  • Added TreeNode.reload method.
  • Removed bower support.

2.0.1

  • Added isTreeNode alias to (and deprecated) isNode.
  • Fixed uglify-incompatible instance checks.

2.0.0

2.0 is an overhaul (and improvement) with how we manage and build InspireTree. The API has not changed aside from some additions, but how you instantiate the DOM has.

Most critically, the inspire-tree package is now only the core API. To use the official DOM renderer, you'll also need the inspire-tree-dom package. This allows us to keep the packages cleaner for those using their own view layer/rendering framework.

Overall, InspireTree is lighter because we now use rollup for bundling, which saves us 20k worth of code versus webpack.

We've ported our DOM rendering to Inferno, and are seeing up to 60% faster rendering (initial render of 10k nodes with Chrome 57 on macOS Sierra: 2265ms average with virtual-dom, 963ms average with Inferno).

  • Added a gzipped copy of distribution files.
  • Added Tree.pagination, TreeNodes.pagination, TreNode.pagination.
  • Added/exposed Tree.loadMore, TreeNodes.loadMore, TreNode.loadMore, which loads additional paginated nodes.
  • Fixed check call failing to reset indeterminate state.
  • Overhauled and added tests for deferral-related pagination.
  • Improved the efficiency of which uuid imports we're referencing.

Breaking Changes

  • Moved DOM code into a new package, inspire-tree-dom.
  • Rewrote the DOM engine with Inferno, replacing virtual-dom.

While there are no official API changes, the node.itree.ref.node Element reference is now just node.itree.ref.

  • We're no longer bundling lodash. If you're not using a bundler like Webpack, Browserify, or rollup, you'll need to include it as a dependency.

  • Removed native context menu support since the node.contextmenu event is passed through anyway.

  • Renamed node.paginate event to node.paginated.

1.0

For 1.0 changelogs, see the 1.x branch: https://github.com/helion3/inspire-tree/tree/1.x