@react-awesome-query-builder/core
Version:
User-friendly query builder for React. Core
568 lines (544 loc) • 19.5 kB
JavaScript
import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
import _defineProperty from "@babel/runtime/helpers/defineProperty";
import Immutable, { fromJS } from "immutable";
import { toImmutableList, isImmutable, applyToJS as immutableToJs } from "./stuff";
import { getFieldConfig } from "./configUtils";
import uuid from "./uuid";
export { toImmutableList, immutableToJs, isImmutable };
/**
* @param {Immutable.List} path
* @param {...string} suffix
* @return {Immutable.List}
*/
export var expandTreePath = function expandTreePath(path) {
for (var _len = arguments.length, suffix = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
suffix[_key - 1] = arguments[_key];
}
return path.interpose("children1").withMutations(function (list) {
list.skip(1);
list.push.apply(list, suffix);
return list;
});
};
/**
* @param {Immutable.List} path
* @param {...string} suffix
* @return {Immutable.List}
*/
export var expandTreeSubpath = function expandTreeSubpath(path) {
for (var _len2 = arguments.length, suffix = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {
suffix[_key2 - 1] = arguments[_key2];
}
return path.interpose("children1").withMutations(function (list) {
list.push.apply(list, suffix);
return list;
});
};
/**
* @param {Immutable.Map} tree
* @param {Immutable.List} path
* @return {Immutable.Map}
*/
export var getItemByPath = function getItemByPath(tree, path) {
var children = new Immutable.OrderedMap(_defineProperty({}, tree.get("id"), tree));
var res = tree;
path.forEach(function (id) {
var _children, _res;
res = (_children = children) === null || _children === void 0 ? void 0 : _children.get(id);
children = (_res = res) === null || _res === void 0 ? void 0 : _res.get("children1");
});
return res;
};
/**
* @param {Immutable.Map} tree
* @param {Immutable.List} path
* @return {field, path}[] ordered by closest
*/
export var getAncestorRuleGroups = function getAncestorRuleGroups(tree, path) {
var parentRuleGroups = path.map(function (_id, i) {
return path.take(i + 1);
}).reverse().toJS().map(function (path) {
return {
item: getItemByPath(tree, path),
path: path
};
}).filter(function (_ref) {
var item = _ref.item;
return (item === null || item === void 0 ? void 0 : item.get("type")) === "rule_group";
});
if (parentRuleGroups.length) {
return parentRuleGroups.map(function (_ref2) {
var item = _ref2.item,
path = _ref2.path;
return {
path: path,
field: item.get("properties").get("field")
};
});
}
return [];
};
/**
* Remove `path` in every item
* @param {Immutable.Map} tree
* @return {Immutable.Map} tree
*/
// export const removePathsInTree = (tree) => {
// let newTree = tree;
// function _processNode (item, path) {
// const itemPath = path.push(item.get("id"));
// if (item.get("path")) {
// newTree = newTree.removeIn(expandTreePath(itemPath, "path"));
// }
// const children = item.get("children1");
// if (children) {
// children.map((child, _childId) => {
// _processNode(child, itemPath);
// });
// }
// }
// _processNode(tree, new Immutable.List());
// return newTree;
// };
/**
* Remove `isLocked` in items that inherit parent's `isLocked`
* @param {Immutable.Map} tree
* @return {Immutable.Map} tree
*/
export var removeIsLockedInTree = function removeIsLockedInTree(tree) {
var newTree = tree;
function _processNode(item, path) {
var isParentLocked = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
var itemPath = path.push(item.get("id"));
var isLocked = item.getIn(["properties", "isLocked"]);
if (isParentLocked && isLocked) {
newTree = newTree.deleteIn(expandTreePath(itemPath, "properties", "isLocked"));
}
var children = item.get("children1");
if (children) {
children.map(function (child, _childId) {
_processNode(child, itemPath, isLocked || isParentLocked);
});
}
}
_processNode(tree, new Immutable.List());
return newTree;
};
/**
* Set correct `path` and `id` in every item
* @param {Immutable.Map} tree
* @return {Immutable.Map} tree
*/
export var fixPathsInTree = function fixPathsInTree(tree) {
var newTree = tree;
function _processNode(item, path, lev, nodeId) {
if (!item) return;
var currPath = item.get("path");
var currId = item.get("id");
var itemId = currId || nodeId || uuid();
var itemPath = path.push(itemId);
if (!currPath || !currPath.equals(itemPath)) {
newTree = newTree.setIn(expandTreePath(itemPath, "path"), itemPath);
}
if (!currId) {
newTree = newTree.setIn(expandTreePath(itemPath, "id"), itemId);
}
var children = item.get("children1");
if (children) {
if (children.constructor.name === "Map") {
// protect: should be OrderedMap, not Map (issue #501)
newTree = newTree.setIn(expandTreePath(itemPath, "children1"), new Immutable.OrderedMap(children));
}
children.map(function (child, childId) {
_processNode(child, itemPath, lev + 1, childId);
});
}
}
_processNode(tree, new Immutable.List(), 0);
return newTree;
};
export var fixEmptyGroupsInTree = function fixEmptyGroupsInTree(tree) {
var newTree = tree;
function _processNode(item, path, lev, nodeId) {
if (!item) return false;
var itemId = item.get("id") || nodeId;
var itemPath = path.push(itemId);
var children = item.get("children1");
if (children) {
var allChildrenGone = children.map(function (child, childId) {
return _processNode(child, itemPath, lev + 1, childId);
}).reduce(function (curr, v) {
return curr && v;
}, true);
if ((children.size == 0 || allChildrenGone) && lev > 0) {
newTree = newTree.deleteIn(expandTreePath(itemPath));
return true;
}
}
return false;
}
_processNode(tree, new Immutable.List(), 0);
return newTree;
};
/**
* @param {Immutable.Map} tree
* @return {Object} {flat, items}
*/
export var getFlatTree = function getFlatTree(tree, config) {
var flat = [];
var items = {};
var cases = [];
var visibleHeight = 0; // number of non-collapsed nodes
var globalLeafCount = 0;
var globalAtomicCount = 0;
var globalGroupCount = 0;
var globalCountByType = {};
// rule_group_ext can be counted as group (group #x)
// or by similars (rule-group #x) (NOT both _ext and no ext)
function _flatizeTree(item, path, insideCollapsed, insideLocked, insideRuleGroup, lev, atomicLev, caseId, childNo) {
var _items$closestRuleGro, _items$closestRuleGro2, _items$closestRuleGro3;
var isRoot = item === tree;
var type = item.get("type");
var collapsed = item.get("collapsed");
var id = item.get("id");
var children = item.get("children1");
var isLocked = item.getIn(["properties", "isLocked"]);
var childrenIds = children ? children.map(function (_child, childId) {
return childId;
}).valueSeq().toArray() : null;
var isRuleGroup = type === "rule_group";
var isRule = type === "rule";
var isGroup = type === "group";
var isCaseGroup = type === "case_group";
// tip: count rule_group as 1 atomic rule
var isAtomicRule = !insideRuleGroup && (!children || isRuleGroup);
var hasChildren = (childrenIds === null || childrenIds === void 0 ? void 0 : childrenIds.length) > 0;
var parentId = path.length ? path[path.length - 1] : null;
var closestRuleGroupId = _toConsumableArray(path).reverse().find(function (id) {
return items[id].type == "rule_group";
});
var field = item.getIn(["properties", "field"]);
var fieldConfig = field && config && getFieldConfig(config, field);
var canRegroup = fieldConfig ? (fieldConfig === null || fieldConfig === void 0 ? void 0 : fieldConfig.canRegroup) !== false : undefined;
var maxNesting = fieldConfig === null || fieldConfig === void 0 ? void 0 : fieldConfig.maxNesting;
var closestRuleGroupCanRegroup = items === null || items === void 0 || (_items$closestRuleGro = items[closestRuleGroupId]) === null || _items$closestRuleGro === void 0 ? void 0 : _items$closestRuleGro.canRegroup;
var closestRuleGroupMaxNesting = items === null || items === void 0 || (_items$closestRuleGro2 = items[closestRuleGroupId]) === null || _items$closestRuleGro2 === void 0 ? void 0 : _items$closestRuleGro2.maxNesting;
var closestRuleGroupLev = items === null || items === void 0 || (_items$closestRuleGro3 = items[closestRuleGroupId]) === null || _items$closestRuleGro3 === void 0 ? void 0 : _items$closestRuleGro3.lev;
var currentCaseId = isCaseGroup ? id : caseId;
// Calculations before
if (isCaseGroup) {
cases.push(id);
// reset counters
globalLeafCount = 0;
globalAtomicCount = 0;
globalGroupCount = 0;
globalCountByType = {};
}
var caseNo = currentCaseId ? cases.indexOf(currentCaseId) : null;
var itemsBefore = flat.length;
var top = visibleHeight;
var position;
if (!isRoot) {
position = {};
position.caseNo = caseNo;
position.globalNoByType = isCaseGroup ? caseNo : globalCountByType[type] || 0;
position.indexPath = [].concat(_toConsumableArray(path.slice(1).map(function (id) {
return items[id].childNo;
})), [childNo]);
if (isRule) {
position.globalLeafNo = globalLeafCount;
} else if (isGroup) {
position.globalGroupNo = globalGroupCount;
}
}
var nextAtomicLev = insideRuleGroup || isRuleGroup ? atomicLev : atomicLev + 1;
flat.push(id);
items[id] = {
node: item,
index: itemsBefore,
// index in `flat`
id: id,
type: type,
parent: parentId,
children: childrenIds,
childNo: childNo,
caseId: currentCaseId,
caseNo: caseNo,
closestRuleGroupId: closestRuleGroupId,
closestRuleGroupLev: closestRuleGroupLev,
closestRuleGroupMaxNesting: closestRuleGroupMaxNesting,
closestRuleGroupCanRegroup: closestRuleGroupCanRegroup,
maxNesting: maxNesting,
canRegroup: canRegroup,
path: path.concat(id),
lev: lev,
// depth level (0 for root node)
atomicLev: atomicLev,
// same as lev, but rules inside rule_group retains same number
nextAtomicLev: nextAtomicLev,
isLeaf: !children,
// is atomic rule OR rule inside rule_group
isAtomicRule: isAtomicRule,
// is atomic (rule or rule_group, but not rules inside rule_group)
isLocked: isLocked || insideLocked,
// vertical
top: insideCollapsed ? null : top,
// for case
isDefaultCase: isCaseGroup ? !children : undefined,
atomicRulesCountInCase: isCaseGroup ? 0 : undefined,
// object with numbers indicating # of item in tree
position: position,
// unused
collapsed: collapsed,
_top: itemsBefore,
parentType: parentId ? items[parentId].type : null,
// @deprecated use isLeaf instead
leaf: !children
// will be added later:
// prev
// next
// depth - for any group (children of rule_group are not counted, collapsed are not counted)
// height - visible height
// bottom = (insideCollapsed ? null : top + height)
// _height = (itemsAfter - itemsBefore) - real height (incl. collapsed)
};
// Calculations before traversing children
var height = 0;
var depth = 0;
if (!insideCollapsed) {
visibleHeight += 1;
height += 1;
if (hasChildren && !collapsed && !isRuleGroup) {
// tip: don't count children of rule_group
depth += 1;
}
if (!isRoot && !isCaseGroup) {
isGroup && globalGroupCount++;
isAtomicRule && globalAtomicCount++;
isRule && globalLeafCount++;
globalCountByType[type] = (globalCountByType[type] || 0) + 1;
}
}
if (caseId && isAtomicRule) {
items[caseId].atomicRulesCountInCase++;
}
// Traverse children deeply
var maxChildDepth = 0;
var sumHeight = 0;
if (hasChildren) {
var childCount = 0;
children.map(function (child, childId) {
if (child) {
_flatizeTree(child, path.concat(id), insideCollapsed || collapsed, insideLocked || isLocked, insideRuleGroup || isRuleGroup, lev + 1, nextAtomicLev, currentCaseId, childCount);
var childItem = items[childId];
// Calculations after deep traversing 1 child
maxChildDepth = Math.max(maxChildDepth, childItem.depth || 0);
sumHeight += childItem.height;
childCount++;
}
});
}
// Calculations after deep traversing ALL children
height += sumHeight;
depth += maxChildDepth;
var itemsAfter = flat.length;
var _height = itemsAfter - itemsBefore;
var bottom = insideCollapsed ? null : top + height;
Object.assign(items[id], {
depth: children ? depth : undefined,
_height: _height,
height: height,
bottom: bottom
});
}
// Start recursion
_flatizeTree(tree, [], false, false, false, 0, 0, null, null);
// Calc after recursion
for (var i = 0; i < flat.length; i++) {
var prevId = i > 0 ? flat[i - 1] : null;
var nextId = i < flat.length - 1 ? flat[i + 1] : null;
var item = items[flat[i]];
item.prev = prevId;
item.next = nextId;
}
return {
flat: flat,
items: items,
cases: cases
};
};
/**
* Returns count of reorderable(!) nodes
* @param {Immutable.Map} tree
* @return {Integer}
*/
export var getTotalReordableNodesCountInTree = function getTotalReordableNodesCountInTree(tree) {
if (!tree) return -1;
var cnt = 0;
function _processNode(item, path, lev) {
var id, children, type;
if (typeof item.get === "function") {
id = item.get("id");
children = item.get("children1");
type = item.get("type");
} else {
id = item.id;
children = item.children1;
type = item.type;
}
cnt++;
if (type == "rule_group" && lev > 0) {
//tip: rules in rule-group can be reordered only inside
} else if (children) {
children.map(function (child, _childId) {
if (child) {
_processNode(child, path.concat(id), lev + 1);
}
});
}
}
_processNode(tree, [], 0);
return cnt - 1; // -1 for root
};
/**
* Returns count of atomic rules (i.e. don't count groups; count rule_group as 1 atomic rule)
* @param {Immutable.Map} tree
* @return {Integer}
*/
export var getTotalRulesCountInTree = function getTotalRulesCountInTree(tree) {
if (!tree) return -1;
var cnt = 0;
function _processNode(item, path, lev) {
var id, children, type;
if (typeof item.get === "function") {
id = item.get("id");
children = item.get("children1");
type = item.get("type");
} else {
id = item.id;
children = item.children1;
type = item.type;
}
if (type == "rule" || type == "rule_group" && lev > 0) {
// tip: count rule_group as 1 rule
cnt++;
} else if (children) {
children.map(function (child, _childId) {
if (child) {
_processNode(child, path.concat(id), lev + 1);
}
});
}
}
_processNode(tree, [], 0);
return cnt;
};
// Remove fields that can be calced: "id", "path"
// Remove empty fields: "operatorOptions"
export var getLightTree = function getLightTree(tree) {
var deleteExcess = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
var children1AsArray = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
var newTree = tree;
function _processNode(item, itemId) {
if (deleteExcess && item.path) {
delete item.path;
}
if (deleteExcess && !children1AsArray && itemId) {
delete item.id;
}
var properties = item.properties;
if (properties) {
if (properties.operatorOptions == null) {
delete properties.operatorOptions;
}
}
var children = item.children1;
if (children) {
for (var id in children) {
if (children[id]) {
_processNode(children[id], id);
}
}
if (children1AsArray) {
item.children1 = Object.values(children);
}
}
}
_processNode(tree, null);
return newTree;
};
export var getSwitchValues = function getSwitchValues(tree) {
var vals = [];
var children = tree.get("children1");
if (children) {
children.map(function (child) {
var value = child.getIn(["properties", "value"]);
var caseValue;
if (value && value.size == 1) {
caseValue = value.get(0);
if (Array.isArray(caseValue) && caseValue.length == 0) {
caseValue = null;
}
} else {
caseValue = null;
}
vals = [].concat(_toConsumableArray(vals), [caseValue]);
});
}
return vals;
};
export var isEmptyTree = function isEmptyTree(tree) {
return !tree.get("children1") || tree.get("children1").size == 0;
};
export var hasChildren = function hasChildren(tree, path) {
return tree.getIn(expandTreePath(path, "children1")).size > 0;
};
export var _fixImmutableValue = function _fixImmutableValue(v) {
if (v !== null && v !== void 0 && v.toJS) {
var _v$toJS;
var vJs = v === null || v === void 0 || (_v$toJS = v.toJS) === null || _v$toJS === void 0 ? void 0 : _v$toJS.call(v);
if (vJs !== null && vJs !== void 0 && vJs.func) {
// `v` is a func, keep Immutable
return v.toOrderedMap();
} else {
// for values of multiselect use Array instead of List
return vJs;
}
} else {
return v;
}
};
export function jsToImmutable(tree) {
var imm = fromJS(tree, function (key, value, path) {
var isFuncArg = path && path.length > 3 && path[path.length - 1] === "value" && path[path.length - 3] === "args";
var isRuleValue = path && path.length > 3 && path[path.length - 1] === "value" && path[path.length - 2] === "properties";
var outValue;
if (key == "properties") {
outValue = value.toOrderedMap();
// `value` should be undefined instead of null
// JSON doesn't support undefined and replaces undefined -> null
// So fix: null -> undefined
for (var i = 0; i < 2; i++) {
var _outValue$get, _outValue$get$get;
if (((_outValue$get = outValue.get("value")) === null || _outValue$get === void 0 || (_outValue$get$get = _outValue$get.get) === null || _outValue$get$get === void 0 ? void 0 : _outValue$get$get.call(_outValue$get, i)) === null) {
outValue = outValue.setIn(["value", i], undefined);
}
}
} else if (isFuncArg) {
outValue = _fixImmutableValue(value);
} else if ((path ? isRuleValue : key == "value") && Immutable.Iterable.isIndexed(value)) {
outValue = value.map(_fixImmutableValue).toList();
} else if (key == "asyncListValues") {
// keep in JS format
outValue = value.toJS();
} else if (key == "children1" && Immutable.Iterable.isIndexed(value)) {
outValue = new Immutable.OrderedMap(value.map(function (child) {
return [(child === null || child === void 0 ? void 0 : child.get("id")) || uuid(), child];
}));
} else {
outValue = Immutable.Iterable.isIndexed(value) ? value.toList() : value.toOrderedMap();
}
return outValue;
});
return imm;
}