Skip to content

Commit e94706d

Browse files
committed
Merge remote-tracking branch 'upstream/main' into add_css_class_option
2 parents 6d7b024 + 79a7b82 commit e94706d

File tree

14 files changed

+387
-288
lines changed

14 files changed

+387
-288
lines changed

demo/index.html

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,15 @@
1111
<script type="importmap">
1212
{
1313
"imports": {
14-
"@ulb-darmstadt/shacl-form/": "https://cdn.jsdelivr.net/npm/@ulb-darmstadt/shacl-form@1.4.2/dist/"
14+
"@ulb-darmstadt/shacl-form/": "https://cdn.jsdelivr.net/npm/@ulb-darmstadt/shacl-form@1.4.3/dist/"
1515
}
1616
}
1717
</script>
1818
<script type="module">
1919
import '@ulb-darmstadt/shacl-form/form-default.js'
20+
import { registerPlugin } from '@ulb-darmstadt/shacl-form/form-default.js'
21+
import { LeafletPlugin } from '@ulb-darmstadt/shacl-form/plugins/leaflet.js'
22+
registerPlugin(new LeafletPlugin({ datatype: 'http://www.opengis.net/ont/geosparql#wktLiteral' }))
2023
</script>
2124
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.9.0/build/styles/vs.min.css">
2225
<script src="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.9.0/build/highlight.min.js"></script>
@@ -187,13 +190,9 @@ <h1>&lt;shacl-form&gt; demo</h1>
187190
<fieldset id="shacl-output" class="mt-1"><legend>Output generated by the form</legend><pre></pre></fieldset>
188191
<script type="module">
189192
// import { MapboxPlugin } from '@ulb-darmstadt/shacl-form/plugins/mapbox.js'
190-
import { LeafletPlugin } from '@ulb-darmstadt/shacl-form/plugins/leaflet.js'
191193

