Skip to content

Commit 058d06e

Browse files
committed
[sveltekit] Add createAction function
1 parent f15de9b commit 058d06e

File tree

11 files changed

+122
-92
lines changed

11 files changed

+122
-92
lines changed

.changeset/strong-plants-find.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@sjsf/sveltekit": minor
3+
---
4+
5+
Add `createAction` function

packages/sveltekit/src/lib/client/form.svelte.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ export function createSvelteKitForm<
7272
if (validationData === undefined) {
7373
return;
7474
}
75-
if (validationData.sendData && form.isSubmitted) {
75+
if (validationData.sendData) {
7676
form.value = validationData.data;
7777
}
7878
updateErrors(form, validationData.errors);

packages/sveltekit/src/lib/server/server.ts

Lines changed: 57 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1+
import { fail, type ActionFailure, type RequestEvent } from '@sveltejs/kit';
12
/* eslint-disable @typescript-eslint/no-explicit-any */
2-
import type { DeepPartial } from '@sjsf/form/lib/types';
3+
import type { DeepPartial, MaybePromise } from '@sjsf/form/lib/types';
34
import type { Validator } from '@sjsf/form/core';
45
import {
56
DEFAULT_ID_PREFIX,
67
isFormValueValidator,
78
type Schema,
8-
type SchemaValue,
99
type UiSchemaRoot,
1010
type ValidationError,
1111
isAsyncFormValueValidator,
@@ -69,7 +69,7 @@ export interface FormHandlerOptions<SendData extends boolean> extends IdOptions
6969
schema: Schema;
7070
uiSchema?: UiSchemaRoot;
7171
uiOptionsRegistry?: UiOptionsRegistry;
72-
idIndexSeparator?: string
72+
idIndexSeparator?: string;
7373
validator: Creatable<Validator, ValidatorFactoryOptions>;
7474
merger: Creatable<FormMerger, MergerFactoryOptions>;
7575
createEntriesConverter?: Creatable<
@@ -135,7 +135,13 @@ export function createFormHandler<SendData extends boolean>({
135135
return async (
136136
signal: AbortSignal,
137137
formData: FormData
138-
): Promise<[ValidatedFormData<SendData>, FormValue]> => {
138+
): Promise<
139+
[
140+
ValidatedFormData<SendData>,
141+
FormValue,
142+
(errors: ValidationError[]) => ValidatedFormData<SendData>
143+
]
144+
> => {
139145
const data = formData.has(JSON_CHUNKS_KEY)
140146
? JSON.parse(formData.getAll(JSON_CHUNKS_KEY).join(''), createReviver(formData))
141147
: await parseSchemaValue(signal, {
@@ -155,20 +161,58 @@ export function createFormHandler<SendData extends boolean>({
155161
: isFormValueValidator(validator)
156162
? validator.validateFormValue(schema, data)
157163
: [];
158-
return [
159-
{
164+
function validated(errors: ValidationError[]) {
165+
return {
160166
isValid: errors.length === 0,
161-
sendData,
162-
data: (sendData ? data : undefined) as SendData extends true
163-
? SchemaValue | undefined
164-
: undefined,
167+
sendData: sendData ? data : undefined,
168+
data: data as FormValue,
165169
errors
166-
},
167-
data
168-
];
170+
} as ValidatedFormData<SendData>;
171+
}
172+
return [validated(errors), data, validated];
169173
};
170174
}
171175

172176
export function isValid<T>(vfd: ValidatedFormData<boolean>, data: unknown): data is T {
173177
return vfd.isValid;
174178
}
179+
180+
type FormRecord<F extends string, SendData extends boolean> = {
181+
[K in F]: ValidatedFormData<SendData>;
182+
};
183+
184+
export function createAction<
185+
const F extends string,
186+
const SendData extends boolean,
187+
E extends RequestEvent,
188+
R extends Record<string, any> | void
189+
>(
190+
options: FormHandlerOptions<SendData> & {
191+
name: F;
192+
},
193+
userAction: (data: any, event: E) => MaybePromise<ValidationError[] | R | void>
194+
) {
195+
const handle = createFormHandler(options);
196+
return async (
197+
event: E
198+
): Promise<(FormRecord<F, SendData> & R) | ActionFailure<FormRecord<F, SendData>>> => {
199+
const [form, data, validated] = await handle(
200+
event.request.signal,
201+
await event.request.formData()
202+
);
203+
if (!form.isValid) {
204+
return fail(400, { [options.name]: form } as FormRecord<F, SendData>);
205+
}
206+
let result = await userAction(data, event);
207+
if (Array.isArray(result)) {
208+
if (result.length > 0) {
209+
return fail(400, {
210+
[options.name]: validated(result)
211+
} as FormRecord<F, SendData>);
212+
} else {
213+
result = undefined;
214+
}
215+
}
216+
return { ...result, [options.name]: form } as FormRecord<F, SendData> & R;
217+
};
218+
}

packages/sveltekit/src/routes/+page.server.ts

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,16 @@
11
import { fail } from '@sveltejs/kit';
2-
import { createFormMerger } from '@sjsf/form/mergers/modern';
3-
import { createFormValidator } from '@sjsf/ajv8-validator';
2+
import type { FormValue } from '@sjsf/form';
43

5-
import { initForm, isValid, createFormHandler } from '$lib/server/index.js';
4+
import { initForm, isValid, createFormHandler, createAction } from '$lib/server/index.js';
65

76
import type { Actions } from './$types.js';
87
import { schema, uiSchema } from './model.js';
8+
import * as defaults from './form-defaults.js';
99

1010
const handleForm = createFormHandler({
11+
...defaults,
1112
schema,
1213
uiSchema,
13-
validator: createFormValidator,
14-
merger: createFormMerger,
1514
sendData: true
1615
});
1716

@@ -31,7 +30,7 @@ export const actions = {
3130
if (!isValid(form, data)) {
3231
return fail(400, { form });
3332
}
34-
console.log(data)
33+
console.log(data);
3534
return {
3635
form
3736
};
@@ -41,5 +40,17 @@ export const actions = {
4140
return {
4241
form2
4342
};
44-
}
43+
},
44+
third: createAction(
45+
{
46+
...defaults,
47+
name: 'form3',
48+
schema,
49+
uiSchema,
50+
sendData: true
51+
},
52+
(data: FormValue) => {
53+
console.log(data);
54+
}
55+
)
4556
} satisfies Actions;

packages/sveltekit/src/routes/+page.svelte

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,13 @@
66
import NativeForm from './native-form.svelte';
77
import Form2 from './form2.svelte';
88
import Form3 from './form3.svelte';
9+
import Form4 from './form4.svelte';
910
</script>
1011

1112
<NativeForm />
1213

1314
<Form2 />
1415

1516
<Form3 />
17+
18+
<Form4 />
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
export { resolver } from '@sjsf/form/resolvers/basic';
2+
import '@sjsf/form/fields/extra-fields/unknown-native-file-include'
3+
4+
export { translation } from '@sjsf/form/translations/en';
5+
export { createFormMerger as merger } from '@sjsf/form/mergers/modern';
6+
export { createFormIdBuilder as idBuilder } from '@sjsf/form/id-builders/modern';
7+
export { createFormValidator as validator } from '@sjsf/ajv8-validator';
8+
export { theme } from '@sjsf/basic-theme';

packages/sveltekit/src/routes/form2.svelte

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,5 @@
11
<script lang="ts">
22
import { BasicForm } from '@sjsf/form';
3-
import { createFormValidator } from '@sjsf/ajv8-validator';
4-
import { theme } from '@sjsf/basic-theme';
5-
import { resolver } from '@sjsf/form/resolvers/basic';
6-
import { translation } from '@sjsf/form/translations/en';
7-
import { createFormMerger } from '@sjsf/form/mergers/modern';
8-
import { createFormIdBuilder } from '@sjsf/form/id-builders/legacy'
93
104
import {
115
createMeta,
@@ -15,9 +9,11 @@
159
1610
import type { PageData, ActionData } from './$types.js';
1711
import { ERROR_TYPE_OBJECTS } from './model.js';
12+
import * as defaults from './form-defaults.js';
1813
1914
const meta = createMeta<ActionData, PageData>().form2;
2015
const { form } = setupSvelteKitForm(meta, {
16+
...defaults,
2117
idPrefix: 'form2',
2218
schema: {
2319
title: 'Parent',
@@ -30,21 +26,16 @@
3026
}
3127
}
3228
},
33-
theme,
34-
resolver,
35-
translation,
3629
onSubmitError: console.warn,
37-
idBuilder: createFormIdBuilder,
3830
validator: (options) =>
3931
Object.assign(
40-
createFormValidator(options),
32+
defaults.validator(options),
4133
createAdditionalPropertyKeyValidator({
4234
error({ type, values }) {
4335
return `The presence of these ${ERROR_TYPE_OBJECTS[type]} ("${values.join('", "')}") is prohibited`;
4436
}
4537
})
46-
),
47-
merger: createFormMerger
38+
)
4839
});
4940
</script>
5041

Lines changed: 9 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,16 @@
11
<script lang="ts">
2-
import { theme } from '@sjsf/basic-theme';
3-
import { createFormValidator } from '@sjsf/ajv8-validator';
4-
import { translation } from '@sjsf/form/translations/en';
5-
import { resolver } from '@sjsf/form/resolvers/basic';
6-
import { createFormMerger } from '@sjsf/form/mergers/modern';
7-
import { createFormIdBuilder } from '@sjsf/form/id-builders/legacy'
8-
9-
import {
10-
SvelteKitForm,
11-
createMeta,
12-
createAdditionalPropertyKeyValidator
13-
} from '$lib/client/index.js';
2+
import { SvelteKitForm, createMeta } from '$lib/client/index.js';
143
154
import type { ActionData, PageData } from './$types.js';
16-
import { ERROR_TYPE_OBJECTS } from './model.js';
5+
import * as defaults from './form-defaults.js';
176
187
const meta = createMeta<ActionData, PageData>().form;
198
</script>
209

2110
<SvelteKitForm
22-
idPrefix="form3"
11+
{...defaults}
2312
{meta}
24-
{theme}
25-
{resolver}
26-
{translation}
27-
idBuilder={createFormIdBuilder}
28-
validator={(options) =>
29-
Object.assign(
30-
createFormValidator(options),
31-
createAdditionalPropertyKeyValidator({
32-
error({ type, values }) {
33-
return `The presence of these ${ERROR_TYPE_OBJECTS[type]} ("${values.join('", "')}") is prohibited`;
34-
}
35-
})
36-
)}
37-
merger={createFormMerger}
13+
idPrefix="form3"
3814
onSubmitError={console.warn}
3915
onSuccess={console.log}
4016
onFailure={console.error}
@@ -44,6 +20,11 @@
4420
action: '?/first',
4521
novalidate: true
4622
}
23+
},
24+
nativeFile: {
25+
'ui:components': {
26+
unknownField: 'unknownNativeFileField'
27+
}
4728
}
4829
}}
4930
/>
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<script lang="ts">
2+
import { createMeta, SvelteKitForm } from '$lib/client/index.js';
3+
4+
import type { PageData, ActionData } from './$types.js';
5+
6+
import * as defaults from './form-defaults.js';
7+
import { schema, uiSchema } from './model.js';
8+
9+
const meta = createMeta<ActionData, PageData>().form3;
10+
</script>
11+
12+
<SvelteKitForm {...defaults} {meta} idPrefix="form4" {schema} {uiSchema} />

packages/sveltekit/src/routes/model.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,9 @@ export const uiSchema: UiSchemaRoot = {
5353
stringField: 'fileField'
5454
}
5555
},
56-
//@ts-expect-error hack
5756
nativeFile: {
5857
'ui:components': {
59-
unknownField: 'nativeFileField'
58+
unknownField: 'unknownNativeFileField'
6059
}
6160
}
6261
};

0 commit comments

Comments
 (0)