@ulb-darmstadt/shacl-form 1.8.2 → 1.8.4
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 +70 -67
- package/dist/form-default.js +58 -55
- package/dist/form-material.js +100 -97
- package/dist/plugins/leaflet.js +7 -7
- package/dist/plugins/mapbox.js +19 -19
- package/package.json +3 -3
- package/src/constants.ts +2 -0
- package/src/property.ts +1 -0
- package/src/styles.css +2 -2
- package/src/util.ts +42 -22
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.4",
|
|
4
4
|
"description": "SHACL form generator",
|
|
5
5
|
"main": "dist/form-default.js",
|
|
6
6
|
"module": "dist/form-default.js",
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
"@types/uuid": "^10.0.0",
|
|
56
56
|
"rollup-plugin-peer-deps-external": "^2.2.4",
|
|
57
57
|
"typescript": "^5.8.3",
|
|
58
|
-
"vite": "^7.0.
|
|
58
|
+
"vite": "^7.0.5",
|
|
59
59
|
"vite-plugin-dts": "^4.5.4"
|
|
60
60
|
},
|
|
61
61
|
"dependencies": {
|
|
@@ -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.32",
|
|
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
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'
|
|
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,62 @@ 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
91
|
// find instances in the shapes graph
|
|
93
|
-
instances = template.config.store.getSubjects(RDF_PREDICATE_TYPE, clazz, SHAPES_GRAPH)
|
|
92
|
+
const instances = template.config.store.getSubjects(RDF_PREDICATE_TYPE, clazz, SHAPES_GRAPH)
|
|
94
93
|
// find instances in the data graph
|
|
95
94
|
instances.push(...template.config.store.getSubjects(RDF_PREDICATE_TYPE, clazz, DATA_GRAPH))
|
|
96
95
|
// find instances in imported taxonomies
|
|
97
96
|
findClassInstancesFromOwlImports(clazz, template, template.config.store, instances)
|
|
98
|
-
}
|
|
99
97
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
98
|
+
// initialize structures needed for building a class instance hierarchy
|
|
99
|
+
const nodes = new Map<string, InputListEntry>() // URI -> InputListEntry
|
|
100
|
+
const childToParent = new Map<string, string>() // URI -> parentURI
|
|
101
|
+
|
|
102
|
+
// initialize all instances as InputListEntry's with no children
|
|
103
|
+
for (const instance of instances) {
|
|
104
|
+
nodes.set(instance.id, { value: instance, label: findLabel(template.config.store.getQuads(instance, null, null, null), template.config.languages), children: [] })
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// record broader/narrower relationships (child -> parent)
|
|
108
|
+
for (const instance of instances) {
|
|
109
|
+
for (const parent of template.config.store.getObjects(instance, SKOS_PREDICATE_BROADER, null)) {
|
|
110
|
+
childToParent.set(instance.id, parent.id)
|
|
111
|
+
if (!nodes.has(parent.id)) {
|
|
112
|
+
nodes.set(parent.id, { value: parent, label: findLabel(template.config.store.getQuads(parent, null, null, null), template.config.languages), children: [] })
|
|
114
113
|
}
|
|
115
|
-
|
|
116
|
-
|
|
114
|
+
}
|
|
115
|
+
for (const child of template.config.store.getObjects(instance, SKOS_PREDICATE_NARROWER, null)) {
|
|
116
|
+
childToParent.set(child.id, instance.id)
|
|
117
|
+
if (!nodes.has(child.id)) {
|
|
118
|
+
nodes.set(child.id, { value: child, label: findLabel(template.config.store.getQuads(child, null, null, null), template.config.languages), children: [] })
|
|
117
119
|
}
|
|
118
120
|
}
|
|
119
121
|
}
|
|
122
|
+
|
|
123
|
+
// build hierarchy by nesting children under parents
|
|
124
|
+
for (const [child, parent] of childToParent.entries()) {
|
|
125
|
+
nodes.get(parent)?.children?.push(nodes.get(child)!)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// find root nodes (no parent relationship)
|
|
129
|
+
const roots: InputListEntry[] = []
|
|
130
|
+
for (const [uri, node] of nodes.entries()) {
|
|
131
|
+
if (!childToParent.has(uri)) {
|
|
132
|
+
roots.push(node)
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// add sub class instances
|
|
137
|
+
for (const subClass of template.config.store.getSubjects(RDFS_PREDICATE_SUBCLASS_OF, clazz, null)) {
|
|
138
|
+
roots.push(...findInstancesOf(subClass as NamedNode, template))
|
|
139
|
+
}
|
|
140
|
+
return roots
|
|
120
141
|
}
|
|
121
|
-
return entries
|
|
122
142
|
}
|
|
123
143
|
|
|
124
144
|
export function isURL(input: string): boolean {
|