Files
auth.andert.me/src/lib/components/refactor/ComposerForm.svelte

190 lines
5.3 KiB
Svelte

<script lang="ts">
import { createMachine, assign } from 'xstate';
import { useMachine } from '@xstate/svelte';
import { superForm } from 'sveltekit-superforms/client';
import TextInput from './inputFields/TextInput.svelte';
import ToggleInput from './inputFields/ToggleInput.svelte';
import SliderInput from './inputFields/SliderInput.svelte';
import SelectInput from './inputFields/SelectInput.svelte';
import TextAreaInput from './inputFields/TextAreaInput.svelte';
import NumberInput from './inputFields/NumberInput.svelte';
export let me;
const { fields, validators } = $me.context;
const initialFormData = fields.reduce((acc, field) => {
acc[field.name] = field.placeholder;
return acc;
}, {});
const { form, errors, validate, constraints } = superForm(initialFormData, {
validators: validators,
warnings: {
noValidationAndConstraints: false
},
validationMethod: 'oninput',
clearOnSubmit: 'errors-and-message'
});
const formMachine = createMachine({
id: 'form',
initial: 'input',
context: {
currentField: 0,
formData: initialFormData
},
states: {
input: {
on: {
NEXT: [
{
target: 'submitting',
actions: assign({
formData: (context, event) => ({
...context.formData,
[fields[context.currentField].name]: event.fieldValue
})
}),
cond: (context) => context.currentField === fields.length - 1
},
{
target: 'input',
actions: [
assign({
currentField: (context) => context.currentField + 1,
formData: (context, event) => ({
...context.formData,
[fields[context.currentField].name]: event.fieldValue
})
})
]
}
],
PREV: {
target: 'input',
actions: assign({
currentField: (context) => context.currentField - 1
}),
cond: (context) => context.currentField > 0
}
}
},
submitting: {
on: {
SUBMIT: 'submitted'
}
},
submitted: {
// Add your submission logic here
}
}
});
const { state, send } = useMachine(formMachine);
async function handleNext() {
const currentFieldName = fields[$state.context.currentField].name;
const validationResult = await validate(currentFieldName);
if (validationResult && !validationResult.valid) {
return;
}
const fieldValue = $form[currentFieldName];
console.log(`Current input value: ${fieldValue}`);
send('NEXT', { fieldValue });
// Log the context formData
console.log(`Context formData: ${JSON.stringify($state.context.formData)}`);
}
function handleKeyDown(event) {
if (event.key === 'Enter') {
event.preventDefault();
handleNext();
}
}
let childInput;
$: {
childInput = {
form,
errors,
validate,
field: fields[$state.context.currentField],
constraints
};
}
</script>
<div class="p-6">
<form on:submit|preventDefault={handleNext} on:keydown={handleKeyDown} class="w-full max-w-md">
<div class="mb-4">
{#if $errors[fields[$state.context.currentField].name]}
<span class="block mb-2 font-semibold text-red-500">
{$errors[fields[$state.context.currentField].name]}
</span>
{:else}
<label
for={fields[$state.context.currentField].name}
class="block mb-2 font-semibold text-white"
>
{fields[$state.context.currentField].name.charAt(0).toUpperCase() +
fields[$state.context.currentField].name.slice(1)}
</label>
{/if}
{#if fields[$state.context.currentField].type === 'text'}
<TextInput {childInput} />
{:else if fields[$state.context.currentField].type === 'email'}
<TextInput {childInput} />
{:else if fields[$state.context.currentField].type === 'textarea'}
<TextAreaInput {childInput} />
{:else if fields[$state.context.currentField].type === 'select'}
<SelectInput {childInput} />
{:else if fields[$state.context.currentField].type === 'slider'}
<SliderInput {childInput} />
{:else if fields[$state.context.currentField].type === 'toggle'}
<ToggleInput {childInput} />
{:else if fields[$state.context.currentField].type === 'number'}
<NumberInput {childInput} />
{/if}
</div>
{#if $state.matches('submitting')}
<div class="mb-4">
<h2 class="text-lg font-semibold text-white">Summary</h2>
{#each Object.entries($state.context.formData) as [key, value]}
<p class="mt-1 text-white">{key}: {value}</p>
{/each}
</div>
{/if}
<div class="flex justify-between mt-4">
<button
type="button"
on:click={() => send('PREV')}
class="px-4 py-2 text-white bg-blue-500 rounded-md hover:bg-blue-600 focus:outline-none focus:ring-2 focus:ring-blue-500"
disabled={$state.context.currentField === 0}
>
Previous
</button>
{#if !$state.matches('submitting')}
<button
type="button"
on:click={() => send('NEXT')}
class="px-4 py-2 text-white bg-blue-500 rounded-md hover:bg-blue-600 focus:outline-none focus:ring-2 focus:ring-blue-500"
disabled={$errors[fields[$state.context.currentField].name]}
>
Next
</button>
{/if}
{#if $state.matches('submitting')}
<button
type="submit"
class="px-4 py-2 text-white bg-green-500 rounded-md hover:bg-green-600 focus:outline-none focus:ring-2 focus:ring-green-500"
>
Submit
</button>
{/if}
</div>
</form>
</div>