Skip to content

Commit c81b17e

Browse files
committed
added save features, copied over code
1 parent cfb0475 commit c81b17e

3 files changed

Lines changed: 130 additions & 15 deletions

File tree

src/components/rdf-form/RDFForm.ts

Lines changed: 38 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,23 @@ export default class RDFForm extends WebComponent {
1616
@property({ attribute: false })
1717
accessor passedInStore: LiveStore | null = null
1818

19+
private get currentStoreContext (): StoreContext | null {
20+
if (this.passedInStore) {
21+
return { store: this.passedInStore }
22+
}
23+
24+
return this.storeContext !== DEFAULT_STORE ? this.storeContext : null
25+
}
26+
27+
@state()
28+
private accessor failed: boolean = false
29+
30+
@state()
31+
private accessor submitting: boolean = false
32+
33+
@state()
34+
private accessor entireDataIsReadonly: boolean = true
35+
1936
@state()
2037
private accessor _parsedUrl: URL | null = null
2138

@@ -67,16 +84,19 @@ export default class RDFForm extends WebComponent {
6784
}
6885

6986
render () {
70-
const currentStoreContext = this.passedInStore
71-
? { store: this.passedInStore }
72-
: this.storeContext
87+
const currentStoreContext = this.currentStoreContext
7388

74-
if (!currentStoreContext?.store) {
89+
if (!currentStoreContext) {
7590
console.warn('RDFForm: store context not available yet')
7691
return html``
7792
}
7893

7994
const store = currentStoreContext.store
95+
96+
if (!store.updater?.editable(this.subjectURI)) {
97+
this.entireDataIsReadonly = true
98+
}
99+
80100
// TODO: detect format
81101
loadDocument(store, this.rdfTurtleFormatSource, this.rdfName, this.rdfURI) // load form
82102
loadDocument(store, this.subjectTurtleFormatSource, this.subjectName, this.subjectURI) // load data
@@ -124,6 +144,7 @@ export default class RDFForm extends WebComponent {
124144
return html` <solid-ui-rdf-input
125145
.formSubject=${sym(part.value)}
126146
.dataSubject=${me}
147+
readonly=${this.entireDataIsReadonly}
127148
></solid-ui-rdf-input>
128149
<br>`
129150
}
@@ -150,25 +171,35 @@ export default class RDFForm extends WebComponent {
150171
return html`<div>Unknown part type: ${part}</div>`
151172
}
152173
})}
174+
<solid-ui-button
175+
?disabled=${!store.updater?.editable(this.subjectURI) || this.submitting}
176+
?loading=${this.submitting}
177+
type="submit"
178+
>
179+
Save
180+
</solid-ui-button>
153181
</form>
154182
`
155183
}
156184

157185
private async onSubmit (e: Event) {
158186
e.preventDefault()
159187

160-
/* this.failed = false
188+
this.failed = false
161189

162190
this.submitting = true
163191

164192
try {
165-
await this.auth.login(this.issuerInputValue)
193+
const currentStoreContext = this.currentStoreContext
194+
if (currentStoreContext?.store.updater?.editable(this.subjectURI)) {
195+
// this.saveStatements()
196+
}
166197
} catch (error) {
167198
console.error(error)
168199

169200
this.failed = true
170201
} finally {
171202
this.submitting = false
172-
} */
203+
}
173204
}
174205
}

src/components/rdf-form/RDForm.stories.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ const meta = {
1717
@prefix vcard: <http://www.w3.org/2006/vcard/ns#>.
1818
@prefix xsd: <http://www.w3.org/2001/XMLSchema#>.
1919
20-
# A Form with 2 fields and a nested subgroup
20+
# A Form with 3 fields and a nested subgroup
2121
2222
:form a ui:Form;
2323
ui:parts (:nameField :emailField :phoneField :addresses) .

src/components/rdf-input/RDFInput.ts

Lines changed: 91 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import { property } from 'lit/decorators.js'
22
import { html } from 'lit/html.js'
33
import ns from '../../lib/ns'
4-
import { customElement, generateId, WebComponent } from '@/lib/components'
5-
import { NamedNode } from 'rdflib'
4+
import { customElement, WebComponent } from '@/lib/components'
5+
import { Literal, LiveStore, NamedNode, Statement, st } from 'rdflib'
66
import { label } from '../../utils'
77
import { mostSpecificClassURI } from '../../lib/forms/rdfFormsHelper'
8-
import { fieldParams as fieldTypeParams, InputType } from '../../lib/forms/fieldParams'
8+
import { FieldParamsObject, fieldParams as fieldTypeParams, InputType } from '../../lib/forms/fieldParams'
99
import { DEFAULT_STORE, storeContext, StoreContext } from '@/lib/forms/store/StoreContext'
1010
import { consume } from '@lit/context'
1111
import '@/components/input'
@@ -33,9 +33,7 @@ export default class RDFInput extends WebComponent {
3333
accessor readonly = true;
3434

3535
render () {
36-
const formGraph = this.getFormGraph(this.formSubject)
37-
const statementCount = this.storeContext.store?.statements?.length ?? 0
38-
console.log('RDFInput render statement count:', statementCount)
36+
const formGraph = this.getGraph(this.formSubject)
3937

4038
// for building the HTML input element
4139
const uiPropertyTerm = this.getFormProperty(this.formSubject, ns.ui('property'), formGraph)
@@ -59,10 +57,11 @@ export default class RDFInput extends WebComponent {
5957
placeholder="${placeholder}"
6058
type="${inputType}"
6159
?readonly=${readonly}
60+
@input=${this.updateData()}
6261
></solid-ui-input>`
6362
}
6463

