Skip to content

Commit 7ce1cf9

Browse files
authored
Merge pull request #41 from psychoinformatics-de/data
Next iteration of updates
2 parents 6267ea7 + aee8241 commit 7ce1cf9

14 files changed

+499
-158
lines changed

src/components/FormEditor.vue

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
<template>
2+
<v-sheet class="pa-4" border rounded elevation="2">
3+
<div style="display: flex;">
4+
<h2>{{ toCURIE(props.shape_iri, shapePrefixes) }}</h2>
5+
<span v-if="validationErrors.length" style="margin-left: auto;">
6+
<v-menu location="end">
7+
<template v-slot:activator="{ props }">
8+
<v-btn color="warning" v-bind="props" density="compact" icon="mdi-alert-circle-outline"></v-btn>
9+
</template>
10+
<v-list>
11+
<v-list-item v-for="e of validationErrors" @click="">
12+
<v-list-item-title> <em>{{ e.name }}</em></v-list-item-title>
13+
<v-list-item-subtitle>{{ e.message }}</v-list-item-subtitle>
14+
</v-list-item>
15+
</v-list>
16+
</v-menu>
17+
</span>
18+
</div>
19+
20+
<br>
21+
<p>{{ shape_obj[SHACL.description] }}</p>
22+
<br>
23+
<v-form ref="form" v-model="formValid" validate-on="lazy input" @submit.prevent="saveForm()" >
24+
<NodeShapeEditor :key="props.shape_iri" :shape_iri="props.shape_iri"/>
25+
<div style="display: flex;">
26+
<v-btn
27+
class="mt-2"
28+
text="Reset"
29+
@click="resetForm()"
30+
style="margin-left: auto; margin-right: 1em;"
31+
></v-btn>
32+
<v-btn
33+
class="mt-2"
34+
text="Save"
35+
type="submit"
36+
></v-btn>
37+
</div>
38+
</v-form>
39+
</v-sheet>
40+
</template>
41+
42+
43+
<script setup>
44+
import { ref, onMounted, onBeforeMount, provide, inject, reactive} from 'vue'
45+
import { SHACL } from '../modules/namespaces'
46+
import { toCURIE } from '../modules/utils';
47+
48+
// ----- //
49+
// Props //
50+
// ----- //
51+
52+
const props = defineProps({
53+
shape_iri: String,
54+
})
55+
56+
// ---- //
57+
// Data //
58+
// ---- //
59+
60+
const add_empty_node = inject('add_empty_node')
61+
const shapePrefixes = inject('shapePrefixes');
62+
const nodeShapes = inject('nodeShapes')
63+
const shape_obj = nodeShapes.value[props.shape_iri]
64+
const form = ref(null)
65+
const formValid = ref(null)
66+
const fieldMap = reactive({}); // Maps element IDs to human-readable labels
67+
const validationErrors = ref([]);
68+
69+
function registerRef(id, fieldData) {
70+
fieldMap[id] = fieldData;
71+
}
72+
function unregisterRef(id) {
73+
delete fieldMap[id];
74+
}
75+
76+
provide('registerRef', registerRef);
77+
provide('unregisterRef', unregisterRef);
78+
79+
80+
// ----------------- //
81+
// Lifecycle methods //
82+
// ----------------- //
83+
84+
onBeforeMount(() => {
85+
console.log(`the FormEditor component is about to be mounted.`)
86+
})
87+
88+
onMounted(() => {
89+
console.log(`the FormEditor component is now mounted.`)
90+
})
91+
92+
// ------------------- //
93+
// Computed properties //
94+
// ------------------- //
95+
96+
97+
98+
// --------- //
99+
// Functions //
100+
// --------- //
101+
102+
async function saveForm() {
103+
try {
104+
105+
validationErrors.value = []
106+
// Await the validation result
107+
const validationResult = await form.value.validate();
108+
109+
110+
if (validationResult.valid) {
111+
// If the form is valid, proceed with form submission
112+
add_empty_node(props.shape_iri);
113+
} else {
114+
console.log("Still some validation errors, bro");
115+
116+
validationResult.errors.forEach(error => {
117+
const id = error.id;
118+
const fieldData = fieldMap[id];
119+
if (fieldData) {
120+
validationErrors.value.push({
121+
name: fieldData.name,
122+
message: error.errorMessages.join(', '),
123+
});
124+
}
125+
});
126+
}
127+
} catch (error) {
128+
console.error("Validation failed:", error);
129+
}
130+
}
131+
132+
function resetForm() {
133+
form.value.reset()
134+
}
135+
136+
137+
138+
</script>

