react-open-source-grid 1.6.4 → 1.6.5

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/dist/lib/index.js CHANGED
@@ -8043,6 +8043,195 @@ var GridApiImpl = class {
8043
8043
  }
8044
8044
  };
8045
8045
 
8046
+ // src/components/DataGrid/pivotEngine.ts
8047
+ var AGGREGATORS = {
8048
+ sum: (values) => values.reduce((acc, val) => acc + val, 0),
8049
+ count: (values) => values.length,
8050
+ avg: (values) => values.length > 0 ? values.reduce((acc, val) => acc + val, 0) / values.length : 0,
8051
+ min: (values) => values.length > 0 ? Math.min(...values) : 0,
8052
+ max: (values) => values.length > 0 ? Math.max(...values) : 0
8053
+ };
8054
+ function getUniqueValues(data, column) {
8055
+ const uniqueSet = /* @__PURE__ */ new Set();
8056
+ data.forEach((row) => {
8057
+ const value = row[column];
8058
+ if (value !== null && value !== void 0) {
8059
+ uniqueSet.add(String(value));
8060
+ }
8061
+ });
8062
+ return Array.from(uniqueSet).sort();
8063
+ }
8064
+ function getAggregatorFn(aggregator) {
8065
+ if (typeof aggregator === "function") {
8066
+ return aggregator;
8067
+ }
8068
+ return AGGREGATORS[aggregator] || AGGREGATORS.sum;
8069
+ }
8070
+ function toNumber(value) {
8071
+ if (typeof value === "number") return value;
8072
+ if (typeof value === "string") {
8073
+ const num = parseFloat(value);
8074
+ return isNaN(num) ? 0 : num;
8075
+ }
8076
+ return 0;
8077
+ }
8078
+ function buildPivot(data, config) {
8079
+ const {
8080
+ pivotColumn,
8081
+ valueColumn,
8082
+ rowGroupColumn,
8083
+ aggregator,
8084
+ showTotals = false,
8085
+ showGrandTotal = false
8086
+ } = config;
8087
+ if (!data || data.length === 0) {
8088
+ return {
8089
+ columns: [],
8090
+ rows: [],
8091
+ pivotValues: []
8092
+ };
8093
+ }
8094
+ const pivotValues = getUniqueValues(data, pivotColumn);
8095
+ const rowGroupValues = getUniqueValues(data, rowGroupColumn);
8096
+ const aggregatorFn = getAggregatorFn(aggregator);
8097
+ const groupedData = /* @__PURE__ */ new Map();
8098
+ rowGroupValues.forEach((rowValue) => {
8099
+ const pivotMap = /* @__PURE__ */ new Map();
8100
+ pivotValues.forEach((pivotValue) => {
8101
+ pivotMap.set(pivotValue, []);
8102
+ });
8103
+ groupedData.set(rowValue, pivotMap);
8104
+ });
8105
+ data.forEach((row) => {
8106
+ const rowValue = String(row[rowGroupColumn] ?? "");
8107
+ const pivotValue = String(row[pivotColumn] ?? "");
8108
+ const value = toNumber(row[valueColumn]);
8109
+ if (groupedData.has(rowValue)) {
8110
+ const pivotMap = groupedData.get(rowValue);
8111
+ if (pivotMap.has(pivotValue)) {
8112
+ pivotMap.get(pivotValue).push(value);
8113
+ }
8114
+ }
8115
+ });
8116
+ const columns = [
8117
+ {
8118
+ field: rowGroupColumn,
8119
+ headerName: formatHeaderName(rowGroupColumn),
8120
+ width: 180,
8121
+ sortable: true,
8122
+ filterable: true,
8123
+ isPivotColumn: false
8124
+ }
8125
+ ];
8126
+ pivotValues.forEach((pivotValue) => {
8127
+ columns.push({
8128
+ field: pivotValue,
8129
+ headerName: pivotValue,
8130
+ width: 120,
8131
+ sortable: true,
8132
+ filterable: true,
8133
+ isPivotColumn: true
8134
+ });
8135
+ });
8136
+ if (showGrandTotal) {
8137
+ columns.push({
8138
+ field: "__grandTotal",
8139
+ headerName: "Grand Total",
8140
+ width: 120,
8141
+ sortable: true,
8142
+ filterable: false,
8143
+ isTotalColumn: true
8144
+ });
8145
+ }
8146
+ const rows = [];
8147
+ rowGroupValues.forEach((rowValue) => {
8148
+ const pivotMap = groupedData.get(rowValue);
8149
+ const row = {
8150
+ [rowGroupColumn]: rowValue,
8151
+ __id: rowValue
8152
+ // Add unique ID for grid
8153
+ };
8154
+ let grandTotal = 0;
8155
+ pivotValues.forEach((pivotValue) => {
8156
+ const values = pivotMap.get(pivotValue);
8157
+ const aggregatedValue = aggregatorFn(values);
8158
+ row[pivotValue] = aggregatedValue;
8159
+ grandTotal += aggregatedValue;
8160
+ });
8161
+ if (showGrandTotal) {
8162
+ row["__grandTotal"] = grandTotal;
8163
+ }
8164
+ rows.push(row);
8165
+ });
8166
+ let totalsRow;
8167
+ if (showTotals) {
8168
+ totalsRow = {
8169
+ [rowGroupColumn]: "Total",
8170
+ __id: "__totals",
8171
+ __isTotal: true
8172
+ };
8173
+ let overallGrandTotal = 0;
8174
+ pivotValues.forEach((pivotValue) => {
8175
+ const allValues = [];
8176
+ groupedData.forEach((pivotMap) => {
8177
+ const values = pivotMap.get(pivotValue);
8178
+ allValues.push(...values);
8179
+ });
8180
+ const total = aggregatorFn(allValues);
8181
+ totalsRow[pivotValue] = total;
8182
+ overallGrandTotal += total;
8183
+ });
8184
+ if (showGrandTotal) {
8185
+ totalsRow["__grandTotal"] = overallGrandTotal;
8186
+ }
8187
+ }
8188
+ return {
8189
+ columns,
8190
+ rows,
8191
+ pivotValues,
8192
+ totalsRow
8193
+ };
8194
+ }
8195
+ function formatHeaderName(field) {
8196
+ return field.split(/(?=[A-Z])|_|-/).map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join(" ");
8197
+ }
8198
+ function exportPivotToCSV(pivotResult) {
8199
+ const { columns, rows, totalsRow } = pivotResult;
8200
+ const headers = columns.map((col) => col.headerName).join(",");
8201
+ const dataRows = rows.map((row) => {
8202
+ return columns.map((col) => {
8203
+ const value = row[col.field];
8204
+ if (typeof value === "string" && value.includes(",")) {
8205
+ return `"${value}"`;
8206
+ }
8207
+ return value ?? "";
8208
+ }).join(",");
8209
+ });
8210
+ if (totalsRow) {
8211
+ const totalRow = columns.map((col) => {
8212
+ const value = totalsRow[col.field];
8213
+ if (typeof value === "string" && value.includes(",")) {
8214
+ return `"${value}"`;
8215
+ }
8216
+ return value ?? "";
8217
+ }).join(",");
8218
+ dataRows.push(totalRow);
8219
+ }
8220
+ return [headers, ...dataRows].join("\n");
8221
+ }
8222
+ function downloadCSV(csvContent, filename = "pivot-table.csv") {
8223
+ const blob = new Blob([csvContent], { type: "text/csv;charset=utf-8;" });
8224
+ const link = document.createElement("a");
8225
+ const url = URL.createObjectURL(blob);
8226
+ link.setAttribute("href", url);
8227
+ link.setAttribute("download", filename);
8228
+ link.style.visibility = "hidden";
8229
+ document.body.appendChild(link);
8230
+ link.click();
8231
+ document.body.removeChild(link);
8232
+ URL.revokeObjectURL(url);
8233
+ }
8234
+
8046
8235
  // src/components/DataGrid/DataGrid.tsx
