-
Notifications
You must be signed in to change notification settings - Fork 22
Sveltekit form actions take 2 #76
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: main
Are you sure you want to change the base?
Changes from all commits
eaec25d
43b2823
23867a6
f6b22ae
bc105ef
042185a
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,5 @@ | ||
| PUBLIC_DIRECTUS_URL=http://localhost:8055 | ||
| PUBLIC_DIRECTUS_TOKEN=STATIC_TOKEN_FROM_Webmaster_account | ||
| DIRECTUS_SERVER_TOKEN=STATIC_TOKEN_FROM_Webmaster_account | ||
| PUBLIC_SITE_URL=http://localhost:3000 | ||
| PUBLIC_DIRECTUS_FORM_TOKEN=STATIC_TOKEN_FROM_Frontend_Bot_User_account | ||
| DRAFT_MODE_SECRET=your-draft-mode-secret | ||
| PUBLIC_ENABLE_VISUAL_EDITING=true |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,17 +1,16 @@ | ||
| { | ||
| "$schema": "https://next.shadcn-svelte.com/schema.json", | ||
| "style": "default", | ||
| "tailwind": { | ||
| "config": "tailwind.config.ts", | ||
| "css": "src/globals.css", | ||
| "baseColor": "slate" | ||
| }, | ||
| "aliases": { | ||
| "components": "$lib/components", | ||
| "utils": "$lib/utils", | ||
| "ui": "$lib/components/ui", | ||
| "hooks": "$lib/hooks" | ||
| "hooks": "$lib/hooks", | ||
| "lib": "$lib" | ||
| }, | ||
| "typescript": true, | ||
| "registry": "https://next.shadcn-svelte.com/registry" | ||
| "registry": "https://tw3.shadcn-svelte.com/registry/default" | ||
| } |
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -1,75 +1,108 @@ | ||||||
| <script lang="ts"> | ||||||
| import { dev } from '$app/environment'; | ||||||
| import { enhance, applyAction } from '$app/forms'; | ||||||
| import { goto } from '$app/navigation'; | ||||||
| import setAttr from '$lib/directus/visualEditing'; | ||||||
| import type { FormField as FormFieldType } from '$lib/types/directus-schema'; | ||||||
| import { buildZodSchema } from '$lib/zodSchemaBuilder'; | ||||||
| import Button from '../blocks/Button.svelte'; | ||||||
| import type { FormBuilderProps } from './formBuilderTypes'; | ||||||
| import Field from './FormField.svelte'; | ||||||
| import { superForm, superValidate } from 'sveltekit-superforms'; | ||||||
| import SuperDebug from 'sveltekit-superforms'; | ||||||
| import { superForm } from 'sveltekit-superforms'; | ||||||
| import { zodClient, zod } from 'sveltekit-superforms/adapters'; | ||||||
| import { zodClient } from 'sveltekit-superforms/adapters'; | ||||||
| const { | ||||||
| form: formProp, | ||||||
| onSubmitted, | ||||||
| onError | ||||||
| }: FormBuilderProps & { onSubmitted: () => void; onError: () => void } = $props(); | ||||||
| interface DynamicFormProps { | ||||||
| fields: FormFieldType[]; | ||||||
| onSubmit: (data: Record<string, any>) => void; | ||||||
| submitLabel: string; | ||||||
| id: string; | ||||||
| } | ||||||
| const fields = $derived(formProp.fields); | ||||||
| const submitLabel = $derived(formProp.submit_label); | ||||||
| const id = $derived(formProp.id); | ||||||
| const { fields, onSubmit, submitLabel, id }: DynamicFormProps = $props(); | ||||||
| const sortedFields = $derived([...fields].sort((a, b) => (a.sort || 0) - (b.sort || 0))); | ||||||
| const formSchema = $derived(buildZodSchema(fields)); | ||||||
| const sortedFields = [...fields].sort((a, b) => (a.sort || 0) - (b.sort || 0)); | ||||||
| const formSchema = buildZodSchema(fields); | ||||||
| const defaultValues = $derived( | ||||||
| fields.reduce<Record<string, any>>((defaults, field) => { | ||||||
| if (!field.name) return defaults; | ||||||
| switch (field.type) { | ||||||
| case 'checkbox': | ||||||
| defaults[field.name] = false; | ||||||
| break; | ||||||
| case 'checkbox_group': | ||||||
| defaults[field.name] = []; | ||||||
| break; | ||||||
| case 'radio': | ||||||
| defaults[field.name] = ''; | ||||||
| break; | ||||||
| default: | ||||||
| defaults[field.name] = ''; | ||||||
| break; | ||||||
| } | ||||||
| const defaultValues = fields.reduce<Record<string, any>>((defaults, field) => { | ||||||
| if (!field.name) return defaults; | ||||||
| switch (field.type) { | ||||||
| case 'checkbox': | ||||||
| defaults[field.name] = false; | ||||||
| break; | ||||||
| case 'checkbox_group': | ||||||
| defaults[field.name] = []; | ||||||
| break; | ||||||
| case 'radio': | ||||||
| defaults[field.name] = ''; | ||||||
| break; | ||||||
| default: | ||||||
| defaults[field.name] = ''; | ||||||
| break; | ||||||
| } | ||||||
| return defaults; | ||||||
| }, {}); | ||||||
| return defaults; | ||||||
| }, {}) | ||||||
| ); | ||||||
| const form = superForm(defaultValues, { | ||||||
| validators: zodClient(formSchema), | ||||||
| SPA: true | ||||||
| }); | ||||||
| const form = $derived( | ||||||
| superForm(defaultValues, { | ||||||
| validators: zodClient(formSchema), | ||||||
| SPA: true | ||||||
| }) | ||||||
| ); | ||||||
| const { enhance, submit, form: formData, errors, validateForm } = $derived(form); | ||||||
| const { form: formData, errors, validateForm } = $derived(form); | ||||||
| </script> | ||||||
|
|
||||||
| const onsubmit = async (e: Event) => { | ||||||
| e.preventDefault(); | ||||||
| // const f = await superValidate($formData, zod(formSchema)); | ||||||
| <form | ||||||
| enctype="multipart/form-data" | ||||||
| class="flex flex-wrap gap-4" | ||||||
| method="POST" | ||||||
| action={`/?/createFormSubmission`} | ||||||
| use:enhance={async ({ formElement, formData, action, cancel, submitter }) => { | ||||||
| // `formElement` is this `<form>` element | ||||||
| // `formData` is its `FormData` object that's about to be submitted | ||||||
| // `action` is the URL to which the form is posted | ||||||
| // calling `cancel()` will prevent the submission | ||||||
| // `submitter` is the `HTMLElement` that caused the form to be submitted | ||||||
| const f = await validateForm(); | ||||||
| $errors = f.errors; | ||||||
| if (f.valid) { | ||||||
| onSubmit($formData); | ||||||
| if (!f.valid) { | ||||||
| console.error('Form is not valid', f); | ||||||
|
||||||
| onError(); | ||||||
| cancel(); | ||||||
| } | ||||||
| }; | ||||||
| </script> | ||||||
|
|
||||||
| <form | ||||||
| class="flex flex-wrap gap-4" | ||||||
| {onsubmit} | ||||||
| return async ({ result }) => { | ||||||
| console.log('formProp', formProp); | ||||||
|
||||||
| console.log('formProp', formProp); |
Copilot
AI
Dec 10, 2025
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.
The cancel() function is called after onError() on line 87, but this doesn't prevent form submission since the form has already been processed (result type is 'failure'). The cancel() call here has no effect. Consider removing it or restructuring the error handling logic.
| cancel(); |
Copilot
AI
Dec 10, 2025
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.
The console.error on line 88 should be removed or replaced with proper error handling before production. Additionally, the cancel() call on line 87 has no effect since this is in the return callback after the form has already been submitted.
| cancel(); | |
| console.error('result is 400', result); |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -74,42 +74,44 @@ | |||||
| <Input | ||||||
| {...props} | ||||||
| placeholder={field.placeholder || ''} | ||||||
| name={field.name || ''} | ||||||
| name={fieldName} | ||||||
| bind:value={$formData[field.name!]} | ||||||
| type={field.validation?.includes('email') ? 'email' : 'text'} | ||||||
| /> | ||||||
| {:else if field.type === 'textarea'} | ||||||
| <Textarea | ||||||
| {...props} | ||||||
| placeholder={field.placeholder || ''} | ||||||
| name={field.name || ''} | ||||||
| bind:value={$formData[field.name!]} | ||||||
| name={fieldName} | ||||||
| bind:value={$formData[fieldName!]} | ||||||
|
Comment on lines
78
to
+86
|
||||||
| required={field.required} | ||||||
| /> | ||||||
| {:else if field.type === 'checkbox'} | ||||||
| <div class="flex items-center space-x-3"> | ||||||
| <Checkbox | ||||||
| {...props} | ||||||
| name={field.name} | ||||||
| bind:checked={$formData[field.name!]} | ||||||
| name={fieldName} | ||||||
| bind:checked={$formData[fieldName!]} | ||||||
| required={!!field.required} | ||||||
| /> | ||||||
| <Label for={field.name}>{field.label}</Label> | ||||||
|
||||||
| <Label for={field.name}>{field.label}</Label> | |
| <Label for={fieldName}>{field.label}</Label> |
Copilot
AI
Dec 10, 2025
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.
[nitpick] The comment here is misleading. The issue isn't specifically related to form fields being "out of line" - this is the standard location for error messages in a form. Consider updating this comment to be more specific about what issue you're referring to, or remove it if the issue has been resolved.
| <!-- When this renders, it causes the form fields to be out of line --> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -16,4 +16,4 @@ | |
| }; | ||
| </script> | ||
|
|
||
| <Input type="file" id={name} onchange={onFileChange} /> | ||
| <Input type="file" id={name} {name} onchange={onFileChange} required={!!formData.required} /> | ||
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| import type { FormField } from "$lib/types/directus-schema"; | ||
|
|
||
| export interface FormBuilderProps { | ||
| class?: string; | ||
| form: { | ||
| id: string; | ||
| on_success?: 'redirect' | 'message' | null; | ||
| sort?: number | null; | ||
| submit_label?: string; | ||
| success_message?: string | null; | ||
| title?: string | null; | ||
| success_redirect_url?: string | null; | ||
| is_active?: boolean | null; | ||
| fields: FormField[]; | ||
| }; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,29 +1,7 @@ | ||
| import Root from './input.svelte'; | ||
|
|
||
| export type FormInputEvent<T extends Event = Event> = T & { | ||
| currentTarget: EventTarget & HTMLInputElement; | ||
| }; | ||
| export type InputEvents = { | ||
| blur: FormInputEvent<FocusEvent>; | ||
| change: FormInputEvent<Event>; | ||
| click: FormInputEvent<MouseEvent>; | ||
| focus: FormInputEvent<FocusEvent>; | ||
| focusin: FormInputEvent<FocusEvent>; | ||
| focusout: FormInputEvent<FocusEvent>; | ||
| keydown: FormInputEvent<KeyboardEvent>; | ||
| keypress: FormInputEvent<KeyboardEvent>; | ||
| keyup: FormInputEvent<KeyboardEvent>; | ||
| mouseover: FormInputEvent<MouseEvent>; | ||
| mouseenter: FormInputEvent<MouseEvent>; | ||
| mouseleave: FormInputEvent<MouseEvent>; | ||
| mousemove: FormInputEvent<MouseEvent>; | ||
| paste: FormInputEvent<ClipboardEvent>; | ||
| input: FormInputEvent<InputEvent>; | ||
| wheel: FormInputEvent<WheelEvent>; | ||
| }; | ||
| import Root from "./input.svelte"; | ||
|
|
||
| export { | ||
| Root, | ||
| // | ||
| Root as Input | ||
| Root as Input, | ||
| }; |
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.
Downgrading
bits-uifrom^2.9.4to^1.8.0is a major version downgrade. This could introduce breaking changes or remove features that may be in use elsewhere in the codebase. Ensure this downgrade is intentional and that all components using bits-ui are compatible with version 1.8.0.