Classes and instances in ontology menu

This commit is contained in:
2026-07-07 16:26:31 +02:00
parent 60646cebf6
commit 6a8acfeb0f
4 changed files with 4443 additions and 26 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -141,25 +141,20 @@
<!-- Nav tabs --> <!-- Nav tabs -->
<ul class="nav nav-pills" id="content-tabs" role="tablist"> <ul class="nav nav-pills" id="content-tabs" role="tablist">
<li class="nav-item" role="presentation"> <li class="nav-item" role="presentation">
<button class="nav-link active" id="layer-tab" data-bs-toggle="tab" data-bs-target="#layer" type="button" role="tab" aria-controls="layer" aria-selected="false"> <button class="nav-link active" id="layer-tab" data-bs-toggle="tab" data-bs-target="#layer" type="button" role="tab" aria-controls="layer" aria-selected="true">
<i class="bi bi-boxes me-1"></i> Elementi 3D <i class="bi bi-boxes me-1"></i> Elementi 3D
</button> </button>
</li> </li>
<li class="nav-item" role="presentation"> <li class="nav-item" role="presentation">
<button class="nav-link" id="content-tab" data-bs-toggle="tab" data-bs-target="#content" type="button" role="tab" aria-controls="media" aria-selected="true"> <button class="nav-link" id="content-tab" data-bs-toggle="tab" data-bs-target="#content" type="button" role="tab" aria-controls="media" aria-selected="false">
<i class="bi bi-diagram-3 me-1"></i> Contenuti <i class="bi bi-diagram-3 me-1"></i> Ontologia
</button> </button>
</li> </li>
</ul> </ul>
<!-- Tab panes --> <!-- Tab panes -->
<div class="tab-content ps-2 ms-2 overflow-y-auto"> <div class="tab-content ps-2 ms-2 overflow-y-auto">
<div class="tab-pane active ps-3 pt-0 mt-4 ms-0" data-menu-target="layers" id="layer" role="tabpanel" aria-labelledby="layer-tab" tabindex="0"></div> <div class="tab-pane active ps-3 pt-0 mt-4 ms-0" data-menu-target="layers" id="layer" role="tabpanel" aria-labelledby="layer-tab" tabindex="0"></div>
<div class="tab-pane pt-3" data-menu-target="ontology" id="content" role="tabpanel" aria-labelledby="media-tab" tabindex="0"> <div class="tab-pane pt-3" data-menu-target="ontology" id="content" role="tabpanel" aria-labelledby="media-tab" tabindex="0"></div>
<!-- Temporary -->
<ul class="list-group me-4 ms-0">
<li class="list-group-item pt-2 pb-2" id="ontology-list"></li>
</ul>
</div>
</div> </div>
</template> </template>

View File

