147 lines
4.7 KiB
JavaScript
147 lines
4.7 KiB
JavaScript
// Global ATON
|
|
import { Controller } from "@hotwired/stimulus";
|
|
import AppState from "../state.js";
|
|
import { traverseOntology } from "../ontology.js";
|
|
|
|
const html = String.raw;
|
|
const domParser = new DOMParser;
|
|
// TODO: hard-coded, but follows a convention...
|
|
const ontologyJsonPath = location.pathname + 'ontology.json';
|
|
|
|
export default class extends Controller {
|
|
static targets = ['trigger', 'layers', 'ontology'];
|
|
|
|
connect() {
|
|
console.log('#menu controller connected');
|
|
}
|
|
/**
|
|
* Open settings panel
|
|
*/
|
|
async toggleMenu() {
|
|
ATON.UI.setSidePanelRight();
|
|
ATON.UI.showSidePanel({header: 'Menu'});
|
|
this.#buildMenuPanel(ATON.UI.elSidePanel);
|
|
this.#buildLayersMenu(AppState.normalizedNodes, this.layersTarget);
|
|
this.#buildOntologyMenu(await traverseOntology(ontologyJsonPath), this.ontologyTarget);
|
|
}
|
|
/**
|
|
* @param {Event} event
|
|
*/
|
|
toggleNode(event) {
|
|
/**
|
|
* The node's id
|
|
* @type {string}
|
|
*/
|
|
const id = event?.params.node;
|
|
const status = event?.target?.checked;
|
|
const node = AppState.normalizedNodes.find(n => n.id === id);
|
|
|
|
if (node.children.length > 0) {
|
|
this.#toggleGroup(node, status);
|
|
this.#syncGroupCheckboxes(node, status, this.layersTarget);
|
|
} else {
|
|
ATON.getSceneNode(id).toggle(status);
|
|
node.active = status;
|
|
}
|
|
}
|
|
/**
|
|
* Recursively toggle children in a nodes group
|
|
* @param {Object} groupNode
|
|
* @param {Boolean} status
|
|
*/
|
|
#toggleGroup(groupNode, status) {
|
|
for (const child of groupNode.children) {
|
|
if (child.model) {
|
|
ATON.getSceneNode(child.id).toggle(status);
|
|
child.active = status;
|
|
}
|
|
if (child.children.length > 0) {
|
|
this.#toggleGroup(child, status);
|
|
}
|
|
}
|
|
}
|
|
#syncGroupCheckboxes(groupNode, status, container) {
|
|
for (const child of groupNode.children) {
|
|
const checkbox = container.querySelector(
|
|
`[data-menu-node-param="${child.id}"]`
|
|
);
|
|
|
|
console.debug(checkbox, child.id);
|
|
|
|
if (checkbox) checkbox.checked = status;
|
|
|
|
if (child.children.length > 0) {
|
|
this.#syncGroupCheckboxes(child, status, container);
|
|
}
|
|
}
|
|
}
|
|
/**
|
|
* Clone a <template> by id
|
|
* @param {String} id
|
|
* @returns {DocumentFragment}
|
|
*/
|
|
#cloneTemplate(id) {
|
|
return document.getElementById(id)?.content?.cloneNode(true);
|
|
}
|
|
/**
|
|
* Create the left-side settings panel
|
|
* content
|
|
* @param {Element} panel
|
|
*/
|
|
#buildMenuPanel(panel) {
|
|
const fragment = this.#cloneTemplate('tmpl-menu-tabs');
|
|
|
|
panel.appendChild(fragment);
|
|
}
|
|
/**
|
|
* @todo Don't rebuild it every time, use caching, return a container with checkboxes
|
|
* @param {Array<import("../state.js").NormalizedSceneNode>} nodes The normalized scene nodes (IDs and status)
|
|
* @param {HTMLElement} tab Tab content element
|
|
*/
|
|
#buildLayersMenu(nodes, tab) {
|
|
for(let node of nodes) {
|
|
const menuItem = html`
|
|
<div class="form-check form-switch ms-${node.depth} ps-${node.depth} mt-2">
|
|
<input class="form-check-input" type="checkbox" ${node.active ? 'checked' : ''} role="switch"
|
|
title="Mostra / nascondi layer"
|
|
data-menu-node-param="${node.id}"
|
|
data-action="change->menu#toggleNode">
|
|
<label class="form-check-label">${node.id}</label>
|
|
</div>
|
|
`;
|
|
|
|
// Awful?
|
|
tab.appendChild(
|
|
domParser.parseFromString(menuItem, 'text/html').querySelector('div')
|
|
);
|
|
}
|
|
}
|
|
/**
|
|
* Temporary implementation to show domains only
|
|
* @todo Don't rebuild it every time, use caching, return a container
|
|
* @param {Object} ontology The traversed ontology object (temp)
|
|
* @param {HTMLElement} tab Tab content element
|
|
*/
|
|
#buildOntologyMenu(ontology, tab) {
|
|
console.debug(ontology);
|
|
|
|
const mainNode = tab.querySelector('#ontology-list');
|
|
mainNode.textContent = ontology.ontology;
|
|
|
|
let domainList = html`
|
|
<ul class="list-group mt-2" id="domains-list"></ul>
|
|
`;
|
|
|
|
// Very fragile and ugly!!
|
|
mainNode.innerHTML += domainList;
|
|
domainList = tab.querySelector('#domains-list');
|
|
|
|
for(let domain of ontology.domains) {
|
|
const domainItem = html`
|
|
<li class="list-group-item">${domain.label}</li>
|
|
`;
|
|
domainList.innerHTML += domainItem;
|
|
}
|
|
}
|
|
}
|