UNPKG

@react-awesome-query-builder/core

Version:
568 lines (544 loc) 19.5 kB
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; }