@ulb-darmstadt/shacl-form
Version:
SHACL form generator
133 lines (121 loc) • 6.24 kB
text/typescript
import { Literal, NamedNode } from 'n3'
import { Term } from '@rdfjs/types'
import { PREFIX_XSD, PREFIX_RDF } from './constants'
import { createInputListEntries, findInstancesOf, findLabel, isURL } from './util'
import { ShaclPropertyTemplate } from './property-template'
import css from './styles.css?raw'
export type Editor = HTMLElement & { value: string, type?: string, shaclDatatype?: NamedNode<string>, binaryData?: string, checked?: boolean, disabled?: boolean }
export type InputListEntry = { value: Term | string, label?: string, indent?: number }
export abstract class Theme {
stylesheet: CSSStyleSheet
constructor(styles?: string) {
let aggregatedStyles = css
if (styles) {
aggregatedStyles += '\n' + styles
}
this.stylesheet = new CSSStyleSheet()
this.stylesheet.replaceSync(aggregatedStyles)
}
apply(root: HTMLFormElement) {
// NOP
}
createViewer(label: string, value: Term, template: ShaclPropertyTemplate): HTMLElement {
const viewer = document.createElement('div')
const labelElem = document.createElement('label')
labelElem.innerHTML = label + ':'
if (template.description) {
labelElem.setAttribute('title', template.description.value)
}
viewer.appendChild(labelElem)
let name = value.value
let lang: HTMLElement | null = null
if (value instanceof NamedNode) {
const quads = template.config.shapesGraph.getQuads(name, null, null, null)
if (quads.length) {
const s = findLabel(quads, template.config.languages)
if (s) {
name = s
}
}
} else if (value instanceof Literal) {
if (value.language) {
lang = document.createElement('span')
lang.classList.add('lang')
lang.innerText = `@${value.language}`
} else if (value.datatype.value === `${PREFIX_XSD}date`) {
name = new Date(Date.parse(value.value)).toDateString()
} else if (value.datatype.value === `${PREFIX_XSD}dateTime`) {
name = new Date(Date.parse(value.value)).toLocaleString()
}
}
let valueElem: HTMLElement
if (isURL(value.value)) {
valueElem = document.createElement('a')
valueElem.setAttribute('href', value.value)
} else {
valueElem = document.createElement('div')
}
valueElem.classList.add('d-flex')
valueElem.innerText = name
if (lang) {
valueElem.appendChild(lang)
}
viewer.appendChild(valueElem)
return viewer
}
abstract createListEditor(label: string, value: Term | null, required: boolean, listEntries: InputListEntry[], template?: ShaclPropertyTemplate): HTMLElement
abstract createLangStringEditor(label: string, value: Term | null, required: boolean, template: ShaclPropertyTemplate): HTMLElement
abstract createTextEditor(label: string, value: Term | null, required: boolean, template: ShaclPropertyTemplate): HTMLElement
abstract createNumberEditor(label: string, value: Term | null, required: boolean, template: ShaclPropertyTemplate): HTMLElement
abstract createDateEditor(label: string, value: Term | null, required: boolean, template: ShaclPropertyTemplate): HTMLElement
abstract createBooleanEditor(label: string, value: Term | null, required: boolean, template: ShaclPropertyTemplate): HTMLElement
abstract createFileEditor(label: string, value: Term | null, required: boolean, template: ShaclPropertyTemplate): HTMLElement
abstract createButton(label: string, primary: boolean): HTMLElement
}
export function fieldFactory(template: ShaclPropertyTemplate, value: Term | null): HTMLElement {
if (template.config.editMode) {
const required = template.minCount !== undefined && template.minCount > 0
// if we have a class, find the instances and display them in a list
if (template.class) {
return template.config.theme.createListEditor(template.label, value, required, findInstancesOf(template.class, template), template)
}
// check if it is a list
if (template.shaclIn) {
const list = template.config.lists[template.shaclIn]
if (list?.length) {
const listEntries = createInputListEntries(list, template.config.shapesGraph, template.config.languages)
return template.config.theme.createListEditor(template.label, value, required, listEntries, template)
}
else {
console.error('list not found:', template.shaclIn, 'existing lists:', template.config.lists)
}
}
// check if it is a langstring
if (template.datatype?.value === `${PREFIX_RDF}langString` || template.languageIn?.length) {
return template.config.theme.createLangStringEditor(template.label, value, required, template)
}
switch (template.datatype?.value.replace(PREFIX_XSD, '')) {
case 'integer':
case 'float':
case 'double':
case 'decimal':
return template.config.theme.createNumberEditor(template.label, value, required, template)
case 'date':
case 'dateTime':
return template.config.theme.createDateEditor(template.label, value, required, template)
case 'boolean':
return template.config.theme.createBooleanEditor(template.label, value, required, template)
case 'base64Binary':
return template.config.theme.createFileEditor(template.label, value, required, template)
}
// nothing found (or datatype is 'string'), fallback to 'text'
return template.config.theme.createTextEditor(template.label, value, required, template)
} else {
if (value) {
return template.config.theme.createViewer(template.label, value, template)
}
const fallback = document.createElement('div')
fallback.innerHTML = 'No value'
return fallback
}
}