ConceptTree
Component to display a hierarchical concept tree (based on narrower), implemented as a thin wrapper around ItemList.
It supports:
- selecting a concept (
v-model) - expanding/collapsing nodes (
open/close) - programmatic scrolling (
scrollToUri) - programmatic navigation in the hierarchy (
navigateToUri)
Props
modelValue(object, v-model) — currently selected concept (highlighted)
default:nullconcepts(array, required) — array of JSKOS concepts to be displayed (usually top concepts)hierarchy(boolean) — whether to display concept hierarchy (vianarrower)
default:trueitemListOptions(object) — options passed through toItemListviav-bind
default:{}
Slots
All slots are forwarded to the internal ItemList.
beforeList— content shown above the list.afterList— content shown below the list.beforeItem— content shown before an item (slot prop:{ item }whereitemis the concept).afterItem— content shown after an item (slot prop:{ item }whereitemis the concept).
Methods
The following methods are exposed via component ref (ref="conceptTree"):
navigateToUri(uriOrConcept, options)
Navigate to a concept URI in the hierarchy:
- If the concept is already rendered: scroll to it (and optionally select it)
- Otherwise: tries to open the path from the top concepts down to the target using:
- a registry (
_registry) if present - parent pointers (
ancestors[0]orbroader[0]) _getNarrower()to load children when needed
- a registry (
Signature
uriOrConcept:string | { uri: string }options(optional):select(boolean) — whether to setmodelValueto the target concept
default:trueonlyIfNotInView(boolean) — pass-through toscrollToUri
default:true
Returns
Promise<boolean>—trueif navigation succeeded, otherwisefalse.
Notes / limitations
- Works best with coli-conc/cocoda-sdk concepts where:
- a registry is attached (
concept._registryorconcept.inScheme[0]._registry) - concepts support
_getNarrower()
- a registry is attached (
- If no registry is available, or if the target cannot be reached from the provided
conceptsroots, it returnsfalse.
Events
select
Emitted when a concept is selected. Parameter is the same payload asItemList’sselectevent: an object with properties:item(the clicked concept)row(truewhen the click was initiated via the row, not on the item directly)
open
Emitted when a concept’s narrower concepts are opened. Parameter is the concept.close
Emitted when a concept’s narrower concepts are closed. Parameter is the concept.update:modelValue
Used for v-model updates. Will always be emitted together withselectwhen selection changes.
CSS Variables
--jskos-vue-conceptTree-selected-bgColor— row background color for selected concept (v-model)
default:#fdbd58aa--jskos-vue-conceptTree-hover-bgColor— row hover background color
default:#fdbd58aa
Examples
Please select a concept.
Loading concepts...
vue
<script setup>
import ConceptTree from "../../src/components/ConceptTree.vue"
import * as jskos from "jskos-tools"
import { cdk } from "cocoda-sdk"
import { reactive, onMounted, useTemplateRef } from "vue"
let registry
const state = reactive({
scheme: null,
async loadScheme() {
this.scheme = (await registry.getSchemes({
params: {
uri: "https://www.ixtheo.de/classification/",
},
}))[0]
},
concepts: null,
async loadConcepts() {
this.concepts = jskos.sortConcepts(await this.scheme._getTop())
},
async loadNarrower(concept) {
if (concept.narrower && !concept.narrower.includes(null)) {
return
}
concept.narrower = jskos.sortConcepts(await concept._getNarrower())
},
selected: null,
})
const conceptTree = useTemplateRef("conceptTree")
onMounted(async () => {
registry = cdk.initializeRegistry({
provider: "ConceptApi",
api: "https://coli-conc.gbv.de/api/",
})
await state.loadScheme()
await state.loadConcepts()
})
const alert = (...args) => window.alert(...args)
</script>
<p>
<button @click="conceptTree.navigateToUri('https://www.ixtheo.de/classification/VB')">
Navigate to VB Hermeneutik ; Philosophie
</button>
</p>
<p v-if="state.selected?.uri">
Selected: {{ state.selected.uri }}
<button @click="conceptTree.scrollToUri(state.selected.uri)">Scroll to selected</button>
</p>
<p v-else>
Please select a concept.
</p>
<concept-tree
ref="conceptTree"
v-if="state.concepts"
v-model="state.selected"
style="height: 400px; overflow-y: auto; border: 2px solid #0001;"
:concepts="state.concepts"
@open="state.loadNarrower($event)">
<template v-slot:beforeItem="{ item }">
<span
class="opacity-hover"
style="margin-right: 5px;"
@click.stop="alert(`Clicked on Star for item ${item.uri}`)">
⭐️
</span>
</template>
<template v-slot:afterItem="{ item }">
<div
class="opacity-hover"
style="position: absolute; width: 20px; right: 2px; top: 50%; transform: translateY(-50%);"
@click.stop="alert(`Clicked on Rocket for item ${item.uri}`)">
🚀
</div>
</template>
</concept-tree>
<div v-else>
Loading concepts...
</div>
<style>
.opacity-hover:hover {
opacity: 50%;
}
</style>