← Public packages
@kentcdodds/package-app-kit
Design tokens, PWA install/update, About/version, cache helpers, and optional realtime notes sync for Kody package apps.
app/ui/double-check-button.tsx
59 lines · 1.4 KB · TypeScript/** @jsxRuntime automatic */
/** @jsxImportSource remix/ui */
import { clientEntry, on, type Handle, type RemixNode } from 'remix/ui'
/**
* Two-step confirm for destructive form submits (progressive enhancement).
* SSR / no-JS: `type="submit"` so the parent form POSTs normally.
* With JS: first click arms (preventDefault); second click submits; blur resets.
* Pass `icon` for a Lucide (or other) glyph beside the label — labeled buttons stay tip-free.
*/
export const DoubleCheckButton = clientEntry(
'kody:app#DoubleCheckButton',
function DoubleCheckButton(
handle: Handle<{
label?: string
confirmLabel?: string
className?: string
icon?: RemixNode
}>,
) {
let armed = false
const label = handle.props.label ?? 'Delete'
const confirmLabel = handle.props.confirmLabel ?? 'Are you sure?'
return () => (
<button
type="submit"
class={handle.props.className ?? 'pak-btn pak-btn-danger'}
data-double-check
data-armed={armed ? 'true' : 'false'}
aria-pressed={armed ? 'true' : 'false'}
mix={[
on('click', (event) => {
if (!armed) {
event.preventDefault()
armed = true
handle.update()
}
}),
on('blur', () => {
if (armed) {
armed = false
handle.update()
}
}),
]}
>
{armed ? (
confirmLabel
) : (
<span class="pak-cluster pak-gap-2">
{handle.props.icon}
{label}
</span>
)}
</button>
)
},
)