@object-ui/plugin-aggrid 0.4.0 → 0.5.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/README.md CHANGED
@@ -31,6 +31,21 @@ pnpm add @object-ui/plugin-aggrid ag-grid-community ag-grid-react
31
31
 
32
32
  Note: `ag-grid-community` and `ag-grid-react` are peer dependencies and must be installed separately.
33
33
 
34
+ ### Next.js App Router Setup
35
+
36
+ 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:
37
+
38
+ ```typescript
39
+ // app/layout.tsx
40
+ import 'ag-grid-community/styles/ag-grid.css';
41
+ import 'ag-grid-community/styles/ag-theme-quartz.css';
42
+ import 'ag-grid-community/styles/ag-theme-alpine.css';
43
+ import 'ag-grid-community/styles/ag-theme-balham.css';
44
+ import 'ag-grid-community/styles/ag-theme-material.css';
45
+ ```
46
+
47
+ 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.
48
+
34
49
  ## Usage
35
50
 
36
51
  ### Automatic Registration (Side-Effect Import)
@@ -497,7 +512,212 @@ pnpm type-check
497
512
 
498
513
  MIT
499
514
 
500
- ## Resources
515
+ ## Object AgGrid - Metadata-Driven AG Grid
516
+
517
+ 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.
518
+
519
+ ### Features
520
+
521
+ - **Automatic Metadata Fetching**: Retrieves object schema from ObjectStack backend
522
+ - **Automatic Data Loading**: Fetches data with pagination, filtering, and sorting
523
+ - **Field Type Support**: Supports all ObjectUI field types with appropriate formatters and renderers
524
+ - **Inline Editing**: Automatically saves changes to the backend
525
+ - **Zero Column Configuration**: Columns are generated from metadata
526
+ - **Selective Field Display**: Optionally show only specific fields
527
+
528
+ ### Installation
529
+
530
+ ```bash
531
+ pnpm add @object-ui/plugin-aggrid @object-ui/data-objectstack ag-grid-community ag-grid-react
532
+ ```
533
+
534
+ ### Usage
535
+
536
+ ```typescript
537
+ import '@object-ui/plugin-aggrid';
538
+ import { ObjectStackAdapter } from '@object-ui/data-objectstack';
539
+
540
+ // Create data source
541
+ const dataSource = new ObjectStackAdapter({
542
+ baseUrl: 'https://api.example.com',
543
+ token: 'your-api-token'
544
+ });
545
+
546
+ // Use in schema
547
+ const schema = {
548
+ type: 'object-aggrid',
549
+ objectName: 'contacts', // Object to fetch
550
+ dataSource: dataSource, // ObjectStack data source
551
+ pagination: true,
552
+ pageSize: 20,
553
+ theme: 'quartz',
554
+ height: 600
555
+ };
556
+ ```
557
+
558
+ ### Supported Field Types
559
+
560
+ The component automatically applies appropriate formatters and cell renderers for all field types:
561
+
562
+ - **Text Types**: text, textarea, markdown, html
563
+ - **Numeric Types**: number, currency (with locale formatting), percent
564
+ - **Boolean**: Displays ✓ Yes / ✗ No
565
+ - **Date/Time**: date, datetime, time (with locale formatting)
566
+ - **Contact**: email (clickable mailto), phone (clickable tel), url (clickable link)
567
+ - **Select/Lookup**: select, lookup, master_detail (shows labels)
568
+ - **Visual**: color (with color swatch), image, avatar, rating (stars)
569
+ - **Advanced**: formula, summary, auto_number, user, file
570
+
571
+ ### Schema API for Object AgGrid
572
+
573
+ ```typescript
574
+ {
575
+ type: 'object-aggrid',
576
+
577
+ // Required
578
+ objectName: string, // Name of the object to fetch
579
+ dataSource: DataSource, // ObjectStack data source instance
580
+
581
+ // Optional Field Configuration
582
+ fieldNames?: string[], // Show only these fields (default: all)
583
+ fields?: FieldMetadata[], // Override field metadata
584
+
585
+ // Query Parameters
586
+ filters?: Record<string, any>, // Query filters
587
+ sort?: Record<string, 'asc' | 'desc'>, // Sorting
588
+ pageSize?: number, // Rows per page (default: 10)
589
+
590
+ // Display Options (same as aggrid)
591
+ pagination?: boolean, // Enable pagination (default: true)
592
+ theme?: string, // Grid theme (default: 'quartz')
593
+ height?: number | string, // Grid height (default: 500)
594
+
595
+ // Editing
596
+ editable?: boolean, // Enable inline editing (auto-saves to backend)
597
+
598
+ // Export, Status Bar, Callbacks, etc. (same as aggrid)
599
+ exportConfig?: ExportConfig,
600
+ statusBar?: StatusBarConfig,
601
+ columnConfig?: ColumnConfig,
602
+ callbacks?: {
603
+ onDataLoaded?: (data: any[]) => void,
604
+ onDataError?: (error: Error) => void,
605
+ // ... other aggrid callbacks
606
+ }
607
+ }
608
+ ```
609
+
610
+ ### Examples
611
+
612
+ #### Basic Usage
613
+
614
+ ```typescript
615
+ const schema = {
616
+ type: 'object-aggrid',
617
+ objectName: 'users',
618
+ dataSource: myDataSource,
619
+ pagination: true,
620
+ pageSize: 25
621
+ };
622
+ ```
623
+
624
+ #### With Field Selection
625
+
626
+ ```typescript
627
+ const schema = {
628
+ type: 'object-aggrid',
629
+ objectName: 'contacts',
630
+ dataSource: myDataSource,
631
+ fieldNames: ['name', 'email', 'phone', 'company'],
632
+ pagination: true
633
+ };
634
+ ```
635
+
636
+ #### With Filters and Sorting
637
+
638
+ ```typescript
639
+ const schema = {
640
+ type: 'object-aggrid',
641
+ objectName: 'products',
642
+ dataSource: myDataSource,
643
+ filters: {
644
+ category: 'Electronics',
645
+ price: { $lt: 1000 }
646
+ },
647
+ sort: {
648
+ price: 'asc'
649
+ },
650
+ pagination: true
651
+ };
652
+ ```
653
+
654
+ #### Editable Grid with Auto-Save
655
+
656
+ ```typescript
657
+ const schema = {
658
+ type: 'object-aggrid',
659
+ objectName: 'tasks',
660
+ dataSource: myDataSource,
661
+ editable: true, // Enable editing
662
+ singleClickEdit: true, // Single-click to edit
663
+ callbacks: {
664
+ onCellValueChanged: (event) => {
665
+ // Changes are automatically saved to backend
666
+ console.log('Saved:', event.data);
667
+ }
668
+ }
669
+ };
670
+ ```
671
+
672
+ #### With Export
673
+
674
+ ```typescript
675
+ const schema = {
676
+ type: 'object-aggrid',
677
+ objectName: 'sales',
678
+ dataSource: myDataSource,
679
+ exportConfig: {
680
+ enabled: true,
681
+ fileName: 'sales-report.csv'
682
+ }
683
+ };
684
+ ```
685
+
686
+ ### Field Type Formatting Examples
687
+
688
+ **Currency Field:**
689
+ ```typescript
690
+ // Schema defines: { type: 'currency', currency: 'USD', precision: 2 }
691
+ // Renders as: $1,234.56
692
+ ```
693
+
694
+ **Percent Field:**
695
+ ```typescript
696
+ // Schema defines: { type: 'percent', precision: 1 }
697
+ // Renders as: 45.5%
698
+ ```
699
+
700
+ **Email Field:**
701
+ ```typescript
702
+ // Schema defines: { type: 'email' }
703
+ // Renders as: <a href="mailto:user@example.com">user@example.com</a>
704
+ ```
705
+
706
+ **Rating Field:**
707
+ ```typescript
708
+ // Schema defines: { type: 'rating', max: 5 }
709
+ // Renders as: ⭐⭐⭐⭐⭐
710
+ ```
711
+
712
+ **Color Field:**
713
+ ```typescript
714
+ // Schema defines: { type: 'color' }
715
+ // Renders as: [color swatch] #FF5733
716
+ ```
717
+
718
+ ## License
719
+
720
+
501
721
 