src/components/InstancesSelectEditor.vue

Lines changed: 73 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,29 +2,53 @@
22
<v-autocomplete
33
density="compact"
44
variant="outlined"
5-
label="(instances select editor)"
5+
label="select an item"
66
v-model="triple_object"
77
:items="instanceItems"
88
validate-on="lazy input"
99
:rules="rules"
1010
item-value="value"
1111
item-text="title"
1212
return-object
13+
ref="fieldRef"
14+
:id="inputId"
1315
>
1416

1517
<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>
18+
<div v-if="data.item.props.isButton">
19+
<v-list-item>
20+
<v-list-item-title>
21+
<v-menu location="end">
22+
<template v-slot:activator="{ props }">
23+
<v-btn variant="tonal" v-bind="props">{{ data.item.title }} &nbsp;&nbsp; <v-icon icon="item.icon">mdi-play</v-icon></v-btn>
24+
</template>
25+
26+
<v-list>
27+
<v-list-item v-for="item in propClassList" @click="add_empty_node(item.value)">
28+
<v-list-item-title>{{ item.title }}</v-list-item-title>
29+
<v-dialog activator="parent" max-width="700">
30+
<template v-slot:default="{ isActive }">
31+
<FormEditor :shape_iri="item.value"></FormEditor>
32+
</template>
33+
</v-dialog>
34+
</v-list-item>
35+
</v-list>
36+
</v-menu>
37+
38+
</v-list-item-title>
39+
</v-list-item>
40+
</div>
41+
<div v-else>
42+
<v-list-item @click="selectItem(data.item)">
43+
<v-list-item-title>{{ data.item.title }}</v-list-item-title>
44+
<v-list-item-subtitle>{{ data.item.props.subtitle }}</v-list-item-subtitle>
45+
</v-list-item>
46+
</div>
2647
</template>
2748
</v-autocomplete>
49+
50+
51+
2852
</template>
2953

