@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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ulb-darmstadt/shacl-form",
3
- "version": "1.8.2",
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.4",
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.29",
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); top: 0.5em; color: red; cursor: help; }
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
- instances = list?.length ? list : []
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
- const entries = createInputListEntries(instances, template.config.store, template.config.languages)
101
- // build inheritance tree only if sh:in is not defined
102
- if (template.shaclIn === undefined) {
103
- // find sub classes via rdfs:subClassOf
104
- for (const subClass of template.config.store.getSubjects(RDFS_PREDICATE_SUBCLASS_OF, clazz, null)) {
105
- const subClassIntances = findInstancesOf(subClass as NamedNode, template)
106
- for (const subClassIntance of subClassIntances) {
107
- let isChild = false
108
- // check if found sub class also is an instance of its super class. if yes, add it to the children of the InputListEntry.
109
- for (const entry of entries) {
110
- if (template.config.store.countQuads(subClassIntance.value, RDF_PREDICATE_TYPE, entry.value, null) > 0) {
111
- entry.children!.push(subClassIntance)
112
- isChild = true
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
- if (!isChild) {
116
- entries.push(subClassIntance)
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 {