inspire-tree
Version:
Inspired JavaScript Tree UI Component
490 lines (431 loc) • 17.8 kB
TypeScript
import { EventEmitter2 } from 'eventemitter2';
/**
* Represents a generic callback which receives a single TreeNode argument.
*/
export interface NodeIteratee {
(node: TreeNode): any;
}
/**
* Represents a processor for nodes matched during search.
*/
export interface MatchProcessor {
(matches: TreeNodes): any;
}
/**
* Represents a search matcher which is given resolve/reject callbacks.
*/
export interface SearchMatcher {
(query: string, resolve: any, reject: any): any;
}
// Copied from EventEmitter2 to avoid imports, see below
interface Listener {
(...values: any[]): void;
}
interface EventAndListener {
(event: string | string[], ...values: any[]): void;
}
// Valid resolver return types
export type NodeConfigs = NodeConfig[]
export type NodeConfigsPromise = Promise<NodeConfigs | undefined>
// Preferred function type
export type NodeConfigsFunctionResolver = (node: TreeNode | null) => NodeConfigs | NodeConfigsPromise
// Effectively deprecated!
export type NodeConfigsResolver = (node: TreeNode | null, resolve: (nodes: Array<NodeConfig>) => void, reject: (err: Error) => void) => void
// All possible provider types
export type NodeConfigsProvider = NodeConfigs | NodeConfigsPromise | NodeConfigsFunctionResolver | NodeConfigsResolver;
/**
* Represents a tree configuration object, of which only "data" is required.
*/
export interface Config {
allowLoadEvents?: Array<string>;
cancelEditOnBlur?: boolean;
contextMenu?: boolean;
data: NodeConfigsProvider;
deferredLoading?: boolean;
editable?: boolean;
editing?: {
add?: boolean;
edit?: boolean;
remove?: boolean;
};
nodes?: {
resetStateOnRestore?: boolean;
};
pagination?: {
limit?: number;
};
renderer?: any;
search?: {
matcher: SearchMatcher;
matchProcess: MatchProcessor;
};
selection?: {
allow?: NodeIteratee;
autoDeselect?: boolean;
autoSelectChildren?: boolean;
autoSelectOnNodeRemoval?: boolean;
disableDirectDeselection?: boolean;
mode?: string;
multiple?: boolean;
require?: boolean;
reselectOnSearchClear?: boolean;
selectOnSearch?: boolean;
};
sort?: string;
}
export interface NodeConfig {
children?: Array<NodeConfig>|boolean,
id?: string,
text: string,
itree?: {
a?: {
attributes?: any
},
icon?: string,
li?: {
attributes?: any
},
state?: {
checked?: boolean,
collapsed?: boolean,
draggable?: boolean,
'drop-target'?: boolean,
editable?: boolean,
focused?: boolean,
hidden?: boolean,
indeterminate?: boolean,
loading?: boolean,
matched?: boolean,
removed?: boolean,
rendered?: boolean,
selectable?: boolean,
selected?: boolean,
[x: string]: boolean | undefined
}
}
}
/**
* Represents a TreeNodes pagination configuration object.
*/
export interface Pagination {
limit: number;
total: number;
}
export interface TreeEvents {
/** @event changes.applied (InspireTree | TreeNode context) - Indicates batched changes are complete for the context. */
'changes.applied'?: (context: InspireTree | TreeNode) => void;
/** @event children.loaded (TreeNode node) - Children were dynamically loaded for a node. */
'children.loaded'?: (node: TreeNode) => void;
/** @event data.loaded (Array nodes) - Data has been loaded successfully (only for data loaded via xhr/callbacks). */
'data.loaded'?: (nodes: any[]) => void;
/** @event data.loaderror (Error err) - Loading failed. */
'data.loaderror'?: (err: Error) => void;
/** @event model.loaded (Array nodes) - Data has been parsed into an internal model. */
'model.loaded'?: (node: TreeNode) => void;
/** @event node.added (TreeNode node) - Node added. */
'node.added'?: (node: TreeNode) => void;
/** @event node.click (TreeNode node) - Node click. */
'node.click'?: (node: TreeNode) => void;
/** @event node.contextmenu (TreeNode node) - Node contextmenu click. */
'node.contextmenu'?: (node: TreeNode) => void;
/** @event node.blurred (TreeNode node, bool isLoadEvent) - Node lost focus. */
'node.blurred'?: (node: TreeNode, isLoadEvent: boolean) => void;
/** @event node.checked (TreeNode node, bool isLoadEvent) - Node checked. */
'node.checked'?: (node: TreeNode, isLoadEvent: boolean) => void;
/** @event node.collapsed (TreeNode node) - Node collapsed. */
'node.collapsed'?: (node: TreeNode) => void;
/** @event node.deselected (TreeNode node) - Node deselected. */
'node.deselected'?: (node: TreeNode) => void;
/** @event node.edited (TreeNode node), (string oldValue), (string newValue) - Node text was altered via inline editing. */
'node.edited'?: (node: TreeNode) => void;
/** @event node.expanded (TreeNode node, bool isLoadEvent) - Node expanded. */
'node.expanded'?: (node: TreeNode, isLoadEvent: boolean) => void;
/** @event node.focused (TreeNode node, bool isLoadEvent) - Node focused. */
'node.focused'?: (node: TreeNode, isLoadEvent: boolean) => void;
/** @event node.hidden (TreeNode node, bool isLoadEvent) - Node hidden. */
'node.hidden'?: (node: TreeNode, isLoadEvent: boolean) => void;
/** @event node.moved (TreeNode node, TreeNodes source, int oldIndex, TreeNodes target, int newIndex) - Node moved. */
'node.moved'?: (node: TreeNode) => void;
/** @event node.paginated (TreeNode context), (Object pagination) (Event event) - Nodes were paginated. Context is undefined when for the root level. */
'node.paginated'?: (node: TreeNode) => void;
/** @event node.propertchanged - (TreeNode node), (String property), (Mixed oldValue), (Mixed) newValue) - A node's root property has changed. */
'node.property.changed'?: (node: TreeNode) => void;
/** @event node.removed (object node) - Node removed. */
'node.removed'?: (node: TreeNode) => void;
/** @event node.restored (TreeNode node) - Node restored. */
'node.restored'?: (node: TreeNode) => void;
/** @event node.selected (TreeNode node, bool isLoadEvent) - Node selected. */
'node.selected'?: (node: TreeNode, isLoadEvent: boolean) => void;
/** @event node.statchanged - (TreeNode node), (String property), (Mixed oldValue), (Mixed) newValue) - A node state boolean has changed. */
'node.state.changed'?: (node: TreeNode) => void;
/** @event node.shown (TreeNode node) - Node shown. */
'node.shown'?: (node: TreeNode) => void;
/** @event node.softremoved (TreeNode node, bool isLoadEvent) - Node soft removed. */
'node.softremoved'?: (node: TreeNode, isLoadEvent: boolean) => void;
/** @event node.unchecked (TreeNode node) - Node unchecked. */
'node.unchecked'?: (node: TreeNode) => void;
}
export interface InspireTree {
emit<E extends keyof TreeEvents>(event: E | E[], ...values: any[]): boolean;
emitAsync<E extends keyof TreeEvents>(event: E | E[], ...values: any[]): Promise<any[]>;
addListener<E extends keyof TreeEvents>(event: E, listener: TreeEvents[E]): this;
on<E extends keyof TreeEvents>(event: E, listener: TreeEvents[E]): this;
prependListener<E extends keyof TreeEvents>(event: E | E[], listener: TreeEvents[E]): this;
once<E extends keyof TreeEvents>(event: E, listener: TreeEvents[E]): this;
prependOnceListener<E extends keyof TreeEvents>(event: E | E[], listener: TreeEvents[E]): this;
many<E extends keyof TreeEvents>(event: E | E[], timesToListen: number, listener: TreeEvents[E]): this;
prependMany<E extends keyof TreeEvents>(event: E | E[], timesToListen: number, listener: TreeEvents[E]): this;
onAny(listener: EventAndListener): this;
prependAny(listener: EventAndListener): this;
offAny(listener: Listener): this;
removeListener<E extends keyof TreeEvents>(event: E | E[], listener: TreeEvents[E]): this;
off<E extends keyof TreeEvents>(event: E, listener: TreeEvents[E]): this;
}
export class InspireTree extends EventEmitter2 {
addNode(node: NodeConfig): TreeNode;
addNodes(node: Array<NodeConfig>): TreeNodes;
autoSelectNode(): TreeNode | null;
available(): TreeNodes;
blur(): TreeNodes;
blurDeep(): TreeNodes;
boundingNodes(first: TreeNode, second: TreeNode): [TreeNode, TreeNode];
cacheSelectedNodes(): void;
canAutoDeselect(): boolean;
checked(): TreeNodes;
clean(): TreeNodes;
clearSearch(): InspireTree;
clone(): TreeNodes;
collapse(): TreeNodes;
collapsed(full?: boolean): TreeNodes;
collapseDeep(): TreeNodes;
constructor(opts: Config);
constructor(tree: InspireTree, array: Array<any>|TreeNodes);
constructor(tree: InspireTree);
context(): TreeNode;
copy(dest: InspireTree, hierarchy?: boolean, includeState?: boolean): TreeNodes;
createNode(obj: any): TreeNode;
deepest(): TreeNodes;
deselect(): TreeNodes;
deselectDeep(): TreeNodes;
disableDeselection(): InspireTree;
each(iteratee: NodeIteratee): TreeNodes;
editable(full?: boolean): TreeNodes;
editing(full?: boolean): TreeNodes;
enableDeselection(): InspireTree;
expand(): Promise<TreeNodes>;
expandDeep(): TreeNodes;
expanded(full?: boolean): TreeNodes;
expandParents(): TreeNodes;
extract(predicate: string|NodeIteratee): TreeNodes;
filterBy(predicate: string|NodeIteratee): TreeNodes;
find(predicate: (node: TreeNode, index?: number, obj?: TreeNode[]) => boolean, thisArg?: any): TreeNode;
first(predicate: (node: TreeNode) => boolean): TreeNode;
flatten(predicate: string|NodeIteratee): TreeNodes;
focused(full?: boolean): TreeNodes;
get(index: number): TreeNode;
hidden(full?: boolean): TreeNodes;
hide(): TreeNodes;
hideDeep(): TreeNodes;
id: string | number;
config: Config;
preventDeselection: boolean;
indeterminate(full?: boolean): TreeNodes;
insertAt(index: number, object: any): TreeNode;
invoke(methods: string|Array<string>): TreeNodes;
invokeDeep(methods: string|Array<string>): TreeNodes;
isEventMuted(eventName: string): boolean;
static isTreeNode(value: any): value is TreeNode;
static isTreeNodes(value: any): value is TreeNodes;
last(predicate: (node: TreeNode) => boolean): TreeNode;
lastSelectedNode(): TreeNode | undefined;
load(loader: Promise<TreeNodes>): Promise<TreeNodes>;
loading(full?: boolean): TreeNodes;
matched(full?: boolean): TreeNodes;
move(index: number, newIndex: number, target: TreeNodes): TreeNode;
mute(events: Array<string>): InspireTree;
muted(): boolean;
node(id: string|number): TreeNode;
nodes(ids?: Array<string>|Array<number>): TreeNodes;
pagination(): Pagination;
previouslySelectedNodes(): TreeNodes;
recurseDown(iteratee: NodeIteratee): TreeNodes;
reload(): Promise<TreeNodes>;
removeAll(): InspireTree;
removed(full?: boolean): TreeNodes;
restore(): TreeNodes;
restoreDeep(): TreeNodes;
search(query: string|RegExp|NodeIteratee): Promise<TreeNodes>;
select(): TreeNodes;
selectable(full?: boolean): TreeNodes;
selectBetween(start: TreeNode, end: TreeNode): InspireTree;
selectDeep(): TreeNodes;
selected(full?: boolean): TreeNodes;
selectFirstAvailableNode(): TreeNode;
show(): TreeNodes;
showDeep(): TreeNodes;
softRemove(): TreeNodes;
sortBy(sorter: string|NodeIteratee): TreeNodes;
state(key: string, val: boolean): TreeNodes;
stateDeep(key: string, val: boolean): TreeNodes;
swap(node1: TreeNode, node2: TreeNode): TreeNodes;
toArray(): Array<any>;
toArray(): Array<any>;
tree(): InspireTree;
unmute(events: Array<string>): InspireTree;
visible(full?: boolean): TreeNodes;
}
export class TreeNodes extends Array<TreeNode> {
addNode(node: NodeConfig): TreeNode;
available(): TreeNodes;
blur(): TreeNodes;
blurDeep(): TreeNodes;
checked(): TreeNodes;
clean(): TreeNodes;
clone(): TreeNodes;
collapse(): TreeNodes;
collapsed(full?: boolean): TreeNodes;
collapseDeep(): TreeNodes;
constructor(tree: InspireTree, array: Array<any>|TreeNodes);
constructor(tree: InspireTree);
context(): TreeNode;
copy(dest: InspireTree, hierarchy?: boolean, includeState?: boolean): TreeNodes;
deepest(): TreeNodes;
deselect(): TreeNodes;
deselectDeep(): TreeNodes;
each(iteratee: NodeIteratee): TreeNodes;
editable(full?: boolean): TreeNodes;
editing(full?: boolean): TreeNodes;
expand(): TreeNodes;
expandDeep(): Promise<TreeNodes>;
expanded(full?: boolean): TreeNodes;
expandParents(): TreeNodes;
extract(predicate: string|NodeIteratee): TreeNodes;
filterBy(predicate: string|NodeIteratee): TreeNodes;
find(predicate: (node: TreeNode, index: number, obj: TreeNode[]) => boolean, thisArg?: any): TreeNode;
flatten(predicate: string|NodeIteratee): TreeNodes;
focused(full?: boolean): TreeNodes;
get(index: number): TreeNode;
hidden(full?: boolean): TreeNodes;
hide(): TreeNodes;
hideDeep(): TreeNodes;
indeterminate(full?: boolean): TreeNodes;
insertAt(index: number, object: any): TreeNode;
invoke(methods: string|Array<string>): TreeNodes;
invokeDeep(methods: string|Array<string>): TreeNodes;
loading(full?: boolean): TreeNodes;
matched(full?: boolean): TreeNodes;
move(index: number, newIndex: number, target: TreeNodes): TreeNode;
node(id: string|number): TreeNode;
nodes(ids?: Array<string>|Array<number>): TreeNodes;
pagination(): Pagination;
recurseDown(iteratee: NodeIteratee): TreeNodes;
removed(full?: boolean): TreeNodes;
restore(): TreeNodes;
restoreDeep(): TreeNodes;
select(): TreeNodes;
selectable(full?: boolean): TreeNodes;
selectBetween(start: TreeNode, end: TreeNode): InspireTree;
selectDeep(): TreeNodes;
selected(full?: boolean): TreeNodes;
show(): TreeNodes;
showDeep(): TreeNodes;
softRemove(): TreeNodes;
sortBy(sorter: string|NodeIteratee): TreeNodes;
state(key: string, val: boolean): TreeNodes;
stateDeep(key: string, val: boolean): TreeNodes;
swap(node1: TreeNode, node2: TreeNode): TreeNodes;
toArray(): Array<any>;
tree(): InspireTree;
visible(full?: boolean): TreeNodes;
}
export interface TreeNode {}
export class TreeNode {
addChild(node: NodeConfig): TreeNode;
addChildren(nodes: Array<NodeConfig>): TreeNodes;
assign(...sources: object[]): TreeNode;
available(): boolean;
blur(): TreeNode;
check(shallow?: boolean): TreeNode;
checked(): boolean;
children: boolean | TreeNodes;
clean(): TreeNode;
clone(excludeKeys?: Array<string>): TreeNode;
collapse(): TreeNode;
collapsed(): boolean;
constructor(tree: InspireTree, source: any|TreeNode, excludeKeys: Array<string>);
constructor(tree: InspireTree, source: any|TreeNode);
constructor(tree: InspireTree);
text: string;
id: string;
itree?: NodeConfig['itree'];
context(): TreeNodes;
copy(dest: InspireTree, hierarchy?: boolean, includeState?: boolean): TreeNode;
copyHierarchy(excludeNode?: boolean, includeState?: boolean): TreeNode;
deselect(shallow?: boolean): TreeNode;
editable(): boolean;
editing(): boolean;
expand(): Promise<TreeNode>;
expanded(): boolean;
expandParents(): TreeNode;
focus(): TreeNode;
focused(): boolean;
getChildren(): TreeNodes;
getParent(): TreeNode | undefined;
getParents(): TreeNodes;
getTextualHierarchy(): Array<string>;
hasAncestor(): boolean;
hasChildren(): boolean;
hasLoadedChildren(): boolean;
hasLoadedOrWillLoadChildren(): boolean;
hasOrWillHaveChildren(): boolean;
hasParent(): boolean;
hasVisibleChildren(): boolean;
hidden(): boolean;
hide(): TreeNode;
indeterminate(): boolean;
indexList(): Array<number>;
indexPath(): string;
isFirstRenderable(): boolean;
isLastRenderable(): boolean;
isOnlyRenderable(): boolean;
lastDeepestVisibleChild(): TreeNode;
loadChildren(): Promise<TreeNodes>;
loading(): boolean;
markDirty(): TreeNode;
matched(): TreeNodes;
nextVisibleAncestralSiblingNode(): TreeNode;
nextVisibleChildNode(): TreeNode;
nextVisibleNode(): TreeNode | undefined;
nextVisibleSiblingNode(): TreeNode;
pagination(): Pagination;
previousVisibleNode(): TreeNode | undefined;
previousVisibleSiblingNode(): TreeNode;
recurseDown(iteratee: NodeIteratee): TreeNode;
recurseUp(iteratee: NodeIteratee): TreeNode;
refreshIndeterminateState(): TreeNode;
reload(): Promise<TreeNodes>;
remove(includeState?: boolean): any;
removed(): boolean;
renderable(): boolean;
rendered(): boolean;
restore(): TreeNode;
select(shallow?: boolean): TreeNode;
selectable(): boolean;
selected(): boolean;
set(key: number|string, val: any): TreeNode;
show(): TreeNode;
softRemove(): TreeNode;
state(key: object|string, val?: boolean): boolean|object;
states(keys: Array<string>, val: boolean): boolean;
toggleCheck(): TreeNode;
toggleCollapse(): TreeNode;
toggleEditing(): TreeNode;
toggleSelect(): TreeNode;
toObject(excludeChildren?: boolean, includeState?: boolean): any;
toString(): string;
tree(): InspireTree;
uncheck(shallow?: boolean): TreeNode;
unshiftChild(node: NodeConfig): TreeNode;
visible(): boolean;
}
export default InspireTree;