@object-ui/plugin-aggrid 0.5.0 → 3.0.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.
Files changed (36) hide show
  1. package/.turbo/turbo-build.log +90 -12
  2. package/CHANGELOG.md +38 -0
  3. package/QUICKSTART.md +1 -1
  4. package/dist/AddressField-CUHm7scl.js +95 -0
  5. package/dist/AgGridImpl-DVyHqLJi.js +229 -0
  6. package/dist/AutoNumberField-twP63aM8.js +8 -0
  7. package/dist/FileField-Cxql0zFQ.js +101 -0
  8. package/dist/FormulaField-CsRwvjVZ.js +9 -0
  9. package/dist/GeolocationField-DQ_J9TGl.js +141 -0
  10. package/dist/GridField-BrF4x9mD.js +29 -0
  11. package/dist/LocationField-C4zcjds3.js +33 -0
  12. package/dist/MasterDetailField-DybIeqDQ.js +117 -0
  13. package/dist/ObjectAgGridImpl-CotBxBL9.js +28727 -0
  14. package/dist/ObjectField-CVXoKqAF.js +51 -0
  15. package/dist/QRCodeField-lmARTFQy.js +96 -0
  16. package/dist/RichTextField-5ce8qKzb.js +37 -0
  17. package/dist/SignatureField-VES1Umvd.js +96 -0
  18. package/dist/SummaryField-CQsFucuH.js +9 -0
  19. package/dist/UserField-Dxq9ZQ9l.js +49 -0
  20. package/dist/VectorField-BJoHEqgv.js +25 -0
  21. package/dist/{index-CLKYMco3.js → index-DSI77JcG.js} +57 -33
  22. package/dist/index.js +1 -1
  23. package/dist/index.umd.cjs +225 -5
  24. package/dist/src/AgGridImpl.d.ts +5 -2
  25. package/dist/src/field-renderers.d.ts +67 -0
  26. package/dist/src/index.d.ts +5 -2
  27. package/dist/src/types.d.ts +48 -1
  28. package/package.json +13 -12
  29. package/src/AgGridImpl.tsx +95 -10
  30. package/src/ObjectAgGridImpl.tsx +67 -169
  31. package/src/field-renderers.test.tsx +383 -0
  32. package/src/field-renderers.tsx +224 -0
  33. package/src/index.tsx +30 -3
  34. package/src/types.ts +57 -1
  35. package/dist/AgGridImpl-BQ6tBvrq.js +0 -175
  36. package/dist/ObjectAgGridImpl-CGFeGvOH.js +0 -372
package/src/types.ts CHANGED
@@ -16,7 +16,7 @@ export interface AgGridCallbacks {
16
16
  onRowClicked?: (event: RowClickedEvent) => void;
17
17
  onSelectionChanged?: (event: SelectionChangedEvent) => void;
18
18
  onCellValueChanged?: (event: CellValueChangedEvent) => void;
19
- onExport?: (data: any[], format: 'csv') => void;
19
+ onExport?: (data: any[], format: 'csv' | 'excel') => void;
20
20
  onContextMenuAction?: (action: string, rowData: any) => void;
21
21
  }
22
22
 
@@ -29,6 +29,53 @@ export interface ExportConfig {
29
29
  skipColumnHeaders?: boolean;
30
30
  allColumns?: boolean;
31
31
  onlySelected?: boolean;
32
+ format?: 'csv' | 'excel';
33
+ }
34
+
35
+ /**
36
+ * Tree data configuration
37
+ */
38
+ export interface TreeDataConfig {
39
+ enabled?: boolean;
40
+ /** Field that contains the hierarchy path (array of strings) */
41
+ pathField?: string;
42
+ /** Field that contains parent ID for parent-child relationships */
43
+ parentIdField?: string;
44
+ /** Field that contains the unique ID */
45
+ idField?: string;
46
+ /** Whether to expand all rows by default */
47
+ expandAll?: boolean;
48
+ /** Depth to expand to by default */
49
+ expandDepth?: number;
50
+ }
51
+
52
+ /**
53
+ * Row grouping configuration
54
+ */
55
+ export interface RowGroupingConfig {
56
+ enabled?: boolean;
57
+ /** Fields to group by */
58
+ groupByFields?: string[];
59
+ /** Whether to show group row count */
60
+ showRowCount?: boolean;
61
+ /** Whether to allow user to change grouping */
62
+ userGroupable?: boolean;
63
+ /** Aggregation functions for grouped columns */
64
+ aggregations?: Record<string, 'sum' | 'avg' | 'count' | 'min' | 'max' | 'first' | 'last'>;
65
+ }
66
+
67
+ /**
68
+ * Excel export configuration
69
+ */
70
+ export interface ExcelExportConfig {
71
+ enabled?: boolean;
72
+ fileName?: string;
73
+ sheetName?: string;
74
+ includeHeaders?: boolean;
75
+ includeGrouping?: boolean;
76
+ onlySelected?: boolean;
77
+ /** Custom column widths */
78
+ columnWidths?: Record<string, number>;
32
79
  }
