127 lines
4.0 KiB
JavaScript
127 lines
4.0 KiB
JavaScript
import { CheckboxTree, loadManifest } from '../vendor/checkbox-tree/checkbox-tree.js';
|
|
import { toTreeNodes } from './product-tree-model.js';
|
|
|
|
const STORAGE_KEY = 'tree-state-v2';
|
|
let productTree = null;
|
|
|
|
export function initializeTree(containerId, onSelectionChange) {
|
|
const container = document.getElementById(containerId);
|
|
if (!container) {
|
|
throw new Error(`Tree container not found: ${containerId}`);
|
|
}
|
|
|
|
productTree?.destroy();
|
|
productTree = new CheckboxTree(container, {
|
|
storageKey: STORAGE_KEY,
|
|
initiallyCollapsed: true,
|
|
stateEncoding: 'tiered',
|
|
onSelectionChange: event => {
|
|
onSelectionChange(event);
|
|
pushTreeState();
|
|
}
|
|
});
|
|
|
|
container.addEventListener('click', event => {
|
|
if (event.target.closest('.checkbox-tree__toggle')) {
|
|
pushTreeState();
|
|
}
|
|
});
|
|
window.addEventListener('popstate', () => {
|
|
productTree?.restoreStateFromLocation();
|
|
onSelectionChange({
|
|
selectedIds: productTree?.getSelectedIds() ?? [],
|
|
selectedNodes: productTree?.getSelectedNodes() ?? []
|
|
});
|
|
});
|
|
}
|
|
|
|
export async function renderProductTree(productsData) {
|
|
const manifestSource = await fetch('data/product-tree-manifest.json').then(response => response.json());
|
|
requireTree().options.manifest = loadManifest(manifestSource);
|
|
requireTree().setData(toTreeNodes(productsData));
|
|
}
|
|
|
|
export function getCheckedPaths() {
|
|
return productTree ? productTree.getSelectedNodes().map(node => node.metadata.path) : [];
|
|
}
|
|
|
|
export function getCheckedFileRefs() {
|
|
const addressedFiles = [];
|
|
const knownFiles = [];
|
|
|
|
productTree?.getSelectedNodes().forEach(node => {
|
|
const { path, addressed = [], known = [] } = node.metadata;
|
|
const productKey = path[0];
|
|
addressed.forEach(fileName => addressedFiles.push({ productKey, fileName }));
|
|
known.forEach(fileName => knownFiles.push({ productKey, fileName }));
|
|
});
|
|
|
|
return {
|
|
addressedFiles: dedupeFileRefs(addressedFiles),
|
|
knownFiles: dedupeFileRefs(knownFiles)
|
|
};
|
|
}
|
|
|
|
export function selectAddressedRelease(product, release) {
|
|
return selectIssueRelease(product, release, 'addressed');
|
|
}
|
|
|
|
export function selectIssueRelease(product, release, issueType = 'addressed') {
|
|
if (!productTree || !product || !release) return false;
|
|
const node = findIssueReleaseNode(productTree.nodesById.values(), product, release, issueType);
|
|
if (!node) return false;
|
|
|
|
productTree.setSelectedIds([node.id]);
|
|
pushTreeState();
|
|
return true;
|
|
}
|
|
|
|
export function findAddressedReleaseNode(nodes, product, release) {
|
|
return findIssueReleaseNode(nodes, product, release, 'addressed');
|
|
}
|
|
|
|
export function findIssueReleaseNode(nodes, product, release, issueType = 'addressed') {
|
|
if (issueType !== 'addressed' && issueType !== 'known') return undefined;
|
|
const filename = `${release}.md`;
|
|
return Array.from(nodes).find(candidate =>
|
|
candidate.metadata?.path?.[0] === product &&
|
|
candidate.metadata?.[issueType]?.includes(filename)
|
|
);
|
|
}
|
|
|
|
// State restoration now happens when setData renders the generic tree.
|
|
export function restoreTreeState() {}
|
|
|
|
function requireTree() {
|
|
if (!productTree) {
|
|
throw new Error('Tree is not initialized. Call initializeTree first.');
|
|
}
|
|
return productTree;
|
|
}
|
|
|
|
function pushTreeState() {
|
|
if (!productTree?.options.manifest) {
|
|
return;
|
|
}
|
|
const url = new URL(window.location.href);
|
|
url.hash = productTree.serializeStateToFragment();
|
|
if (url.href !== window.location.href) {
|
|
history.pushState(null, '', url);
|
|
}
|
|
}
|
|
|
|
function dedupeFileRefs(fileRefs) {
|
|
const seen = new Set();
|
|
return fileRefs.filter(({ productKey, fileName }) => {
|
|
if (!productKey || !fileName) {
|
|
return false;
|
|
}
|
|
const key = `${productKey}::${fileName}`;
|
|
if (seen.has(key)) {
|
|
return false;
|
|
}
|
|
seen.add(key);
|
|
return true;
|
|
});
|
|
}
|