3054
<script setup>
@@ -33,6 +57,9 @@
3357
import rdf from 'rdf-ext'
3458
import {SHACL, RDF, RDFS, DLTHING, XSD} from '@/modules/namespaces'
3559
import { toCURIE } from '../modules/utils';
60+
import { useRegisterRef } from '../composables/refregister';
61+
62+
3663
3764
// ----- //
3865
// Props //
@@ -50,16 +77,21 @@
5077
// ---- //
5178
5279
const formData = inject('formData');
80+
const propertyGroups = inject('propertyGroups');
81+
const nodeShapes = inject('nodeShapes');
5382
const graphData = inject('graphData');
83+
const add_empty_node = inject('add_empty_node');
5484
const graphPrefixes = inject('graphPrefixes');
5585
const shapePrefixes = inject('shapePrefixes');
5686
const classPrefixes = inject('classPrefixes');
87+
const allPrefixes = {...shapePrefixes, ...graphPrefixes, ...classPrefixes};
5788
const classData = inject('classData');
5889
const { rules } = useRules(props.property_shape)
5990
var propClass = ref(null)
60-
const combinedQuads = reactive([])
6191
const instanceItems = reactive([])
62-
92+
const inputId = `input-${Date.now()}`;
93+
const { fieldRef } = useRegisterRef(inputId, props);
94+
6395
// ------------------- //
6496
// Computed properties //
6597
// ------------------- //
@@ -85,13 +117,12 @@
85117
// ---
86118
// TODO: what should the correct default value be here?
87119
propClass.value = props.property_shape[SHACL.class.value] ?? false
88-
let allprefixes = {...shapePrefixes, ...graphPrefixes, ...classPrefixes};
89120
// find nodes with predicate rdf:type and object being the property class
90121
var quads = getLiteralAndNamedNodes(
91122
graphData,
92123
rdf.namedNode(RDF.type),
93124
propClass.value,
94-
allprefixes
125+
allPrefixes
95126
)
96127
// then find nodes with predicate rdfs:subClassOf and object being the property class
97128
// TODO: here we are only using a named node for the object because this is how the
@@ -105,7 +136,7 @@
105136
var myArr = []
106137
Array.from(subClasses).forEach(quad => {
107138
const cl = quad.subject.value
108-
myArr = myArr.concat(getLiteralAndNamedNodes(graphData, rdf.namedNode(RDF.type), cl, allprefixes))
139+
myArr = myArr.concat(getLiteralAndNamedNodes(graphData, rdf.namedNode(RDF.type), cl, allPrefixes))
109140
});
110141
// Then combine all quad arrays
111142
const combinedQuads = quads.concat(myArr);
@@ -126,12 +157,36 @@
126157
{
127158
title: quad.subject.value + extra,
128159
value: quad.subject.value,
129-
props: { subtitle: toCURIE(quad.object.value, allprefixes) },
160+
props: { subtitle: toCURIE(quad.object.value, allPrefixes) },
130161
}
131162
)
132163
});
133164
})
134165
166+
const propClassList = computed(() => {
167+
var items = []
168+
// first add main property class
169+
items.push(
170+
{
171+
title: toCURIE(propClass.value, allPrefixes),
172+
value: propClass.value
173+
}
174+
)
175+
const subClasses = rdf.grapoi({ dataset: classData })
176+
.hasOut(rdf.namedNode(RDFS.subClassOf.value), rdf.namedNode(propClass.value))
177+
.quads();
178+
179+
Array.from(subClasses).forEach(quad => {
180+
items.push(
181+
{
182+
title: toCURIE(quad.subject.value, allPrefixes),
183+
value: quad.subject.value
184+
}
185+
)
186+
});
187+
return items
188+
})
189+
135190
// --------- //
136191
// Functions //
137192
// --------- //
@@ -152,9 +207,9 @@
152207
153208
function selectItem(item) {
154209
triple_object.value = item.value;
210+
fieldRef.value.blur();
155211
}
156212
157-
158213
</script>
159214
160215
<!-- Component matching logic -->

src/components/MainForm.vue

Lines changed: 9 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -14,28 +14,10 @@
1414
<v-row align="start" style="flex-wrap: nowrap" no-gutters>
1515
<!-- Display selected form -->
1616
<v-col cols="6">
17-
<h3>Selected form</h3>
18-
<v-sheet class="pa-4" border rounded elevation="2">
19-
<span v-if="selectedIRI && prefixes_ready">
20-
<v-form ref="form" validate-on="submit lazy" @submit.prevent="saveForm()" >
21-
<NodeShapeEditor :key="selectedIRI" :shape_iri="selectedIRI" :shape_obj="selectedShape" :prop_groups="propertyGroups"/>
22-
23-
<div style="display: flex;">
24-
<v-btn
25-
class="mt-2"
26-
text="Reset"
27-
@click="resetForm()"
28-
style="margin-left: auto; margin-right: 1em;"
29-
></v-btn>
30-
<v-btn
31-
class="mt-2"
32-
text="Save"
33-
type="submit"
34-
></v-btn>
35-
</div>
36-
</v-form>
37-
</span>
38-
</v-sheet>
17+
<span v-if="selectedIRI && prefixes_ready">
18+
<h3>Selected form</h3>
19+
<FormEditor :key="selectedIRI" :shape_iri="selectedIRI"></FormEditor>
20+
</span>
3921
</v-col>
4022
<!-- Display entered form data -->
4123
<v-col class="ml-6">
@@ -115,6 +97,11 @@
11597
provide('shapePrefixes', shapePrefixes)
11698
provide('editorMatchers', editorMatchers)
11799
provide('defaultEditor', defaultEditor)
100+
provide('nodeShapes', nodeShapes)
101+
provide('propertyGroups', propertyGroups)
102+
provide('shapesDataset', shapesDataset)
103+
provide('prefixArray', prefixArray)
104+
provide('nodeShapeIRIs', nodeShapeIRIs)
118105
119106
120107
// ----------------- //

0 commit comments

Comments
 (0)