Skip to content

Commit c08aa27

Browse files
Merge pull request SolidOS#275 from solid/basicField
Basic field cleanup
2 parents 7da679b + e642e93 commit c08aa27

8 files changed

Lines changed: 1270 additions & 932 deletions

File tree

__mocks__/rdflib.ts

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
// @@ TODO: Remove currently untyped methods as they are added
22

3+
import { IndexedFormula } from 'rdflib'
4+
35
export {
46
BlankNode,
57
Collection,
@@ -69,16 +71,23 @@ export class Fetcher {
6971

7072
export class UpdateManager {
7173
// mock as needed
74+
updated: boolean = false
75+
reportSuccess: boolean = true
7276

73-
editable () {
74-
return Promise.resolve()
77+
editable (uri: string) {
78+
if (uri === 'http://not.editable/') {
79+
return false
80+
}
81+
return true
7582
}
7683

7784
put () {
7885
return Promise.resolve()
7986
}
8087

81-
update () {
88+
update (_deletes, _inserts, onDone: (uri: string, ok: boolean, body: string) => void) {
89+
this.updated = true
90+
onDone('uri', this.reportSuccess, 'body')
8291
return Promise.resolve()
8392
}
8493
}

examples/forms/index.html

Lines changed: 49 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -33,41 +33,6 @@ <h1>See also:</h1>
3333
<pre id="viewSource-templateTitle"></pre><script>showSource('templateTitle')</script>
3434
<div id="div-templateTitle"></div> -->
3535

36-
<h2 id="mostSpecificClassURI"><a href="#mostSpecificClassURI">mostSpecificClassURI</a></h2>
37-
<script id="script-mostSpecificClassURI">
38-
window.addEventListener('DOMContentLoaded', async (event) => {
39-
const fullNameField = UI.rdf.namedNode('https://timbl.com/timbl/Public/Test/Forms/individualForm.ttl#fullNameField')
40-
await UI.store.fetcher.load(fullNameField.doc())
41-
const result = UI.widgets.mostSpecificClassURI(fullNameField)
42-
document.getElementById('div-mostSpecificClassURI').appendChild(document.createTextNode(result))
43-
})
44-
</script>
45-
<pre id="viewSource-mostSpecificClassURI"></pre><script>showSource('mostSpecificClassURI')</script>
46-
<div id="div-mostSpecificClassURI"></div>
47-
48-
<h2 id="fieldFunction"><a href="#fieldFunction">fieldFunction</a></h2>
49-
<script id="script-fieldFunction">
50-
window.addEventListener('DOMContentLoaded', async (event) => {
51-
const dom = document
52-
const container = document.getElementById('div-fieldFunction')
53-
const already = {}
54-
const subject = UI.rdf.namedNode('https://michielbdejong.inrupt.net/profile/card#me')
55-
const form = UI.rdf.namedNode('https://timbl.com/timbl/Public/Test/Forms/individualForm.ttl#form1')
56-
const doc = subject.doc()
57-
const callbackFunction = () => {
58-
console.log('callback fieldFunction called!')
59-
}
60-
await UI.store.fetcher.load(subject.doc())
61-
const fullNameField = UI.rdf.namedNode('https://timbl.com/timbl/Public/Test/Forms/individualForm.ttl#fullNameField')
62-
await UI.store.fetcher.load(fullNameField.doc())
63-
const fieldFunction = UI.widgets.fieldFunction(dom, fullNameField)
64-
const result = fieldFunction(dom, container, already, subject, fullNameField, doc)
65-
document.getElementById('div-fieldFunction').appendChild(result)
66-
})
67-
</script>
68-
<pre id="viewSource-fieldFunction"></pre><script>showSource('fieldFunction')</script>
69-
<div id="div-fieldFunction"></div>
70-
7136
<h2 id="appendFormTrivial"><a href="#appendFormTrivial">appendForm, trivial / Comment Field</a></h2>
7237
<p>This example shows a trivial use of the `appendForm` function, in conjunction with a Comment field.</p>
7338
<script id="script-appendFormTrivial">
@@ -110,6 +75,55 @@ <h2 id="appendFormLive"><a href="#appendFormLive">appendForm, live</a></h2>
11075
<pre id="viewSource-appendFormLive"></pre><script>showSource('appendFormLive')</script>
11176
<div id="div-appendFormLive"></div>
11277

78+
<h2 id="basicField"><a href="#basicField">basic fields</a></h2>
79+
<script id="script-basicField">
80+
window.addEventListener('DOMContentLoaded', (event) => {
81+
const container = document.getElementById('div-basicField')
82+
const already = {}
83+
const subject = UI.rdf.namedNode('http://example.com/#this')
84+
const form = UI.rdf.namedNode('http://example.com/#form')
85+
const doc = UI.rdf.namedNode('http://example.com/')
86+
const callbackFunction = (ok, errorMessage) => {
87+
console.log(ok, errorMessage, document.getElementById('div-basicField').innerHTML);
88+
}
89+
UI.store.add(form, UI.ns.ui('property'), UI.rdf.namedNode('http://example.com/#some-property'), doc)
90+
91+
// FIXME: https://github.com/solid/solid-ui/issues/239
92+
document.outlineManager = {
93+
appendPropertyTRs: () => {}
94+
}
95+
;[
96+
'PhoneField',
97+
'EmailField',
98+
'ColorField',
99+
'DateField',
100+
'DateTimeField',
101+
'TimeField',
102+
'NumericField',
103+
'IntegerField',
104+
'DecimalField',
105+
'FloatField',
106+
'TextField',
107+
'SingleLineTextField',
108+
'NamedNodeURIField'
109+
].forEach((fieldName) => {
110+
UI.widgets.field[UI.ns.ui(fieldName).uri](
111+
document,
112+
container,
113+
already,
114+
subject,
115+
form,
116+
doc,
117+
callbackFunction
118+
);
119+
})
120+
});
121+
</script>
122+
<pre id="viewSource-basicField"></pre><script>showSource('basicField')</script>
123+
<div id="div-basicField"></div>
124+
125+
126+
113127
<h2 id="formField"><a href="#formField">UI.widgets.field[UI.ns.ui('Form').uri] / UI.widgets.field[UI.ns.ui('Group').uri], no already</a></h2>
114128
<script id="script-formField">
115129
window.addEventListener('DOMContentLoaded', (event) => {

src/widgets/forms.js

Lines changed: 1 addition & 177 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import { fieldParams } from './forms/fieldParams'
99
import { field, mostSpecificClassURI, fieldFunction } from './forms/fieldFunction'
10+
import { basicField } from './forms/basic'
1011

1112
module.exports = {}
1213

@@ -545,183 +546,6 @@ forms.field[ns.ui('Multiple').uri] = function (
545546

546547
forms.fieldParams = fieldParams
547548

548-
/** Render a basic form field
549-
*
550-
** @param {Document} dom The HTML Document object aka Document Object Model
551-
** @param {Element?} container If present, the created widget will be appended to this
552-
** @param {Map} already A hash table of (form, subject) kept to prevent recursive forms looping
553-
** @param {Node} subject The thing about which the form displays/edits data
554-
** @param {Node} form The form or field to be rendered
555-
** @param {Node} store The web document in which the data is
556-
** @param {function(ok, errorMessage)} callbackFunction Called when data is changed?
557-
**
558-
** @returns {Element} The HTML widget created
559-
**
560-
** The same function is used for many similar one-value fields, with different
561-
** regexps used to validate.
562-
*/
563-
function basicField (
564-
dom,
565-
container,
566-
already,
567-
subject,
568-
form,
569-
store,
570-
callbackFunction
571-
) {
572-
const ui = UI.ns.ui
573-
const kb = UI.store
574-
575-
var box = dom.createElement('tr')
576-
if (container) container.appendChild(box)
577-
var lhs = dom.createElement('td')
578-
lhs.setAttribute('class', 'formFieldName')
579-
lhs.setAttribute('style', ' vertical-align: middle;')
580-
box.appendChild(lhs)
581-
var rhs = dom.createElement('td')
582-
rhs.setAttribute('class', 'formFieldValue')
583-
box.appendChild(rhs)
584-
585-
var property = kb.any(form, ui('property'))
586-
if (!property) {
587-
box.appendChild(
588-
dom.createTextNode('Error: No property given for text field: ' + form)
589-
)
590-
return box
591-
}
592-
lhs.appendChild(forms.fieldLabel(dom, property, form))
593-
var uri = mostSpecificClassURI(form)
594-
var params = forms.fieldParams[uri]
595-
if (params === undefined) params = {} // non-bottom field types can do this
596-
var style = params.style || UI.style.textInputStyle || 'font-size: 100%; margin: 0.1em; padding: 0.1em;'
597-
// box.appendChild(dom.createTextNode(' uri='+uri+', pattern='+ params.pattern))
598-
var field = dom.createElement('input')
599-
field.style = UI.style.textInputStyle // Do we have to override length etc?
600-
rhs.appendChild(field)
601-
field.setAttribute('type', params.type ? params.type : 'text')
602-
603-
var size = kb.any(form, ui('size')) // Form has precedence
604-
field.setAttribute(
605-
'size',
606-
size ? '' + size : params.size ? '' + params.size : '20'
607-
)
608-
var maxLength = kb.any(form, ui('maxLength'))
609-
field.setAttribute('maxLength', maxLength ? '' + maxLength : '4096')
610-
611-
store = store || forms.fieldStore(subject, property, store)
612-
613-
var obj = kb.any(subject, property, undefined, store)
614-
if (!obj) {
615-
obj = kb.any(form, ui('default'))
616-
}
617-
if (obj && obj.uri && params.uriPrefix) {
618-
// eg tel: or mailto:
619-
field.value = decodeURIComponent(obj.uri.replace(params.uriPrefix, '')) // should have no spaces but in case
620-
.replace(/ /g, '')
621-
} else if (obj) {
622-
field.value = obj.value || obj.uri || ''
623-
}
624-
field.setAttribute('style', style)
625-
626-
if (!kb.updater.editable(store.uri)) {
627-
field.readonly = true // was: disabled. readonly is better
628-
return box
629-
}
630-
631-
// read-write:
632-
field.addEventListener(
633-
'keyup',
634-
function (_e) {
635-
if (params.pattern) {
636-
field.setAttribute(
637-
'style',
638-
style +
639-
(field.value.match(params.pattern)
640-
? 'color: green;'
641-
: 'color: red;')
642-
)
643-
}
644-
},
645-
true
646-
)
647-
field.addEventListener(
648-
'change',
649-
function (_e) {
650-
// i.e. lose focus with changed data
651-
if (params.pattern && !field.value.match(params.pattern)) return
652-
field.disabled = true // See if this stops getting two dates from fumbling e.g the chrome datepicker.
653-
field.setAttribute('style', style + 'color: gray;') // pending
654-
var ds = kb.statementsMatching(subject, property) // remove any multiple values
655-
var result
656-
if (params.namedNode) {
657-
result = kb.sym(field.value)
658-
} else if (params.uriPrefix) {
659-
result = encodeURIComponent(field.value.replace(/ /g, ''))
660-
result = kb.sym(params.uriPrefix + field.value)
661-
} else {
662-
if (params.dt) {
663-
result = new $rdf.Literal(
664-
field.value.trim(),
665-
undefined,
666-
ns.xsd(params.dt)
667-
)
668-
} else {
669-
result = new $rdf.Literal(field.value)
670-
}
671-
}
672-
var is = ds.map(st => $rdf.st(st.subject, st.predicate, result, st.why)) // can include >1 doc
673-
if (is.length === 0) {
674-
// or none
675-
is = [$rdf.st(subject, property, result, store)]
676-
}
677-
678-
function updateMany (ds, is, callback) {
679-
var docs = []
680-
is.forEach(st => {
681-
if (!docs.includes(st.why.uri)) docs.push(st.why.uri)
682-
})
683-
ds.forEach(st => {
684-
if (!docs.includes(st.why.uri)) docs.push(st.why.uri)
685-
})
686-
if (docs.length === 0) {
687-
throw new Error('updateMany has no docs to patch')
688-
}
689-
if (docs.length === 1) {
690-
return kb.updater.update(ds, is, callback)
691-
}
692-
// return kb.updater.update(ds, is, callback)
693-
694-
const doc = docs.pop()
695-
const is1 = is.filter(st => st.why.uri === doc)
696-
const is2 = is.filter(st => st.why.uri !== doc)
697-
const ds1 = ds.filter(st => st.why.uri === doc)
698-
const ds2 = ds.filter(st => st.why.uri !== doc)
699-
kb.updater.update(ds1, is1, function (uri, ok, body) {
700-
if (ok) {
701-
updateMany(ds2, is2, callback)
702-
} else {
703-
console.log('Update many failed on: ' + doc)
704-
callback(uri, ok, body)
705-
}
706-
})
707-
}
708-
709-
updateMany(ds, is, function (uri, ok, body) {
710-
// kb.updater.update(ds, is, function (uri, ok, body) {
711-
if (ok) {
712-
field.disabled = false
713-
field.setAttribute('style', style)
714-
} else {
715-
box.appendChild(error.errorMessageBlock(dom, body))
716-
}
717-
callbackFunction(ok, body)
718-
})
719-
},
720-
true
721-
)
722-
return box
723-
}
724-
725549
forms.field[ns.ui('PhoneField').uri] = basicField
726550
forms.field[ns.ui('EmailField').uri] = basicField
727551
forms.field[ns.ui('ColorField').uri] = basicField

0 commit comments

Comments
 (0)