@ulb-darmstadt/shacl-form 1.7.4 → 1.8.1
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/README.md +20 -7
- package/dist/config.d.ts +4 -5
- package/dist/constants.d.ts +13 -13
- package/dist/constraints.d.ts +2 -2
- package/dist/exports.d.ts +2 -1
- package/dist/form-bootstrap.d.ts +1 -1
- package/dist/form-bootstrap.js +361 -2
- package/dist/form-default.d.ts +1 -1
- package/dist/form-default.js +350 -2
- package/dist/form-material.d.ts +1 -1
- package/dist/form-material.js +670 -2
- package/dist/form.d.ts +3 -2
- package/dist/node.d.ts +2 -1
- package/dist/plugins/leaflet.d.ts +2 -4
- package/dist/plugins/leaflet.js +720 -2
- package/dist/plugins/mapbox.d.ts +2 -2
- package/dist/plugins/mapbox.js +2764 -2
- package/dist/property-template.d.ts +1 -1
- package/dist/property.d.ts +6 -2
- package/dist/theme.d.ts +3 -3
- package/dist/themes/default.d.ts +3 -3
- package/dist/themes/material.d.ts +2 -3
- package/dist/util.d.ts +2 -2
- package/package.json +26 -12
- package/src/config.ts +11 -10
- package/src/constants.ts +3 -3
- package/src/constraints.ts +15 -18
- package/src/exports.ts +2 -1
- package/src/form.ts +32 -17
- package/src/group.ts +1 -1
- package/src/loader.ts +12 -13
- package/src/node.ts +40 -38
- package/src/plugins/leaflet.ts +2 -2
- package/src/plugins/mapbox.ts +4 -4
- package/src/property-template.ts +4 -5
- package/src/property.ts +154 -56
- package/src/serialize.ts +14 -1
- package/src/styles.css +8 -10
- package/src/theme.ts +6 -6
- package/src/themes/bootstrap.ts +1 -1
- package/src/themes/default.css +2 -2
- package/src/themes/default.ts +38 -30
- package/src/themes/material.ts +37 -34
- package/src/util.ts +26 -20
- package/dist/form-bootstrap.js.LICENSE.txt +0 -69
- package/dist/form-default.js.LICENSE.txt +0 -69
- package/dist/form-material.js.LICENSE.txt +0 -69
- package/dist/plugins/file-upload.js +0 -1
- package/dist/plugins/fixed-list.js +0 -1
- package/dist/plugins/leaflet.js.LICENSE.txt +0 -4
- package/dist/plugins/mapbox.js.LICENSE.txt +0 -10
|
@@ -35,7 +35,7 @@ export declare class ShaclPropertyTemplate {
|
|
|
35
35
|
qualifiedValueShape: Term | undefined;
|
|
36
36
|
owlImports: NamedNode[];
|
|
37
37
|
config: Config;
|
|
38
|
-
extendedShapes: NamedNode[]
|
|
38
|
+
extendedShapes: NamedNode[];
|
|
39
39
|
constructor(quads: Quad[], parent: ShaclNode, config: Config);
|
|
40
40
|
merge(quads: Quad[]): ShaclPropertyTemplate;
|
|
41
41
|
clone(): ShaclPropertyTemplate;
|
package/dist/property.d.ts
CHANGED
|
@@ -3,12 +3,16 @@ import { Term } from '@rdfjs/types';
|
|
|
3
3
|
import { ShaclNode } from './node';
|
|
4
4
|
import { Config } from './config';
|
|
5
5
|
import { ShaclPropertyTemplate } from './property-template';
|
|
6
|
+
import { RokitSelect } from '@ro-kit/ui-widgets';
|
|
6
7
|
export declare class ShaclProperty extends HTMLElement {
|
|
7
8
|
template: ShaclPropertyTemplate;
|
|
8
|
-
addButton:
|
|
9
|
+
addButton: RokitSelect | undefined;
|
|
9
10
|
constructor(shaclSubject: BlankNode | NamedNode, parent: ShaclNode, config: Config, valueSubject?: NamedNode | BlankNode);
|
|
10
11
|
addPropertyInstance(value?: Term): HTMLElement;
|
|
11
12
|
updateControls(): void;
|
|
12
13
|
toRDF(graph: Store, subject: NamedNode | BlankNode): void;
|
|
14
|
+
getRdfClassToLinkOrCreate(): NamedNode<string> | undefined;
|
|
15
|
+
isValueValid(value: Term): boolean;
|
|
16
|
+
createAddButton(): RokitSelect;
|
|
13
17
|
}
|
|
14
|
-
export declare function createPropertyInstance(template: ShaclPropertyTemplate, value?: Term, forceRemovable?: boolean): HTMLElement;
|
|
18
|
+
export declare function createPropertyInstance(template: ShaclPropertyTemplate, value?: Term, forceRemovable?: boolean, linked?: boolean): HTMLElement;
|
package/dist/theme.d.ts
CHANGED
|
@@ -12,12 +12,12 @@ export type Editor = HTMLElement & {
|
|
|
12
12
|
export type InputListEntry = {
|
|
13
13
|
value: Term | string;
|
|
14
14
|
label?: string;
|
|
15
|
-
|
|
15
|
+
children?: InputListEntry[];
|
|
16
16
|
};
|
|
17
17
|
export declare abstract class Theme {
|
|
18
18
|
stylesheet: CSSStyleSheet;
|
|
19
19
|
constructor(styles?: string);
|
|
20
|
-
apply(
|
|
20
|
+
apply(_: HTMLFormElement): void;
|
|
21
21
|
createViewer(label: string, value: Term, template: ShaclPropertyTemplate): HTMLElement;
|
|
22
22
|
abstract createListEditor(label: string, value: Term | null, required: boolean, listEntries: InputListEntry[], template?: ShaclPropertyTemplate): HTMLElement;
|
|
23
23
|
abstract createLangStringEditor(label: string, value: Term | null, required: boolean, template: ShaclPropertyTemplate): HTMLElement;
|
|
@@ -28,4 +28,4 @@ export declare abstract class Theme {
|
|
|
28
28
|
abstract createFileEditor(label: string, value: Term | null, required: boolean, template: ShaclPropertyTemplate): HTMLElement;
|
|
29
29
|
abstract createButton(label: string, primary: boolean): HTMLElement;
|
|
30
30
|
}
|
|
31
|
-
export declare function fieldFactory(template: ShaclPropertyTemplate, value: Term | null): HTMLElement;
|
|
31
|
+
export declare function fieldFactory(template: ShaclPropertyTemplate, value: Term | null, editable: boolean): HTMLElement;
|
package/dist/themes/default.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Term } from '@rdfjs/types';
|
|
2
|
-
import { ShaclPropertyTemplate } from
|
|
3
|
-
import { Editor, InputListEntry, Theme } from
|
|
2
|
+
import { ShaclPropertyTemplate } from '../property-template';
|
|
3
|
+
import { Editor, InputListEntry, Theme } from '../theme';
|
|
4
4
|
export declare class DefaultTheme extends Theme {
|
|
5
5
|
idCtr: number;
|
|
6
6
|
constructor(overiddenCss?: string);
|
|
@@ -12,5 +12,5 @@ export declare class DefaultTheme extends Theme {
|
|
|
12
12
|
createFileEditor(label: string, value: Term | null, required: boolean, template: ShaclPropertyTemplate): HTMLElement;
|
|
13
13
|
createNumberEditor(label: string, value: Term | null, required: boolean, template: ShaclPropertyTemplate): HTMLElement;
|
|
14
14
|
createListEditor(label: string, value: Term | null, required: boolean, listEntries: InputListEntry[], template?: ShaclPropertyTemplate): HTMLElement;
|
|
15
|
-
createButton(label: string,
|
|
15
|
+
createButton(label: string, _: boolean): HTMLElement;
|
|
16
16
|
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { ShaclPropertyTemplate } from '../property-template';
|
|
2
2
|
import { Term } from '@rdfjs/types';
|
|
3
|
-
import { Theme } from '../theme';
|
|
4
|
-
import { InputListEntry, Editor } from '../theme';
|
|
3
|
+
import { Theme, InputListEntry, Editor } from '../theme';
|
|
5
4
|
export declare class MaterialTheme extends Theme {
|
|
6
5
|
constructor();
|
|
7
6
|
createDefaultTemplate(label: string, value: Term | null, required: boolean, editor: Editor, template?: ShaclPropertyTemplate): HTMLElement;
|
|
@@ -9,7 +8,7 @@ export declare class MaterialTheme extends Theme {
|
|
|
9
8
|
createNumberEditor(label: string, value: Term | null, required: boolean, template: ShaclPropertyTemplate): HTMLElement;
|
|
10
9
|
createListEditor(label: string, value: Term | null, required: boolean, listEntries: InputListEntry[], template?: ShaclPropertyTemplate): HTMLElement;
|
|
11
10
|
createBooleanEditor(label: string, value: Term | null, required: boolean, template: ShaclPropertyTemplate): HTMLElement;
|
|
12
|
-
createDateEditor(
|
|
11
|
+
createDateEditor(_: string, value: Term | null, required: boolean, template: ShaclPropertyTemplate): HTMLElement;
|
|
13
12
|
createLangStringEditor(label: string, value: Term | null, required: boolean, template: ShaclPropertyTemplate): HTMLElement;
|
|
14
13
|
createFileEditor(label: string, value: Term | null, required: boolean, template: ShaclPropertyTemplate): HTMLElement;
|
|
15
14
|
createButton(label: string, primary: boolean): HTMLElement;
|
package/dist/util.d.ts
CHANGED
|
@@ -6,8 +6,8 @@ export declare function findObjectValueByPredicate(quads: Quad[], predicate: str
|
|
|
6
6
|
export declare function findObjectByPredicate(quads: Quad[], predicate: string, prefix?: string, languages?: string[]): Term | undefined;
|
|
7
7
|
export declare function focusFirstInputElement(context: HTMLElement): void;
|
|
8
8
|
export declare function findLabel(quads: Quad[], languages: string[]): string;
|
|
9
|
-
export declare function createInputListEntries(subjects: Term[], shapesGraph: Store, languages: string[]
|
|
9
|
+
export declare function createInputListEntries(subjects: Term[], shapesGraph: Store, languages: string[]): InputListEntry[];
|
|
10
10
|
export declare function removePrefixes(id: string, prefixes: Prefixes): string;
|
|
11
|
-
export declare function findInstancesOf(clazz: NamedNode, template: ShaclPropertyTemplate
|
|
11
|
+
export declare function findInstancesOf(clazz: NamedNode, template: ShaclPropertyTemplate): InputListEntry[];
|
|
12
12
|
export declare function isURL(input: string): boolean;
|
|
13
13
|
export declare function prioritizeByLanguage(languages: string[], text1?: Literal, text2?: Literal): Literal | undefined;
|
package/package.json
CHANGED
|
@@ -1,10 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ulb-darmstadt/shacl-form",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.8.1",
|
|
4
4
|
"description": "SHACL form generator",
|
|
5
5
|
"main": "dist/form-default.js",
|
|
6
6
|
"module": "dist/form-default.js",
|
|
7
7
|
"types": "dist/form-default.d.ts",
|
|
8
|
+
"type": "module",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"import": "./dist/form-default.js",
|
|
12
|
+
"types": "./dist/form-default.d.ts"
|
|
13
|
+
},
|
|
14
|
+
"./*.ts": "./src/*.ts",
|
|
15
|
+
"./plugins/*.ts": "./src/plugins/*.ts"
|
|
16
|
+
},
|
|
8
17
|
"files": [
|
|
9
18
|
"dist",
|
|
10
19
|
"src"
|
|
@@ -26,26 +35,28 @@
|
|
|
26
35
|
"viewer"
|
|
27
36
|
],
|
|
28
37
|
"scripts": {
|
|
29
|
-
"
|
|
30
|
-
"
|
|
38
|
+
"dev": "vite serve ./demo --host",
|
|
39
|
+
"build": "tsc && npm run build-default && npm run build-bootstrap && npm run build-material && npm run build-leaflet && npm run build-mapbox",
|
|
40
|
+
"preview": "vite preview",
|
|
31
41
|
"test": "echo \"no tests specified\" && exit 0",
|
|
32
|
-
"
|
|
42
|
+
"build-default": "vite build -c ./vite.form-default.config.ts",
|
|
43
|
+
"build-bootstrap": "vite build -c ./vite.form-bootstrap.config.ts",
|
|
44
|
+
"build-material": "vite build -c ./vite.form-material.config.ts",
|
|
45
|
+
"build-leaflet": "vite build -c ./vite.plugin-leaflet.config.ts",
|
|
46
|
+
"build-mapbox": "vite build -c ./vite.plugin-mapbox.config.ts"
|
|
33
47
|
},
|
|
34
48
|
"devDependencies": {
|
|
35
49
|
"@types/jsonld": "^1.5.15",
|
|
36
|
-
"@types/leaflet": "^1.9.
|
|
50
|
+
"@types/leaflet": "^1.9.20",
|
|
37
51
|
"@types/leaflet-editable": "^1.2.6",
|
|
38
52
|
"@types/leaflet.fullscreen": "^3.0.2",
|
|
39
53
|
"@types/mapbox__mapbox-gl-draw": "^1.4.9",
|
|
40
54
|
"@types/n3": "^1.26.0",
|
|
41
55
|
"@types/uuid": "^10.0.0",
|
|
42
|
-
"
|
|
43
|
-
"rimraf": "^6.0.1",
|
|
44
|
-
"ts-loader": "^9.5.2",
|
|
56
|
+
"rollup-plugin-peer-deps-external": "^2.2.4",
|
|
45
57
|
"typescript": "^5.8.3",
|
|
46
|
-
"
|
|
47
|
-
"
|
|
48
|
-
"webpack-dev-server": "^5.2.2"
|
|
58
|
+
"vite": "^7.0.4",
|
|
59
|
+
"vite-plugin-dts": "^4.5.4"
|
|
49
60
|
},
|
|
50
61
|
"dependencies": {
|
|
51
62
|
"@mapbox/mapbox-gl-draw": "^1.5.0",
|
|
@@ -55,9 +66,12 @@
|
|
|
55
66
|
"leaflet-editable": "^1.3.1",
|
|
56
67
|
"leaflet.fullscreen": "^4.0.0",
|
|
57
68
|
"mapbox-gl": "^3.13.0",
|
|
58
|
-
"mdui": "^2.1.4",
|
|
59
69
|
"n3": "^1.26.0",
|
|
60
70
|
"shacl-engine": "^1.0.2",
|
|
61
71
|
"uuid": "^11.1.0"
|
|
72
|
+
},
|
|
73
|
+
"peerDependencies": {
|
|
74
|
+
"@ro-kit/ui-widgets": "^0.0.28",
|
|
75
|
+
"mdui": "^2.1.4"
|
|
62
76
|
}
|
|
63
77
|
}
|
package/src/config.ts
CHANGED
|
@@ -36,14 +36,13 @@ export class Config {
|
|
|
36
36
|
editMode = true
|
|
37
37
|
languages: string[]
|
|
38
38
|
|
|
39
|
-
dataGraph = new Store()
|
|
40
39
|
lists: Record<string, Term[]> = {}
|
|
41
40
|
groups: Array<string> = []
|
|
42
41
|
theme: Theme
|
|
43
42
|
form: HTMLElement
|
|
44
43
|
renderedNodes = new Set<string>()
|
|
45
|
-
|
|
46
|
-
private
|
|
44
|
+
valuesGraphId: NamedNode | undefined
|
|
45
|
+
private _store = new Store()
|
|
47
46
|
|
|
48
47
|
constructor(theme: Theme, form: HTMLElement) {
|
|
49
48
|
this.theme = theme
|
|
@@ -80,7 +79,9 @@ export class Config {
|
|
|
80
79
|
// now prepend preferred language at start of the list of languages
|
|
81
80
|
this.languages.unshift(atts.language)
|
|
82
81
|
}
|
|
83
|
-
|
|
82
|
+
if (atts.valuesGraph) {
|
|
83
|
+
this.valuesGraphId = DataFactory.namedNode(atts.valuesGraph)
|
|
84
|
+
}
|
|
84
85
|
}
|
|
85
86
|
|
|
86
87
|
static dataAttributes(): Array<string> {
|
|
@@ -92,15 +93,15 @@ export class Config {
|
|
|
92
93
|
})
|
|
93
94
|
}
|
|
94
95
|
|
|
95
|
-
get
|
|
96
|
-
return this.
|
|
96
|
+
get store() {
|
|
97
|
+
return this._store
|
|
97
98
|
}
|
|
98
99
|
|
|
99
|
-
set
|
|
100
|
-
this.
|
|
101
|
-
this.lists =
|
|
100
|
+
set store(store: Store) {
|
|
101
|
+
this._store = store
|
|
102
|
+
this.lists = store.extractLists()
|
|
102
103
|
this.groups = []
|
|
103
|
-
|
|
104
|
+
store.forSubjects(subject => {
|
|
104
105
|
this.groups.push(subject.id)
|
|
105
106
|
}, RDF_PREDICATE_TYPE, `${PREFIX_SHACL}PropertyGroup`, null)
|
|
106
107
|
}
|
package/src/constants.ts
CHANGED
|
@@ -9,15 +9,15 @@ export const PREFIX_SKOS = 'http://www.w3.org/2004/02/skos/core#'
|
|
|
9
9
|
export const PREFIX_OWL = 'http://www.w3.org/2002/07/owl#'
|
|
10
10
|
export const PREFIX_OA = 'http://www.w3.org/ns/oa#'
|
|
11
11
|
export const PREFIX_DCTERMS = 'http://purl.org/dc/terms/'
|
|
12
|
+
export const PREFIX_FOAF = 'http://xmlns.com/foaf/0.1/'
|
|
12
13
|
|
|
13
|
-
export const SHAPES_GRAPH = DataFactory.namedNode('shapes')
|
|
14
|
+
export const SHAPES_GRAPH = DataFactory.namedNode('loaded-shapes')
|
|
15
|
+
export const DATA_GRAPH = DataFactory.namedNode('loaded-data')
|
|
14
16
|
|
|
15
17
|
export const RDF_PREDICATE_TYPE = DataFactory.namedNode(PREFIX_RDF + 'type')
|
|
16
18
|
export const DCTERMS_PREDICATE_CONFORMS_TO = DataFactory.namedNode(PREFIX_DCTERMS + 'conformsTo')
|
|
17
19
|
export const RDFS_PREDICATE_SUBCLASS_OF = DataFactory.namedNode(PREFIX_RDFS + 'subClassOf')
|
|
18
|
-
export const SKOS_PREDICATE_BROADER = DataFactory.namedNode(PREFIX_SKOS + 'broader')
|
|
19
20
|
export const OWL_PREDICATE_IMPORTS = DataFactory.namedNode(PREFIX_OWL + 'imports')
|
|
20
|
-
export const OWL_OBJECT_NAMED_INDIVIDUAL = DataFactory.namedNode(PREFIX_OWL + 'NamedIndividual')
|
|
21
21
|
export const SHACL_OBJECT_NODE_SHAPE = DataFactory.namedNode(PREFIX_SHACL + 'NodeShape')
|
|
22
22
|
export const SHACL_OBJECT_IRI = DataFactory.namedNode(PREFIX_SHACL + 'IRI')
|
|
23
23
|
export const SHACL_PREDICATE_PROPERTY = DataFactory.namedNode(PREFIX_SHACL + 'property')
|
package/src/constraints.ts
CHANGED
|
@@ -5,7 +5,6 @@ import { ShaclProperty, createPropertyInstance } from "./property"
|
|
|
5
5
|
import { Config } from './config'
|
|
6
6
|
import { PREFIX_SHACL, RDF_PREDICATE_TYPE, SHACL_PREDICATE_CLASS, SHACL_PREDICATE_TARGET_CLASS, SHACL_PREDICATE_NODE_KIND, SHACL_OBJECT_IRI, SHACL_PREDICATE_PROPERTY } from './constants'
|
|
7
7
|
import { findLabel, removePrefixes } from './util'
|
|
8
|
-
import { ShaclPropertyTemplate } from './property-template'
|
|
9
8
|
import { Editor, InputListEntry } from './theme'
|
|
10
9
|
|
|
11
10
|
|
|
@@ -21,11 +20,11 @@ export function createShaclOrConstraint(options: Term[], context: ShaclNode | Sh
|
|
|
21
20
|
// options can be shacl properties or blank nodes referring to (list of) properties
|
|
22
21
|
let optionsAreReferencedProperties = false
|
|
23
22
|
if (options.length) {
|
|
24
|
-
optionsAreReferencedProperties = config.
|
|
23
|
+
optionsAreReferencedProperties = config.store.countQuads(options[0], SHACL_PREDICATE_PROPERTY, null, null) > 0
|
|
25
24
|
}
|
|
26
25
|
for (let i = 0; i < options.length; i++) {
|
|
27
26
|
if (optionsAreReferencedProperties) {
|
|
28
|
-
const quads = config.
|
|
27
|
+
const quads = config.store.getObjects(options[i] , SHACL_PREDICATE_PROPERTY, null)
|
|
29
28
|
// option can be single property or list of properties
|
|
30
29
|
const list: ShaclProperty[] = []
|
|
31
30
|
let combinedText = ''
|
|
@@ -53,8 +52,8 @@ export function createShaclOrConstraint(options: Term[], context: ShaclNode | Sh
|
|
|
53
52
|
constraintElement.replaceWith(selectedOptions[0])
|
|
54
53
|
}
|
|
55
54
|
for (let i = 1; i < selectedOptions.length; i++) {
|
|
56
|
-
lastAddedProperty!.after(selectedOptions[
|
|
57
|
-
lastAddedProperty = selectedOptions[
|
|
55
|
+
lastAddedProperty!.after(selectedOptions[i])
|
|
56
|
+
lastAddedProperty = selectedOptions[i]
|
|
58
57
|
}
|
|
59
58
|
}
|
|
60
59
|
}
|
|
@@ -62,7 +61,7 @@ export function createShaclOrConstraint(options: Term[], context: ShaclNode | Sh
|
|
|
62
61
|
} else {
|
|
63
62
|
const values: Quad[][] = []
|
|
64
63
|
for (let i = 0; i < options.length; i++) {
|
|
65
|
-
const quads = config.
|
|
64
|
+
const quads = config.store.getQuads(options[i], null, null, null)
|
|
66
65
|
if (quads.length) {
|
|
67
66
|
values.push(quads)
|
|
68
67
|
optionElements.push({ label: findLabel(quads, config.languages) || (removePrefixes(quads[0].predicate.value, config.prefixes) + ' = ' + removePrefixes(quads[0].object.value, config.prefixes)), value: i.toString() })
|
|
@@ -86,7 +85,7 @@ export function resolveShaclOrConstraintOnProperty(subjects: Term[], value: Term
|
|
|
86
85
|
// value is a literal, try to resolve sh:or/sh:xone by matching on given value datatype
|
|
87
86
|
const valueType = value.datatype
|
|
88
87
|
for (const subject of subjects) {
|
|
89
|
-
const options = config.
|
|
88
|
+
const options = config.store.getQuads(subject, null, null, null)
|
|
90
89
|
for (const quad of options) {
|
|
91
90
|
if (quad.predicate.value === `${PREFIX_SHACL}datatype` && quad.object.equals(valueType)) {
|
|
92
91
|
return options
|
|
@@ -95,16 +94,15 @@ export function resolveShaclOrConstraintOnProperty(subjects: Term[], value: Term
|
|
|
95
94
|
}
|
|
96
95
|
} else {
|
|
97
96
|
// value is a NamedNode or BlankNode, try to resolve sh:or/sh:xone by matching rdf:type of given value with sh:node or sh:class in data graph or shapes graph
|
|
98
|
-
const types = config.
|
|
99
|
-
types.push(...config.shapesGraph.getObjects(value, RDF_PREDICATE_TYPE, null))
|
|
97
|
+
const types = config.store.getObjects(value, RDF_PREDICATE_TYPE, null)
|
|
100
98
|
for (const subject of subjects) {
|
|
101
|
-
const options = config.
|
|
99
|
+
const options = config.store.getQuads(subject, null, null, null)
|
|
102
100
|
for (const quad of options) {
|
|
103
101
|
if (types.length > 0) {
|
|
104
102
|
// try to find matching sh:node in sh:or/sh:xone values
|
|
105
103
|
if (quad.predicate.value === `${PREFIX_SHACL}node`) {
|
|
106
104
|
for (const type of types) {
|
|
107
|
-
if (config.
|
|
105
|
+
if (config.store.getQuads(quad.object, SHACL_PREDICATE_TARGET_CLASS, type, null).length > 0) {
|
|
108
106
|
return options
|
|
109
107
|
}
|
|
110
108
|
}
|
|
@@ -130,15 +128,14 @@ export function resolveShaclOrConstraintOnProperty(subjects: Term[], value: Term
|
|
|
130
128
|
|
|
131
129
|
export function resolveShaclOrConstraintOnNode(subjects: Term[], value: Term, config: Config): Term[] {
|
|
132
130
|
for (const subject of subjects) {
|
|
133
|
-
let subjectMatches =
|
|
134
|
-
const propertySubjects = config.
|
|
131
|
+
let subjectMatches = false
|
|
132
|
+
const propertySubjects = config.store.getObjects(subject, SHACL_PREDICATE_PROPERTY, null)
|
|
135
133
|
for (const propertySubject of propertySubjects) {
|
|
136
|
-
const paths = config.
|
|
134
|
+
const paths = config.store.getObjects(propertySubject, `${PREFIX_SHACL}path`, null)
|
|
137
135
|
for (const path of paths) {
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
if (
|
|
141
|
-
subjectMatches = false
|
|
136
|
+
// this allows partial matches in data or shapes graph on properties
|
|
137
|
+
subjectMatches = config.store.countQuads(value, path, null, null) > 0
|
|
138
|
+
if (subjectMatches) {
|
|
142
139
|
break
|
|
143
140
|
}
|
|
144
141
|
}
|
package/src/exports.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export type { InputListEntry, Editor } from './theme'
|
|
2
|
+
export { Theme } from './theme'
|
|
2
3
|
export { Loader, setSharedShapesGraph } from './loader'
|
|
3
4
|
export { Config } from './config'
|
|
4
5
|
export { Plugin, registerPlugin } from './plugin'
|
package/src/form.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { ShaclNode } from './node'
|
|
2
2
|
import { Config } from './config'
|
|
3
3
|
import { ClassInstanceProvider, Plugin, listPlugins, registerPlugin } from './plugin'
|
|
4
|
-
import { Store, NamedNode, DataFactory, Quad } from 'n3'
|
|
5
|
-
import { DCTERMS_PREDICATE_CONFORMS_TO, PREFIX_SHACL, RDF_PREDICATE_TYPE, SHACL_OBJECT_NODE_SHAPE, SHACL_PREDICATE_TARGET_CLASS } from './constants'
|
|
4
|
+
import { Store, NamedNode, DataFactory, Quad, BlankNode } from 'n3'
|
|
5
|
+
import { DATA_GRAPH, DCTERMS_PREDICATE_CONFORMS_TO, PREFIX_SHACL, RDF_PREDICATE_TYPE, SHACL_OBJECT_NODE_SHAPE, SHACL_PREDICATE_TARGET_CLASS, SHAPES_GRAPH } from './constants'
|
|
6
6
|
import { Editor, Theme } from './theme'
|
|
7
7
|
import { serialize } from './serialize'
|
|
8
8
|
import { Validator } from 'shacl-engine'
|
|
@@ -95,9 +95,13 @@ export class ShaclForm extends HTMLElement {
|
|
|
95
95
|
})
|
|
96
96
|
this.form.appendChild(button)
|
|
97
97
|
}
|
|
98
|
+
// delete bound values from data graph, otherwise validation would be confused
|
|
99
|
+
if (this.config.attributes.valuesSubject) {
|
|
100
|
+
this.removeFromDataGraph(DataFactory.namedNode(this.config.attributes.valuesSubject))
|
|
101
|
+
}
|
|
98
102
|
await this.validate(true)
|
|
99
103
|
}
|
|
100
|
-
} else if (this.config.
|
|
104
|
+
} else if (this.config.store.countQuads(null, null, null, SHAPES_GRAPH) > 0) {
|
|
101
105
|
// raise error only when shapes graph is not empty
|
|
102
106
|
throw new Error('shacl root node shape not found')
|
|
103
107
|
}
|
|
@@ -154,14 +158,14 @@ export class ShaclForm extends HTMLElement {
|
|
|
154
158
|
}
|
|
155
159
|
}
|
|
156
160
|
|
|
157
|
-
this.config.
|
|
158
|
-
this.shape?.toRDF(this.config.
|
|
161
|
+
this.config.store.deleteGraph(this.config.valuesGraphId || '')
|
|
162
|
+
this.shape?.toRDF(this.config.store)
|
|
159
163
|
if (this.shape) {
|
|
160
164
|
// add node target for validation. this is required in case of missing sh:targetClass in root shape
|
|
161
|
-
this.config.
|
|
165
|
+
this.config.store.add(new Quad(this.shape.shaclSubject, DataFactory.namedNode(PREFIX_SHACL + 'targetNode'), this.shape.nodeId, this.config.valuesGraphId))
|
|
162
166
|
}
|
|
163
167
|
try {
|
|
164
|
-
const dataset = this.config.
|
|
168
|
+
const dataset = this.config.store
|
|
165
169
|
const report = await new Validator(dataset, { details: true, factory: DataFactory }).validate({ dataset })
|
|
166
170
|
|
|
167
171
|
for (const result of report.results) {
|
|
@@ -172,10 +176,10 @@ export class ShaclForm extends HTMLElement {
|
|
|
172
176
|
if (result.path?.length) {
|
|
173
177
|
const path = result.path[0].predicates[0]
|
|
174
178
|
// try to find most specific editor elements first
|
|
175
|
-
let invalidElements = this.form.querySelectorAll(`:scope [data-node-id='${focusNode.id}'] [data-path='${path.id}'] > .editor`)
|
|
179
|
+
let invalidElements = this.form.querySelectorAll(`:scope shacl-node[data-node-id='${focusNode.id}'] > shacl-property > .property-instance[data-path='${path.id}'] > .editor, :scope shacl-node[data-node-id='${focusNode.id}'] > .shacl-group > shacl-property > .property-instance[data-path='${path.id}'] > .editor`)
|
|
176
180
|
if (invalidElements.length === 0) {
|
|
177
181
|
// if no editors found, select respective node. this will be the case for node shape violations.
|
|
178
|
-
invalidElements = this.form.querySelectorAll(`:scope [data-node-id='${focusNode.id}'] [data-path='${path.id}']`)
|
|
182
|
+
invalidElements = this.form.querySelectorAll(`:scope [data-node-id='${focusNode.id}'] > * > [data-path='${path.id}']`)
|
|
179
183
|
}
|
|
180
184
|
|
|
181
185
|
for (const invalidElement of invalidElements) {
|
|
@@ -236,18 +240,18 @@ export class ShaclForm extends HTMLElement {
|
|
|
236
240
|
// if data-shape-subject is set, use that
|
|
237
241
|
if (this.config.attributes.shapeSubject) {
|
|
238
242
|
rootShapeShaclSubject = DataFactory.namedNode(this.config.attributes.shapeSubject)
|
|
239
|
-
if (this.config.
|
|
243
|
+
if (this.config.store.getQuads(rootShapeShaclSubject, RDF_PREDICATE_TYPE, SHACL_OBJECT_NODE_SHAPE, null).length === 0) {
|
|
240
244
|
console.warn(`shapes graph does not contain requested root shape ${this.config.attributes.shapeSubject}`)
|
|
241
245
|
return
|
|
242
246
|
}
|
|
243
247
|
}
|
|
244
248
|
else {
|
|
245
249
|
// if we have a data graph and data-values-subject is set, use shape of that
|
|
246
|
-
if (this.config.attributes.valuesSubject && this.config.
|
|
250
|
+
if (this.config.attributes.valuesSubject && this.config.store.countQuads(null, null, null, DATA_GRAPH) > 0) {
|
|
247
251
|
const rootValueSubject = DataFactory.namedNode(this.config.attributes.valuesSubject)
|
|
248
252
|
const rootValueSubjectTypes = [
|
|
249
|
-
...this.config.
|
|
250
|
-
...this.config.
|
|
253
|
+
...this.config.store.getQuads(rootValueSubject, RDF_PREDICATE_TYPE, null, DATA_GRAPH),
|
|
254
|
+
...this.config.store.getQuads(rootValueSubject, DCTERMS_PREDICATE_CONFORMS_TO, null, DATA_GRAPH)
|
|
251
255
|
]
|
|
252
256
|
if (rootValueSubjectTypes.length === 0) {
|
|
253
257
|
console.warn(`value subject '${this.config.attributes.valuesSubject}' has neither ${RDF_PREDICATE_TYPE.id} nor ${DCTERMS_PREDICATE_CONFORMS_TO.id} statement`)
|
|
@@ -255,13 +259,13 @@ export class ShaclForm extends HTMLElement {
|
|
|
255
259
|
}
|
|
256
260
|
// if type/conformsTo refers to a node shape, prioritize that over targetClass resolution
|
|
257
261
|
for (const rootValueSubjectType of rootValueSubjectTypes) {
|
|
258
|
-
if (this.config.
|
|
262
|
+
if (this.config.store.getQuads(rootValueSubjectType.object as NamedNode, RDF_PREDICATE_TYPE, SHACL_OBJECT_NODE_SHAPE, null).length > 0) {
|
|
259
263
|
rootShapeShaclSubject = rootValueSubjectType.object as NamedNode
|
|
260
264
|
break
|
|
261
265
|
}
|
|
262
266
|
}
|
|
263
267
|
if (!rootShapeShaclSubject) {
|
|
264
|
-
const rootShapes = this.config.
|
|
268
|
+
const rootShapes = this.config.store.getQuads(null, SHACL_PREDICATE_TARGET_CLASS, rootValueSubjectTypes[0].object, null)
|
|
265
269
|
if (rootShapes.length === 0) {
|
|
266
270
|
console.error(`value subject '${this.config.attributes.valuesSubject}' has no shacl shape definition in the shapes graph`)
|
|
267
271
|
return
|
|
@@ -269,7 +273,7 @@ export class ShaclForm extends HTMLElement {
|
|
|
269
273
|
if (rootShapes.length > 1) {
|
|
270
274
|
console.warn(`value subject '${this.config.attributes.valuesSubject}' has multiple shacl shape definitions in the shapes graph, choosing the first found (${rootShapes[0].subject})`)
|
|
271
275
|
}
|
|
272
|
-
if (this.config.
|
|
276
|
+
if (this.config.store.getQuads(rootShapes[0].subject, RDF_PREDICATE_TYPE, SHACL_OBJECT_NODE_SHAPE, null).length === 0) {
|
|
273
277
|
console.error(`value subject '${this.config.attributes.valuesSubject}' references a shape which is not a NodeShape (${rootShapes[0].subject})`)
|
|
274
278
|
return
|
|
275
279
|
}
|
|
@@ -278,7 +282,7 @@ export class ShaclForm extends HTMLElement {
|
|
|
278
282
|
}
|
|
279
283
|
else {
|
|
280
284
|
// choose first of all defined root shapes
|
|
281
|
-
const rootShapes = this.config.
|
|
285
|
+
const rootShapes = this.config.store.getQuads(null, RDF_PREDICATE_TYPE, SHACL_OBJECT_NODE_SHAPE, null)
|
|
282
286
|
if (rootShapes.length == 0) {
|
|
283
287
|
console.warn('shapes graph does not contain any root shapes')
|
|
284
288
|
return
|
|
@@ -292,4 +296,15 @@ export class ShaclForm extends HTMLElement {
|
|
|
292
296
|
}
|
|
293
297
|
return rootShapeShaclSubject
|
|
294
298
|
}
|
|
299
|
+
|
|
300
|
+
private removeFromDataGraph(subject: NamedNode | BlankNode) {
|
|
301
|
+
this.config.attributes.valuesSubject
|
|
302
|
+
for (const quad of this.config.store.getQuads(subject, null, null, DATA_GRAPH)) {
|
|
303
|
+
this.config.store.delete(quad)
|
|
304
|
+
if (quad.object.termType === 'NamedNode' || quad.object.termType === 'BlankNode') {
|
|
305
|
+
// recurse
|
|
306
|
+
this.removeFromDataGraph(quad.object)
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
295
310
|
}
|
package/src/group.ts
CHANGED
|
@@ -7,7 +7,7 @@ export function createShaclGroup(groupSubject: string, config: Config): HTMLElem
|
|
|
7
7
|
group.dataset['subject'] = groupSubject
|
|
8
8
|
group.classList.add('shacl-group')
|
|
9
9
|
let name = groupSubject
|
|
10
|
-
const quads = config.
|
|
10
|
+
const quads = config.store.getQuads(groupSubject, null, null, null)
|
|
11
11
|
const label = findObjectValueByPredicate(quads, "label", PREFIX_RDFS, config.languages)
|
|
12
12
|
if (label) {
|
|
13
13
|
name = label
|
package/src/loader.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Store, Parser, Quad, Prefixes, NamedNode, DataFactory } from 'n3'
|
|
2
2
|
import { toRDF } from 'jsonld'
|
|
3
|
-
import { DCTERMS_PREDICATE_CONFORMS_TO, OWL_PREDICATE_IMPORTS, RDF_PREDICATE_TYPE, SHACL_PREDICATE_CLASS, SHAPES_GRAPH } from './constants'
|
|
3
|
+
import { DATA_GRAPH, DCTERMS_PREDICATE_CONFORMS_TO, OWL_PREDICATE_IMPORTS, RDF_PREDICATE_TYPE, SHACL_PREDICATE_CLASS, SHAPES_GRAPH } from './constants'
|
|
4
4
|
import { Config } from './config'
|
|
5
5
|
import { isURL } from './util'
|
|
6
6
|
|
|
@@ -25,31 +25,31 @@ export class Loader {
|
|
|
25
25
|
this.loadedExternalUrls = []
|
|
26
26
|
this.loadedClasses = []
|
|
27
27
|
|
|
28
|
-
let
|
|
29
|
-
const valuesStore = new Store()
|
|
28
|
+
let store = sharedShapesGraph
|
|
30
29
|
this.config.prefixes = {}
|
|
31
30
|
|
|
32
|
-
const promises = [
|
|
33
|
-
if (!
|
|
34
|
-
|
|
35
|
-
promises.push(this.importRDF(this.config.attributes.shapes ? this.config.attributes.shapes : this.config.attributes.shapesUrl ? this.fetchRDF(this.config.attributes.shapesUrl) : '',
|
|
31
|
+
const promises: Promise<void>[] = []
|
|
32
|
+
if (!store) {
|
|
33
|
+
store = new Store()
|
|
34
|
+
promises.push(this.importRDF(this.config.attributes.shapes ? this.config.attributes.shapes : this.config.attributes.shapesUrl ? this.fetchRDF(this.config.attributes.shapesUrl) : '', store, SHAPES_GRAPH))
|
|
36
35
|
}
|
|
36
|
+
promises.push(this.importRDF(this.config.attributes.values ? this.config.attributes.values : this.config.attributes.valuesUrl ? this.fetchRDF(this.config.attributes.valuesUrl) : '', store, DATA_GRAPH, new Parser({ blankNodePrefix: '' })))
|
|
37
37
|
await Promise.all(promises)
|
|
38
38
|
|
|
39
39
|
// if shapes graph is empty, but we have the following triples:
|
|
40
40
|
// <valueSubject> a <uri> or <valueSubject> dcterms:conformsTo <uri>
|
|
41
41
|
// then try to load the referenced object into the shapes graph
|
|
42
|
-
if (!sharedShapesGraph &&
|
|
42
|
+
if (!sharedShapesGraph && store?.size == 0 && this.config.attributes.valuesSubject) {
|
|
43
43
|
const shapeCandidates = [
|
|
44
|
-
...
|
|
45
|
-
...
|
|
44
|
+
...store.getObjects(this.config.attributes.valuesSubject, RDF_PREDICATE_TYPE, DATA_GRAPH),
|
|
45
|
+
...store.getObjects(this.config.attributes.valuesSubject, DCTERMS_PREDICATE_CONFORMS_TO, DATA_GRAPH)
|
|
46
46
|
]
|
|
47
47
|
const promises: Promise<void>[] = []
|
|
48
48
|
for (const uri of shapeCandidates) {
|
|
49
49
|
const url = this.toURL(uri.value)
|
|
50
50
|
if (url && this.loadedExternalUrls.indexOf(url) < 0) {
|
|
51
51
|
this.loadedExternalUrls.push(url)
|
|
52
|
-
promises.push(this.importRDF(this.fetchRDF(url),
|
|
52
|
+
promises.push(this.importRDF(this.fetchRDF(url), store, SHAPES_GRAPH))
|
|
53
53
|
}
|
|
54
54
|
}
|
|
55
55
|
try {
|
|
@@ -59,8 +59,7 @@ export class Loader {
|
|
|
59
59
|
}
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
-
this.config.
|
|
63
|
-
this.config.dataGraph = valuesStore
|
|
62
|
+
this.config.store = store
|
|
64
63
|
}
|
|
65
64
|
|
|
66
65
|
async importRDF(input: string | Promise<string>, store: Store, graph?: NamedNode, parser?: Parser) {
|