Komponen / Aksi
ActionMenu
Menu aksi pop-up dengan grup, pemisah, dan aksi merusak: aksi per baris di DataTable (⋯), "Aksi lain" di header detail PO (Duplikat, Cetak, Kirim ke customer, Batalkan PO).
Dasar
Item berisi ikon 16, label kata kerja, dan pintasan keyboard di kanan. Item nonaktif tetap terlihat (opasitas 40%) agar pengguna tahu aksinya ada.
Belum ada aksi dipilih.
import { useState } from 'react';
import { ActionMenu, Button } from '@divistant/circca-react';
import { CaretDown, Copy, PaperPlaneTilt, PencilSimple, Printer } from '@phosphor-icons/react';
// Menu aksi dokumen: ikon 16, label kata kerja, pintasan keyboard di kanan (hanya tampilan).
export default function Example() {
const [last, setLast] = useState<string | null>(null);
return (
<div className="flex flex-wrap items-center gap-4">
<ActionMenu trigger={<Button trailingVisual={CaretDown}>Aksi PO</Button>}>
<ActionMenu.Item leadingVisual={PencilSimple} shortcut="⌘E" onSelect={() => setLast('Ubah PO')}>
Ubah
</ActionMenu.Item>
<ActionMenu.Item leadingVisual={Copy} shortcut="⌘D" onSelect={() => setLast('Duplikat PO')}>
Duplikat
</ActionMenu.Item>
<ActionMenu.Item leadingVisual={Printer} shortcut="⌘P" onSelect={() => setLast('Cetak')}>
Cetak
</ActionMenu.Item>
<ActionMenu.Item leadingVisual={PaperPlaneTilt} disabled>
Kirim ke customer
</ActionMenu.Item>
</ActionMenu>
<p className="type-callout-default text-muted" aria-live="polite">
{last ? `Dipilih: ${last}` : 'Belum ada aksi dipilih.'}
</p>
</div>
);
}Grup, pemisah, dan deskripsi
Judul grup (Footnote/Medium) dan pemisah memecah menu panjang. Contoh ini memakai prop `items` sebagai pintasan data.
import { ActionMenu, IconButton } from '@divistant/circca-react';
import { ArrowsClockwise, Copy, DotsThree, FileCsv, FilePdf, Printer, Receipt } from '@phosphor-icons/react';
// Grup berjudul + pemisah, deskripsi singkat, dan prop `items` sebagai pintasan data.
export default function Example() {
return (
<ActionMenu
align="start"
trigger={<IconButton icon={DotsThree} label="Aksi lain" />}
items={[
{
type: 'group',
label: 'Dokumen',
items: [
{ label: 'Duplikat PO', leadingVisual: Copy, shortcut: '⌘D' },
{ label: 'Cetak', leadingVisual: Printer, shortcut: '⌘P' },
{
label: 'Buat invoice',
leadingVisual: Receipt,
description: 'Tagihan termin 1 dari PO ini',
},
],
},
{ type: 'separator' },
{
type: 'group',
label: 'Ekspor',
items: [
{ label: 'PDF', leadingVisual: FilePdf },
{ label: 'CSV', leadingVisual: FileCsv },
],
},
{ type: 'separator' },
{
label: 'Sinkronkan ke BizOps',
leadingVisual: ArrowsClockwise,
disabled: true,
description: 'Sedang berjalan…',
},
]}
/>
);
}Aksi merusak + konfirmasi
Batalkan PO berada paling bawah setelah pemisah, lalu dikonfirmasi Dialog `alert`. `onSelectAfterClose` membuka Dialog setelah menu tertutup sehingga fokus kembali ke tombol ⋯ saat Dialog ditutup.
PO-2026-0142 · Disetujui
import { useState } from 'react';
import { ActionMenu, Button, Dialog, IconButton } from '@divistant/circca-react';
import { Copy, DotsThree, Printer, XCircle } from '@phosphor-icons/react';
// Aksi merusak paling bawah setelah pemisah, lalu dikonfirmasi Dialog alert.
// `onSelectAfterClose` membuka Dialog setelah menu tertutup dan fokus kembali ke tombol ⋯,
// sehingga saat Dialog ditutup fokus kembali ke tombol itu.
export default function Example() {
const [confirm, setConfirm] = useState(false);
const [status, setStatus] = useState('Disetujui');
return (
<div className="flex items-center gap-3">
<span className="type-body-default text-default">PO-2026-0142 · {status}</span>
<ActionMenu align="end" trigger={<IconButton variant="ghost" size="sm" icon={DotsThree} label="Aksi lain untuk PO-2026-0142" />}>
<ActionMenu.Item leadingVisual={Copy} shortcut="⌘D">
Duplikat PO
</ActionMenu.Item>
<ActionMenu.Item leadingVisual={Printer} shortcut="⌘P">
Cetak
</ActionMenu.Item>
<ActionMenu.Separator />
<ActionMenu.Item
variant="danger"
leadingVisual={XCircle}
disabled={status === 'Dibatalkan'}
onSelectAfterClose={() => setConfirm(true)}
>
Batalkan PO…
</ActionMenu.Item>
</ActionMenu>
<Dialog
alert
open={confirm}
onOpenChange={setConfirm}
title="Batalkan PO-2026-0142?"
subtitle="PO tidak bisa diproses lagi. Delivery note dan invoice yang belum dibuat ikut dibatalkan."
footer={
<>
<Dialog.Close asChild>
<Button>Kembali</Button>
</Dialog.Close>
<Button
variant="danger"
onClick={() => {
setStatus('Dibatalkan');
setConfirm(false);
}}
>
Batalkan PO
</Button>
</>
}
/>
</div>
);
}