beautiful-grid 1.0.12 → 1.0.13

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.
@@ -0,0 +1,317 @@
1
+ "use strict";
2
+ var __values = (this && this.__values) || function(o) {
3
+ var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
4
+ if (m) return m.call(o);
5
+ if (o && typeof o.length === "number") return {
6
+ next: function () {
7
+ if (o && i >= o.length) o = void 0;
8
+ return { value: o && o[i++], done: !o };
9
+ }
10
+ };
11
+ throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
12
+ };
13
+ Object.defineProperty(exports, "__esModule", { value: true });
14
+ exports.escapeXml = escapeXml;
15
+ exports.colIndexToExcelColumn = colIndexToExcelColumn;
16
+ exports.serializeGridExportDataToExcel = serializeGridExportDataToExcel;
17
+ exports.downloadExcel = downloadExcel;
18
+ function makeCrc32Table() {
19
+ var table = new Uint32Array(256);
20
+ for (var i = 0; i < 256; i++) {
21
+ var c = i;
22
+ for (var k = 0; k < 8; k++) {
23
+ c = (c & 1) ? (0xedb88320 ^ (c >>> 1)) : (c >>> 1);
24
+ }
25
+ table[i] = c;
26
+ }
27
+ return table;
28
+ }
29
+ var crcTable = makeCrc32Table();
30
+ function calculateCrc32(bytes) {
31
+ var crc = 0xffffffff;
32
+ for (var i = 0; i < bytes.length; i++) {
33
+ crc = (crc >>> 8) ^ crcTable[(crc ^ bytes[i]) & 0xff];
34
+ }
35
+ return (crc ^ 0xffffffff) >>> 0;
36
+ }
37
+ function escapeXml(value) {
38
+ return value
39
+ .replace(/&/g, '&amp;')
40
+ .replace(/</g, '&lt;')
41
+ .replace(/>/g, '&gt;')
42
+ .replace(/"/g, '&quot;')
43
+ .replace(/'/g, '&apos;')
44
+ .replace(/[\x00-\x08\x0b\x0c\x0e-\x1f]/g, '');
45
+ }
46
+ function colIndexToExcelColumn(colIndex) {
47
+ var name = '';
48
+ var n = colIndex;
49
+ while (n >= 0) {
50
+ name = String.fromCharCode((n % 26) + 65) + name;
51
+ n = Math.floor(n / 26) - 1;
52
+ }
53
+ return name;
54
+ }
55
+ function createZipArchive(files) {
56
+ var e_1, _a, e_2, _b, e_3, _c, e_4, _d;
57
+ var encoder = new TextEncoder();
58
+ var fileEntries = [];
59
+ var offset = 0;
60
+ try {
61
+ for (var files_1 = __values(files), files_1_1 = files_1.next(); !files_1_1.done; files_1_1 = files_1.next()) {
62
+ var file = files_1_1.value;
63
+ var nameBytes = encoder.encode(file.name);
64
+ var dataBytes = typeof file.data === 'string' ? encoder.encode(file.data) : file.data;
65
+ var crc = calculateCrc32(dataBytes);
66
+ var localHeader = new Uint8Array(30 + nameBytes.length);
67
+ var dv = new DataView(localHeader.buffer);
68
+ dv.setUint32(0, 0x04034b50, true); // Local file header signature
69
+ dv.setUint16(4, 20, true); // Version needed to extract (2.0)
70
+ dv.setUint16(6, 0x0800, true); // Bit flag: UTF-8 filename (bit 11)
71
+ dv.setUint16(8, 0, true); // Compression method: STORE (0)
72
+ dv.setUint16(10, 0, true); // File last mod time
73
+ dv.setUint16(12, 0, true); // File last mod date
74
+ dv.setUint32(14, crc, true); // CRC-32
75
+ dv.setUint32(18, dataBytes.length, true); // Compressed size
76
+ dv.setUint32(22, dataBytes.length, true); // Uncompressed size
77
+ dv.setUint16(26, nameBytes.length, true); // Filename length
78
+ dv.setUint16(28, 0, true); // Extra field length
79
+ localHeader.set(nameBytes, 30);
80
+ fileEntries.push({
81
+ nameBytes: nameBytes,
82
+ dataBytes: dataBytes,
83
+ crc: crc,
84
+ localHeader: localHeader,
85
+ offset: offset,
86
+ });
87
+ offset += localHeader.length + dataBytes.length;
88
+ }
89
+ }
90
+ catch (e_1_1) { e_1 = { error: e_1_1 }; }
91
+ finally {
92
+ try {
93
+ if (files_1_1 && !files_1_1.done && (_a = files_1.return)) _a.call(files_1);
94
+ }
95
+ finally { if (e_1) throw e_1.error; }
96
+ }
97
+ var centralDirEntries = [];
98
+ var centralDirSize = 0;
99
+ try {
100
+ for (var fileEntries_1 = __values(fileEntries), fileEntries_1_1 = fileEntries_1.next(); !fileEntries_1_1.done; fileEntries_1_1 = fileEntries_1.next()) {
101
+ var entry = fileEntries_1_1.value;
102
+ var cd = new Uint8Array(46 + entry.nameBytes.length);
103
+ var dv = new DataView(cd.buffer);
104
+ dv.setUint32(0, 0x02014b50, true); // Central file header signature
105
+ dv.setUint16(4, 20, true); // Version made by
106
+ dv.setUint16(6, 20, true); // Version needed to extract
107
+ dv.setUint16(8, 0x0800, true); // Bit flag: UTF-8
108
+ dv.setUint16(10, 0, true); // Compression method: STORE
109
+ dv.setUint16(12, 0, true); // File last mod time
110
+ dv.setUint16(14, 0, true); // File last mod date
111
+ dv.setUint32(16, entry.crc, true); // CRC-32
112
+ dv.setUint32(20, entry.dataBytes.length, true); // Compressed size
113
+ dv.setUint32(24, entry.dataBytes.length, true); // Uncompressed size
114
+ dv.setUint16(28, entry.nameBytes.length, true); // Filename length
115
+ dv.setUint16(30, 0, true); // Extra field length
116
+ dv.setUint16(32, 0, true); // File comment length
117
+ dv.setUint16(34, 0, true); // Disk number start
118
+ dv.setUint16(36, 0, true); // Internal file attributes
119
+ dv.setUint32(38, 0, true); // External file attributes
120
+ dv.setUint32(42, entry.offset, true); // Relative offset of local header
121
+ cd.set(entry.nameBytes, 46);
122
+ centralDirEntries.push(cd);
123
+ centralDirSize += cd.length;
124
+ }
125
+ }
126
+ catch (e_2_1) { e_2 = { error: e_2_1 }; }
127
+ finally {
128
+ try {
129
+ if (fileEntries_1_1 && !fileEntries_1_1.done && (_b = fileEntries_1.return)) _b.call(fileEntries_1);
130
+ }
131
+ finally { if (e_2) throw e_2.error; }
132
+ }
133
+ var eocd = new Uint8Array(22);
134
+ var dvEocd = new DataView(eocd.buffer);
135
+ dvEocd.setUint32(0, 0x06054b50, true); // End of central dir signature
136
+ dvEocd.setUint16(4, 0, true); // Number of this disk
137
+ dvEocd.setUint16(6, 0, true); // Number of disk with start of central directory
138
+ dvEocd.setUint16(8, fileEntries.length, true); // Total entries in this disk
139
+ dvEocd.setUint16(10, fileEntries.length, true); // Total entries
140
+ dvEocd.setUint32(12, centralDirSize, true); // Size of the central directory
141
+ dvEocd.setUint32(16, offset, true); // Offset of start of central directory
142
+ dvEocd.setUint16(20, 0, true); // ZIP file comment length
143
+ var totalLength = offset + centralDirSize + 22;
144
+ var result = new Uint8Array(totalLength);
145
+ var pos = 0;
146
+ try {
147
+ for (var fileEntries_2 = __values(fileEntries), fileEntries_2_1 = fileEntries_2.next(); !fileEntries_2_1.done; fileEntries_2_1 = fileEntries_2.next()) {
148
+ var entry = fileEntries_2_1.value;
149
+ result.set(entry.localHeader, pos);
150
+ pos += entry.localHeader.length;
151
+ result.set(entry.dataBytes, pos);
152
+ pos += entry.dataBytes.length;
153
+ }
154
+ }
155
+ catch (e_3_1) { e_3 = { error: e_3_1 }; }
156
+ finally {
157
+ try {
158
+ if (fileEntries_2_1 && !fileEntries_2_1.done && (_c = fileEntries_2.return)) _c.call(fileEntries_2);
159
+ }
160
+ finally { if (e_3) throw e_3.error; }
161
+ }
162
+ try {
163
+ for (var centralDirEntries_1 = __values(centralDirEntries), centralDirEntries_1_1 = centralDirEntries_1.next(); !centralDirEntries_1_1.done; centralDirEntries_1_1 = centralDirEntries_1.next()) {
164
+ var cd = centralDirEntries_1_1.value;
165
+ result.set(cd, pos);
166
+ pos += cd.length;
167
+ }
168
+ }
169
+ catch (e_4_1) { e_4 = { error: e_4_1 }; }
170
+ finally {
171
+ try {
172
+ if (centralDirEntries_1_1 && !centralDirEntries_1_1.done && (_d = centralDirEntries_1.return)) _d.call(centralDirEntries_1);
173
+ }
174
+ finally { if (e_4) throw e_4.error; }
175
+ }
176
+ result.set(eocd, pos);
177
+ return result;
178
+ }
179
+ function formatCellValueAsXml(cellValue, cellRef) {
180
+ if (cellValue === null || cellValue === undefined || cellValue === '') {
181
+ return "<c r=\"".concat(cellRef, "\"/>");
182
+ }
183
+ if (typeof cellValue === 'number') {
184
+ if (Number.isFinite(cellValue)) {
185
+ return "<c r=\"".concat(cellRef, "\"><v>").concat(cellValue, "</v></c>");
186
+ }
187
+ return "<c r=\"".concat(cellRef, "\" t=\"inlineStr\"><is><t>").concat(escapeXml(String(cellValue)), "</t></is></c>");
188
+ }
189
+ if (typeof cellValue === 'boolean') {
190
+ return "<c r=\"".concat(cellRef, "\" t=\"b\"><v>").concat(cellValue ? 1 : 0, "</v></c>");
191
+ }
192
+ if (typeof cellValue === 'bigint') {
193
+ return "<c r=\"".concat(cellRef, "\" t=\"inlineStr\"><is><t>").concat(cellValue.toString(), "</t></is></c>");
194
+ }
195
+ if (cellValue instanceof Date) {
196
+ var dateStr = Number.isNaN(cellValue.getTime()) ? 'Invalid Date' : cellValue.toISOString();
197
+ return "<c r=\"".concat(cellRef, "\" t=\"inlineStr\"><is><t>").concat(escapeXml(dateStr), "</t></is></c>");
198
+ }
199
+ var text;
200
+ if (typeof cellValue === 'object') {
201
+ try {
202
+ text = JSON.stringify(cellValue);
203
+ }
204
+ catch (_a) {
205
+ text = String(cellValue);
206
+ }
207
+ }
208
+ else {
209
+ text = String(cellValue);
210
+ }
211
+ return "<c r=\"".concat(cellRef, "\" t=\"inlineStr\"><is><t xml:space=\"preserve\">").concat(escapeXml(text), "</t></is></c>");
212
+ }
213
+ function serializeGridExportDataToExcel(data, options) {
214
+ var e_5, _a;
215
+ var _b;
216
+ var includeHeader = (_b = options === null || options === void 0 ? void 0 : options.includeHeader) !== null && _b !== void 0 ? _b : true;
217
+ var rawSheetName = ((options === null || options === void 0 ? void 0 : options.sheetName) || 'Sheet1').trim().replace(/[:\\/?*\[\]]/g, '');
218
+ if (!rawSheetName) {
219
+ rawSheetName = 'Sheet1';
220
+ }
221
+ else if (rawSheetName.length > 31) {
222
+ rawSheetName = rawSheetName.slice(0, 31);
223
+ }
224
+ var sheetRows = [];
225
+ var rowIndex = 1;
226
+ if (includeHeader) {
227
+ var headerCells = data.columns
228
+ .map(function (col, colIdx) {
229
+ var cellRef = "".concat(colIndexToExcelColumn(colIdx)).concat(rowIndex);
230
+ return formatCellValueAsXml(col.header, cellRef);
231
+ })
232
+ .join('');
233
+ sheetRows.push("<row r=\"".concat(rowIndex, "\">").concat(headerCells, "</row>"));
234
+ rowIndex++;
235
+ }
236
+ try {
237
+ for (var _c = __values(data.rows), _d = _c.next(); !_d.done; _d = _c.next()) {
238
+ var row = _d.value;
239
+ var rowCells = row.cells
240
+ .map(function (cell, colIdx) {
241
+ var cellRef = "".concat(colIndexToExcelColumn(colIdx)).concat(rowIndex);
242
+ return formatCellValueAsXml(cell, cellRef);
243
+ })
244
+ .join('');
245
+ sheetRows.push("<row r=\"".concat(rowIndex, "\">").concat(rowCells, "</row>"));
246
+ rowIndex++;
247
+ }
248
+ }
249
+ catch (e_5_1) { e_5 = { error: e_5_1 }; }
250
+ finally {
251
+ try {
252
+ if (_d && !_d.done && (_a = _c.return)) _a.call(_c);
253
+ }
254
+ finally { if (e_5) throw e_5.error; }
255
+ }
256
+ var contentTypesXml = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n' +
257
+ '<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">' +
258
+ '<Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>' +
259
+ '<Default Extension="xml" ContentType="application/xml"/>' +
260
+ '<Override PartName="/xl/workbook.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"/>' +
261
+ '<Override PartName="/xl/worksheets/sheet1.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"/>' +
262
+ '</Types>';
263
+ var rootRelsXml = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n' +
264
+ '<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">' +
265
+ '<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="xl/workbook.xml"/>' +
266
+ '</Relationships>';
267
+ var workbookRelsXml = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n' +
268
+ '<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">' +
269
+ '<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet" Target="worksheets/sheet1.xml"/>' +
270
+ '</Relationships>';
271
+ var workbookXml = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n' +
272
+ '<workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">' +
273
+ "<sheets><sheet name=\"".concat(escapeXml(rawSheetName), "\" sheetId=\"1\" r:id=\"rId1\"/></sheets>") +
274
+ '</workbook>';
275
+ var worksheetXml = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n' +
276
+ '<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">' +
277
+ "<sheetData>".concat(sheetRows.join(''), "</sheetData>") +
278
+ '</worksheet>';
279
+ return createZipArchive([
280
+ { name: '[Content_Types].xml', data: contentTypesXml },
281
+ { name: '_rels/.rels', data: rootRelsXml },
282
+ { name: 'xl/_rels/workbook.xml.rels', data: workbookRelsXml },
283
+ { name: 'xl/workbook.xml', data: workbookXml },
284
+ { name: 'xl/worksheets/sheet1.xml', data: worksheetXml },
285
+ ]);
286
+ }
287
+ function downloadExcel(blobOrBytes, fileName) {
288
+ if (fileName === void 0) { fileName = 'export.xlsx'; }
289
+ if (typeof window === 'undefined' || typeof document === 'undefined') {
290
+ return;
291
+ }
292
+ var resolvedName = (fileName || '').trim();
293
+ if (!resolvedName) {
294
+ resolvedName = 'export.xlsx';
295
+ }
296
+ else if (!resolvedName.toLowerCase().endsWith('.xlsx')) {
297
+ resolvedName += '.xlsx';
298
+ }
299
+ var blob = blobOrBytes instanceof Blob
300
+ ? blobOrBytes
301
+ : new Blob([blobOrBytes], {
302
+ type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
303
+ });
304
+ if (typeof URL !== 'undefined' && typeof URL.createObjectURL === 'function') {
305
+ var url = URL.createObjectURL(blob);
306
+ var link = document.createElement('a');
307
+ link.href = url;
308
+ link.setAttribute('download', resolvedName);
309
+ link.style.display = 'none';
310
+ document.body.appendChild(link);
311
+ link.click();
312
+ document.body.removeChild(link);
313
+ if (typeof URL.revokeObjectURL === 'function') {
314
+ URL.revokeObjectURL(url);
315
+ }
316
+ }
317
+ }
@@ -43,3 +43,6 @@ __exportStar(require("./rowReorder"), exports);
43
43
  __exportStar(require("./checkboxEditor"), exports);
44
44
  __exportStar(require("./columnVisibility"), exports);
45
45
  __exportStar(require("./checkedIndexes"), exports);
46
+ __exportStar(require("./exportData"), exports);
47
+ __exportStar(require("./exportCsv"), exports);
48
+ __exportStar(require("./exportExcel"), exports);
package/esm/BGrid.js CHANGED
@@ -685,7 +685,8 @@ export function BGrid({ ref, width, height, headerHeight = 30, footerHeight, sum
685
685
  ]);
686
686
  return (React.createElement(AppStoreProvider, { initialState: initialStoreState },
687
687
  React.createElement(TreeContext.Provider, { value: treeContextValue },
688
- React.createElement(Table, { ref: ref, columns: computedColumns,
688
+ React.createElement(Table, { ref: ref, sourceColumns: resolvedColumns,
689
+ columns: computedColumns,
689
690
  columnsGroup: visibilityProjection.columnsGroup,
690
691
  columnGroups: visibilityProjection.columnGroups,
691
692
  onChangeColumns: resolvedOnChangeColumns,
@@ -1,9 +1,9 @@
1
1
  import * as React from 'react';
2
2
  import { useCallback, useEffect, useRef } from 'react';
3
3
  import { useShallow } from 'zustand/react/shallow';
4
- import { useAppStore } from '../store';
4
+ import { useAppStore, useAppStoreApi } from '../store';
5
5
  import { BGridDataItemStatus, } from '../types';
6
- import { getCellValueByRowKey, getFrozenColumnsWidth, getRowIndexAtOffset, getVisibleScrollableRowRange, getVirtualScrollWindowMetrics, getVirtualScrollWindowPosition, isCheckboxValueChecked, markCellEdited, markCellValueChanged, rebaseVirtualScrollWindow, resolveLogicalCell, shouldRenderBottomBar, } from '../utils';
6
+ import { getCellValueByRowKey, getFrozenColumnsWidth, getRowIndexAtOffset, getVisibleScrollableRowRange, getVirtualScrollWindowMetrics, getVirtualScrollWindowPosition, isCheckboxValueChecked, markCellEdited, markCellValueChanged, rebaseVirtualScrollWindow, resolveLogicalCell, shouldRenderBottomBar, createGridExportData, serializeGridExportDataToCsv, downloadCsv, } from '../utils';
7
7
  import { clamp, ensureCellVisible } from '../utils/coordinate';
8
8
  import Loading from './Loading';
9
9
  import TableBody, { createRowKeyRegistry } from './TableBody';
@@ -29,6 +29,7 @@ const LazyGridOptionalSurfaces = React.lazy(() => import('./GridOptionalSurfaces
29
29
  default: module.GridOptionalSurfaces,
30
30
  })));
31
31
  function Table(props) {
32
+ const store = useAppStoreApi();
32
33
  const { cellSelectionOptions, onChangeData, sourceIndexByVisibleIndex } = props;
33
34
  const cellSelectionEnabled = cellSelectionOptions?.enabled ?? true;
34
35
  const rowKeyRegistryState = useRef({
@@ -507,11 +508,42 @@ function Table(props) {
507
508
  const scrollPropsRef = useRef({ top: props.scrollTop, left: props.scrollLeft });
508
509
  const syncedDataPropRef = useRef(props.data);
509
510
  const [rowScrollRequest, setRowScrollRequest] = React.useState(null);
511
+ const sourceColumnsRef = useRef(props.sourceColumns);
512
+ sourceColumnsRef.current = props.sourceColumns;
513
+ const getCurrentExportData = useCallback((options) => {
514
+ const state = store.getState();
515
+ return createGridExportData({
516
+ options,
517
+ columns: state.columns,
518
+ sourceColumns: sourceColumnsRef.current,
519
+ data: state.data,
520
+ sourceData: state.sourceData,
521
+ sourceIndexByVisibleIndex: state.sourceIndexByVisibleIndex,
522
+ visibleIndexBySourceIndex: state.visibleIndexBySourceIndex,
523
+ checkedIndexesMap: state.checkedIndexesMap,
524
+ rowKey: state.rowKey,
525
+ });
526
+ }, [store]);
510
527
  React.useImperativeHandle(props.ref, () => ({
511
528
  scrollToRow(rowIndex, options) {
512
529
  setRowScrollRequest({ rowIndex, align: options?.align ?? 'nearest' });
513
530
  },
514
- }), []);
531
+ getExportData(options) {
532
+ return getCurrentExportData(options);
533
+ },
534
+ exportCsv(options) {
535
+ const exportData = getCurrentExportData(options);
536
+ const csv = serializeGridExportDataToCsv(exportData, options);
537
+ downloadCsv(csv, options?.fileName ?? 'export.csv');
538
+ },
539
+ exportExcel(options) {
540
+ const exportData = getCurrentExportData(options);
541
+ void import('../utils/exportExcel').then(({ serializeGridExportDataToExcel, downloadExcel }) => {
542
+ const excelBytes = serializeGridExportDataToExcel(exportData, options);
543
+ downloadExcel(excelBytes, options?.fileName ?? 'export.xlsx');
544
+ });
545
+ },
546
+ }), [getCurrentExportData]);
515
547
  const scrollRafRef = useRef(null);
516
548
  const scrollIdleTimerRef = useRef(null);
517
549
  const selectionAutoScrollRafRef = useRef(null);
@@ -0,0 +1,97 @@
1
+ export function formatCsvCellValue(value, preventFormulaInjection = true) {
2
+ if (value === null || value === undefined) {
3
+ return '';
4
+ }
5
+ let formatted;
6
+ if (typeof value === 'string') {
7
+ formatted = value;
8
+ const startsWithFormula = /^[=+\-@]/.test(formatted);
9
+ const startsWithDangerousControl = /^[\t\r\n]/.test(formatted);
10
+ const hasFormulaAfterLeadingWhitespace = /^\s+[=+\-@]/.test(formatted);
11
+ if (preventFormulaInjection &&
12
+ (startsWithFormula || startsWithDangerousControl || hasFormulaAfterLeadingWhitespace)) {
13
+ formatted = `'${formatted}`;
14
+ }
15
+ return formatted;
16
+ }
17
+ if (typeof value === 'number') {
18
+ return String(value);
19
+ }
20
+ if (typeof value === 'boolean') {
21
+ return value ? 'true' : 'false';
22
+ }
23
+ if (typeof value === 'bigint') {
24
+ return value.toString();
25
+ }
26
+ if (value instanceof Date) {
27
+ return isNaN(value.getTime()) ? 'Invalid Date' : value.toISOString();
28
+ }
29
+ if (typeof value === 'object') {
30
+ try {
31
+ formatted = JSON.stringify(value);
32
+ }
33
+ catch {
34
+ formatted = String(value);
35
+ }
36
+ return formatted;
37
+ }
38
+ return String(value);
39
+ }
40
+ export function escapeCsvCell(cell, delimiter = ',') {
41
+ const needsQuotes = cell.includes(delimiter) ||
42
+ cell.includes('"') ||
43
+ cell.includes('\r') ||
44
+ cell.includes('\n');
45
+ if (needsQuotes) {
46
+ return `"${cell.replace(/"/g, '""')}"`;
47
+ }
48
+ return cell;
49
+ }
50
+ export function serializeGridExportDataToCsv(data, options) {
51
+ const delimiter = options?.delimiter ?? ',';
52
+ const newline = options?.newline ?? '\r\n';
53
+ const includeHeader = options?.includeHeader ?? true;
54
+ const bom = options?.bom ?? true;
55
+ const preventFormulaInjection = options?.preventFormulaInjection ?? true;
56
+ const lines = [];
57
+ if (includeHeader) {
58
+ const headerLine = data.columns
59
+ .map(col => escapeCsvCell(formatCsvCellValue(col.header, preventFormulaInjection), delimiter))
60
+ .join(delimiter);
61
+ lines.push(headerLine);
62
+ }
63
+ for (const row of data.rows) {
64
+ const rowLine = row.cells
65
+ .map(cell => escapeCsvCell(formatCsvCellValue(cell, preventFormulaInjection), delimiter))
66
+ .join(delimiter);
67
+ lines.push(rowLine);
68
+ }
69
+ const csvText = lines.join(newline);
70
+ return (bom ? '\uFEFF' : '') + csvText;
71
+ }
72
+ export function downloadCsv(csv, fileName = 'export.csv') {
73
+ if (typeof window === 'undefined' || typeof document === 'undefined') {
74
+ return;
75
+ }
76
+ let resolvedName = (fileName || '').trim();
77
+ if (!resolvedName) {
78
+ resolvedName = 'export.csv';
79
+ }
80
+ else if (!resolvedName.toLowerCase().endsWith('.csv')) {
81
+ resolvedName += '.csv';
82
+ }
83
+ const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' });
84
+ if (typeof URL !== 'undefined' && typeof URL.createObjectURL === 'function') {
85
+ const url = URL.createObjectURL(blob);
86
+ const link = document.createElement('a');
87
+ link.href = url;
88
+ link.setAttribute('download', resolvedName);
89
+ link.style.display = 'none';
90
+ document.body.appendChild(link);
91
+ link.click();
92
+ document.body.removeChild(link);
93
+ if (typeof URL.revokeObjectURL === 'function') {
94
+ URL.revokeObjectURL(url);
95
+ }
96
+ }
97
+ }
@@ -0,0 +1,154 @@
1
+ import { getColumnId } from './getColumnId';
2
+ import { getCellValueByRowKey } from './getCellValue';
3
+ export function resolveExportColumns(columns, sourceColumns, scope = 'visible') {
4
+ let selectedColumns;
5
+ if (Array.isArray(scope)) {
6
+ const candidatePool = sourceColumns && sourceColumns.length > 0 ? sourceColumns : columns;
7
+ const columnMap = new Map();
8
+ for (const col of candidatePool) {
9
+ columnMap.set(getColumnId(col), col);
10
+ }
11
+ selectedColumns = [];
12
+ for (const id of scope) {
13
+ const col = columnMap.get(id);
14
+ if (col) {
15
+ selectedColumns.push(col);
16
+ }
17
+ }
18
+ }
19
+ else if (scope === 'all') {
20
+ selectedColumns = sourceColumns && sourceColumns.length > 0 ? [...sourceColumns] : [...columns];
21
+ }
22
+ else {
23
+ // 'visible'
24
+ selectedColumns = [...columns];
25
+ }
26
+ const exportableColumns = selectedColumns.filter(col => col.exportable !== false);
27
+ return exportableColumns.map((column, columnIndex) => {
28
+ const columnId = getColumnId(column);
29
+ let header;
30
+ if (typeof column.exportHeader === 'function') {
31
+ header = column.exportHeader(column);
32
+ }
33
+ else if (typeof column.exportHeader === 'string') {
34
+ header = column.exportHeader;
35
+ }
36
+ else if (typeof column.label === 'string' || typeof column.label === 'number') {
37
+ header = String(column.label);
38
+ }
39
+ else {
40
+ header = columnId;
41
+ }
42
+ return {
43
+ columnId,
44
+ header,
45
+ column,
46
+ columnIndex,
47
+ };
48
+ });
49
+ }
50
+ export function resolveExportRows(exportColumns, params) {
51
+ const rowScope = params.options?.rows ?? 'displayed';
52
+ const rows = [];
53
+ const sourcePool = params.sourceData && params.sourceData.length > 0 ? params.sourceData : params.data;
54
+ const buildCells = (item, values, sourceIndex, visibleIndex) => {
55
+ const cells = [];
56
+ for (const expCol of exportColumns) {
57
+ const rawValue = getCellValueByRowKey(expCol.column.key, values);
58
+ if (expCol.column.getExportValue) {
59
+ cells.push(expCol.column.getExportValue({
60
+ column: expCol.column,
61
+ columnId: expCol.columnId,
62
+ visibleIndex,
63
+ sourceIndex,
64
+ item,
65
+ values,
66
+ value: rawValue,
67
+ }));
68
+ }
69
+ else {
70
+ cells.push(rawValue);
71
+ }
72
+ }
73
+ return cells;
74
+ };
75
+ const resolveRowKey = (item, values) => {
76
+ let rowKey;
77
+ if (params.rowKey !== undefined && values) {
78
+ const rawKey = getCellValueByRowKey(params.rowKey, values);
79
+ if (rawKey !== undefined && rawKey !== null) {
80
+ rowKey = rawKey;
81
+ }
82
+ }
83
+ if (rowKey === undefined && item?.key !== undefined) {
84
+ rowKey = item.key;
85
+ }
86
+ return rowKey;
87
+ };
88
+ if (rowScope === 'displayed') {
89
+ const displayData = params.data;
90
+ for (let visibleIndex = 0; visibleIndex < displayData.length; visibleIndex++) {
91
+ const item = displayData[visibleIndex] ?? { values: {} };
92
+ const sourceIndex = params.sourceIndexByVisibleIndex?.[visibleIndex] ?? visibleIndex;
93
+ const values = item.values ?? {};
94
+ const rowKey = resolveRowKey(item, values);
95
+ const cells = buildCells(item, values, sourceIndex, visibleIndex);
96
+ rows.push({
97
+ visibleIndex,
98
+ sourceIndex,
99
+ rowKey,
100
+ item,
101
+ values,
102
+ cells,
103
+ });
104
+ }
105
+ }
106
+ else if (rowScope === 'checked') {
107
+ const checkedMap = params.checkedIndexesMap;
108
+ for (let sourceIndex = 0; sourceIndex < sourcePool.length; sourceIndex++) {
109
+ if (!checkedMap?.has(sourceIndex))
110
+ continue;
111
+ const item = sourcePool[sourceIndex] ?? { values: {} };
112
+ const visibleIndex = params.visibleIndexBySourceIndex?.get(sourceIndex);
113
+ const values = item.values ?? {};
114
+ const rowKey = resolveRowKey(item, values);
115
+ const cells = buildCells(item, values, sourceIndex, visibleIndex);
116
+ rows.push({
117
+ visibleIndex,
118
+ sourceIndex,
119
+ rowKey,
120
+ item,
121
+ values,
122
+ cells,
123
+ });
124
+ }
125
+ }
126
+ else {
127
+ // 'source'
128
+ for (let sourceIndex = 0; sourceIndex < sourcePool.length; sourceIndex++) {
129
+ const item = sourcePool[sourceIndex] ?? { values: {} };
130
+ const visibleIndex = params.visibleIndexBySourceIndex?.get(sourceIndex);
131
+ const values = item.values ?? {};
132
+ const rowKey = resolveRowKey(item, values);
133
+ const cells = buildCells(item, values, sourceIndex, visibleIndex);
134
+ rows.push({
135
+ visibleIndex,
136
+ sourceIndex,
137
+ rowKey,
138
+ item,
139
+ values,
140
+ cells,
141
+ });
142
+ }
143
+ }
144
+ return rows;
145
+ }
146
+ export function createGridExportData(params) {
147
+ const columnScope = params.options?.columns ?? 'visible';
148
+ const columns = resolveExportColumns(params.columns, params.sourceColumns, columnScope);
149
+ const rows = resolveExportRows(columns, params);
150
+ return {
151
+ columns,
152
+ rows,
153
+ };
154
+ }