@ulb-darmstadt/shacl-form 1.10.3 → 2.0.0-rc1

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.
Files changed (55) hide show
  1. package/README.md +1 -1
  2. package/dist/bundle.js +412 -0
  3. package/dist/constants.d.ts +16 -16
  4. package/dist/constraints.d.ts +2 -2
  5. package/dist/form.d.ts +4 -3
  6. package/dist/index.js +62 -0
  7. package/dist/plugins/assets/plugin-VN3CfgGe.js +1 -0
  8. package/dist/plugins/file-upload.js +1 -0
  9. package/dist/plugins/leaflet.d.ts +3 -1
  10. package/dist/plugins/leaflet.js +5 -708
  11. package/dist/plugins/map-util.js +1 -0
  12. package/dist/{themes/default.d.ts → theme.default.d.ts} +2 -2
  13. package/package.json +28 -33
  14. package/dist/form-bootstrap.d.ts +0 -5
  15. package/dist/form-bootstrap.js +0 -413
  16. package/dist/form-default.d.ts +0 -5
  17. package/dist/form-default.js +0 -402
  18. package/dist/form-material.d.ts +0 -5
  19. package/dist/form-material.js +0 -722
  20. package/dist/plugins/fixed-list.d.ts +0 -9
  21. package/dist/plugins/mapbox.d.ts +0 -19
  22. package/dist/plugins/mapbox.js +0 -2870
  23. package/dist/themes/bootstrap.d.ts +0 -10
  24. package/dist/themes/material.d.ts +0 -15
  25. package/src/config.ts +0 -110
  26. package/src/constants.ts +0 -30
  27. package/src/constraints.ts +0 -149
  28. package/src/exports.ts +0 -7
  29. package/src/form-bootstrap.ts +0 -12
  30. package/src/form-default.ts +0 -12
  31. package/src/form-material.ts +0 -12
  32. package/src/form.ts +0 -319
  33. package/src/globals.d.ts +0 -2
  34. package/src/group.ts +0 -34
  35. package/src/loader.ts +0 -187
  36. package/src/node.ts +0 -192
  37. package/src/plugin.ts +0 -60
  38. package/src/plugins/file-upload.ts +0 -26
  39. package/src/plugins/fixed-list.ts +0 -19
  40. package/src/plugins/leaflet.ts +0 -196
  41. package/src/plugins/map-util.ts +0 -41
  42. package/src/plugins/mapbox.ts +0 -157
  43. package/src/property-template.ts +0 -151
  44. package/src/property.ts +0 -309
  45. package/src/serialize.ts +0 -96
  46. package/src/shacl-engine.d.ts +0 -2
  47. package/src/styles.css +0 -49
  48. package/src/theme.ts +0 -132
  49. package/src/themes/bootstrap.css +0 -6
  50. package/src/themes/bootstrap.ts +0 -44
  51. package/src/themes/default.css +0 -4
  52. package/src/themes/default.ts +0 -255
  53. package/src/themes/material.css +0 -14
  54. package/src/themes/material.ts +0 -250
  55. package/src/util.ts +0 -275
