@texaryn/svelte 0.0.0-stage → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/LICENSE +190 -0
  3. package/README.md +128 -2
  4. package/dist/components/ArrayControl.svelte +210 -0
  5. package/dist/components/ArrayControl.svelte.d.ts +8 -0
  6. package/dist/components/ArrayControl.svelte.d.ts.map +1 -0
  7. package/dist/components/DocumentList.svelte +22 -0
  8. package/dist/components/DocumentList.svelte.d.ts +9 -0
  9. package/dist/components/DocumentList.svelte.d.ts.map +1 -0
  10. package/dist/components/DocumentNode.svelte +68 -0
  11. package/dist/components/DocumentNode.svelte.d.ts +14 -0
  12. package/dist/components/DocumentNode.svelte.d.ts.map +1 -0
  13. package/dist/components/DocumentRoot.svelte +33 -0
  14. package/dist/components/DocumentRoot.svelte.d.ts +13 -0
  15. package/dist/components/DocumentRoot.svelte.d.ts.map +1 -0
  16. package/dist/components/DocumentTable.svelte +35 -0
  17. package/dist/components/DocumentTable.svelte.d.ts +9 -0
  18. package/dist/components/DocumentTable.svelte.d.ts.map +1 -0
  19. package/dist/components/ErrorSummary.svelte +41 -0
  20. package/dist/components/ErrorSummary.svelte.d.ts +21 -0
  21. package/dist/components/ErrorSummary.svelte.d.ts.map +1 -0
  22. package/dist/components/FieldErrors.svelte +14 -0
  23. package/dist/components/FieldErrors.svelte.d.ts +23 -0
  24. package/dist/components/FieldErrors.svelte.d.ts.map +1 -0
  25. package/dist/components/FieldWidget.svelte +109 -0
  26. package/dist/components/FieldWidget.svelte.d.ts +22 -0
  27. package/dist/components/FieldWidget.svelte.d.ts.map +1 -0
  28. package/dist/components/FormRoot.svelte +96 -0
  29. package/dist/components/FormRoot.svelte.d.ts +17 -0
  30. package/dist/components/FormRoot.svelte.d.ts.map +1 -0
  31. package/dist/components/NodeRenderer.svelte +19 -0
  32. package/dist/components/NodeRenderer.svelte.d.ts +8 -0
  33. package/dist/components/NodeRenderer.svelte.d.ts.map +1 -0
  34. package/dist/components/ObjectLayout.svelte +72 -0
  35. package/dist/components/ObjectLayout.svelte.d.ts +8 -0
  36. package/dist/components/ObjectLayout.svelte.d.ts.map +1 -0
  37. package/dist/context.d.ts +13 -0
  38. package/dist/context.d.ts.map +1 -0
  39. package/dist/context.js +12 -0
  40. package/dist/field-props.d.ts +20 -0
  41. package/dist/field-props.d.ts.map +1 -0
  42. package/dist/field-props.js +31 -0
  43. package/dist/field.d.ts +34 -0
  44. package/dist/field.d.ts.map +1 -0
  45. package/dist/field.js +116 -0
  46. package/dist/form.d.ts +17 -0
  47. package/dist/form.d.ts.map +1 -0
  48. package/dist/form.js +20 -0
  49. package/dist/index.d.ts +21 -0
  50. package/dist/index.d.ts.map +1 -0
  51. package/dist/index.js +14 -0
  52. package/dist/registry.d.ts +4 -0
  53. package/dist/registry.d.ts.map +1 -0
  54. package/dist/registry.js +24 -0
  55. package/dist/store.d.ts +4 -0
  56. package/dist/store.d.ts.map +1 -0
  57. package/dist/store.js +7 -0
  58. package/dist/svelte.d.ts +5 -0
  59. package/dist/widget.d.ts +10 -0
  60. package/dist/widget.d.ts.map +1 -0
  61. package/dist/widget.js +1 -0
  62. package/package.json +68 -3
