Feature(map): Filter challenges on Map

closes #9346
This commit is contained in:
Berkeley Martinez
2016-07-14 17:13:48 -07:00
parent ff64349e9a
commit 0995e3bba6
12 changed files with 708 additions and 79 deletions

View File

@@ -284,3 +284,224 @@ export function getMouse(e, [dx, dy]) {
return [pageX - dx, pageY - dy];
}
const emptyProtector = {
blocks: [],
challenges: []
};
// protect against malformed data
function protect(block) {
// if no block or block has no challenges or blocks
// use protector
if (!block || !(block.challenges || block.blocks)) {
return emptyProtector;
}
return block;
}
// interface Node {
// isHidden: Boolean,
// children: Void|[ ...Node ],
// isOpen?: Boolean
// }
//
// interface MapUi
// {
// children: [...{
// name: (superBlock: String),
// isOpen: Boolean,
// isHidden: Boolean,
// children: [...{
// name: (blockName: String),
// isOpen: Boolean,
// isHidden: Boolean,
// children: [...{
// name: (challengeName: String),
// isHidden: Boolean
// }]
// }]
// }]
// }
export function createMapUi(
{ superBlock: superBlockMap, block: blockMap } = {},
superBlocks
) {
if (!superBlocks || !superBlockMap || !blockMap) {
return {};
}
return {
children: superBlocks.map(superBlock => {
return {
name: superBlock,
isOpen: true,
isHidden: false,
children: protect(superBlockMap[superBlock]).blocks.map(block => {
return {
name: block,
isOpen: true,
isHidden: false,
children: protect(blockMap[block]).challenges.map(challenge => {
return {
name: challenge,
isHidden: false,
children: null
};
})
};
})
};
})
};
}
// synchronise
// traverseMapUi(
// tree: MapUi|Node,
// update: ((MapUi|Node) => MapUi|Node)
// ) => MapUi|Node
export function traverseMapUi(tree, update) {
let childrenChanged;
if (!Array.isArray(tree.children)) {
return update(tree);
}
const newChildren = tree.children.map(node => {
const newNode = traverseMapUi(node, update);
if (!childrenChanged && newNode !== node) {
childrenChanged = true;
}
return newNode;
});
if (childrenChanged) {
tree = {
...tree,
children: newChildren
};
}
return update(tree);
}
// synchronise
// getNode(tree: MapUi, name: String) => MapUi
export function getNode(tree, name) {
let node;
traverseMapUi(tree, thisNode => {
if (thisNode.name === name) {
node = thisNode;
}
return thisNode;
});
return node;
}
// synchronise
// updateSingelNode(
// tree: MapUi,
// name: String,
// update(MapUi|Node) => MapUi|Node
// ) => MapUi
export function updateSingleNode(tree, name, update) {
return traverseMapUi(tree, node => {
if (name !== node.name) {
return node;
}
return update(node);
});
}
// synchronise
// toggleThisPanel(tree: MapUi, name: String) => MapUi
export function toggleThisPanel(tree, name) {
return updateSingleNode(tree, name, node => {
return {
...node,
isOpen: !node.isOpen
};
});
}
// toggleAllPanels(tree: MapUi, isOpen: Boolean = false ) => MapUi
export function toggleAllPanels(tree, isOpen = false) {
return traverseMapUi(tree, node => {
if (!Array.isArray(node.children) || node.isOpen === isOpen) {
return node;
}
return {
...node,
isOpen
};
});
}
// collapseAllPanels(tree: MapUi) => MapUi
export function collapseAllPanels(tree) {
return toggleAllPanels(tree);
}
// expandAllPanels(tree: MapUi) => MapUi
export function expandAllPanels(tree) {
return toggleAllPanels(tree, true);
}
// applyFilterToMap(tree: MapUi, filterRegex: RegExp) => MapUi
export function applyFilterToMap(tree, filterRegex) {
return traverseMapUi(
tree,
node => {
// no children indicates a challenge node
// if leaf (challenge) then test if regex is a match
if (!Array.isArray(node.children)) {
// does challenge name meet filter criteria?
if (filterRegex.test(node.name)) {
// is challenge currently hidden?
if (node.isHidden) {
// unhide challenge, it matches
return {
...node,
isHidden: false
};
}
} else if (!node.isHidden) {
return {
...node,
isHidden: true
};
}
return node;
}
// if not leaf node (challenge) then
// test to see if all its children are hidden
if (node.children.every(node => node.isHidden)) {
if (node.isHidden) {
return node;
}
return {
...node,
isHidden: true
};
} else if (node.isHidden) {
return {
...node,
isHidden: false
};
}
// nothing has changed
return node;
}
);
}
// unfilterMapUi(tree: MapUi) => MapUi
export function unfilterMapUi(tree) {
return traverseMapUi(
tree,
node => {
if (!node.isHidden) {
return node;
}
return {
...node,
isHidden: false
};
}
);
}