Components Menus and overlays

Modal

A composable modal dialog for host apps: header, content, footer; it traps focus and closes on Escape or an outside press.

import { Modal, ModalContent, ModalHeader, ModalTitle, ModalDescription, ModalBody, ModalFooter, ModalCloseButton } from '@kition/tessera-ui'

When to use

  • Confirming details or collecting a few values in an app (rename, share, create).

When not to

  • A kit tool’s settings: use Dialog.
  • Editing beside the work: use Sheet.

Examples

Rename
modal.tsx
import { useState } from 'react'
import { Button, Field, Modal, ModalContent, ModalDescription, ModalFooter, ModalHeader, ModalTitle, TextInput } from '@kition/tessera-ui'

export default function Demo() {
  const [open, setOpen] = useState(false)
  const [name, setName] = useState('Quarterly plan')
  return (
    <>
      <Button variant="secondary" size="md" onClick={() => setOpen(true)}>Rename…</Button>
      <Modal open={open} onOpenChange={setOpen}>
        <ModalContent size="sm">
          <ModalHeader>
            <ModalTitle>Rename document</ModalTitle>
            <ModalDescription>The name shows in the tab and the file tree.</ModalDescription>
          </ModalHeader>
          <Field label="Name">{({ id }) => <TextInput id={id} size="lg" value={name} onChange={(event) => setName(event.target.value)} />}</Field>
          <ModalFooter>
            <Button variant="secondary" size="md" onClick={() => setOpen(false)}>Cancel</Button>
            <Button variant="primary" size="md" onClick={() => setOpen(false)}>Rename</Button>
          </ModalFooter>
        </ModalContent>
      </Modal>
    </>
  )
}
A long dialog

With `fixed` the header and footer stay put and ModalBody scrolls; a ModalCloseButton of your own can be disabled while work runs.

modal-fixed.tsx
import { useState } from 'react'
import { Button, Modal, ModalBody, ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalTitle } from '@kition/tessera-ui'

export default function Demo() {
  const [open, setOpen] = useState(false)
  const [running, setRunning] = useState(false)
  return (
    <>
      <Button variant="secondary" size="md" onClick={() => setOpen(true)}>Import file…</Button>
      <Modal open={open} onOpenChange={(next) => (running ? undefined : setOpen(next))}>
        <ModalContent size="xl" fixed hideClose>
          <ModalHeader>
            <ModalTitle>Import a file</ModalTitle>
          </ModalHeader>
          <ModalBody>
            {Array.from({ length: 24 }, (_, i) => (
              <p key={i} style={{ margin: 0, fontSize: 14 }}>Column {i + 1}: text</p>
            ))}
          </ModalBody>
          <ModalFooter>
            <Button variant="ghost" size="md" disabled={running} onClick={() => setOpen(false)}>Cancel</Button>
            <Button
              variant="primary"
              size="md"
              loading={running}
              onClick={() => {
                setRunning(true)
                setTimeout(() => {
                  setRunning(false)
                  setOpen(false)
                }, 1200)
              }}
            >
              Import
            </Button>
          </ModalFooter>
          <ModalCloseButton disabled={running} />
        </ModalContent>
      </Modal>
    </>
  )
}

Props

Modal

A modal dialog. Controlled with open and onOpenChange; Escape and an outside press close it.

No props.

Also takes the attributes of its HTML element (`className`, `aria-*`, events…).

ModalContent

The dialog's panel, over a dimmed page: 12px radius, a hairline, the modal shadow.

PropTypeDefaultDescription
sizeModalSize'lg'Its widest: xs 320px to 4xl 896px; none leaves the width to className.
hideCloseboolean—No close button in the corner (the footer has its own way out).
dismissiblebooleantrueWhen false, an outside press does not close it (Escape still does).
disableAutoFocusboolean—Keeps focus where it was instead of moving it into the dialog when it opens.
fixedboolean—A long dialog: the header and footer stay put with hairlines, and ModalBody scrolls between them.

Also takes the attributes of its HTML element (`className`, `aria-*`, events…).

ModalHeader

The title and description at the top of a modal.

No props.

Also takes the attributes of its HTML element (`className`, `aria-*`, events…).

ModalTitle

No props.

Also takes the attributes of its HTML element (`className`, `aria-*`, events…).

ModalDescription

No props.

Also takes the attributes of its HTML element (`className`, `aria-*`, events…).

ModalBody

The content between a modal's header and footer; it scrolls in a fixed modal.

No props.

Also takes the attributes of its HTML element (`className`, `aria-*`, events…).

ModalFooter

The actions at the bottom of a modal, right-aligned; full width on a narrow window.

No props.

Also takes the attributes of its HTML element (`className`, `aria-*`, events…).

ModalCloseButton

The close button in a modal's corner. ModalContent draws one; with hideClose, place your own, for one you can disable (disabled while an import runs).

No props.

Also takes the attributes of its HTML element (`className`, `aria-*`, events…).

Used in

2 of Kition's 16 kits use these components.

  • Sheet
  • Table