Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions packages/plugin-aggrid/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# @object-ui/plugin-aggrid

## 0.3.0

### Minor Changes

- Initial release of AG Grid plugin
- Support for AG Grid Community Edition
- Lazy loading with React.Suspense
- Multiple theme support (Quartz, Alpine, Balham, Material)
- Full pagination, sorting, and filtering support
- TypeScript support with type definitions
- Automatic component registration
- Comprehensive test coverage
232 changes: 232 additions & 0 deletions packages/plugin-aggrid/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,232 @@
# 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()` and `Suspense`
- **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
- **Multiple Themes**: Support for Quartz, Alpine, Balham, and Material themes
- **Customizable**: Full access to AG Grid's GridOptions for advanced configuration

## Installation

```bash
pnpm add @object-ui/plugin-aggrid ag-grid-community ag-grid-react
```

Note: `ag-grid-community` and `ag-grid-react` are peer dependencies and must be installed separately.

## Usage

### Automatic Registration (Side-Effect Import)

```typescript
// 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

```typescript
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:

```typescript
import type { AgGridSchema, SimpleColumnDef } 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'
};
```

## Schema API

```typescript
{
type: 'aggrid',
rowData?: any[], // Grid data (required)
columnDefs?: ColDef[], // Column definitions (required)
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)
gridOptions?: GridOptions, // Advanced AG Grid options
className?: string // Tailwind classes
}
```

## Column Definition Examples

### Basic Column

```typescript
{
field: 'name',
headerName: 'Name',
sortable: true,
filter: true
}
```

### Numeric Column with Formatter

```typescript
{
field: 'price',
headerName: 'Price',
sortable: true,
filter: 'number',
valueFormatter: (params) => '$' + params.value.toLocaleString()
}
```

### Column with Custom Cell Renderer

```typescript
{
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

```typescript
const schema = {
type: 'aggrid',
theme: 'alpine', // or 'quartz', 'balham', 'material'
// ... other props
};
```

## Lazy Loading Architecture

The plugin uses a two-file pattern for optimal code splitting:

1. **`AgGridImpl.tsx`**: Contains the actual AG Grid imports (heavy ~200-300 KB)
2. **`index.tsx`**: Entry point with `React.lazy()` wrapper (light)

When bundled, Vite automatically creates separate chunks:
- `index.js` (~200 bytes) - The entry point
- `AgGridImpl-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

```typescript
Comment on lines +178 to +183
Copy link

Copilot AI Jan 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The valueFormatter example uses an arrow function, which cannot be serialized to JSON. This contradicts ObjectUI's JSON-first Schema-Driven UI principle where all component configurations should be representable as pure JSON. While AG Grid supports functions in columnDefs, in a truly schema-driven architecture, formatters should be referenced by string identifiers or use expression syntax instead of inline functions. Consider documenting how valueFormatter should be handled in a JSON schema context, or note this as a limitation that requires programmatic configuration.

Copilot uses AI. Check for mistakes.
const schema = {
type: 'aggrid',
rowData: [...],
columnDefs: [...],
gridOptions: {
suppressCellFocus: true,
enableCellTextSelection: true,
enableRangeSelection: true,
rowMultiSelectWithClick: true,
// Any AG Grid GridOptions property
}
};
```

### Auto Height Layout

```typescript
const schema = {
type: 'aggrid',
rowData: [...],
columnDefs: [...],
domLayout: 'autoHeight', // Grid adjusts height to fit all rows
};
```

## Development

```bash
# 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 preserved
```

## License

MIT

## Resources

