@ulb-darmstadt/shacl-form 1.8.0 → 1.8.2

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.
@@ -1,12 +1,13 @@
1
1
  import { ShaclPropertyTemplate } from '../property-template'
2
2
  import { Term } from '@rdfjs/types'
3
- import { Button, TextField, Select, MenuItem, Checkbox } from 'mdui'
3
+ import { Button, TextField, Checkbox } from 'mdui'
4
4
  import { Theme } from '../theme'
5
5
  import { InputListEntry, Editor } from '../theme'
6
- import { Literal } from 'n3'
6
+ import { Literal, NamedNode } from 'n3'
7
7
  import { Term as N3Term } from 'n3'
8
8
  import css from './material.css?raw'
9
- import { PREFIX_XSD } from '../constants'
9
+ import { PREFIX_SHACL, PREFIX_XSD, XSD_DATATYPE_STRING } from '../constants'
10
+ import { RokitSelect } from '@ro-kit/ui-widgets'
10
11
 
11
12
  export class MaterialTheme extends Theme {
12
13
  constructor() {
@@ -29,6 +30,8 @@ export class MaterialTheme extends Theme {
29
30
  }
30
31
  if (template?.nodeKind) {
31
32
  editor.dataset.nodeKind = template.nodeKind.value
33
+ } else if (value instanceof NamedNode) {
34
+ editor.dataset.nodeKind = PREFIX_SHACL + 'IRI'
32
35
  }
33
36
  if (template?.hasValue || template?.readonly) {
34
37
  editor.disabled = true
@@ -92,47 +95,45 @@ export class MaterialTheme extends Theme {
92
95
  }
93
96
 
94
97
  createListEditor(label: string, value: Term | null, required: boolean, listEntries: InputListEntry[], template?: ShaclPropertyTemplate): HTMLElement {
95
- const editor = new Select()
96
- editor.variant = 'outlined'
97
- editor.label = label
98
- editor.helper = template?.description?.value
98
+ const editor = new RokitSelect()
99
99
  editor.clearable = true
100
- // @ts-ignore
101
- const result = this.createDefaultTemplate('', null, required, editor, template)
102
- let addEmptyOption = true
103
-
104
- for (const item of listEntries) {
105
- const option = new MenuItem()
106
- let itemValue = ''
107
- if (typeof item.value === 'string') {
108
- itemValue = item.value
100
+ const result = this.createDefaultTemplate(label, null, required, editor, template)
101
+ const ul = document.createElement('ul')
102
+ let isFlatList = true
103
+
104
+ const appendListEntry = (entry: InputListEntry, parent: HTMLUListElement) => {
105
+ const li = document.createElement('li')
106
+ if (typeof entry.value === 'string') {
107
+ li.dataset.value = entry.value
108
+ li.innerText = entry.label ? entry.label : entry.value
109
109
  } else {
110
- // this is needed for typed rdf literals
111
- itemValue = (item.value as N3Term).id
112
- }
113
- const itemLabel = item.label ? item.label : itemValue
114
- option.value = itemValue
115
- option.textContent = itemLabel || itemValue
116
- // if (value && value.value === itemValue) {
117
- // option.selected = true
118
- // }
119
- if (item.indent) {
120
- for (let i = 0; i < item.indent; i++) {
121
- option.innerHTML = '&#160;&#160;' + option.innerHTML
110
+ if (entry.value instanceof Literal && entry.value.datatype.equals(XSD_DATATYPE_STRING)) {
111
+ li.dataset.value = entry.value.value
112
+ } else {
113
+ // this is needed for typed rdf literals
114
+ li.dataset.value = (entry.value as N3Term).id
122
115
  }
116
+ li.innerText = entry.label ? entry.label : entry.value.value
123
117
  }
124
- if (itemValue === '') {
125
- addEmptyOption = false
126
- option.ariaLabel = 'blank'
118
+ parent.appendChild(li)
119
+ if (entry.children?.length) {
120
+ isFlatList = false
121
+ const ul = document.createElement('ul')
122
+ li.appendChild(ul)
123
+ for (const child of entry.children) {
124
+ appendListEntry(child, ul)
125
+ }
127
126
  }
128
- editor.appendChild(option)
129
127
  }
130
- if (addEmptyOption) {
131
- // add an empty element
132
- const empty = new MenuItem()
133
- empty.ariaLabel = 'blank'
134
- editor.prepend(empty)
128
+
129
+ for (const item of listEntries) {
130
+ appendListEntry(item, ul)
131
+ }
132
+ if (!isFlatList) {
133
+ editor.collapse = true
135
134
  }
135
+
136
+ editor.appendChild(ul)
136
137
  if (value) {
137
138
  editor.value = value.value
138
139
  }
package/src/util.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { Literal, NamedNode, Prefixes, Quad, Store } from 'n3'
2
- import { DATA_GRAPH, OWL_OBJECT_NAMED_INDIVIDUAL, PREFIX_FOAF, PREFIX_RDFS, PREFIX_SHACL, PREFIX_SKOS, RDFS_PREDICATE_SUBCLASS_OF, RDF_PREDICATE_TYPE, SHAPES_GRAPH, SKOS_PREDICATE_BROADER } from './constants'
2
+ import { DATA_GRAPH, PREFIX_FOAF, PREFIX_RDFS, PREFIX_SHACL, PREFIX_SKOS, RDFS_PREDICATE_SUBCLASS_OF, RDF_PREDICATE_TYPE, SHAPES_GRAPH } from './constants'
3
3
  import { Term } from '@rdfjs/types'
4
4
  import { InputListEntry } from './theme'
5
5
  import { ShaclPropertyTemplate } from './property-template'
@@ -53,10 +53,10 @@ export function findLabel(quads: Quad[], languages: string[]): string {
53
53
  findObjectValueByPredicate(quads, 'name', PREFIX_FOAF, languages)
54
54
  }
55
55
 
56
- export function createInputListEntries(subjects: Term[], shapesGraph: Store, languages: string[], indent?: number): InputListEntry[] {
56
+ export function createInputListEntries(subjects: Term[], shapesGraph: Store, languages: string[]): InputListEntry[] {
57
57
  const entries: InputListEntry[] = []
58
58
  for (const subject of subjects) {
59
- entries.push({ value: subject, label: findLabel(shapesGraph.getQuads(subject, null, null, null), languages), indent: indent })
59
+ entries.push({ value: subject, label: findLabel(shapesGraph.getQuads(subject, null, null, null), languages), children: [] })
60
60
  }
61
61
  return entries
62
62
  }
@@ -82,7 +82,7 @@ function findClassInstancesFromOwlImports(clazz: NamedNode, context: ShaclNode |
82
82
  }
83
83
  }
84
84
 
85
- export function findInstancesOf(clazz: NamedNode, template: ShaclPropertyTemplate, indent = 0): InputListEntry[] {
85
+ export function findInstancesOf(clazz: NamedNode, template: ShaclPropertyTemplate): InputListEntry[] {
86
86
  let instances: Term[]
87
87
  // if template has sh:in, then just use that as class instances
88
88
  if (template.shaclIn) {
@@ -97,16 +97,24 @@ export function findInstancesOf(clazz: NamedNode, template: ShaclPropertyTemplat
97
97
  findClassInstancesFromOwlImports(clazz, template, template.config.store, instances)
98
98
  }
99
99
 
100
- const entries = createInputListEntries(instances, template.config.store, template.config.languages, indent)
100
+ const entries = createInputListEntries(instances, template.config.store, template.config.languages)
101
101
  // build inheritance tree only if sh:in is not defined
102
102
  if (template.shaclIn === undefined) {
103
+ // find sub classes via rdfs:subClassOf
103
104
  for (const subClass of template.config.store.getSubjects(RDFS_PREDICATE_SUBCLASS_OF, clazz, null)) {
104
- entries.push(...findInstancesOf(subClass as NamedNode, template, indent + 1))
105
- }
106
- if (template.config.store.getQuads(clazz, RDF_PREDICATE_TYPE, OWL_OBJECT_NAMED_INDIVIDUAL, null).length > 0) {
107
- entries.push(...createInputListEntries([ clazz ], template.config.store, template.config.languages, indent))
108
- for (const subClass of template.config.store.getSubjects(SKOS_PREDICATE_BROADER, clazz, null)) {
109
- entries.push(...findInstancesOf(subClass as NamedNode, template, indent + 1))
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
+ }
114
+ }
115
+ if (!isChild) {
116
+ entries.push(subClassIntance)
117
+ }
110
118
  }
111
119
  }
112
120
  }
@@ -1,17 +0,0 @@
1
- import { BlankNode, NamedNode, Quad } from 'n3';
2
- import { Config } from './config';
3
- import { Term } from '@rdfjs/types';
4
- export declare class ShaclNodeTemplate {
5
- properties: (NamedNode | BlankNode)[];
6
- node: NamedNode | undefined;
7
- shaclAnd: string | undefined;
8
- shaclOr: Term[] | undefined;
9
- shaclXone: Term[] | undefined;
10
- targetClass: NamedNode | undefined;
11
- owlImports: NamedNode[];
12
- config: Config;
13
- extendedShapes: NamedNode[];
14
- constructor(quads: Quad[], config: Config);
15
- merge(quads: Quad[]): ShaclNodeTemplate;
16
- clone(): ShaclNodeTemplate;
17
- }
@@ -1,82 +0,0 @@
1
- import { BlankNode, NamedNode, Quad } from "n3"
2
- import { Config } from "./config"
3
- import { OWL_PREDICATE_IMPORTS, PREFIX_SHACL } from "./constants";
4
- import { Term } from "@rdfjs/types";
5
-
6
- const mappers: Record<string, (template: ShaclNodeTemplate, term: Term) => void> = {
7
- [`${PREFIX_SHACL}property`]: (template, term) => { template.properties.push(term as (BlankNode | NamedNode)) },
8
- [`${PREFIX_SHACL}and`]: (template, term) => { template.shaclAnd = term.value },
9
- [`${PREFIX_SHACL}node`]: (template, term) => { template.node = term as NamedNode },
10
- [`${PREFIX_SHACL}targetClass`]: (template, term) => { template.targetClass = term as NamedNode },
11
- [`${PREFIX_SHACL}or`]: (template, term) => {
12
- const list = template.config.lists[term.value]
13
- if (list?.length) {
14
- template.shaclOr = list
15
- } else {
16
- console.error('list for sh:or not found:', term.value, 'existing lists:', template.config.lists)
17
- }
18
- },
19
- [`${PREFIX_SHACL}xone`]: (template, term) => {
20
- const list = template.config.lists[term.value]
21
- if (list?.length) {
22
- template.shaclXone = list
23
- } else {
24
- console.error('list for sh:xone not found:', term.value, 'existing lists:', template.config.lists)
25
- }
26
- },
27
- [OWL_PREDICATE_IMPORTS.id]: (template, term) => { template.owlImports.push(term as NamedNode) }
28
- }
29
-
30
- export class ShaclNodeTemplate {
31
- properties: (NamedNode | BlankNode)[] = []
32
- node: NamedNode | undefined
33
- shaclAnd: string | undefined
34
- shaclOr: Term[] | undefined
35
- shaclXone: Term[] | undefined
36
- targetClass: NamedNode | undefined
37
-
38
- owlImports: NamedNode[] = []
39
- config: Config
40
- extendedShapes: NamedNode[] = []
41
-
42
- constructor(quads: Quad[], config: Config) {
43
- this.config = config
44
- this.merge(quads)
45
- }
46
-
47
- merge(quads: Quad[]): ShaclNodeTemplate {
48
- for (const quad of quads) {
49
- mappers[quad.predicate.id]?.call(this, this, quad.object)
50
- }
51
- // resolve extended shapes
52
- if (this.node || this.shaclAnd) {
53
- if (this.node) {
54
- this.extendedShapes.push(this.node)
55
- }
56
- if (this.shaclAnd) {
57
- const list = this.config.lists[this.shaclAnd]
58
- if (list?.length) {
59
- for (const node of list) {
60
- this.extendedShapes.push(node as NamedNode)
61
- }
62
- }
63
- }
64
- }
65
- return this
66
- }
67
-
68
- clone(): ShaclNodeTemplate {
69
- const copy = Object.assign({}, this)
70
- // arrays are not cloned but referenced, so create them manually
71
- copy.owlImports = [ ...this.owlImports ]
72
- if (this.shaclOr) {
73
- copy.shaclOr = [ ...this.shaclOr ]
74
- }
75
- if (this.shaclXone) {
76
- copy.shaclXone = [ ...this.shaclXone ]
77
- }
78
- copy.merge = this.merge.bind(copy)
79
- copy.clone = this.clone.bind(copy)
80
- return copy
81
- }
82
- }