@ulb-darmstadt/shacl-form 1.10.4 → 2.0.0-rc2

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/dist/bundle.d.ts +3 -0
  2. package/dist/bundle.js +415 -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 +8 -2
  10. package/dist/plugins/leaflet.js +5 -708
  11. package/dist/{themes/default.d.ts → theme.default.d.ts} +2 -2
  12. package/package.json +20 -26
  13. package/dist/form-bootstrap.d.ts +0 -5
  14. package/dist/form-bootstrap.js +0 -413
  15. package/dist/form-default.d.ts +0 -5
  16. package/dist/form-default.js +0 -402
  17. package/dist/form-material.d.ts +0 -5
  18. package/dist/form-material.js +0 -722
  19. package/dist/plugins/fixed-list.d.ts +0 -9
  20. package/dist/plugins/map-util.d.ts +0 -5
  21. package/dist/plugins/mapbox.d.ts +0 -19
  22. package/dist/plugins/mapbox.js +0 -2904
  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 -258
  53. package/src/themes/material.css +0 -14
  54. package/src/themes/material.ts +0 -253
  55. package/src/util.ts +0 -275
@@ -1,253 +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
- // @ts-ignore
75
- return this.createDefaultTemplate('', value, required, editor, template)
76
- }
77
-
78
- createNumberEditor(label: string, value: Term | null, required: boolean, template: ShaclPropertyTemplate): HTMLElement {
79
- const editor = new TextField()
80
- editor.variant = 'outlined'
81
- editor.type = 'number'
82
- editor.label = label
83
- editor.helper = template.description ?.value || ''
84
- const min = template.minInclusive !== undefined ? template.minInclusive : template.minExclusive !== undefined ? template.minExclusive + 1 : undefined
85
- const max = template.maxInclusive !== undefined ? template.maxInclusive : template.maxExclusive !== undefined ? template.maxExclusive - 1 : undefined
86
- if (min !== undefined) {
87
- editor.setAttribute('min', String(min))
88
- }
89
- if (max !== undefined) {
90
- editor.setAttribute('max', String(max))
91
- }
92
- if (template.datatype?.value !== PREFIX_XSD + 'integer') {
93
- editor.setAttribute('step', '0.1')
94
- }
95
- // @ts-ignore
96
- return this.createDefaultTemplate('', value, required, editor, template)
97
- }
98
-
99
- createListEditor(label: string, value: Term | null, required: boolean, listEntries: InputListEntry[], template?: ShaclPropertyTemplate): HTMLElement {
100
- const editor = new RokitSelect()
101
- editor.clearable = true
102
- const result = this.createDefaultTemplate(label, null, required, editor, template)
103
- const ul = document.createElement('ul')
104
- let isFlatList = true
105
-
106
- const appendListEntry = (entry: InputListEntry, parent: HTMLUListElement) => {
107
- const li = document.createElement('li')
108
- if (typeof entry.value === 'string') {
109
- li.dataset.value = entry.value
110
- li.innerText = entry.label ? entry.label : entry.value
111
- } else {
112
- if (entry.value instanceof Literal && entry.value.datatype.equals(XSD_DATATYPE_STRING)) {
113
- li.dataset.value = entry.value.value
114
- } else {
115
- // this is needed for typed rdf literals
116
- li.dataset.value = (entry.value as N3Term).id
117
- }
118
- li.innerText = entry.label ? entry.label : entry.value.value
119
- }
120
- parent.appendChild(li)
121
- if (entry.children?.length) {
122
- isFlatList = false
123
- const ul = document.createElement('ul')
124
- li.appendChild(ul)
125
- for (const child of entry.children) {
126
- appendListEntry(child, ul)
127
- }
128
- }
129
- }
130
-
131
- for (const item of listEntries) {
132
- appendListEntry(item, ul)
133
- }
134
- if (!isFlatList) {
135
- editor.collapse = true
136
- }
137
-
138
- editor.appendChild(ul)
139
- if (value) {
140
- editor.value = value.value
141
- }
142
- return result
143
- }
144
-
145
- createBooleanEditor(label: string, value: Term | null, required: boolean, template: ShaclPropertyTemplate): HTMLElement {
146
- const editor = new Checkbox()
147
- const result = this.createDefaultTemplate('', value, required, editor, template)
148
- // 'required' on checkboxes forces the user to tick the checkbox, which is not what we want here
149
- editor.removeAttribute('required')
150
- if (value instanceof Literal) {
151
- editor.checked = value.value === 'true'
152
- }
153
- editor.innerText = label
154
- return result
155
- }
156
-
157
- createDateEditor(_: string, value: Term | null, required: boolean, template: ShaclPropertyTemplate): HTMLElement {
158
- const editor = new TextField()
159
- editor.variant = 'outlined'
160
- editor.helper = template?.description?.value || template?.label || ''
161
- if (template.datatype?.value === PREFIX_XSD + 'dateTime') {
162
- editor.type = 'datetime-local'
163
- // this enables seconds in dateTime input
164
- editor.setAttribute('step', '1')
165
- }
166
- else {
167
- editor.type = 'date'
168
- }
169
- editor.classList.add('pr-0')
170
- // @ts-ignore
171
- const result = this.createDefaultTemplate('', null, required, editor, template)
172
- if (value) {
173
- try {
174
- let isoDate = new Date(value.value).toISOString()
175
- if (template.datatype?.value === PREFIX_XSD + 'dateTime') {
176
- isoDate = isoDate.slice(0, 19)
177
- } else {
178
- isoDate = isoDate.slice(0, 10)
179
- }
180
- editor.value = isoDate
181
- } catch(ex) {
182
- console.error(ex, value)
183
- }
184
- }
185
- return result
186
- }
187
-
188
- createLangStringEditor(label: string, value: Term | null, required: boolean, template: ShaclPropertyTemplate): HTMLElement {
189
- const result = this.createTextEditor(label, value, required, template)
190
- const editor = result.querySelector(':scope .editor') as Editor
191
- let langChooser: HTMLSelectElement | HTMLInputElement
192
- if (template.languageIn?.length) {
193
- langChooser = document.createElement('select')
194
- for (const lang of template.languageIn) {
195
- const option = document.createElement('option')
196
- option.innerText = lang.value
197
- langChooser.appendChild(option)
198
- }
199
- } else {
200
- langChooser = document.createElement('input')
201
- langChooser.maxLength = 5 // e.g. en-US
202
- langChooser.placeholder = 'lang?'
203
- }
204
- langChooser.title = 'Language of the text'
205
- langChooser.classList.add('lang-chooser')
206
- // if lang chooser changes, fire a change event on the text input instead. this is for shacl validation handling.
207
- langChooser.addEventListener('change', (ev) => {
208
- ev.stopPropagation();
209
- if (editor) {
210
- editor.dataset.lang = langChooser.value
211
- editor.dispatchEvent(new Event('change', { bubbles: true }))
212
- }
213
- })
214
- if (value instanceof Literal) {
215
- langChooser.value = value.language
216
- }
217
- editor.dataset.lang = langChooser.value
218
- editor.after(langChooser)
219
- return result
220
- }
221
-
222
- createFileEditor(label: string, value: Term | null, required: boolean, template: ShaclPropertyTemplate): HTMLElement {
223
- const editor = document.createElement('input')
224
- editor.type = 'file'
225
- editor.addEventListener('change', (e) => {
226
- if (editor.files?.length) {
227
- e.stopPropagation()
228
- const reader = new FileReader()
229
- reader.readAsDataURL(editor.files[0])
230
- reader.onload = () => {
231
- (editor as Editor)['binaryData'] = btoa(reader.result as string)
232
- editor.parentElement?.dispatchEvent(new Event('change', { bubbles: true }))
233
- }
234
- } else {
235
- (editor as Editor)['binaryData'] = undefined
236
- }
237
- })
238
- return this.createDefaultTemplate(label, value, required, editor, template)
239
- }
240
-
241
- createButton(label: string, primary: boolean): HTMLElement {
242
- let button
243
- if (primary) {
244
- button = new Button()
245
- button.classList.add('primary')
246
- } else {
247
- button = new Button()
248
- button.classList.add('secondary')
249
- }
250
- button.innerHTML = label
251
- return button
252
- }
253
- }
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
- }