- [AG Grid Community Documentation](https://www.ag-grid.com/documentation/)
- [AG Grid Column Definitions](https://www.ag-grid.com/documentation/javascript/column-definitions/)
- [AG Grid Grid Options](https://www.ag-grid.com/documentation/javascript/grid-options/)
55 changes: 55 additions & 0 deletions packages/plugin-aggrid/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
{
"name": "@object-ui/plugin-aggrid",
"version": "0.3.0",
"type": "module",
"license": "MIT",
"description": "AG Grid data grid plugin for Object UI, powered by AG Grid Community",
"homepage": "https://www.objectui.org",
"repository": {
"type": "git",
"url": "https://github.com/objectstack-ai/objectui.git",
"directory": "packages/plugin-aggrid"
},
"bugs": {
"url": "https://github.com/objectstack-ai/objectui/issues"
},
"main": "dist/index.umd.cjs",
"module": "dist/index.js",
"types": "dist/index.d.ts",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js",
"require": "./dist/index.umd.cjs"
}
},
"scripts": {
"build": "vite build",
"test": "vitest run",
"test:watch": "vitest",
"type-check": "tsc --noEmit",
"lint": "eslint ."
},
"dependencies": {
"@object-ui/components": "workspace:*",
"@object-ui/core": "workspace:*",
"@object-ui/react": "workspace:*",
"@object-ui/types": "workspace:*"
},
Comment on lines +33 to +38
Copy link

Copilot AI Jan 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

react and react-dom should not be listed in dependencies when they are already declared as peerDependencies. This creates bundling issues and version conflicts. Following the established pattern in plugin-charts and plugin-kanban, these packages should only be in peerDependencies, not in dependencies. Remove them from the dependencies section.

Copilot uses AI. Check for mistakes.
"peerDependencies": {
"react": "^18.0.0 || ^19.0.0",
"react-dom": "^18.0.0 || ^19.0.0",
"ag-grid-community": "^32.0.0",
"ag-grid-react": "^32.0.0"
},
"devDependencies": {
"@types/react": "^19.0.6",
Copy link

Copilot AI Jan 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The @types/react version is set to 19.0.6, which is inconsistent with other plugin packages (plugin-charts and plugin-kanban use ^18.3.12). While React 19 is in the peerDependencies as an option (^18.0.0 || ^19.0.0), the devDependencies should align with the established pattern for consistency. Consider using ^18.3.12 or updating all plugins consistently to use React 19 types.

Copilot uses AI. Check for mistakes.
"@types/react-dom": "^19.0.3",
Copy link

Copilot AI Jan 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The @types/react-dom version is set to 19.0.3, which is inconsistent with other plugin packages (plugin-charts and plugin-kanban use ^18.3.1). While React 19 is supported in peerDependencies, the devDependencies should align with the established pattern for consistency. Consider using ^18.3.1 or updating all plugins consistently to use React 19 types.

Copilot uses AI. Check for mistakes.
"@vitejs/plugin-react": "^4.2.1",
"ag-grid-community": "^32.3.4",
"ag-grid-react": "^32.3.4",
"typescript": "^5.9.3",
"vite": "^7.3.1",
"vite-plugin-dts": "^4.5.4"
}
}
95 changes: 95 additions & 0 deletions packages/plugin-aggrid/src/AgGridImpl.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/**
* ObjectUI
* Copyright (c) 2024-present ObjectStack Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

import React, { useMemo } from 'react';
import { AgGridReact } from 'ag-grid-react';
import type { ColDef, GridOptions } from 'ag-grid-community';

// Import AG Grid CSS - These are required for AG Grid to render properly
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';

export interface AgGridImplProps {
rowData?: any[];
columnDefs?: ColDef[];
gridOptions?: GridOptions;
pagination?: boolean;
paginationPageSize?: number;
domLayout?: 'normal' | 'autoHeight' | 'print';
animateRows?: boolean;
rowSelection?: 'single' | 'multiple';
theme?: 'alpine' | 'balham' | 'material' | 'quartz';
height?: number | string;
className?: string;
}

/**
* AgGridImpl - The heavy implementation that imports AG Grid
* This component is lazy-loaded to avoid including AG Grid in the initial bundle
*/
export default function AgGridImpl({
rowData = [],
columnDefs = [],
gridOptions = {},
pagination = false,
paginationPageSize = 10,
domLayout = 'normal',
animateRows = true,
rowSelection,
theme = 'quartz',
height = 500,
className = '',
}: AgGridImplProps) {
// Merge grid options with props
const mergedGridOptions: GridOptions = useMemo(() => ({
...gridOptions,
pagination,
paginationPageSize,
domLayout,
animateRows,
rowSelection,
// Default options for better UX
suppressCellFocus: true,
enableCellTextSelection: true,
ensureDomOrder: true,
}), [gridOptions, pagination, paginationPageSize, domLayout, animateRows, rowSelection]);

// Compute container style
const containerStyle = useMemo(() => ({
height: typeof height === 'number' ? `${height}px` : height,
width: '100%',
}), [height]);

// Determine theme class and build complete class list
const themeClass = `ag-theme-${theme}`;
const classList = [
themeClass,
'rounded-xl',
'border',
'border-border',
'overflow-hidden',
'shadow-lg',
className
].filter(Boolean).join(' ');

return (
<div
className={classList}
style={containerStyle}
>
<AgGridReact
rowData={rowData}
columnDefs={columnDefs}
gridOptions={mergedGridOptions}
/>
</div>
);
}
Loading
Loading