import AppState from "./state.js"; import { changeLightDirection, toggleAmbientOcclusion } from "./utils/environment.js"; import { resetClipping, addClippingPlane } from "./utils/clipping.js"; import { traverseOntology } from "./ontology.js"; /** * @module UI */ const domParser = new DOMParser; const contentMenuTabs = `
`; const audioExample = ` `; /** * * @param {String} triggerSelector - Usually, the close modal trigger element(s) selector */ export function pauseAudio(triggerSelector) { // What if more than one audio element is playing? const audio = document.querySelector('audio'); if (audio) { document.querySelectorAll(triggerSelector).forEach(el => { el.addEventListener('click', () => audio.pause()); }); document.querySelector('.modal').addEventListener('blur', () => { audio.pause(); }); } } /** * Resets the UI state (essentially hides the clipper toolbar if visible...) * @todo Other elements to reset?? Restore inital lighting conditions and viewpoint... */ function reset() { document.querySelector('#clipper-bar')?.classList.add('d-none'); document.querySelector('#clipper')?.classList.remove('border', 'border-2', 'border-white'); } /** * Right-side main menu panel * @param {String} triggerId - The menu button id * @param {String} ontologyJsonPath */ function toggleContentMenu(triggerId, ontologyJsonPath) { const btn = document.querySelector(`#${triggerId}`); btn.addEventListener('click', async () => { ATON.UI.setSidePanelRight(); ATON.UI.showSidePanel({header: 'Menu'}); // Append tabs, then tab panes const tabs = domParser.parseFromString(contentMenuTabs, 'text/html'); ATON.UI.elSidePanel.appendChild(tabs.querySelector('#content-tabs')); ATON.UI.elSidePanel.appendChild(tabs.querySelector('.tab-content')); buildLayersMenu(AppState.normalizedNodes, ATON.UI.elSidePanel.querySelector('#layer')); buildOntologyMenu(await traverseOntology(ontologyJsonPath), ATON.UI.elSidePanel.querySelector('#content')); }); } /** * @todo Don't rebuild it every time the side panel is shown... * @param {Array} nodes The normalized scene nodes (IDs and status) * @param {HTMLElement} sidePanel ATON's side panel element */ function buildLayersMenu(nodes, sidePanel) { for(let node of nodes) { const menuItem = document.createElement('div'); menuItem.className = `form-check form-switch ms-${node.depth} ps-${node.depth} mt-2`; const checkbox = document.createElement('input'); checkbox.type = 'checkbox'; checkbox.className = "form-check-input"; checkbox.checked = node.active; checkbox.role = 'switch'; checkbox.title = "Mostra / nascondi layer"; menuItem.appendChild(checkbox); const label = document.createElement('label'); label.className = "form-check-label"; label.textContent = node.id; menuItem.appendChild(label); sidePanel.appendChild(menuItem); // Will this ever work?? menuItem.addEventListener('change', event => toggleNode(node.id, event.target.checked)); } /** * This is terrible... * @param {String} id * @param {Boolean} status */ const toggleNode = (id, status) => { ATON.getSceneNode(id).toggle(status); AppState.normalizedNodes.find(n => n.id === id).active = status; } } /** * @see traverseOntology * @param {Object} ontology The traversed ontology object (temp) * @param {HTMLElement} sidePanel ATON's side panel element */ function buildOntologyMenu(ontology, sidePanel) { const list = document.createElement('ul'); list.className = 'list-group'; const mainNode = document.createElement('li'); mainNode.className = 'list-group-item'; mainNode.textContent = ontology.ontology; const domainList = document.createElement('ul'); domainList.className = 'list-group'; for(let domain of ontology.domains) { const domainItem = document.createElement('li'); domainItem.textContent = domain.label; domainItem.className = 'list-group-item'; domainList.appendChild(domainItem); } mainNode.appendChild(domainList); list.appendChild(mainNode); sidePanel.appendChild(list); } /** * Initialize required components for scene UI * @param {String} ontologyJsonPath */ export async function initUI(ontologyJsonPath) { toggleContentMenu('menu', ontologyJsonPath); }