forked from cms/tibi-svelte-starter
40ffa8207e
- Introduced ContactFormBlock, FeaturesBlock, HeroBlock, and RichtextBlock components. - Implemented a scroll-reveal action for animations on element visibility. - Enhanced CSS styles for better theming and prose formatting. - Added localization support for new components and updated existing translations. - Created e2e tests for demo pages including contact form validation and navigation. - Added a video tour showcasing the demo pages and interactions.
148 lines
5.0 KiB
Svelte
148 lines
5.0 KiB
Svelte
<script lang="ts">
|
|
import { _ } from "../lib/i18n/index"
|
|
import { reveal } from "../lib/actions/reveal"
|
|
import { addToast } from "../lib/toast"
|
|
import Form from "../widgets/Form.svelte"
|
|
import Input from "../widgets/Input.svelte"
|
|
import Select from "../widgets/Select.svelte"
|
|
import Button from "../widgets/Button.svelte"
|
|
|
|
let { block }: { block: ContentBlockEntry } = $props()
|
|
|
|
const paddingTop = $derived(
|
|
block.padding?.top === "lg"
|
|
? "pt-20"
|
|
: block.padding?.top === "md"
|
|
? "pt-12"
|
|
: block.padding?.top === "sm"
|
|
? "pt-8"
|
|
: "pt-4"
|
|
)
|
|
const paddingBottom = $derived(
|
|
block.padding?.bottom === "lg"
|
|
? "pb-20"
|
|
: block.padding?.bottom === "md"
|
|
? "pb-12"
|
|
: block.padding?.bottom === "sm"
|
|
? "pb-8"
|
|
: "pb-4"
|
|
)
|
|
|
|
let name = $state("")
|
|
let email = $state("")
|
|
let subject = $state("")
|
|
let message = $state("")
|
|
let sending = $state(false)
|
|
let formRef = $state<{ validate: () => Promise<boolean> } | null>(null)
|
|
|
|
const subjectOptions = $derived([
|
|
{ value: "", label: $_("form.selectSubject") },
|
|
{ value: "general", label: $_("form.subjects.general") },
|
|
{ value: "project", label: $_("form.subjects.project") },
|
|
{ value: "support", label: $_("form.subjects.support") },
|
|
{ value: "feedback", label: $_("form.subjects.feedback") },
|
|
])
|
|
|
|
async function handleSubmit(e: SubmitEvent) {
|
|
e.preventDefault()
|
|
if (!formRef) return
|
|
|
|
const isValid = await formRef.validate()
|
|
if (!isValid) return
|
|
|
|
sending = true
|
|
|
|
// Simulate API call
|
|
await new Promise((resolve) => setTimeout(resolve, 1200))
|
|
|
|
addToast($_("form.success"), "success", 5000)
|
|
|
|
// Reset form
|
|
name = ""
|
|
email = ""
|
|
subject = ""
|
|
message = ""
|
|
sending = false
|
|
}
|
|
</script>
|
|
|
|
<section
|
|
data-block="contact-form"
|
|
class="contact-form-section {paddingTop} {paddingBottom}"
|
|
id={block.anchorId || undefined}
|
|
>
|
|
<div class="max-w-2xl mx-auto px-6">
|
|
{#if block.headline}
|
|
<div use:reveal>
|
|
<h2 class="text-3xl font-display font-bold text-gray-900 mb-8">
|
|
{block.headline}
|
|
</h2>
|
|
</div>
|
|
{/if}
|
|
|
|
<div use:reveal={{ delay: 150 }}>
|
|
<Form bind:this={formRef} onsubmit={handleSubmit} class="space-y-6">
|
|
<div class="grid sm:grid-cols-2 gap-6">
|
|
<Input
|
|
label={$_("form.name")}
|
|
hideLabel={false}
|
|
bind:value={name}
|
|
placeholder={$_("form.name")}
|
|
required
|
|
name="name"
|
|
messages={{ valueMissing: $_("form.validation.required") }}
|
|
/>
|
|
<Input
|
|
label={$_("form.email")}
|
|
hideLabel={false}
|
|
bind:value={email}
|
|
placeholder={$_("form.email")}
|
|
type="email"
|
|
required
|
|
name="email"
|
|
messages={{
|
|
valueMissing: $_("form.validation.required"),
|
|
typeMismatch: $_("form.validation.email"),
|
|
}}
|
|
/>
|
|
</div>
|
|
|
|
<Select
|
|
label={$_("form.subject")}
|
|
hideLabel={false}
|
|
bind:value={subject}
|
|
options={subjectOptions}
|
|
required
|
|
name="subject"
|
|
/>
|
|
|
|
<div>
|
|
<label for="contact-message" class="block text-sm font-medium text-gray-700 mb-1">
|
|
{$_("form.message")}
|
|
</label>
|
|
<textarea
|
|
id="contact-message"
|
|
bind:value={message}
|
|
rows="5"
|
|
required
|
|
name="message"
|
|
placeholder={$_("form.message")}
|
|
class="w-full rounded-lg border border-gray-300 px-4 py-3 text-gray-900 shadow-sm focus:border-brand-500 focus:ring-2 focus:ring-brand-500/20 focus:outline-none transition-colors resize-y"
|
|
></textarea>
|
|
</div>
|
|
|
|
<div class="flex justify-end">
|
|
<Button
|
|
type="submit"
|
|
variant="primary"
|
|
size="lg"
|
|
text={sending ? $_("loading") : $_("form.send")}
|
|
disabled={sending}
|
|
class="bg-brand-600! hover:bg-brand-700! rounded-xl!"
|
|
/>
|
|
</div>
|
|
</Form>
|
|
</div>
|
|
</div>
|
|
</section>
|