@@ -0,0 +1,68 @@
1
+ <script lang="ts">
2
+ import type { DocumentNode, DocumentRuntime, UIDocumentV2 } from '@texaryn/core'
3
+ import type { RendererRegistry } from '@texaryn/core'
4
+ import type { DocumentWidgetComponent } from '../widget.js'
5
+ import DocumentList from './DocumentList.svelte'
6
+ import DocumentTable from './DocumentTable.svelte'
7
+ import DocumentNodeView from './DocumentNode.svelte'
8
+
9
+ let {
10
+ nodeId,
11
+ runtime,
12
+ document,
13
+ registry,
14
+ onActionError,
15
+ }: {
16
+ nodeId: string
17
+ runtime: DocumentRuntime
18
+ document: UIDocumentV2
19
+ registry?: RendererRegistry<DocumentWidgetComponent, DocumentNode>
20
+ onActionError?: (error: unknown) => void
21
+ } = $props()
22
+
23
+ let node = $derived(document.nodes[nodeId])
24
+ let widget = $derived(node ? registry?.resolve(node) : undefined)
25
+
26
+ function invokeAction(actionNode: Extract<DocumentNode, { type: 'action' }>): void {
27
+ void runtime.invokeAction(actionNode.id).catch((error: unknown) => {
28
+ if (onActionError) onActionError(error)
29
+ else console.error(error)
30
+ })
31
+ }
32
+ </script>
33
+
34
+ {#if node}
35
+ {#if widget}
36
+ {@const Widget = widget}
37
+ <Widget {node} {runtime} />
38
+ {:else if node.type === 'container'}
39
+ {#if node.containerType === 'group'}
40
+ <fieldset>
41
+ <legend>{node.annotations.title}</legend>
42
+ {#each node.children as childId (childId)}
43
+ <DocumentNodeView nodeId={childId} {runtime} {document} {registry} {onActionError} />
44
+ {/each}
45
+ </fieldset>
46
+ {:else}
47
+ <div data-texaryn-layout="">
48
+ {#each node.children as childId (childId)}
49
+ <DocumentNodeView nodeId={childId} {runtime} {document} {registry} {onActionError} />
50
+ {/each}
51
+ </div>
52
+ {/if}
53
+ {:else if node.type === 'text'}
54
+ {#if node.textRole === 'heading'}
55
+ <h2>{node.content}</h2>
56
+ {:else if node.textRole === 'help'}
57
+ <p role="note">{node.content}</p>
58
+ {:else}
59
+ <p>{node.content}</p>
60
+ {/if}
61
+ {:else if node.type === 'list'}
62
+ <DocumentList {node} {runtime} />
63
+ {:else if node.type === 'table'}
64
+ <DocumentTable {node} {runtime} />
65
+ {:else}
66
+ <button type="button" disabled={!runtime.hasActionHandler(node.actionType)} onclick={() => invokeAction(node)}>{node.label}</button>
67
+ {/if}
68
+ {/if}
@@ -0,0 +1,14 @@
1
+ import type { DocumentNode, DocumentRuntime, UIDocumentV2 } from '@texaryn/core';
2
+ import type { RendererRegistry } from '@texaryn/core';
3
+ import type { DocumentWidgetComponent } from '../widget.js';
4
+ type $$ComponentProps = {
5
+ nodeId: string;
6
+ runtime: DocumentRuntime;
7
+ document: UIDocumentV2;
8
+ registry?: RendererRegistry<DocumentWidgetComponent, DocumentNode>;
9
+ onActionError?: (error: unknown) => void;
10
+ };
11
+ declare const DocumentNode: import("svelte").Component<$$ComponentProps, {}, "">;
12
+ type DocumentNode = ReturnType<typeof DocumentNode>;
13
+ export default DocumentNode;
14
+ //# sourceMappingURL=DocumentNode.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DocumentNode.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/components/DocumentNode.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAChF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA;AACrD,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAA;AAK1D,KAAK,gBAAgB,GAAI;IACtB,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,eAAe,CAAA;IACxB,QAAQ,EAAE,YAAY,CAAA;IACtB,QAAQ,CAAC,EAAE,gBAAgB,CAAC,uBAAuB,EAAE,YAAY,CAAC,CAAA;IAClE,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAA;CACzC,CAAC;AAkEJ,QAAA,MAAM,YAAY,sDAAwC,CAAC;AAC3D,KAAK,YAAY,GAAG,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC;AACpD,eAAe,YAAY,CAAC"}
@@ -0,0 +1,33 @@
1
+ <script lang="ts">
2
+ import type { DocumentNode, DocumentRuntime } from '@texaryn/core'
3
+ import type { RendererRegistry } from '@texaryn/core'
4
+ import type { DocumentWidgetComponent } from '../widget.js'
5
+ import DocumentNodeView from './DocumentNode.svelte'
6
+
7
+ interface Props {
8
+ runtime: DocumentRuntime
9
+ registry?: RendererRegistry<DocumentWidgetComponent, DocumentNode>
10
+ onActionError?: (error: unknown) => void
11
+ children?: import('svelte').Snippet
12
+ }
13
+
14
+ let { runtime, registry, onActionError, children }: Props = $props()
15
+ let document = $state<import('@texaryn/core').UIDocumentV2 | undefined>()
16
+ $effect(() => {
17
+ const store = runtime.document
18
+ document = store.getSnapshot()
19
+ return store.subscribe(() => { document = store.getSnapshot() })
20
+ })
21
+ </script>
22
+
23
+ {#if document?.nodes[document.rootId]}
24
+ <DocumentNodeView
25
+ nodeId={document.rootId}
26
+ {runtime}
27
+ {document}
28
+ {registry}
29
+ {onActionError}
30
+ />
31
+ {/if}
32
+
33
+ {@render children?.()}
@@ -0,0 +1,13 @@
1
+ import type { DocumentNode, DocumentRuntime } from '@texaryn/core';
2
+ import type { RendererRegistry } from '@texaryn/core';
3
+ import type { DocumentWidgetComponent } from '../widget.js';
4
+ interface Props {
5
+ runtime: DocumentRuntime;
6
+ registry?: RendererRegistry<DocumentWidgetComponent, DocumentNode>;
7
+ onActionError?: (error: unknown) => void;
8
+ children?: import('svelte').Snippet;
9
+ }
10
+ declare const DocumentRoot: import("svelte").Component<Props, {}, "">;
11
+ type DocumentRoot = ReturnType<typeof DocumentRoot>;
12
+ export default DocumentRoot;
13
+ //# sourceMappingURL=DocumentRoot.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DocumentRoot.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/components/DocumentRoot.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAClE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA;AACrD,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAA;AAIzD,UAAU,KAAK;IACb,OAAO,EAAE,eAAe,CAAA;IACxB,QAAQ,CAAC,EAAE,gBAAgB,CAAC,uBAAuB,EAAE,YAAY,CAAC,CAAA;IAClE,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAA;IACxC,QAAQ,CAAC,EAAE,OAAO,QAAQ,EAAE,OAAO,CAAA;CACpC;AAyBH,QAAA,MAAM,YAAY,2CAAwC,CAAC;AAC3D,KAAK,YAAY,GAAG,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC;AACpD,eAAe,YAAY,CAAC"}
@@ -0,0 +1,35 @@
1
+ <script lang="ts">
2
+ import type { DocumentCollectionRow, DocumentRuntime, TableNode } from '@texaryn/core'
3
+
4
+ let { node, runtime }: { node: TableNode; runtime: DocumentRuntime } = $props()
5
+ let rows = $state<readonly DocumentCollectionRow[]>([])
6
+
7
+ $effect(() => {
8
+ const store = runtime.getCollection(node.id)
9
+ if (!store) {
10
+ rows = []
11
+ return
12
+ }
13
+ rows = store.getSnapshot()
14
+ return store.subscribe(() => { rows = store.getSnapshot() })
15
+ })
16
+ </script>
17
+
18
+ <table>
19
+ <thead>
20
+ <tr>
21
+ {#each node.columns as column (column.id)}
22
+ <th scope="col">{column.label}</th>
23
+ {/each}
24
+ </tr>
25
+ </thead>
26
+ <tbody>
27
+ {#each rows as row (row.id)}
28
+ <tr>
29
+ {#each node.columns as column, index (column.id)}
30
+ <td>{row.cells[index] === null ? '' : String(row.cells[index])}</td>
31
+ {/each}
32
+ </tr>
33
+ {/each}
34
+ </tbody>
35
+ </table>
@@ -0,0 +1,9 @@
1
+ import type { DocumentRuntime, TableNode } from '@texaryn/core';
2
+ type $$ComponentProps = {
3
+ node: TableNode;
4
+ runtime: DocumentRuntime;
5
+ };
6
+ declare const DocumentTable: import("svelte").Component<$$ComponentProps, {}, "">;
7
+ type DocumentTable = ReturnType<typeof DocumentTable>;
8
+ export default DocumentTable;
9
+ //# sourceMappingURL=DocumentTable.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DocumentTable.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/components/DocumentTable.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAyB,eAAe,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAEtF,KAAK,gBAAgB,GAAI;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,OAAO,EAAE,eAAe,CAAA;CAAE,CAAC;AAuCxE,QAAA,MAAM,aAAa,sDAAwC,CAAC;AAC5D,KAAK,aAAa,GAAG,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC;AACtD,eAAe,aAAa,CAAC"}
@@ -0,0 +1,41 @@
1
+ <script lang="ts">
2
+ import { tick } from 'svelte'
3
+ import { createFailedSubmitTracker, visibleErrorLabel, visibleErrorMessages } from '@texaryn/core'
4
+ import type { VisibleError } from '@texaryn/core'
5
+ import { useFormContext } from '../context.js'
6
+ import { makeId } from '../field-props.js'
7
+
8
+ export let focus = true
9
+
10
+ const context = useFormContext()
11
+ const visibleErrors = context.form.visibleErrors
12
+ const submission = context.form.submission
13
+ const messages = context.messages
14
+ const tracker = createFailedSubmitTracker(context.form.runtime.submission.getSnapshot())
15
+ const headingId = makeId(context.idPrefix, 'error-summary', 'heading')
16
+ let container: HTMLDivElement | undefined
17
+
18
+ $: if (tracker.settle($submission, $visibleErrors)) {
19
+ void tick().then(() => {
20
+ if (focus && container?.isConnected) container.focus()
21
+ })
22
+ }
23
+
24
+ function details(entry: VisibleError): string {
25
+ return $messages.errorSummaryDetail({ messages: visibleErrorMessages(entry, $messages) })
26
+ }
27
+ </script>
28
+
29
+ {#if $visibleErrors.length > 0}
30
+ <div bind:this={container} role="group" tabindex="-1" aria-labelledby={headingId}>
31
+ <h2 id={headingId}>{$messages.errorSummaryHeading({ count: $visibleErrors.length })}</h2>
32
+ <ul>
33
+ {#each $visibleErrors as entry (entry.nodeId)}
34
+ <li>
35
+ <a href={`#${makeId(context.idPrefix, entry.nodeId, 'input')}`}>{visibleErrorLabel(entry, $messages)}</a>
36
+ {details(entry)}
37
+ </li>
38
+ {/each}
39
+ </ul>
40
+ </div>
41
+ {/if}
@@ -0,0 +1,21 @@
1
+ interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
2
+ new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
3
+ $$bindings?: Bindings;
4
+ } & Exports;
5
+ (internal: unknown, props: Props & {
6
+ $$events?: Events;
7
+ $$slots?: Slots;
8
+ }): Exports & {
9
+ $set?: any;
10
+ $on?: any;
11
+ };
12
+ z_$$bindings?: Bindings;
13
+ }
14
+ declare const ErrorSummary: $$__sveltets_2_IsomorphicComponent<{
15
+ focus?: boolean;
16
+ }, {
17
+ [evt: string]: CustomEvent<any>;
18
+ }, {}, {}, string>;
19
+ type ErrorSummary = InstanceType<typeof ErrorSummary>;
20
+ export default ErrorSummary;
21
+ //# sourceMappingURL=ErrorSummary.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ErrorSummary.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/components/ErrorSummary.svelte.ts"],"names":[],"mappings":"AAqDA,UAAU,kCAAkC,CAAC,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,EAAE,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,EAAE,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,EAAE,OAAO,GAAG,EAAE,EAAE,QAAQ,GAAG,MAAM;IACpM,KAAK,OAAO,EAAE,OAAO,QAAQ,EAAE,2BAA2B,CAAC,KAAK,CAAC,GAAG,OAAO,QAAQ,EAAE,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,GAAG;QAAE,UAAU,CAAC,EAAE,QAAQ,CAAA;KAAE,GAAG,OAAO,CAAC;IACjK,CAAC,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,GAAG;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,KAAK,CAAA;KAAC,GAAG,OAAO,GAAG;QAAE,IAAI,CAAC,EAAE,GAAG,CAAC;QAAC,GAAG,CAAC,EAAE,GAAG,CAAA;KAAE,CAAC;IAC9G,YAAY,CAAC,EAAE,QAAQ,CAAC;CAC3B;AAKD,QAAA,MAAM,YAAY;;;;kBAA+E,CAAC;AAChF,KAAK,YAAY,GAAG,YAAY,CAAC,OAAO,YAAY,CAAC,CAAC;AACxD,eAAe,YAAY,CAAC"}
@@ -0,0 +1,14 @@
1
+ <script lang="ts">
2
+ import type { ValidationError } from '@texaryn/core'
3
+ import { useFormContext } from '../context.js'
4
+
5
+ export let id: string
6
+ export let errors: readonly ValidationError[] = []
7
+ const { messages } = useFormContext()
8
+ </script>
9
+
10
+ <div {id} aria-live="polite" aria-atomic="true">
11
+ {#each errors as error, index (`${error.instancePointer}:${error.keyword}:${index}`)}
12
+ <div>{$messages.validationMessage(error)}</div>
13
+ {/each}
14
+ </div>
@@ -0,0 +1,23 @@
1
+ import type { ValidationError } from '@texaryn/core';
2
+ interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
3
+ new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
4
+ $$bindings?: Bindings;
5
+ } & Exports;
6
+ (internal: unknown, props: Props & {
7
+ $$events?: Events;
8
+ $$slots?: Slots;
9
+ }): Exports & {
10
+ $set?: any;
11
+ $on?: any;
12
+ };
13
+ z_$$bindings?: Bindings;
14
+ }
15
+ declare const FieldErrors: $$__sveltets_2_IsomorphicComponent<{
16
+ id: string;
17
+ errors?: readonly ValidationError[];
18
+ }, {
19
+ [evt: string]: CustomEvent<any>;
20
+ }, {}, {}, string>;
21
+ type FieldErrors = InstanceType<typeof FieldErrors>;
22
+ export default FieldErrors;
23
+ //# sourceMappingURL=FieldErrors.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FieldErrors.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/components/FieldErrors.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAoBpD,UAAU,kCAAkC,CAAC,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,EAAE,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,EAAE,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,EAAE,OAAO,GAAG,EAAE,EAAE,QAAQ,GAAG,MAAM;IACpM,KAAK,OAAO,EAAE,OAAO,QAAQ,EAAE,2BAA2B,CAAC,KAAK,CAAC,GAAG,OAAO,QAAQ,EAAE,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,GAAG;QAAE,UAAU,CAAC,EAAE,QAAQ,CAAA;KAAE,GAAG,OAAO,CAAC;IACjK,CAAC,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,GAAG;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,KAAK,CAAA;KAAC,GAAG,OAAO,GAAG;QAAE,IAAI,CAAC,EAAE,GAAG,CAAC;QAAC,GAAG,CAAC,EAAE,GAAG,CAAA;KAAE,CAAC;IAC9G,YAAY,CAAC,EAAE,QAAQ,CAAC;CAC3B;AAKD,QAAA,MAAM,WAAW;QAViC,MAAM;aAAW,SAAS,eAAe,EAAE;;;kBAUG,CAAC;AAC/E,KAAK,WAAW,GAAG,YAAY,CAAC,OAAO,WAAW,CAAC,CAAC;AACtD,eAAe,WAAW,CAAC"}
@@ -0,0 +1,109 @@
1
+ <script lang="ts">
2
+ import { writable } from 'svelte/store'
3
+ import type { FieldNode, UINode } from '@texaryn/core'
4
+ import { useFormContext } from '../context.js'
5
+ import { createFieldBinding, sameEnumValue } from '../field.js'
6
+ import FieldErrors from './FieldErrors.svelte'
7
+
8
+ export let node: UINode
9
+
10
+ const context = useFormContext()
11
+ const nodeId = writable(node.id)
12
+ $: nodeId.set(node.id)
13
+
14
+ const field = createFieldBinding(context.form, nodeId, context.idPrefix, context.messages)
15
+ const {
16
+ node: fieldNode,
17
+ value,
18
+ visibleErrors,
19
+ display,
20
+ label,
21
+ description,
22
+ labelFor,
23
+ descriptionId,
24
+ errorId,
25
+ aria,
26
+ messages,
27
+ } = field
28
+
29
+ $: enumOptions = ($fieldNode?.enumValues ?? []).map((entry, index) => ({
30
+ token: String(index),
31
+ value: entry.value,
32
+ title: entry.title ?? String(entry.value),
33
+ }))
34
+ $: selectedToken = enumOptions.find((option) => sameEnumValue(option.value, $value))?.token ?? ''
35
+
36
+ function handleInput(event: Event): void {
37
+ field.setRaw((event.currentTarget as HTMLInputElement | HTMLTextAreaElement).value)
38
+ }
39
+
40
+ function handleSelect(event: Event): void {
41
+ const select = event.currentTarget as HTMLSelectElement
42
+ if ($fieldNode?.readOnly) {
43
+ select.value = selectedToken
44
+ return
45
+ }
46
+ field.setRaw(select.value, select.value)
47
+ }
48
+
49
+ function handleCheckbox(event: Event): void {
50
+ const checkbox = event.currentTarget as HTMLInputElement
51
+ if ($fieldNode?.readOnly) {
52
+ checkbox.checked = Boolean($value)
53
+ return
54
+ }
55
+ field.setValue(checkbox.checked)
56
+ }
57
+
58
+ function handleBlur(): void {
59
+ field.onBlur()
60
+ }
61
+ </script>
62
+
63
+ {#if $fieldNode && $aria}
64
+ <div>
65
+ <label for={$labelFor}>
66
+ {#if $aria['aria-required'] && $messages.requiredIndicator().placement === 'before'}
67
+ {@const indicator = $messages.requiredIndicator()}
68
+ <span aria-hidden="true">{indicator.text} </span>
69
+ {/if}
70
+ {$label}
71
+ {#if $aria['aria-required'] && $messages.requiredIndicator().placement === 'after'}
72
+ {@const indicator = $messages.requiredIndicator()}
73
+ <span aria-hidden="true"> {indicator.text}</span>
74
+ {/if}
75
+ </label>
76
+
77
+ {#if $fieldNode.enumValues != null && $fieldNode.enumValues.length > 0}
78
+ <select {...$aria} value={selectedToken} on:change={handleSelect} on:blur={handleBlur}>
79
+ <option value=""></option>
80
+ {#each enumOptions as option (option.token)}
81
+ <option value={option.token}>{option.title}</option>
82
+ {/each}
83
+ </select>
84
+ {:else if $fieldNode.fieldType === 'boolean'}
85
+ <input
86
+ {...$aria}
87
+ type="checkbox"
88
+ checked={Boolean($value)}
89
+ on:change={handleCheckbox}
90
+ on:blur={handleBlur}
91
+ />
92
+ {:else if $fieldNode.widget === 'textarea'}
93
+ <textarea {...$aria} value={$display} on:input={handleInput} on:blur={handleBlur}></textarea>
94
+ {:else}
95
+ <input
96
+ {...$aria}
97
+ type={$fieldNode.fieldType === 'number' || $fieldNode.fieldType === 'integer' ? 'number' : 'text'}
98
+ value={$display}
99
+ on:input={handleInput}
100
+ on:blur={handleBlur}
101
+ />
102
+ {/if}
103
+
104
+ {#if $description}
105
+ <div id={$descriptionId}>{$description}</div>
106
+ {/if}
107
+ <FieldErrors id={$errorId} errors={$visibleErrors} />
108
+ </div>
109
+ {/if}
@@ -0,0 +1,22 @@
1
+ import type { UINode } from '@texaryn/core';
2
+ interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
3
+ new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
4
+ $$bindings?: Bindings;
5
+ } & Exports;
6
+ (internal: unknown, props: Props & {
7
+ $$events?: Events;
8
+ $$slots?: Slots;
9
+ }): Exports & {
10
+ $set?: any;
11
+ $on?: any;
12
+ };
13
+ z_$$bindings?: Bindings;
14
+ }
15
+ declare const FieldWidget: $$__sveltets_2_IsomorphicComponent<{
16
+ node: UINode;
17
+ }, {
18
+ [evt: string]: CustomEvent<any>;
19
+ }, {}, {}, string>;
20
+ type FieldWidget = InstanceType<typeof FieldWidget>;
21
+ export default FieldWidget;
22
+ //# sourceMappingURL=FieldWidget.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FieldWidget.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/components/FieldWidget.svelte.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAa,MAAM,EAAE,MAAM,eAAe,CAAA;AAyGtD,UAAU,kCAAkC,CAAC,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,EAAE,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,EAAE,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,EAAE,OAAO,GAAG,EAAE,EAAE,QAAQ,GAAG,MAAM;IACpM,KAAK,OAAO,EAAE,OAAO,QAAQ,EAAE,2BAA2B,CAAC,KAAK,CAAC,GAAG,OAAO,QAAQ,EAAE,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,GAAG;QAAE,UAAU,CAAC,EAAE,QAAQ,CAAA;KAAE,GAAG,OAAO,CAAC;IACjK,CAAC,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,GAAG;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,KAAK,CAAA;KAAC,GAAG,OAAO,GAAG;QAAE,IAAI,CAAC,EAAE,GAAG,CAAC;QAAC,GAAG,CAAC,EAAE,GAAG,CAAA;KAAE,CAAC;IAC9G,YAAY,CAAC,EAAE,QAAQ,CAAC;CAC3B;AAKD,QAAA,MAAM,WAAW;UAVsB,MAAM;;;kBAUmD,CAAC;AAC/E,KAAK,WAAW,GAAG,YAAY,CAAC,OAAO,WAAW,CAAC,CAAC;AACtD,eAAe,WAAW,CAAC"}
@@ -0,0 +1,96 @@
1
+ <script lang="ts">
2
+ import { onDestroy } from 'svelte'
3
+ import type { Snippet } from 'svelte'
4
+ import { englishMessages } from '@texaryn/core'
5
+ import type { FormMessages, RendererRegistry, UINode } from '@texaryn/core'
6
+ import { derived, get, writable } from 'svelte/store'
7
+ import type { SvelteFormBinding } from '../form.js'
8
+ import type { WidgetComponent } from '../widget.js'
9
+ import { provideFormContext } from '../context.js'
10
+ import { createDefaultRegistry } from '../registry.js'
11
+ import ErrorSummary from './ErrorSummary.svelte'
12
+ import NodeRenderer from './NodeRenderer.svelte'
13
+
14
+ interface Props {
15
+ form: SvelteFormBinding
16
+ registry?: RendererRegistry<WidgetComponent>
17
+ messages?: FormMessages
18
+ idPrefix?: string
19
+ destroyOnUnmount?: boolean
20
+ showErrorSummary?: boolean
21
+ children?: Snippet
22
+ }
23
+
24
+ let {
25
+ form,
26
+ registry = createDefaultRegistry(),
27
+ messages = englishMessages,
28
+ idPrefix,
29
+ destroyOnUnmount = true,
30
+ showErrorSummary = false,
31
+ children,
32
+ }: Props = $props()
33
+
34
+ function capture<T>(read: () => T): T {
35
+ return read()
36
+ }
37
+
38
+ const formStore = writable(capture(() => form))
39
+ const currentForm: SvelteFormBinding = {
40
+ get runtime() {
41
+ return get(formStore).runtime
42
+ },
43
+ document: derived(formStore, ($form, set) => $form.document.subscribe(set)),
44
+ data: derived(formStore, ($form, set) => $form.data.subscribe(set)),
45
+ submission: derived(formStore, ($form, set) => $form.submission.subscribe(set)),
46
+ visibleErrors: derived(formStore, ($form, set) => $form.visibleErrors.subscribe(set)),
47
+ dispatch: (command) => get(formStore).dispatch(command),
48
+ getNodeState: (nodeId) => get(formStore).getNodeState(nodeId),
49
+ }
50
+ const registryStore = writable(capture(() => registry))
51
+ const messagesStore = writable(capture(() => messages))
52
+ const generatedId = $props.id()
53
+ const generatedPrefix = `texaryn-${Array.from(generatedId, (char) => char.codePointAt(0)!.toString(36)).join('_')}`
54
+ const resolvedIdPrefix = capture(() => idPrefix ?? generatedPrefix)
55
+ const runtime = derived(formStore, ($form) => $form.runtime)
56
+ const context = {
57
+ form: currentForm,
58
+ registry: registryStore,
59
+ messages: messagesStore,
60
+ idPrefix: resolvedIdPrefix,
61
+ }
62
+ provideFormContext(context)
63
+
64
+ const document = currentForm.document
65
+ const rootNode = $derived($document.nodes[$document.rootId] as UINode | undefined)
66
+
67
+ let previousForm = capture(() => form)
68
+ $effect(() => {
69
+ registryStore.set(registry)
70
+ messagesStore.set(messages)
71
+ const nextForm = form
72
+ if (nextForm === previousForm) return
73
+
74
+ const replacedForm = previousForm
75
+ previousForm = nextForm
76
+ formStore.set(nextForm)
77
+ if (destroyOnUnmount && replacedForm.runtime !== nextForm.runtime) {
78
+ replacedForm.runtime.destroy()
79
+ }
80
+ })
81
+
82
+ onDestroy(() => {
83
+ if (destroyOnUnmount) get(formStore).runtime.destroy()
84
+ })
85
+
86
+ </script>
87
+
88
+ {#if showErrorSummary}
89
+ {#key $runtime}
90
+ <ErrorSummary />
91
+ {/key}
92
+ {/if}
93
+ {#if rootNode}
94
+ <NodeRenderer node={rootNode} />
95
+ {/if}
96
+ {@render children?.()}
@@ -0,0 +1,17 @@
1
+ import type { Snippet } from 'svelte';
2
+ import type { FormMessages, RendererRegistry } from '@texaryn/core';
3
+ import type { SvelteFormBinding } from '../form.js';
4
+ import type { WidgetComponent } from '../widget.js';
5
+ interface Props {
6
+ form: SvelteFormBinding;
7
+ registry?: RendererRegistry<WidgetComponent>;
8
+ messages?: FormMessages;
9
+ idPrefix?: string;
10
+ destroyOnUnmount?: boolean;
11
+ showErrorSummary?: boolean;
12
+ children?: Snippet;
13
+ }
14
+ declare const FormRoot: import("svelte").Component<Props, {}, "">;
15
+ type FormRoot = ReturnType<typeof FormRoot>;
16
+ export default FormRoot;
17
+ //# sourceMappingURL=FormRoot.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FormRoot.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/components/FormRoot.svelte.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAA;AAErC,OAAO,KAAK,EAAE,YAAY,EAAE,gBAAgB,EAAU,MAAM,eAAe,CAAA;AAE3E,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AACnD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA;AAOjD,UAAU,KAAK;IACb,IAAI,EAAE,iBAAiB,CAAA;IACvB,QAAQ,CAAC,EAAE,gBAAgB,CAAC,eAAe,CAAC,CAAA;IAC5C,QAAQ,CAAC,EAAE,YAAY,CAAA;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,gBAAgB,CAAC,EAAE,OAAO,CAAA;IAC1B,gBAAgB,CAAC,EAAE,OAAO,CAAA;IAC1B,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB;AA2FH,QAAA,MAAM,QAAQ,2CAAwC,CAAC;AACvD,KAAK,QAAQ,GAAG,UAAU,CAAC,OAAO,QAAQ,CAAC,CAAC;AAC5C,eAAe,QAAQ,CAAC"}
@@ -0,0 +1,19 @@
1
+ <script lang="ts">
2
+ import type { UINode } from '@texaryn/core'
3
+ import { useFormContext } from '../context.js'
4
+ import type { WidgetComponent } from '../widget.js'
5
+
6
+ interface Props {
7
+ node: UINode
8
+ }
9
+
10
+ let { node }: Props = $props()
11
+ const context = useFormContext()
12
+ const registry = context.registry
13
+ const widget = $derived($registry.resolve(node) as WidgetComponent | undefined)
14
+ </script>
15
+
16
+ {#if node.visible && widget}
17
+ {@const Widget = widget}
18
+ <Widget {node} />
19
+ {/if}
@@ -0,0 +1,8 @@
1
+ import type { UINode } from '@texaryn/core';
2
+ interface Props {
3
+ node: UINode;
4
+ }
5
+ declare const NodeRenderer: import("svelte").Component<Props, {}, "">;
6
+ type NodeRenderer = ReturnType<typeof NodeRenderer>;
7
+ export default NodeRenderer;
8
+ //# sourceMappingURL=NodeRenderer.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NodeRenderer.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/components/NodeRenderer.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAA;AAKzC,UAAU,KAAK;IACb,IAAI,EAAE,MAAM,CAAA;CACb;AAoBH,QAAA,MAAM,YAAY,2CAAwC,CAAC;AAC3D,KAAK,YAAY,GAAG,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC;AACpD,eAAe,YAAY,CAAC"}
@@ -0,0 +1,72 @@
1
+ <script lang="ts">
2
+ import { objectChildKey } from '@texaryn/core'
3
+ import type { ContainerNode, UINode } from '@texaryn/core'
4
+ import { englishMessages } from '@texaryn/core'
5
+ import { useFormContext } from '../context.js'
6
+ import NodeRenderer from './NodeRenderer.svelte'
7
+
8
+ interface Props {
9
+ node: UINode
10
+ }
11
+
12
+ let { node }: Props = $props()
13
+ const context = useFormContext()
14
+ const document = context.form.document
15
+ const messages = context.messages
16
+ const current = $derived(($document.nodes[node.id] ?? node) as ContainerNode)
17
+ const nested = $derived(node.parentId !== null)
18
+ const title = $derived(current.annotations.title)
19
+ const children = $derived(
20
+ current.children
21
+ .map((id) => $document.nodes[id])
22
+ .filter((child): child is UINode => child != null),
23
+ )
24
+ const actions = $derived(firstTargets(current.boundaryTargets ?? []))
25
+
26
+ function firstTargets(targets: NonNullable<ContainerNode['boundaryTargets']>) {
27
+ const byReason = new Map<string, (typeof targets)[number]>()
28
+ for (const target of targets) {
29
+ if (!byReason.has(target.reason)) byReason.set(target.reason, target)
30
+ }
31
+ return [...byReason.values()]
32
+ }
33
+
34
+ function expand(target: (typeof actions)[number]): void {
35
+ context.form.dispatch({
36
+ type: 'ExpandBoundary',
37
+ containerId: current.id,
38
+ targetToken: target.token,
39
+ })
40
+ }
41
+
42
+ function expandMessage(target: (typeof actions)[number]) {
43
+ const makeMessage = $messages.expandBoundary ?? englishMessages.expandBoundary!
44
+ return makeMessage({
45
+ boundary: target.reason,
46
+ containerTitle: title,
47
+ position: 1,
48
+ count: 1,
49
+ })
50
+ }
51
+ </script>
52
+
53
+ {#snippet renderedChildren()}
54
+ {#each children as child (objectChildKey(child))}
55
+ <NodeRenderer node={child} />
56
+ {/each}
57
+ {#each actions as target (target.reason)}
58
+ {@const message = expandMessage(target)}
59
+ <button type="button" aria-label={message.accessibleName} onclick={() => expand(target)}>
60
+ {message.label}
61
+ </button>
62
+ {/each}
63
+ {/snippet}
64
+
65
+ {#if nested}
66
+ <fieldset role={title === undefined ? 'none' : undefined}>
67
+ {#if title !== undefined}<legend>{title}</legend>{/if}
68
+ <div>{@render renderedChildren()}</div>
69
+ </fieldset>
70
+ {:else}
71
+ <div>{@render renderedChildren()}</div>
72
+ {/if}