Skip to content

Commit 242180a

Browse files
committed
simplify components properties
1 parent b076703 commit 242180a

4 files changed

Lines changed: 65 additions & 152 deletions

File tree

src/components/rdf-form/RDFForm.ts

Lines changed: 19 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ import { html } from 'lit/html.js'
33
import { consume } from '@lit/context'
44
import { customElement, WebComponent } from '@/lib/components'
55
import ns from '../../lib/ns'
6-
import { loadDocument, sortBySequence } from '../../lib/forms/rdfFormsHelper'
7-
import { sym, Namespace, LiveStore } from 'rdflib'
6+
import { fetchData, findForm, sortBySequence } from '../../lib/forms/rdfFormsHelper'
7+
import { sym, LiveStore } from 'rdflib'
88
import '@/components/rdf-input'
99
import { DEFAULT_STORE, storeContext, StoreContext } from '@/lib/forms/store/StoreContext'
1010

@@ -52,49 +52,29 @@ export default class RDFForm extends WebComponent {
5252
@state()
5353
private accessor _documentsLoaded = false
5454

55-
@property({ type: String })
56-
accessor whichForm = 'this'
57-
58-
@property({ type: String })
59-
accessor rdfTurtleFormatSource = ''
60-
61-
@property({ type: String })
62-
accessor rdfName = ''
63-
6455
@property({ converter: urlConverter })
65-
accessor rdfURI: URL | null = null
66-
67-
@property({ type: String })
68-
accessor whichSubject = 'me'
69-
70-
@property({ type: String })
71-
accessor subjectTurtleFormatSource = ''
72-
73-
@property({ type: String })
74-
accessor subjectName = ''
56+
accessor formUrl: URL | null = null
7557

7658
@property({ converter: urlConverter })
77-
accessor subjectURI: URL | null = null
59+
accessor subjectUrl: URL | null = null
7860

7961
render () {
80-
console.log('subjectURI ', this.subjectURI)
81-
console.log('rdfURI ', this.rdfURI)
8262
if (!this._documentsLoaded) {
8363
return html``
8464
}
8565

8666
const store = this.currentStoreContext.store
87-
const subjectURI = hrefFromUrlValue(this.subjectURI)
8867

89-
if (subjectURI && store.updater?.editable(subjectURI) === false) {
68+
const subjectUrl = hrefFromUrlValue(this.subjectUrl)
69+
if (subjectUrl && store.updater?.editable(subjectUrl) === false) {
9070
this.entireDataIsReadonly = true
9171
}
9272

93-
const document = sym(hrefFromUrlValue(this.rdfURI)) // rdflib NamedNode for the document
94-
const exactForm = this.whichForm // If there are more 'a ui:Form' elements in a form file
95-
const formThis = Namespace(`${hrefFromUrlValue(this.rdfURI)}#`)(exactForm) // NamedNode for #this in the form
73+
const formRoot = findForm(this.currentStoreContext.store, hrefFromUrlValue(this.formUrl)) // If there are more 'a ui:Form' elements in a form file
74+
if (!formRoot) throw new Error('No ui:Form found in ' + hrefFromUrlValue(this.formUrl))
9675

97-
const parts = store.each(formThis, ns.ui('parts'), null, document)
76+
const formDocument = sym(hrefFromUrlValue(this.formUrl)) // rdflib NamedNode for the document
77+
const parts = store.each(formRoot, ns.ui('parts'), null, formDocument)
9878
const partsBySequence = sortBySequence(store, parts)
9979
const partItems = (partsBySequence || []).flatMap(item => {
10080
if (item && typeof item === 'object' && 'elements' in item && Array.isArray((item as any).elements)) {
@@ -103,7 +83,7 @@ export default class RDFForm extends WebComponent {
10383
return [item]
10484
})
10585
const uiFields = partItems.map(item => {
106-
const types = store.each(item as any, ns.rdf('type'), null, document)
86+
const types = store.each(item as any, ns.rdf('type'), null, formDocument)
10787
const typeNode = types[0]
10888
const value = typeNode ? ((typeNode as any).value || String(typeNode)) : ((item as any).value || String(item))
10989
const hashIndex = value.lastIndexOf('#')
@@ -112,7 +92,6 @@ export default class RDFForm extends WebComponent {
11292
fieldValue: hashIndex >= 0 ? value.slice(hashIndex + 1) : value
11393
}
11494
})
115-
const me = Namespace(`${hrefFromUrlValue(this.subjectURI)}#`)(this.whichSubject)
11695

11796
return html`
11897
<form>
@@ -133,7 +112,7 @@ export default class RDFForm extends WebComponent {
133112
case 'NamedNodeURIField': {
134113
return html` <solid-ui-rdf-input
135114
.formSubject=${sym(part.value)}
136-
.dataSubject=${me}
115+
.dataSubject=${sym(subjectUrl)}
137116
.storeVersion=${this._loadVersion}
138117
.readonly=${this.entireDataIsReadonly}
139118
></solid-ui-rdf-input>
@@ -168,13 +147,8 @@ export default class RDFForm extends WebComponent {
168147

169148
protected updated (changedProperties: Map<PropertyKey, unknown>) {
170149
super.updated(changedProperties)
171-
if (
172-
changedProperties.has('rdfTurtleFormatSource') ||
173-
changedProperties.has('rdfName') ||
174-
changedProperties.has('rdfURI') ||
175-
changedProperties.has('subjectTurtleFormatSource') ||
176-
changedProperties.has('subjectName') ||
177-
changedProperties.has('subjectURI') ||
150+
if (changedProperties.has('formUrl') ||
151+
changedProperties.has('subjectUrl') ||
178152
changedProperties.has('passedInStore')
179153
) {
180154
this.loadDocumentsIfNeeded()
@@ -183,14 +157,14 @@ export default class RDFForm extends WebComponent {
183157

184158
private async loadDocumentsIfNeeded () {
185159
const store = this.currentStoreContext.store
186-
const rdfURI = hrefFromUrlValue(this.rdfURI)
187-
const subjectURI = hrefFromUrlValue(this.subjectURI)
160+
const formUrl = hrefFromUrlValue(this.formUrl)
161+
const subjectUrl = hrefFromUrlValue(this.subjectUrl)
188162

189-
if (!rdfURI || !subjectURI) return
163+
if (!formUrl || !subjectUrl) return
190164

191165
try {
192-
await loadDocument(store, this.rdfTurtleFormatSource, this.rdfName, rdfURI, false)
193-
await loadDocument(store, this.subjectTurtleFormatSource, this.subjectName, subjectURI, true)
166+
await fetchData(store, formUrl)
167+
await fetchData(store, subjectUrl)
194168
this._loadVersion += 1
195169
this._documentsLoaded = true
196170
} catch (error) {

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

Lines changed: 8 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -5,112 +5,21 @@ import './RDFForm'
55
const meta = {
66
title: 'Design System/RDF Form',
77
args: {
8-
rdfTurtleFormatSource: `
9-
@prefix : <https://solidos.solidcommunity.net/public/2021/solidUiFormTestData/dummyFormTestFile.ttl#>.
10-
@prefix foaf: <http://xmlns.com/foaf/0.1/>.
11-
@prefix sched: <http://www.w3.org/ns/pim/schedule#>.
12-
@prefix cal: <http://www.w3.org/2002/12/cal/ical#>.
13-
@prefix dc: <http://purl.org/dc/elements/1.1/>.
14-
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#>.
15-
@prefix ui: <http://www.w3.org/ns/ui#>.
16-
@prefix trip: <http://www.w3.org/ns/pim/trip#>.
17-
@prefix vcard: <http://www.w3.org/2006/vcard/ns#>.
18-
@prefix xsd: <http://www.w3.org/2001/XMLSchema#>.
19-
20-
# A Form with 3 fields and a nested subgroup
21-
22-
:form a ui:Form;
23-
ui:parts (:nameField :emailField :phoneField :addresses) .
24-
25-
:nameField a ui:SingleLineTextField ;
26-
ui:property vcard:fn;
27-
ui:label "name" .
28-
29-
30-
:emailField a ui:EmailField ;
31-
ui:property vcard:hasEmail; # @@ check
32-
ui:label "email" .
33-
34-
:phoneField a ui:PhoneField ;
35-
ui:property vcard:hasTelephone;
36-
ui:label "phone" .
37-
38-
:addresses
39-
a ui:Multiple ; # -- Allows zero or one or more
40-
ui:part :oneAddress ;
41-
ui:property vcard:hasAddress .
42-
43-
:oneAddress
44-
a ui:Group ; # A subgroup of the main form
45-
ui:parts ( :street :locality :postcode :region :country ).
46-
47-
:street
48-
a ui:SingleLineTextField ;
49-
ui:maxLength "128" ;
50-
ui:property vcard:street-address ;
51-
ui:size "40" .
52-
53-
:locality
54-
a ui:SingleLineTextField ;
55-
ui:maxLength "128" ;
56-
ui:property vcard:locality ;
57-
ui:size "40" .
58-
59-
:postcode
60-
a ui:SingleLineTextField ;
61-
ui:maxLength "25" ;
62-
ui:property vcard:postal-code ;
63-
ui:size "25" .
64-
65-
:region
66-
a ui:SingleLineTextField ;
67-
ui:maxLength "128" ;
68-
ui:property vcard:region ;
69-
ui:size "40" .
70-
71-
:country
72-
a ui:SingleLineTextField ;
73-
ui:maxLength "128" ;
74-
ui:property vcard:country-name ;
75-
ui:size "40" .
76-
`,
77-
rdfURI: 'https://solidos.solidcommunity.net/public/2021/solidUiFormTestData/dummyFormTestFile.ttl', // we need a working URL
78-
whichForm: 'form',
79-
rdfName: 'dummyFormTestFile.ttl',
80-
whichSubject: 'me',
81-
subjectTurtleFormatSource: `
82-
@prefix : <https://solidos.solidcommunity.net/public/2021/alice.ttl#>.
83-
@prefix vcard: <http://www.w3.org/2006/vcard/ns#>.
84-
85-
:me a vcard:Individual ;
86-
vcard:fn "Alice" ;
87-
vcard:hasEmail <mailto:alice@example.com> .
88-
`,
89-
subjectName: 'alice.ttl',
90-
subjectURI: 'https://solidos.solidcommunity.net/public/2021/alice.ttl'
8+
formUrl: 'https://solidos.solidcommunity.net/public/2021/solidUiFormTestData/dummyFormTestFile.ttl', // we need a working URL
9+
subjectUrl: 'https://solidos.solidcommunity.net/public/2021/alice.ttl#me'
9110
},
9211

9312
argTypes: {
94-
rdfTurtleFormatSource: { control: 'text' },
95-
rdfURI: { control: 'text' },
96-
whichForm: { control: 'text' },
97-
rdfName: { control: 'text' },
98-
subjectTurtleFormatSource: { control: 'text' },
99-
subjectName: { control: 'text' },
100-
subjectURI: { control: 'text' }
13+
formUrl: { control: 'text' },
14+
subjectUrl: { control: 'text' }
10115
},
10216
} as const
10317

104-
const render = defineStoryRender<typeof meta.argTypes>(({ rdfTurtleFormatSource, rdfURI, whichForm, rdfName, subjectTurtleFormatSource, subjectName, subjectURI }) => {
18+
const render = defineStoryRender<typeof meta.argTypes>(({ formUrl, subjectUrl }) => {
10519
return html`
106-
<solid-ui-rdf-form
107-
rdfTurtleFormatSource=${rdfTurtleFormatSource}
108-
.rdfURI=${new URL(rdfURI)}
109-
whichForm=${whichForm}
110-
rdfName=${rdfName}
111-
subjectTurtleFormatSource=${subjectTurtleFormatSource}
112-
subjectName=${subjectName}
113-
subjectURI=${subjectURI}>
20+
<solid-ui-rdf-form
21+
formUrl=${formUrl}
22+
.subjectUrl=${new URL(subjectUrl)}>
11423
</solid-ui-rdf-form>
11524
`
11625
})

src/components/rdf-input/RDFInput.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,12 @@ export default class RDFInput extends WebComponent {
3939
accessor readonly = false;
4040

4141
render () {
42-
const formGraph = this.getGraph(this.formSubject)
42+
const document = this.getDocument(this.formSubject)
4343

4444
// for building the HTML input element
45-
const uiPropertyTerm = this.getFormProperty(this.formSubject, ns.ui('property'), formGraph)
46-
const inputLabel = this.getInputLabel(this.formSubject, uiPropertyTerm, formGraph)
47-
const readonly = this.getReadOnly(this.readonly, this.formSubject, formGraph)
45+
const uiPropertyTerm = this.getFormProperty(this.formSubject, ns.ui('property'), document)
46+
const inputLabel = this.getInputLabel(this.formSubject, uiPropertyTerm, document)
47+
const readonly = this.getReadOnly(this.readonly, this.formSubject, document)
4848

4949
const fieldType = this.formSubject ? mostSpecificClassURI(this.storeContext.store, this.formSubject) : undefined
5050
const params = fieldType ? fieldTypeParams[fieldType] ?? {} : {}
@@ -67,7 +67,7 @@ export default class RDFInput extends WebComponent {
6767
></solid-ui-input>`
6868
}
6969

70-
private getGraph (subject?: NamedNode) {
70+
private getDocument (subject?: NamedNode) {
7171
return subject?.doc ? subject.doc() : undefined
7272
}
7373

@@ -139,7 +139,7 @@ export default class RDFInput extends WebComponent {
139139
this._pendingUpdateValue = null
140140
this._updateInFlight = true
141141

142-
const uiPropertyTerm = this.getFormProperty(this.formSubject, ns.ui('property'), this.getGraph(this.formSubject))
142+
const uiPropertyTerm = this.getFormProperty(this.formSubject, ns.ui('property'), this.getDocument(this.formSubject))
143143
if (!uiPropertyTerm || !this.dataSubject) {
144144
this._updateInFlight = false
145145
return
@@ -175,7 +175,7 @@ export default class RDFInput extends WebComponent {
175175
}
176176
toInsertSt = toDeleteSt.map(statement => st(statement.subject, statement.predicate, objectFromNewValue, statement.why))
177177
if (toInsertSt.length === 0) {
178-
toInsertSt = [st(this.formSubject, property as any, objectFromNewValue, this.getGraph(this.dataSubject))]
178+
toInsertSt = [st(this.formSubject, property as any, objectFromNewValue, this.getDocument(this.dataSubject))]
179179
}
180180
}
181181

src/lib/forms/rdfFormsHelper.ts

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { sym, LiveStore, parse } from 'rdflib'
22
import type { Term } from 'rdflib/lib/tf-types'
33
// eslint-disable-next-line camelcase
4-
import type { Quad_Subject } from 'rdflib/lib/tf-types'
4+
import type { Quad_Subject, NamedNode } from 'rdflib/lib/tf-types'
55
import ns from '../../lib/ns'
66

77
const baseUri = 'https://solidos.github.io/solid-ui/src/ontology/'
@@ -59,6 +59,22 @@ export function loadDocument (
5959
return Promise.reject(new Error(`No document source or URI for ${documentName}`))
6060
}
6161

62+
export async function fetchData (
63+
store: LiveStore,
64+
documentURI: string
65+
) {
66+
const document = sym(documentURI) // rdflib NamedNode for the document
67+
68+
if (store.holds(undefined, undefined, undefined, document)) {
69+
store.removeStatements(store.statementsMatching(undefined, undefined, undefined, document))
70+
}
71+
72+
return await store.fetcher.load(documentURI, {
73+
force: true,
74+
clearPreviousData: true,
75+
})
76+
}
77+
6278
export function sortBySequence (
6379
store: LiveStore,
6480
list: Term[]
@@ -91,3 +107,17 @@ export function mostSpecificClassURI (store: LiveStore, subject: Quad_Subject):
91107
// if (finalTypes.length > 1) throw "Didn't expect "+subject+" to have multiple bottom types: "+finalTypes
92108
return finalTypes[0]
93109
}
110+
111+
// Find the first ui:Form node in a store, optionally matching a fragment.
112+
// code based on Jeff Zucker's sol-components: https://github.com/jeff-zucker/sol-components (core/form-utils.js)
113+
export function findForm (store: LiveStore, sourceUri: string): NamedNode | null {
114+
const docUrl = sourceUri.split('#')[0]
115+
const fragment = sourceUri.includes('#') ? sourceUri.split('#')[1] : null
116+
if (fragment) {
117+
const candidate = sym(docUrl + '#' + fragment)
118+
if (store.holds(candidate, ns.rdf('type'), ns.ui('Form'))) return candidate
119+
}
120+
const forms = store.each(null, ns.rdf('type'), ns.ui('Form'))
121+
const found = forms.find((term) => term.termType === 'NamedNode')
122+
return found ? (found as NamedNode) : null
123+
}

0 commit comments

Comments
 (0)