@ulb-darmstadt/shacl-form 1.8.2 → 1.8.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -12
- package/dist/constants.d.ts +2 -0
- package/dist/form-bootstrap.js +69 -65
- package/dist/form-default.js +66 -62
- package/dist/form-material.js +99 -95
- package/dist/plugins/leaflet.js +7 -7
- package/dist/plugins/mapbox.js +19 -19
- package/package.json +2 -2
- package/src/constants.ts +2 -0
- package/src/property.ts +1 -0
- package/src/styles.css +2 -2
- package/src/util.ts +51 -26
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ulb-darmstadt/shacl-form",
|
|
3
|
-
"version": "1.8.
|
|
3
|
+
"version": "1.8.3",
|
|
4
4
|
"description": "SHACL form generator",
|
|
5
5
|
"main": "dist/form-default.js",
|
|
6
6
|
"module": "dist/form-default.js",
|
|
@@ -71,7 +71,7 @@
|
|
|
71
71
|
"uuid": "^11.1.0"
|
|
72
72
|
},
|
|
73
73
|
"peerDependencies": {
|
|
74
|
-
"@ro-kit/ui-widgets": "^0.0.
|
|
74
|
+
"@ro-kit/ui-widgets": "^0.0.31",
|
|
75
75
|
"mdui": "^2.1.4"
|
|
76
76
|
}
|
|
77
77
|
}
|
package/src/constants.ts
CHANGED
|
@@ -18,6 +18,8 @@ export const RDF_PREDICATE_TYPE = DataFactory.namedNode(PREFIX_RDF + 'type')
|
|
|
18
18
|
export const DCTERMS_PREDICATE_CONFORMS_TO = DataFactory.namedNode(PREFIX_DCTERMS + 'conformsTo')
|
|
19
19
|
export const RDFS_PREDICATE_SUBCLASS_OF = DataFactory.namedNode(PREFIX_RDFS + 'subClassOf')
|
|
20
20
|
export const OWL_PREDICATE_IMPORTS = DataFactory.namedNode(PREFIX_OWL + 'imports')
|
|
21
|
+
export const SKOS_PREDICATE_BROADER = DataFactory.namedNode(PREFIX_SKOS + 'broader')
|
|
22
|
+
export const SKOS_PREDICATE_NARROWER = DataFactory.namedNode(PREFIX_SKOS + 'narrower')
|
|
21
23
|
export const SHACL_OBJECT_NODE_SHAPE = DataFactory.namedNode(PREFIX_SHACL + 'NodeShape')
|
|
22
24
|
export const SHACL_OBJECT_IRI = DataFactory.namedNode(PREFIX_SHACL + 'IRI')
|
|
23
25
|
export const SHACL_PREDICATE_PROPERTY = DataFactory.namedNode(PREFIX_SHACL + 'property')
|
package/src/property.ts
CHANGED
|
@@ -294,6 +294,7 @@ function appendRemoveButton(instance: HTMLElement, label: string, forceRemovable
|
|
|
294
294
|
removeButton.classList.add('remove-button', 'clear')
|
|
295
295
|
removeButton.title = 'Remove ' + label
|
|
296
296
|
removeButton.dense = true
|
|
297
|
+
removeButton.icon = true
|
|
297
298
|
removeButton.addEventListener('click', _ => {
|
|
298
299
|
instance.classList.remove('fadeIn')
|
|
299
300
|
instance.classList.add('fadeOut')
|
package/src/styles.css
CHANGED
|
@@ -13,7 +13,7 @@ shacl-property:not(.may-remove) > .property-instance > .remove-button:not(.persi
|
|
|
13
13
|
shacl-property:not(.may-remove) > .shacl-or-constraint > .remove-button:not(.persistent) { visibility: hidden; }
|
|
14
14
|
.mode-view .shacl-group:not(:has(shacl-property)) { display: none; }
|
|
15
15
|
.property-instance, .shacl-or-constraint { display: flex; align-items: flex-start; padding: 4px 0; width: 100%; position: relative; }
|
|
16
|
-
.shacl-or-constraint > div { display: flex; flex-grow: 1; }
|
|
16
|
+
.shacl-or-constraint > div { display: flex; flex-grow: 1; align-items: flex-start; }
|
|
17
17
|
.shacl-or-constraint label { display: inline-block; word-break: break-word; width: var(--label-width); line-height: 1em; padding-top: 0.15em; padding-right: 1em; flex-shrink: 0; position: relative; }
|
|
18
18
|
.property-instance label[title] { cursor: help; text-decoration: underline dashed #AAA; }
|
|
19
19
|
.property-instance.linked label:after, label.linked:after { content: '\1F517'; font-size: 0.6em; padding-left: 6px; }
|
|
@@ -22,7 +22,7 @@ shacl-property:not(.may-remove) > .shacl-or-constraint > .remove-button:not(.per
|
|
|
22
22
|
.editor:not([type='checkbox']) { flex-grow: 1; }
|
|
23
23
|
textarea.editor { resize: vertical; }
|
|
24
24
|
.lang-chooser { border: 0; background-color: #e9e9ed; padding: 2px 4px; align-self: flex-start; }
|
|
25
|
-
.validation-error { position: absolute; left: calc(var(--label-width) - 1em);
|
|
25
|
+
.validation-error { position: absolute; left: calc(var(--label-width) - 1em); color: red; cursor: help; }
|
|
26
26
|
.validation-error::before { content: '\26a0' }
|
|
27
27
|
.validation-error.node { left: -1em; }
|
|
28
28
|
.invalid > .editor { border-color: red !important; }
|
package/src/util.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Literal, NamedNode, Prefixes, Quad, Store } from 'n3'
|
|
2
|
-
import { DATA_GRAPH, PREFIX_FOAF, PREFIX_RDFS, PREFIX_SHACL, PREFIX_SKOS, RDFS_PREDICATE_SUBCLASS_OF, RDF_PREDICATE_TYPE, SHAPES_GRAPH } from './constants'
|
|
1
|
+
import { Literal, NamedNode, Prefixes, Quad, Term as N3Term, Store } from 'n3'
|
|
2
|
+
import { DATA_GRAPH, PREFIX_FOAF, PREFIX_RDFS, PREFIX_SHACL, PREFIX_SKOS, RDFS_PREDICATE_SUBCLASS_OF, RDF_PREDICATE_TYPE, SHAPES_GRAPH, SKOS_PREDICATE_BROADER, SKOS_PREDICATE_NARROWER } from './constants'
|
|
3
3
|
import { Term } from '@rdfjs/types'
|
|
4
4
|
import { InputListEntry } from './theme'
|
|
5
5
|
import { ShaclPropertyTemplate } from './property-template'
|
|
@@ -83,42 +83,67 @@ function findClassInstancesFromOwlImports(clazz: NamedNode, context: ShaclNode |
|
|
|
83
83
|
}
|
|
84
84
|
|
|
85
85
|
export function findInstancesOf(clazz: NamedNode, template: ShaclPropertyTemplate): InputListEntry[] {
|
|
86
|
-
let instances: Term[]
|
|
87
86
|
// if template has sh:in, then just use that as class instances
|
|
88
87
|
if (template.shaclIn) {
|
|
89
88
|
const list = template.config.lists[template.shaclIn]
|
|
90
|
-
|
|
89
|
+
return createInputListEntries(list?.length ? list : [], template.config.store, template.config.languages)
|
|
91
90
|
} else {
|
|
92
|
-
|
|
93
|
-
instances = template.config.store.getSubjects(RDF_PREDICATE_TYPE, clazz, SHAPES_GRAPH)
|
|
91
|
+
const instances = template.config.store.getSubjects(RDF_PREDICATE_TYPE, clazz, SHAPES_GRAPH)
|
|
94
92
|
// find instances in the data graph
|
|
95
93
|
instances.push(...template.config.store.getSubjects(RDF_PREDICATE_TYPE, clazz, DATA_GRAPH))
|
|
96
94
|
// find instances in imported taxonomies
|
|
97
95
|
findClassInstancesFromOwlImports(clazz, template, template.config.store, instances)
|
|
98
|
-
}
|
|
99
96
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
97
|
+
// structures needed for build a class instance hierarchy
|
|
98
|
+
const nodes = new Map<string, InputListEntry>() // URI -> InputListEntry
|
|
99
|
+
const childToParent = new Map<string, string>() // URI -> parentURI
|
|
100
|
+
|
|
101
|
+
// Step 1: Initialize all instances as InputListEntry's with no children
|
|
102
|
+
for (const instance of instances) {
|
|
103
|
+
nodes.set(instance.id, { value: instance, label: findLabel(template.config.store.getQuads(instance, null, null, null), template.config.languages), children: [] })
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Step 2: Record broader/narrower relationships (child -> parent)
|
|
107
|
+
for (const instance of instances) {
|
|
108
|
+
appendSkosBroaderNarrower(instance, nodes, childToParent, template)
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Step 3: Build hierarchy by nesting children under parents
|
|
112
|
+
for (const [child, parent] of childToParent.entries()) {
|
|
113
|
+
nodes.get(parent)!.children!.push(nodes.get(child)!)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Step 4: Find root nodes (no broader relationship)
|
|
117
|
+
const roots: InputListEntry[] = []
|
|
118
|
+
for (const [uri, node] of nodes.entries()) {
|
|
119
|
+
if (!childToParent.has(uri)) {
|
|
120
|
+
roots.push(node)
|
|
118
121
|
}
|
|
119
122
|
}
|
|
123
|
+
|
|
124
|
+
// Step 5: Add sub class instances
|
|
125
|
+
for (const subClass of template.config.store.getSubjects(RDFS_PREDICATE_SUBCLASS_OF, clazz, null)) {
|
|
126
|
+
roots.push(...findInstancesOf(subClass as NamedNode, template))
|
|
127
|
+
}
|
|
128
|
+
return roots
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function appendSkosBroaderNarrower(subject: N3Term, nodes: Map<string, InputListEntry>, childToParent: Map<string, string>, template: ShaclPropertyTemplate) {
|
|
133
|
+
for (const parent of template.config.store.getObjects(subject, SKOS_PREDICATE_BROADER, null)) {
|
|
134
|
+
childToParent.set(subject.id, parent.id)
|
|
135
|
+
if (!nodes.has(parent.id)) {
|
|
136
|
+
nodes.set(parent.id, { value: parent, label: findLabel(template.config.store.getQuads(parent, null, null, null), template.config.languages), children: [] })
|
|
137
|
+
}
|
|
138
|
+
appendSkosBroaderNarrower(parent, nodes, childToParent, template)
|
|
139
|
+
}
|
|
140
|
+
for (const child of template.config.store.getObjects(subject, SKOS_PREDICATE_NARROWER, null)) {
|
|
141
|
+
childToParent.set(child.id, subject.id)
|
|
142
|
+
if (!nodes.has(child.id)) {
|
|
143
|
+
nodes.set(child.id, { value: child, label: findLabel(template.config.store.getQuads(child, null, null, null), template.config.languages), children: [] })
|
|
144
|
+
}
|
|
145
|
+
appendSkosBroaderNarrower(child, nodes, childToParent, template)
|
|
120
146
|
}
|
|
121
|
-
return entries
|
|
122
147
|
}
|
|
123
148
|
|
|
124
149
|
export function isURL(input: string): boolean {
|