@ulb-darmstadt/shacl-form 1.8.1 → 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.
- package/dist/constants.d.ts +1 -0
- package/dist/form-bootstrap.js +76 -75
- package/dist/form-default.js +73 -72
- package/dist/form-material.js +93 -92
- package/dist/plugins/leaflet.js +5 -5
- package/dist/plugins/mapbox.js +12 -12
- package/dist/property.d.ts +1 -0
- package/package.json +2 -2
- package/src/constants.ts +2 -1
- package/src/constraints.ts +0 -1
- package/src/form.ts +17 -5
- package/src/group.ts +17 -18
- package/src/property-template.ts +1 -0
- package/src/property.ts +22 -20
- package/src/styles.css +12 -20
- package/src/themes/default.css +1 -1
- package/src/themes/default.ts +34 -22
- package/src/themes/material.ts +16 -9
package/dist/property.d.ts
CHANGED
|
@@ -7,6 +7,7 @@ import { RokitSelect } from '@ro-kit/ui-widgets';
|
|
|
7
7
|
export declare class ShaclProperty extends HTMLElement {
|
|
8
8
|
template: ShaclPropertyTemplate;
|
|
9
9
|
addButton: RokitSelect | undefined;
|
|
10
|
+
container: HTMLElement;
|
|
10
11
|
constructor(shaclSubject: BlankNode | NamedNode, parent: ShaclNode, config: Config, valueSubject?: NamedNode | BlankNode);
|
|
11
12
|
addPropertyInstance(value?: Term): HTMLElement;
|
|
12
13
|
updateControls(): void;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ulb-darmstadt/shacl-form",
|
|
3
|
-
"version": "1.8.
|
|
3
|
+
"version": "1.8.2",
|
|
4
4
|
"description": "SHACL form generator",
|
|
5
5
|
"main": "dist/form-default.js",
|
|
6
6
|
"module": "dist/form-default.js",
|
|
@@ -71,7 +71,7 @@
|
|
|
71
71
|
"uuid": "^11.1.0"
|
|
72
72
|
},
|
|
73
73
|
"peerDependencies": {
|
|
74
|
-
"@ro-kit/ui-widgets": "^0.0.
|
|
74
|
+
"@ro-kit/ui-widgets": "^0.0.29",
|
|
75
75
|
"mdui": "^2.1.4"
|
|
76
76
|
}
|
|
77
77
|
}
|
package/src/constants.ts
CHANGED
|
@@ -23,4 +23,5 @@ export const SHACL_OBJECT_IRI = DataFactory.namedNode(PREFIX_SHACL + 'IRI')
|
|
|
23
23
|
export const SHACL_PREDICATE_PROPERTY = DataFactory.namedNode(PREFIX_SHACL + 'property')
|
|
24
24
|
export const SHACL_PREDICATE_CLASS = DataFactory.namedNode(PREFIX_SHACL + 'class')
|
|
25
25
|
export const SHACL_PREDICATE_TARGET_CLASS = DataFactory.namedNode(PREFIX_SHACL + 'targetClass')
|
|
26
|
-
export const SHACL_PREDICATE_NODE_KIND = DataFactory.namedNode(PREFIX_SHACL + 'nodeKind')
|
|
26
|
+
export const SHACL_PREDICATE_NODE_KIND = DataFactory.namedNode(PREFIX_SHACL + 'nodeKind')
|
|
27
|
+
export const XSD_DATATYPE_STRING = DataFactory.namedNode(PREFIX_XSD + 'string')
|
package/src/constraints.ts
CHANGED
|
@@ -13,7 +13,6 @@ export function createShaclOrConstraint(options: Term[], context: ShaclNode | Sh
|
|
|
13
13
|
constraintElement.classList.add('shacl-or-constraint')
|
|
14
14
|
|
|
15
15
|
const optionElements: InputListEntry[] = []
|
|
16
|
-
optionElements.push({ label: '--- please choose ---', value: '' })
|
|
17
16
|
|
|
18
17
|
if (context instanceof ShaclNode) {
|
|
19
18
|
const properties: ShaclProperty[][] = []
|
package/src/form.ts
CHANGED
|
@@ -7,6 +7,7 @@ import { Editor, Theme } from './theme'
|
|
|
7
7
|
import { serialize } from './serialize'
|
|
8
8
|
import { Validator } from 'shacl-engine'
|
|
9
9
|
import { setSharedShapesGraph } from './loader'
|
|
10
|
+
import { RokitCollapsible } from '@ro-kit/ui-widgets'
|
|
10
11
|
|
|
11
12
|
export class ShaclForm extends HTMLElement {
|
|
12
13
|
static get observedAttributes() { return Config.dataAttributes() }
|
|
@@ -88,7 +89,12 @@ export class ShaclForm extends HTMLElement {
|
|
|
88
89
|
this.dispatchEvent(new Event('submit', { bubbles: true, cancelable: true }))
|
|
89
90
|
} else {
|
|
90
91
|
// focus first invalid element
|
|
91
|
-
|
|
92
|
+
let invalidEditor = this.form.querySelector(':scope .invalid > .editor')
|
|
93
|
+
if (invalidEditor) {
|
|
94
|
+
(invalidEditor as HTMLElement).focus()
|
|
95
|
+
} else {
|
|
96
|
+
this.form.querySelector(':scope .invalid')?.scrollIntoView()
|
|
97
|
+
}
|
|
92
98
|
}
|
|
93
99
|
})
|
|
94
100
|
}
|
|
@@ -176,10 +182,16 @@ export class ShaclForm extends HTMLElement {
|
|
|
176
182
|
if (result.path?.length) {
|
|
177
183
|
const path = result.path[0].predicates[0]
|
|
178
184
|
// try to find most specific editor elements first
|
|
179
|
-
let invalidElements = this.form.querySelectorAll(
|
|
185
|
+
let invalidElements = this.form.querySelectorAll(`
|
|
186
|
+
:scope shacl-node[data-node-id='${focusNode.id}'] > shacl-property > .property-instance[data-path='${path.id}'] > .editor,
|
|
187
|
+
:scope shacl-node[data-node-id='${focusNode.id}'] > shacl-property > .shacl-group > .property-instance[data-path='${path.id}'] > .editor,
|
|
188
|
+
:scope shacl-node[data-node-id='${focusNode.id}'] > .shacl-group > shacl-property > .property-instance[data-path='${path.id}'] > .editor,
|
|
189
|
+
:scope shacl-node[data-node-id='${focusNode.id}'] > .shacl-group > shacl-property > .shacl-group > .property-instance[data-path='${path.id}'] > .editor`)
|
|
180
190
|
if (invalidElements.length === 0) {
|
|
181
191
|
// if no editors found, select respective node. this will be the case for node shape violations.
|
|
182
|
-
invalidElements = this.form.querySelectorAll(
|
|
192
|
+
invalidElements = this.form.querySelectorAll(`
|
|
193
|
+
:scope [data-node-id='${focusNode.id}'] > shacl-property > .property-instance[data-path='${path.id}'],
|
|
194
|
+
:scope [data-node-id='${focusNode.id}'] > shacl-property > .shacl-group > .property-instance[data-path='${path.id}']`)
|
|
183
195
|
}
|
|
184
196
|
|
|
185
197
|
for (const invalidElement of invalidElements) {
|
|
@@ -191,8 +203,8 @@ export class ShaclForm extends HTMLElement {
|
|
|
191
203
|
parent.classList.remove('valid')
|
|
192
204
|
parent.appendChild(this.createValidationErrorDisplay(result))
|
|
193
205
|
do {
|
|
194
|
-
if (parent
|
|
195
|
-
parent.
|
|
206
|
+
if (parent instanceof RokitCollapsible) {
|
|
207
|
+
parent.open = true
|
|
196
208
|
}
|
|
197
209
|
parent = parent.parentElement
|
|
198
210
|
} while (parent)
|
package/src/group.ts
CHANGED
|
@@ -1,35 +1,34 @@
|
|
|
1
1
|
import { PREFIX_RDFS } from './constants'
|
|
2
2
|
import { Config } from './config'
|
|
3
3
|
import { findObjectValueByPredicate } from './util'
|
|
4
|
+
import { RokitCollapsible } from '@ro-kit/ui-widgets'
|
|
4
5
|
|
|
5
6
|
export function createShaclGroup(groupSubject: string, config: Config): HTMLElement {
|
|
6
|
-
const group = document.createElement('div')
|
|
7
|
-
group.dataset['subject'] = groupSubject
|
|
8
|
-
group.classList.add('shacl-group')
|
|
9
7
|
let name = groupSubject
|
|
10
8
|
const quads = config.store.getQuads(groupSubject, null, null, null)
|
|
11
9
|
const label = findObjectValueByPredicate(quads, "label", PREFIX_RDFS, config.languages)
|
|
12
10
|
if (label) {
|
|
13
11
|
name = label
|
|
14
12
|
}
|
|
15
|
-
const order = findObjectValueByPredicate(quads, "order")
|
|
16
|
-
if (order) {
|
|
17
|
-
group.style.order = order
|
|
18
|
-
}
|
|
19
|
-
const header = document.createElement('h1')
|
|
20
|
-
header.innerText = name
|
|
21
|
-
group.appendChild(header)
|
|
22
13
|
|
|
14
|
+
let group: HTMLElement
|
|
23
15
|
if (config.attributes.collapse !== null) {
|
|
24
|
-
group
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
16
|
+
group = new RokitCollapsible()
|
|
17
|
+
group.classList.add('collapsible');
|
|
18
|
+
(group as RokitCollapsible).open = config.attributes.collapse === 'open';
|
|
19
|
+
(group as RokitCollapsible).label = name
|
|
20
|
+
} else {
|
|
21
|
+
group = document.createElement('div')
|
|
22
|
+
const header = document.createElement('h1')
|
|
23
|
+
header.innerText = name
|
|
24
|
+
group.appendChild(header)
|
|
25
|
+
}
|
|
32
26
|
|
|
27
|
+
group.dataset['subject'] = groupSubject
|
|
28
|
+
group.classList.add('shacl-group')
|
|
29
|
+
const order = findObjectValueByPredicate(quads, "order")
|
|
30
|
+
if (order) {
|
|
31
|
+
group.style.order = order
|
|
33
32
|
}
|
|
34
33
|
return group
|
|
35
34
|
}
|
package/src/property-template.ts
CHANGED
|
@@ -135,6 +135,7 @@ export class ShaclPropertyTemplate {
|
|
|
135
135
|
clone(): ShaclPropertyTemplate {
|
|
136
136
|
const copy = Object.assign({}, this)
|
|
137
137
|
// arrays are not cloned but referenced, so create them manually
|
|
138
|
+
copy.extendedShapes = [ ...this.extendedShapes ]
|
|
138
139
|
copy.owlImports = [ ...this.owlImports ]
|
|
139
140
|
if (this.languageIn) {
|
|
140
141
|
copy.languageIn = [ ...this.languageIn ]
|
package/src/property.ts
CHANGED
|
@@ -9,15 +9,25 @@ import { Editor, fieldFactory, InputListEntry } from './theme'
|
|
|
9
9
|
import { toRDF } from './serialize'
|
|
10
10
|
import { findPlugin } from './plugin'
|
|
11
11
|
import { DATA_GRAPH, RDF_PREDICATE_TYPE, SHACL_PREDICATE_TARGET_CLASS } from './constants'
|
|
12
|
-
import { RokitButton, RokitSelect } from '@ro-kit/ui-widgets'
|
|
12
|
+
import { RokitButton, RokitCollapsible, RokitSelect } from '@ro-kit/ui-widgets'
|
|
13
13
|
|
|
14
14
|
export class ShaclProperty extends HTMLElement {
|
|
15
15
|
template: ShaclPropertyTemplate
|
|
16
16
|
addButton: RokitSelect | undefined
|
|
17
|
+
container: HTMLElement
|
|
17
18
|
|
|
18
19
|
constructor(shaclSubject: BlankNode | NamedNode, parent: ShaclNode, config: Config, valueSubject?: NamedNode | BlankNode) {
|
|
19
20
|
super()
|
|
20
21
|
this.template = new ShaclPropertyTemplate(config.store.getQuads(shaclSubject, null, null, null), parent, config)
|
|
22
|
+
if (this.template.extendedShapes.length && this.template.config.attributes.collapse !== null && (!this.template.maxCount || this.template.maxCount > 1)) {
|
|
23
|
+
const collapsible = new RokitCollapsible()
|
|
24
|
+
collapsible.classList.add('collapsible', 'shacl-group');
|
|
25
|
+
collapsible.open = config.attributes.collapse === 'open';
|
|
26
|
+
collapsible.label = this.template.label;
|
|
27
|
+
this.container = collapsible
|
|
28
|
+
} else {
|
|
29
|
+
this.container = this
|
|
30
|
+
}
|
|
21
31
|
|
|
22
32
|
if (this.template.order !== undefined) {
|
|
23
33
|
this.style.order = `${this.template.order}`
|
|
@@ -28,7 +38,7 @@ export class ShaclProperty extends HTMLElement {
|
|
|
28
38
|
|
|
29
39
|
if (config.editMode && !parent.linked) {
|
|
30
40
|
this.addButton = this.createAddButton()
|
|
31
|
-
this.appendChild(this.addButton)
|
|
41
|
+
this.container.appendChild(this.addButton)
|
|
32
42
|
}
|
|
33
43
|
|
|
34
44
|
// bind existing values
|
|
@@ -65,21 +75,10 @@ export class ShaclProperty extends HTMLElement {
|
|
|
65
75
|
this.updateControls()
|
|
66
76
|
}
|
|
67
77
|
|
|
68
|
-
if (this.
|
|
78
|
+
if (this.container instanceof RokitCollapsible) {
|
|
69
79
|
// in view mode, show collapsible only when we have something to show
|
|
70
|
-
if ((config.editMode && !parent.linked) || this.childElementCount > 0) {
|
|
71
|
-
|
|
72
|
-
collapsible.classList.add('collapsible')
|
|
73
|
-
if (this.template.config.attributes.collapse === 'open') {
|
|
74
|
-
collapsible.classList.add('open')
|
|
75
|
-
}
|
|
76
|
-
const activator = document.createElement('h1')
|
|
77
|
-
activator.classList.add('activator')
|
|
78
|
-
activator.innerText = this.template.label
|
|
79
|
-
activator.addEventListener('click', () => {
|
|
80
|
-
collapsible.classList.toggle('open')
|
|
81
|
-
})
|
|
82
|
-
this.prepend(activator)
|
|
80
|
+
if ((config.editMode && !parent.linked) || this.container.childElementCount > 0) {
|
|
81
|
+
this.appendChild(this.container)
|
|
83
82
|
}
|
|
84
83
|
}
|
|
85
84
|
}
|
|
@@ -113,9 +112,9 @@ export class ShaclProperty extends HTMLElement {
|
|
|
113
112
|
instance = createPropertyInstance(this.template, value, undefined, linked || this.template.parent.linked)
|
|
114
113
|
}
|
|
115
114
|
if (this.addButton) {
|
|
116
|
-
this.insertBefore(instance!, this.addButton)
|
|
115
|
+
this.container.insertBefore(instance!, this.addButton)
|
|
117
116
|
} else {
|
|
118
|
-
this.appendChild(instance!)
|
|
117
|
+
this.container.appendChild(instance!)
|
|
119
118
|
}
|
|
120
119
|
return instance!
|
|
121
120
|
}
|
|
@@ -136,10 +135,13 @@ export class ShaclProperty extends HTMLElement {
|
|
|
136
135
|
const mayAdd = this.template.maxCount === undefined || instanceCount < this.template.maxCount
|
|
137
136
|
this.classList.toggle('may-remove', mayRemove)
|
|
138
137
|
this.classList.toggle('may-add', mayAdd)
|
|
138
|
+
if (mayAdd && this.addButton?.input) {
|
|
139
|
+
this.addButton.input.updateMinWidth()
|
|
140
|
+
}
|
|
139
141
|
}
|
|
140
142
|
|
|
141
143
|
toRDF(graph: Store, subject: NamedNode | BlankNode) {
|
|
142
|
-
for (const instance of this.querySelectorAll(':scope > .property-instance')) {
|
|
144
|
+
for (const instance of this.querySelectorAll(':scope > .property-instance, :scope > .collapsible > .property-instance')) {
|
|
143
145
|
const pathNode = DataFactory.namedNode((instance as HTMLElement).dataset.path!)
|
|
144
146
|
if (instance.firstChild instanceof ShaclNode) {
|
|
145
147
|
const shapeSubject = instance.firstChild.toRDF(graph)
|
|
@@ -247,7 +249,7 @@ export class ShaclProperty extends HTMLElement {
|
|
|
247
249
|
} else {
|
|
248
250
|
// user wants to link existing instance
|
|
249
251
|
const value = JSON.parse(addButton.value) as Term
|
|
250
|
-
this.insertBefore(createPropertyInstance(this.template, value, true, true), addButton)
|
|
252
|
+
this.container.insertBefore(createPropertyInstance(this.template, value, true, true), addButton)
|
|
251
253
|
}
|
|
252
254
|
addButton.value = ''
|
|
253
255
|
})
|
package/src/styles.css
CHANGED
|
@@ -1,30 +1,28 @@
|
|
|
1
|
-
form {
|
|
1
|
+
form { display:block; --label-width: 8em; --caret-size: 10px; }
|
|
2
2
|
form.mode-edit { padding-left: 1em; }
|
|
3
|
-
form
|
|
4
|
-
shacl-node, .
|
|
3
|
+
form, form * { box-sizing: border-box; }
|
|
4
|
+
shacl-node, .collapsible::part(content) { display: flex; flex-direction: column; width: 100%; position: relative; }
|
|
5
5
|
shacl-node .remove-button { margin-left: 4px; margin-top: 1px; }
|
|
6
6
|
shacl-node .add-button { color: #555; background-color: transparent; margin: 4px 24px 0 0; border: 0; }
|
|
7
7
|
shacl-node .add-button:hover { color:#222; }
|
|
8
8
|
shacl-node .add-button:focus { box-shadow: none; }
|
|
9
|
-
shacl-node h1 { font-size:
|
|
10
|
-
shacl-property { display: flex; flex-direction: column; align-items: end; position: relative; }
|
|
9
|
+
shacl-node h1 { font-size: 16px; border-bottom: 1px solid #AAA; margin-top: 4px; color: #555; }
|
|
10
|
+
shacl-property:not(:has(>.collapsible)), shacl-property>.collapsible::part(content) { display: flex; flex-direction: column; align-items: end; position: relative; }
|
|
11
11
|
shacl-property:not(.may-add) > .add-button { display: none; }
|
|
12
12
|
shacl-property:not(.may-remove) > .property-instance > .remove-button:not(.persistent) { visibility: hidden; }
|
|
13
13
|
shacl-property:not(.may-remove) > .shacl-or-constraint > .remove-button:not(.persistent) { visibility: hidden; }
|
|
14
14
|
.mode-view .shacl-group:not(:has(shacl-property)) { display: none; }
|
|
15
15
|
.property-instance, .shacl-or-constraint { display: flex; align-items: flex-start; padding: 4px 0; width: 100%; position: relative; }
|
|
16
|
+
.shacl-or-constraint > div { display: flex; flex-grow: 1; }
|
|
16
17
|
.shacl-or-constraint label { display: inline-block; word-break: break-word; width: var(--label-width); line-height: 1em; padding-top: 0.15em; padding-right: 1em; flex-shrink: 0; position: relative; }
|
|
17
18
|
.property-instance label[title] { cursor: help; text-decoration: underline dashed #AAA; }
|
|
18
19
|
.property-instance.linked label:after, label.linked:after { content: '\1F517'; font-size: 0.6em; padding-left: 6px; }
|
|
19
20
|
.mode-edit .property-instance label.required::before { color: red; content: '\2736'; font-size: 0.6rem; position: absolute; left: -1.4em; top: 0.15rem; }
|
|
20
|
-
.property-instance.valid::before { position: absolute; left: calc(var(--label-width) - 1em); top:
|
|
21
|
-
.editor:not([type='checkbox'])
|
|
22
|
-
.shacl-or-constraint select { border: 1px solid #DDD; padding: 2px 4px; }
|
|
23
|
-
select { overflow: hidden; text-overflow: ellipsis; }
|
|
21
|
+
.property-instance.valid::before { content: ''; position: absolute; left: calc(var(--label-width) - 1em); top:0.5em; width: 0.9em; height: 0.9em; background: url('data:image/svg+xml;utf8,<svg viewBox="0 0 1024 1024" fill="green" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M866.133333 258.133333L362.666667 761.6l-204.8-204.8L98.133333 618.666667 362.666667 881.066667l563.2-563.2z"/></svg>'); }
|
|
22
|
+
.editor:not([type='checkbox']) { flex-grow: 1; }
|
|
24
23
|
textarea.editor { resize: vertical; }
|
|
25
|
-
.lang-chooser {
|
|
26
|
-
.
|
|
27
|
-
.validation-error { position: absolute; left: calc(var(--label-width) - 1em); top: 3px; color: red; cursor: help; }
|
|
24
|
+
.lang-chooser { border: 0; background-color: #e9e9ed; padding: 2px 4px; align-self: flex-start; }
|
|
25
|
+
.validation-error { position: absolute; left: calc(var(--label-width) - 1em); top: 0.5em; color: red; cursor: help; }
|
|
28
26
|
.validation-error::before { content: '\26a0' }
|
|
29
27
|
.validation-error.node { left: -1em; }
|
|
30
28
|
.invalid > .editor { border-color: red !important; }
|
|
@@ -43,15 +41,9 @@ a, a:visited { color: inherit; }
|
|
|
43
41
|
0% { opacity: 0; transform: scaleY(0.8); }
|
|
44
42
|
100% { opacity: 1; transform: scaleY(1); }
|
|
45
43
|
}
|
|
46
|
-
|
|
47
|
-
.collapsible > .
|
|
48
|
-
.collapsible > .activator:hover, .collapsible.open > .activator { background-color: #F5F5F5; }
|
|
49
|
-
.collapsible > .activator::after { content:''; width: var(--caret-size); height: var(--caret-size); border-style: none solid solid none; border-width: calc(0.3 * var(--caret-size)); transform: rotate(45deg); transition: transform .15s ease-out; margin-right: calc(0.5 * var(--caret-size)); }
|
|
50
|
-
.collapsible.open > .activator::after { transform: rotate(225deg); }
|
|
51
|
-
.collapsible > *:not(.activator) { transition: all 0.2s ease-out; opacity: 1; }
|
|
52
|
-
.collapsible:not(.open) > *:not(.activator) { max-height: 0; padding: 0; opacity: 0; overflow: hidden; }
|
|
44
|
+
.collapsible::part(label) { font-weight: 600; }
|
|
45
|
+
.collapsible > .property-instance:nth-child(even) { background-color: #F8F8F8; }
|
|
53
46
|
.collapsible > .property-instance > shacl-node > h1 { display: none; }
|
|
54
|
-
.collapsible.open > .property-instance:nth-child(odd) { background-color: #F5F5F5; }
|
|
55
47
|
.ref-link { cursor: pointer; }
|
|
56
48
|
.ref-link:hover { text-decoration: underline; }
|
|
57
49
|
.node-id-display { color: #999; font-size: 11px; }
|
package/src/themes/default.css
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
.editor:not([type='checkbox']) { border: 1px solid #DDD;
|
|
1
|
+
.editor:not([type='checkbox']) { border: 1px solid #DDD; }
|
|
2
2
|
.property-instance label { display: inline-flex; word-break: break-word; line-height: 1em; padding-top: 0.15em; padding-right: 1em; flex-shrink: 0; position: relative; }
|
|
3
3
|
.property-instance:not(:first-child) > label:not(.persistent) { visibility: hidden; max-height: 0; }
|
|
4
4
|
.mode-edit .property-instance label { width: var(--label-width); }
|
package/src/themes/default.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { Term } from '@rdfjs/types'
|
|
2
2
|
import { ShaclPropertyTemplate } from "../property-template"
|
|
3
3
|
import { Editor, InputListEntry, Theme } from "../theme"
|
|
4
|
-
import { PREFIX_SHACL, PREFIX_XSD } from '../constants'
|
|
4
|
+
import { PREFIX_SHACL, PREFIX_XSD, XSD_DATATYPE_STRING } from '../constants'
|
|
5
5
|
import { Literal, NamedNode } from 'n3'
|
|
6
6
|
import { Term as N3Term } from 'n3'
|
|
7
7
|
import css from './default.css?raw'
|
|
8
|
-
import { RokitSelect } from '@ro-kit/ui-widgets'
|
|
8
|
+
import { RokitInput, RokitSelect, RokitTextArea } from '@ro-kit/ui-widgets'
|
|
9
9
|
|
|
10
10
|
export class DefaultTheme extends Theme {
|
|
11
11
|
idCtr = 0
|
|
@@ -62,7 +62,7 @@ export class DefaultTheme extends Theme {
|
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
createDateEditor(label: string, value: Term | null, required: boolean, template: ShaclPropertyTemplate): HTMLElement {
|
|
65
|
-
const editor
|
|
65
|
+
const editor = new RokitInput()
|
|
66
66
|
if (template.datatype?.value === PREFIX_XSD + 'dateTime') {
|
|
67
67
|
editor.type = 'datetime-local'
|
|
68
68
|
// this enables seconds in dateTime input
|
|
@@ -71,6 +71,8 @@ export class DefaultTheme extends Theme {
|
|
|
71
71
|
else {
|
|
72
72
|
editor.type = 'date'
|
|
73
73
|
}
|
|
74
|
+
editor.clearable = true
|
|
75
|
+
editor.dense = true
|
|
74
76
|
editor.classList.add('pr-0')
|
|
75
77
|
const result = this.createDefaultTemplate(label, null, required, editor, template)
|
|
76
78
|
if (value) {
|
|
@@ -92,17 +94,16 @@ export class DefaultTheme extends Theme {
|
|
|
92
94
|
createTextEditor(label: string, value: Term | null, required: boolean, template: ShaclPropertyTemplate): HTMLElement {
|
|
93
95
|
let editor
|
|
94
96
|
if (template.singleLine === false) {
|
|
95
|
-
editor =
|
|
96
|
-
editor.
|
|
97
|
+
editor = new RokitTextArea()
|
|
98
|
+
editor.resize = 'auto'
|
|
97
99
|
}
|
|
98
100
|
else {
|
|
99
|
-
editor =
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
101
|
+
editor = new RokitInput()
|
|
102
|
+
}
|
|
103
|
+
editor.dense = true
|
|
104
|
+
if (template.pattern) {
|
|
105
|
+
editor.pattern = template.pattern
|
|
104
106
|
}
|
|
105
|
-
|
|
106
107
|
if (template.minLength) {
|
|
107
108
|
editor.minLength = template.minLength
|
|
108
109
|
}
|
|
@@ -126,10 +127,12 @@ export class DefaultTheme extends Theme {
|
|
|
126
127
|
} else {
|
|
127
128
|
langChooser = document.createElement('input')
|
|
128
129
|
langChooser.maxLength = 5 // e.g. en-US
|
|
130
|
+
langChooser.size = 5
|
|
129
131
|
langChooser.placeholder = 'lang?'
|
|
130
132
|
}
|
|
131
133
|
langChooser.title = 'Language of the text'
|
|
132
134
|
langChooser.classList.add('lang-chooser')
|
|
135
|
+
langChooser.slot = 'suffix'
|
|
133
136
|
// if lang chooser changes, fire a change event on the text input instead. this is for shacl validation handling.
|
|
134
137
|
langChooser.addEventListener('change', (ev) => {
|
|
135
138
|
ev.stopPropagation();
|
|
@@ -142,7 +145,7 @@ export class DefaultTheme extends Theme {
|
|
|
142
145
|
langChooser.value = value.language
|
|
143
146
|
}
|
|
144
147
|
editor.dataset.lang = langChooser.value
|
|
145
|
-
editor.
|
|
148
|
+
editor.appendChild(langChooser)
|
|
146
149
|
return result
|
|
147
150
|
}
|
|
148
151
|
|
|
@@ -182,8 +185,10 @@ export class DefaultTheme extends Theme {
|
|
|
182
185
|
}
|
|
183
186
|
|
|
184
187
|
createNumberEditor(label: string, value: Term | null, required: boolean, template: ShaclPropertyTemplate): HTMLElement {
|
|
185
|
-
const editor =
|
|
188
|
+
const editor = new RokitInput()
|
|
186
189
|
editor.type = 'number'
|
|
190
|
+
editor.clearable = true
|
|
191
|
+
editor.dense = true
|
|
187
192
|
editor.classList.add('pr-0')
|
|
188
193
|
const min = template.minInclusive !== undefined ? template.minInclusive : template.minExclusive !== undefined ? template.minExclusive + 1 : undefined
|
|
189
194
|
const max = template.maxInclusive !== undefined ? template.maxInclusive : template.maxExclusive !== undefined ? template.maxExclusive - 1 : undefined
|
|
@@ -201,25 +206,29 @@ export class DefaultTheme extends Theme {
|
|
|
201
206
|
|
|
202
207
|
createListEditor(label: string, value: Term | null, required: boolean, listEntries: InputListEntry[], template?: ShaclPropertyTemplate): HTMLElement {
|
|
203
208
|
const editor = new RokitSelect()
|
|
204
|
-
editor.dense = true
|
|
205
209
|
editor.clearable = true
|
|
206
|
-
editor.
|
|
210
|
+
editor.dense = true
|
|
207
211
|
const result = this.createDefaultTemplate(label, null, required, editor, template)
|
|
208
212
|
const ul = document.createElement('ul')
|
|
209
|
-
|
|
213
|
+
let isFlatList = true
|
|
214
|
+
|
|
210
215
|
const appendListEntry = (entry: InputListEntry, parent: HTMLUListElement) => {
|
|
211
216
|
const li = document.createElement('li')
|
|
212
|
-
let entryValue = ''
|
|
213
217
|
if (typeof entry.value === 'string') {
|
|
214
|
-
|
|
218
|
+
li.dataset.value = entry.value
|
|
219
|
+
li.innerText = entry.label ? entry.label : entry.value
|
|
215
220
|
} else {
|
|
216
|
-
|
|
217
|
-
|
|
221
|
+
if (entry.value instanceof Literal && entry.value.datatype.equals(XSD_DATATYPE_STRING)) {
|
|
222
|
+
li.dataset.value = entry.value.value
|
|
223
|
+
} else {
|
|
224
|
+
// this is needed for typed rdf literals
|
|
225
|
+
li.dataset.value = (entry.value as N3Term).id
|
|
226
|
+
}
|
|
227
|
+
li.innerText = entry.label ? entry.label : entry.value.value
|
|
218
228
|
}
|
|
219
|
-
li.innerText = entry.label ? entry.label : entryValue
|
|
220
|
-
li.dataset.value = entryValue
|
|
221
229
|
parent.appendChild(li)
|
|
222
230
|
if (entry.children?.length) {
|
|
231
|
+
isFlatList = false
|
|
223
232
|
const ul = document.createElement('ul')
|
|
224
233
|
li.appendChild(ul)
|
|
225
234
|
for (const child of entry.children) {
|
|
@@ -231,6 +240,9 @@ export class DefaultTheme extends Theme {
|
|
|
231
240
|
for (const item of listEntries) {
|
|
232
241
|
appendListEntry(item, ul)
|
|
233
242
|
}
|
|
243
|
+
if (!isFlatList) {
|
|
244
|
+
editor.collapse = true
|
|
245
|
+
}
|
|
234
246
|
|
|
235
247
|
editor.appendChild(ul)
|
|
236
248
|
if (value) {
|
package/src/themes/material.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { InputListEntry, Editor } from '../theme'
|
|
|
6
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_SHACL, PREFIX_XSD } from '../constants'
|
|
9
|
+
import { PREFIX_SHACL, PREFIX_XSD, XSD_DATATYPE_STRING } from '../constants'
|
|
10
10
|
import { RokitSelect } from '@ro-kit/ui-widgets'
|
|
11
11
|
|
|
12
12
|
export class MaterialTheme extends Theme {
|
|
@@ -96,24 +96,28 @@ export class MaterialTheme extends Theme {
|
|
|
96
96
|
|
|
97
97
|
createListEditor(label: string, value: Term | null, required: boolean, listEntries: InputListEntry[], template?: ShaclPropertyTemplate): HTMLElement {
|
|
98
98
|
const editor = new RokitSelect()
|
|
99
|
-
editor.dense = true
|
|
100
99
|
editor.clearable = true
|
|
101
100
|
const result = this.createDefaultTemplate(label, null, required, editor, template)
|
|
102
101
|
const ul = document.createElement('ul')
|
|
103
|
-
|
|
102
|
+
let isFlatList = true
|
|
103
|
+
|
|
104
104
|
const appendListEntry = (entry: InputListEntry, parent: HTMLUListElement) => {
|
|
105
105
|
const li = document.createElement('li')
|
|
106
|
-
let entryValue = ''
|
|
107
106
|
if (typeof entry.value === 'string') {
|
|
108
|
-
|
|
107
|
+
li.dataset.value = entry.value
|
|
108
|
+
li.innerText = entry.label ? entry.label : entry.value
|
|
109
109
|
} else {
|
|
110
|
-
|
|
111
|
-
|
|
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
|
|
112
117
|
}
|
|
113
|
-
li.innerText = entry.label ? entry.label : entryValue
|
|
114
|
-
li.dataset.value = entryValue
|
|
115
118
|
parent.appendChild(li)
|
|
116
119
|
if (entry.children?.length) {
|
|
120
|
+
isFlatList = false
|
|
117
121
|
const ul = document.createElement('ul')
|
|
118
122
|
li.appendChild(ul)
|
|
119
123
|
for (const child of entry.children) {
|
|
@@ -125,6 +129,9 @@ export class MaterialTheme extends Theme {
|
|
|
125
129
|
for (const item of listEntries) {
|
|
126
130
|
appendListEntry(item, ul)
|
|
127
131
|
}
|
|
132
|
+
if (!isFlatList) {
|
|
133
|
+
editor.collapse = true
|
|
134
|
+
}
|
|
128
135
|
|
|
129
136
|
editor.appendChild(ul)
|
|
130
137
|
if (value) {
|