502
722
  - [AG Grid Community Documentation](https://www.ag-grid.com/documentation/)
503
723
  - [AG Grid Column Definitions](https://www.ag-grid.com/documentation/javascript/column-definitions/)
@@ -1,35 +1,35 @@
1
- import { j as u } from "./index-B6NPAFZx.js";
2
- import { useRef as J, useMemo as p, useCallback as i } from "react";
3
- import { AgGridReact as K } from "ag-grid-react";
4
- function Y({
5
- rowData: f = [],
6
- columnDefs: c = [],
7
- gridOptions: v = {},
1
+ import { j as d } from "./index-CLKYMco3.js";
2
+ import { useRef as O, useMemo as f, useCallback as i } from "react";
3
+ import { AgGridReact as Q } from "ag-grid-react";
4
+ function Z({
5
+ rowData: u = [],
6
+ columnDefs: g = [],
7
+ gridOptions: m = {},
8
8
  pagination: x = !1,
9
- paginationPageSize: P = 10,
10
- domLayout: A = "normal",
11
- animateRows: b = !0,
9
+ paginationPageSize: b = 10,
10
+ domLayout: P = "normal",
11
+ animateRows: A = !0,
12
12
  rowSelection: S,
13
13
  theme: H = "quartz",
14
- height: d = 500,
15
- className: O = "",
16
- editable: m = !1,
14
+ height: c = 500,
15
+ className: B = "",
16
+ editable: p = !1,
17
17
  editType: j,
18
18
  singleClickEdit: y = !1,
19
19
  stopEditingWhenCellsLoseFocus: R = !0,
20
20
  exportConfig: o,
21
- statusBar: g,
21
+ statusBar: h,
22
22
  callbacks: t,
23
23
  columnConfig: a,
24
- enableRangeSelection: z = !1,
25
- enableCharts: E = !1,
24
+ enableRangeSelection: V = !1,
25
+ enableCharts: z = !1,
26
26
  contextMenu: l
27
27
  }) {
28
- const n = J(null), h = p(() => {
29
- if (!g?.enabled) return;
30
- const e = g.aggregations || ["count", "sum", "avg"], s = [];
28
+ const n = O(null), C = f(() => {
29
+ if (!h?.enabled) return;
30
+ const e = h.aggregations || ["count", "sum", "avg"], s = [];
31
31
  return e.includes("count") && s.push({ statusPanel: "agAggregationComponent", statusPanelParams: { aggFuncs: ["count"] } }), e.includes("sum") && s.push({ statusPanel: "agAggregationComponent", statusPanelParams: { aggFuncs: ["sum"] } }), e.includes("avg") && s.push({ statusPanel: "agAggregationComponent", statusPanelParams: { aggFuncs: ["avg"] } }), e.includes("min") && s.push({ statusPanel: "agAggregationComponent", statusPanelParams: { aggFuncs: ["min"] } }), e.includes("max") && s.push({ statusPanel: "agAggregationComponent", statusPanelParams: { aggFuncs: ["max"] } }), s;
32
- }, [g]), C = i(() => {
32
+ }, [h]), v = i(() => {
33
33
  if (!n.current?.api) return;
34
34
  const e = {
35
35
  fileName: o?.fileName || "export.csv",
@@ -38,17 +38,17 @@ function Y({
38
38
  onlySelected: o?.onlySelected || !1
39
39
  };
40
40
  if (n.current.api.exportDataAsCsv(e), t?.onExport) {
41
- const s = o?.onlySelected ? n.current.api.getSelectedRows() : f;
41
+ const s = o?.onlySelected ? n.current.api.getSelectedRows() : u;
42
42
  t.onExport(s || [], "csv");
43
43
  }
44
- }, [o, t, f]), N = i((e) => {
44
+ }, [o, t, u]), E = i((e) => {
45
45
  if (!l?.enabled) return [];
46
46
  const s = [];
47
47
  return (l.items || ["copy", "copyWithHeaders", "separator", "export"]).forEach((r) => {
48
48
  r === "export" ? s.push({
49
49
  name: "Export CSV",
50
50
  icon: "<span>📥</span>",
51
- action: () => C()
51
+ action: () => v()
52
52
  }) : r === "autoSizeAll" ? s.push({
53
53
  name: "Auto-size All Columns",
54
54
  action: () => {
@@ -69,7 +69,7 @@ function Y({
69
69
  }
70
70
  });
71
71
  })), s;
72
- }, [l, C, t]), V = i((e) => {
72
+ }, [l, v, t]), N = i((e) => {
73
73
  t?.onCellClicked?.(e);
74
74
  }, [t]), F = i((e) => {
75
75
  t?.onRowClicked?.(e);
@@ -79,85 +79,89 @@ function Y({
79
79
  t?.onCellValueChanged?.(e);
80
80
  }, [t]), G = i((e) => {
81
81
  n.current = e;
82
- }, []), $ = p(() => c ? c.map((e) => {
82
+ }, []), $ = f(() => g ? g.map((e) => {
83
83
  const s = { ...e };
84
- return m && e.editable !== !1 && (s.editable = !0), a && (a.resizable !== void 0 && e.resizable === void 0 && (s.resizable = a.resizable), a.sortable !== void 0 && e.sortable === void 0 && (s.sortable = a.sortable), a.filterable !== void 0 && e.filter === void 0 && (s.filter = a.filterable)), s;
85
- }) : [], [c, m, a]), q = p(() => ({
86
- ...v,
84
+ return p && e.editable !== !1 && (s.editable = !0), a && (a.resizable !== void 0 && e.resizable === void 0 && (s.resizable = a.resizable), a.sortable !== void 0 && e.sortable === void 0 && (s.sortable = a.sortable), a.filterable !== void 0 && e.filter === void 0 && (s.filter = a.filterable)), s;
85
+ }) : [], [g, p, a]), q = f(() => ({
86
+ ...m,
87
87
  pagination: x,
88
- paginationPageSize: P,
89
- domLayout: A,
90
- animateRows: b,
88
+ paginationPageSize: b,
89
+ domLayout: P,
90
+ animateRows: A,
91
91
  rowSelection: S,
92
92
  editType: j,
93
93
  singleClickEdit: y,
94
94
  stopEditingWhenCellsLoseFocus: R,
95
- statusBar: h ? { statusPanels: h } : void 0,
96
- enableRangeSelection: z,
97
- enableCharts: E,
98
- getContextMenuItems: l?.enabled ? N : void 0,
95
+ statusBar: C ? { statusPanels: C } : void 0,
96
+ enableRangeSelection: V,
97
+ enableCharts: z,
98
+ getContextMenuItems: l?.enabled ? E : void 0,
99
99
  // Default options for better UX
100
- suppressCellFocus: !m,
100
+ suppressCellFocus: !p,
101
101
  enableCellTextSelection: !0,
102
102
  ensureDomOrder: !0,
103
+ // Virtual scrolling optimizations for large datasets
104
+ rowBuffer: m.rowBuffer ?? 10,
105
+ debounceVerticalScrollbar: m.debounceVerticalScrollbar ?? u.length > 1e3,
103
106
  // Event handlers
104
- onCellClicked: V,
107
+ onCellClicked: N,
105
108
  onRowClicked: F,
106
109
  onSelectionChanged: I,
107
110
  onCellValueChanged: w,
108
111
  onGridReady: G
109
112
  }), [
110
- v,
113
+ m,
111
114
  x,
115
+ b,
112
116
  P,
113
117
  A,
114
- b,
115
118
  S,
116
119
  j,
117
120
  y,
118
121
  R,
119
- h,
122
+ C,
123
+ V,
120
124
  z,
121
- E,
122
125
  l,
126
+ E,
127
+ p,
128
+ u.length,
123
129
  N,
124
- m,
125
- V,
126
130
  F,
127
131
  I,
128
132
  w,
129
133
  G
130
- ]), B = p(() => ({
131
- height: typeof d == "number" ? `${d}px` : d,
134
+ ]), J = f(() => ({
135
+ height: typeof c == "number" ? `${c}px` : c,
132
136
  width: "100%"
133
- }), [d]), D = [
137
+ }), [c]), K = [
134
138
  `ag-theme-${H}`,
135
139
  "rounded-xl",
136
140
  "border",
137
141
  "border-border",
138
142
  "overflow-hidden",
139
143
  "shadow-lg",
140
- O
144
+ B
141
145
  ].filter(Boolean).join(" ");
142
- return /* @__PURE__ */ u.jsxs("div", { className: "ag-grid-container", children: [
143
- o?.enabled && /* @__PURE__ */ u.jsx("div", { className: "mb-2 flex gap-2", children: /* @__PURE__ */ u.jsx(
146
+ return /* @__PURE__ */ d.jsxs("div", { className: "ag-grid-container", children: [
147
+ o?.enabled && /* @__PURE__ */ d.jsx("div", { className: "mb-2 flex gap-2", children: /* @__PURE__ */ d.jsx(
144
148
  "button",
145
149
  {
146
- onClick: C,
150
+ onClick: v,
147
151
  className: "px-3 py-1.5 text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 rounded-md transition-colors",
148
152
  children: "Export CSV"
149
153
  }
150
154
  ) }),
151
- /* @__PURE__ */ u.jsx(
155
+ /* @__PURE__ */ d.jsx(
152
156
  "div",
153
157
  {
154
- className: D,
155
- style: B,
156
- children: /* @__PURE__ */ u.jsx(
157
- K,
158
+ className: K,
159
+ style: J,
160
+ children: /* @__PURE__ */ d.jsx(
161
+ Q,
158
162
  {
159
163
  ref: n,
160
- rowData: f,
164
+ rowData: u,
161
165
  columnDefs: $,
162
166
  gridOptions: q
163
167
  }
@@ -167,5 +171,5 @@ function Y({
167
171
  ] });
168
172
  }
169
173
  export {
170
- Y as default
174
+ Z as default
171
175
  };