Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

make "ctrl + c" great again #1379

Merged
merged 9 commits into from
Feb 8, 2024
6 changes: 4 additions & 2 deletions examples/vite-demo-vanilla-bundle/src/examples/example19.html
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,10 @@ <h3 class="title is-3">
</h3>

<h5 class="title is-5 mb-3">
Grid - using <code>enableExcelCopyBuffer</code> which uses <code>SlickCellSelectionModel</code><br />
The complete first row and the cells C - E of the second row are not allowing to paste values.
<p>Grid - using <code>enableExcelCopyBuffer</code> which uses <code>SlickCellSelectionModel</code></p>
<p>The complete first row and the cells C - E of the second row are not allowing to paste values. </p>
<p>Additionally the columns are configured to <code>exportWithFormatter</code> and a custom formatter that renders the cells coordinates or value if available.
When selecting one or more cells and pressing CTRL + C, the ExcelCopyBuffer will be filled with the formatter outputs of the selected cells.</p>
</h5>
<h6 class="title is-6">
<button class="button is-small is-primary" onclick.delegate="togglePagination()"
Expand Down
8 changes: 8 additions & 0 deletions examples/vite-demo-vanilla-bundle/src/examples/example19.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,14 @@ export default class Example19 {
: String.fromCharCode('A'.charCodeAt(0) + (Math.floor(i / 26)) - 1) + String.fromCharCode('A'.charCodeAt(0) + (i % 26)),
field: String(i),
minWidth: 60,
exportWithFormatter: true,
formatter: (row: number, cell: number, value: any) => {
zewa666 marked this conversation as resolved.
Show resolved Hide resolved
if (value !== null && value !== undefined) {
return value;
}

return `${row + 1}:${cell + 1}`;
},
width: 60,
editor: { model: Editors.text }
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import type { EditCommand, Formatter, GridOption } from '../../interfaces/index';
import { Formatters } from '../../formatters';
import { SharedService } from '../../services/shared.service';
import { SlickCellExcelCopyManager } from '../slickCellExcelCopyManager';
import { SlickCellSelectionModel } from '../slickCellSelectionModel';
import { SlickCellExternalCopyManager } from '../slickCellExternalCopyManager';
import { SlickEvent, SlickEventData, SlickGrid, SlickRange } from '../../core/index';
import { Editors } from '../../editors';

jest.mock('flatpickr', () => { });

Expand All @@ -17,6 +17,8 @@ const gridStub = {
getData: jest.fn(),
getOptions: jest.fn(),
getSelectionModel: jest.fn(),
getActiveCell: jest.fn(),
getCellEditor: jest.fn(),
getEditorLock: () => getEditorLockMock,
focus: jest.fn(),
registerPlugin: jest.fn(),
Expand Down Expand Up @@ -356,14 +358,36 @@ describe('CellExcelCopyManager', () => {
expect(output).toBe('John');
});

it('should return null when calling "dataItemColumnValueExtractor" callback without editable', () => {
it('should return null when calling "dataItemColumnValueExtractor" callback with editable and editor, which is active on the current cell', () => {
jest.spyOn(gridStub, 'getOptions').mockReturnValue(gridOptionsMock);
plugin.init(gridStub);
(gridStub.getCellEditor as jest.Mock).mockReturnValue({});
(gridStub.getActiveCell as jest.Mock).mockReturnValue({ row: 6, cell: 6 });

const output = plugin.addonOptions!.dataItemColumnValueExtractor!({ firstName: '<b>John</b>', lastName: 'Doe' }, { id: 'firstName', field: 'firstName', exportWithFormatter: true, editor: { model: Editors.text }, formatter: myBoldFormatter}, 6, 6);

expect(output).toBeNull();
});

it('should forward provided row and cell to formatter when calling "dataItemColumnValueExtractor"', () => {
jest.spyOn(SharedService.prototype, 'gridOptions', 'get').mockReturnValue(gridOptionsMock);
plugin.init(gridStub);

const rowCellFormatter: Formatter = (row, cell) => `${row}:${cell}`;
const output = plugin.addonOptions!.dataItemColumnValueExtractor!({ firstName: '<b>John</b>', lastName: 'Doe' }, { id: 'firstName', field: 'firstName', exportWithFormatter: true, formatter: rowCellFormatter }, 6, 6);

expect(output).toBe('6:6');
});

it('should format output even if not editable and an editor is configured but a formatter is defined', () => {
gridOptionsMock.editable = false;
jest.spyOn(SharedService.prototype, 'gridOptions', 'get').mockReturnValue(gridOptionsMock);
plugin.init(gridStub);

const output = plugin.addonOptions!.dataItemColumnValueExtractor!({ firstName: '<b>John</b>', lastName: 'Doe' }, { id: 'firstName', field: 'firstName' });
const rowCellFormatter: Formatter = (row, cell) => `${row}:${cell}`;
const output = plugin.addonOptions!.dataItemColumnValueExtractor!({ firstName: '<b>John</b>', lastName: 'Doe' }, { id: 'firstName', field: 'firstName', exportWithFormatter: true, editor: { model: Editors.text }, formatter: rowCellFormatter }, 6, 6);

expect(output).toBeNull();
expect(output).toBe('6:6');
});
});
});
9 changes: 6 additions & 3 deletions packages/common/src/extensions/slickCellExcelCopyManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,13 +142,16 @@ export class SlickCellExcelCopyManager {
clipboardCommandHandler: (editCommand: EditCommand) => {
this._undoRedoBuffer.queueAndExecuteCommand.call(this._undoRedoBuffer, editCommand);
},
dataItemColumnValueExtractor: (item: any, columnDef: Column) => {
dataItemColumnValueExtractor: (item: any, columnDef: Column, row: number = 0, cell: number = 0) => {
zewa666 marked this conversation as resolved.
Show resolved Hide resolved
// when grid or cell is not editable, we will possibly evaluate the Formatter if it was passed
// to decide if we evaluate the Formatter, we will use the same flag from Export which is "exportWithFormatter"
if (!this.gridOptions.editable || !columnDef.editor) {
const activeCell = this._grid.getActiveCell();
const isActiveEditorCurrentCell = this._grid.getCellEditor() && activeCell?.row === row && activeCell?.cell === cell;

if (!this.gridOptions.editable || !columnDef.editor || !isActiveEditorCurrentCell) {
const isEvaluatingFormatter = (columnDef.exportWithFormatter !== undefined) ? columnDef.exportWithFormatter : (this.gridOptions.textExportOptions?.exportWithFormatter);
if (columnDef.formatter && isEvaluatingFormatter) {
const formattedOutput = columnDef.formatter(0, 0, item[columnDef.field], columnDef, item, this._grid);
const formattedOutput = columnDef.formatter(row, cell, item[columnDef.field], columnDef, item, this._grid);
const cellResult = isPrimitiveOrHTML(formattedOutput) ? formattedOutput : (formattedOutput as FormatterResultWithHtml).html || (formattedOutput as FormatterResultWithText).text;
if (columnDef.sanitizeDataExport || (this.gridOptions.textExportOptions?.sanitizeDataExport)) {
const outputString = (cellResult instanceof HTMLElement) ? cellResult.innerHTML : cellResult as string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,9 +115,9 @@ export class SlickCellExternalCopyManager {
return getHtmlStringOutput(columnDef.name || '', 'innerHTML');
}

getDataItemValueForColumn(item: any, columnDef: Column, event: SlickEventData) {
getDataItemValueForColumn(item: any, columnDef: Column, row: number, cell: number, event: SlickEventData) {
if (typeof this._addonOptions.dataItemColumnValueExtractor === 'function') {
const val = this._addonOptions.dataItemColumnValueExtractor(item, columnDef) as string | HTMLElement;
const val = this._addonOptions.dataItemColumnValueExtractor(item, columnDef, row, cell) as string | HTMLElement;
if (val) {
return (val instanceof HTMLElement) ? stripTags(val.innerHTML) : val;
}
Expand Down Expand Up @@ -433,7 +433,7 @@ export class SlickCellExternalCopyManager {
? stripTags((columns[j].name as HTMLElement).innerHTML)
: columns[j].name as string;
if (colName.length > 0 && !columns[j].hidden) {
clipTextCells.push(this.getDataItemValueForColumn(dt, columns[j], e));
clipTextCells.push(this.getDataItemValueForColumn(dt, columns[j], i, j, e));
}
}
clipTextRows.push(clipTextCells.join('\t'));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export interface ExcelCopyBufferOption<T = any> {
copiedCellStyleLayerKey?: string;

/** option to specify a custom column value extractor function */
dataItemColumnValueExtractor?: (item: any, columnDef: Column<T>) => string | HTMLElement | DocumentFragment | FormatterResultWithHtml | FormatterResultWithText | null;
dataItemColumnValueExtractor?: (item: any, columnDef: Column<T>, row?: number, cell?: number) => string | HTMLElement | DocumentFragment | FormatterResultWithHtml | FormatterResultWithText | null;

/** option to specify a custom column value setter function */
dataItemColumnValueSetter?: (item: any, columnDef: Column<T>, value: any) => string | FormatterResultWithHtml | FormatterResultWithText | null;
Expand Down
Loading