-
Notifications
You must be signed in to change notification settings - Fork 46
Initial RDF forms component #798
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: staging
Are you sure you want to change the base?
Changes from all commits
0a6d16b
7c5a7c3
35c066f
a20fa4f
5cd82c0
344b904
9d5565a
0656653
84345df
372135f
638786e
235b0d0
da5c6fa
35f4445
0d99a1b
7b7f9a9
65735ae
cfb0475
c81b17e
082b9ee
2b53d6c
403a143
0af9ce9
9d9f48a
3da4992
643dc5a
d40ac8b
b076703
242180a
c410374
5fdb7bc
bfeed32
eba9a70
c9c7cf3
a0eaae5
d8604c6
6280cb4
71a305f
eb1fd4d
0429495
74ca18e
78cb2fc
4109abe
089892d
b7946d4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,7 @@ | ||
| node_modules | ||
| dist | ||
| src/versionInfo.ts | ||
| src/types/custom-elements.d.ts | ||
| .idea | ||
| .vscode | ||
| coverage | ||
|
|
||
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,171 @@ | ||
| import { property, state } from 'lit/decorators.js' | ||
| import { html } from 'lit/html.js' | ||
| import type { PropertyValues } from 'lit' | ||
| import { consume } from '@lit/context' | ||
| import { customElement, WebComponent } from '@/lib/components' | ||
| import ns from '../../lib/ns' | ||
| import { fetchData, findForm, sortBySequence } from '../../lib/forms/rdfFormsHelper' | ||
| import { sym, LiveStore } from 'rdflib' | ||
| import '@/components/rdf-input' | ||
| import { DEFAULT_STORE, storeContext, StoreContext } from '@/lib/forms/store/StoreContext' | ||
|
|
||
| const urlConverter = { | ||
| fromAttribute (value: string | null): URL | null { | ||
| if (!value) return null | ||
|
|
||
| try { | ||
| return new URL(value) | ||
| } catch { | ||
| return null | ||
| } | ||
| }, | ||
| toAttribute (value: URL | null) { | ||
| if (!value) return null | ||
| return value | ||
| } | ||
| } | ||
|
|
||
| const hrefFromUrlValue = (value: URL | null): string => | ||
| value?.href ?? '' | ||
|
|
||
| @customElement('solid-ui-rdf-form') | ||
| export default class RDFForm extends WebComponent { | ||
| @consume({ context: storeContext, subscribe: true }) | ||
| private accessor storeContext: StoreContext = DEFAULT_STORE | ||
|
|
||
| @property({ attribute: false }) | ||
|
timea-solid marked this conversation as resolved.
|
||
| accessor passedInStore: LiveStore | null = null | ||
|
|
||
| private get currentStore (): LiveStore { | ||
| return this.passedInStore ?? this.storeContext.store | ||
| } | ||
|
|
||
| @state() | ||
| private accessor entireDataIsReadonly: boolean = true // to protect data, we default to not editable | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This seems to be used only in the |
||
|
|
||
| @state() | ||
| private accessor _loadVersion = 0 | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure what this and the |
||
|
|
||
| @state() | ||
| private accessor _documentsLoaded = false | ||
|
|
||
| @property({ converter: urlConverter }) | ||
| accessor formUrl: URL | null = null | ||
|
|
||
| @property({ converter: urlConverter }) | ||
| accessor subjectUrl: URL | null = null | ||
|
|
||
| render () { | ||
| if (!this._documentsLoaded) { | ||
| return html`` | ||
| } | ||
|
|
||
| const store = this.currentStore | ||
|
|
||
| const subjectUrl = hrefFromUrlValue(this.subjectUrl) | ||
| if (subjectUrl && store.updater?.editable(subjectUrl) !== undefined && store.updater?.editable(subjectUrl) !== false) { | ||
| this.entireDataIsReadonly = false | ||
| } | ||
|
|
||
| const formRoot = findForm(this.currentStore, hrefFromUrlValue(this.formUrl)) // If there are more 'a ui:Form' elements in a form file | ||
| if (!formRoot) throw new Error('No ui:Form found in ' + hrefFromUrlValue(this.formUrl)) | ||
|
|
||
| const formDocument = sym(hrefFromUrlValue(this.formUrl)) // rdflib NamedNode for the document | ||
| const parts = store.each(formRoot, ns.ui('parts'), null, formDocument) | ||
| const partsBySequence = sortBySequence(store, parts) | ||
| const partItems = (partsBySequence || []).flatMap(item => { | ||
| if (item && typeof item === 'object' && 'elements' in item && Array.isArray((item as any).elements)) { | ||
| return (item as any).elements | ||
| } | ||
| return [item] | ||
| }) | ||
| const uiFields = partItems.map(item => { | ||
| const types = store.each(item as any, ns.rdf('type'), null, formDocument) | ||
| const typeNode = types[0] | ||
| const value = typeNode ? ((typeNode as any).value || String(typeNode)) : ((item as any).value || String(item)) | ||
| const hashIndex = value.lastIndexOf('#') | ||
| return { | ||
| value: item, | ||
| fieldValue: hashIndex >= 0 ? value.slice(hashIndex + 1) : value | ||
| } | ||
| }) | ||
|
|
||
| return html` | ||
|
timea-solid marked this conversation as resolved.
|
||
| <form> | ||
| ${uiFields.map(part => { | ||
| switch (part.fieldValue) { | ||
| case 'PhoneField': | ||
| case 'EmailField': | ||
| case 'ColorField': | ||
| case 'DateField': | ||
| case 'DateTimeField': | ||
| case 'TimeField': | ||
| case 'NumericField': | ||
| case 'IntegerField': | ||
| case 'DecimalField': | ||
| case 'FloatField': | ||
| case 'TextField': | ||
| case 'SingleLineTextField': | ||
| case 'NamedNodeURIField': { | ||
| return html` <solid-ui-rdf-input | ||
| .formSubject=${sym(part.value)} | ||
| .dataSubject=${sym(subjectUrl)} | ||
| .storeVersion=${this._loadVersion} | ||
| .readonly=${this.entireDataIsReadonly} | ||
|
Comment on lines
+110
to
+114
|
||
| ></solid-ui-rdf-input> | ||
| <br>` | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think this |
||
| } | ||
| case 'MultiLineTextField': | ||
| return html`<input .rdf=${part}></input>` | ||
| case 'BooleanField': | ||
| return html`<input .rdf=${part}></input>` | ||
| case 'TristateField': | ||
| return html`<input .rdf=${part}></input>` | ||
| case 'Classifier': | ||
| return html`<input .rdf=${part}></input>` | ||
| case 'Choice': | ||
| return html`<input .rdf=${part}></input>` | ||
| case 'Multiple': | ||
| return html`<input .rdf=${part}></input>` | ||
| case 'Options': | ||
| return html`<input .rdf=${part}></input>` | ||
| case 'AutocompleteField': | ||
| return html`<input .rdf=${part}></input>` | ||
| case 'Comment': | ||
| case 'Heading': | ||
| return html`<input .rdf=${part}></input>` | ||
| default: | ||
| return html`<div>Unknown part type: ${part}</div>` | ||
| } | ||
| })} | ||
| </form> | ||
| ` | ||
| } | ||
|
|
||
| protected override willUpdate (changedProperties: PropertyValues<this>) { | ||
| super.willUpdate(changedProperties) | ||
| if (changedProperties.has('formUrl') || | ||
| changedProperties.has('subjectUrl') || | ||
| changedProperties.has('passedInStore') | ||
| ) { | ||
| this.loadDocumentsIfNeeded() | ||
| } | ||
| } | ||
|
|
||
| private async loadDocumentsIfNeeded () { | ||
| const store = this.currentStore | ||
| const formUrl = hrefFromUrlValue(this.formUrl) | ||
| const subjectUrl = hrefFromUrlValue(this.subjectUrl) | ||
|
|
||
| if (!formUrl || !subjectUrl) return | ||
|
|
||
| try { | ||
| await fetchData(store, formUrl) | ||
| await fetchData(store, subjectUrl) | ||
| this._loadVersion += 1 | ||
| this._documentsLoaded = true | ||
| } catch (error) { | ||
| console.error('Failed to load RDF documents', error) | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| import { html } from 'lit' | ||
| import './RDFForm' | ||
|
timea-solid marked this conversation as resolved.
|
||
|
|
||
| const meta = { | ||
| title: 'Design System/RDF Form', | ||
| args: { | ||
| formUrl: 'https://solidos.solidcommunity.net/public/2021/solidUiFormTestData/dummyFormTestFile.ttl', // we need a working URL | ||
| subjectUrl: 'https://solidos.solidcommunity.net/public/2021/alice.ttl#me' | ||
| }, | ||
|
|
||
| argTypes: { | ||
| formUrl: { control: 'text' }, | ||
| subjectUrl: { control: 'text' } | ||
| }, | ||
| } as const | ||
|
|
||
| const render = ({ formUrl, subjectUrl }: typeof meta.args) => { | ||
| return html` | ||
| <solid-ui-rdf-form | ||
| formUrl=${formUrl} | ||
| .subjectUrl=${new URL(subjectUrl)}> | ||
| </solid-ui-rdf-form> | ||
| ` | ||
| } | ||
|
|
||
| export default meta | ||
|
|
||
| export const Primary = { render } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| import RDFForm from './RDFForm' | ||
|
|
||
| export { RDFForm } | ||
| export default RDFForm |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since this was added to the gitignore (which I like!), we should remove the file from the repository as well.