tapspace
Version:
A zoomable user interface lib for web apps
107 lines (92 loc) • 2.82 kB
JavaScript
const treeDistance = require('./utils/treeDistance')
module.exports = function (rootIds, maxDepth, data) {
// @TreeLoader:closeNeighbors(rootIds, maxDepth[, data])
//
// Close those neighbors that are farther than the given depth
// from one of the given IDs.
//
// Parameters:
// rootIds
// array of string, for example `['1234', '2345']`
// maxDepth
// a number, for example `4`.
// If zero or negative, all neighbors will be closed except the roots.
// data
// optional object to be passed to 'close' event.
//
// Normalize to array
if (!Array.isArray(rootIds)) {
rootIds = [rootIds]
}
if (!maxDepth || maxDepth < 0) {
maxDepth = 0
}
if (!data) {
data = {}
}
// Use only those roots that exist
const rids = rootIds.filter((id) => {
if (this.spaces[id]) {
return true
}
return false
})
if (rids.length < 1) {
return
}
// Pick only non-root spaces for closing.
const neighborIds = Object.keys(this.spaces).filter((id) => {
return !rids.includes(id)
})
// Find depths
const findings = neighborIds.map(nid => {
const neighbor = this.spaces[nid]
// Measure to closest root
let minDepth = Infinity
for (let i = 0; i < rids.length; i += 1) {
const d = treeDistance(this.spaces, this.backtracker, rids[i], nid)
if (d >= 0) {
minDepth = Math.min(minDepth, d)
}
}
return {
id: nid,
space: neighbor,
depth: minDepth
}
})
// Split findings to those that we close and those that we keep.
const emits = []
const keeps = []
findings.forEach(finding => {
if (finding.depth > maxDepth) {
// Finding is outside the depth threshold.
const space = this.spaces[finding.id]
if (space) {
emits.push({ id: finding.id, space, data })
}
} else {
// Finding is inside the depth threshold, including depth === maxDepth.
keeps.push(finding.id)
}
})
// Clear loading states outside the neighborhood.
// Purpose: this prevents addition of spaces outside the neighborhood.
// Purpose: clear up loading states that never finished.
// Detail: to allow loading to continue inside the neighborhood,
// only clear up those loading states that were not initiated by
// the spaces within the neighborhood (keeps + roots).
const loadingIds = Object.keys(this.loading)
for (let i = 0; i < loadingIds.length; i += 1) {
const loadingId = loadingIds[i]
const initiatorId = this.loading[loadingId]
if (!keeps.includes(initiatorId) && !rids.includes(initiatorId)) {
delete this.loading[loadingId]
}
}
// Enable host app to hook to removals
for (let i = 0; i < emits.length; i += 1) {
const ev = emits[i]
this.emit('close', ev)
}
}