/**
* Shared state for the registration dialog.
*
* `` is mounted exactly once (app.vue), so every call to action on
* the site — header, hero, courier band, registration section — drives the same instance.
* Consequence worth keeping: the form keeps whatever the visitor already typed when the
* dialog is closed and reopened.
*
* …which is exactly why the dialog used to reopen on the "check your inbox" screen after
* a successful submission: `done` and the form data are per-instance state, and closing
* the dialog never touched them. `openDialog()` therefore bumps `resetToken`, and
* RegistrationForm listens for it. The reset happens on OPEN, never on close, so
* dismissing the dialog halfway through typing still preserves the input.
*
* The element that opened the dialog is held in a module-level variable rather than in
* `useState`, because it is a DOM node (never serialisable into the SSR payload). It is
* only ever written from a browser event handler.
*/
export type RegistrationPreset = '' | 'klinik' | 'kurierdienst'
let lastTrigger: HTMLElement | null = null
export const useRegistrationDialog = () => {
const open = useState('b24-registration-dialog', () => false)
const presetType = useState('b24-registration-preset', () => '')
/** Incremented on every open; RegistrationForm watches it to clear finished state. */
const resetToken = useState('b24-registration-reset', () => 0)
/**
* Opens the dialog. Pass the click event so focus can be returned to the exact CTA that
* opened it; pass a preset to skip step 1 for CTAs that already state the type
* ("Als Kurierdienst registrieren").
*/
const openDialog = (event?: Event | null, preset: RegistrationPreset = '') => {
if (import.meta.client) {
const candidate = (event?.currentTarget ?? document.activeElement) as HTMLElement | null
lastTrigger = candidate && typeof candidate.focus === 'function' ? candidate : null
}
// Assigned unconditionally: a preset left over from an earlier CTA must not leak into
// an open that deliberately asks for no preselection ("Registrierung starten").
presetType.value = preset
resetToken.value++
open.value = true
}
const closeDialog = () => {
open.value = false
}
/** Returns focus to the CTA that opened the dialog (WCAG 2.4.3 "Focus Order"). */
const restoreTriggerFocus = () => {
const el = lastTrigger
lastTrigger = null
if (!import.meta.client || !el || !document.contains(el)) return
// One frame later: the dialog is out of the top layer by then, so no scroll fight.
requestAnimationFrame(() => el.focus())
}
return { open, presetType, resetToken, openDialog, closeDialog, restoreTriggerFocus }
}