65-
private getFormGraph (subject?: NamedNode) {
64+
private getGraph (subject?: NamedNode) {
6665
return subject?.doc ? subject.doc() : undefined
6766
}
6867

@@ -113,4 +112,89 @@ export default class RDFInput extends WebComponent {
113112
const stripped = params.defaultInputValue ?? ''
114113
return stripped.replace(/ /g, '')
115114
}
115+
116+
private updateData () {
117+
return (e: CustomEvent) => {
118+
const newValue = (e.target as HTMLInputElement).value
119+
120+
const uiPropertyTerm = this.getFormProperty(this.formSubject, ns.ui('property'), this.getGraph(this.formSubject))
121+
if (!uiPropertyTerm || !this.dataSubject) return
122+
123+
const currentStoreContext = this.storeContext.store
124+
if (!currentStoreContext.updater.editable(this.dataSubject)) return
125+
126+
const toDeleteSt = currentStoreContext.statementsMatching(this.dataSubject, uiPropertyTerm)
127+
128+
if (newValue) {
129+
let objectFromNewValue
130+
const fieldType = this.formSubject ? mostSpecificClassURI(this.storeContext.store, this.formSubject) : undefined
131+
const params: FieldParamsObject = fieldType ? fieldTypeParams[fieldType] ?? {} : {}
132+
if (params.namedNode) {
133+
objectFromNewValue = currentStoreContext.sym(newValue)
134+
} else if (params.defaultInputValue) {
135+
objectFromNewValue = encodeURIComponent(newValue.replace(/ /g, ''))
136+
objectFromNewValue = currentStoreContext.sym(params.defaultInputValue + objectFromNewValue)
137+
} else {
138+
if (params.dt) {
139+
objectFromNewValue = new Literal(
140+
newValue.trim(),
141+
undefined,
142+
ns.xsd(params.dt)
143+
)
144+
} else {
145+
objectFromNewValue = new Literal(newValue)
146+
}
147+
}
148+
let toInsertSt = toDeleteSt.map(statement => st(statement.subject, statement.predicate, objectFromNewValue, statement.why)) // can include >1 doc
149+
if (toInsertSt.length === 0) {
150+
toInsertSt = [st(this.formSubject, property as any, objectFromNewValue, this.getGraph(this.dataSubject))]
151+
}
152+
153+
this.updateMany(currentStoreContext, toDeleteSt, toInsertSt)
154+
}
155+
}
156+
}
157+
158+
private updateMany (
159+
store: LiveStore,
160+
ds: Statement[],
161+
is: Statement[]
162+
) {
163+
const getDocUri = (statement: Statement) => {
164+
const why = statement.why as any
165+
return why?.uri ?? why?.value
166+
}
167+
168+
const docs: string[] = []
169+
is.forEach(st => {
170+
const uri = getDocUri(st)
171+
if (uri && !docs.includes(uri)) docs.push(uri)
172+
})
173+
ds.forEach(st => {
174+
const uri = getDocUri(st)
175+
if (uri && !docs.includes(uri)) docs.push(uri)
176+
})
177+
if (docs.length === 0) {
178+
throw new Error('No concrete document to update')
179+
}
180+
if (!store.updater) {
181+
throw new Error('Store has no updater')
182+
}
183+
if (docs.length === 1) {
184+
return store.updater.update(ds, is as any)
185+
}
186+
187+
const doc = docs.pop()
188+
const is1 = is.filter(st => getDocUri(st) === doc)
189+
const is2 = is.filter(st => getDocUri(st) !== doc)
190+
const ds1 = ds.filter(st => getDocUri(st) === doc)
191+
const ds2 = ds.filter(st => getDocUri(st) !== doc)
192+
store.updater.update(ds1, is1 as any, (uri, ok, body) => {
193+
if (ok) {
194+
this.updateMany(store, ds2, is2)
195+
} else {
196+
throw new Error(`Failed to update data for ${uri}: ${body}`)
197+
}
198+
})
199+
}
116200
}

0 commit comments

Comments
 (0)