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 -->
<ul class="nav nav-pills" id="content-tabs" role="tablist">
<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
</button>
</li>
<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">
<i class="bi bi-diagram-3 me-1"></i> Contenuti
<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> Ontologia
</button>
</li>
</ul>
<!-- Tab panes -->
<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 pt-3" data-menu-target="ontology" id="content" role="tabpanel" aria-labelledby="media-tab" tabindex="0">
<!-- 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 class="tab-pane pt-3" data-menu-target="ontology" id="content" role="tabpanel" aria-labelledby="media-tab" tabindex="0"></div>
</div>
</template>

View File

@@ -1,18 +1,19 @@
// Global ATON
import { Controller } from "@hotwired/stimulus";
import AppState from "../state.js";
import { traverseOntology } from "../ontology.js";
import { loadOntology, traverseOntology, Ontology } from "../ontology.js";
const html = String.raw;
const domParser = new DOMParser;
// 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 {
static targets = ['trigger', 'layers', 'ontology'];
connect() {
async connect() {
console.log('#menu controller connected');
this.ontologyObject = await loadOntology(ontologyJsonPath);
}
/**
* Open settings panel
@@ -22,7 +23,7 @@ export default class extends Controller {
ATON.UI.showSidePanel({header: 'Menu'});
this.#buildMenuPanel(ATON.UI.elSidePanel);
this.#buildLayersMenu(AppState.treeNodes, this.layersTarget);
this.#buildOntologyMenu(await traverseOntology(ontologyJsonPath), this.ontologyTarget);
this.#buildOntologyMenu(await traverseOntology(this.ontologyObject), this.ontologyTarget);
}
/**
* @param {Event} event
@@ -229,24 +230,31 @@ export default class extends Controller {
* @param {HTMLElement} tab Tab content element
*/
#buildOntologyMenu(domains, tab) {
const mainNode = tab.querySelector('#ontology-list');
mainNode.classList.add('pb-3');
const ontology = new Ontology;
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) {
const heading = document.createElement('span');
heading.className ="fs-5 fw-bold";
heading.textContent = domain.id;
mainNode.appendChild(heading);
classesTab.appendChild(heading);
let domainList = document.createElement('div');
console.debug(domain.tree);
// Traverse class trees for each domain
for (let item of domain.tree) {
const domainItem = document.createElement('div');
domainItem.className = 'border-start border-bottom rounded';
domainItem.textContent = item.label;
domainItem.className = 'border-start border-bottom rounded border-1 border-light-subtle';
domainItem.textContent = item.label.it ?? item.label.en;
// Indentation...
domainItem.classList.add(`ms-${item.depth}`, `ps-${item.depth}`, 'py-2');
@@ -255,7 +263,48 @@ export default class extends Controller {
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
*/
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
* 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}
*/
export async function traverseOntology(jsonPath) {
const ontology = await loadOntology(jsonPath);
const modules = ontology.modules;
export async function traverseOntology(ontologyObject) {
//const ontology = await loadOntology(jsonPath);
const modules = ontologyObject.domains;
const domains = [];
@@ -20,7 +52,7 @@ export async function traverseOntology(jsonPath) {
traverseClasses(classTree, tree);
}
domains.push({
id: module.id,
id: module.label.it ?? module.label.en,
tree
});
}