49 lines
1.5 KiB
TypeScript
49 lines
1.5 KiB
TypeScript
import React from 'react'
|
|
import { Card, Group, Stack, Text, Title } from '@mantine/core'
|
|
|
|
export interface PanelProps {
|
|
/** Optional panel heading. */
|
|
title?: React.ReactNode
|
|
/** Optional sub-text under the heading. */
|
|
description?: React.ReactNode
|
|
/** Right-aligned header actions. */
|
|
actions?: React.ReactNode
|
|
/** Panel body. */
|
|
children: React.ReactNode
|
|
/** Remove inner padding (e.g. for a flush table). */
|
|
noPadding?: boolean
|
|
}
|
|
|
|
// A bordered content section with an optional titled header. The workhorse
|
|
// container — group related content into Panels instead of loose Cards.
|
|
export function Panel({ title, description, actions, children, noPadding }: PanelProps) {
|
|
return (
|
|
<Card withBorder radius="lg" padding={noPadding ? 0 : 'lg'}>
|
|
{title || actions ? (
|
|
<Group
|
|
justify="space-between"
|
|
align="flex-end"
|
|
wrap="nowrap"
|
|
p={noPadding ? 'lg' : 0}
|
|
pb={noPadding ? 'sm' : 'md'}
|
|
>
|
|
<Stack gap={2} style={{ minWidth: 0 }}>
|
|
{title ? (
|
|
<Title order={2} fz={16} fw={620} style={{ letterSpacing: '-0.02em' }}>
|
|
{title}
|
|
</Title>
|
|
) : null}
|
|
{description ? (
|
|
<Text c="dimmed" size="xs">
|
|
{description}
|
|
</Text>
|
|
) : null}
|
|
</Stack>
|
|
{actions ? <div style={{ flexShrink: 0 }}>{actions}</div> : null}
|
|
</Group>
|
|
) : null}
|
|
{children}
|
|
</Card>
|
|
)
|
|
}
|