94 lines
3.0 KiB
JavaScript
94 lines
3.0 KiB
JavaScript
import { CheckboxTree } from '../lib/checkbox-tree/checkbox-tree.js';
|
|
import { getSortedKeys } from './sort.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,
|
|
onSelectionChange
|
|
});
|
|
}
|
|
|
|
export function renderProductTree(productsData) {
|
|
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)
|
|
};
|
|
}
|
|
|
|
// 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 toTreeNodes(value, path = []) {
|
|
return getSortedKeys(value)
|
|
.filter(key => key !== 'addressed' && key !== 'known')
|
|
.map(key => {
|
|
const childValue = value[key];
|
|
const childPath = [...path, key];
|
|
const isObject = childValue && typeof childValue === 'object';
|
|
const hasFiles = isObject && ('addressed' in childValue || 'known' in childValue);
|
|
const children = isObject ? toTreeNodes(childValue, childPath) : [];
|
|
|
|
return {
|
|
id: JSON.stringify(childPath),
|
|
label: key,
|
|
selectable: hasFiles || children.length > 0,
|
|
metadata: {
|
|
path: childPath,
|
|
addressed: hasFiles && Array.isArray(childValue.addressed) ? childValue.addressed : [],
|
|
known: hasFiles && Array.isArray(childValue.known) ? childValue.known : []
|
|
},
|
|
...(children.length > 0 ? { children } : {})
|
|
};
|
|
});
|
|
}
|
|
|
|
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;
|
|
});
|
|
}
|