33
80
 
34
81
  /**
@@ -105,6 +152,15 @@ export interface AgGridSchema {
105
152
  // Context menu
106
153
  contextMenu?: ContextMenuConfig;
107
154
 
155
+ // Tree data
156
+ treeData?: TreeDataConfig;
157
+
158
+ // Row grouping
159
+ rowGrouping?: RowGroupingConfig;
160
+
161
+ // Excel export
162
+ excelExport?: ExcelExportConfig;
163
+
108
164
  // Event callbacks
109
165
  callbacks?: AgGridCallbacks;
110
166
 
@@ -1,175 +0,0 @@
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
- pagination: x = !1,
9
- paginationPageSize: b = 10,
10
- domLayout: P = "normal",
11
- animateRows: A = !0,
12
- rowSelection: S,
13
- theme: H = "quartz",
14
- height: c = 500,
15
- className: B = "",
16
- editable: p = !1,
17
- editType: j,
18
- singleClickEdit: y = !1,
19
- stopEditingWhenCellsLoseFocus: R = !0,
20
- exportConfig: o,
21
- statusBar: h,
22
- callbacks: t,
23
- columnConfig: a,
24
- enableRangeSelection: V = !1,
25
- enableCharts: z = !1,
26
- contextMenu: l
27
- }) {
28
- const n = O(null), C = f(() => {
29
- if (!h?.enabled) return;
30
- const e = h.aggregations || ["count", "sum", "avg"], s = [];
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
- }, [h]), v = i(() => {
33
- if (!n.current?.api) return;
34
- const e = {
35
- fileName: o?.fileName || "export.csv",
36
- skipColumnHeaders: o?.skipColumnHeaders || !1,
37
- allColumns: o?.allColumns || !1,
38
- onlySelected: o?.onlySelected || !1
39
- };
40
- if (n.current.api.exportDataAsCsv(e), t?.onExport) {
41
- const s = o?.onlySelected ? n.current.api.getSelectedRows() : u;
42
- t.onExport(s || [], "csv");
43
- }
44
- }, [o, t, u]), E = i((e) => {
45
- if (!l?.enabled) return [];
46
- const s = [];
47
- return (l.items || ["copy", "copyWithHeaders", "separator", "export"]).forEach((r) => {
48
- r === "export" ? s.push({
49
- name: "Export CSV",
50
- icon: "<span>📥</span>",
51
- action: () => v()
52
- }) : r === "autoSizeAll" ? s.push({
53
- name: "Auto-size All Columns",
54
- action: () => {
55
- n.current?.api && n.current.api.autoSizeAllColumns();
56
- }
57
- }) : r === "resetColumns" ? s.push({
58
- name: "Reset Columns",
59
- action: () => {
60
- n.current?.api && n.current.api.resetColumnState();
61
- }
62
- }) : s.push(r);
63
- }), l.customItems && (s.length > 0 && s.push("separator"), l.customItems.forEach((r) => {
64
- s.push({
65
- name: r.name,
66
- disabled: r.disabled,
67
- action: () => {
68
- t?.onContextMenuAction && t.onContextMenuAction(r.action, e.node?.data);
69
- }
70
- });
71
- })), s;
72
- }, [l, v, t]), N = i((e) => {
73
- t?.onCellClicked?.(e);
74
- }, [t]), F = i((e) => {
75
- t?.onRowClicked?.(e);
76
- }, [t]), I = i((e) => {
77
- t?.onSelectionChanged?.(e);
78
- }, [t]), w = i((e) => {
79
- t?.onCellValueChanged?.(e);
80
- }, [t]), G = i((e) => {
81
- n.current = e;
82
- }, []), $ = f(() => g ? g.map((e) => {
83
- const s = { ...e };
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
- pagination: x,
88
- paginationPageSize: b,
89
- domLayout: P,
90
- animateRows: A,
91
- rowSelection: S,
92
- editType: j,
93
- singleClickEdit: y,
94
- stopEditingWhenCellsLoseFocus: R,
95
- statusBar: C ? { statusPanels: C } : void 0,
96
- enableRangeSelection: V,
97
- enableCharts: z,
98
- getContextMenuItems: l?.enabled ? E : void 0,
99
- // Default options for better UX
100
- suppressCellFocus: !p,
101
- enableCellTextSelection: !0,
102
- ensureDomOrder: !0,
103
- // Virtual scrolling optimizations for large datasets
104
- rowBuffer: m.rowBuffer ?? 10,
105
- debounceVerticalScrollbar: m.debounceVerticalScrollbar ?? u.length > 1e3,
106
- // Event handlers
107
- onCellClicked: N,
108
- onRowClicked: F,
109
- onSelectionChanged: I,
110
- onCellValueChanged: w,
111
- onGridReady: G
112
- }), [
113
- m,
114
- x,
115
- b,
116
- P,
117
- A,
118
- S,
119
- j,
120
- y,
121
- R,
122
- C,
123
- V,
124
- z,
125
- l,
126
- E,
127
- p,
128
- u.length,
129
- N,
130
- F,
131
- I,
132
- w,
133
- G
134
- ]), J = f(() => ({
135
- height: typeof c == "number" ? `${c}px` : c,
136
- width: "100%"
137
- }), [c]), K = [
138
- `ag-theme-${H}`,
139
- "rounded-xl",
140
- "border",
141
- "border-border",
142
- "overflow-hidden",
143
- "shadow-lg",
144
- B
145
- ].filter(Boolean).join(" ");
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(
148
- "button",
149
- {
150
- onClick: v,
151
- className: "px-3 py-1.5 text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 rounded-md transition-colors",
152
- children: "Export CSV"
153
- }
154
- ) }),
155
- /* @__PURE__ */ d.jsx(
156
- "div",
157
- {
158
- className: K,
159
- style: J,
160
- children: /* @__PURE__ */ d.jsx(
161
- Q,
162
- {
163
- ref: n,
164
- rowData: u,
165
- columnDefs: $,
166
- gridOptions: q
167
- }
168
- )
169
- }
170
- )
171
- ] });
172
- }
173
- export {
174
- Z as default
175
- };
@@ -1,372 +0,0 @@
1
- import { j as d } from "./index-CLKYMco3.js";
2
- import { useRef as ne, useState as y, useEffect as B, useMemo as P, useCallback as g } from "react";
3
- import { AgGridReact as ae } from "ag-grid-react";
4
- const le = {
5
- text: "agTextColumnFilter",
6
- textarea: "agTextColumnFilter",
7
- number: "agNumberColumnFilter",
8
- currency: "agNumberColumnFilter",
9
- percent: "agNumberColumnFilter",
10
- boolean: "agSetColumnFilter",
11
- date: "agDateColumnFilter",
12
- datetime: "agDateColumnFilter",
13
- time: "agTextColumnFilter",
14
- email: "agTextColumnFilter",
15
- phone: "agTextColumnFilter",
16
- url: "agTextColumnFilter",
17
- select: "agSetColumnFilter"
18
- };
19
- function fe({
20
- objectName: a,
21
- dataSource: l,
22
- fields: e,
23
- fieldNames: t,
24
- filters: i,
25
- sort: h,
26
- pageSize: F = 10,
27
- pagination: A = !0,
28
- domLayout: k = "normal",
29
- animateRows: N = !0,
30
- rowSelection: D,
31
- theme: J = "quartz",
32
- height: w = 500,
33
- className: K = "",
34
- editable: E = !1,
35
- editType: V,
36
- singleClickEdit: I = !1,
37
- stopEditingWhenCellsLoseFocus: L = !0,
38
- exportConfig: m,
39
- statusBar: S,
40
- callbacks: o,
41
- columnConfig: v,
42
- enableRangeSelection: O = !1,
43
- enableCharts: U = !1,
44
- contextMenu: f
45
- }) {
46
- const c = ne(null), [Q, x] = y(!0), [_, C] = y(null), [b, W] = y(null), [T, X] = y([]), [ue, Z] = y(0);
47
- B(() => {
48
- if (!l) {
49
- C(new Error("DataSource is required")), x(!1);
50
- return;
51
- }
52
- (async () => {
53
- try {
54
- x(!0), C(null);
55
- const r = await l.getObjectSchema(a);
56
- W(r);
57
- } catch (r) {
58
- const s = r instanceof Error ? r : new Error(String(r));
59
- C(s), o?.onDataError?.(s);
60
- } finally {
61
- x(!1);
62
- }
63
- })();
64
- }, [a, l, o]), B(() => {
65
- if (!l || !b) return;
66
- (async () => {
67
- try {
68
- x(!0), C(null);
69
- const r = {
70
- $top: F,
71
- $skip: 0
72
- };
73
- i && (r.$filter = i), h && (r.$orderby = h);
74
- const s = await l.find(a, r);
75
- X(s.data || []), Z(s.total || 0), o?.onDataLoaded?.(s.data || []);
76
- } catch (r) {
77
- const s = r instanceof Error ? r : new Error(String(r));
78
- C(s), o?.onDataError?.(s);
79
- } finally {
80
- x(!1);
81
- }
82
- })();
83
- }, [a, l, b, i, h, F, o]);
84
- const ee = P(() => {
85
- if (!b?.fields) return [];
86
- const n = e || Object.values(b.fields);
87
- return (t ? n.filter((s) => t.includes(s.name)) : n).map((s) => {
88
- const u = {
89
- field: s.name,
90
- headerName: s.label || s.name,
91
- sortable: s.sortable !== !1,
92
- filter: se(s),
93
- editable: E && !s.readonly
94
- // visible_on will be evaluated by the core renderer
95
- // For now, we just show all fields. Conditional visibility
96
- // should be handled at a higher level or via dynamic column updates
97
- };
98
- return v && (v.resizable !== void 0 && (u.resizable = v.resizable), v.sortable !== void 0 && u.sortable === void 0 && (u.sortable = v.sortable)), oe(u, s), u;
99
- });
100
- }, [b, e, t, E, v]), R = P(() => {
101
- if (!S?.enabled) return;
102
- const n = S.aggregations || ["count", "sum", "avg"], r = [];
103
- return n.includes("count") && r.push({ statusPanel: "agAggregationComponent", statusPanelParams: { aggFuncs: ["count"] } }), n.includes("sum") && r.push({ statusPanel: "agAggregationComponent", statusPanelParams: { aggFuncs: ["sum"] } }), n.includes("avg") && r.push({ statusPanel: "agAggregationComponent", statusPanelParams: { aggFuncs: ["avg"] } }), n.includes("min") && r.push({ statusPanel: "agAggregationComponent", statusPanelParams: { aggFuncs: ["min"] } }), n.includes("max") && r.push({ statusPanel: "agAggregationComponent", statusPanelParams: { aggFuncs: ["max"] } }), r;
104
- }, [S]), $ = g(() => {
105
- if (!c.current?.api) return;
106
- const n = {
107
- fileName: m?.fileName || `${a}-export.csv`,
108
- skipColumnHeaders: m?.skipColumnHeaders || !1,
109
- allColumns: m?.allColumns || !1,
110
- onlySelected: m?.onlySelected || !1
111
- };
112
- if (c.current.api.exportDataAsCsv(n), o?.onExport) {
113
- const r = m?.onlySelected ? c.current.api.getSelectedRows() : T;
114
- o.onExport(r || [], "csv");
115
- }
116
- }, [m, o, T, a]), z = g((n) => {
117
- if (!f?.enabled) return [];
118
- const r = [];
119
- return (f.items || ["copy", "copyWithHeaders", "separator", "export"]).forEach((u) => {
120
- u === "export" ? r.push({
121
- name: "Export CSV",
122
- icon: "<span>📥</span>",
123
- action: () => $()
124
- }) : u === "autoSizeAll" ? r.push({
125
- name: "Auto-size All Columns",
126
- action: () => {
127
- c.current?.api && c.current.api.autoSizeAllColumns();
128
- }
129
- }) : u === "resetColumns" ? r.push({
130
- name: "Reset Columns",
131
- action: () => {
132
- c.current?.api && c.current.api.resetColumnState();
133
- }
134
- }) : r.push(u);
135
- }), f.customItems && (r.length > 0 && r.push("separator"), f.customItems.forEach((u) => {
136
- r.push({
137
- name: u.name,
138
- disabled: u.disabled,
139
- action: () => {
140
- o?.onContextMenuAction && o.onContextMenuAction(u.action, n.node?.data);
141
- }
142
- });
143
- })), r;
144
- }, [f, $, o]), H = g((n) => {
145
- o?.onCellClicked?.(n);
146
- }, [o]), M = g((n) => {
147
- o?.onRowClicked?.(n);
148
- }, [o]), q = g((n) => {
149
- o?.onSelectionChanged?.(n);
150
- }, [o]), G = g(async (n) => {
151
- if (o?.onCellValueChanged?.(n), l && n.data && n.data.id)
152
- try {
153
- await l.update(a, n.data.id, {
154
- [n.colDef.field]: n.newValue
155
- });
156
- } catch (r) {
157
- console.error("Failed to update record:", r), n.node.setDataValue(n.colDef.field, n.oldValue);
158
- }
159
- }, [o, l, a]), Y = g((n) => {
160
- c.current = n;
161
- }, []), te = P(() => ({
162
- pagination: A,
163
- paginationPageSize: F,
164
- domLayout: k,
165
- animateRows: N,
166
- rowSelection: D,
167
- editType: V,
168
- singleClickEdit: I,
169
- stopEditingWhenCellsLoseFocus: L,
170
- statusBar: R ? { statusPanels: R } : void 0,
171
- enableRangeSelection: O,
172
- enableCharts: U,
173
- getContextMenuItems: f?.enabled ? z : void 0,
174
- suppressCellFocus: !E,
175
- enableCellTextSelection: !0,
176
- ensureDomOrder: !0,
177
- onCellClicked: H,
178
- onRowClicked: M,
179
- onSelectionChanged: q,
180
- onCellValueChanged: G,
181
- onGridReady: Y
182
- }), [
183
- A,
184
- F,
185
- k,
186
- N,
187
- D,
188
- V,
189
- I,
190
- L,
191
- R,
192
- O,
193
- U,
194
- f,
195
- z,
196
- E,
197
- H,
198
- M,
199
- q,
200
- G,
201
- Y
202
- ]), j = P(() => ({
203
- height: typeof w == "number" ? `${w}px` : w,
204
- width: "100%"
205
- }), [w]), re = [
206
- `ag-theme-${J}`,
207
- "rounded-xl",
208
- "border",
209
- "border-border",
210
- "overflow-hidden",
211
- "shadow-lg",
212
- K
213
- ].filter(Boolean).join(" ");
214
- return Q ? /* @__PURE__ */ d.jsx("div", { className: "flex items-center justify-center", style: j, children: /* @__PURE__ */ d.jsxs("div", { className: "text-muted-foreground", children: [
215
- "Loading ",
216
- a,
217
- "..."
218
- ] }) }) : _ ? /* @__PURE__ */ d.jsx("div", { className: "flex items-center justify-center", style: j, children: /* @__PURE__ */ d.jsxs("div", { className: "text-destructive", children: [
219
- "Error loading data: ",
220
- _.message
221
- ] }) }) : /* @__PURE__ */ d.jsxs("div", { className: "object-aggrid-container", children: [
222
- m?.enabled && /* @__PURE__ */ d.jsx("div", { className: "mb-2 flex gap-2", children: /* @__PURE__ */ d.jsx(
223
- "button",
224
- {
225
- onClick: $,
226
- className: "px-3 py-1.5 text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 rounded-md transition-colors",
227
- children: "Export CSV"
228
- }
229
- ) }),
230
- /* @__PURE__ */ d.jsx(
231
- "div",
232
- {
233
- className: re,
234
- style: j,
235
- children: /* @__PURE__ */ d.jsx(
236
- ae,
237
- {
238
- ref: c,
239
- rowData: T,
240
- columnDefs: ee,
241
- gridOptions: te
242
- }
243
- )
244
- }
245
- )
246
- ] });
247
- }
248
- function p(a) {
249
- const l = document.createElement("div");
250
- return l.textContent = a, l.innerHTML;
251
- }
252
- function se(a) {
253
- return a.filterable === !1 ? !1 : le[a.type] || "agTextColumnFilter";
254
- }
255
- function oe(a, l) {
256
- switch (l.type) {
257
- case "boolean":
258
- a.cellRenderer = (e) => e.value === !0 ? "✓ Yes" : e.value === !1 ? "✗ No" : "";
259
- break;
260
- case "currency":
261
- a.valueFormatter = (e) => {
262
- if (e.value == null) return "";
263
- const t = l.currency || "USD", i = l.precision || 2;
264
- return new Intl.NumberFormat("en-US", {
265
- style: "currency",
266
- currency: t,
267
- minimumFractionDigits: i,
268
- maximumFractionDigits: i
269
- }).format(e.value);
270
- };
271
- break;
272
- case "percent":
273
- a.valueFormatter = (e) => {
274
- if (e.value == null) return "";
275
- const t = l.precision || 2;
276
- return `${(e.value * 100).toFixed(t)}%`;
277
- };
278
- break;
279
- case "date":
280
- a.valueFormatter = (e) => {
281
- if (!e.value) return "";
282
- try {
283
- const t = new Date(e.value);
284
- return isNaN(t.getTime()) ? "" : t.toLocaleDateString();
285
- } catch {
286
- return "";
287
- }
288
- };
289
- break;
290
- case "datetime":
291
- a.valueFormatter = (e) => {
292
- if (!e.value) return "";
293
- try {
294
- const t = new Date(e.value);
295
- return isNaN(t.getTime()) ? "" : t.toLocaleString();
296
- } catch {
297
- return "";
298
- }
299
- };
300
- break;
301
- case "time":
302
- a.valueFormatter = (e) => e.value ? e.value : "";
303
- break;
304
- case "email":
305
- a.cellRenderer = (e) => {
306
- if (!e.value) return "";
307
- const t = p(e.value);
308
- return `<a href="mailto:${t}" class="text-blue-600 hover:underline">${t}</a>`;
309
- };
310
- break;
311
- case "url":
312
- a.cellRenderer = (e) => {
313
- if (!e.value) return "";
314
- const t = p(e.value);
315
- return `<a href="${t}" target="_blank" rel="noopener noreferrer" class="text-blue-600 hover:underline">${t}</a>`;
316
- };
317
- break;
318
- case "phone":
319
- a.cellRenderer = (e) => {
320
- if (!e.value) return "";
321
- const t = p(e.value);
322
- return `<a href="tel:${t}" class="text-blue-600 hover:underline">${t}</a>`;
323
- };
324
- break;
325
- case "select":
326
- a.valueFormatter = (e) => e.value ? (l.options || []).find((h) => h.value === e.value)?.label || e.value : "";
327
- break;
328
- case "lookup":
329
- case "master_detail":
330
- a.valueFormatter = (e) => e.value ? typeof e.value == "object" ? e.value.name || e.value.label || e.value.id || "" : String(e.value) : "";
331
- break;
332
- case "number": {
333
- const e = l.precision;
334
- e !== void 0 && (a.valueFormatter = (t) => t.value == null ? "" : Number(t.value).toFixed(e));
335
- break;
336
- }
337
- case "color":
338
- a.cellRenderer = (e) => {
339
- if (!e.value) return "";
340
- const t = p(e.value);
341
- return `<div class="flex items-center gap-2">
342
- <div style="width: 16px; height: 16px; background-color: ${t}; border: 1px solid #ccc; border-radius: 2px;"></div>
343
- <span>${t}</span>
344
- </div>`;
345
- };
346
- break;
347
- case "rating":
348
- a.cellRenderer = (e) => {
349
- if (e.value == null) return "";
350
- const t = l.max || 5;
351
- return "⭐".repeat(Math.min(e.value, t));
352
- };
353
- break;
354
- case "image":
355
- a.cellRenderer = (e) => {
356
- if (!e.value) return "";
357
- const t = typeof e.value == "string" ? e.value : e.value.url;
358
- return t ? `<img src="${p(t)}" alt="" style="width: 40px; height: 40px; object-fit: cover; border-radius: 4px;" />` : "";
359
- };
360
- break;
361
- case "avatar":
362
- a.cellRenderer = (e) => {
363
- if (!e.value) return "";
364
- const t = typeof e.value == "string" ? e.value : e.value.url;
365
- return t ? `<img src="${p(t)}" alt="" style="width: 32px; height: 32px; object-fit: cover; border-radius: 50%;" />` : "";
366
- };
367
- break;
368
- }
369
- }
370
- export {
371
- fe as default
372
- };