@object-ui/plugin-aggrid
v3.3.0
Published
AG Grid data grid plugin for Object UI, powered by AG Grid Community
Readme
Plugin AgGrid - Lazy-Loaded AG Grid Data Grid
A lazy-loaded data grid component for Object UI based on AG Grid Community Edition.
Features
- Internal Lazy Loading: AG Grid libraries are loaded on-demand using
React.lazy()andSuspense - Zero Configuration: Just import the package and use
type: 'aggrid'in your schema - Automatic Registration: Components auto-register with the ComponentRegistry
- Skeleton Loading: Shows a skeleton while AG Grid loads
- Full AG Grid Features: Sorting, filtering, pagination, cell rendering, and more
- Cell & Row Editing: Inline editing using AG Grid's built-in editors
- CSV Export: Built-in data export functionality
- Event Callbacks: Handle cell clicks, selection changes, and value updates
- Status Bar: Display aggregations (count, sum, avg, min, max)
- Context Menu: Customizable right-click menu with built-in and custom actions
- Column Configuration: Global settings for resizable, sortable, and filterable columns
- Range Selection: Excel-like range selection support
- Multiple Themes: Support for Quartz, Alpine, Balham, and Material themes
- Customizable: Full access to AG Grid's GridOptions for advanced configuration
AG Grid Community vs Enterprise
This plugin uses AG Grid Community Edition which is free and open source. Most features (sorting, filtering, editing, CSV export, basic context menu) work with the Community edition. Some advanced features like integrated charting may require AG Grid Enterprise (commercial license). See AG Grid Pricing for details.
Installation
pnpm add @object-ui/plugin-aggrid ag-grid-community ag-grid-reactNote: ag-grid-community and ag-grid-react are peer dependencies and must be installed separately.
Next.js App Router Setup
If you're using Next.js with the App Router and dynamic imports, you may need to import AG Grid CSS in your root layout to ensure styles load correctly:
// app/layout.tsx
import 'ag-grid-community/styles/ag-grid.css';
import 'ag-grid-community/styles/ag-theme-quartz.css';
import 'ag-grid-community/styles/ag-theme-alpine.css';
import 'ag-grid-community/styles/ag-theme-balham.css';
import 'ag-grid-community/styles/ag-theme-material.css';This is necessary when the plugin is loaded dynamically (e.g., via React.lazy() or dynamic imports in client components), as Next.js may not properly process CSS imports from dynamically loaded modules.
Usage
Automatic Registration (Side-Effect Import)
// In your app entry point (e.g., App.tsx or main.tsx)
import '@object-ui/plugin-aggrid';
// Now you can use aggrid type in your schemas
const schema = {
type: 'aggrid',
rowData: [
{ make: 'Tesla', model: 'Model Y', price: 64950 },
{ make: 'Ford', model: 'F-Series', price: 33850 },
{ make: 'Toyota', model: 'Corolla', price: 29600 }
],
columnDefs: [
{ field: 'make', headerName: 'Make', sortable: true, filter: true },
{ field: 'model', headerName: 'Model', sortable: true, filter: true },
{ field: 'price', headerName: 'Price', sortable: true, filter: 'number' }
],
pagination: true,
paginationPageSize: 10,
theme: 'quartz',
height: 500
};Manual Integration
import { aggridComponents } from '@object-ui/plugin-aggrid';
import { ComponentRegistry } from '@object-ui/core';
// Manually register if needed
Object.entries(aggridComponents).forEach(([type, component]) => {
ComponentRegistry.register(type, component);
});TypeScript Support
The plugin exports TypeScript types for full type safety:
import type {
AgGridSchema,
SimpleColumnDef,
AgGridCallbacks,
ExportConfig,
StatusBarConfig,
ColumnConfig,
ContextMenuConfig
} from '@object-ui/plugin-aggrid';
const schema: AgGridSchema = {
type: 'aggrid',
rowData: [
{ make: 'Tesla', model: 'Model Y', price: 64950 }
],
columnDefs: [
{ field: 'make', headerName: 'Make', sortable: true, filter: true }
],
pagination: true,
theme: 'quartz',
editable: true,
exportConfig: {
enabled: true,
fileName: 'cars.csv'
},
columnConfig: {
resizable: true,
sortable: true,
filterable: true
},
contextMenu: {
enabled: true,
items: ['copy', 'export']
},
callbacks: {
onCellValueChanged: (event) => {
console.log('Changed:', event);
}
}
};Schema API
{
type: 'aggrid',
// Data
rowData?: any[], // Grid data (required)
columnDefs?: ColDef[], // Column definitions (required)
// Display
pagination?: boolean, // Enable pagination (default: false)
paginationPageSize?: number, // Rows per page (default: 10)
theme?: 'quartz' | 'alpine' | 'balham' | 'material', // Grid theme (default: 'quartz')
height?: number | string, // Grid height (default: 500)
rowSelection?: 'single' | 'multiple', // Row selection mode
domLayout?: 'normal' | 'autoHeight' | 'print', // Layout mode
animateRows?: boolean, // Animate row changes (default: true)
// Editing
editable?: boolean, // Enable cell editing (default: false)
editType?: 'fullRow', // Row editing mode
singleClickEdit?: boolean, // Start edit on single click (default: false)
stopEditingWhenCellsLoseFocus?: boolean, // Stop editing on blur (default: true)
// Export
exportConfig?: {
enabled?: boolean, // Show export button (default: false)
fileName?: string, // Export filename (default: 'export.csv')
skipColumnHeaders?: boolean, // Skip column headers in export (default: false)
onlySelected?: boolean, // Export only selected rows (default: false)
allColumns?: boolean // Export all columns (default: false)
},
// Status Bar
statusBar?: {
enabled?: boolean, // Show status bar (default: false)
aggregations?: ('sum' | 'avg' | 'count' | 'min' | 'max')[] // Aggregations to show
},
// Event Callbacks
callbacks?: {
onCellClicked?: (event) => void, // Cell click handler
onRowClicked?: (event) => void, // Row click handler
onSelectionChanged?: (event) => void, // Selection change handler
onCellValueChanged?: (event) => void, // Cell value change handler
onExport?: (data, format) => void, // Export handler (CSV only)
onContextMenuAction?: (action, rowData) => void // Context menu action handler
},
// Column Configuration
columnConfig?: {
resizable?: boolean, // Make all columns resizable
sortable?: boolean, // Make all columns sortable
filterable?: boolean, // Make all columns filterable
},
enableRangeSelection?: boolean, // Enable Excel-like range selection (default: false)
enableCharts?: boolean, // Enable integrated charts (requires Enterprise, default: false)
// Context Menu
contextMenu?: {
enabled?: boolean, // Enable context menu (default: false)
items?: string[], // Menu items: 'copy', 'export', 'autoSizeAll', etc.
customItems?: Array<{ // Custom menu items
name: string,
action: string,
icon?: string,
disabled?: boolean
}>
},
// Advanced
gridOptions?: GridOptions, // Advanced AG Grid options
className?: string // Tailwind classes
}Column Definition Examples
Basic Column
{
field: 'name',
headerName: 'Name',
sortable: true,
filter: true
}Numeric Column with Formatter
{
field: 'price',
headerName: 'Price',
sortable: true,
filter: 'number',
valueFormatter: (params) => '$' + params.value.toLocaleString()
}Column with Custom Cell Renderer
{
field: 'status',
headerName: 'Status',
cellRenderer: (params) => {
return params.value === 'active'
? '<span class="text-green-500">✓ Active</span>'
: '<span class="text-red-500">✗ Inactive</span>';
}
}Themes
AG Grid comes with four built-in themes:
- Quartz (default): Modern, clean design
- Alpine: Traditional data grid appearance
- Balham: Professional business look
- Material: Google Material Design inspired
const schema = {
type: 'aggrid',
theme: 'alpine', // or 'quartz', 'balham', 'material'
// ... other props
};Cell & Row Editing
Enable inline editing of cells:
const schema = {
type: 'aggrid',
rowData: [...],
columnDefs: [
{ field: 'name', headerName: 'Name', editable: true },
{ field: 'price', headerName: 'Price', editable: true },
{ field: 'status', headerName: 'Status', editable: false }
],
editable: true, // Enable editing globally
singleClickEdit: false, // Double-click to edit (default)
callbacks: {
onCellValueChanged: (event) => {
console.log('Cell changed:', event.data, event.colDef.field, event.newValue);
// Save to backend here
}
}
};CSV Export
Enable data export with a built-in button:
const schema = {
type: 'aggrid',
rowData: [...],
columnDefs: [...],
exportConfig: {
enabled: true, // Show export button
fileName: 'my-data.csv', // Custom filename
onlySelected: false, // Export all rows (or only selected)
allColumns: true // Export all columns
},
callbacks: {
onExport: (data, format) => {
console.log(`Exporting ${data.length} rows as ${format}`);
// Track export event or custom processing
}
}
};Status Bar & Aggregations
Display summary statistics at the bottom of the grid:
const schema = {
type: 'aggrid',
rowData: [...],
columnDefs: [...],
statusBar: {
enabled: true,
aggregations: ['count', 'sum', 'avg', 'min', 'max']
}
};Event Callbacks
Handle user interactions with the grid:
const schema = {
type: 'aggrid',
rowData: [...],
columnDefs: [...],
rowSelection: 'multiple',
callbacks: {
onRowClicked: (event) => {
console.log('Row clicked:', event.data);
},
onCellClicked: (event) => {
console.log('Cell clicked:', event.colDef.field, event.value);
},
onSelectionChanged: (event) => {
const selectedRows = event.api.getSelectedRows();
console.log('Selected rows:', selectedRows);
},
onCellValueChanged: (event) => {
console.log('Value changed from', event.oldValue, 'to', event.newValue);
// Save changes to backend
saveToBackend(event.data);
}
}
};Context Menu
Add a right-click context menu with custom actions:
const schema = {
type: 'aggrid',
rowData: [...],
columnDefs: [...],
contextMenu: {
enabled: true,
items: [
'copy', // Copy selected cells
'copyWithHeaders', // Copy with column headers
'separator',
'export', // Export to CSV
'autoSizeAll', // Auto-size all columns
'resetColumns' // Reset column state
],
customItems: [
{
name: 'Delete Row',
action: 'delete',
disabled: false
},
{
name: 'View Details',
action: 'view'
}
]
},
callbacks: {
onContextMenuAction: (action, rowData) => {
console.log(`Action: ${action}`, rowData);
// Handle custom menu actions
if (action === 'delete') {
deleteRow(rowData);
} else if (action === 'view') {
viewDetails(rowData);
}
}
}
};Column Configuration
Apply global settings to all columns:
const schema = {
type: 'aggrid',
rowData: [...],
columnDefs: [...],
columnConfig: {
resizable: true, // All columns resizable by default
sortable: true, // All columns sortable by default
filterable: true, // All columns filterable by default
},
enableRangeSelection: true // Excel-like range selection
};You can override these defaults on individual columns:
const schema = {
type: 'aggrid',
rowData: [...],
columnDefs: [
{ field: 'id', headerName: 'ID', sortable: false }, // Override: not sortable
{ field: 'name', headerName: 'Name' }, // Uses defaults
{ field: 'email', headerName: 'Email' } // Uses defaults
],
columnConfig: {
resizable: true,
sortable: true,
filterable: true
}
};Lazy Loading Architecture
The plugin uses a two-file pattern for optimal code splitting:
AgGridImpl.tsx: Contains the actual AG Grid imports (heavy ~200-300 KB)index.tsx: Entry point withReact.lazy()wrapper (light)
When bundled, Vite automatically creates separate chunks:
index.js(~200 bytes) - The entry pointAgGridImpl-xxx.js(~200-300 KB) - The lazy-loaded implementation
The AG Grid library is only downloaded when an aggrid component is actually rendered, not on initial page load.
Bundle Size Impact
By using lazy loading, the main application bundle stays lean:
- Without lazy loading: +200-300 KB on initial load
- With lazy loading: +0.19 KB on initial load, +200-300 KB only when grid is rendered
This results in significantly faster initial page loads for applications that don't use data grids on every page.
Advanced Usage
With GridOptions
const schema = {
type: 'aggrid',
rowData: [...],
columnDefs: [...],
gridOptions: {
suppressCellFocus: true,
enableCellTextSelection: true,
enableRangeSelection: true,
rowMultiSelectWithClick: true,
// Any AG Grid GridOptions property
}
};Auto Height Layout
const schema = {
type: 'aggrid',
rowData: [...],
columnDefs: [...],
domLayout: 'autoHeight', // Grid adjusts height to fit all rows
};Development
# Build the plugin
pnpm build
# Run tests
pnpm test
# Type check
pnpm type-check
# The package will generate proper ESM and UMD builds with lazy loading preservedLicense
MIT
Object AgGrid - Metadata-Driven AG Grid
The object-aggrid component extends the standard aggrid with metadata-driven capabilities using @objectstack/client. It automatically fetches object schemas and data, and generates appropriate column definitions based on field types.
Features
- Automatic Metadata Fetching: Retrieves object schema from ObjectStack backend
- Automatic Data Loading: Fetches data with pagination, filtering, and sorting
- Field Type Support: Supports all ObjectUI field types with appropriate formatters and renderers
- Inline Editing: Automatically saves changes to the backend
- Zero Column Configuration: Columns are generated from metadata
- Selective Field Display: Optionally show only specific fields
Installation
pnpm add @object-ui/plugin-aggrid @object-ui/data-objectstack ag-grid-community ag-grid-reactUsage
import '@object-ui/plugin-aggrid';
import { ObjectStackAdapter } from '@object-ui/data-objectstack';
// Create data source
const dataSource = new ObjectStackAdapter({
baseUrl: 'https://api.example.com',
token: 'your-api-token'
});
// Use in schema
const schema = {
type: 'object-aggrid',
objectName: 'contacts', // Object to fetch
dataSource: dataSource, // ObjectStack data source
pagination: true,
pageSize: 20,
theme: 'quartz',
height: 600
};Supported Field Types
The component automatically applies appropriate formatters and cell renderers for all field types:
- Text Types: text, textarea, markdown, html
- Numeric Types: number, currency (with locale formatting), percent
- Boolean: Displays ✓ Yes / ✗ No
- Date/Time: date, datetime, time (with locale formatting)
- Contact: email (clickable mailto), phone (clickable tel), url (clickable link)
- Select/Lookup: select, lookup, master_detail (shows labels)
- Visual: color (with color swatch), image, avatar, rating (stars)
- Advanced: formula, summary, auto_number, user, file
Schema API for Object AgGrid
{
type: 'object-aggrid',
// Required
objectName: string, // Name of the object to fetch
dataSource: DataSource, // ObjectStack data source instance
// Optional Field Configuration
fieldNames?: string[], // Show only these fields (default: all)
fields?: FieldMetadata[], // Override field metadata
// Query Parameters
filters?: Record<string, any>, // Query filters
sort?: Record<string, 'asc' | 'desc'>, // Sorting
pageSize?: number, // Rows per page (default: 10)
// Display Options (same as aggrid)
pagination?: boolean, // Enable pagination (default: true)
theme?: string, // Grid theme (default: 'quartz')
height?: number | string, // Grid height (default: 500)
// Editing
editable?: boolean, // Enable inline editing (auto-saves to backend)
// Export, Status Bar, Callbacks, etc. (same as aggrid)
exportConfig?: ExportConfig,
statusBar?: StatusBarConfig,
columnConfig?: ColumnConfig,
callbacks?: {
onDataLoaded?: (data: any[]) => void,
onDataError?: (error: Error) => void,
// ... other aggrid callbacks
}
}Examples
Basic Usage
const schema = {
type: 'object-aggrid',
objectName: 'users',
dataSource: myDataSource,
pagination: true,
pageSize: 25
};With Field Selection
const schema = {
type: 'object-aggrid',
objectName: 'contacts',
dataSource: myDataSource,
fieldNames: ['name', 'email', 'phone', 'company'],
pagination: true
};With Filters and Sorting
const schema = {
type: 'object-aggrid',
objectName: 'products',
dataSource: myDataSource,
filters: {
category: 'Electronics',
price: { $lt: 1000 }
},
sort: {
price: 'asc'
},
pagination: true
};Editable Grid with Auto-Save
const schema = {
type: 'object-aggrid',
objectName: 'tasks',
dataSource: myDataSource,
editable: true, // Enable editing
singleClickEdit: true, // Single-click to edit
callbacks: {
onCellValueChanged: (event) => {
// Changes are automatically saved to backend
console.log('Saved:', event.data);
}
}
};With Export
const schema = {
type: 'object-aggrid',
objectName: 'sales',
dataSource: myDataSource,
exportConfig: {
enabled: true,
fileName: 'sales-report.csv'
}
};Field Type Formatting Examples
Currency Field:
// Schema defines: { type: 'currency', currency: 'USD', precision: 2 }
// Renders as: $1,234.56Percent Field:
// Schema defines: { type: 'percent', precision: 1 }
// Renders as: 45.5%Email Field:
// Schema defines: { type: 'email' }
// Renders as: <a href="mailto:[email protected]">[email protected]</a>Rating Field:
// Schema defines: { type: 'rating', max: 5 }
// Renders as: ⭐⭐⭐⭐⭐Color Field:
// Schema defines: { type: 'color' }
// Renders as: [color swatch] #FF5733