Skip to content

Commit 1fb3197

Browse files
committed
Include subclasses and 'add new item' in InstanceSelectEditor
When users want to select an item from a dropdown list with a specific target class (e.g. Contributor), the list should also show all items that belong to the subclasses of the target class (e.g. Person or Organization). In addition, if the preferred item is not found, the list should allow the option of adding more items, also belonging to both the target class and all subclasses. This commit provides changes to achieve this. The selection of a specific class to add as a new item, as well as entering the fields for the new item, remains as a TODO.
1 parent d96fff7 commit 1fb3197

File tree

1 file changed

+113
-22
lines changed

1 file changed

+113
-22
lines changed

src/components/InstancesSelectEditor.vue

Lines changed: 113 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -7,66 +7,157 @@
77
:items="instanceItems"
88
validate-on="lazy input"
99
:rules="rules"
10-
></v-autocomplete>
10+
item-value="value"
11+
item-text="title"
12+
return-object
13+
>
14+
15+
<template v-slot:item="data">
16+
<v-list-item @click="console.log('add new item')" v-if="data.item.props.isButton">
17+
<template v-slot:prepend>
18+
<v-icon icon="item.icon">mdi-plus-box</v-icon>
19+
</template>
20+
<v-list-item-title>{{ data.item.title }}</v-list-item-title>
21+
</v-list-item>
22+
<v-list-item @click="selectItem(data.item)" v-else>
23+
<v-list-item-title>{{ data.item.title }}</v-list-item-title>
24+
<v-list-item-subtitle>{{ data.item.props.subtitle }}</v-list-item-subtitle>
25+
</v-list-item>
26+
</template>
27+
</v-autocomplete>
1128
</template>
1229

