feat: integrate AG Grid with Mantine theme and add showcase component
This commit is contained in:
@@ -0,0 +1,218 @@
|
||||
import { useState, useMemo } from 'react';
|
||||
import { Card, Title, Text, Stack, Badge, Group, Select, NumberInput } from '@repo/ui/components';
|
||||
import { AgGridProvider, DataGrid } from '@repo/ui/ag-grid';
|
||||
import type { ColDef, ValueFormatterParams } from '@repo/ui/ag-grid';
|
||||
|
||||
/* ─── Sample Data ──────────────────────────────────────────────── */
|
||||
|
||||
interface OrderRow {
|
||||
id: string;
|
||||
orderCode: string;
|
||||
customer: string;
|
||||
email: string;
|
||||
product: string;
|
||||
quantity: number;
|
||||
unitPrice: number;
|
||||
total: number;
|
||||
status: 'Draft' | 'Pending' | 'Processing' | 'Shipped' | 'Delivered' | 'Cancelled';
|
||||
createdAt: string;
|
||||
region: string;
|
||||
}
|
||||
|
||||
const SAMPLE_DATA: OrderRow[] = [
|
||||
{ id: '1', orderCode: 'ORD-2026-001', customer: 'Acme Corp', email: 'acme@example.com', product: 'Widget A', quantity: 120, unitPrice: 24.5, total: 2940, status: 'Delivered', createdAt: '2026-01-15', region: 'North America' },
|
||||
{ id: '2', orderCode: 'ORD-2026-002', customer: 'Globex Inc', email: 'info@globex.com', product: 'Widget B', quantity: 50, unitPrice: 89.99, total: 4499.5, status: 'Shipped', createdAt: '2026-02-03', region: 'Europe' },
|
||||
{ id: '3', orderCode: 'ORD-2026-003', customer: 'Initech', email: 'orders@initech.com', product: 'Gadget Pro', quantity: 200, unitPrice: 15.0, total: 3000, status: 'Processing', createdAt: '2026-03-21', region: 'Asia Pacific' },
|
||||
{ id: '4', orderCode: 'ORD-2026-004', customer: 'Umbrella Ltd', email: 'sales@umbrella.co', product: 'Widget A', quantity: 75, unitPrice: 24.5, total: 1837.5, status: 'Pending', createdAt: '2026-04-10', region: 'Europe' },
|
||||
{ id: '5', orderCode: 'ORD-2026-005', customer: 'Stark Industries', email: 'tony@stark.io', product: 'Gadget Elite', quantity: 10, unitPrice: 499.0, total: 4990, status: 'Delivered', createdAt: '2026-04-18', region: 'North America' },
|
||||
{ id: '6', orderCode: 'ORD-2026-006', customer: 'Wayne Enterprises', email: 'bruce@wayne.com', product: 'Widget C', quantity: 300, unitPrice: 12.75, total: 3825, status: 'Draft', createdAt: '2026-05-02', region: 'North America' },
|
||||
{ id: '7', orderCode: 'ORD-2026-007', customer: 'Cyberdyne', email: 'info@cyberdyne.jp', product: 'Gadget Pro', quantity: 150, unitPrice: 15.0, total: 2250, status: 'Cancelled', createdAt: '2026-05-15', region: 'Asia Pacific' },
|
||||
{ id: '8', orderCode: 'ORD-2026-008', customer: 'Oscorp', email: 'orders@oscorp.com', product: 'Widget B', quantity: 90, unitPrice: 89.99, total: 8099.1, status: 'Shipped', createdAt: '2026-06-01', region: 'North America' },
|
||||
{ id: '9', orderCode: 'ORD-2026-009', customer: 'LexCorp', email: 'lex@lexcorp.com', product: 'Gadget Elite', quantity: 5, unitPrice: 499.0, total: 2495, status: 'Processing', createdAt: '2026-06-12', region: 'Europe' },
|
||||
{ id: '10', orderCode: 'ORD-2026-010', customer: 'Pied Piper', email: 'richard@piedpiper.io', product: 'Widget A', quantity: 500, unitPrice: 24.5, total: 12250, status: 'Pending', createdAt: '2026-06-20', region: 'North America' },
|
||||
{ id: '11', orderCode: 'ORD-2026-011', customer: 'Hooli', email: 'gavin@hooli.com', product: 'Gadget Pro', quantity: 1000, unitPrice: 15.0, total: 15000, status: 'Delivered', createdAt: '2026-07-01', region: 'North America' },
|
||||
{ id: '12', orderCode: 'ORD-2026-012', customer: 'Massive Dynamic', email: 'nina@massive.com', product: 'Widget C', quantity: 60, unitPrice: 12.75, total: 765, status: 'Shipped', createdAt: '2026-07-08', region: 'Europe' },
|
||||
];
|
||||
|
||||
/* ─── Status Badge Renderer ─────────────────────────────────────── */
|
||||
|
||||
const STATUS_COLORS: Record<OrderRow['status'], string> = {
|
||||
Draft: 'gray',
|
||||
Pending: 'warning',
|
||||
Processing: 'info',
|
||||
Shipped: 'brand',
|
||||
Delivered: 'success',
|
||||
Cancelled: 'error',
|
||||
};
|
||||
|
||||
function StatusCellRenderer({ value }: { value: OrderRow['status'] }) {
|
||||
return (
|
||||
<Badge size="sm" variant="light" color={STATUS_COLORS[value] || 'gray'}>
|
||||
{value}
|
||||
</Badge>
|
||||
);
|
||||
}
|
||||
|
||||
/* ─── Currency Formatter ────────────────────────────────────────── */
|
||||
|
||||
function currencyFormatter(params: ValueFormatterParams): string {
|
||||
if (params.value == null) return '-';
|
||||
return new Intl.NumberFormat('en-US', {
|
||||
style: 'currency',
|
||||
currency: 'USD',
|
||||
minimumFractionDigits: 2,
|
||||
}).format(params.value);
|
||||
}
|
||||
|
||||
/* ─── Showcase Component ────────────────────────────────────────── */
|
||||
|
||||
export default function AgGridShowcase() {
|
||||
const [gridHeight, setGridHeight] = useState(460);
|
||||
const [rowModelType, setRowModelType] = useState<string>('clientSide');
|
||||
|
||||
const defaultColDef = useMemo<ColDef>(
|
||||
() => ({
|
||||
flex: 1,
|
||||
minWidth: 100,
|
||||
sortable: true,
|
||||
filter: true,
|
||||
resizable: true,
|
||||
}),
|
||||
[],
|
||||
);
|
||||
|
||||
const columnDefs = useMemo<ColDef<OrderRow>[]>(
|
||||
() => [
|
||||
{
|
||||
field: 'orderCode',
|
||||
headerName: 'Order Code',
|
||||
pinned: 'left',
|
||||
minWidth: 140,
|
||||
filter: 'agTextColumnFilter',
|
||||
},
|
||||
{
|
||||
field: 'status',
|
||||
headerName: 'Status',
|
||||
minWidth: 120,
|
||||
cellRenderer: StatusCellRenderer,
|
||||
filter: 'agSetColumnFilter',
|
||||
},
|
||||
{
|
||||
field: 'customer',
|
||||
headerName: 'Customer',
|
||||
minWidth: 160,
|
||||
filter: 'agTextColumnFilter',
|
||||
},
|
||||
{
|
||||
field: 'email',
|
||||
headerName: 'Email',
|
||||
minWidth: 180,
|
||||
filter: 'agTextColumnFilter',
|
||||
},
|
||||
{
|
||||
field: 'product',
|
||||
headerName: 'Product',
|
||||
minWidth: 130,
|
||||
filter: 'agSetColumnFilter',
|
||||
},
|
||||
{
|
||||
field: 'region',
|
||||
headerName: 'Region',
|
||||
minWidth: 140,
|
||||
filter: 'agSetColumnFilter',
|
||||
enableRowGroup: true,
|
||||
},
|
||||
{
|
||||
field: 'quantity',
|
||||
headerName: 'Qty',
|
||||
minWidth: 80,
|
||||
filter: 'agNumberColumnFilter',
|
||||
type: 'numericColumn',
|
||||
},
|
||||
{
|
||||
field: 'unitPrice',
|
||||
headerName: 'Unit Price',
|
||||
minWidth: 110,
|
||||
filter: 'agNumberColumnFilter',
|
||||
type: 'numericColumn',
|
||||
valueFormatter: currencyFormatter,
|
||||
},
|
||||
{
|
||||
field: 'total',
|
||||
headerName: 'Total',
|
||||
minWidth: 120,
|
||||
filter: 'agNumberColumnFilter',
|
||||
type: 'numericColumn',
|
||||
valueFormatter: currencyFormatter,
|
||||
},
|
||||
{
|
||||
field: 'createdAt',
|
||||
headerName: 'Created Date',
|
||||
minWidth: 130,
|
||||
filter: 'agDateColumnFilter',
|
||||
},
|
||||
],
|
||||
[],
|
||||
);
|
||||
|
||||
return (
|
||||
<AgGridProvider bypassLicense>
|
||||
<Stack gap="xl">
|
||||
{/* ── Info Card ────────────────────────────────── */}
|
||||
<Card withBorder shadow="sm" radius="md" p="md">
|
||||
<Title order={4} mb="xs">
|
||||
AG Grid Enterprise — Mantine Integration
|
||||
</Title>
|
||||
<Text size="sm" c="dimmed" mb="md">
|
||||
This showcase demonstrates AG Grid integrated with Mantine's theme system. The grid
|
||||
automatically adapts to dark/light mode changes, inherits brand colors, typography, and
|
||||
border radius from the Mantine theme — all via AG Grid's built-in CSS variable system with
|
||||
zero custom stylesheets. Toggle the color scheme in the Theme Controls above to see it in
|
||||
action.
|
||||
</Text>
|
||||
<Group>
|
||||
<NumberInput
|
||||
label="Grid Height (px)"
|
||||
value={gridHeight}
|
||||
onChange={(val) => setGridHeight(Number(val) || 400)}
|
||||
min={300}
|
||||
max={800}
|
||||
step={50}
|
||||
w={180}
|
||||
/>
|
||||
<Select
|
||||
label="Row Model"
|
||||
value={rowModelType}
|
||||
onChange={(val) => setRowModelType(val || 'clientSide')}
|
||||
data={[
|
||||
{ value: 'clientSide', label: 'Client Side' },
|
||||
{ value: 'serverSide', label: 'Server Side (no datasource)' },
|
||||
]}
|
||||
w={260}
|
||||
/>
|
||||
</Group>
|
||||
</Card>
|
||||
|
||||
{/* ── AG Grid ──────────────────────────────────── */}
|
||||
<Card withBorder shadow="sm" radius="md" p="md">
|
||||
<Title order={4} mb="md">
|
||||
Orders Data Grid
|
||||
</Title>
|
||||
<div style={{ height: gridHeight }}>
|
||||
<DataGrid<OrderRow>
|
||||
rowData={SAMPLE_DATA}
|
||||
columnDefs={columnDefs}
|
||||
defaultColDef={defaultColDef}
|
||||
rowSelection={{ mode: 'multiRow', checkboxes: true }}
|
||||
pagination
|
||||
paginationPageSize={10}
|
||||
animateRows
|
||||
enableCellTextSelection
|
||||
suppressCopyRowsToClipboard
|
||||
/>
|
||||
</div>
|
||||
</Card>
|
||||
</Stack>
|
||||
</AgGridProvider>
|
||||
);
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import { lazy } from 'react';
|
||||
import { Layers } from 'lucide-react';
|
||||
import { Layers, Table2 } from 'lucide-react';
|
||||
|
||||
export const COMPONENTS_REGISTRY = {
|
||||
actionTools: {
|
||||
@@ -9,4 +9,12 @@ export const COMPONENTS_REGISTRY = {
|
||||
icon: Layers,
|
||||
component: lazy(() => import('./components/ActionToolsShowcase')),
|
||||
},
|
||||
agGrid: {
|
||||
id: 'ag-grid',
|
||||
name: 'AG Grid',
|
||||
description: 'Enterprise Data Grid with Mantine Theme Integration',
|
||||
icon: Table2,
|
||||
component: lazy(() => import('./components/AgGridShowcase')),
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user