Drastically improved tree tab search UX for locating parts of the sheet

This commit is contained in:
Stefan Zermatten
2021-07-31 15:19:54 +02:00
parent 0dc0bea53e
commit 02434de34c
13 changed files with 215 additions and 88 deletions

View File

@@ -7,7 +7,7 @@ import { CreatureLogSchema, insertCreatureLogWork } from '/imports/api/creature/
import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js';
import { assertEditPermission } from '/imports/api/creature/creatures/creaturePermissions.js';
import { recomputeCreatureByDoc } from '/imports/api/creature/computation/methods/recomputeCreature.js';
import { nodesToTree } from '/imports/api/parenting/parenting.js';
import nodesToTree from '/imports/api/parenting/nodesToTree.js';
import applyProperties from '/imports/api/creature/actions/applyProperties.js';
import recomputeInventory from '/imports/api/creature/denormalise/recomputeInventory.js';
import recomputeInactiveProperties from '/imports/api/creature/denormalise/recomputeInactiveProperties.js';

View File

@@ -1,6 +1,6 @@
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
import Creatures from '/imports/api/creature/creatures/Creatures.js';
import { nodesToTree } from '/imports/api/parenting/parenting.js';
import nodesToTree from '/imports/api/parenting/nodesToTree.js';
export default function recomputeInventory(creatureId){
let inventoryForest = nodesToTree({

View File

@@ -1,4 +1,4 @@
import { nodesToTree } from '/imports/api/parenting/parenting.js';
import nodesToTree from '/imports/api/parenting/nodesToTree.js';
export default function getDescendantsInDepthFirstOrder({
collection,

View File

@@ -0,0 +1,115 @@
import { union, difference, sortBy, findLast } from 'lodash';
export function nodeArrayToTree(nodes){
// Store a dict and list of all the nodes
let nodeIndex = {};
let nodeList = [];
nodes.forEach( node => {
let treeNode = {
node: node,
children: [],
};
nodeIndex[node._id] = treeNode;
nodeList.push(treeNode);
});
// Create a forest of trees
let forest = [];
// Either the node is a child of its nearest found ancestor, or in the forest as a root
nodeList.forEach(treeNode => {
let ancestorInForest = findLast(
treeNode.node.ancestors,
ancestor => !!nodeIndex[ancestor.id]
);
if (ancestorInForest){
nodeIndex[ancestorInForest.id].children.push(treeNode);
} else {
forest.push(treeNode);
}
});
return forest;
}
// Fetch the documents from a collection, and return the tree of those documents
export default function nodesToTree({
collection, ancestorId, filter, options = {},
includeFilteredDocAncestors = false, includeFilteredDocDescendants = false
}){
// Setup the filter
let collectionFilter = {
'ancestors.id': ancestorId,
'removed': {$ne: true},
};
if (filter){
collectionFilter = {
...collectionFilter,
...filter,
}
}
// Set up the options
let collectionSort = {
order: 1
};
if (options && options.sort){
collectionSort = {
...collectionSort,
...options.sort,
}
}
let collectionOptions = {
sort: collectionSort,
}
if (options){
collectionOptions = {
...collectionOptions,
...options,
}
}
// Find all the nodes that match the filter
let docs = collection.find(collectionFilter, collectionOptions).map(doc => {
if (!filter) return doc;
// Mark the nodes that were found by the custom filter
doc._matchedDocumentFilter = true;
return doc;
});
let ancestors = [];
let ancestorIds = [];
let docIds = [];
if (filter && (includeFilteredDocAncestors || includeFilteredDocDescendants)){
docIds = docs.map(doc => doc._id)
}
if (filter && includeFilteredDocAncestors){
// Add all ancestor ids to an array
docs.forEach(doc => {
ancestorIds = union(ancestorIds, doc.ancestors.map(ref => ref.id));
});
// Remove the IDs of docs we have already found
ancestorIds = difference(ancestorIds, docIds);
// Get the docs from the collection, don't worry about `removed` docs,
// if their descendant was not removed, neither are they
ancestors = collection.find({_id: {$in: ancestorIds}}).map(doc => {
// Mark that the nodes are ancestors of the found nodes
doc._ancestorOfMatchedDocument = true;
return doc;
});
}
let descendants = [];
if (filter && includeFilteredDocDescendants){
let exludeIds = union(ancestorIds, docIds);
descendants = collection.find({
'_id': {$nin: exludeIds},
'ancestors.id': {$in: docIds},
'removed': {$ne: true},
}).map(doc => {
// Mark that the nodes are descendants of the found nodes
doc._descendantOfMatchedDocument = true;
return doc;
});
}
let nodes = sortBy([
...ancestors,
...docs,
...descendants
], 'order');
// Find all the nodes
return nodeArrayToTree(nodes);
}

View File

@@ -1,6 +1,6 @@
import fetchDocByRef from '/imports/api/parenting/fetchDocByRef.js';
import getCollectionByName from '/imports/api/parenting/getCollectionByName.js';
import { flatten, findLast } from 'lodash';
import { flatten } from 'lodash';
const generalParents = [
'attribute',
@@ -217,41 +217,3 @@ export function getName(doc){
if (doc.ancestors[i].name) return doc.ancestors[i].name;
}
}
export function nodeArrayToTree(nodes){
// Store a dict and list of all the nodes
let nodeIndex = {};
let nodeList = [];
nodes.forEach( node => {
let treeNode = {
node: node,
children: [],
};
nodeIndex[node._id] = treeNode;
nodeList.push(treeNode);
});
// Create a forest of trees
let forest = [];
// Either the node is a child of its nearest found ancestor, or in the forest as a root
nodeList.forEach(treeNode => {
let ancestorInForest = findLast(
treeNode.node.ancestors,
ancestor => !!nodeIndex[ancestor.id]
);
if (ancestorInForest){
nodeIndex[ancestorInForest.id].children.push(treeNode);
} else {
forest.push(treeNode);
}
});
return forest;
}
export function nodesToTree({collection, ancestorId, filter = {}, options = {}}){
if (!('ancestors.id' in filter)) filter['ancestors.id'] = ancestorId;
if (!('removed' in filter)) filter['removed'] = {$ne: true};
if (!options.sort) options.sort = {order: 1};
if (!('order' in options.sort)) options.sort.order = 1;
let nodes = collection.find(filter, options);
return nodeArrayToTree(nodes);
}