UNPKG

contacts-pane

Version:

Contacts Pane: Contacts manager for Address Book, Groups, and Individuals.

181 lines (154 loc) 6.04 kB
/* Form field for doing autocompleete */ import { BlankNode, NamedNode, st, Variable } from 'rdflib' import { store } from 'solid-logic' import { ns, style, widgets } from 'solid-ui' import { renderAutoComplete } from './autocompletePicker' // dbpediaParameters const AUTOCOMPLETE_THRESHOLD = 4 // don't check until this many characters typed const AUTOCOMPLETE_ROWS = 12 // 20? /** * Render a autocomplete form field * * The same function is used for many similar one-value fields, with different * regexps used to validate. * * @param dom The HTML Document object aka Document Object Model * @param container If present, the created widget will be appended to this * @param already A hash table of (form, subject) kept to prevent recursive forms looping * @param subject The thing about which the form displays/edits data * @param form The form or field to be rendered * @param doc The web document in which the data is * @param callbackFunction Called when data is changed? * * @returns The HTML widget created */ // eslint-disable-next-line complexity export function autocompleteField ( // @@ are they allowed too be async?? dom: HTMLDocument, container: HTMLElement | undefined, already, subject: NamedNode | BlankNode | Variable, form: NamedNode, doc: NamedNode | undefined, callbackFunction: (ok: boolean, errorMessage: string) => void ): HTMLElement { async function addOneIdAndRefresh (result, _name) { const ds = kb.statementsMatching(subject, property as any) // remove any multiple values let is = ds.map(statement => st(statement.subject, statement.predicate, result, statement.why)) // can include >1 doc if (is.length === 0) { // or none is = [st(subject, property as any, result, doc)] } try { await kb.updater.updateMany(ds, is) } catch (err) { callbackFunction(false, err) box.appendChild(widgets.errorMessageBlock(dom, 'Autocomplete form data write error:' + err)) return } callbackFunction(true, '') } const kb = store const formDoc = form.doc ? form.doc() : null // @@ if blank no way to know const box = dom.createElement('tr') if (container) container.appendChild(box) const lhs = dom.createElement('td') lhs.setAttribute('class', 'formFieldName') lhs.setAttribute('style', ' vertical-align: middle;') box.appendChild(lhs) const rhs = dom.createElement('td') rhs.setAttribute('class', 'formFieldValue') box.appendChild(rhs) const property = kb.any(form, ns.ui('property')) if (!property) { box.appendChild( dom.createTextNode('Error: No property given for autocomplete field: ' + form) ) return box } const searchClass = kb.any(form, ns.ui('searchClass')) if (!searchClass) { box.appendChild( dom.createTextNode('Error: No searchClass given for autocomplete field: ' + form) ) return box } const endPoint = kb.any(form, ns.ui('endPoint')) if (!endPoint) { box.appendChild( dom.createTextNode('Error: No SPARQL endPoint given for autocomplete field: ' + form) ) return box } const queryTemplate = kb.any(form, ns.ui('queryTemplate')) if (!queryTemplate) { box.appendChild( dom.createTextNode('Error: No queryTemplate given for autocomplete field: ' + form) ) return box } // It can be cleaner to just remove empty fields if you can't edit them anyway const suppressEmptyUneditable = kb.anyJS(form, ns.ui('suppressEmptyUneditable'), null, formDoc) lhs.appendChild(widgets.fieldLabel(dom, property as any, form)) const uri = widgets.mostSpecificClassURI(form) let params = widgets.fieldParams[uri] if (params === undefined) params = {} // non-bottom field types can do this const theStyle = params.style || style.textInputStyle const klass = kb.the(form, ns.ui('category'), null, formDoc) /* { label: string; logo: string; searchByNameQuery?: string; searchByNameURI?: string; insitituteDetailsQuery?: string; endPoint?: string; class: object } */ queryParams.endPoint = endPoint.uri const searchByNameQuery = kb.the(form, ns.ui('searchByNameQuery'), null, formDoc) queryParams.searchByNameQuery = searchByNameQuery var queryParams = {label: 'from form', logo: '', class: klass, endPoint, searchByNameQuery} const options = { // cancelButton?: HTMLElement, // acceptButton?: HTMLElement, class: klass, queryParams } // const acWiget = rhs.appendChild(await renderAutoComplete(dom, options, addOneIdAndRefresh)) // @@ set existing value is any renderAutoComplete(dom, options, addOneIdAndRefresh).then(acWiget => rhs.appendChild(acWiget)) const field = dom.createElement('input') ;(field as any).style = style.textInputStyle // Do we have to override length etc? rhs.appendChild(field) field.setAttribute('type', params.type ? params.type : 'text') const size = kb.any(form, ns.ui('size')) // Form has precedence field.setAttribute( 'size', size ? '' + size : params.size ? '' + params.size : '20' ) const maxLength = kb.any(form, ns.ui('maxLength')) field.setAttribute('maxLength', maxLength ? '' + maxLength : '4096') doc = doc || widgets.fieldStore(subject, property as any, doc) let obj = kb.any(subject, property as any, undefined, doc) if (!obj) { obj = kb.any(form, ns.ui('default')) } if (obj) { /* istanbul ignore next */ field.value = obj.value || obj.value || '' } field.setAttribute('style', style) if (!kb.updater) { throw new Error('kb has no updater') } if (!kb.updater.editable((doc as NamedNode).uri)) { field.readOnly = true // was: disabled. readOnly is better ;(field as any).style = style.textInputStyleUneditable // backgroundColor = textInputBackgroundColorUneditable if (suppressEmptyUneditable && field.value === '') { box.style.display = 'none' // clutter } return box } return box } // ends