Mikkel Manniche
Dansk English Deutsch

Kontroller

Danger zone

Den røde sektion nederst på en indstillingsside med én række pr. handling, der ikke let kan fortrydes.

Forhåndsvisning

Brug

danger-zone-demo.tsx
import { useState } from 'react'
import { ConfirmDialog } from '@/components/confirm-dialog'
import { DangerZone, dangerButton } from '@/components/danger-zone'
import { HoldToConfirm } from '@/components/hold-to-confirm'

export default function DangerZoneDemo() {
  const [open, setOpen] = useState(false)
  return (
    <div className="mx-auto w-full max-w-xl">
      <DangerZone
        actions={[
          {
            id: 'pause',
            title: 'Pause the shop',
            description: 'Visitors see a closed sign. Orders already placed still ship.',
            action: (
              <HoldToConfirm onConfirm={() => console.log('paused')} doneLabel="Paused" className="rounded-xl text-sm">
                Hold to pause
              </HoldToConfirm>
            ),
          },
          {
            id: 'delete',
            title: 'Delete the shop',
            description: 'Products, orders and customers are kept for 14 days, then removed for good.',
            action: (
              <button type="button" onClick={() => setOpen(true)} className={dangerButton}>
                Delete shop
              </button>
            ),
          },
        ]}
      />
      <ConfirmDialog
        open={open}
        onOpenChange={setOpen}
        destructive
        title="Delete Nordlys Home?"
        description="You can restore the shop for 14 days. After that it is gone."
        typeToConfirm="Nordlys Home"
        confirmLabel="Delete shop"
        cancelLabel="Keep shop"
        onConfirm={() => console.log('shop deleted')}
      />
    </div>
  )
}

Kilde

danger-zone.tsx
import { TriangleAlert } from 'lucide-react'
import { useId, type ReactNode } from 'react'
import { cn } from '@/lib/utils'

export type DangerZoneAction = {
  id: string
  title: string
  /** What happens and whether it can be undone. */
  description: ReactNode
  /** The button, usually a ConfirmDialog trigger or HoldToConfirm. */
  action: ReactNode
}

export type DangerZoneProps = {
  title?: string
  actions: DangerZoneAction[]
  className?: string
}

/**
 * The last section of a settings page: a red frame with a heading, one row per action that cannot
 * easily be undone. Keeping them together and at the bottom means nobody meets them by accident.
 */
export function DangerZone({ title = 'Danger zone', actions, className }: DangerZoneProps) {
  const headingId = useId()
  return (
    <section aria-labelledby={headingId} className={cn('grid gap-3', className)}>
      <h2 id={headingId} className="flex items-center gap-2 text-base font-semibold text-destructive">
        <TriangleAlert className="size-4" aria-hidden />
        {title}
      </h2>
      <ul className="divide-y divide-destructive/20 overflow-hidden rounded-2xl border border-destructive/40 bg-card">
        {actions.map((a) => (
          <li key={a.id} className="flex flex-wrap items-center justify-between gap-x-6 gap-y-3 p-4">
            <div className="grid min-w-0 flex-1 basis-60 gap-0.5">
              <h3 className="text-sm font-medium">{a.title}</h3>
              <p className="text-sm text-pretty text-muted-foreground">{a.description}</p>
            </div>
            <div className="shrink-0">{a.action}</div>
          </li>
        ))}
      </ul>
    </section>
  )
}

/** The outlined red button that fits a danger-zone row. Turns solid red only on hover. */
export const dangerButton =
  'inline-flex min-h-11 items-center rounded-xl border border-destructive/40 bg-card px-4 text-sm font-medium text-destructive transition-[background-color,color,transform] duration-150 ease-out-quint hover:bg-destructive hover:text-white active:scale-[0.97]'

Installér

npx shadcn@latest add https://mikkelmanniche.dk/lab/r/danger-zone.json

Med registret tilføjet (components.json)

npx shadcn@latest add @manniche/danger-zone

Filer

  • danger-zone.tsx

Manniche Lab · open source

Gratis værktøjer til AI-agenter og webudvikling.