@object-ui/plugin-aggrid 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +28 -0
- package/LICENSE +21 -0
- package/README.md +504 -0
- package/dist/AgGridImpl-DKkq6v1B.js +171 -0
- package/dist/index-B6NPAFZx.js +504 -0
- package/dist/index.css +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +8 -0
- package/dist/index.umd.cjs +6 -0
- package/dist/src/AgGridImpl.d.ts +31 -0
- package/dist/src/index.d.ts +39 -0
- package/dist/src/types.d.ts +91 -0
- package/package.json +56 -0
- package/src/AgGridImpl.tsx +316 -0
- package/src/index.test.ts +139 -0
- package/src/index.tsx +305 -0
- package/src/types.ts +128 -0
- package/tsconfig.json +17 -0
- package/vite.config.ts +50 -0
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
(function(d,l){typeof exports=="object"&&typeof module<"u"?l(exports,require("react"),require("@object-ui/core"),require("@object-ui/components"),require("ag-grid-react")):typeof define=="function"&&define.amd?define(["exports","react","@object-ui/core","@object-ui/components","ag-grid-react"],l):(d=typeof globalThis<"u"?globalThis:d||self,l(d.ObjectUIPluginAgGrid={},d.React,d.ObjectUICore,d.ObjectUIComponents,d.AgGridReact))})(this,(function(d,l,ae,re,ne){"use strict";var F={exports:{}},P={};var $;function le(){if($)return P;$=1;var t=Symbol.for("react.transitional.element"),v=Symbol.for("react.fragment");function E(C,u,c){var R=null;if(c!==void 0&&(R=""+c),u.key!==void 0&&(R=""+u.key),"key"in u){c={};for(var y in u)y!=="key"&&(c[y]=u[y])}else c=u;return u=c.ref,{$$typeof:t,type:C,key:R,ref:u!==void 0?u:null,props:c}}return P.Fragment=v,P.jsx=E,P.jsxs=E,P}var O={};var ee;function oe(){return ee||(ee=1,process.env.NODE_ENV!=="production"&&(function(){function t(e){if(e==null)return null;if(typeof e=="function")return e.$$typeof===L?null:e.displayName||e.name||null;if(typeof e=="string")return e;switch(e){case _:return"Fragment";case m:return"Profiler";case s:return"StrictMode";case f:return"Suspense";case N:return"SuspenseList";case Y:return"Activity"}if(typeof e=="object")switch(typeof e.tag=="number"&&console.error("Received an unexpected object in getComponentNameFromType(). This is likely a bug in React. Please file an issue."),e.$$typeof){case p:return"Portal";case I:return e.displayName||"Context";case D:return(e._context.displayName||"Context")+".Consumer";case b:var a=e.render;return e=e.displayName,e||(e=a.displayName||a.name||"",e=e!==""?"ForwardRef("+e+")":"ForwardRef"),e;case V:return a=e.displayName||null,a!==null?a:t(e.type)||"Memo";case A:a=e._payload,e=e._init;try{return t(e(a))}catch{}}return null}function v(e){return""+e}function E(e){try{v(e);var a=!1}catch{a=!0}if(a){a=console;var r=a.error,o=typeof Symbol=="function"&&Symbol.toStringTag&&e[Symbol.toStringTag]||e.constructor.name||"Object";return r.call(a,"The provided key is an unsupported type %s. This value must be coerced to a string before using it here.",o),v(e)}}function C(e){if(e===_)return"<>";if(typeof e=="object"&&e!==null&&e.$$typeof===A)return"<...>";try{var a=t(e);return a?"<"+a+">":"<...>"}catch{return"<...>"}}function u(){var e=T.A;return e===null?null:e.getOwner()}function c(){return Error("react-stack-top-frame")}function R(e){if(z.call(e,"key")){var a=Object.getOwnPropertyDescriptor(e,"key").get;if(a&&a.isReactWarning)return!1}return e.key!==void 0}function y(e,a){function r(){U||(U=!0,console.error("%s: `key` is not a prop. Trying to access it will result in `undefined` being returned. If you need to access the same value within the child component, you should pass it as a different prop. (https://react.dev/link/special-props)",a))}r.isReactWarning=!0,Object.defineProperty(e,"key",{get:r,configurable:!0})}function q(){var e=t(this.type);return W[e]||(W[e]=!0,console.error("Accessing element.ref was removed in React 19. ref is now a regular prop. It will be removed from the JSX Element type in a future release.")),e=this.props.ref,e!==void 0?e:null}function k(e,a,r,o,B,Z){var i=r.ref;return e={$$typeof:j,type:e,key:a,props:r,_owner:o},(i!==void 0?i:null)!==null?Object.defineProperty(e,"ref",{enumerable:!1,get:q}):Object.defineProperty(e,"ref",{enumerable:!1,value:null}),e._store={},Object.defineProperty(e._store,"validated",{configurable:!1,enumerable:!1,writable:!0,value:0}),Object.defineProperty(e,"_debugInfo",{configurable:!1,enumerable:!1,writable:!0,value:null}),Object.defineProperty(e,"_debugStack",{configurable:!1,enumerable:!1,writable:!0,value:B}),Object.defineProperty(e,"_debugTask",{configurable:!1,enumerable:!1,writable:!0,value:Z}),Object.freeze&&(Object.freeze(e.props),Object.freeze(e)),e}function G(e,a,r,o,B,Z){var i=a.children;if(i!==void 0)if(o)if(J(i)){for(o=0;o<i.length;o++)S(i[o]);Object.freeze&&Object.freeze(i)}else console.error("React.jsx: Static children should always be an array. You are likely explicitly calling React.jsxs or React.jsxDEV. Use the Babel transform instead.");else S(i);if(z.call(a,"key")){i=t(e);var x=Object.keys(a).filter(function(fe){return fe!=="key"});o=0<x.length?"{key: someKey, "+x.join(": ..., ")+": ...}":"{key: someKey}",n[i+o]||(x=0<x.length?"{"+x.join(": ..., ")+": ...}":"{}",console.error(`A props object containing a "key" prop is being spread into JSX:
|
|
2
|
+
let props = %s;
|
|
3
|
+
<%s {...props} />
|
|
4
|
+
React keys must be passed directly to JSX without using spread:
|
|
5
|
+
let props = %s;
|
|
6
|
+
<%s key={someKey} {...props} />`,o,i,x,i),n[i+o]=!0)}if(i=null,r!==void 0&&(E(r),i=""+r),R(a)&&(E(a.key),i=""+a.key),"key"in a){r={};for(var K in a)K!=="key"&&(r[K]=a[K])}else r=a;return i&&y(r,typeof e=="function"?e.displayName||e.name||"Unknown":e),k(e,i,r,u(),B,Z)}function S(e){w(e)?e._store&&(e._store.validated=1):typeof e=="object"&&e!==null&&e.$$typeof===A&&(e._payload.status==="fulfilled"?w(e._payload.value)&&e._payload.value._store&&(e._payload.value._store.validated=1):e._store&&(e._store.validated=1))}function w(e){return typeof e=="object"&&e!==null&&e.$$typeof===j}var h=l,j=Symbol.for("react.transitional.element"),p=Symbol.for("react.portal"),_=Symbol.for("react.fragment"),s=Symbol.for("react.strict_mode"),m=Symbol.for("react.profiler"),D=Symbol.for("react.consumer"),I=Symbol.for("react.context"),b=Symbol.for("react.forward_ref"),f=Symbol.for("react.suspense"),N=Symbol.for("react.suspense_list"),V=Symbol.for("react.memo"),A=Symbol.for("react.lazy"),Y=Symbol.for("react.activity"),L=Symbol.for("react.client.reference"),T=h.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,z=Object.prototype.hasOwnProperty,J=Array.isArray,M=console.createTask?console.createTask:function(){return null};h={react_stack_bottom_frame:function(e){return e()}};var U,W={},Q=h.react_stack_bottom_frame.bind(h,c)(),H=M(C(c)),n={};O.Fragment=_,O.jsx=function(e,a,r){var o=1e4>T.recentlyCreatedOwnerStacks++;return G(e,a,r,!1,o?Error("react-stack-top-frame"):Q,o?M(C(e)):H)},O.jsxs=function(e,a,r){var o=1e4>T.recentlyCreatedOwnerStacks++;return G(e,a,r,!0,o?Error("react-stack-top-frame"):Q,o?M(C(e)):H)}})()),O}var te;function ie(){return te||(te=1,process.env.NODE_ENV==="production"?F.exports=le():F.exports=oe()),F.exports}var g=ie();const se=l.lazy(()=>Promise.resolve().then(()=>de)),X=({schema:t})=>g.jsx(l.Suspense,{fallback:g.jsx(re.Skeleton,{className:"w-full h-[500px]"}),children:g.jsx(se,{rowData:t.rowData,columnDefs:t.columnDefs,gridOptions:t.gridOptions,pagination:t.pagination,paginationPageSize:t.paginationPageSize,domLayout:t.domLayout,animateRows:t.animateRows,rowSelection:t.rowSelection,theme:t.theme,height:t.height,className:t.className,editable:t.editable,editType:t.editType,singleClickEdit:t.singleClickEdit,stopEditingWhenCellsLoseFocus:t.stopEditingWhenCellsLoseFocus,exportConfig:t.exportConfig,statusBar:t.statusBar,callbacks:t.callbacks,columnConfig:t.columnConfig,enableRangeSelection:t.enableRangeSelection,enableCharts:t.enableCharts,contextMenu:t.contextMenu})});ae.ComponentRegistry.register("aggrid",X,{label:"AG Grid",icon:"Table",category:"plugin",inputs:[{name:"rowData",type:"array",label:"Row Data",description:"Array of objects to display in the grid",required:!0},{name:"columnDefs",type:"array",label:"Column Definitions",description:"Array of column definitions",required:!0},{name:"pagination",type:"boolean",label:"Enable Pagination",defaultValue:!1},{name:"paginationPageSize",type:"number",label:"Page Size",defaultValue:10,description:"Number of rows per page when pagination is enabled"},{name:"theme",type:"enum",label:"Theme",enum:[{label:"Quartz",value:"quartz"},{label:"Alpine",value:"alpine"},{label:"Balham",value:"balham"},{label:"Material",value:"material"}],defaultValue:"quartz"},{name:"rowSelection",type:"enum",label:"Row Selection",enum:[{label:"None",value:void 0},{label:"Single",value:"single"},{label:"Multiple",value:"multiple"}],defaultValue:void 0,advanced:!0},{name:"domLayout",type:"enum",label:"DOM Layout",enum:[{label:"Normal",value:"normal"},{label:"Auto Height",value:"autoHeight"},{label:"Print",value:"print"}],defaultValue:"normal",advanced:!0},{name:"animateRows",type:"boolean",label:"Animate Rows",defaultValue:!0,advanced:!0},{name:"height",type:"number",label:"Height (px)",defaultValue:500},{name:"editable",type:"boolean",label:"Enable Editing",defaultValue:!1,description:"Allow cells to be edited inline",advanced:!0},{name:"singleClickEdit",type:"boolean",label:"Single Click Edit",defaultValue:!1,description:"Start editing on single click instead of double click",advanced:!0},{name:"exportConfig",type:"code",label:"Export Config (JSON)",description:'Configure CSV export: { enabled: true, fileName: "data.csv" }',advanced:!0},{name:"statusBar",type:"code",label:"Status Bar Config (JSON)",description:'Configure status bar: { enabled: true, aggregations: ["count", "sum", "avg"] }',advanced:!0},{name:"callbacks",type:"code",label:"Event Callbacks (JSON)",description:"Event handlers for cell clicks, selection changes, etc.",advanced:!0},{name:"columnConfig",type:"code",label:"Column Config (JSON)",description:"Global column settings: { resizable: true, sortable: true, filterable: true }",advanced:!0},{name:"enableRangeSelection",type:"boolean",label:"Enable Range Selection",defaultValue:!1,description:"Allow selecting ranges of cells (like Excel)",advanced:!0},{name:"enableCharts",type:"boolean",label:"Enable Charts",defaultValue:!1,description:"Enable integrated charting (requires AG Grid Enterprise)",advanced:!0},{name:"contextMenu",type:"code",label:"Context Menu Config (JSON)",description:'Configure right-click menu: { enabled: true, items: ["copy", "export"] }',advanced:!0},{name:"gridOptions",type:"code",label:"Grid Options (JSON)",description:"Advanced AG Grid options",advanced:!0},{name:"className",type:"string",label:"CSS Class"}],defaultProps:{rowData:[{make:"Tesla",model:"Model Y",price:64950,electric:!0},{make:"Ford",model:"F-Series",price:33850,electric:!1},{make:"Toyota",model:"Corolla",price:29600,electric:!1},{make:"Mercedes",model:"EQA",price:48890,electric:!0},{make:"Fiat",model:"500",price:15774,electric:!1},{make:"Nissan",model:"Juke",price:20675,electric:!1},{make:"Vauxhall",model:"Corsa",price:18460,electric:!1},{make:"Volvo",model:"XC90",price:72835,electric:!1},{make:"Mercedes",model:"GLA",price:47825,electric:!1},{make:"Ford",model:"Puma",price:27420,electric:!1},{make:"Volkswagen",model:"Golf",price:28850,electric:!1},{make:"Kia",model:"Sportage",price:31095,electric:!1}],columnDefs:[{field:"make",headerName:"Make",sortable:!0,filter:!0},{field:"model",headerName:"Model",sortable:!0,filter:!0},{field:"price",headerName:"Price",sortable:!0,filter:"number",valueFormatter:t=>t.value!=null?"$"+t.value.toLocaleString():""},{field:"electric",headerName:"Electric",sortable:!0,filter:!0,cellRenderer:t=>t.value===!0?"⚡ Yes":"No"}],pagination:!0,paginationPageSize:10,theme:"quartz",height:500,animateRows:!0,domLayout:"normal"}});const ue={aggrid:X};function ce({rowData:t=[],columnDefs:v=[],gridOptions:E={},pagination:C=!1,paginationPageSize:u=10,domLayout:c="normal",animateRows:R=!0,rowSelection:y,theme:q="quartz",height:k=500,className:G="",editable:S=!1,editType:w,singleClickEdit:h=!1,stopEditingWhenCellsLoseFocus:j=!0,exportConfig:p,statusBar:_,callbacks:s,columnConfig:m,enableRangeSelection:D=!1,enableCharts:I=!1,contextMenu:b}){const f=l.useRef(null),N=l.useMemo(()=>{if(!_?.enabled)return;const n=_.aggregations||["count","sum","avg"],e=[];return n.includes("count")&&e.push({statusPanel:"agAggregationComponent",statusPanelParams:{aggFuncs:["count"]}}),n.includes("sum")&&e.push({statusPanel:"agAggregationComponent",statusPanelParams:{aggFuncs:["sum"]}}),n.includes("avg")&&e.push({statusPanel:"agAggregationComponent",statusPanelParams:{aggFuncs:["avg"]}}),n.includes("min")&&e.push({statusPanel:"agAggregationComponent",statusPanelParams:{aggFuncs:["min"]}}),n.includes("max")&&e.push({statusPanel:"agAggregationComponent",statusPanelParams:{aggFuncs:["max"]}}),e},[_]),V=l.useCallback(()=>{if(!f.current?.api)return;const n={fileName:p?.fileName||"export.csv",skipColumnHeaders:p?.skipColumnHeaders||!1,allColumns:p?.allColumns||!1,onlySelected:p?.onlySelected||!1};if(f.current.api.exportDataAsCsv(n),s?.onExport){const e=p?.onlySelected?f.current.api.getSelectedRows():t;s.onExport(e||[],"csv")}},[p,s,t]),A=l.useCallback(n=>{if(!b?.enabled)return[];const e=[];return(b.items||["copy","copyWithHeaders","separator","export"]).forEach(r=>{r==="export"?e.push({name:"Export CSV",icon:"<span>📥</span>",action:()=>V()}):r==="autoSizeAll"?e.push({name:"Auto-size All Columns",action:()=>{f.current?.api&&f.current.api.autoSizeAllColumns()}}):r==="resetColumns"?e.push({name:"Reset Columns",action:()=>{f.current?.api&&f.current.api.resetColumnState()}}):e.push(r)}),b.customItems&&(e.length>0&&e.push("separator"),b.customItems.forEach(r=>{e.push({name:r.name,disabled:r.disabled,action:()=>{s?.onContextMenuAction&&s.onContextMenuAction(r.action,n.node?.data)}})})),e},[b,V,s]),Y=l.useCallback(n=>{s?.onCellClicked?.(n)},[s]),L=l.useCallback(n=>{s?.onRowClicked?.(n)},[s]),T=l.useCallback(n=>{s?.onSelectionChanged?.(n)},[s]),z=l.useCallback(n=>{s?.onCellValueChanged?.(n)},[s]),J=l.useCallback(n=>{f.current=n},[]),M=l.useMemo(()=>v?v.map(n=>{const e={...n};return S&&n.editable!==!1&&(e.editable=!0),m&&(m.resizable!==void 0&&n.resizable===void 0&&(e.resizable=m.resizable),m.sortable!==void 0&&n.sortable===void 0&&(e.sortable=m.sortable),m.filterable!==void 0&&n.filter===void 0&&(e.filter=m.filterable)),e}):[],[v,S,m]),U=l.useMemo(()=>({...E,pagination:C,paginationPageSize:u,domLayout:c,animateRows:R,rowSelection:y,editType:w,singleClickEdit:h,stopEditingWhenCellsLoseFocus:j,statusBar:N?{statusPanels:N}:void 0,enableRangeSelection:D,enableCharts:I,getContextMenuItems:b?.enabled?A:void 0,suppressCellFocus:!S,enableCellTextSelection:!0,ensureDomOrder:!0,onCellClicked:Y,onRowClicked:L,onSelectionChanged:T,onCellValueChanged:z,onGridReady:J}),[E,C,u,c,R,y,w,h,j,N,D,I,b,A,S,Y,L,T,z,J]),W=l.useMemo(()=>({height:typeof k=="number"?`${k}px`:k,width:"100%"}),[k]),H=[`ag-theme-${q}`,"rounded-xl","border","border-border","overflow-hidden","shadow-lg",G].filter(Boolean).join(" ");return g.jsxs("div",{className:"ag-grid-container",children:[p?.enabled&&g.jsx("div",{className:"mb-2 flex gap-2",children:g.jsx("button",{onClick:V,className:"px-3 py-1.5 text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 rounded-md transition-colors",children:"Export CSV"})}),g.jsx("div",{className:H,style:W,children:g.jsx(ne.AgGridReact,{ref:f,rowData:t,columnDefs:M,gridOptions:U})})]})}const de=Object.freeze(Object.defineProperty({__proto__:null,default:ce},Symbol.toStringTag,{value:"Module"}));d.AgGridRenderer=X,d.aggridComponents=ue,Object.defineProperty(d,Symbol.toStringTag,{value:"Module"})}));
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { ColDef, GridOptions } from 'ag-grid-community';
|
|
2
|
+
import { AgGridCallbacks, ExportConfig, StatusBarConfig, ColumnConfig, ContextMenuConfig } from './types';
|
|
3
|
+
export interface AgGridImplProps {
|
|
4
|
+
rowData?: any[];
|
|
5
|
+
columnDefs?: ColDef[];
|
|
6
|
+
gridOptions?: GridOptions;
|
|
7
|
+
pagination?: boolean;
|
|
8
|
+
paginationPageSize?: number;
|
|
9
|
+
domLayout?: 'normal' | 'autoHeight' | 'print';
|
|
10
|
+
animateRows?: boolean;
|
|
11
|
+
rowSelection?: 'single' | 'multiple';
|
|
12
|
+
theme?: 'alpine' | 'balham' | 'material' | 'quartz';
|
|
13
|
+
height?: number | string;
|
|
14
|
+
className?: string;
|
|
15
|
+
editable?: boolean;
|
|
16
|
+
editType?: 'fullRow';
|
|
17
|
+
singleClickEdit?: boolean;
|
|
18
|
+
stopEditingWhenCellsLoseFocus?: boolean;
|
|
19
|
+
exportConfig?: ExportConfig;
|
|
20
|
+
statusBar?: StatusBarConfig;
|
|
21
|
+
callbacks?: AgGridCallbacks;
|
|
22
|
+
columnConfig?: ColumnConfig;
|
|
23
|
+
enableRangeSelection?: boolean;
|
|
24
|
+
enableCharts?: boolean;
|
|
25
|
+
contextMenu?: ContextMenuConfig;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* AgGridImpl - The heavy implementation that imports AG Grid
|
|
29
|
+
* This component is lazy-loaded to avoid including AG Grid in the initial bundle
|
|
30
|
+
*/
|
|
31
|
+
export default function AgGridImpl({ rowData, columnDefs, gridOptions, pagination, paginationPageSize, domLayout, animateRows, rowSelection, theme, height, className, editable, editType, singleClickEdit, stopEditingWhenCellsLoseFocus, exportConfig, statusBar, callbacks, columnConfig, enableRangeSelection, enableCharts, contextMenu, }: AgGridImplProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { default as React } from 'react';
|
|
2
|
+
import { AgGridCallbacks, ExportConfig, StatusBarConfig, ColumnConfig, ContextMenuConfig } from './types';
|
|
3
|
+
export type { AgGridSchema, SimpleColumnDef, AgGridCallbacks, ExportConfig, StatusBarConfig, ColumnConfig, ContextMenuConfig } from './types';
|
|
4
|
+
export interface AgGridRendererProps {
|
|
5
|
+
schema: {
|
|
6
|
+
type: string;
|
|
7
|
+
id?: string;
|
|
8
|
+
className?: string;
|
|
9
|
+
rowData?: any[];
|
|
10
|
+
columnDefs?: any[];
|
|
11
|
+
gridOptions?: any;
|
|
12
|
+
pagination?: boolean;
|
|
13
|
+
paginationPageSize?: number;
|
|
14
|
+
domLayout?: 'normal' | 'autoHeight' | 'print';
|
|
15
|
+
animateRows?: boolean;
|
|
16
|
+
rowSelection?: 'single' | 'multiple';
|
|
17
|
+
theme?: 'alpine' | 'balham' | 'material' | 'quartz';
|
|
18
|
+
height?: number | string;
|
|
19
|
+
editable?: boolean;
|
|
20
|
+
editType?: 'fullRow';
|
|
21
|
+
singleClickEdit?: boolean;
|
|
22
|
+
stopEditingWhenCellsLoseFocus?: boolean;
|
|
23
|
+
exportConfig?: ExportConfig;
|
|
24
|
+
statusBar?: StatusBarConfig;
|
|
25
|
+
callbacks?: AgGridCallbacks;
|
|
26
|
+
columnConfig?: ColumnConfig;
|
|
27
|
+
enableRangeSelection?: boolean;
|
|
28
|
+
enableCharts?: boolean;
|
|
29
|
+
contextMenu?: ContextMenuConfig;
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* AgGridRenderer - The public API for the AG Grid component
|
|
34
|
+
* This wrapper handles lazy loading internally using React.Suspense
|
|
35
|
+
*/
|
|
36
|
+
export declare const AgGridRenderer: React.FC<AgGridRendererProps>;
|
|
37
|
+
export declare const aggridComponents: {
|
|
38
|
+
aggrid: React.FC<AgGridRendererProps>;
|
|
39
|
+
};
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { ColDef, GridOptions, CellClickedEvent, RowClickedEvent, SelectionChangedEvent, CellValueChangedEvent } from 'ag-grid-community';
|
|
2
|
+
/**
|
|
3
|
+
* Event callback types
|
|
4
|
+
*/
|
|
5
|
+
export interface AgGridCallbacks {
|
|
6
|
+
onCellClicked?: (event: CellClickedEvent) => void;
|
|
7
|
+
onRowClicked?: (event: RowClickedEvent) => void;
|
|
8
|
+
onSelectionChanged?: (event: SelectionChangedEvent) => void;
|
|
9
|
+
onCellValueChanged?: (event: CellValueChangedEvent) => void;
|
|
10
|
+
onExport?: (data: any[], format: 'csv') => void;
|
|
11
|
+
onContextMenuAction?: (action: string, rowData: any) => void;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Export configuration
|
|
15
|
+
*/
|
|
16
|
+
export interface ExportConfig {
|
|
17
|
+
enabled?: boolean;
|
|
18
|
+
fileName?: string;
|
|
19
|
+
skipColumnHeaders?: boolean;
|
|
20
|
+
allColumns?: boolean;
|
|
21
|
+
onlySelected?: boolean;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Status bar configuration
|
|
25
|
+
*/
|
|
26
|
+
export interface StatusBarConfig {
|
|
27
|
+
enabled?: boolean;
|
|
28
|
+
aggregations?: ('sum' | 'avg' | 'count' | 'min' | 'max')[];
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Column configuration enhancements
|
|
32
|
+
*/
|
|
33
|
+
export interface ColumnConfig {
|
|
34
|
+
resizable?: boolean;
|
|
35
|
+
sortable?: boolean;
|
|
36
|
+
filterable?: boolean;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Context menu configuration
|
|
40
|
+
*/
|
|
41
|
+
export interface ContextMenuConfig {
|
|
42
|
+
enabled?: boolean;
|
|
43
|
+
items?: ('copy' | 'copyWithHeaders' | 'paste' | 'separator' | 'export' | 'autoSizeAll' | 'resetColumns' | string)[];
|
|
44
|
+
customItems?: Array<{
|
|
45
|
+
name: string;
|
|
46
|
+
action: string;
|
|
47
|
+
disabled?: boolean;
|
|
48
|
+
}>;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* AG Grid schema for ObjectUI
|
|
52
|
+
*/
|
|
53
|
+
export interface AgGridSchema {
|
|
54
|
+
type: 'aggrid';
|
|
55
|
+
id?: string;
|
|
56
|
+
className?: string;
|
|
57
|
+
rowData?: any[];
|
|
58
|
+
columnDefs?: ColDef[];
|
|
59
|
+
gridOptions?: GridOptions;
|
|
60
|
+
pagination?: boolean;
|
|
61
|
+
paginationPageSize?: number;
|
|
62
|
+
domLayout?: 'normal' | 'autoHeight' | 'print';
|
|
63
|
+
animateRows?: boolean;
|
|
64
|
+
rowSelection?: 'single' | 'multiple';
|
|
65
|
+
editable?: boolean;
|
|
66
|
+
editType?: 'fullRow';
|
|
67
|
+
singleClickEdit?: boolean;
|
|
68
|
+
stopEditingWhenCellsLoseFocus?: boolean;
|
|
69
|
+
exportConfig?: ExportConfig;
|
|
70
|
+
statusBar?: StatusBarConfig;
|
|
71
|
+
columnConfig?: ColumnConfig;
|
|
72
|
+
enableRangeSelection?: boolean;
|
|
73
|
+
enableCharts?: boolean;
|
|
74
|
+
contextMenu?: ContextMenuConfig;
|
|
75
|
+
callbacks?: AgGridCallbacks;
|
|
76
|
+
theme?: 'alpine' | 'balham' | 'material' | 'quartz';
|
|
77
|
+
height?: number | string;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Column definition with simplified schema
|
|
81
|
+
*/
|
|
82
|
+
export interface SimpleColumnDef {
|
|
83
|
+
field: string;
|
|
84
|
+
headerName?: string;
|
|
85
|
+
width?: number;
|
|
86
|
+
sortable?: boolean;
|
|
87
|
+
filter?: boolean | 'text' | 'number' | 'date';
|
|
88
|
+
editable?: boolean;
|
|
89
|
+
cellRenderer?: string;
|
|
90
|
+
valueFormatter?: (params: any) => string;
|
|
91
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@object-ui/plugin-aggrid",
|
|
3
|
+
"version": "0.4.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"description": "AG Grid data grid plugin for Object UI, powered by AG Grid Community",
|
|
7
|
+
"homepage": "https://www.objectui.org",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "https://github.com/objectstack-ai/objectui.git",
|
|
11
|
+
"directory": "packages/plugin-aggrid"
|
|
12
|
+
},
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/objectstack-ai/objectui/issues"
|
|
15
|
+
},
|
|
16
|
+
"main": "dist/index.umd.cjs",
|
|
17
|
+
"module": "dist/index.js",
|
|
18
|
+
"types": "dist/index.d.ts",
|
|
19
|
+
"exports": {
|
|
20
|
+
".": {
|
|
21
|
+
"types": "./dist/index.d.ts",
|
|
22
|
+
"import": "./dist/index.js",
|
|
23
|
+
"require": "./dist/index.umd.cjs"
|
|
24
|
+
},
|
|
25
|
+
"./style.css": "./dist/index.css"
|
|
26
|
+
},
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"@object-ui/components": "0.3.0",
|
|
29
|
+
"@object-ui/core": "0.3.0",
|
|
30
|
+
"@object-ui/react": "0.3.0",
|
|
31
|
+
"@object-ui/types": "0.3.0"
|
|
32
|
+
},
|
|
33
|
+
"peerDependencies": {
|
|
34
|
+
"react": "^18.0.0 || ^19.0.0",
|
|
35
|
+
"react-dom": "^18.0.0 || ^19.0.0",
|
|
36
|
+
"ag-grid-community": "^32.0.0",
|
|
37
|
+
"ag-grid-react": "^32.0.0"
|
|
38
|
+
},
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"@types/react": "^19.2.9",
|
|
41
|
+
"@types/react-dom": "^19.2.3",
|
|
42
|
+
"@vitejs/plugin-react": "^4.2.1",
|
|
43
|
+
"ag-grid-community": "^35.0.1",
|
|
44
|
+
"ag-grid-react": "^32.3.4",
|
|
45
|
+
"typescript": "^5.9.3",
|
|
46
|
+
"vite": "^7.3.1",
|
|
47
|
+
"vite-plugin-dts": "^4.5.4"
|
|
48
|
+
},
|
|
49
|
+
"scripts": {
|
|
50
|
+
"build": "vite build",
|
|
51
|
+
"test": "vitest run",
|
|
52
|
+
"test:watch": "vitest",
|
|
53
|
+
"type-check": "tsc --noEmit",
|
|
54
|
+
"lint": "eslint ."
|
|
55
|
+
}
|
|
56
|
+
}
|
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ObjectUI
|
|
3
|
+
* Copyright (c) 2024-present ObjectStack Inc.
|
|
4
|
+
*
|
|
5
|
+
* This source code is licensed under the MIT license found in the
|
|
6
|
+
* LICENSE file in the root directory of this source tree.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import React, { useMemo, useRef, useCallback } from 'react';
|
|
10
|
+
import { AgGridReact } from 'ag-grid-react';
|
|
11
|
+
import type { ColDef, GridOptions, GridReadyEvent, CellClickedEvent, RowClickedEvent, SelectionChangedEvent, CellValueChangedEvent, StatusPanelDef, GetContextMenuItemsParams, MenuItemDef } from 'ag-grid-community';
|
|
12
|
+
import type { AgGridCallbacks, ExportConfig, StatusBarConfig, ColumnConfig, ContextMenuConfig } from './types';
|
|
13
|
+
|
|
14
|
+
export interface AgGridImplProps {
|
|
15
|
+
rowData?: any[];
|
|
16
|
+
columnDefs?: ColDef[];
|
|
17
|
+
gridOptions?: GridOptions;
|
|
18
|
+
pagination?: boolean;
|
|
19
|
+
paginationPageSize?: number;
|
|
20
|
+
domLayout?: 'normal' | 'autoHeight' | 'print';
|
|
21
|
+
animateRows?: boolean;
|
|
22
|
+
rowSelection?: 'single' | 'multiple';
|
|
23
|
+
theme?: 'alpine' | 'balham' | 'material' | 'quartz';
|
|
24
|
+
height?: number | string;
|
|
25
|
+
className?: string;
|
|
26
|
+
editable?: boolean;
|
|
27
|
+
editType?: 'fullRow';
|
|
28
|
+
singleClickEdit?: boolean;
|
|
29
|
+
stopEditingWhenCellsLoseFocus?: boolean;
|
|
30
|
+
exportConfig?: ExportConfig;
|
|
31
|
+
statusBar?: StatusBarConfig;
|
|
32
|
+
callbacks?: AgGridCallbacks;
|
|
33
|
+
columnConfig?: ColumnConfig;
|
|
34
|
+
enableRangeSelection?: boolean;
|
|
35
|
+
enableCharts?: boolean;
|
|
36
|
+
contextMenu?: ContextMenuConfig;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* AgGridImpl - The heavy implementation that imports AG Grid
|
|
41
|
+
* This component is lazy-loaded to avoid including AG Grid in the initial bundle
|
|
42
|
+
*/
|
|
43
|
+
export default function AgGridImpl({
|
|
44
|
+
rowData = [],
|
|
45
|
+
columnDefs = [],
|
|
46
|
+
gridOptions = {},
|
|
47
|
+
pagination = false,
|
|
48
|
+
paginationPageSize = 10,
|
|
49
|
+
domLayout = 'normal',
|
|
50
|
+
animateRows = true,
|
|
51
|
+
rowSelection,
|
|
52
|
+
theme = 'quartz',
|
|
53
|
+
height = 500,
|
|
54
|
+
className = '',
|
|
55
|
+
editable = false,
|
|
56
|
+
editType,
|
|
57
|
+
singleClickEdit = false,
|
|
58
|
+
stopEditingWhenCellsLoseFocus = true,
|
|
59
|
+
exportConfig,
|
|
60
|
+
statusBar,
|
|
61
|
+
callbacks,
|
|
62
|
+
columnConfig,
|
|
63
|
+
enableRangeSelection = false,
|
|
64
|
+
enableCharts = false,
|
|
65
|
+
contextMenu,
|
|
66
|
+
}: AgGridImplProps) {
|
|
67
|
+
const gridRef = useRef<any>(null);
|
|
68
|
+
|
|
69
|
+
// Build status bar panels
|
|
70
|
+
const statusPanels = useMemo((): StatusPanelDef[] | undefined => {
|
|
71
|
+
if (!statusBar?.enabled) return undefined;
|
|
72
|
+
|
|
73
|
+
const aggregations = statusBar.aggregations || ['count', 'sum', 'avg'];
|
|
74
|
+
const panels: StatusPanelDef[] = [];
|
|
75
|
+
|
|
76
|
+
if (aggregations.includes('count')) {
|
|
77
|
+
panels.push({ statusPanel: 'agAggregationComponent', statusPanelParams: { aggFuncs: ['count'] } });
|
|
78
|
+
}
|
|
79
|
+
if (aggregations.includes('sum')) {
|
|
80
|
+
panels.push({ statusPanel: 'agAggregationComponent', statusPanelParams: { aggFuncs: ['sum'] } });
|
|
81
|
+
}
|
|
82
|
+
if (aggregations.includes('avg')) {
|
|
83
|
+
panels.push({ statusPanel: 'agAggregationComponent', statusPanelParams: { aggFuncs: ['avg'] } });
|
|
84
|
+
}
|
|
85
|
+
if (aggregations.includes('min')) {
|
|
86
|
+
panels.push({ statusPanel: 'agAggregationComponent', statusPanelParams: { aggFuncs: ['min'] } });
|
|
87
|
+
}
|
|
88
|
+
if (aggregations.includes('max')) {
|
|
89
|
+
panels.push({ statusPanel: 'agAggregationComponent', statusPanelParams: { aggFuncs: ['max'] } });
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return panels;
|
|
93
|
+
}, [statusBar]);
|
|
94
|
+
|
|
95
|
+
// CSV Export handler
|
|
96
|
+
const handleExportCSV = useCallback(() => {
|
|
97
|
+
if (!gridRef.current?.api) return;
|
|
98
|
+
|
|
99
|
+
const params = {
|
|
100
|
+
fileName: exportConfig?.fileName || 'export.csv',
|
|
101
|
+
skipColumnHeaders: exportConfig?.skipColumnHeaders || false,
|
|
102
|
+
allColumns: exportConfig?.allColumns || false,
|
|
103
|
+
onlySelected: exportConfig?.onlySelected || false,
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
gridRef.current.api.exportDataAsCsv(params);
|
|
107
|
+
|
|
108
|
+
if (callbacks?.onExport) {
|
|
109
|
+
const data = exportConfig?.onlySelected
|
|
110
|
+
? gridRef.current.api.getSelectedRows()
|
|
111
|
+
: rowData;
|
|
112
|
+
callbacks.onExport(data || [], 'csv');
|
|
113
|
+
}
|
|
114
|
+
}, [exportConfig, callbacks, rowData]);
|
|
115
|
+
|
|
116
|
+
// Context Menu handler
|
|
117
|
+
const getContextMenuItems = useCallback((params: GetContextMenuItemsParams): (string | MenuItemDef)[] => {
|
|
118
|
+
if (!contextMenu?.enabled) return [];
|
|
119
|
+
|
|
120
|
+
const items: (string | MenuItemDef)[] = [];
|
|
121
|
+
const defaultItems = contextMenu.items || ['copy', 'copyWithHeaders', 'separator', 'export'];
|
|
122
|
+
|
|
123
|
+
defaultItems.forEach(item => {
|
|
124
|
+
if (item === 'export') {
|
|
125
|
+
items.push({
|
|
126
|
+
name: 'Export CSV',
|
|
127
|
+
icon: '<span>📥</span>',
|
|
128
|
+
action: () => handleExportCSV(),
|
|
129
|
+
});
|
|
130
|
+
} else if (item === 'autoSizeAll') {
|
|
131
|
+
items.push({
|
|
132
|
+
name: 'Auto-size All Columns',
|
|
133
|
+
action: () => {
|
|
134
|
+
if (gridRef.current?.api) {
|
|
135
|
+
gridRef.current.api.autoSizeAllColumns();
|
|
136
|
+
}
|
|
137
|
+
},
|
|
138
|
+
});
|
|
139
|
+
} else if (item === 'resetColumns') {
|
|
140
|
+
items.push({
|
|
141
|
+
name: 'Reset Columns',
|
|
142
|
+
action: () => {
|
|
143
|
+
if (gridRef.current?.api) {
|
|
144
|
+
gridRef.current.api.resetColumnState();
|
|
145
|
+
}
|
|
146
|
+
},
|
|
147
|
+
});
|
|
148
|
+
} else {
|
|
149
|
+
items.push(item);
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
// Add custom items
|
|
154
|
+
if (contextMenu.customItems) {
|
|
155
|
+
if (items.length > 0) {
|
|
156
|
+
items.push('separator');
|
|
157
|
+
}
|
|
158
|
+
contextMenu.customItems.forEach(customItem => {
|
|
159
|
+
items.push({
|
|
160
|
+
name: customItem.name,
|
|
161
|
+
disabled: customItem.disabled,
|
|
162
|
+
action: () => {
|
|
163
|
+
// Trigger dedicated context menu action callback
|
|
164
|
+
if (callbacks?.onContextMenuAction) {
|
|
165
|
+
callbacks.onContextMenuAction(customItem.action, params.node?.data);
|
|
166
|
+
}
|
|
167
|
+
},
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return items;
|
|
173
|
+
}, [contextMenu, handleExportCSV, callbacks]);
|
|
174
|
+
|
|
175
|
+
// Event handlers
|
|
176
|
+
const handleCellClicked = useCallback((event: CellClickedEvent) => {
|
|
177
|
+
callbacks?.onCellClicked?.(event);
|
|
178
|
+
}, [callbacks]);
|
|
179
|
+
|
|
180
|
+
const handleRowClicked = useCallback((event: RowClickedEvent) => {
|
|
181
|
+
callbacks?.onRowClicked?.(event);
|
|
182
|
+
}, [callbacks]);
|
|
183
|
+
|
|
184
|
+
const handleSelectionChanged = useCallback((event: SelectionChangedEvent) => {
|
|
185
|
+
callbacks?.onSelectionChanged?.(event);
|
|
186
|
+
}, [callbacks]);
|
|
187
|
+
|
|
188
|
+
const handleCellValueChanged = useCallback((event: CellValueChangedEvent) => {
|
|
189
|
+
callbacks?.onCellValueChanged?.(event);
|
|
190
|
+
}, [callbacks]);
|
|
191
|
+
|
|
192
|
+
const onGridReady = useCallback((params: GridReadyEvent) => {
|
|
193
|
+
gridRef.current = params;
|
|
194
|
+
}, []);
|
|
195
|
+
|
|
196
|
+
// Make columns editable if global editable flag is set
|
|
197
|
+
const processedColumnDefs = useMemo(() => {
|
|
198
|
+
if (!columnDefs) return [];
|
|
199
|
+
|
|
200
|
+
return columnDefs.map(col => {
|
|
201
|
+
const processed: ColDef = { ...col };
|
|
202
|
+
|
|
203
|
+
// Apply editable setting
|
|
204
|
+
if (editable && col.editable !== false) {
|
|
205
|
+
processed.editable = true;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// Apply column config defaults
|
|
209
|
+
if (columnConfig) {
|
|
210
|
+
if (columnConfig.resizable !== undefined && col.resizable === undefined) {
|
|
211
|
+
processed.resizable = columnConfig.resizable;
|
|
212
|
+
}
|
|
213
|
+
if (columnConfig.sortable !== undefined && col.sortable === undefined) {
|
|
214
|
+
processed.sortable = columnConfig.sortable;
|
|
215
|
+
}
|
|
216
|
+
if (columnConfig.filterable !== undefined && col.filter === undefined) {
|
|
217
|
+
processed.filter = columnConfig.filterable;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
return processed;
|
|
222
|
+
});
|
|
223
|
+
}, [columnDefs, editable, columnConfig]);
|
|
224
|
+
|
|
225
|
+
// Merge grid options with props
|
|
226
|
+
const mergedGridOptions: GridOptions = useMemo(() => ({
|
|
227
|
+
...gridOptions,
|
|
228
|
+
pagination,
|
|
229
|
+
paginationPageSize,
|
|
230
|
+
domLayout,
|
|
231
|
+
animateRows,
|
|
232
|
+
rowSelection,
|
|
233
|
+
editType,
|
|
234
|
+
singleClickEdit,
|
|
235
|
+
stopEditingWhenCellsLoseFocus,
|
|
236
|
+
statusBar: statusPanels ? { statusPanels } : undefined,
|
|
237
|
+
enableRangeSelection,
|
|
238
|
+
enableCharts,
|
|
239
|
+
getContextMenuItems: contextMenu?.enabled ? getContextMenuItems : undefined,
|
|
240
|
+
// Default options for better UX
|
|
241
|
+
suppressCellFocus: !editable,
|
|
242
|
+
enableCellTextSelection: true,
|
|
243
|
+
ensureDomOrder: true,
|
|
244
|
+
// Event handlers
|
|
245
|
+
onCellClicked: handleCellClicked,
|
|
246
|
+
onRowClicked: handleRowClicked,
|
|
247
|
+
onSelectionChanged: handleSelectionChanged,
|
|
248
|
+
onCellValueChanged: handleCellValueChanged,
|
|
249
|
+
onGridReady,
|
|
250
|
+
}), [
|
|
251
|
+
gridOptions,
|
|
252
|
+
pagination,
|
|
253
|
+
paginationPageSize,
|
|
254
|
+
domLayout,
|
|
255
|
+
animateRows,
|
|
256
|
+
rowSelection,
|
|
257
|
+
editType,
|
|
258
|
+
singleClickEdit,
|
|
259
|
+
stopEditingWhenCellsLoseFocus,
|
|
260
|
+
statusPanels,
|
|
261
|
+
enableRangeSelection,
|
|
262
|
+
enableCharts,
|
|
263
|
+
contextMenu,
|
|
264
|
+
getContextMenuItems,
|
|
265
|
+
editable,
|
|
266
|
+
handleCellClicked,
|
|
267
|
+
handleRowClicked,
|
|
268
|
+
handleSelectionChanged,
|
|
269
|
+
handleCellValueChanged,
|
|
270
|
+
onGridReady,
|
|
271
|
+
]);
|
|
272
|
+
|
|
273
|
+
// Compute container style
|
|
274
|
+
const containerStyle = useMemo(() => ({
|
|
275
|
+
height: typeof height === 'number' ? `${height}px` : height,
|
|
276
|
+
width: '100%',
|
|
277
|
+
}), [height]);
|
|
278
|
+
|
|
279
|
+
// Determine theme class and build complete class list
|
|
280
|
+
const themeClass = `ag-theme-${theme}`;
|
|
281
|
+
const classList = [
|
|
282
|
+
themeClass,
|
|
283
|
+
'rounded-xl',
|
|
284
|
+
'border',
|
|
285
|
+
'border-border',
|
|
286
|
+
'overflow-hidden',
|
|
287
|
+
'shadow-lg',
|
|
288
|
+
className
|
|
289
|
+
].filter(Boolean).join(' ');
|
|
290
|
+
|
|
291
|
+
return (
|
|
292
|
+
<div className="ag-grid-container">
|
|
293
|
+
{exportConfig?.enabled && (
|
|
294
|
+
<div className="mb-2 flex gap-2">
|
|
295
|
+
<button
|
|
296
|
+
onClick={handleExportCSV}
|
|
297
|
+
className="px-3 py-1.5 text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 rounded-md transition-colors"
|
|
298
|
+
>
|
|
299
|
+
Export CSV
|
|
300
|
+
</button>
|
|
301
|
+
</div>
|
|
302
|
+
)}
|
|
303
|
+
<div
|
|
304
|
+
className={classList}
|
|
305
|
+
style={containerStyle}
|
|
306
|
+
>
|
|
307
|
+
<AgGridReact
|
|
308
|
+
ref={gridRef}
|
|
309
|
+
rowData={rowData}
|
|
310
|
+
columnDefs={processedColumnDefs}
|
|
311
|
+
gridOptions={mergedGridOptions}
|
|
312
|
+
/>
|
|
313
|
+
</div>
|
|
314
|
+
</div>
|
|
315
|
+
);
|
|
316
|
+
}
|