@zag-js/collection
Version:
Utilities to manage a collection of items.
469 lines (467 loc) • 18.6 kB
JavaScript
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
// src/tree-collection.ts
var tree_collection_exports = {};
__export(tree_collection_exports, {
TreeCollection: () => TreeCollection,
filePathToTree: () => filePathToTree,
flattenedToTree: () => flattenedToTree
});
module.exports = __toCommonJS(tree_collection_exports);
var import_utils = require("@zag-js/utils");
var import_tree_visit = require("./tree-visit.js");
var TreeCollection = class _TreeCollection {
constructor(options) {
this.options = options;
__publicField(this, "rootNode");
__publicField(this, "isEqual", (other) => {
return (0, import_utils.isEqual)(this.rootNode, other.rootNode);
});
__publicField(this, "getNodeChildren", (node) => {
return this.options.nodeToChildren?.(node) ?? fallbackMethods.nodeToChildren(node) ?? [];
});
__publicField(this, "resolveIndexPath", (valueOrIndexPath) => {
return typeof valueOrIndexPath === "string" ? this.getIndexPath(valueOrIndexPath) : valueOrIndexPath;
});
__publicField(this, "resolveNode", (valueOrIndexPath) => {
const indexPath = this.resolveIndexPath(valueOrIndexPath);
return indexPath ? this.at(indexPath) : void 0;
});
__publicField(this, "getNodeChildrenCount", (node) => {
return this.options.nodeToChildrenCount?.(node) ?? fallbackMethods.nodeToChildrenCount(node);
});
__publicField(this, "getNodeValue", (node) => {
return this.options.nodeToValue?.(node) ?? fallbackMethods.nodeToValue(node);
});
__publicField(this, "getNodeDisabled", (node) => {
return this.options.isNodeDisabled?.(node) ?? fallbackMethods.isNodeDisabled(node);
});
__publicField(this, "stringify", (value) => {
const node = this.findNode(value);
if (!node) return null;
return this.stringifyNode(node);
});
__publicField(this, "stringifyNode", (node) => {
return this.options.nodeToString?.(node) ?? fallbackMethods.nodeToString(node);
});
__publicField(this, "getFirstNode", (rootNode = this.rootNode, opts = {}) => {
let firstChild;
(0, import_tree_visit.visit)(rootNode, {
getChildren: this.getNodeChildren,
onEnter: (node, indexPath) => {
if (this.isSameNode(node, rootNode)) return;
if (opts.skip?.({ value: this.getNodeValue(node), node, indexPath })) return "skip";
if (!firstChild && indexPath.length > 0 && !this.getNodeDisabled(node)) {
firstChild = node;
return "stop";
}
}
});
return firstChild;
});
__publicField(this, "getLastNode", (rootNode = this.rootNode, opts = {}) => {
let lastChild;
(0, import_tree_visit.visit)(rootNode, {
getChildren: this.getNodeChildren,
onEnter: (node, indexPath) => {
if (this.isSameNode(node, rootNode)) return;
if (opts.skip?.({ value: this.getNodeValue(node), node, indexPath })) return "skip";
if (indexPath.length > 0 && !this.getNodeDisabled(node)) {
lastChild = node;
}
}
});
return lastChild;
});
__publicField(this, "at", (indexPath) => {
return (0, import_tree_visit.access)(this.rootNode, indexPath, {
getChildren: this.getNodeChildren
});
});
__publicField(this, "findNode", (value, rootNode = this.rootNode) => {
return (0, import_tree_visit.find)(rootNode, {
getChildren: this.getNodeChildren,
predicate: (node) => this.getNodeValue(node) === value
});
});
__publicField(this, "findNodes", (values, rootNode = this.rootNode) => {
const v = new Set(values.filter((v2) => v2 != null));
return (0, import_tree_visit.findAll)(rootNode, {
getChildren: this.getNodeChildren,
predicate: (node) => v.has(this.getNodeValue(node))
});
});
__publicField(this, "sort", (values) => {
return values.reduce((acc, value) => {
const indexPath = this.getIndexPath(value);
if (indexPath) acc.push({ value, indexPath });
return acc;
}, []).sort((a, b) => (0, import_tree_visit.compareIndexPaths)(a.indexPath, b.indexPath)).map(({ value }) => value);
});
__publicField(this, "getValue", (indexPath) => {
const node = this.at(indexPath);
return node ? this.getNodeValue(node) : void 0;
});
__publicField(this, "getValuePath", (indexPath) => {
if (!indexPath) return [];
const valuePath = [];
let currentPath = [...indexPath];
while (currentPath.length > 0) {
const node = this.at(currentPath);
if (node) valuePath.unshift(this.getNodeValue(node));
currentPath.pop();
}
return valuePath;
});
__publicField(this, "getDepth", (value) => {
const indexPath = (0, import_tree_visit.findIndexPath)(this.rootNode, {
getChildren: this.getNodeChildren,
predicate: (node) => this.getNodeValue(node) === value
});
return indexPath?.length ?? 0;
});
__publicField(this, "isSameNode", (node, other) => {
return this.getNodeValue(node) === this.getNodeValue(other);
});
__publicField(this, "isRootNode", (node) => {
return this.isSameNode(node, this.rootNode);
});
__publicField(this, "contains", (parentIndexPath, valueIndexPath) => {
if (!parentIndexPath || !valueIndexPath) return false;
return valueIndexPath.slice(0, parentIndexPath.length).every((_, i) => parentIndexPath[i] === valueIndexPath[i]);
});
__publicField(this, "getNextNode", (value, opts = {}) => {
let found = false;
let nextNode;
(0, import_tree_visit.visit)(this.rootNode, {
getChildren: this.getNodeChildren,
onEnter: (node, indexPath) => {
if (this.isRootNode(node)) return;
const nodeValue = this.getNodeValue(node);
if (opts.skip?.({ value: nodeValue, node, indexPath })) {
if (nodeValue === value) {
found = true;
}
return "skip";
}
if (found && !this.getNodeDisabled(node)) {
nextNode = node;
return "stop";
}
if (nodeValue === value) {
found = true;
}
}
});
return nextNode;
});
__publicField(this, "getPreviousNode", (value, opts = {}) => {
let previousNode;
let found = false;
(0, import_tree_visit.visit)(this.rootNode, {
getChildren: this.getNodeChildren,
onEnter: (node, indexPath) => {
if (this.isRootNode(node)) return;
const nodeValue = this.getNodeValue(node);
if (opts.skip?.({ value: nodeValue, node, indexPath })) {
return "skip";
}
if (nodeValue === value) {
found = true;
return "stop";
}
if (!this.getNodeDisabled(node)) {
previousNode = node;
}
}
});
return found ? previousNode : void 0;
});
__publicField(this, "getParentNodes", (valueOrIndexPath) => {
const indexPath = this.resolveIndexPath(valueOrIndexPath)?.slice();
if (!indexPath) return [];
const result = [];
while (indexPath.length > 0) {
indexPath.pop();
const parentNode = this.at(indexPath);
if (parentNode && !this.isRootNode(parentNode)) {
result.unshift(parentNode);
}
}
return result;
});
__publicField(this, "getDescendantNodes", (valueOrIndexPath, options) => {
const parentNode = this.resolveNode(valueOrIndexPath);
if (!parentNode) return [];
const result = [];
(0, import_tree_visit.visit)(parentNode, {
getChildren: this.getNodeChildren,
onEnter: (node, nodeIndexPath) => {
if (nodeIndexPath.length === 0) return;
if (!options?.withBranch && this.isBranchNode(node)) return;
result.push(node);
}
});
return result;
});
__publicField(this, "getDescendantValues", (valueOrIndexPath, options) => {
const children = this.getDescendantNodes(valueOrIndexPath, options);
return children.map((child) => this.getNodeValue(child));
});
__publicField(this, "getParentIndexPath", (indexPath) => {
return indexPath.slice(0, -1);
});
__publicField(this, "getParentNode", (valueOrIndexPath) => {
const indexPath = this.resolveIndexPath(valueOrIndexPath);
return indexPath ? this.at(this.getParentIndexPath(indexPath)) : void 0;
});
__publicField(this, "visit", (opts) => {
const { skip, ...rest } = opts;
(0, import_tree_visit.visit)(this.rootNode, {
...rest,
getChildren: this.getNodeChildren,
onEnter: (node, indexPath) => {
if (this.isRootNode(node)) return;
if (skip?.({ value: this.getNodeValue(node), node, indexPath })) return "skip";
return rest.onEnter?.(node, indexPath);
}
});
});
__publicField(this, "getPreviousSibling", (indexPath) => {
const parentNode = this.getParentNode(indexPath);
if (!parentNode) return;
const siblings = this.getNodeChildren(parentNode);
let idx = indexPath[indexPath.length - 1];
while (--idx >= 0) {
const sibling = siblings[idx];
if (!this.getNodeDisabled(sibling)) return sibling;
}
return;
});
__publicField(this, "getNextSibling", (indexPath) => {
const parentNode = this.getParentNode(indexPath);
if (!parentNode) return;
const siblings = this.getNodeChildren(parentNode);
let idx = indexPath[indexPath.length - 1];
while (++idx < siblings.length) {
const sibling = siblings[idx];
if (!this.getNodeDisabled(sibling)) return sibling;
}
return;
});
__publicField(this, "getSiblingNodes", (indexPath) => {
const parentNode = this.getParentNode(indexPath);
return parentNode ? this.getNodeChildren(parentNode) : [];
});
__publicField(this, "getValues", (rootNode = this.rootNode) => {
const values = (0, import_tree_visit.flatMap)(rootNode, {
getChildren: this.getNodeChildren,
transform: (node) => [this.getNodeValue(node)]
});
return values.slice(1);
});
__publicField(this, "isValidDepth", (indexPath, depth) => {
if (depth == null) return true;
if (typeof depth === "function") return depth(indexPath.length);
return indexPath.length === depth;
});
__publicField(this, "isBranchNode", (node) => {
return this.getNodeChildren(node).length > 0 || this.getNodeChildrenCount(node) != null;
});
__publicField(this, "getBranchValues", (rootNode = this.rootNode, opts = {}) => {
let values = [];
(0, import_tree_visit.visit)(rootNode, {
getChildren: this.getNodeChildren,
onEnter: (node, indexPath) => {
if (indexPath.length === 0) return;
const nodeValue = this.getNodeValue(node);
if (opts.skip?.({ value: nodeValue, node, indexPath })) return "skip";
if (this.isBranchNode(node) && this.isValidDepth(indexPath, opts.depth)) {
values.push(this.getNodeValue(node));
}
}
});
return values;
});
__publicField(this, "flatten", (rootNode = this.rootNode) => {
return (0, import_tree_visit.flatten)(rootNode, { getChildren: this.getNodeChildren });
});
__publicField(this, "_create", (node, children) => {
if (this.getNodeChildren(node).length > 0 || children.length > 0) {
return { ...node, children };
}
return { ...node };
});
__publicField(this, "_insert", (rootNode, indexPath, nodes) => {
return this.copy(
(0, import_tree_visit.insert)(rootNode, { at: indexPath, nodes, getChildren: this.getNodeChildren, create: this._create })
);
});
__publicField(this, "copy", (rootNode) => {
return new _TreeCollection({ ...this.options, rootNode });
});
__publicField(this, "_replace", (rootNode, indexPath, node) => {
return this.copy(
(0, import_tree_visit.replace)(rootNode, { at: indexPath, node, getChildren: this.getNodeChildren, create: this._create })
);
});
__publicField(this, "_move", (rootNode, indexPaths, to) => {
return this.copy((0, import_tree_visit.move)(rootNode, { indexPaths, to, getChildren: this.getNodeChildren, create: this._create }));
});
__publicField(this, "_remove", (rootNode, indexPaths) => {
return this.copy((0, import_tree_visit.remove)(rootNode, { indexPaths, getChildren: this.getNodeChildren, create: this._create }));
});
__publicField(this, "replace", (indexPath, node) => {
return this._replace(this.rootNode, indexPath, node);
});
__publicField(this, "remove", (indexPaths) => {
return this._remove(this.rootNode, indexPaths);
});
__publicField(this, "insertBefore", (indexPath, nodes) => {
const parentNode = this.getParentNode(indexPath);
return parentNode ? this._insert(this.rootNode, indexPath, nodes) : void 0;
});
__publicField(this, "insertAfter", (indexPath, nodes) => {
const parentNode = this.getParentNode(indexPath);
if (!parentNode) return;
const nextIndex = [...indexPath.slice(0, -1), indexPath[indexPath.length - 1] + 1];
return this._insert(this.rootNode, nextIndex, nodes);
});
__publicField(this, "move", (fromIndexPaths, toIndexPath) => {
return this._move(this.rootNode, fromIndexPaths, toIndexPath);
});
__publicField(this, "filter", (predicate) => {
const filteredRoot = (0, import_tree_visit.filter)(this.rootNode, {
predicate,
getChildren: this.getNodeChildren,
create: this._create
});
return this.copy(filteredRoot);
});
__publicField(this, "toJSON", () => {
return this.getValues(this.rootNode);
});
this.rootNode = options.rootNode;
}
getIndexPath(valueOrValuePath) {
if (Array.isArray(valueOrValuePath)) {
if (valueOrValuePath.length === 0) return [];
const indexPath = [];
let currentChildren = this.getNodeChildren(this.rootNode);
for (let i = 0; i < valueOrValuePath.length; i++) {
const currentValue = valueOrValuePath[i];
const matchingChildIndex = currentChildren.findIndex((child) => this.getNodeValue(child) === currentValue);
if (matchingChildIndex === -1) break;
indexPath.push(matchingChildIndex);
if (i < valueOrValuePath.length - 1) {
const currentNode = currentChildren[matchingChildIndex];
currentChildren = this.getNodeChildren(currentNode);
}
}
return indexPath;
} else {
return (0, import_tree_visit.findIndexPath)(this.rootNode, {
getChildren: this.getNodeChildren,
predicate: (node) => this.getNodeValue(node) === valueOrValuePath
});
}
}
};
function flattenedToTree(nodes, options = fallbackMethods) {
if (nodes.length === 0) {
throw new Error("[zag-js/tree] Cannot create tree from empty flattened array");
}
const rootFlatNode = nodes.find((node) => node._parent === void 0);
if (!rootFlatNode) {
throw new Error("[zag-js/tree] No root node found in flattened data");
}
const nodeMap = /* @__PURE__ */ new Map();
nodes.forEach((node) => {
nodeMap.set(node._index, node);
});
const buildNode = (idx) => {
const flatNode = nodeMap.get(idx);
if (!flatNode) return {};
const { _children, _parent, _index, ...cleanNode } = flatNode;
const children = [];
_children?.forEach((childIndex) => {
children.push(buildNode(childIndex));
});
return {
...cleanNode,
...children.length > 0 && { children }
};
};
const rootNode = buildNode(rootFlatNode._index);
return new TreeCollection({ ...options, rootNode });
}
function filePathToTree(paths) {
const rootNode = {
label: "",
value: "ROOT",
children: []
};
paths.forEach((path) => {
const parts = path.split("/");
let currentNode = rootNode;
parts.forEach((part, index) => {
let childNode = currentNode.children?.find((child) => child.label === part);
if (!childNode) {
childNode = {
value: parts.slice(0, index + 1).join("/"),
label: part
};
currentNode.children || (currentNode.children = []);
currentNode.children.push(childNode);
}
currentNode = childNode;
});
});
return new TreeCollection({ rootNode });
}
var fallbackMethods = {
nodeToValue(node) {
if (typeof node === "string") return node;
if ((0, import_utils.isObject)(node) && (0, import_utils.hasProp)(node, "value")) return node.value;
return "";
},
nodeToString(node) {
if (typeof node === "string") return node;
if ((0, import_utils.isObject)(node) && (0, import_utils.hasProp)(node, "label")) return node.label;
return fallbackMethods.nodeToValue(node);
},
isNodeDisabled(node) {
if ((0, import_utils.isObject)(node) && (0, import_utils.hasProp)(node, "disabled")) return !!node.disabled;
return false;
},
nodeToChildren(node) {
return node.children;
},
nodeToChildrenCount(node) {
if ((0, import_utils.isObject)(node) && (0, import_utils.hasProp)(node, "childrenCount")) return node.childrenCount;
}
};
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
TreeCollection,
filePathToTree,
flattenedToTree
});