@@ -1,18 +1,19 @@
// Global ATON // Global ATON
import { Controller } from "@hotwired/stimulus"; import { Controller } from "@hotwired/stimulus";
import AppState from "../state.js"; import AppState from "../state.js";
import { traverseOntology } from "../ontology.js"; import { loadOntology, traverseOntology, Ontology } from "../ontology.js";
const html = String.raw; const html = String.raw;
const domParser = new DOMParser; const domParser = new DOMParser;
// TODO: hard-coded, but follows a convention... // TODO: hard-coded, but follows a convention...
const ontologyJsonPath = '/a/scaenae/scenes/BaroqueTheatreOntology_BT_EN_v1_7_UI_by_domain_v3.json'; const ontologyJsonPath = '/a/scaenae/scenes/BaroqueTheatreOntology_BT_v1_8_ATON_by_domain_with_instances.json';
export default class extends Controller { export default class extends Controller {
static targets = ['trigger', 'layers', 'ontology']; static targets = ['trigger', 'layers', 'ontology'];
connect() { async connect() {
console.log('#menu controller connected'); console.log('#menu controller connected');
this.ontologyObject = await loadOntology(ontologyJsonPath);
} }
/** /**
* Open settings panel * Open settings panel
@@ -22,7 +23,7 @@ export default class extends Controller {
ATON.UI.showSidePanel({header: 'Menu'}); ATON.UI.showSidePanel({header: 'Menu'});
this.#buildMenuPanel(ATON.UI.elSidePanel); this.#buildMenuPanel(ATON.UI.elSidePanel);
this.#buildLayersMenu(AppState.treeNodes, this.layersTarget); this.#buildLayersMenu(AppState.treeNodes, this.layersTarget);
this.#buildOntologyMenu(await traverseOntology(ontologyJsonPath), this.ontologyTarget); this.#buildOntologyMenu(await traverseOntology(this.ontologyObject), this.ontologyTarget);
} }
/** /**
* @param {Event} event * @param {Event} event
@@ -229,24 +230,31 @@ export default class extends Controller {
* @param {HTMLElement} tab Tab content element * @param {HTMLElement} tab Tab content element
*/ */
#buildOntologyMenu(domains, tab) { #buildOntologyMenu(domains, tab) {
const mainNode = tab.querySelector('#ontology-list'); const ontology = new Ontology;
mainNode.classList.add('pb-3'); ontology.data = this.ontologyObject;
const tabs = this.#createOntologyTabs().tabs;
const content = this.#createOntologyTabs().content;
tab.appendChild(tabs);
tab.appendChild(content);
const classesTab = content.querySelector('#classes');
const instancesTab = content.querySelector('#instances');
for (let domain of domains) { for (let domain of domains) {
const heading = document.createElement('span'); const heading = document.createElement('span');
heading.className ="fs-5 fw-bold"; heading.className ="fs-5 fw-bold";
heading.textContent = domain.id; heading.textContent = domain.id;
mainNode.appendChild(heading); classesTab.appendChild(heading);
let domainList = document.createElement('div'); let domainList = document.createElement('div');
console.debug(domain.tree);
// Traverse class trees for each domain // Traverse class trees for each domain
for (let item of domain.tree) { for (let item of domain.tree) {
const domainItem = document.createElement('div'); const domainItem = document.createElement('div');
domainItem.className = 'border-start border-bottom rounded'; domainItem.className = 'border-start border-bottom rounded border-1 border-light-subtle';
domainItem.textContent = item.label; domainItem.textContent = item.label.it ?? item.label.en;
// Indentation... // Indentation...
domainItem.classList.add(`ms-${item.depth}`, `ps-${item.depth}`, 'py-2'); domainItem.classList.add(`ms-${item.depth}`, `ps-${item.depth}`, 'py-2');
@@ -255,7 +263,48 @@ export default class extends Controller {
domainList.appendChild(domainItem); domainList.appendChild(domainItem);
} }
mainNode.appendChild(domainList); classesTab.appendChild(domainList);
} }
const sortedItLabels = ontology.getInstanceLabels().map(l => l.it).sort();
for (let label of sortedItLabels) {
const instanceItem = document.createElement('div');
instanceItem.className = 'border-bottom ps-3 py-2';
instanceItem.textContent = label;
instancesTab.appendChild(instanceItem);
}
console.debug(ontology.getInstanceById('ex:SSGJP_StructuralBackdrop'), ontology.getInstanceLabels());
}
/**
* @returns {{tabs: HTMLUListElement, content: HTMLDivElement}}
*/
#createOntologyTabs() {
const tabs = html`
<ul class="nav nav-pills" role="tablist" id="ontoTabs">
<li class="nav-item" role="presentation">
<button class="nav-link active" id="classes-tab" data-bs-toggle="tab" data-bs-target="#classes" type="button"
role="tab" aria-controls="classes-tab-pane" aria-selected="true">Classi</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="instances-tab" data-bs-toggle="tab" data-bs-target="#instances" type="button"
role="tab" aria-controls="instances-tab-pane" aria-selected="true">Istanze</button>
</li>
</ul>
`;
const content = html`
<div class="tab-content" id="ontoTabsContent">
<div class="tab-pane fade p-3 show active" id="classes" role="tabpanel" aria-labelledby="classes-tab" tabindex="0"></div>
<div class="tab-pane fade" id="instances" role="tabpanel" aria-labelledby="instances-tab" tabindex="0"></div>
</div>
`;
return {
tabs: domParser.parseFromString(tabs, 'text/html').querySelector('ul'),
content: domParser.parseFromString(content, 'text/html').querySelector('div'),
};
} }
} }

View File

@@ -2,15 +2,47 @@
* @module Ontology * @module Ontology
*/ */
export class Ontology {
#data;
/**
* @param {Object} data The parsed ontology object (from JSON)
*/
set data(data) {
this.#data = data;
}
/**
* @param {String} id
*/
getInstanceById(id) {
return this.#data.instancesById[id];
}
/**
* @returns {Array<{en: string, it: string}>} The bloody array of label objects
*/
getInstanceLabels() {
const data = this.#data;
const instances = [];
for (const key of Object.keys(data.instancesById)) {
instances.push(data.instancesById[key]?.label);
}
return instances;
}
}
/** /**
* @todo Temporarily returns domains and ontology labels only * @todo Temporarily returns domains and ontology labels only
* Traverse an ontology from its JSON description * Traverse an ontology from its JSON description
* @param {String} jsonPath The path (URI) of the ontology JSON file * @param {Object} ontologyObject The parsed ontology JSON
* @returns {Object} * @returns {Object}
*/ */
export async function traverseOntology(jsonPath) { export async function traverseOntology(ontologyObject) {
const ontology = await loadOntology(jsonPath); //const ontology = await loadOntology(jsonPath);
const modules = ontology.modules; const modules = ontologyObject.domains;
const domains = []; const domains = [];
@@ -20,7 +52,7 @@ export async function traverseOntology(jsonPath) {
traverseClasses(classTree, tree); traverseClasses(classTree, tree);
} }
domains.push({ domains.push({
id: module.id, id: module.label.it ?? module.label.en,
tree tree
}); });
} }