192194
setTimeout(async () => {
193195
const form = document.getElementById("shacl-form")
194-
// form.registerPlugin(new MapboxPlugin({ datatype: 'http://www.opengis.net/ont/geosparql#wktLiteral' }, 'pk.eyJ1IjoiaHViZXJtb3NlciIsImEiOiJja3c2NDI2MXAwbWx0MnVudnJiOGV6NjRqIn0.va4IWkUk-USoL2Z8FylNzA'))
195-
form.registerPlugin(new LeafletPlugin({ datatype: 'http://www.opengis.net/ont/geosparql#wktLiteral' }))
196-
197196
const shapes = document.getElementById("shacl-shape-input")
198197
const data = document.getElementById("shacl-data-input")
199198
const output = document.getElementById("shacl-output")
@@ -235,14 +234,8 @@ <h1>&lt;shacl-form&gt; demo</h1>
235234
<fieldset><legend>Generated viewer</legend><shacl-form id="shacl-form" data-collapse="open" data-view data-value-subject="http://example.org/4f2a8de3-9fc8-40a9-9237-d5964520ec54"></shacl-form></fieldset>
236235
</div>
237236
<script type="module">
238-
// import { MapboxPlugin } from '@ulb-darmstadt/shacl-form/plugins/mapbox.js'
239-
import { LeafletPlugin } from '@ulb-darmstadt/shacl-form/plugins/leaflet.js'
240-
241237
setTimeout(async () => {
242238
const form = document.getElementById("shacl-form")
243-
// form.registerPlugin(new MapboxPlugin({ datatype: 'http://www.opengis.net/ont/geosparql#wktLiteral' }, 'pk.eyJ1IjoiaHViZXJtb3NlciIsImEiOiJja3c2NDI2MXAwbWx0MnVudnJiOGV6NjRqIn0.va4IWkUk-USoL2Z8FylNzA'))
244-
form.registerPlugin(new LeafletPlugin({ datatype: 'http://www.opengis.net/ont/geosparql#wktLiteral' }))
245-
246239
const shapes = document.getElementById("shacl-shape-input")
247240
const data = document.getElementById("shacl-data-input")
248241
form.setClassInstanceProvider((clazz) => {

package-lock.json

Lines changed: 198 additions & 138 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@ulb-darmstadt/shacl-form",
3-
"version": "1.4.2",
3+
"version": "1.4.4",
44
"description": "SHACL form generator",
55
"main": "dist/form-default.js",
66
"module": "dist/form-default.js",
@@ -32,8 +32,8 @@
3232
},
3333
"devDependencies": {
3434
"@types/jsonld": "^1.5.13",
35-
"@types/leaflet": "^1.9.8",
36-
"@types/leaflet-draw": "^1.0.11",
35+
"@types/leaflet": "^1.9.9",
36+
"@types/leaflet-editable": "^1.2.6",
3737
"@types/leaflet.fullscreen": "^3.0.2",
3838
"@types/mapbox__mapbox-gl-draw": "^1.4.6",
3939
"@types/n3": "^1.16.4",
@@ -53,10 +53,10 @@
5353
"bootstrap": "^5.3.3",
5454
"jsonld": "^8.3.2",
5555
"leaflet": "^1.9.4",
56-
"leaflet-draw": "^1.0.4",
56+
"leaflet-editable": "^1.2.0",
5757
"leaflet.fullscreen": "^3.0.1",
5858
"mapbox-gl": "^3.2.0",
59-
"n3": "^1.17.2",
59+
"n3": "^1.17.3",
6060
"rdf-validate-shacl": "^0.5.3",
6161
"uuid": "^9.0.1"
6262
}

src/config.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { Prefixes, Store } from 'n3'
22
import { Term } from '@rdfjs/types'
33
import { PREFIX_SHACL, RDF_PREDICATE_TYPE, SHAPES_GRAPH } from './constants'
4-
import { ClassInstanceProvider, Plugins } from './plugin'
4+
import { ClassInstanceProvider } from './plugin'
55
import { Loader } from './loader'
66
import { Theme } from './theme'
77

@@ -26,7 +26,6 @@ export class Config {
2626
loader = new Loader(this)
2727
classInstanceProvider: ClassInstanceProvider | undefined
2828
prefixes: Prefixes = {}
29-
plugins = new Plugins()
3029
editMode = true
3130

3231
dataGraph = new Store()

src/exports.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
export { Theme, InputListEntry, Editor } from './theme'
22
export { Loader } from './loader'
33
export { Config } from './config'
4-
export { Plugin } from './plugin'
4+
export { Plugin, registerPlugin } from './plugin'
55
export { ShaclPropertyTemplate } from './property-template'
66
export { findLabel } from './util'

src/form.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { ShaclNode } from './node'
22
import { Config } from './config'
3-
import { ClassInstanceProvider, Plugin, PluginOptions } from './plugin'
3+
import { ClassInstanceProvider, Plugin, listPlugins, registerPlugin } from './plugin'
44
import { Quad, Store, NamedNode, DataFactory } from 'n3'
55
import { RDF_PREDICATE_TYPE, SHACL_OBJECT_NODE_SHAPE, SHACL_PREDICATE_TARGET_CLASS, SHAPES_GRAPH } from './constants'
66
import { Editor, Theme } from './theme'
@@ -60,7 +60,7 @@ export class ShaclForm extends HTMLElement {
6060
this.config.theme.apply(this.form)
6161
// adopt stylesheets from theme and plugins
6262
const styles: CSSStyleSheet[] = [ this.config.theme.stylesheet ]
63-
for (const plugin of this.config.plugins.list()) {
63+
for (const plugin of listPlugins()) {
6464
if (plugin.stylesheet) {
6565
styles.push(plugin.stylesheet)
6666
}
@@ -117,8 +117,8 @@ export class ShaclForm extends HTMLElement {
117117
return graph
118118
}
119119

120-
public registerPlugin(plugin: Plugin, options?: PluginOptions) {
121-
this.config.plugins.register(plugin)
120+
public registerPlugin(plugin: Plugin) {
121+
registerPlugin(plugin)
122122
this.initialize()
123123
}
124124

src/node.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,21 +27,17 @@ export class ShaclNode extends HTMLElement {
2727
nodeKind = spec[0] as NamedNode
2828
}
2929
}
30-
if (nodeKind === undefined && config.attributes.valuesNamespace) {
30+
// if nodeKind is not set, but a value namespace is configured or if nodeKind is sh:IRI, then create a NamedNode
31+
if ((nodeKind === undefined && config.attributes.valuesNamespace) || nodeKind?.id === `${PREFIX_SHACL}IRI`) {
3132
// no requirements on node type, so create a NamedNode and use configured value namespace
3233
nodeId = DataFactory.namedNode(config.attributes.valuesNamespace + uuidv4())
33-
}
34-
else if (nodeKind?.id === `${PREFIX_SHACL}IRI`) {
35-
nodeId = DataFactory.namedNode(config.attributes.valuesNamespace + uuidv4())
36-
}
37-
else {
38-
// default to BlankNode
34+
} else {
35+
// otherwise create a BlankNode
3936
nodeId = DataFactory.blankNode(uuidv4())
4037
}
4138
}
4239
this.nodeId = nodeId
4340
this.dataset.nodeId = this.nodeId.id
44-
4541

4642
const quads = config.shapesGraph.getQuads(shaclSubject, null, null, SHAPES_GRAPH)
4743
let list: Term[] | undefined
@@ -66,7 +62,11 @@ export class ShaclNode extends HTMLElement {
6662
console.warn('ignoring unknown group reference', groupRef[0])
6763
}
6864
}
69-
parent.appendChild(new ShaclProperty(quad.object as NamedNode | BlankNode, config, this.nodeId, valueSubject))
65+
const property = new ShaclProperty(quad.object as NamedNode | BlankNode, config, this.nodeId, valueSubject)
66+
// do not add empty properties (i.e. properties with no instances). This can be the case e.g. in viewer mode when there is no data for the respective property.
67+
if (property.childElementCount > 0) {
68+
parent.appendChild(property)
69+
}
7070
break;
7171
case `${PREFIX_SHACL}and`:
7272
// inheritance via sh:and

src/plugin.ts

Lines changed: 24 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,34 @@
11
import { ShaclPropertyTemplate } from './property-template'
22
import { Term } from '@rdfjs/types'
33

4-
export class Plugins {
5-
private plugins: Record<string, Plugin> = {}
6-
7-
register(plugin: Plugin) {
8-
if (plugin.predicate === undefined && plugin.datatype === undefined) {
9-
console.warn('not registering plugin because it does neither define "predicate" nor "datatype"', plugin)
10-
} else {
11-
this.plugins[`${plugin.predicate}^${plugin.datatype}`] = plugin
12-
}
4+
// store plugins in module scope so that they apply to all shacl-form elements
5+
const plugins: Record<string, Plugin> = {}
6+
7+
export function registerPlugin(plugin: Plugin) {
8+
if (plugin.predicate === undefined && plugin.datatype === undefined) {
9+
console.warn('not registering plugin because it does neither define "predicate" nor "datatype"', plugin)
10+
} else {
11+
plugins[`${plugin.predicate}^${plugin.datatype}`] = plugin
1312
}
13+
}
1414

15-
list(): Plugin[] {
16-
return Object.entries(this.plugins).map((value: [_: string, plugin: Plugin]) => { return value[1] })
17-
}
15+
export function listPlugins(): Plugin[] {
16+
return Object.entries(plugins).map((value: [_: string, plugin: Plugin]) => { return value[1] })
17+
}
1818

19-
find(predicate: string | undefined, datatype: string | undefined): Plugin | undefined {
20-
// first try to find plugin with matching predicate and datatype
21-
let plugin = this.plugins[`${predicate}^${datatype}`]
22-
if (plugin) {
23-
return plugin
24-
}
25-
// now prefer predicate over datatype
26-
plugin = this.plugins[`${predicate}^${undefined}`]
27-
if (plugin) {
28-
return plugin
29-
}
30-
// last, try to find plugin with matching datatype
31-
return this.plugins[`${undefined}^${datatype}`]
19+
export function findPlugin(predicate: string | undefined, datatype: string | undefined): Plugin | undefined {
20+
// first try to find plugin with matching predicate and datatype
21+
let plugin = plugins[`${predicate}^${datatype}`]
22+
if (plugin) {
23+
return plugin
24+
}
25+
// now prefer predicate over datatype
26+
plugin = plugins[`${predicate}^${undefined}`]
27+
if (plugin) {
28+
return plugin
3229
}
30+
// last, try to find plugin with matching datatype
31+
return plugins[`${undefined}^${datatype}`]
3332
}
3433

3534
export type PluginOptions = {

src/plugins/file-upload.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { Plugin, PluginOptions } from '../plugin'
2+
import { ShaclPropertyTemplate } from '../property-template'
3+
4+
export class FileUploadPlugin extends Plugin {
5+
onChange: (event: Event) => void
6+
fileType: string | undefined
7+
8+
constructor(options: PluginOptions, onChange: (event: Event) => void, fileType?: string) {
9+
super(options)
10+
this.onChange = onChange
11+
this.fileType = fileType
12+
}
13+
14+
createEditor(template: ShaclPropertyTemplate): HTMLElement {
15+
const required = template.minCount !== undefined && template.minCount > 0
16+
const editor = template.config.theme.createFileEditor(template.label, null, required, template)
17+
editor.addEventListener('change', event => {
18+
event.stopPropagation()
19+
this.onChange(event)
20+
})
21+
if (this.fileType) {
22+
editor.querySelector('input[type="file"]')?.setAttribute('accept', this.fileType)
23+
}
24+
return editor
25+
}
26+
}

0 commit comments

Comments
 (0)