@rufous/ui 0.1.66 → 0.1.67

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.
@@ -47,7 +47,8 @@ function DataGrid({
47
47
  const [columnWidths, setColumnWidths] = (0, import_react.useState)(() => {
48
48
  const widths = {};
49
49
  initialColumns.forEach((col) => {
50
- widths[String(col.key)] = parseInt(col.width || "200");
50
+ const w = col.width || 200;
51
+ widths[String(col.key)] = typeof w === "number" ? w : parseInt(w);
51
52
  });
52
53
  return widths;
53
54
  });
@@ -74,8 +75,8 @@ function DataGrid({
74
75
  if (!resizingColumn) return;
75
76
  const col = columns.find((c) => String(c.key) === resizingColumn);
76
77
  const diff = e.clientX - startX;
77
- const minW = col?.minWidth ? parseInt(col.minWidth) : 80;
78
- const maxW = col?.maxWidth ? parseInt(col.maxWidth) : Infinity;
78
+ const minW = col?.minWidth ? typeof col.minWidth === "number" ? col.minWidth : parseInt(col.minWidth) : 80;
79
+ const maxW = col?.maxWidth ? typeof col.maxWidth === "number" ? col.maxWidth : parseInt(col.maxWidth) : Infinity;
79
80
  const newWidth = Math.min(maxW, Math.max(minW, startWidth + diff));
80
81
  setColumnWidths((prev) => ({ ...prev, [resizingColumn]: newWidth }));
81
82
  };
@@ -166,14 +167,19 @@ function DataGrid({
166
167
  }, [data, filterText, advancedFilters]);
167
168
  const sortedData = (0, import_react.useMemo)(() => {
168
169
  if (!sortKey || !sortDirection) return filteredData;
170
+ const col = columns.find((c) => c.key === sortKey);
169
171
  return [...filteredData].sort((a, b) => {
170
- const aVal = a[sortKey];
171
- const bVal = b[sortKey];
172
+ let aVal = a[sortKey];
173
+ let bVal = b[sortKey];
174
+ if (col?.valueGetter) {
175
+ aVal = col.valueGetter({ value: aVal, row: a, field: String(sortKey) });
176
+ bVal = col.valueGetter({ value: bVal, row: b, field: String(sortKey) });
177
+ }
172
178
  if (aVal < bVal) return sortDirection === "asc" ? -1 : 1;
173
179
  if (aVal > bVal) return sortDirection === "asc" ? 1 : -1;
174
180
  return 0;
175
181
  });
176
- }, [filteredData, sortKey, sortDirection]);
182
+ }, [filteredData, sortKey, sortDirection, columns]);
177
183
  const totalPages = Math.max(1, Math.ceil(sortedData.length / pageSize));
178
184
  const paginatedData = (0, import_react.useMemo)(() => {
179
185
  const start = (currentPage - 1) * pageSize;
@@ -253,8 +259,8 @@ function DataGrid({
253
259
  "th",
254
260
  {
255
261
  key: colKey,
256
- className: `dg-thead-cell${col.pinned === "left" ? " pinned-left" : col.pinned === "right" ? " pinned-right" : ""}`,
257
- style: { width, minWidth: width, left: leftOffset }
262
+ className: `dg-thead-cell${col.pinned === "left" ? " pinned-left" : col.pinned === "right" ? " pinned-right" : ""} ${col.headerClassName || ""}`,
263
+ style: { width, minWidth: width, left: leftOffset, flex: col.flex }
258
264
  },
259
265
  /* @__PURE__ */ import_react.default.createElement("div", { className: "dg-th-inner" }, /* @__PURE__ */ import_react.default.createElement(
260
266
  "div",
@@ -265,7 +271,7 @@ function DataGrid({
265
271
  col.header,
266
272
  isSorted && sortDirection === "asc" && /* @__PURE__ */ import_react.default.createElement(import_lucide_react.ChevronUp, { size: 12 }),
267
273
  isSorted && sortDirection === "desc" && /* @__PURE__ */ import_react.default.createElement(import_lucide_react.ChevronDown, { size: 12 })
268
- ), /* @__PURE__ */ import_react.default.createElement("div", { className: "dg-th-actions" }, /* @__PURE__ */ import_react.default.createElement(
274
+ ), /* @__PURE__ */ import_react.default.createElement("div", { className: "dg-th-actions" }, !col.disableColumnMenu && /* @__PURE__ */ import_react.default.createElement(
269
275
  "button",
270
276
  {
271
277
  className: "dg-th-menu-btn",
@@ -293,22 +299,39 @@ function DataGrid({
293
299
  "td",
294
300
  {
295
301
  key: `${item.id}-${colKey}`,
296
- className: `dg-td${col.pinned === "left" ? " pinned-left" : ""}`,
297
- style: { width, minWidth: width, maxWidth: width, left: leftOffset }
302
+ className: `dg-td${col.pinned === "left" ? " pinned-left" : ""} ${col.cellClassName || ""}`,
303
+ style: { width, minWidth: width, maxWidth: width, left: leftOffset, flex: col.flex }
298
304
  },
299
- col.render ? col.render(item[col.key], item) : String(item[col.key] ?? "")
305
+ (() => {
306
+ const field = String(col.key);
307
+ const rawValue = item[col.key];
308
+ let value = col.valueGetter ? col.valueGetter({ value: rawValue, row: item, field }) : rawValue;
309
+ const formattedValue = col.valueFormatter ? col.valueFormatter({ value, row: item, field }) : value;
310
+ if (col.renderCell) {
311
+ return col.renderCell({ value, row: item, field });
312
+ }
313
+ if (col.render) {
314
+ return col.render(value, item);
315
+ }
316
+ return String(formattedValue ?? "");
317
+ })()
300
318
  );
301
- }), actions && /* @__PURE__ */ import_react.default.createElement("td", { className: "dg-row-actions-cell" }, /* @__PURE__ */ import_react.default.createElement("div", { className: "dg-row-actions" }, /* @__PURE__ */ import_react.default.createElement("div", { className: "dg-action-group" }, actions.map((action, i) => /* @__PURE__ */ import_react.default.createElement(
302
- "button",
303
- {
304
- key: i,
305
- className: "dg-row-action-btn",
306
- style: { color: action.color || "var(--text-secondary)" },
307
- onClick: () => action.onClick(item),
308
- title: action.label
309
- },
310
- action.icon
311
- )))))))))), /* @__PURE__ */ import_react.default.createElement("div", { className: "dg-pagination" }, /* @__PURE__ */ import_react.default.createElement("div", { className: "dg-page-info" }, /* @__PURE__ */ import_react.default.createElement("div", { className: "dg-per-page" }, /* @__PURE__ */ import_react.default.createElement("span", null, "Rows per page:"), /* @__PURE__ */ import_react.default.createElement(
319
+ }), actions && /* @__PURE__ */ import_react.default.createElement("td", { className: "dg-row-actions-cell" }, (() => {
320
+ const resolvedActions = typeof actions === "function" ? actions(item) : actions;
321
+ const visibleActions = resolvedActions.filter((a) => !a.show || a.show(item));
322
+ if (visibleActions.length === 0) return null;
323
+ return /* @__PURE__ */ import_react.default.createElement("div", { className: "dg-row-actions" }, /* @__PURE__ */ import_react.default.createElement("div", { className: "dg-action-group" }, visibleActions.map((action, i) => /* @__PURE__ */ import_react.default.createElement(
324
+ "button",
325
+ {
326
+ key: i,
327
+ className: "dg-row-action-btn",
328
+ style: { color: action.color || "var(--text-secondary)" },
329
+ onClick: () => action.onClick(item),
330
+ title: action.label
331
+ },
332
+ action.icon
333
+ ))));
334
+ })())))))), /* @__PURE__ */ import_react.default.createElement("div", { className: "dg-pagination" }, /* @__PURE__ */ import_react.default.createElement("div", { className: "dg-page-info" }, /* @__PURE__ */ import_react.default.createElement("div", { className: "dg-per-page" }, /* @__PURE__ */ import_react.default.createElement("span", null, "Rows per page:"), /* @__PURE__ */ import_react.default.createElement(
312
335
  "select",
313
336
  {
314
337
  value: pageSize,
@@ -349,7 +372,7 @@ function DataGrid({
349
372
  value: colSearch,
350
373
  onChange: (e) => setColSearch(e.target.value)
351
374
  }
352
- )), columns.filter((c) => c.header.toLowerCase().includes(colSearch.toLowerCase())).map((col) => /* @__PURE__ */ import_react.default.createElement("div", { key: String(col.key), className: "dg-col-row" }, /* @__PURE__ */ import_react.default.createElement("div", { className: "dg-col-label" }, /* @__PURE__ */ import_react.default.createElement("div", { className: "dg-col-dot", style: { background: col.hidden ? "var(--border-color)" : "var(--primary-color)" } }), col.header), /* @__PURE__ */ import_react.default.createElement("button", { className: "dg-icon-btn", onClick: () => toggleHide(String(col.key)) }, col.hidden ? /* @__PURE__ */ import_react.default.createElement(import_lucide_react.EyeOff, { size: 14 }) : /* @__PURE__ */ import_react.default.createElement(import_lucide_react.EyeOff, { size: 14, style: { opacity: 0.4 } }))))), /* @__PURE__ */ import_react.default.createElement("div", { className: "dg-modal-footer" }, /* @__PURE__ */ import_react.default.createElement("button", { className: "dg-action-btn", onClick: () => setColumns((p) => p.map((c) => ({ ...c, hidden: false }))) }, "Show All"), /* @__PURE__ */ import_react.default.createElement("button", { className: "dg-action-btn", onClick: () => setColumns((p) => p.map((c) => ({ ...c, hidden: true }))) }, "Hide All")))), showAdvancedFilter && /* @__PURE__ */ import_react.default.createElement("div", { className: "dg-modal-overlay", onClick: () => setShowAdvancedFilter(false) }, /* @__PURE__ */ import_react.default.createElement("div", { className: "dg-modal dg-modal-wide", onClick: (e) => e.stopPropagation() }, /* @__PURE__ */ import_react.default.createElement("div", { className: "dg-modal-header" }, /* @__PURE__ */ import_react.default.createElement("h3", null, "Filters"), /* @__PURE__ */ import_react.default.createElement("button", { className: "dg-icon-btn", onClick: () => setShowAdvancedFilter(false) }, /* @__PURE__ */ import_react.default.createElement(import_lucide_react.X, { size: 18 }))), /* @__PURE__ */ import_react.default.createElement("div", { className: "dg-modal-body" }, advancedFilters.map((f, idx) => /* @__PURE__ */ import_react.default.createElement("div", { key: idx }, idx > 0 && /* @__PURE__ */ import_react.default.createElement("div", { className: "dg-filter-logic" }, /* @__PURE__ */ import_react.default.createElement(
375
+ )), columns.filter((c) => c.header.toLowerCase().includes(colSearch.toLowerCase()) && c.hideable !== false).map((col) => /* @__PURE__ */ import_react.default.createElement("div", { key: String(col.key), className: "dg-col-row" }, /* @__PURE__ */ import_react.default.createElement("div", { className: "dg-col-label" }, /* @__PURE__ */ import_react.default.createElement("div", { className: "dg-col-dot", style: { background: col.hidden ? "var(--border-color)" : "var(--primary-color)" } }), col.header), /* @__PURE__ */ import_react.default.createElement("button", { className: "dg-icon-btn", onClick: () => toggleHide(String(col.key)) }, col.hidden ? /* @__PURE__ */ import_react.default.createElement(import_lucide_react.EyeOff, { size: 14 }) : /* @__PURE__ */ import_react.default.createElement(import_lucide_react.EyeOff, { size: 14, style: { opacity: 0.4 } }))))), /* @__PURE__ */ import_react.default.createElement("div", { className: "dg-modal-footer" }, /* @__PURE__ */ import_react.default.createElement("button", { className: "dg-action-btn", onClick: () => setColumns((p) => p.map((c) => ({ ...c, hidden: false }))) }, "Show All"), /* @__PURE__ */ import_react.default.createElement("button", { className: "dg-action-btn", onClick: () => setColumns((p) => p.map((c) => c.hideable !== false ? { ...c, hidden: true } : c)) }, "Hide All")))), showAdvancedFilter && /* @__PURE__ */ import_react.default.createElement("div", { className: "dg-modal-overlay", onClick: () => setShowAdvancedFilter(false) }, /* @__PURE__ */ import_react.default.createElement("div", { className: "dg-modal dg-modal-wide", onClick: (e) => e.stopPropagation() }, /* @__PURE__ */ import_react.default.createElement("div", { className: "dg-modal-header" }, /* @__PURE__ */ import_react.default.createElement("h3", null, "Filters"), /* @__PURE__ */ import_react.default.createElement("button", { className: "dg-icon-btn", onClick: () => setShowAdvancedFilter(false) }, /* @__PURE__ */ import_react.default.createElement(import_lucide_react.X, { size: 18 }))), /* @__PURE__ */ import_react.default.createElement("div", { className: "dg-modal-body" }, advancedFilters.map((f, idx) => /* @__PURE__ */ import_react.default.createElement("div", { key: idx }, idx > 0 && /* @__PURE__ */ import_react.default.createElement("div", { className: "dg-filter-logic" }, /* @__PURE__ */ import_react.default.createElement(
353
376
  "button",
354
377
  {
355
378
  className: `dg-logic-btn${f.logic === "AND" ? " active" : ""}`,
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  DataGrid,
3
3
  DataGrid_default
4
- } from "../chunk-J6E3UO2W.js";
4
+ } from "../chunk-IKG2B6JQ.js";
5
5
  import "../chunk-LI4N7JWK.js";
6
6
  export {
7
7
  DataGrid,
@@ -48,7 +48,8 @@ function DataGrid({
48
48
  const [columnWidths, setColumnWidths] = (0, import_react.useState)(() => {
49
49
  const widths = {};
50
50
  initialColumns.forEach((col) => {
51
- widths[String(col.key)] = parseInt(col.width || "200");
51
+ const w = col.width || 200;
52
+ widths[String(col.key)] = typeof w === "number" ? w : parseInt(w);
52
53
  });
53
54
  return widths;
54
55
  });
@@ -75,8 +76,8 @@ function DataGrid({
75
76
  if (!resizingColumn) return;
76
77
  const col = columns.find((c) => String(c.key) === resizingColumn);
77
78
  const diff = e.clientX - startX;
78
- const minW = col?.minWidth ? parseInt(col.minWidth) : 80;
79
- const maxW = col?.maxWidth ? parseInt(col.maxWidth) : Infinity;
79
+ const minW = col?.minWidth ? typeof col.minWidth === "number" ? col.minWidth : parseInt(col.minWidth) : 80;
80
+ const maxW = col?.maxWidth ? typeof col.maxWidth === "number" ? col.maxWidth : parseInt(col.maxWidth) : Infinity;
80
81
  const newWidth = Math.min(maxW, Math.max(minW, startWidth + diff));
81
82
  setColumnWidths((prev) => ({ ...prev, [resizingColumn]: newWidth }));
82
83
  };
@@ -167,14 +168,19 @@ function DataGrid({
167
168
  }, [data, filterText, advancedFilters]);
168
169
  const sortedData = (0, import_react.useMemo)(() => {
169
170
  if (!sortKey || !sortDirection) return filteredData;
171
+ const col = columns.find((c) => c.key === sortKey);
170
172
  return [...filteredData].sort((a, b) => {
171
- const aVal = a[sortKey];
172
- const bVal = b[sortKey];
173
+ let aVal = a[sortKey];
174
+ let bVal = b[sortKey];
175
+ if (col?.valueGetter) {
176
+ aVal = col.valueGetter({ value: aVal, row: a, field: String(sortKey) });
177
+ bVal = col.valueGetter({ value: bVal, row: b, field: String(sortKey) });
178
+ }
173
179
  if (aVal < bVal) return sortDirection === "asc" ? -1 : 1;
174
180
  if (aVal > bVal) return sortDirection === "asc" ? 1 : -1;
175
181
  return 0;
176
182
  });
177
- }, [filteredData, sortKey, sortDirection]);
183
+ }, [filteredData, sortKey, sortDirection, columns]);
178
184
  const totalPages = Math.max(1, Math.ceil(sortedData.length / pageSize));
179
185
  const paginatedData = (0, import_react.useMemo)(() => {
180
186
  const start = (currentPage - 1) * pageSize;
@@ -254,8 +260,8 @@ function DataGrid({
254
260
  "th",
255
261
  {
256
262
  key: colKey,
257
- className: `dg-thead-cell${col.pinned === "left" ? " pinned-left" : col.pinned === "right" ? " pinned-right" : ""}`,
258
- style: { width, minWidth: width, left: leftOffset }
263
+ className: `dg-thead-cell${col.pinned === "left" ? " pinned-left" : col.pinned === "right" ? " pinned-right" : ""} ${col.headerClassName || ""}`,
264
+ style: { width, minWidth: width, left: leftOffset, flex: col.flex }
259
265
  },
260
266
  /* @__PURE__ */ import_react.default.createElement("div", { className: "dg-th-inner" }, /* @__PURE__ */ import_react.default.createElement(
261
267
  "div",
@@ -266,7 +272,7 @@ function DataGrid({
266
272
  col.header,
267
273
  isSorted && sortDirection === "asc" && /* @__PURE__ */ import_react.default.createElement(import_lucide_react.ChevronUp, { size: 12 }),
268
274
  isSorted && sortDirection === "desc" && /* @__PURE__ */ import_react.default.createElement(import_lucide_react.ChevronDown, { size: 12 })
269
- ), /* @__PURE__ */ import_react.default.createElement("div", { className: "dg-th-actions" }, /* @__PURE__ */ import_react.default.createElement(
275
+ ), /* @__PURE__ */ import_react.default.createElement("div", { className: "dg-th-actions" }, !col.disableColumnMenu && /* @__PURE__ */ import_react.default.createElement(
270
276
  "button",
271
277
  {
272
278
  className: "dg-th-menu-btn",
@@ -294,22 +300,39 @@ function DataGrid({
294
300
  "td",
295
301
  {
296
302
  key: `${item.id}-${colKey}`,
297
- className: `dg-td${col.pinned === "left" ? " pinned-left" : ""}`,
298
- style: { width, minWidth: width, maxWidth: width, left: leftOffset }
303
+ className: `dg-td${col.pinned === "left" ? " pinned-left" : ""} ${col.cellClassName || ""}`,
304
+ style: { width, minWidth: width, maxWidth: width, left: leftOffset, flex: col.flex }
299
305
  },
300
- col.render ? col.render(item[col.key], item) : String(item[col.key] ?? "")
306
+ (() => {
307
+ const field = String(col.key);
308
+ const rawValue = item[col.key];
309
+ let value = col.valueGetter ? col.valueGetter({ value: rawValue, row: item, field }) : rawValue;
310
+ const formattedValue = col.valueFormatter ? col.valueFormatter({ value, row: item, field }) : value;
311
+ if (col.renderCell) {
312
+ return col.renderCell({ value, row: item, field });
313
+ }
314
+ if (col.render) {
315
+ return col.render(value, item);
316
+ }
317
+ return String(formattedValue ?? "");
318
+ })()
301
319
  );
302
- }), actions && /* @__PURE__ */ import_react.default.createElement("td", { className: "dg-row-actions-cell" }, /* @__PURE__ */ import_react.default.createElement("div", { className: "dg-row-actions" }, /* @__PURE__ */ import_react.default.createElement("div", { className: "dg-action-group" }, actions.map((action, i) => /* @__PURE__ */ import_react.default.createElement(
303
- "button",
304
- {
305
- key: i,
306
- className: "dg-row-action-btn",
307
- style: { color: action.color || "var(--text-secondary)" },
308
- onClick: () => action.onClick(item),
309
- title: action.label
310
- },
311
- action.icon
312
- )))))))))), /* @__PURE__ */ import_react.default.createElement("div", { className: "dg-pagination" }, /* @__PURE__ */ import_react.default.createElement("div", { className: "dg-page-info" }, /* @__PURE__ */ import_react.default.createElement("div", { className: "dg-per-page" }, /* @__PURE__ */ import_react.default.createElement("span", null, "Rows per page:"), /* @__PURE__ */ import_react.default.createElement(
320
+ }), actions && /* @__PURE__ */ import_react.default.createElement("td", { className: "dg-row-actions-cell" }, (() => {
321
+ const resolvedActions = typeof actions === "function" ? actions(item) : actions;
322
+ const visibleActions = resolvedActions.filter((a) => !a.show || a.show(item));
323
+ if (visibleActions.length === 0) return null;
324
+ return /* @__PURE__ */ import_react.default.createElement("div", { className: "dg-row-actions" }, /* @__PURE__ */ import_react.default.createElement("div", { className: "dg-action-group" }, visibleActions.map((action, i) => /* @__PURE__ */ import_react.default.createElement(
325
+ "button",
326
+ {
327
+ key: i,
328
+ className: "dg-row-action-btn",
329
+ style: { color: action.color || "var(--text-secondary)" },
330
+ onClick: () => action.onClick(item),
331
+ title: action.label
332
+ },
333
+ action.icon
334
+ ))));
335
+ })())))))), /* @__PURE__ */ import_react.default.createElement("div", { className: "dg-pagination" }, /* @__PURE__ */ import_react.default.createElement("div", { className: "dg-page-info" }, /* @__PURE__ */ import_react.default.createElement("div", { className: "dg-per-page" }, /* @__PURE__ */ import_react.default.createElement("span", null, "Rows per page:"), /* @__PURE__ */ import_react.default.createElement(
313
336
  "select",
314
337
  {
315
338
  value: pageSize,
@@ -350,7 +373,7 @@ function DataGrid({
350
373
  value: colSearch,
351
374
  onChange: (e) => setColSearch(e.target.value)
352
375
  }
353
- )), columns.filter((c) => c.header.toLowerCase().includes(colSearch.toLowerCase())).map((col) => /* @__PURE__ */ import_react.default.createElement("div", { key: String(col.key), className: "dg-col-row" }, /* @__PURE__ */ import_react.default.createElement("div", { className: "dg-col-label" }, /* @__PURE__ */ import_react.default.createElement("div", { className: "dg-col-dot", style: { background: col.hidden ? "var(--border-color)" : "var(--primary-color)" } }), col.header), /* @__PURE__ */ import_react.default.createElement("button", { className: "dg-icon-btn", onClick: () => toggleHide(String(col.key)) }, col.hidden ? /* @__PURE__ */ import_react.default.createElement(import_lucide_react.EyeOff, { size: 14 }) : /* @__PURE__ */ import_react.default.createElement(import_lucide_react.EyeOff, { size: 14, style: { opacity: 0.4 } }))))), /* @__PURE__ */ import_react.default.createElement("div", { className: "dg-modal-footer" }, /* @__PURE__ */ import_react.default.createElement("button", { className: "dg-action-btn", onClick: () => setColumns((p) => p.map((c) => ({ ...c, hidden: false }))) }, "Show All"), /* @__PURE__ */ import_react.default.createElement("button", { className: "dg-action-btn", onClick: () => setColumns((p) => p.map((c) => ({ ...c, hidden: true }))) }, "Hide All")))), showAdvancedFilter && /* @__PURE__ */ import_react.default.createElement("div", { className: "dg-modal-overlay", onClick: () => setShowAdvancedFilter(false) }, /* @__PURE__ */ import_react.default.createElement("div", { className: "dg-modal dg-modal-wide", onClick: (e) => e.stopPropagation() }, /* @__PURE__ */ import_react.default.createElement("div", { className: "dg-modal-header" }, /* @__PURE__ */ import_react.default.createElement("h3", null, "Filters"), /* @__PURE__ */ import_react.default.createElement("button", { className: "dg-icon-btn", onClick: () => setShowAdvancedFilter(false) }, /* @__PURE__ */ import_react.default.createElement(import_lucide_react.X, { size: 18 }))), /* @__PURE__ */ import_react.default.createElement("div", { className: "dg-modal-body" }, advancedFilters.map((f, idx) => /* @__PURE__ */ import_react.default.createElement("div", { key: idx }, idx > 0 && /* @__PURE__ */ import_react.default.createElement("div", { className: "dg-filter-logic" }, /* @__PURE__ */ import_react.default.createElement(
376
+ )), columns.filter((c) => c.header.toLowerCase().includes(colSearch.toLowerCase()) && c.hideable !== false).map((col) => /* @__PURE__ */ import_react.default.createElement("div", { key: String(col.key), className: "dg-col-row" }, /* @__PURE__ */ import_react.default.createElement("div", { className: "dg-col-label" }, /* @__PURE__ */ import_react.default.createElement("div", { className: "dg-col-dot", style: { background: col.hidden ? "var(--border-color)" : "var(--primary-color)" } }), col.header), /* @__PURE__ */ import_react.default.createElement("button", { className: "dg-icon-btn", onClick: () => toggleHide(String(col.key)) }, col.hidden ? /* @__PURE__ */ import_react.default.createElement(import_lucide_react.EyeOff, { size: 14 }) : /* @__PURE__ */ import_react.default.createElement(import_lucide_react.EyeOff, { size: 14, style: { opacity: 0.4 } }))))), /* @__PURE__ */ import_react.default.createElement("div", { className: "dg-modal-footer" }, /* @__PURE__ */ import_react.default.createElement("button", { className: "dg-action-btn", onClick: () => setColumns((p) => p.map((c) => ({ ...c, hidden: false }))) }, "Show All"), /* @__PURE__ */ import_react.default.createElement("button", { className: "dg-action-btn", onClick: () => setColumns((p) => p.map((c) => c.hideable !== false ? { ...c, hidden: true } : c)) }, "Hide All")))), showAdvancedFilter && /* @__PURE__ */ import_react.default.createElement("div", { className: "dg-modal-overlay", onClick: () => setShowAdvancedFilter(false) }, /* @__PURE__ */ import_react.default.createElement("div", { className: "dg-modal dg-modal-wide", onClick: (e) => e.stopPropagation() }, /* @__PURE__ */ import_react.default.createElement("div", { className: "dg-modal-header" }, /* @__PURE__ */ import_react.default.createElement("h3", null, "Filters"), /* @__PURE__ */ import_react.default.createElement("button", { className: "dg-icon-btn", onClick: () => setShowAdvancedFilter(false) }, /* @__PURE__ */ import_react.default.createElement(import_lucide_react.X, { size: 18 }))), /* @__PURE__ */ import_react.default.createElement("div", { className: "dg-modal-body" }, advancedFilters.map((f, idx) => /* @__PURE__ */ import_react.default.createElement("div", { key: idx }, idx > 0 && /* @__PURE__ */ import_react.default.createElement("div", { className: "dg-filter-logic" }, /* @__PURE__ */ import_react.default.createElement(
354
377
  "button",
355
378
  {
356
379
  className: `dg-logic-btn${f.logic === "AND" ? " active" : ""}`,
@@ -1,7 +1,7 @@
1
1
  import "../chunk-7KRG7VNW.js";
2
2
  import {
3
3
  DataGrid
4
- } from "../chunk-J6E3UO2W.js";
4
+ } from "../chunk-IKG2B6JQ.js";
5
5
  import "../chunk-LI4N7JWK.js";
6
6
  export {
7
7
  DataGrid
@@ -7,22 +7,45 @@ interface Column<T> {
7
7
  sortable?: boolean;
8
8
  filterable?: boolean;
9
9
  render?: (value: any, item: T) => React__default.ReactNode;
10
- width?: string;
11
- minWidth?: string;
12
- maxWidth?: string;
10
+ renderCell?: (params: {
11
+ value: any;
12
+ row: T;
13
+ field: string;
14
+ }) => React__default.ReactNode;
15
+ valueGetter?: (params: {
16
+ value: any;
17
+ row: T;
18
+ field: string;
19
+ }) => any;
20
+ valueFormatter?: (params: {
21
+ value: any;
22
+ row: T;
23
+ field: string;
24
+ }) => string;
25
+ width?: string | number;
26
+ minWidth?: string | number;
27
+ maxWidth?: string | number;
28
+ flex?: number;
13
29
  pinned?: 'left' | 'right';
14
30
  hidden?: boolean;
31
+ type?: 'string' | 'number' | 'date' | 'boolean' | 'actions';
32
+ editable?: boolean;
33
+ headerClassName?: string;
34
+ cellClassName?: string;
35
+ hideable?: boolean;
36
+ disableColumnMenu?: boolean;
15
37
  }
16
38
  interface Action<T> {
17
39
  label: string;
18
40
  icon: React__default.ReactNode;
19
41
  onClick: (item: T) => void;
20
42
  color?: string;
43
+ show?: (item: T) => boolean;
21
44
  }
22
45
  interface DataGridProps<T> {
23
46
  columns: Column<T>[];
24
47
  data: T[];
25
- actions?: Action<T>[];
48
+ actions?: Action<T>[] | ((item: T) => Action<T>[]);
26
49
  pageSize?: number;
27
50
  pageSizeOptions?: number[];
28
51
  title?: string;
@@ -7,22 +7,45 @@ interface Column<T> {
7
7
  sortable?: boolean;
8
8
  filterable?: boolean;
9
9
  render?: (value: any, item: T) => React__default.ReactNode;
10
- width?: string;
11
- minWidth?: string;
12
- maxWidth?: string;
10
+ renderCell?: (params: {
11
+ value: any;
12
+ row: T;
13
+ field: string;
14
+ }) => React__default.ReactNode;
15
+ valueGetter?: (params: {
16
+ value: any;
17
+ row: T;
18
+ field: string;
19
+ }) => any;
20
+ valueFormatter?: (params: {
21
+ value: any;
22
+ row: T;
23
+ field: string;
24
+ }) => string;
25
+ width?: string | number;
26
+ minWidth?: string | number;
27
+ maxWidth?: string | number;
28
+ flex?: number;
13
29
  pinned?: 'left' | 'right';
14
30
  hidden?: boolean;
31
+ type?: 'string' | 'number' | 'date' | 'boolean' | 'actions';
32
+ editable?: boolean;
33
+ headerClassName?: string;
34
+ cellClassName?: string;
35
+ hideable?: boolean;
36
+ disableColumnMenu?: boolean;
15
37
  }
16
38
  interface Action<T> {
17
39
  label: string;
18
40
  icon: React__default.ReactNode;
19
41
  onClick: (item: T) => void;
20
42
  color?: string;
43
+ show?: (item: T) => boolean;
21
44
  }
22
45
  interface DataGridProps<T> {
23
46
  columns: Column<T>[];
24
47
  data: T[];
25
- actions?: Action<T>[];
48
+ actions?: Action<T>[] | ((item: T) => Action<T>[]);
26
49
  pageSize?: number;
27
50
  pageSizeOptions?: number[];
28
51
  title?: string;
@@ -54,16 +54,17 @@ import "../chunk-XPJVVKOU.js";
54
54
  import "../chunk-GL43GPEM.js";
55
55
  import "../chunk-FZCFFVXW.js";
56
56
  import "../chunk-N26C33E6.js";
57
- import "../chunk-EB6MPFGC.js";
58
- import "../chunk-UPCMMCPQ.js";
59
57
  import "../chunk-AH6RCYDL.js";
60
58
  import "../chunk-3IBCGGN3.js";
61
59
  import "../chunk-MNPAE2ZF.js";
62
60
  import "../chunk-Q5XKCUE3.js";
63
- import "../chunk-X357WQOT.js";
64
- import "../chunk-7KRG7VNW.js";
65
- import "../chunk-J6E3UO2W.js";
66
61
  import "../chunk-GJGRMMAQ.js";
62
+ import "../chunk-GHCM2AWR.js";
63
+ import "../chunk-UPCMMCPQ.js";
64
+ import "../chunk-7KRG7VNW.js";
65
+ import "../chunk-BOE27BFQ.js";
66
+ import "../chunk-S7BNFVQO.js";
67
+ import "../chunk-X357WQOT.js";
67
68
  import "../chunk-66HHM7VI.js";
68
69
  import "../chunk-QPGJCRBS.js";
69
70
  import "../chunk-U7SARO5B.js";
@@ -71,8 +72,7 @@ import "../chunk-BMMDUQDJ.js";
71
72
  import "../chunk-R3GARAVJ.js";
72
73
  import "../chunk-YRLN3TBF.js";
73
74
  import "../chunk-CTBYVXFP.js";
74
- import "../chunk-BOE27BFQ.js";
75
- import "../chunk-S7BNFVQO.js";
75
+ import "../chunk-IKG2B6JQ.js";
76
76
  import "../chunk-LI4N7JWK.js";
77
77
  export {
78
78
  BaseDialog_default as default
@@ -54,16 +54,17 @@ import "../chunk-XPJVVKOU.js";
54
54
  import "../chunk-GL43GPEM.js";
55
55
  import "../chunk-FZCFFVXW.js";
56
56
  import "../chunk-N26C33E6.js";
57
- import "../chunk-EB6MPFGC.js";
58
- import "../chunk-UPCMMCPQ.js";
59
57
  import "../chunk-AH6RCYDL.js";
60
58
  import "../chunk-3IBCGGN3.js";
61
59
  import "../chunk-MNPAE2ZF.js";
62
60
  import "../chunk-Q5XKCUE3.js";
63
- import "../chunk-X357WQOT.js";
64
- import "../chunk-7KRG7VNW.js";
65
- import "../chunk-J6E3UO2W.js";
66
61
  import "../chunk-GJGRMMAQ.js";
62
+ import "../chunk-GHCM2AWR.js";
63
+ import "../chunk-UPCMMCPQ.js";
64
+ import "../chunk-7KRG7VNW.js";
65
+ import "../chunk-BOE27BFQ.js";
66
+ import "../chunk-S7BNFVQO.js";
67
+ import "../chunk-X357WQOT.js";
67
68
  import "../chunk-66HHM7VI.js";
68
69
  import "../chunk-QPGJCRBS.js";
69
70
  import "../chunk-U7SARO5B.js";
@@ -71,8 +72,7 @@ import "../chunk-BMMDUQDJ.js";
71
72
  import "../chunk-R3GARAVJ.js";
72
73
  import "../chunk-YRLN3TBF.js";
73
74
  import "../chunk-CTBYVXFP.js";
74
- import "../chunk-BOE27BFQ.js";
75
- import "../chunk-S7BNFVQO.js";
75
+ import "../chunk-IKG2B6JQ.js";
76
76
  import "../chunk-LI4N7JWK.js";
77
77
  export {
78
78
  BaseDialog_default as BaseDialog
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  AddressLookup_default
3
- } from "../chunk-EB6MPFGC.js";
3
+ } from "../chunk-GHCM2AWR.js";
4
4
  import "../chunk-UPCMMCPQ.js";
5
- import "../chunk-CTBYVXFP.js";
6
5
  import "../chunk-BOE27BFQ.js";
7
6
  import "../chunk-S7BNFVQO.js";
7
+ import "../chunk-CTBYVXFP.js";
8
8
  import "../chunk-LI4N7JWK.js";
9
9
  export {
10
10
  AddressLookup_default as default
@@ -30,7 +30,8 @@ function DataGrid({
30
30
  const [columnWidths, setColumnWidths] = useState(() => {
31
31
  const widths = {};
32
32
  initialColumns.forEach((col) => {
33
- widths[String(col.key)] = parseInt(col.width || "200");
33
+ const w = col.width || 200;
34
+ widths[String(col.key)] = typeof w === "number" ? w : parseInt(w);
34
35
  });
35
36
  return widths;
36
37
  });
@@ -57,8 +58,8 @@ function DataGrid({
57
58
  if (!resizingColumn) return;
58
59
  const col = columns.find((c) => String(c.key) === resizingColumn);
59
60
  const diff = e.clientX - startX;
60
- const minW = col?.minWidth ? parseInt(col.minWidth) : 80;
61
- const maxW = col?.maxWidth ? parseInt(col.maxWidth) : Infinity;
61
+ const minW = col?.minWidth ? typeof col.minWidth === "number" ? col.minWidth : parseInt(col.minWidth) : 80;
62
+ const maxW = col?.maxWidth ? typeof col.maxWidth === "number" ? col.maxWidth : parseInt(col.maxWidth) : Infinity;
62
63
  const newWidth = Math.min(maxW, Math.max(minW, startWidth + diff));
63
64
  setColumnWidths((prev) => ({ ...prev, [resizingColumn]: newWidth }));
64
65
  };
@@ -149,14 +150,19 @@ function DataGrid({
149
150
  }, [data, filterText, advancedFilters]);
150
151
  const sortedData = useMemo(() => {
151
152
  if (!sortKey || !sortDirection) return filteredData;
153
+ const col = columns.find((c) => c.key === sortKey);
152
154
  return [...filteredData].sort((a, b) => {
153
- const aVal = a[sortKey];
154
- const bVal = b[sortKey];
155
+ let aVal = a[sortKey];
156
+ let bVal = b[sortKey];
157
+ if (col?.valueGetter) {
158
+ aVal = col.valueGetter({ value: aVal, row: a, field: String(sortKey) });
159
+ bVal = col.valueGetter({ value: bVal, row: b, field: String(sortKey) });
160
+ }
155
161
  if (aVal < bVal) return sortDirection === "asc" ? -1 : 1;
156
162
  if (aVal > bVal) return sortDirection === "asc" ? 1 : -1;
157
163
  return 0;
158
164
  });
159
- }, [filteredData, sortKey, sortDirection]);
165
+ }, [filteredData, sortKey, sortDirection, columns]);
160
166
  const totalPages = Math.max(1, Math.ceil(sortedData.length / pageSize));
161
167
  const paginatedData = useMemo(() => {
162
168
  const start = (currentPage - 1) * pageSize;
@@ -236,8 +242,8 @@ function DataGrid({
236
242
  "th",
237
243
  {
238
244
  key: colKey,
239
- className: `dg-thead-cell${col.pinned === "left" ? " pinned-left" : col.pinned === "right" ? " pinned-right" : ""}`,
240
- style: { width, minWidth: width, left: leftOffset }
245
+ className: `dg-thead-cell${col.pinned === "left" ? " pinned-left" : col.pinned === "right" ? " pinned-right" : ""} ${col.headerClassName || ""}`,
246
+ style: { width, minWidth: width, left: leftOffset, flex: col.flex }
241
247
  },
242
248
  /* @__PURE__ */ React.createElement("div", { className: "dg-th-inner" }, /* @__PURE__ */ React.createElement(
243
249
  "div",
@@ -248,7 +254,7 @@ function DataGrid({
248
254
  col.header,
249
255
  isSorted && sortDirection === "asc" && /* @__PURE__ */ React.createElement(ChevronUp, { size: 12 }),
250
256
  isSorted && sortDirection === "desc" && /* @__PURE__ */ React.createElement(ChevronDown, { size: 12 })
251
- ), /* @__PURE__ */ React.createElement("div", { className: "dg-th-actions" }, /* @__PURE__ */ React.createElement(
257
+ ), /* @__PURE__ */ React.createElement("div", { className: "dg-th-actions" }, !col.disableColumnMenu && /* @__PURE__ */ React.createElement(
252
258
  "button",
253
259
  {
254
260
  className: "dg-th-menu-btn",
@@ -276,22 +282,39 @@ function DataGrid({
276
282
  "td",
277
283
  {
278
284
  key: `${item.id}-${colKey}`,
279
- className: `dg-td${col.pinned === "left" ? " pinned-left" : ""}`,
280
- style: { width, minWidth: width, maxWidth: width, left: leftOffset }
285
+ className: `dg-td${col.pinned === "left" ? " pinned-left" : ""} ${col.cellClassName || ""}`,
286
+ style: { width, minWidth: width, maxWidth: width, left: leftOffset, flex: col.flex }
281
287
  },
282
- col.render ? col.render(item[col.key], item) : String(item[col.key] ?? "")
288
+ (() => {
289
+ const field = String(col.key);
290
+ const rawValue = item[col.key];
291
+ let value = col.valueGetter ? col.valueGetter({ value: rawValue, row: item, field }) : rawValue;
292
+ const formattedValue = col.valueFormatter ? col.valueFormatter({ value, row: item, field }) : value;
293
+ if (col.renderCell) {
294
+ return col.renderCell({ value, row: item, field });
295
+ }
296
+ if (col.render) {
297
+ return col.render(value, item);
298
+ }
299
+ return String(formattedValue ?? "");
300
+ })()
283
301
  );
284
- }), actions && /* @__PURE__ */ React.createElement("td", { className: "dg-row-actions-cell" }, /* @__PURE__ */ React.createElement("div", { className: "dg-row-actions" }, /* @__PURE__ */ React.createElement("div", { className: "dg-action-group" }, actions.map((action, i) => /* @__PURE__ */ React.createElement(
285
- "button",
286
- {
287
- key: i,
288
- className: "dg-row-action-btn",
289
- style: { color: action.color || "var(--text-secondary)" },
290
- onClick: () => action.onClick(item),
291
- title: action.label
292
- },
293
- action.icon
294
- )))))))))), /* @__PURE__ */ React.createElement("div", { className: "dg-pagination" }, /* @__PURE__ */ React.createElement("div", { className: "dg-page-info" }, /* @__PURE__ */ React.createElement("div", { className: "dg-per-page" }, /* @__PURE__ */ React.createElement("span", null, "Rows per page:"), /* @__PURE__ */ React.createElement(
302
+ }), actions && /* @__PURE__ */ React.createElement("td", { className: "dg-row-actions-cell" }, (() => {
303
+ const resolvedActions = typeof actions === "function" ? actions(item) : actions;
304
+ const visibleActions = resolvedActions.filter((a) => !a.show || a.show(item));
305
+ if (visibleActions.length === 0) return null;
306
+ return /* @__PURE__ */ React.createElement("div", { className: "dg-row-actions" }, /* @__PURE__ */ React.createElement("div", { className: "dg-action-group" }, visibleActions.map((action, i) => /* @__PURE__ */ React.createElement(
307
+ "button",
308
+ {
309
+ key: i,
310
+ className: "dg-row-action-btn",
311
+ style: { color: action.color || "var(--text-secondary)" },
312
+ onClick: () => action.onClick(item),
313
+ title: action.label
314
+ },
315
+ action.icon
316
+ ))));
317
+ })())))))), /* @__PURE__ */ React.createElement("div", { className: "dg-pagination" }, /* @__PURE__ */ React.createElement("div", { className: "dg-page-info" }, /* @__PURE__ */ React.createElement("div", { className: "dg-per-page" }, /* @__PURE__ */ React.createElement("span", null, "Rows per page:"), /* @__PURE__ */ React.createElement(
295
318
  "select",
296
319
  {
297
320
  value: pageSize,
@@ -332,7 +355,7 @@ function DataGrid({
332
355
  value: colSearch,
333
356
  onChange: (e) => setColSearch(e.target.value)
334
357
  }
335
- )), columns.filter((c) => c.header.toLowerCase().includes(colSearch.toLowerCase())).map((col) => /* @__PURE__ */ React.createElement("div", { key: String(col.key), className: "dg-col-row" }, /* @__PURE__ */ React.createElement("div", { className: "dg-col-label" }, /* @__PURE__ */ React.createElement("div", { className: "dg-col-dot", style: { background: col.hidden ? "var(--border-color)" : "var(--primary-color)" } }), col.header), /* @__PURE__ */ React.createElement("button", { className: "dg-icon-btn", onClick: () => toggleHide(String(col.key)) }, col.hidden ? /* @__PURE__ */ React.createElement(EyeOff, { size: 14 }) : /* @__PURE__ */ React.createElement(EyeOff, { size: 14, style: { opacity: 0.4 } }))))), /* @__PURE__ */ React.createElement("div", { className: "dg-modal-footer" }, /* @__PURE__ */ React.createElement("button", { className: "dg-action-btn", onClick: () => setColumns((p) => p.map((c) => ({ ...c, hidden: false }))) }, "Show All"), /* @__PURE__ */ React.createElement("button", { className: "dg-action-btn", onClick: () => setColumns((p) => p.map((c) => ({ ...c, hidden: true }))) }, "Hide All")))), showAdvancedFilter && /* @__PURE__ */ React.createElement("div", { className: "dg-modal-overlay", onClick: () => setShowAdvancedFilter(false) }, /* @__PURE__ */ React.createElement("div", { className: "dg-modal dg-modal-wide", onClick: (e) => e.stopPropagation() }, /* @__PURE__ */ React.createElement("div", { className: "dg-modal-header" }, /* @__PURE__ */ React.createElement("h3", null, "Filters"), /* @__PURE__ */ React.createElement("button", { className: "dg-icon-btn", onClick: () => setShowAdvancedFilter(false) }, /* @__PURE__ */ React.createElement(X, { size: 18 }))), /* @__PURE__ */ React.createElement("div", { className: "dg-modal-body" }, advancedFilters.map((f, idx) => /* @__PURE__ */ React.createElement("div", { key: idx }, idx > 0 && /* @__PURE__ */ React.createElement("div", { className: "dg-filter-logic" }, /* @__PURE__ */ React.createElement(
358
+ )), columns.filter((c) => c.header.toLowerCase().includes(colSearch.toLowerCase()) && c.hideable !== false).map((col) => /* @__PURE__ */ React.createElement("div", { key: String(col.key), className: "dg-col-row" }, /* @__PURE__ */ React.createElement("div", { className: "dg-col-label" }, /* @__PURE__ */ React.createElement("div", { className: "dg-col-dot", style: { background: col.hidden ? "var(--border-color)" : "var(--primary-color)" } }), col.header), /* @__PURE__ */ React.createElement("button", { className: "dg-icon-btn", onClick: () => toggleHide(String(col.key)) }, col.hidden ? /* @__PURE__ */ React.createElement(EyeOff, { size: 14 }) : /* @__PURE__ */ React.createElement(EyeOff, { size: 14, style: { opacity: 0.4 } }))))), /* @__PURE__ */ React.createElement("div", { className: "dg-modal-footer" }, /* @__PURE__ */ React.createElement("button", { className: "dg-action-btn", onClick: () => setColumns((p) => p.map((c) => ({ ...c, hidden: false }))) }, "Show All"), /* @__PURE__ */ React.createElement("button", { className: "dg-action-btn", onClick: () => setColumns((p) => p.map((c) => c.hideable !== false ? { ...c, hidden: true } : c)) }, "Hide All")))), showAdvancedFilter && /* @__PURE__ */ React.createElement("div", { className: "dg-modal-overlay", onClick: () => setShowAdvancedFilter(false) }, /* @__PURE__ */ React.createElement("div", { className: "dg-modal dg-modal-wide", onClick: (e) => e.stopPropagation() }, /* @__PURE__ */ React.createElement("div", { className: "dg-modal-header" }, /* @__PURE__ */ React.createElement("h3", null, "Filters"), /* @__PURE__ */ React.createElement("button", { className: "dg-icon-btn", onClick: () => setShowAdvancedFilter(false) }, /* @__PURE__ */ React.createElement(X, { size: 18 }))), /* @__PURE__ */ React.createElement("div", { className: "dg-modal-body" }, advancedFilters.map((f, idx) => /* @__PURE__ */ React.createElement("div", { key: idx }, idx > 0 && /* @__PURE__ */ React.createElement("div", { className: "dg-filter-logic" }, /* @__PURE__ */ React.createElement(
336
359
  "button",
337
360
  {
338
361
  className: `dg-logic-btn${f.logic === "AND" ? " active" : ""}`,
package/dist/main.cjs CHANGED
@@ -30388,7 +30388,8 @@ function DataGrid({
30388
30388
  const [columnWidths, setColumnWidths] = (0, import_react18.useState)(() => {
30389
30389
  const widths = {};
30390
30390
  initialColumns.forEach((col) => {
30391
- widths[String(col.key)] = parseInt(col.width || "200");
30391
+ const w = col.width || 200;
30392
+ widths[String(col.key)] = typeof w === "number" ? w : parseInt(w);
30392
30393
  });
30393
30394
  return widths;
30394
30395
  });
@@ -30415,8 +30416,8 @@ function DataGrid({
30415
30416
  if (!resizingColumn) return;
30416
30417
  const col = columns.find((c) => String(c.key) === resizingColumn);
30417
30418
  const diff = e.clientX - startX;
30418
- const minW = col?.minWidth ? parseInt(col.minWidth) : 80;
30419
- const maxW = col?.maxWidth ? parseInt(col.maxWidth) : Infinity;
30419
+ const minW = col?.minWidth ? typeof col.minWidth === "number" ? col.minWidth : parseInt(col.minWidth) : 80;
30420
+ const maxW = col?.maxWidth ? typeof col.maxWidth === "number" ? col.maxWidth : parseInt(col.maxWidth) : Infinity;
30420
30421
  const newWidth = Math.min(maxW, Math.max(minW, startWidth + diff));
30421
30422
  setColumnWidths((prev) => ({ ...prev, [resizingColumn]: newWidth }));
30422
30423
  };
@@ -30507,14 +30508,19 @@ function DataGrid({
30507
30508
  }, [data, filterText, advancedFilters]);
30508
30509
  const sortedData = (0, import_react18.useMemo)(() => {
30509
30510
  if (!sortKey || !sortDirection) return filteredData;
30511
+ const col = columns.find((c) => c.key === sortKey);
30510
30512
  return [...filteredData].sort((a, b) => {
30511
- const aVal = a[sortKey];
30512
- const bVal = b[sortKey];
30513
+ let aVal = a[sortKey];
30514
+ let bVal = b[sortKey];
30515
+ if (col?.valueGetter) {
30516
+ aVal = col.valueGetter({ value: aVal, row: a, field: String(sortKey) });
30517
+ bVal = col.valueGetter({ value: bVal, row: b, field: String(sortKey) });
30518
+ }
30513
30519
  if (aVal < bVal) return sortDirection === "asc" ? -1 : 1;
30514
30520
  if (aVal > bVal) return sortDirection === "asc" ? 1 : -1;
30515
30521
  return 0;
30516
30522
  });
30517
- }, [filteredData, sortKey, sortDirection]);
30523
+ }, [filteredData, sortKey, sortDirection, columns]);
30518
30524
  const totalPages = Math.max(1, Math.ceil(sortedData.length / pageSize));
30519
30525
  const paginatedData = (0, import_react18.useMemo)(() => {
30520
30526
  const start = (currentPage - 1) * pageSize;
@@ -30594,8 +30600,8 @@ function DataGrid({
30594
30600
  "th",
30595
30601
  {
30596
30602
  key: colKey,
30597
- className: `dg-thead-cell${col.pinned === "left" ? " pinned-left" : col.pinned === "right" ? " pinned-right" : ""}`,
30598
- style: { width, minWidth: width, left: leftOffset }
30603
+ className: `dg-thead-cell${col.pinned === "left" ? " pinned-left" : col.pinned === "right" ? " pinned-right" : ""} ${col.headerClassName || ""}`,
30604
+ style: { width, minWidth: width, left: leftOffset, flex: col.flex }
30599
30605
  },
30600
30606
  /* @__PURE__ */ import_react18.default.createElement("div", { className: "dg-th-inner" }, /* @__PURE__ */ import_react18.default.createElement(
30601
30607
  "div",
@@ -30606,7 +30612,7 @@ function DataGrid({
30606
30612
  col.header,
30607
30613
  isSorted && sortDirection === "asc" && /* @__PURE__ */ import_react18.default.createElement(import_lucide_react.ChevronUp, { size: 12 }),
30608
30614
  isSorted && sortDirection === "desc" && /* @__PURE__ */ import_react18.default.createElement(import_lucide_react.ChevronDown, { size: 12 })
30609
- ), /* @__PURE__ */ import_react18.default.createElement("div", { className: "dg-th-actions" }, /* @__PURE__ */ import_react18.default.createElement(
30615
+ ), /* @__PURE__ */ import_react18.default.createElement("div", { className: "dg-th-actions" }, !col.disableColumnMenu && /* @__PURE__ */ import_react18.default.createElement(
30610
30616
  "button",
30611
30617
  {
30612
30618
  className: "dg-th-menu-btn",
@@ -30634,22 +30640,39 @@ function DataGrid({
30634
30640
  "td",
30635
30641
  {
30636
30642
  key: `${item.id}-${colKey}`,
30637
- className: `dg-td${col.pinned === "left" ? " pinned-left" : ""}`,
30638
- style: { width, minWidth: width, maxWidth: width, left: leftOffset }
30643
+ className: `dg-td${col.pinned === "left" ? " pinned-left" : ""} ${col.cellClassName || ""}`,
30644
+ style: { width, minWidth: width, maxWidth: width, left: leftOffset, flex: col.flex }
30639
30645
  },
30640
- col.render ? col.render(item[col.key], item) : String(item[col.key] ?? "")
30646
+ (() => {
30647
+ const field = String(col.key);
30648
+ const rawValue = item[col.key];
30649
+ let value = col.valueGetter ? col.valueGetter({ value: rawValue, row: item, field }) : rawValue;
30650
+ const formattedValue = col.valueFormatter ? col.valueFormatter({ value, row: item, field }) : value;
30651
+ if (col.renderCell) {
30652
+ return col.renderCell({ value, row: item, field });
30653
+ }
30654
+ if (col.render) {
30655
+ return col.render(value, item);
30656
+ }
30657
+ return String(formattedValue ?? "");
30658
+ })()
30641
30659
  );
30642
- }), actions && /* @__PURE__ */ import_react18.default.createElement("td", { className: "dg-row-actions-cell" }, /* @__PURE__ */ import_react18.default.createElement("div", { className: "dg-row-actions" }, /* @__PURE__ */ import_react18.default.createElement("div", { className: "dg-action-group" }, actions.map((action, i) => /* @__PURE__ */ import_react18.default.createElement(
30643
- "button",
30644
- {
30645
- key: i,
30646
- className: "dg-row-action-btn",
30647
- style: { color: action.color || "var(--text-secondary)" },
30648
- onClick: () => action.onClick(item),
30649
- title: action.label
30650
- },
30651
- action.icon
30652
- )))))))))), /* @__PURE__ */ import_react18.default.createElement("div", { className: "dg-pagination" }, /* @__PURE__ */ import_react18.default.createElement("div", { className: "dg-page-info" }, /* @__PURE__ */ import_react18.default.createElement("div", { className: "dg-per-page" }, /* @__PURE__ */ import_react18.default.createElement("span", null, "Rows per page:"), /* @__PURE__ */ import_react18.default.createElement(
30660
+ }), actions && /* @__PURE__ */ import_react18.default.createElement("td", { className: "dg-row-actions-cell" }, (() => {
30661
+ const resolvedActions = typeof actions === "function" ? actions(item) : actions;
30662
+ const visibleActions = resolvedActions.filter((a) => !a.show || a.show(item));
30663
+ if (visibleActions.length === 0) return null;
30664
+ return /* @__PURE__ */ import_react18.default.createElement("div", { className: "dg-row-actions" }, /* @__PURE__ */ import_react18.default.createElement("div", { className: "dg-action-group" }, visibleActions.map((action, i) => /* @__PURE__ */ import_react18.default.createElement(
30665
+ "button",
30666
+ {
30667
+ key: i,
30668
+ className: "dg-row-action-btn",
30669
+ style: { color: action.color || "var(--text-secondary)" },
30670
+ onClick: () => action.onClick(item),
30671
+ title: action.label
30672
+ },
30673
+ action.icon
30674
+ ))));
30675
+ })())))))), /* @__PURE__ */ import_react18.default.createElement("div", { className: "dg-pagination" }, /* @__PURE__ */ import_react18.default.createElement("div", { className: "dg-page-info" }, /* @__PURE__ */ import_react18.default.createElement("div", { className: "dg-per-page" }, /* @__PURE__ */ import_react18.default.createElement("span", null, "Rows per page:"), /* @__PURE__ */ import_react18.default.createElement(
30653
30676
  "select",
30654
30677
  {
30655
30678
  value: pageSize,
@@ -30690,7 +30713,7 @@ function DataGrid({
30690
30713
  value: colSearch,
30691
30714
  onChange: (e) => setColSearch(e.target.value)
30692
30715
  }
30693
- )), columns.filter((c) => c.header.toLowerCase().includes(colSearch.toLowerCase())).map((col) => /* @__PURE__ */ import_react18.default.createElement("div", { key: String(col.key), className: "dg-col-row" }, /* @__PURE__ */ import_react18.default.createElement("div", { className: "dg-col-label" }, /* @__PURE__ */ import_react18.default.createElement("div", { className: "dg-col-dot", style: { background: col.hidden ? "var(--border-color)" : "var(--primary-color)" } }), col.header), /* @__PURE__ */ import_react18.default.createElement("button", { className: "dg-icon-btn", onClick: () => toggleHide(String(col.key)) }, col.hidden ? /* @__PURE__ */ import_react18.default.createElement(import_lucide_react.EyeOff, { size: 14 }) : /* @__PURE__ */ import_react18.default.createElement(import_lucide_react.EyeOff, { size: 14, style: { opacity: 0.4 } }))))), /* @__PURE__ */ import_react18.default.createElement("div", { className: "dg-modal-footer" }, /* @__PURE__ */ import_react18.default.createElement("button", { className: "dg-action-btn", onClick: () => setColumns((p) => p.map((c) => ({ ...c, hidden: false }))) }, "Show All"), /* @__PURE__ */ import_react18.default.createElement("button", { className: "dg-action-btn", onClick: () => setColumns((p) => p.map((c) => ({ ...c, hidden: true }))) }, "Hide All")))), showAdvancedFilter && /* @__PURE__ */ import_react18.default.createElement("div", { className: "dg-modal-overlay", onClick: () => setShowAdvancedFilter(false) }, /* @__PURE__ */ import_react18.default.createElement("div", { className: "dg-modal dg-modal-wide", onClick: (e) => e.stopPropagation() }, /* @__PURE__ */ import_react18.default.createElement("div", { className: "dg-modal-header" }, /* @__PURE__ */ import_react18.default.createElement("h3", null, "Filters"), /* @__PURE__ */ import_react18.default.createElement("button", { className: "dg-icon-btn", onClick: () => setShowAdvancedFilter(false) }, /* @__PURE__ */ import_react18.default.createElement(import_lucide_react.X, { size: 18 }))), /* @__PURE__ */ import_react18.default.createElement("div", { className: "dg-modal-body" }, advancedFilters.map((f, idx) => /* @__PURE__ */ import_react18.default.createElement("div", { key: idx }, idx > 0 && /* @__PURE__ */ import_react18.default.createElement("div", { className: "dg-filter-logic" }, /* @__PURE__ */ import_react18.default.createElement(
30716
+ )), columns.filter((c) => c.header.toLowerCase().includes(colSearch.toLowerCase()) && c.hideable !== false).map((col) => /* @__PURE__ */ import_react18.default.createElement("div", { key: String(col.key), className: "dg-col-row" }, /* @__PURE__ */ import_react18.default.createElement("div", { className: "dg-col-label" }, /* @__PURE__ */ import_react18.default.createElement("div", { className: "dg-col-dot", style: { background: col.hidden ? "var(--border-color)" : "var(--primary-color)" } }), col.header), /* @__PURE__ */ import_react18.default.createElement("button", { className: "dg-icon-btn", onClick: () => toggleHide(String(col.key)) }, col.hidden ? /* @__PURE__ */ import_react18.default.createElement(import_lucide_react.EyeOff, { size: 14 }) : /* @__PURE__ */ import_react18.default.createElement(import_lucide_react.EyeOff, { size: 14, style: { opacity: 0.4 } }))))), /* @__PURE__ */ import_react18.default.createElement("div", { className: "dg-modal-footer" }, /* @__PURE__ */ import_react18.default.createElement("button", { className: "dg-action-btn", onClick: () => setColumns((p) => p.map((c) => ({ ...c, hidden: false }))) }, "Show All"), /* @__PURE__ */ import_react18.default.createElement("button", { className: "dg-action-btn", onClick: () => setColumns((p) => p.map((c) => c.hideable !== false ? { ...c, hidden: true } : c)) }, "Hide All")))), showAdvancedFilter && /* @__PURE__ */ import_react18.default.createElement("div", { className: "dg-modal-overlay", onClick: () => setShowAdvancedFilter(false) }, /* @__PURE__ */ import_react18.default.createElement("div", { className: "dg-modal dg-modal-wide", onClick: (e) => e.stopPropagation() }, /* @__PURE__ */ import_react18.default.createElement("div", { className: "dg-modal-header" }, /* @__PURE__ */ import_react18.default.createElement("h3", null, "Filters"), /* @__PURE__ */ import_react18.default.createElement("button", { className: "dg-icon-btn", onClick: () => setShowAdvancedFilter(false) }, /* @__PURE__ */ import_react18.default.createElement(import_lucide_react.X, { size: 18 }))), /* @__PURE__ */ import_react18.default.createElement("div", { className: "dg-modal-body" }, advancedFilters.map((f, idx) => /* @__PURE__ */ import_react18.default.createElement("div", { key: idx }, idx > 0 && /* @__PURE__ */ import_react18.default.createElement("div", { className: "dg-filter-logic" }, /* @__PURE__ */ import_react18.default.createElement(
30694
30717
  "button",
30695
30718
  {
30696
30719
  className: `dg-logic-btn${f.logic === "AND" ? " active" : ""}`,
package/dist/main.js CHANGED
@@ -158,12 +158,6 @@ import {
158
158
  import {
159
159
  downloadPdfIcon_default
160
160
  } from "./chunk-N26C33E6.js";
161
- import {
162
- AddressLookup_default
163
- } from "./chunk-EB6MPFGC.js";
164
- import {
165
- FloatingInput
166
- } from "./chunk-UPCMMCPQ.js";
167
161
  import {
168
162
  activateUserIcon_default
169
163
  } from "./chunk-AH6RCYDL.js";
@@ -176,14 +170,24 @@ import {
176
170
  import {
177
171
  closeIcon_default
178
172
  } from "./chunk-Q5XKCUE3.js";
173
+ import "./chunk-GJGRMMAQ.js";
179
174
  import {
180
- Checkbox
181
- } from "./chunk-X357WQOT.js";
175
+ AddressLookup_default
176
+ } from "./chunk-GHCM2AWR.js";
177
+ import {
178
+ FloatingInput
179
+ } from "./chunk-UPCMMCPQ.js";
182
180
  import "./chunk-7KRG7VNW.js";
183
181
  import {
184
- DataGrid
185
- } from "./chunk-J6E3UO2W.js";
186
- import "./chunk-GJGRMMAQ.js";
182
+ RufousThemeProvider,
183
+ useRufousTheme
184
+ } from "./chunk-BOE27BFQ.js";
185
+ import {
186
+ APP_THEMES
187
+ } from "./chunk-S7BNFVQO.js";
188
+ import {
189
+ Checkbox
190
+ } from "./chunk-X357WQOT.js";
187
191
  import {
188
192
  RichTextEditor
189
193
  } from "./chunk-66HHM7VI.js";
@@ -204,12 +208,8 @@ import {
204
208
  circularProgress_default
205
209
  } from "./chunk-CTBYVXFP.js";
206
210
  import {
207
- RufousThemeProvider,
208
- useRufousTheme
209
- } from "./chunk-BOE27BFQ.js";
210
- import {
211
- APP_THEMES
212
- } from "./chunk-S7BNFVQO.js";
211
+ DataGrid
212
+ } from "./chunk-IKG2B6JQ.js";
213
213
  import "./chunk-LI4N7JWK.js";
214
214
  export {
215
215
  APP_THEMES,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@rufous/ui",
3
3
  "private": false,
4
- "version": "0.1.66",
4
+ "version": "0.1.67",
5
5
  "type": "module",
6
6
  "description": "Experimental: A lightweight React UI component library (Beta)",
7
7
  "style": "./dist/style.css",
@@ -67,4 +67,4 @@
67
67
  "react": "^18.0.0 || ^19.0.0",
68
68
  "react-dom": "^18.0.0 || ^19.0.0"
69
69
  }
70
- }
70
+ }
@@ -1,12 +1,12 @@
1
1
  import {
2
2
  FloatingInput
3
3
  } from "./chunk-UPCMMCPQ.js";
4
- import {
5
- circularProgress_default
6
- } from "./chunk-CTBYVXFP.js";
7
4
  import {
8
5
  useRufousTheme
9
6
  } from "./chunk-BOE27BFQ.js";
7
+ import {
8
+ circularProgress_default
9
+ } from "./chunk-CTBYVXFP.js";
10
10
 
11
11
  // lib/TextFields/AddressLookup.tsx
12
12
  import React, { useState, useRef, useEffect } from "react";