8047
8236
  var DataGrid = forwardRef(({
8048
8237
  columns,
@@ -8057,6 +8246,7 @@ var DataGrid = forwardRef(({
8057
8246
  rowPinConfig,
8058
8247
  contextMenuConfig,
8059
8248
  tooltipConfig,
8249
+ pivotConfig,
8060
8250
  tableId,
8061
8251
  theme: _theme = "quartz",
8062
8252
  densityMode: _densityMode = "normal",
@@ -8077,6 +8267,42 @@ var DataGrid = forwardRef(({
8077
8267
  );
8078
8268
  const [internalRows, setInternalRows] = useState14(null);
8079
8269
  const activeRows = internalRows !== null ? internalRows : rows;
8270
+ const { pivotedData, effectiveColumns } = useMemo4(() => {
8271
+ if (!pivotConfig) {
8272
+ return { pivotedData: activeRows, effectiveColumns: columns };
8273
+ }
8274
+ const pivotResult = buildPivot(activeRows, pivotConfig);
8275
+ console.log("Pivot Result:", {
8276
+ columnsCount: pivotResult.columns.length,
8277
+ columns: pivotResult.columns.map((c) => c.field),
8278
+ rowsCount: pivotResult.rows.length,
8279
+ sampleRow: pivotResult.rows[0]
8280
+ });
8281
+ const pivotColumns = pivotResult.columns.map((col) => ({
8282
+ field: col.field,
8283
+ headerName: col.headerName,
8284
+ width: col.width || 120,
8285
+ sortable: col.sortable !== false,
8286
+ filterable: col.filterable !== false,
8287
+ editable: false,
8288
+ renderCell: col.isTotalColumn || col.isPivotColumn ? (row) => {
8289
+ const value = row[col.field];
8290
+ if (typeof value === "number") {
8291
+ return /* @__PURE__ */ React20.createElement("span", { style: {
8292
+ fontWeight: col.isTotalColumn ? "700" : "600",
8293
+ color: col.isTotalColumn ? "#0f766e" : "#475569"
8294
+ } }, value.toLocaleString(void 0, { minimumFractionDigits: 2, maximumFractionDigits: 2 }));
8295
+ }
8296
+ return value;
8297
+ } : void 0
8298
+ }));
8299
+ const allRows = pivotResult.totalsRow ? [...pivotResult.rows, pivotResult.totalsRow] : pivotResult.rows;
8300
+ const rowsWithId = allRows.map((row, index) => ({
8301
+ ...row,
8302
+ id: row.__id || row.id || `pivot-row-${index}`
8303
+ }));
8304
+ return { pivotedData: rowsWithId, effectiveColumns: pivotColumns };
8305
+ }, [activeRows, columns, pivotConfig]);
8080
8306
  const rowsRef = useRef10(rows);
8081
8307
  useEffect9(() => {
8082
8308
  if (rows !== rowsRef.current) {
@@ -8179,8 +8405,8 @@ var DataGrid = forwardRef(({
8179
8405
  gridApiRef.current = new GridApiImpl(
8180
8406
  state,
8181
8407
  dispatch,
8182
- columns,
8183
- activeRows,
8408
+ effectiveColumns,
8409
+ pivotedData,
8184
8410
  containerRef,
8185
8411
  persistenceManager,
8186
8412
  setInternalRows
@@ -8190,24 +8416,24 @@ var DataGrid = forwardRef(({
8190
8416
  useEffect9(() => {
8191
8417
  if (gridApiRef.current && !gridApiRef.current.isDestroyed()) {
8192
8418
  gridApiRef.current.updateState(state);
8193
- gridApiRef.current.updateData(columns, activeRows);
8419
+ gridApiRef.current.updateData(effectiveColumns, pivotedData);
8194
8420
  gridApiRef.current.updateCallbacks(setInternalRows);
8195
8421
  }
8196
- }, [state, columns, activeRows, setInternalRows]);
8422
+ }, [state, effectiveColumns, pivotedData, setInternalRows]);
8197
8423
  useImperativeHandle(ref, () => {
8198
8424
  if (!gridApiRef.current || gridApiRef.current.isDestroyed()) {
8199
8425
  gridApiRef.current = new GridApiImpl(
8200
8426
  state,
8201
8427
  dispatch,
8202
- columns,
8203
- activeRows,
8428
+ effectiveColumns,
8429
+ pivotedData,
8204
8430
  containerRef,
8205
8431
  persistenceManager,
8206
8432
  setInternalRows
8207
8433
  );
8208
8434
  }
8209
8435
  return gridApiRef.current;
8210
- }, [state, columns, activeRows, persistenceManager]);
8436
+ }, [state, effectiveColumns, pivotedData, persistenceManager]);
8211
8437
  const onGridReadyCalledRef = useRef10(false);
8212
8438
  const onGridReadyCallbackRef = useRef10(onGridReady);
8213
8439
  useEffect9(() => {
@@ -8288,9 +8514,11 @@ var DataGrid = forwardRef(({
8288
8514
  ...middleColumns,
8289
8515
  ...pinnedRightFields
8290
8516
  ];
8517
+ console.log("Display column order:", displayColumnOrder, "from state.columnOrder:", state.columnOrder);
8291
8518
  useEffect9(() => {
8292
- dispatch({ type: "RESET_COLUMNS", payload: columns });
8293
- }, [columns]);
8519
+ console.log("Dispatching RESET_COLUMNS with", effectiveColumns.length, "columns:", effectiveColumns.map((c) => c.field));
8520
+ dispatch({ type: "RESET_COLUMNS", payload: effectiveColumns });
8521
+ }, [effectiveColumns]);
8294
8522
  useEffect9(() => {
8295
8523
  if (onSelectionChange) {
8296
8524
  onSelectionChange(Array.from(state.selection.selectedRows));
@@ -8306,17 +8534,17 @@ var DataGrid = forwardRef(({
8306
8534
  }, [state.pinnedRowsTop, state.pinnedRowsBottom]);
8307
8535
  useEffect9(() => {
8308
8536
  if (state.sortConfig.field) {
8309
- const column = columns.find((c) => c.field === state.sortConfig.field);
8537
+ const column = effectiveColumns.find((c) => c.field === state.sortConfig.field);
8310
8538
  if (column) {
8311
8539
  announceSorting(column.headerName, state.sortConfig.direction);
8312
8540
  }
8313
8541
  }
8314
- }, [state.sortConfig.field, state.sortConfig.direction]);
8542
+ }, [state.sortConfig.field, state.sortConfig.direction, effectiveColumns]);
8315
8543
  const sortedRows = useMemo4(() => {
8316
8544
  if (!state.sortConfig.field || !state.sortConfig.direction) {
8317
- return activeRows;
8545
+ return pivotedData;
8318
8546
  }
8319
- const sorted = [...activeRows].sort((a, b) => {
8547
+ const sorted = [...pivotedData].sort((a, b) => {
8320
8548
  const aValue = a[state.sortConfig.field];
8321
8549
  const bValue = b[state.sortConfig.field];
8322
8550
  if (aValue == null && bValue == null) return 0;
@@ -8333,7 +8561,7 @@ var DataGrid = forwardRef(({
8333
8561
  sorted.reverse();
8334
8562
  }
8335
8563
  return sorted;
8336
- }, [activeRows, state.sortConfig]);
8564
+ }, [pivotedData, state.sortConfig]);
8337
8565
  const filteredRows = useMemo4(() => {
8338
8566
  if (!hasActiveFilters(state.filterConfig)) {
8339
8567
  return sortedRows;
@@ -8479,7 +8707,7 @@ var DataGrid = forwardRef(({
8479
8707
  /* @__PURE__ */ React20.createElement("div", { style: { position: "relative", display: "flex", alignItems: "center", justifyContent: "space-between", paddingLeft: "16px", paddingRight: "16px", paddingTop: "10px", paddingBottom: "10px", backgroundColor: "var(--grid-bg-alt)", borderBottom: "var(--grid-border-width, 1px) solid var(--grid-border)", zIndex: 30 } }, /* @__PURE__ */ React20.createElement("div", { style: { position: "relative", display: "flex", alignItems: "center", gap: "8px" } }, /* @__PURE__ */ React20.createElement(
8480
8708
  ColumnChooser,
8481
8709
  {
8482
- columns,
8710
+ columns: effectiveColumns,
8483
8711
  columnOrder: state.columnOrder,
8484
8712
  hiddenColumns: state.hiddenColumns,
8485
8713
  onToggleVisibility: (field) => dispatch({ type: "TOGGLE_COLUMN_VISIBILITY", payload: field }),
@@ -8489,7 +8717,7 @@ var DataGrid = forwardRef(({
8489
8717
  ), /* @__PURE__ */ React20.createElement(
8490
8718
  ExportMenu,
8491
8719
  {
8492
- columns,
8720
+ columns: effectiveColumns,
8493
8721
  fullDataset: rows,
8494
8722
  filteredData: filteredRows.filter((r) => !("isGroup" in r)),
8495
8723
  selectedRows: state.selection.selectedRows,
@@ -8507,7 +8735,7 @@ var DataGrid = forwardRef(({
8507
8735
  /* @__PURE__ */ React20.createElement(
8508
8736
  GroupByPanel,
8509
8737
  {
8510
- columns,
8738
+ columns: effectiveColumns,
8511
8739
  groupBy: state.groupBy,
8512
8740
  dispatch
8513
8741
  }
@@ -8515,7 +8743,7 @@ var DataGrid = forwardRef(({
8515
8743
  /* @__PURE__ */ React20.createElement("div", { role: "rowgroup", style: { position: "sticky", top: 0, zIndex: 20, width: "100%" } }, /* @__PURE__ */ React20.createElement(
8516
8744
  GridHeader,
8517
8745
  {
8518
- columns,
8746
+ columns: effectiveColumns,
8519
8747
  columnOrder: state.columnOrder,
8520
8748
  displayColumnOrder,
8521
8749
  columnWidths: state.columnWidths,
@@ -8534,7 +8762,7 @@ var DataGrid = forwardRef(({
8534
8762
  ), /* @__PURE__ */ React20.createElement(
8535
8763
  ColumnFilters,
8536
8764
  {
8537
- columns,
8765
+ columns: effectiveColumns,
8538
8766
  displayColumnOrder,
8539
8767
  columnWidths: state.columnWidths,
8540
8768
  filterConfig: state.filterConfig,
@@ -8547,7 +8775,7 @@ var DataGrid = forwardRef(({
8547
8775
  /* @__PURE__ */ React20.createElement(
8548
8776
  GridBody,
8549
8777
  {
8550
- columns,
8778
+ columns: effectiveColumns,
8551
8779
  rows: (virtualScrollConfig == null ? void 0 : virtualScrollConfig.enabled) ? unpinnedRows : paginatedRows,
8552
8780
  pinnedRowsTop: pinnedRowsTopData,
8553
8781
  pinnedRowsBottom: pinnedRowsBottomData,
@@ -8587,7 +8815,7 @@ var DataGrid = forwardRef(({
8587
8815
  (footerConfig == null ? void 0 : footerConfig.show) && footerConfig.aggregates && /* @__PURE__ */ React20.createElement(
8588
8816
  GridFooter,
8589
8817
  {
8590
- columns,
8818
+ columns: effectiveColumns,
8591
8819
  displayColumnOrder,
8592
8820
  columnWidths: state.columnWidths,
8593
8821
  aggregates: globalAggregates,
@@ -11752,6 +11980,258 @@ function createMockFeed(config) {
11752
11980
  createConnection: () => createMockWebSocket(feed)
11753
11981
  };
11754
11982
  }
11983
+
11984
+ // src/components/DataGrid/PivotToolbar.tsx
11985
+ import React29, { useState as useState20, useMemo as useMemo8 } from "react";
11986
+ var AGGREGATOR_OPTIONS = [
11987
+ { value: "sum", label: "Sum" },
11988
+ { value: "avg", label: "Average" },
11989
+ { value: "count", label: "Count" },
11990
+ { value: "min", label: "Minimum" },
11991
+ { value: "max", label: "Maximum" }
11992
+ ];
11993
+ var PivotToolbar = ({
11994
+ columns,
11995
+ pivotConfig,
11996
+ onPivotToggle,
11997
+ onConfigChange,
11998
+ isPivotMode = false,
11999
+ style,
12000
+ className
12001
+ }) => {
12002
+ const [isExpanded, setIsExpanded] = useState20(isPivotMode);
12003
+ const [rowGroupColumn, setRowGroupColumn] = useState20((pivotConfig == null ? void 0 : pivotConfig.rowGroupColumn) || "");
12004
+ const [pivotColumn, setPivotColumn] = useState20((pivotConfig == null ? void 0 : pivotConfig.pivotColumn) || "");
12005
+ const [valueColumn, setValueColumn] = useState20((pivotConfig == null ? void 0 : pivotConfig.valueColumn) || "");
12006
+ const [aggregator, setAggregator] = useState20(
12007
+ (pivotConfig == null ? void 0 : pivotConfig.aggregator) || "sum"
12008
+ );
12009
+ const [showTotals, setShowTotals] = useState20((pivotConfig == null ? void 0 : pivotConfig.showTotals) ?? true);
12010
+ const [showGrandTotal, setShowGrandTotal] = useState20((pivotConfig == null ? void 0 : pivotConfig.showGrandTotal) ?? true);
12011
+ const isConfigValid = useMemo8(() => {
12012
+ return rowGroupColumn && pivotColumn && valueColumn;
12013
+ }, [rowGroupColumn, pivotColumn, valueColumn]);
12014
+ const handleApply = () => {
12015
+ if (!isConfigValid) return;
12016
+ const config = {
12017
+ rowGroupColumn,
12018
+ pivotColumn,
12019
+ valueColumn,
12020
+ aggregator,
12021
+ showTotals,
12022
+ showGrandTotal
12023
+ };
12024
+ onConfigChange(config);
12025
+ onPivotToggle(true);
12026
+ };
12027
+ const handleClear = () => {
12028
+ setRowGroupColumn("");
12029
+ setPivotColumn("");
12030
+ setValueColumn("");
12031
+ setAggregator("sum");
12032
+ setShowTotals(true);
12033
+ setShowGrandTotal(true);
12034
+ onConfigChange(null);
12035
+ onPivotToggle(false);
12036
+ setIsExpanded(false);
12037
+ };
12038
+ const handleToggle = () => {
12039
+ setIsExpanded(!isExpanded);
12040
+ };
12041
+ return /* @__PURE__ */ React29.createElement(
12042
+ "div",
12043
+ {
12044
+ className,
12045
+ style: {
12046
+ backgroundColor: "#f8fafc",
12047
+ border: "1px solid #e2e8f0",
12048
+ borderRadius: "8px",
12049
+ padding: "12px",
12050
+ ...style
12051
+ }
12052
+ },
12053
+ /* @__PURE__ */ React29.createElement("div", { style: { display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: isExpanded ? "12px" : "0" } }, /* @__PURE__ */ React29.createElement("div", { style: { display: "flex", alignItems: "center", gap: "8px" } }, /* @__PURE__ */ React29.createElement("span", { style: { fontSize: "20px" } }, "\u{1F4CA}"), /* @__PURE__ */ React29.createElement("h3", { style: { margin: 0, fontSize: "14px", fontWeight: "600", color: "#1e293b" } }, "Pivot Table"), isPivotMode && /* @__PURE__ */ React29.createElement("span", { style: {
12054
+ backgroundColor: "#10b981",
12055
+ color: "white",
12056
+ padding: "2px 8px",
12057
+ borderRadius: "12px",
12058
+ fontSize: "11px",
12059
+ fontWeight: "600"
12060
+ } }, "Active")), /* @__PURE__ */ React29.createElement("div", { style: { display: "flex", gap: "8px" } }, isPivotMode && /* @__PURE__ */ React29.createElement(
12061
+ "button",
12062
+ {
12063
+ onClick: handleClear,
12064
+ style: {
12065
+ padding: "6px 12px",
12066
+ fontSize: "13px",
12067
+ fontWeight: "500",
12068
+ color: "#dc2626",
12069
+ backgroundColor: "#fee2e2",
12070
+ border: "1px solid #fecaca",
12071
+ borderRadius: "6px",
12072
+ cursor: "pointer",
12073
+ transition: "all 0.15s"
12074
+ },
12075
+ onMouseEnter: (e) => {
12076
+ e.currentTarget.style.backgroundColor = "#fecaca";
12077
+ },
12078
+ onMouseLeave: (e) => {
12079
+ e.currentTarget.style.backgroundColor = "#fee2e2";
12080
+ }
12081
+ },
12082
+ "Clear Pivot"
12083
+ ), /* @__PURE__ */ React29.createElement(
12084
+ "button",
12085
+ {
12086
+ onClick: handleToggle,
12087
+ style: {
12088
+ padding: "6px 10px",
12089
+ fontSize: "13px",
12090
+ color: "#64748b",
12091
+ backgroundColor: "white",
12092
+ border: "1px solid #cbd5e1",
12093
+ borderRadius: "6px",
12094
+ cursor: "pointer",
12095
+ transition: "all 0.15s"
12096
+ },
12097
+ onMouseEnter: (e) => {
12098
+ e.currentTarget.style.backgroundColor = "#f1f5f9";
12099
+ },
12100
+ onMouseLeave: (e) => {
12101
+ e.currentTarget.style.backgroundColor = "white";
12102
+ }
12103
+ },
12104
+ isExpanded ? "\u25B2" : "\u25BC"
12105
+ ))),
12106
+ isExpanded && /* @__PURE__ */ React29.createElement("div", { style: { display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(200px, 1fr))", gap: "12px" } }, /* @__PURE__ */ React29.createElement("div", null, /* @__PURE__ */ React29.createElement("label", { style: { display: "block", fontSize: "12px", fontWeight: "600", color: "#475569", marginBottom: "6px" } }, "Row Group By ", /* @__PURE__ */ React29.createElement("span", { style: { color: "#dc2626" } }, "*")), /* @__PURE__ */ React29.createElement(
12107
+ "select",
12108
+ {
12109
+ value: rowGroupColumn,
12110
+ onChange: (e) => setRowGroupColumn(e.target.value),
12111
+ style: {
12112
+ width: "100%",
12113
+ padding: "8px",
12114
+ fontSize: "13px",
12115
+ border: "1px solid #cbd5e1",
12116
+ borderRadius: "6px",
12117
+ backgroundColor: "white",
12118
+ color: "#1e293b",
12119
+ cursor: "pointer"
12120
+ }
12121
+ },
12122
+ /* @__PURE__ */ React29.createElement("option", { value: "" }, "Select column..."),
12123
+ columns.map((col) => /* @__PURE__ */ React29.createElement("option", { key: col.field, value: col.field }, col.headerName))
12124
+ )), /* @__PURE__ */ React29.createElement("div", null, /* @__PURE__ */ React29.createElement("label", { style: { display: "block", fontSize: "12px", fontWeight: "600", color: "#475569", marginBottom: "6px" } }, "Pivot Column ", /* @__PURE__ */ React29.createElement("span", { style: { color: "#dc2626" } }, "*")), /* @__PURE__ */ React29.createElement(
12125
+ "select",
12126
+ {
12127
+ value: pivotColumn,
12128
+ onChange: (e) => setPivotColumn(e.target.value),
12129
+ style: {
12130
+ width: "100%",
12131
+ padding: "8px",
12132
+ fontSize: "13px",
12133
+ border: "1px solid #cbd5e1",
12134
+ borderRadius: "6px",
12135
+ backgroundColor: "white",
12136
+ color: "#1e293b",
12137
+ cursor: "pointer"
12138
+ }
12139
+ },
12140
+ /* @__PURE__ */ React29.createElement("option", { value: "" }, "Select column..."),
12141
+ columns.map((col) => /* @__PURE__ */ React29.createElement("option", { key: col.field, value: col.field }, col.headerName))
12142
+ )), /* @__PURE__ */ React29.createElement("div", null, /* @__PURE__ */ React29.createElement("label", { style: { display: "block", fontSize: "12px", fontWeight: "600", color: "#475569", marginBottom: "6px" } }, "Value Column ", /* @__PURE__ */ React29.createElement("span", { style: { color: "#dc2626" } }, "*")), /* @__PURE__ */ React29.createElement(
12143
+ "select",
12144
+ {
12145
+ value: valueColumn,
12146
+ onChange: (e) => setValueColumn(e.target.value),
12147
+ style: {
12148
+ width: "100%",
12149
+ padding: "8px",
12150
+ fontSize: "13px",
12151
+ border: "1px solid #cbd5e1",
12152
+ borderRadius: "6px",
12153
+ backgroundColor: "white",
12154
+ color: "#1e293b",
12155
+ cursor: "pointer"
12156
+ }
12157
+ },
12158
+ /* @__PURE__ */ React29.createElement("option", { value: "" }, "Select column..."),
12159
+ columns.map((col) => /* @__PURE__ */ React29.createElement("option", { key: col.field, value: col.field }, col.headerName))
12160
+ )), /* @__PURE__ */ React29.createElement("div", null, /* @__PURE__ */ React29.createElement("label", { style: { display: "block", fontSize: "12px", fontWeight: "600", color: "#475569", marginBottom: "6px" } }, "Aggregation"), /* @__PURE__ */ React29.createElement(
12161
+ "select",
12162
+ {
12163
+ value: aggregator,
12164
+ onChange: (e) => setAggregator(e.target.value),
12165
+ style: {
12166
+ width: "100%",
12167
+ padding: "8px",
12168
+ fontSize: "13px",
12169
+ border: "1px solid #cbd5e1",
12170
+ borderRadius: "6px",
12171
+ backgroundColor: "white",
12172
+ color: "#1e293b",
12173
+ cursor: "pointer"
12174
+ }
12175
+ },
12176
+ AGGREGATOR_OPTIONS.map((opt) => /* @__PURE__ */ React29.createElement("option", { key: opt.value, value: opt.value }, opt.label))
12177
+ )), /* @__PURE__ */ React29.createElement("div", { style: { display: "flex", flexDirection: "column", gap: "8px", justifyContent: "center" } }, /* @__PURE__ */ React29.createElement("label", { style: { display: "flex", alignItems: "center", gap: "6px", fontSize: "13px", color: "#475569", cursor: "pointer" } }, /* @__PURE__ */ React29.createElement(
12178
+ "input",
12179
+ {
12180
+ type: "checkbox",
12181
+ checked: showTotals,
12182
+ onChange: (e) => setShowTotals(e.target.checked),
12183
+ style: { cursor: "pointer" }
12184
+ }
12185
+ ), "Show Totals Row"), /* @__PURE__ */ React29.createElement("label", { style: { display: "flex", alignItems: "center", gap: "6px", fontSize: "13px", color: "#475569", cursor: "pointer" } }, /* @__PURE__ */ React29.createElement(
12186
+ "input",
12187
+ {
12188
+ type: "checkbox",
12189
+ checked: showGrandTotal,
12190
+ onChange: (e) => setShowGrandTotal(e.target.checked),
12191
+ style: { cursor: "pointer" }
12192
+ }
12193
+ ), "Show Grand Total Column")), /* @__PURE__ */ React29.createElement("div", { style: { display: "flex", alignItems: "flex-end" } }, /* @__PURE__ */ React29.createElement(
12194
+ "button",
12195
+ {
12196
+ onClick: handleApply,
12197
+ disabled: !isConfigValid,
12198
+ style: {
12199
+ width: "100%",
12200
+ padding: "10px",
12201
+ fontSize: "14px",
12202
+ fontWeight: "600",
12203
+ color: "white",
12204
+ backgroundColor: isConfigValid ? "#2563eb" : "#94a3b8",
12205
+ border: "none",
12206
+ borderRadius: "6px",
12207
+ cursor: isConfigValid ? "pointer" : "not-allowed",
12208
+ transition: "all 0.15s",
12209
+ boxShadow: isConfigValid ? "0 2px 4px rgba(37, 99, 235, 0.2)" : "none"
12210
+ },
12211
+ onMouseEnter: (e) => {
12212
+ if (isConfigValid) {
12213
+ e.currentTarget.style.backgroundColor = "#1d4ed8";
12214
+ }
12215
+ },
12216
+ onMouseLeave: (e) => {
12217
+ if (isConfigValid) {
12218
+ e.currentTarget.style.backgroundColor = "#2563eb";
12219
+ }
12220
+ }
12221
+ },
12222
+ "Apply Pivot"
12223
+ ))),
12224
+ isExpanded && /* @__PURE__ */ React29.createElement("div", { style: {
12225
+ marginTop: "12px",
12226
+ padding: "10px",
12227
+ backgroundColor: "#eff6ff",
12228
+ border: "1px solid #bfdbfe",
12229
+ borderRadius: "6px",
12230
+ fontSize: "12px",
12231
+ color: "#1e40af"
12232
+ } }, /* @__PURE__ */ React29.createElement("strong", null, "\u{1F4A1} Tip:"), " Select a Row Group column to organize data by, a Pivot column whose values become new columns, and a Value column to aggregate.")
12233
+ );
12234
+ };
11755
12235
  export {
11756
12236
  AdvancedFilterBuilder,
11757
12237
  BadgeCell,
@@ -11774,6 +12254,7 @@ export {
11774
12254
  LocalStorageAdapter,
11775
12255
  MarketDataEngine,
11776
12256
  MarketDataGrid,
12257
+ PivotToolbar,
11777
12258
  PriorityIndicator,
11778
12259
  ProgressBar,
11779
12260
  Rating,
@@ -11786,6 +12267,7 @@ export {
11786
12267
  VirtualScroller,
11787
12268
  WebSocketMockFeed,
11788
12269
  alpineTheme,
12270
+ buildPivot,
11789
12271
  buildTreeFromFlat,
11790
12272
  collapseAllNodes,
11791
12273
  countTreeNodes,
@@ -11796,7 +12278,9 @@ export {
11796
12278
  createPreset,
11797
12279
  darkTheme,
11798
12280
  densityConfigs,
12281
+ downloadCSV,
11799
12282
  expandAllNodes,
12283
+ exportPivotToCSV,
11800
12284
  exportToCSV,
11801
12285
  exportToXLSX,
11802
12286
  filterTree,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "react-open-source-grid",
3
3
  "private": false,
4
- "version": "1.6.4",
4
+ "version": "1.6.5",
5
5
  "type": "module",
6
6
  "description": "A high-performance React DataGrid component with advanced features like virtual scrolling, infinite scrolling, tree data, market data mode, and more",
7
7
  "main": "./dist/lib/index.cjs",