1330
<script setup>
1431
import { inject, reactive, onMounted, ref, computed} from 'vue'
1532
import { useRules } from '../composables/rules'
1633
import rdf from 'rdf-ext'
17-
import {SHACL, RDF, DLTHING, XSD} from '@/modules/namespaces'
34+
import {SHACL, RDF, RDFS, DLTHING, XSD} from '@/modules/namespaces'
1835
import { toCURIE } from '../modules/utils';
1936
20-
const predicateSelector = DLTHING.meta_type
21-
// const predicateSelector = RDF.type.value
37+
// ----- //
38+
// Props //
39+
// ----- //
2240
2341
const props = defineProps({
2442
property_shape: Object,
2543
node_uid: String,
26-
triple_uid: String
44+
triple_uid: String,
45+
triple_idx: Number,
2746
})
47+
48+
// ---- //
49+
// Data //
50+
// ---- //
51+
2852
const formData = inject('formData');
2953
const graphData = inject('graphData');
3054
const graphPrefixes = inject('graphPrefixes');
3155
const shapePrefixes = inject('shapePrefixes');
56+
const classPrefixes = inject('classPrefixes');
57+
const classData = inject('classData');
3258
const { rules } = useRules(props.property_shape)
33-
// var classNodes = reactive(null)
34-
// var classNodes2 = reactive(null)
3559
var propClass = ref(null)
3660
const combinedQuads = reactive([])
3761
const instanceItems = reactive([])
3862
63+
// ------------------- //
64+
// Computed properties //
65+
// ------------------- //
66+
3967
const triple_object = computed({
4068
get() {
41-
return formData[props.node_uid].at(-1)[props.triple_uid].at(-1);
69+
return formData[props.node_uid].at(-1)[props.triple_uid][props.triple_idx];
4270
},
4371
set(value) {
4472
const node_idx = formData[props.node_uid].length - 1
45-
const triple_idx = formData[props.node_uid][node_idx][props.triple_uid].length - 1
46-
formData[props.node_uid][node_idx][props.triple_uid][triple_idx] = value;
73+
formData[props.node_uid][node_idx][props.triple_uid][props.triple_idx] = value;
4774
}
4875
});
4976
50-
onMounted(() => {
51-
propClass.value = props.property_shape[SHACL.class.value] ?? false // TODO: what should the correct default value be here?
77+
// ----------------- //
78+
// Lifecycle methods //
79+
// ----------------- //
5280
53-
let allprefixes = {...shapePrefixes, ...graphPrefixes};
54-
var propClassCurie = toCURIE(propClass.value, allprefixes)
55-
const literalNodes = rdf.grapoi({ dataset: graphData })
56-
.hasOut(predicateSelector, rdf.literal(String(propClassCurie), XSD.anyURI))
57-
.quads();
58-
const uriNodes = rdf.grapoi({ dataset: graphData })
59-
.hasOut(predicateSelector, propClass.value)
81+
onMounted(() => {
82+
// ---
83+
// The goal of this method is to populate the list of items for the
84+
// InstancesSelectEditor
85+
// ---
86+
// TODO: what should the correct default value be here?
87+
propClass.value = props.property_shape[SHACL.class.value] ?? false
88+
let allprefixes = {...shapePrefixes, ...graphPrefixes, ...classPrefixes};
89+
// find nodes with predicate rdf:type and object being the property class
90+
var quads = getLiteralAndNamedNodes(
91+
graphData,
92+
rdf.namedNode(RDF.type),
93+
propClass.value,
94+
allprefixes
95+
)
96+
// then find nodes with predicate rdfs:subClassOf and object being the property class
97+
// TODO: here we are only using a named node for the object because this is how the
98+
// tools/gen_owl_minimal.py script outputs the triples in the ttl file. This should be
99+
// generalised
100+
const subClasses = rdf.grapoi({ dataset: classData })
101+
.hasOut(rdf.namedNode(RDFS.subClassOf.value), rdf.namedNode(propClass.value))
60102
.quads();
61-
const combinedQuads = Array.from(literalNodes).concat(Array.from(uriNodes));
62-
console.log("Concatenated results")
103+
// For each subclass, find the quads in graphData that has the class name as object
104+
// and RDF.type as predicate
105+
var myArr = []
106+
Array.from(subClasses).forEach(quad => {
107+
const cl = quad.subject.value
108+
myArr = myArr.concat(getLiteralAndNamedNodes(graphData, rdf.namedNode(RDF.type), cl, allprefixes))
109+
});
110+
// Then combine all quad arrays
111+
const combinedQuads = quads.concat(myArr);
112+
// Finally, create list items from quads
113+
instanceItems.push(
114+
{
115+
title: "Add New Item",
116+
props: { subtitle: "bla", isButton: true, },
117+
}
118+
)
63119
combinedQuads.forEach(quad => {
64120
console.log(`\t${quad.subject.value}`)
65-
instanceItems.push(quad.subject.value)
121+
var extra = ''
122+
if (quad.subject.termType === 'BlankNode') {
123+
extra = ' (BlankNode)'
124+
}
125+
instanceItems.push(
126+
{
127+
title: quad.subject.value + extra,
128+
value: quad.subject.value,
129+
props: { subtitle: toCURIE(quad.object.value, allprefixes) },
130+
}
131+
)
66132
});
67133
})
134+
135+
// --------- //
136+
// Functions //
137+
// --------- //
138+
139+
function getLiteralAndNamedNodes(graphData, predicate, propertyClass, prefixes) {
140+
var propClassCurie = toCURIE(propertyClass, prefixes)
141+
// a) use the literal node with xsd data type
142+
const literalNodes = rdf.grapoi({ dataset: graphData })
143+
.hasOut(predicate, rdf.literal(String(propClassCurie), XSD.anyURI))
144+
.quads();
145+
// b) and the named node
146+
const uriNodes = rdf.grapoi({ dataset: graphData })
147+
.hasOut(predicate, rdf.namedNode(propClass.value))
148+
.quads();
149+
// return as a concatenated array of quads
150+
return Array.from(literalNodes).concat(Array.from(uriNodes))
151+
}
152+
153+
function selectItem(item) {
154+
triple_object.value = item.value;
155+
}
156+
157+
68158
</script>
69159
160+
<!-- Component matching logic -->
70161
71162
<script>
72163
import { SHACL } from '@/modules/namespaces'

0 commit comments

Comments
 (0)