Dialog

Usage

import { Button } from '@jamie/ui/button'
import {
  Dialog,
  DialogClose,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle,
  DialogTrigger
} from '@jamie/ui/dialog'
 
export function DialogDemo() {
  return (
    <Dialog>
      <DialogTrigger render={<Button variant="outline" />}>Share Summary</DialogTrigger>
      <DialogContent>
        <DialogHeader>
          <DialogTitle>Share meeting summary</DialogTitle>
          <DialogDescription>
            This will share the summary of &ldquo;Q3 Revenue Review&rdquo; with all meeting
            participants via email.
          </DialogDescription>
        </DialogHeader>
        <DialogFooter>
          <DialogClose render={<Button variant="outline" />}>Cancel</DialogClose>
          <Button>Share</Button>
        </DialogFooter>
      </DialogContent>
    </Dialog>
  )
}

With Form

Embed form fields inside a dialog for inline data entry.

'use client'
 
import { Button } from '@jamie/ui/button'
import {
  Dialog,
  DialogClose,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle,
  DialogTrigger
} from '@jamie/ui/dialog'
import { Input } from '@jamie/ui/input'
import { Label } from '@jamie/ui/label'
import { useState } from 'react'
 
export function DialogWithFormDemo() {
  const [open, setOpen] = useState(false)
 
  return (
    <Dialog open={open} onOpenChange={setOpen}>
      <DialogTrigger render={<Button variant="outline" />}>Create Agenda</DialogTrigger>
      <DialogContent>
        <DialogHeader>
          <DialogTitle>Create meeting agenda</DialogTitle>
          <DialogDescription>
            Set up the agenda for your upcoming meeting. Participants will be notified.
          </DialogDescription>
        </DialogHeader>
        <div className="grid gap-4 py-4">
          <div className="grid gap-2">
            <Label htmlFor="title">Meeting Title</Label>
            <Input id="title" placeholder="Q4 Budget Planning" />
          </div>
          <div className="grid gap-2">
            <Label htmlFor="topics">Topics</Label>
            <Input id="topics" placeholder="Revenue targets, headcount, vendor costs" />
          </div>
        </div>
        <DialogFooter>
          <DialogClose render={<Button variant="outline" />}>Cancel</DialogClose>
          <Button onClick={() => setOpen(false)}>Create</Button>
        </DialogFooter>
      </DialogContent>
    </Dialog>
  )
}

Custom Close Button

Replace the default close control with your own buttons in the footer.

import { Button } from '@jamie/ui/button'
import {
  Dialog,
  DialogClose,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle,
  DialogTrigger
} from '@jamie/ui/dialog'
 
export function DialogCloseButtonDemo() {
  return (
    <Dialog>
      <DialogTrigger render={<Button variant="outline" />}>Delete Recording</DialogTrigger>
      <DialogContent showCloseButton={false}>
        <DialogHeader>
          <DialogTitle>Delete recording?</DialogTitle>
          <DialogDescription>
            This will permanently remove the recording and its transcript. This action cannot be
            undone.
          </DialogDescription>
        </DialogHeader>
        <DialogFooter>
          <DialogClose render={<Button variant="outline" />}>Keep Recording</DialogClose>
          <Button variant="destructive">Delete</Button>
        </DialogFooter>
      </DialogContent>
    </Dialog>
  )
}

No Close Button

Use showCloseButton={false} to hide the close button.

import { Button } from '@jamie/ui/button'
import {
  Dialog,
  DialogClose,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle,
  DialogTrigger
} from '@jamie/ui/dialog'
 
export function DialogNoCloseButtonDemo() {
  return (
    <Dialog>
      <DialogTrigger render={<Button variant="outline" />}>Export Notes</DialogTrigger>
      <DialogContent showCloseButton={false}>
        <DialogHeader>
          <DialogTitle>Export meeting notes</DialogTitle>
          <DialogDescription>
            Choose a format to export the notes from &ldquo;Weekly Standup&rdquo;.
          </DialogDescription>
        </DialogHeader>
        <DialogFooter>
          <DialogClose render={<Button variant="outline" />}>Cancel</DialogClose>
          <Button>Export as PDF</Button>
        </DialogFooter>
      </DialogContent>
    </Dialog>
  )
}

Keep actions visible while the content scrolls. Use the showCloseButton prop on DialogFooter.

import { Button } from '@jamie/ui/button'
import {
  Dialog,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle,
  DialogTrigger
} from '@jamie/ui/dialog'
 
const actionItems = [
  { title: 'Update Q4 revenue projections', assignee: 'Sarah', due: 'Feb 20' },
  { title: 'Schedule vendor review meeting', assignee: 'Mike', due: 'Feb 22' },
  { title: 'Prepare headcount proposal', assignee: 'Lisa', due: 'Feb 25' },
  { title: 'Review marketing budget allocation', assignee: 'James', due: 'Feb 28' },
  { title: 'Draft customer success playbook', assignee: 'Sarah', due: 'Mar 1' },
  { title: 'Analyze NPS survey by segment', assignee: 'James', due: 'Mar 3' },
  { title: 'Set up retention task force kickoff', assignee: 'Mike', due: 'Mar 5' },
  { title: 'Finalize Q4 content calendar', assignee: 'Lisa', due: 'Mar 7' },
  { title: 'Present product launch marketing plan', assignee: 'James', due: 'Mar 10' },
  { title: 'Coordinate cross-team OKR alignment', assignee: 'Sarah', due: 'Mar 12' }
]
 
