device-navigation
Version:
Navigate HTML elements in two dimensions with non-pointer devices.
84 lines (83 loc) • 2.33 kB
JavaScript
import { check } from '@augment-vir/assert';
import { getOrSet } from '@augment-vir/common';
import { extractNavEntry } from '../directives/nav-entry.js';
/**
* Maps an `ElementTree` to a {@link NavTree}.
*
* @category Internal
*/
export function mapTree(elementTree) {
const children = mapTreeRecursively(elementTree)?.children || [];
return {
root: true,
children,
};
}
function mapTreeRecursively(elementTree) {
const element = elementTree.element;
if (!(element instanceof HTMLElement)) {
return undefined;
}
const navEntry = extractNavEntry(element);
const children = expandChildren(elementTree);
const isValidGroup = navEntry?.navParams.group ? !!children.length : false;
if (isValidGroup || !!children.length || !!navEntry) {
return {
root: false,
element,
navEntry,
children,
};
}
else {
return undefined;
}
}
function expandChildren(elementTreeNode) {
const rawChildren = [];
function pushNode(node) {
if (node.navEntry?.navParams.group && !node.children.length) {
return;
}
else if (!node.navEntry) {
node.children.forEach((row) => row.forEach((child) => pushNode(child)));
return;
}
const x = node.navEntry.navParams.x;
const y = node.navEntry.navParams.y || 0;
const row = getOrSet(rawChildren, y, () => {
return {
noX: [],
withX: [],
y,
};
});
if (x == undefined) {
row.noX.push(node);
}
else {
row.withX.push({ x, node });
}
}
elementTreeNode.children.forEach((child) => {
const newNode = mapTreeRecursively(child);
if (newNode) {
pushNode(newNode);
}
});
// eslint-disable-next-line sonarjs/no-misleading-array-reverse
return rawChildren
.sort((rowA, rowB) => {
return rowA.y - rowB.y;
})
.map((row) => {
row.withX.sort((a, b) => {
return a.x - b.x;
});
row.withX.forEach(({ x, node }) => {
row.noX.splice(x, 0, node);
});
return row.noX;
})
.filter(check.isTruthy);
}