A little headless CMS that stores models and content in your PDS
sites.wisp.place/wilb.me/pds-cms/
3.8 kB
102 lines
1<script lang="ts">
2 import { Field } from '@formisch/svelte'
3 import type { Field as ModelField } from '@pds-cms/core'
4
5 let {
6 form,
7 field,
8 path,
9 compact = false,
10 }: {
11 form: unknown
12 field: ModelField
13 path: (string | number)[]
14 compact?: boolean
15 } = $props()
16
17 const label = $derived((field.label ?? field.name) + (field.required ? ' *' : ''))
18 const uid = $props.id()
19</script>
20
21{#snippet fieldErrors(errors: [string, ...string[]] | null)}
22 {#if errors}<p class="error">{errors[0]}</p>{/if}
23{/snippet}
24
25{#if field.type === 'boolean'}
26 <div class="field" class:subfield={compact}>
27 <label class="inline">
28 <Field of={form as never} path={path as never}>
29 {#snippet children(f: never)}
30 <input {...(f as any).props} checked={(f as any).input === true} type="checkbox" />
31 {/snippet}
32 </Field>
33 {label}
34 </label>
35 </div>
36{:else if field.type === 'link'}
37 <div class="field" class:subfield={compact}>
38 {#if !compact}<span class="label">{label}</span>{/if}
39 <div class="subfield">
40 <Field of={form as never} path={[...path, 'uri'] as never}>
41 {#snippet children(f: never)}
42 <input {...(f as any).props} value={(f as any).input ?? ''} type="url" placeholder="https://…" />
43 {@render fieldErrors((f as any).errors)}
44 {/snippet}
45 </Field>
46 </div>
47 <div class="subfield">
48 <Field of={form as never} path={[...path, 'title'] as never}>
49 {#snippet children(f: never)}
50 <input {...(f as any).props} value={(f as any).input ?? ''} type="text" placeholder="Link text (optional)" />
51 {/snippet}
52 </Field>
53 </div>
54 </div>
55{:else if field.type === 'image'}
56 <div class="field" class:subfield={compact}>
57 {#if !compact}<span class="label">{label}</span>{/if}
58 <div class="subfield">
59 <Field of={form as never} path={[...path, 'file'] as never}>
60 {#snippet children(f: never)}
61 <input {...(f as any).props} type="file" accept="image/*" />
62 {#if (f as any).input instanceof File}
63 <p class="muted">{(f as any).input.name} ({Math.round((f as any).input.size / 1024)} kB)</p>
64 {/if}
65 {@render fieldErrors((f as any).errors)}
66 {/snippet}
67 </Field>
68 </div>
69 <div class="subfield">
70 <Field of={form as never} path={[...path, 'alt'] as never}>
71 {#snippet children(f: never)}
72 <input {...(f as any).props} value={(f as any).input ?? ''} type="text" placeholder="Alt text" />
73 {/snippet}
74 </Field>
75 </div>
76 </div>
77{:else}
78 <div class="field" class:subfield={compact}>
79 {#if !compact}<label for={uid}>{label}</label>{/if}
80 <Field of={form as never} path={path as never}>
81 {#snippet children(f: never)}
82 {#if field.type === 'longtext'}
83 <textarea {...(f as any).props} id={uid} value={(f as any).input ?? ''}></textarea>
84 {:else if field.type === 'select'}
85 <select {...(f as any).props} id={uid} value={(f as any).input ?? ''}>
86 {#if !field.required}<option value="">—</option>{/if}
87 {#each field.type === 'select' ? field.options : [] as option (option)}
88 <option value={option}>{option}</option>
89 {/each}
90 </select>
91 {:else if field.type === 'number'}
92 <input {...(f as any).props} id={uid} value={(f as any).input ?? ''} type="text" inputmode="numeric" />
93 {:else if field.type === 'date'}
94 <input {...(f as any).props} id={uid} value={(f as any).input ?? ''} type="date" />
95 {:else}
96 <input {...(f as any).props} id={uid} value={(f as any).input ?? ''} type="text" />
97 {/if}
98 {@render fieldErrors((f as any).errors)}
99 {/snippet}
100 </Field>
101 </div>
102{/if}