export function DialogStickyFooterDemo() {
  return (
    <Dialog>
      <DialogTrigger render={<Button variant="outline" />}>Review Action Items</DialogTrigger>
      <DialogContent className="max-h-[min(80vh,28rem)] flex flex-col">
        <DialogHeader>
          <DialogTitle>Action items</DialogTitle>
          <DialogDescription>
            Review the action items captured during the meeting.
          </DialogDescription>
        </DialogHeader>
        <div className="-mx-4 flex-1 overflow-y-auto px-4">
          <div className="grid gap-3 text-sm">
            {actionItems.map((item) => (
              <div key={item.title} className="rounded-lg border p-3">
                <p className="font-medium">{item.title}</p>
                <p className="text-muted-foreground mt-1">
                  Assigned to {item.assignee} &middot; Due {item.due}
                </p>
              </div>
            ))}
          </div>
        </div>
        <DialogFooter showCloseButton>
          <Button>Send Reminders</Button>
        </DialogFooter>
      </DialogContent>
    </Dialog>
  )
}

Scrollable Content

Long content can scroll while the header stays in view.

import { Button } from '@jamie/ui/button'
import {
  Dialog,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle,
  DialogTrigger
} from '@jamie/ui/dialog'
 
const transcript = [
  {
    name: 'Sarah Chen',
    text: 'Let\u2019s start with the revenue numbers for Q3. Overall, we\u2019re tracking about 12% above our initial projections, which is great news.'
  },
  {
    name: 'Mike Rodriguez',
    text: 'That\u2019s largely driven by the enterprise segment. We closed three major deals that weren\u2019t in the original forecast.'
  },
  {
    name: 'Lisa Park',
    text: 'On the SMB side, we\u2019re seeing higher churn than expected. I think we need to revisit our onboarding process.'
  },
  {
    name: 'Sarah Chen',
    text: 'Agreed. Let\u2019s put together a task force to look at that. Mike, can you lead the effort on improving retention?'
  },
  {
    name: 'Mike Rodriguez',
    text: 'Sure. I\u2019ll set up a kickoff meeting next week and loop in the customer success team. We should also look at the data from our recent NPS survey.'
  },
  {
    name: 'James Wilson',
    text: 'I can pull the NPS data and segment it by account size. That should help us identify where the pain points are.'
  },
  {
    name: 'Lisa Park',
    text: 'One more thing \u2014 the marketing budget for Q4. We need to decide if we\u2019re increasing spend on paid channels or shifting more toward content.'
  },
  {
    name: 'Sarah Chen',
    text: 'Good point. Content has been performing well organically, but paid is what drives the short-term pipeline. We probably need a mix.'
  },
  {
    name: 'Mike Rodriguez',
    text: 'I\u2019d lean toward keeping paid at current levels and investing the incremental budget into content and SEO. The ROI on content compounds over time.'
  },
  {
    name: 'James Wilson',
    text: 'I agree with Mike. Our blog traffic is up 40% quarter-over-quarter, and we\u2019re seeing strong conversion from organic search.'
  },
  {
    name: 'Lisa Park',
    text: 'We should also think about the product launch in November. That\u2019s going to need dedicated marketing spend regardless of the channel mix.'
  },
  {
    name: 'Sarah Chen',
    text: 'Right. Let\u2019s allocate a separate budget for the launch campaign. James, can you put together a proposal by end of next week?'
  },
  {
    name: 'James Wilson',
    text: 'Absolutely. I\u2019ll coordinate with the product team to align messaging and timing.'
  },
  {
    name: 'Sarah Chen',
    text: 'Let\u2019s table that for now and revisit once we have the full Q3 report. I want to make sure we\u2019re making data-driven decisions there.'
  }
]
 
export function DialogScrollableContentDemo() {
  return (
    <Dialog>
      <DialogTrigger render={<Button variant="outline" />}>View Transcript</DialogTrigger>
      <DialogContent className="max-h-[min(80vh,28rem)] flex flex-col">
        <DialogHeader>
          <DialogTitle>Meeting transcript</DialogTitle>
          <DialogDescription>
            Full transcript for &ldquo;Q3 Revenue Review&rdquo;.
          </DialogDescription>
        </DialogHeader>
        <div className="-mx-4 flex-1 overflow-y-auto px-4">
          <div className="grid gap-4 text-sm">
            {transcript.map((entry) => (
              <div key={entry.name}>
                <p className="font-medium">{entry.name}</p>
                <p className="text-muted-foreground">{entry.text}</p>
              </div>
            ))}
          </div>
        </div>
        <DialogFooter showCloseButton />
      </DialogContent>
    </Dialog>
  )
}