|
1 | 1 | <script lang="ts"> |
2 | | - import { SvelteMap } from 'svelte/reactivity'; |
| 2 | + import { SvelteMap } from "svelte/reactivity"; |
3 | 3 | import Ajv from "ajv"; |
4 | | - import { ON_BLUR, ON_CHANGE, ON_INPUT, AFTER_CHANGED, AFTER_SUBMITTED, AFTER_TOUCHED, useForm2, SimpleForm } from "@sjsf/form"; |
5 | | - import { translation, handleValidationProcessError } from "@sjsf/form/translations/en"; |
| 4 | + import { |
| 5 | + ON_BLUR, |
| 6 | + ON_CHANGE, |
| 7 | + ON_INPUT, |
| 8 | + AFTER_CHANGED, |
| 9 | + AFTER_SUBMITTED, |
| 10 | + AFTER_TOUCHED, |
| 11 | + useForm2, |
| 12 | + SimpleForm, |
| 13 | + ON_ARRAY_CHANGE, |
| 14 | + ON_OBJECT_CHANGE, |
| 15 | + } from "@sjsf/form"; |
| 16 | + import { |
| 17 | + translation, |
| 18 | + handleValidationProcessError, |
| 19 | + } from "@sjsf/form/translations/en"; |
6 | 20 | import { addFormComponents, DEFAULT_AJV_CONFIG } from "@sjsf/ajv8-validator"; |
7 | | - import { focusOnFirstError } from '@sjsf/form/focus-on-first-error'; |
8 | | - import { setThemeContext } from '@sjsf/shadcn-theme' |
9 | | - import { components } from '@sjsf/shadcn-theme/default' |
| 21 | + import { focusOnFirstError } from "@sjsf/form/focus-on-first-error"; |
| 22 | + import { setThemeContext } from "@sjsf/shadcn-theme"; |
| 23 | + import { components } from "@sjsf/shadcn-theme/default"; |
10 | 24 |
|
11 | | - import { themes, themeStyles } from './themes' |
12 | | - import { icons, iconsStyles } from './icons' |
| 25 | + import { themes, themeStyles } from "./themes"; |
| 26 | + import { icons, iconsStyles } from "./icons"; |
13 | 27 | import { ShadowHost } from "./shadow"; |
14 | 28 | import Github from "./github.svelte"; |
15 | 29 | import OpenBook from "./open-book.svelte"; |
16 | 30 | import ThemePicker from "./theme-picker.svelte"; |
17 | | - import Editor from './editor.svelte'; |
18 | | - import Debug from './debug.svelte'; |
| 31 | + import Editor from "./editor.svelte"; |
| 32 | + import Debug from "./debug.svelte"; |
19 | 33 |
|
20 | 34 | import { samples } from "./samples"; |
21 | | - import { validators } from './validators'; |
| 35 | + import { validators } from "./validators"; |
| 36 | + import Bits from "./bits.svelte"; |
22 | 37 |
|
23 | 38 | function isSampleName(name: unknown): name is keyof typeof samples { |
24 | 39 | return typeof name === "string" && name in samples; |
|
88 | 103 | : selectValidator("ajv8", true); |
89 | 104 | let validatorName = $state(initialValidatorName); |
90 | 105 | const validator = $derived.by(() => { |
91 | | - const Val = samples[sampleName].Validator |
| 106 | + const Val = samples[sampleName].Validator; |
92 | 107 | if (Val) { |
93 | | - return new Val( |
94 | | - addFormComponents(new Ajv(DEFAULT_AJV_CONFIG)), |
95 | | - uiSchema |
96 | | - ) |
| 108 | + return new Val(addFormComponents(new Ajv(DEFAULT_AJV_CONFIG)), uiSchema); |
97 | 109 | } |
98 | | - return validators[validatorName]() |
| 110 | + return validators[validatorName](); |
99 | 111 | }); |
100 | 112 |
|
101 | 113 | let disabled = $state(false); |
|
105 | 117 | const form = useForm2({ |
106 | 118 | handleValidationProcessError: (state) => { |
107 | 119 | console.error(state); |
108 | | - return handleValidationProcessError(state) |
| 120 | + return handleValidationProcessError(state); |
109 | 121 | }, |
110 | 122 | initialValue: samples[initialSampleName].formData, |
111 | 123 | initialErrors: samples[initialSampleName].errors ?? new SvelteMap(), |
|
129 | 141 | return disabled; |
130 | 142 | }, |
131 | 143 | get inputsValidationMode() { |
132 | | - return validationEvent | validationAfter |
| 144 | + return validationEvent | validationAfter | ON_ARRAY_CHANGE; |
133 | 145 | }, |
134 | 146 | get icons() { |
135 | 147 | return iconSet; |
136 | 148 | }, |
137 | | - onSubmit (value) { |
| 149 | + onSubmit(value) { |
138 | 150 | console.log("submit", value); |
139 | 151 | }, |
140 | | - onSubmitError (errors, e) { |
| 152 | + onSubmitError(errors, e) { |
141 | 153 | if (doFocusOnFirstError) { |
142 | 154 | focusOnFirstError(errors, e); |
143 | 155 | } |
144 | 156 | console.log("errors", errors); |
145 | 157 | }, |
146 | | - }) |
| 158 | + }); |
147 | 159 |
|
148 | 160 | let playgroundTheme = $state<"system" | "light" | "dark">( |
149 | 161 | localStorage.theme ?? "system" |
150 | 162 | ); |
151 | 163 |
|
152 | | - const lightOrDark = $derived(playgroundTheme === "system" ? window.matchMedia("(prefers-color-scheme: dark)") ? "dark" : "light" : playgroundTheme) |
153 | | -
|
| 164 | + const lightOrDark = $derived( |
| 165 | + playgroundTheme === "system" |
| 166 | + ? window.matchMedia("(prefers-color-scheme: dark)") |
| 167 | + ? "dark" |
| 168 | + : "light" |
| 169 | + : playgroundTheme |
| 170 | + ); |
154 | 171 |
|
155 | | - function setValidation(name: "vevent" | "vafter", value: number, replace = false) { |
| 172 | + function setValidation( |
| 173 | + name: "vevent" | "vafter", |
| 174 | + value: number, |
| 175 | + replace = false |
| 176 | + ) { |
156 | 177 | url.searchParams.set(name, value.toString()); |
157 | 178 | history[replace ? "replaceState" : "pushState"](null, "", url); |
158 | 179 | return value; |
159 | 180 | } |
160 | | - const urlValidationEvent = Number(url.searchParams.get("vevent") ?? 0) |
161 | | - const initialValidationEvent = urlValidationEvent > 0 && urlValidationEvent < 8 ? urlValidationEvent : 0 |
162 | | - let validationEvent = $state(setValidation("vevent", initialValidationEvent, true)); |
163 | | - const urlValidationAfter = Number(url.searchParams.get("vafter") ?? 0) |
164 | | - const initialValidationAfter = [0, AFTER_SUBMITTED, AFTER_TOUCHED, AFTER_CHANGED].find((v) => v === urlValidationAfter) ?? 0 |
165 | | - let validationAfter = $state(setValidation("vafter", initialValidationAfter, true)); |
| 181 | + const urlValidationEvent = Number(url.searchParams.get("vevent") ?? 0); |
| 182 | + const initialValidationEvent = |
| 183 | + urlValidationEvent > 0 && urlValidationEvent < 8 ? urlValidationEvent : 0; |
| 184 | + let validationEvent = $state( |
| 185 | + setValidation("vevent", initialValidationEvent, true) |
| 186 | + ); |
| 187 | + $effect(() => { |
| 188 | + setValidation("vevent", validationEvent) |
| 189 | + }) |
| 190 | + const urlValidationAfter = Number(url.searchParams.get("vafter") ?? 0); |
| 191 | + const initialValidationAfter = |
| 192 | + [0, AFTER_SUBMITTED, AFTER_TOUCHED, AFTER_CHANGED].find( |
| 193 | + (v) => v === urlValidationAfter |
| 194 | + ) ?? 0; |
| 195 | + let validationAfter = $state( |
| 196 | + setValidation("vafter", initialValidationAfter, true) |
| 197 | + ); |
| 198 | + $effect(() => { |
| 199 | + setValidation("vafter", validationAfter); |
| 200 | + }) |
166 | 201 |
|
167 | 202 | setThemeContext({ components }); |
168 | 203 | </script> |
|
184 | 219 | <input type="checkbox" bind:checked={doFocusOnFirstError} /> |
185 | 220 | Focus on first error |
186 | 221 | </label> |
187 | | - <select bind:value={validationEvent} onchange={() => setValidation("vevent", validationEvent)}> |
188 | | - <option value={0}>None</option> |
189 | | - <option value={ON_INPUT}>On Input</option> |
190 | | - <option value={ON_CHANGE}>On Change</option> |
191 | | - <option value={ON_BLUR}>On Blur</option> |
192 | | - <option value={ON_INPUT | ON_BLUR}>Input & Blur</option> |
193 | | - <option value={ON_INPUT | ON_CHANGE}>Input & Change</option> |
194 | | - <option value={ON_BLUR | ON_CHANGE}>Blur & Change</option> |
195 | | - <option value={ON_INPUT | ON_BLUR | ON_CHANGE}>All</option> |
196 | | - </select> |
197 | | - <select bind:value={validationAfter} onchange={() => setValidation("vafter", validationAfter)}> |
198 | | - <option value={0}>Always</option> |
199 | | - <option value={AFTER_CHANGED}>After Changed</option> |
200 | | - <option value={AFTER_TOUCHED}>After Touched</option> |
201 | | - <option value={AFTER_SUBMITTED}>After Submitted</option> |
202 | | - </select> |
203 | | - <select bind:value={validatorName} onchange={() => selectValidator(validatorName)}> |
| 222 | + <Bits |
| 223 | + title="Validation Events" |
| 224 | + bind:value={validationEvent} |
| 225 | + flags={[ |
| 226 | + [ON_INPUT, "On Input"], |
| 227 | + [ON_CHANGE, "On Change"], |
| 228 | + [ON_BLUR, "On Blur"], |
| 229 | + [ON_ARRAY_CHANGE, "Array Changed"], |
| 230 | + [ON_OBJECT_CHANGE, "Object Changed"], |
| 231 | + ]} |
| 232 | + /> |
| 233 | + <Bits |
| 234 | + title="Validation Modifiers" |
| 235 | + bind:value={validationAfter} |
| 236 | + flags={[ |
| 237 | + [AFTER_CHANGED, "After Changed"], |
| 238 | + [AFTER_TOUCHED, "After Touched"], |
| 239 | + [AFTER_SUBMITTED, "After Submitted"], |
| 240 | + ]} |
| 241 | + /> |
| 242 | + <select |
| 243 | + bind:value={validatorName} |
| 244 | + onchange={() => selectValidator(validatorName)} |
| 245 | + > |
204 | 246 | {#each Object.keys(validators) as name (name)} |
205 | 247 | <option value={name}>{name}</option> |
206 | 248 | {/each} |
|
210 | 252 | <option value={name}>{name}</option> |
211 | 253 | {/each} |
212 | 254 | </select> |
213 | | - <select bind:value={iconSetName} onchange={() => selectIconSet(iconSetName)}> |
| 255 | + <select |
| 256 | + bind:value={iconSetName} |
| 257 | + onchange={() => selectIconSet(iconSetName)} |
| 258 | + > |
214 | 259 | {#each Object.keys(icons) as name (name)} |
215 | 260 | <option value={name}>{name}</option> |
216 | 261 | {/each} |
|
249 | 294 | </div> |
250 | 295 | <div class="flex gap-8"> |
251 | 296 | <div class="flex-[4] flex flex-col gap-2"> |
252 | | - <Editor class="font-mono h-[400px] border rounded p-2 data-[error=true]:border-red-500 data-[error=true]:outline-none bg-transparent" bind:value={schema} /> |
| 297 | + <Editor |
| 298 | + class="font-mono h-[400px] border rounded p-2 data-[error=true]:border-red-500 data-[error=true]:outline-none bg-transparent" |
| 299 | + bind:value={schema} |
| 300 | + /> |
253 | 301 | <div class="flex gap-2"> |
254 | | - <Editor class="font-mono h-[400px] grow border rounded p-2 data-[error=true]:border-red-500 data-[error=true]:outline-none bg-transparent" bind:value={uiSchema} /> |
255 | | - <Editor class="font-mono h-[400px] grow border rounded p-2 data-[error=true]:border-red-500 data-[error=true]:outline-none bg-transparent" bind:value={form.value} /> |
| 302 | + <Editor |
| 303 | + class="font-mono h-[400px] grow border rounded p-2 data-[error=true]:border-red-500 data-[error=true]:outline-none bg-transparent" |
| 304 | + bind:value={uiSchema} |
| 305 | + /> |
| 306 | + <Editor |
| 307 | + class="font-mono h-[400px] grow border rounded p-2 data-[error=true]:border-red-500 data-[error=true]:outline-none bg-transparent" |
| 308 | + bind:value={form.value} |
| 309 | + /> |
256 | 310 | </div> |
257 | 311 | </div> |
258 | | - <ShadowHost class="flex-[3] max-h-[808px] overflow-y-auto" style={`${themeStyle}\n${iconSetStyle}`}> |
| 312 | + <ShadowHost |
| 313 | + class="flex-[3] max-h-[808px] overflow-y-auto" |
| 314 | + style={`${themeStyle}\n${iconSetStyle}`} |
| 315 | + > |
259 | 316 | <SimpleForm |
260 | 317 | {form} |
261 | 318 | data-theme={themeName === "skeleton" ? "cerberus" : lightOrDark} |
|
0 commit comments