es-grid-template
Version:
es-grid-template
217 lines (213 loc) • 6.33 kB
JavaScript
import isLeafNode from "../isLeafNode";
import { arrayUtils } from "../others";
class Wrapper {
constructor(input) {
Object.assign(this, input);
}
root;
children;
parent;
// @ts-ignore
node;
detached;
checked;
exactChecked;
parentChecked;
anyDescendentsChecked;
allChildrenChecked;
}
export default class TreeDataHelper {
opts;
// @ts-ignore
wrapperMap;
// @ts-ignore
rootWrapper;
constructor(opts) {
this.opts = opts;
this.initWrapperTree();
}
get value() {
return this.opts.value;
}
isDetached = node => {
return this.opts.isDetached?.(node) ?? false;
};
initWrapperTree() {
const valueSet = new Set(this.value);
this.rootWrapper = new Wrapper({
root: true,
children: []
});
this.wrapperMap = new Map();
const getNodeValue = this.opts.getNodeValue;
const {
isDetached,
wrapperMap
} = this;
dfs(this.rootWrapper, this.opts.tree, false);
// const self = this
function dfs(parentWrapper, nodes, inheritParentChecked) {
parentWrapper.children ??= [];
parentWrapper.allChildrenChecked = true;
if (process.env.NODE_ENV !== 'production') {
if (nodes.length > 0 && nodes.every(isDetached)) {
console.warn('TreeDataHelper detected that some nodes have all of their child nodes in a detached state, which will cause those nodes to become "invalid nodes".', parentWrapper.node);
}
}
for (const node of nodes) {
const key = getNodeValue(node);
if (wrapperMap.has(key)) {
console.warn('Duplicate node value detected:', key);
}
const detached = isDetached(node);
const exactChecked = valueSet.has(key);
if (exactChecked && !detached) {
parentWrapper.anyDescendentsChecked = true;
}
const parentChecked = !detached && inheritParentChecked;
const checked = exactChecked || parentChecked;
const wrapper = new Wrapper({
parent: parentWrapper,
node,
checked,
exactChecked,
parentChecked,
anyDescendentsChecked: checked,
detached
});
wrapperMap.set(key, wrapper);
parentWrapper.children.push(wrapper);
if (!isLeafNode(node)) {
wrapper.children = [];
dfs(wrapper, node.children, checked);
if (wrapper.anyDescendentsChecked && !detached) {
parentWrapper.anyDescendentsChecked = true;
}
if (wrapper.allChildrenChecked) {
wrapper.checked = true;
for (const child of wrapper.children) {
if (!child.detached) {
child.parentChecked = true;
}
}
}
}
if (!wrapper.checked && !detached) {
parentWrapper.allChildrenChecked = false;
}
}
}
}
isIndeterminate(nodeValue) {
const wrapper = this.wrapperMap.get(nodeValue);
return !wrapper?.checked && !!wrapper?.anyDescendentsChecked;
}
isChecked(nodeValue) {
const wrapper = this.wrapperMap.get(nodeValue);
return !!wrapper?.checked;
}
getValueAfterCheck(nodeValue) {
if (this.isChecked(nodeValue)) {
return this.getCleanValue();
}
const nextValue = arrayUtils.merge(this.value, [nodeValue]);
return new TreeDataHelper({
...this.opts,
value: nextValue
}).getCleanValue();
}
getValueAfterUncheck(nodeValue) {
if (!this.isChecked(nodeValue)) {
return this.getCleanValue();
}
const wrapper = this.wrapperMap.get(nodeValue);
const {
getNodeValue
} = this.opts;
const appendArray = getAppendArray(wrapper);
const removeSet = getRemoveSet(wrapper);
const nextValue = arrayUtils.diff(this.value.concat(appendArray), removeSet);
return new TreeDataHelper({
...this.opts,
value: nextValue
}).getCleanValue();
function getAppendArray(startWrapper) {
const result = [];
let current = startWrapper;
while (current.parentChecked && !current.detached) {
const parent = current.parent;
if (!parent) break;
for (const sibling of parent.children ?? []) {
if (sibling === current || sibling.exactChecked || sibling.detached) {
continue;
}
result.push(getNodeValue(sibling.node));
}
current = parent;
}
return result;
}
function getRemoveSet(startWrapper) {
const result = new Set();
let wrapper1 = startWrapper;
while (wrapper1) {
result.add(getNodeValue(wrapper1.node));
if (wrapper1.detached || !wrapper1.parentChecked) {
break;
}
wrapper1 = wrapper1.parent;
}
function dfs(wrappers) {
for (const wrapperIt of wrappers) {
if (wrapperIt.detached || !wrapperIt.checked) continue;
result.add(getNodeValue(wrapperIt.node));
if (!isLeafNode(wrapperIt.node) && wrapperIt.anyDescendentsChecked) {
dfs(wrapperIt.children ?? []);
}
}
}
dfs(startWrapper.children ?? []);
return result;
}
}
getValueAfterToggle(nodeValue) {
if (this.isChecked(nodeValue)) {
return this.getValueAfterUncheck(nodeValue);
}
return this.getValueAfterCheck(nodeValue);
}
getNode(nodeValue) {
return this.wrapperMap.get(nodeValue)?.node;
}
getCleanValue() {
const {
checkedStrategy,
getNodeValue
} = this.opts;
const result = this.value.filter(nodeValue => {
return !this.wrapperMap.has(nodeValue);
});
dfs(this.rootWrapper.children ?? []);
return result;
function dfs(wrappers) {
for (const wrapper of wrappers) {
if (wrapper.checked) {
if (checkedStrategy === 'all') {
result.push(getNodeValue(wrapper.node));
} else if (checkedStrategy === 'parent') {
if (!wrapper.parentChecked) {
result.push(getNodeValue(wrapper.node));
}
} else {
if (isLeafNode(wrapper.node)) {
result.push(getNodeValue(wrapper.node));
}
}
}
if (!isLeafNode(wrapper.node)) {
dfs(wrapper.children ?? []);
}
}
}
}
}