@@ -1,250 +0,0 @@
1
- import { ShaclPropertyTemplate } from '../property-template'
2
- import { Term } from '@rdfjs/types'
3
- import { Button, TextField, Checkbox } from 'mdui'
4
- import { Theme } from '../theme'
5
- import { InputListEntry, Editor } from '../theme'
6
- import { Literal, NamedNode } from 'n3'
7
- import { Term as N3Term } from 'n3'
8
- import css from './material.css?raw'
9
- import { PREFIX_SHACL, PREFIX_XSD, XSD_DATATYPE_STRING } from '../constants'
10
- import { RokitSelect } from '@ro-kit/ui-widgets'
11
-
12
- export class MaterialTheme extends Theme {
13
- constructor() {
14
- super(css)
15
- }
16
-
17
- createDefaultTemplate(label: string, value: Term | null, required: boolean, editor: Editor, template?: ShaclPropertyTemplate): HTMLElement {
18
- editor.classList.add('editor')
19
- if (template?.datatype) {
20
- // store datatype on editor, this is used for RDF serialization
21
- editor.shaclDatatype = template.datatype
22
- } else if (value instanceof Literal) {
23
- editor.shaclDatatype = value.datatype
24
- }
25
- if (template?.minCount !== undefined) {
26
- editor.dataset.minCount = String(template.minCount)
27
- }
28
- if (template?.class) {
29
- editor.dataset.class = template.class.value
30
- }
31
- if (template?.nodeKind) {
32
- editor.dataset.nodeKind = template.nodeKind.value
33
- } else if (value instanceof NamedNode) {
34
- editor.dataset.nodeKind = PREFIX_SHACL + 'IRI'
35
- }
36
- if (template?.hasValue || template?.readonly) {
37
- editor.disabled = true
38
- }
39
- editor.value = value?.value || template?.defaultValue?.value || ''
40
-
41
- const placeholder = template?.description ? template.description.value : template?.pattern ? template.pattern : null
42
- if (placeholder) {
43
- editor.setAttribute('placeholder', placeholder)
44
- }
45
- if (required) {
46
- editor.setAttribute('required', 'true')
47
- }
48
-
49
- const result = document.createElement('div')
50
- if (label) {
51
- const labelElem = document.createElement('label')
52
- labelElem.htmlFor = editor.id
53
- labelElem.innerText = label
54
- result.appendChild(labelElem)
55
- }
56
- result.appendChild(editor)
57
- return result
58
- }
59
-
60
- createTextEditor(label: string, value: Term | null, required: boolean, template: ShaclPropertyTemplate): HTMLElement {
61
- const editor = new TextField()
62
- editor.variant = 'outlined'
63
- editor.label = label
64
- editor.type = 'text'
65
- if (template.description) {
66
- editor.helper = template.description.value
67
- }
68
- if (template.singleLine === false) {
69
- editor.rows = 5
70
- }
71
- if (template.pattern) {
72
- editor.pattern = template.pattern
73
- }
74
- return this.createDefaultTemplate('', value, required, editor, template)
75
- }
76
-
77
- createNumberEditor(label: string, value: Term | null, required: boolean, template: ShaclPropertyTemplate): HTMLElement {
78
- const editor = new TextField()
79
- editor.variant = 'outlined'
80
- editor.type = 'number'
81
- editor.label = label
82
- editor.helper = template.description ?.value || ''
83
- const min = template.minInclusive !== undefined ? template.minInclusive : template.minExclusive !== undefined ? template.minExclusive + 1 : undefined
84
- const max = template.maxInclusive !== undefined ? template.maxInclusive : template.maxExclusive !== undefined ? template.maxExclusive - 1 : undefined
85
- if (min !== undefined) {
86
- editor.setAttribute('min', String(min))
87
- }
88
- if (max !== undefined) {
89
- editor.setAttribute('max', String(max))
90
- }
91
- if (template.datatype?.value !== PREFIX_XSD + 'integer') {
92
- editor.setAttribute('step', '0.1')
93
- }
94
- return this.createDefaultTemplate('', value, required, editor, template)
95
- }
96
-
97
- createListEditor(label: string, value: Term | null, required: boolean, listEntries: InputListEntry[], template?: ShaclPropertyTemplate): HTMLElement {
98
- const editor = new RokitSelect()
99
- editor.clearable = true
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
- } else {
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
115
- }
116
- li.innerText = entry.label ? entry.label : entry.value.value
117
- }
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
- }
126
- }
127
- }
128
-
129
- for (const item of listEntries) {
130
- appendListEntry(item, ul)
131
- }
132
- if (!isFlatList) {
133
- editor.collapse = true
134
- }
135
-
136
- editor.appendChild(ul)
137
- if (value) {
138
- editor.value = value.value
139
- }
140
- return result
141
- }
142
-
143
- createBooleanEditor(label: string, value: Term | null, required: boolean, template: ShaclPropertyTemplate): HTMLElement {
144
- const editor = new Checkbox()
145
- const result = this.createDefaultTemplate('', value, required, editor, template)
146
- // 'required' on checkboxes forces the user to tick the checkbox, which is not what we want here
147
- editor.removeAttribute('required')
148
- if (value instanceof Literal) {
149
- editor.checked = value.value === 'true'
150
- }
151
- editor.innerText = label
152
- return result
153
- }
154
-
155
- createDateEditor(_: string, value: Term | null, required: boolean, template: ShaclPropertyTemplate): HTMLElement {
156
- const editor = new TextField()
157
- editor.variant = 'outlined'
158
- editor.helper = template?.description?.value || template?.label || ''
159
- if (template.datatype?.value === PREFIX_XSD + 'dateTime') {
160
- editor.type = 'datetime-local'
161
- // this enables seconds in dateTime input
162
- editor.setAttribute('step', '1')
163
- }
164
- else {
165
- editor.type = 'date'
166
- }
167
- editor.classList.add('pr-0')
168
- const result = this.createDefaultTemplate('', null, required, editor, template)
169
- if (value) {
170
- try {
171
- let isoDate = new Date(value.value).toISOString()
172
- if (template.datatype?.value === PREFIX_XSD + 'dateTime') {
173
- isoDate = isoDate.slice(0, 19)
174
- } else {
175
- isoDate = isoDate.slice(0, 10)
176
- }
177
- editor.value = isoDate
178
- } catch(ex) {
179
- console.error(ex, value)
180
- }
181
- }
182
- return result
183
- }
184
-
185
- createLangStringEditor(label: string, value: Term | null, required: boolean, template: ShaclPropertyTemplate): HTMLElement {
186
- const result = this.createTextEditor(label, value, required, template)
187
- const editor = result.querySelector(':scope .editor') as Editor
188
- let langChooser: HTMLSelectElement | HTMLInputElement
189
- if (template.languageIn?.length) {
190
- langChooser = document.createElement('select')
191
- for (const lang of template.languageIn) {
192
- const option = document.createElement('option')
193
- option.innerText = lang.value
194
- langChooser.appendChild(option)
195
- }
196
- } else {
197
- langChooser = document.createElement('input')
198
- langChooser.maxLength = 5 // e.g. en-US
199
- langChooser.placeholder = 'lang?'
200
- }
201
- langChooser.title = 'Language of the text'
202
- langChooser.classList.add('lang-chooser')
203
- // if lang chooser changes, fire a change event on the text input instead. this is for shacl validation handling.
204
- langChooser.addEventListener('change', (ev) => {
205
- ev.stopPropagation();
206
- if (editor) {
207
- editor.dataset.lang = langChooser.value
208
- editor.dispatchEvent(new Event('change', { bubbles: true }))
209
- }
210
- })
211
- if (value instanceof Literal) {
212
- langChooser.value = value.language
213
- }
214
- editor.dataset.lang = langChooser.value
215
- editor.after(langChooser)
216
- return result
217
- }
218
-
219
- createFileEditor(label: string, value: Term | null, required: boolean, template: ShaclPropertyTemplate): HTMLElement {
220
- const editor = document.createElement('input')
221
- editor.type = 'file'
222
- editor.addEventListener('change', (e) => {
223
- if (editor.files?.length) {
224
- e.stopPropagation()
225
- const reader = new FileReader()
226
- reader.readAsDataURL(editor.files[0])
227
- reader.onload = () => {
228
- (editor as Editor)['binaryData'] = btoa(reader.result as string)
229
- editor.parentElement?.dispatchEvent(new Event('change', { bubbles: true }))
230
- }
231
- } else {
232
- (editor as Editor)['binaryData'] = undefined
233
- }
234
- })
235
- return this.createDefaultTemplate(label, value, required, editor, template)
236
- }
237
-
238
- createButton(label: string, primary: boolean): HTMLElement {
239
- let button
240
- if (primary) {
241
- button = new Button()
242
- button.classList.add('primary')
243
- } else {
244
- button = new Button()
245
- button.classList.add('secondary')
246
- }
247
- button.innerHTML = label
248
- return button
249
- }
250
- }
package/src/util.ts DELETED
@@ -1,275 +0,0 @@
1
- import { Literal, NamedNode, Prefixes, Quad, Store } from 'n3'
2
- import { DATA_GRAPH, PREFIX_FOAF, PREFIX_RDF, PREFIX_RDFS, PREFIX_SHACL, PREFIX_SKOS, RDFS_PREDICATE_SUBCLASS_OF, RDF_PREDICATE_TYPE, SHAPES_GRAPH, SKOS_PREDICATE_BROADER, SKOS_PREDICATE_NARROWER } from './constants'
3
- import { Term } from '@rdfjs/types'
4
- import { InputListEntry } from './theme'
5
- import { ShaclPropertyTemplate } from './property-template'
6
- import { ShaclNode } from './node'
7
-
8
- export function findObjectValueByPredicate(quads: Quad[], predicate: string, prefix: string = PREFIX_SHACL, languages?: string[]): string {
9
- let result = ''
10
- const object = findObjectByPredicate(quads, predicate, prefix, languages)
11
- if (object) {
12
- result = object.value
13
- }
14
- return result
15
- }
16
-
17
- export function findObjectByPredicate(quads: Quad[], predicate: string, prefix: string = PREFIX_SHACL, languages?: string[]): Term | undefined {
18
- let candidate: Term | undefined
19
- const prefixedPredicate = prefix + predicate
20
-
21
- if (languages?.length) {
22
- for (const language of languages) {
23
- for (const quad of quads) {
24
- if (quad.predicate.value === prefixedPredicate) {
25
- if (quad.object.id.endsWith(`@${language}`)) {
26
- return quad.object
27
- }
28
- else if (quad.object.id.indexOf('@') < 0) {
29
- candidate = quad.object
30
- } else if (!candidate) {
31
- candidate = quad.object
32
- }
33
- }
34
- }
35
- }
36
- } else {
37
- for (const quad of quads) {
38
- if (quad.predicate.value === prefixedPredicate) {
39
- return quad.object
40
- }
41
- }
42
- }
43
- return candidate
44
- }
45
-
46
- export function focusFirstInputElement(context: HTMLElement) {
47
- (context.querySelector('.editor') as HTMLElement)?.focus()
48
- }
49
-
50
- export function findLabel(quads: Quad[], languages: string[]): string {
51
- return findObjectValueByPredicate(quads, 'prefLabel', PREFIX_SKOS, languages) ||
52
- findObjectValueByPredicate(quads, 'label', PREFIX_RDFS, languages) ||
53
- findObjectValueByPredicate(quads, 'name', PREFIX_FOAF, languages)
54
- }
55
-
56
- export function createInputListEntries(subjects: Term[], shapesGraph: Store, languages: string[]): InputListEntry[] {
57
- const entries: InputListEntry[] = []
58
- for (const subject of subjects) {
59
- entries.push({ value: subject, label: findLabel(shapesGraph.getQuads(subject, null, null, null), languages), children: [] })
60
- }
61
- return entries
62
- }
63
-
64
- export function removePrefixes(id: string, prefixes: Prefixes): string {
65
- for (const key in prefixes) {
66
- // need to ignore type check. 'prefix' is a string and not a NamedNode<string> (seems to be a bug in n3 typings)
67
- // @ts-ignore
68
- id = id.replace(prefixes[key], '')
69
- }
70
- return id
71
- }
72
-
73
- function findClassInstancesFromOwlImports(clazz: NamedNode, context: ShaclNode | ShaclPropertyTemplate, shapesGraph: Store, instances: Term[], alreadyCheckedImports = new Set<string>()) {
74
- for (const owlImport of context.owlImports) {
75
- if (!alreadyCheckedImports.has(owlImport.id)) {
76
- alreadyCheckedImports.add(owlImport.id)
77
- instances.push(...shapesGraph.getSubjects(RDF_PREDICATE_TYPE, clazz, owlImport))
78
- }
79
- }
80
- if (context.parent) {
81
- findClassInstancesFromOwlImports(clazz, context.parent, shapesGraph, instances, alreadyCheckedImports)
82
- }
83
- }
84
-
85
- export function findInstancesOf(clazz: NamedNode, template: ShaclPropertyTemplate): InputListEntry[] {
86
- // if template has sh:in, then just use that as class instances
87
- if (template.shaclIn) {
88
- const list = template.config.lists[template.shaclIn]
89
- return createInputListEntries(list?.length ? list : [], template.config.store, template.config.languages)
90
- } else {
91
- // find instances in the shapes graph
92
- const instances = template.config.store.getSubjects(RDF_PREDICATE_TYPE, clazz, SHAPES_GRAPH)
93
- // find instances in the data graph
94
- instances.push(...template.config.store.getSubjects(RDF_PREDICATE_TYPE, clazz, DATA_GRAPH))
95
- // find instances in imported taxonomies
96
- findClassInstancesFromOwlImports(clazz, template, template.config.store, instances)
97
-
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/subClassOf hierarchical relationships
108
- for (const instance of instances) {
109
- for (const parent of template.config.store.getObjects(instance, SKOS_PREDICATE_BROADER, null)) {
110
- if (nodes.has(parent.id)) {
111
- childToParent.set(instance.id, parent.id)
112
- }
113
- }
114
- for (const child of template.config.store.getObjects(instance, SKOS_PREDICATE_NARROWER, null)) {
115
- if (nodes.has(child.id)) {
116
- childToParent.set(child.id, instance.id)
117
- }
118
- }
119
- for (const parent of template.config.store.getObjects(instance, RDFS_PREDICATE_SUBCLASS_OF, null)) {
120
- if (nodes.has(parent.id)) {
121
- childToParent.set(instance.id, parent.id)
122
- }
123
- }
124
- }
125
-
126
- // build hierarchy by nesting children under parents
127
- for (const [child, parent] of childToParent.entries()) {
128
- nodes.get(parent)?.children?.push(nodes.get(child)!)
129
- }
130
-
131
- // find root nodes (no parent relationship)
132
- const roots: InputListEntry[] = []
133
- for (const [uri, node] of nodes.entries()) {
134
- if (!childToParent.has(uri)) {
135
- roots.push(node)
136
- }
137
- }
138
-
139
- // add sub class instances
140
- for (const subClass of template.config.store.getSubjects(RDFS_PREDICATE_SUBCLASS_OF, clazz, null)) {
141
- roots.push(...findInstancesOf(subClass as NamedNode, template))
142
- }
143
- return roots
144
- }
145
- }
146
-
147
- export function isURL(input: string): boolean {
148
- let url: URL
149
- try {
150
- url = new URL(input)
151
- } catch (_) {
152
- return false
153
- }
154
- return url.protocol === 'http:' || url.protocol === 'https:'
155
- }
156
-
157
- export function prioritizeByLanguage(languages: string[], text1?: Literal, text2?: Literal): Literal | undefined {
158
- if (text1 === undefined) {
159
- return text2
160
- }
161
- if (text2 === undefined) {
162
- return text1
163
- }
164
- const index1 = languages.indexOf(text1.language)
165
- if (index1 < 0) {
166
- return text2
167
- }
168
- const index2 = languages.indexOf(text2.language)
169
- if (index2 < 0) {
170
- return text1
171
- }
172
- return index2 > index1 ? text1 : text2
173
- }
174
-
175
- /*
176
- This code is taken from https://github.com/rdfjs/N3.js/blob/main/src/N3Store.js and adapted to allow rdf:type triples in lists.
177
- Can be removed as soon as https://github.com/rdfjs/N3.js/issues/546 is fixed.
178
- */
179
- export function extractLists(store: Store, { remove = false, ignoreErrors = false } = {}) {
180
- const lists: Record<string, Term[]> = {} // has scalar keys so could be a simple Object
181
- const onError = ignoreErrors ? (() => true) :
182
- ((node: Term, message: string) => { throw new Error(`${node.value} ${message}`) })
183
-
184
- // Traverse each list from its tail
185
- const tails = store.getQuads(null, PREFIX_RDF + 'rest', PREFIX_RDF + 'nil', null)
186
- const toRemove = remove ? [...tails] : []
187
- tails.forEach(tailQuad => {
188
- const items = [] // the members found as objects of rdf:first quads
189
- let malformed = false // signals whether the current list is malformed
190
- let head // the head of the list (_:b1 in above example)
191
- let headPos: string // set to subject or object when head is set
192
- const graph = tailQuad.graph // make sure list is in exactly one graph
193
-
194
- // Traverse the list from tail to end
195
- let current: Term | null = tailQuad.subject
196
- while (current && !malformed) {
197
- const objectQuads = store.getQuads(null, null, current, null)
198
- const subjectQuads = store.getQuads(current, null, null, null).filter(quad => !quad.predicate.equals(RDF_PREDICATE_TYPE))
199
- let quad, first = null, rest = null, parent = null
200
-
201
- // Find the first and rest of this list node
202
- for (let i = 0; i < subjectQuads.length && !malformed; i++) {
203
- quad = subjectQuads[i]
204
- if (!quad.graph.equals(graph))
205
- malformed = onError(current, 'not confined to single graph')
206
- else if (head)
207
- malformed = onError(current, 'has non-list arcs out')
208
-
209
- // one rdf:first
210
- else if (quad.predicate.value === PREFIX_RDF + 'first') {
211
- if (first)
212
- malformed = onError(current, 'has multiple rdf:first arcs')
213
- else
214
- toRemove.push(first = quad)
215
- }
216
-
217
- // one rdf:rest
218
- else if (quad.predicate.value === PREFIX_RDF + 'rest') {
219
- if (rest)
220
- malformed = onError(current, 'has multiple rdf:rest arcs')
221
- else
222
- toRemove.push(rest = quad)
223
- }
224
-
225
- // alien triple
226
- else if (objectQuads.length)
227
- malformed = onError(current, 'can\'t be subject and object')
228
- else {
229
- head = quad // e.g. { (1 2 3) :p :o }
230
- headPos = 'subject'
231
- }
232
- }
233
-
234
- // { :s :p (1 2) } arrives here with no head
235
- // { (1 2) :p :o } arrives here with head set to the list.
236
- for (let i = 0; i < objectQuads.length && !malformed; ++i) {
237
- quad = objectQuads[i]
238
- if (head)
239
- malformed = onError(current, 'can\'t have coreferences')
240
- // one rdf:rest
241
- else if (quad.predicate.value === PREFIX_RDF + 'rest') {
242
- if (parent)
243
- malformed = onError(current, 'has incoming rdf:rest arcs')
244
- else
245
- parent = quad
246
- }
247
- else {
248
- head = quad // e.g. { :s :p (1 2) }
249
- headPos = 'object'
250
- }
251
- }
252
-
253
- // Store the list item and continue with parent
254
- if (!first)
255
- malformed = onError(current, 'has no list head')
256
- else
257
- items.unshift(first.object)
258
- current = parent && parent.subject
259
- }
260
-
261
- // Don't remove any quads if the list is malformed
262
- if (malformed)
263
- remove = false
264
- // Store the list under the value of its head
265
- else if (head) {
266
- // @ts-ignore
267
- lists[head[headPos].value] = items
268
- }
269
- })
270
-
271
- // Remove list quads if requested
272
- if (remove)
273
- store.removeQuads(toRemove)
274
- return lists
275
- }